root/ipc/sem.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sem_init
  2. findkey
  3. newary
  4. sys_semget
  5. insert_into_queue
  6. remove_from_queue
  7. try_semop
  8. do_semop
  9. update_queue
  10. count_semncnt
  11. count_semzcnt
  12. freeary
  13. sys_semctl
  14. sys_semop
  15. sem_exit

   1 /*
   2  * linux/ipc/sem.c
   3  * Copyright (C) 1992 Krishna Balasubramanian
   4  * Copyright (C) 1995 Eric Schenk, Bruno Haible
   5  *
   6  * IMPLEMENTATION NOTES ON CODE REWRITE (Eric Schenk, January 1995):
   7  * This code underwent a massive rewrite in order to solve some problems
   8  * with the original code. In particular the original code failed to
   9  * wake up processes that were waiting for semval to go to 0 if the
  10  * value went to 0 and was then incremented rapidly enough. In solving
  11  * this problem I have also modified the implementation so that it
  12  * processes pending operations in a FIFO manner, thus give a guarantee
  13  * that processes waiting for a lock on the semaphore won't starve
  14  * unless another locking process fails to unlock.
  15  * In addition the following two changes in behavior have been introduced:
  16  * - The original implementation of semop returned the value
  17  *   last semaphore element examined on success. This does not
  18  *   match the manual page specifications, and effectively
  19  *   allows the user to read the semaphore even if they do not
  20  *   have read permissions. The implementation now returns 0
  21  *   on success as stated in the manual page.
  22  * - There is some confusion over whether the set of undo adjustments
  23  *   to be performed at exit should be done in an atomic manner.
  24  *   That is, if we are attempting to decrement the semval should we queue
  25  *   up and wait until we can do so legally?
  26  *   The original implementation attempted to do this.
  27  *   The current implementation does not do so. This is because I don't
  28  *   think it is the right thing (TM) to do, and because I couldn't
  29  *   see a clean way to get the old behavior with the new design.
  30  *   The POSIX standard and SVID should be consulted to determine
  31  *   what behavior is mandated.
  32  */
  33 
  34 #include <linux/errno.h>
  35 #include <asm/segment.h>
  36 #include <linux/string.h>
  37 #include <linux/sched.h>
  38 #include <linux/sem.h>
  39 #include <linux/ipc.h>
  40 #include <linux/stat.h>
  41 #include <linux/malloc.h>
  42 
  43 extern int ipcperms (struct ipc_perm *ipcp, short semflg);
  44 static int newary (key_t, int, int);
  45 static int findkey (key_t key);
  46 static void freeary (int id);
  47 
  48 static struct semid_ds *semary[SEMMNI];
  49 static int used_sems = 0, used_semids = 0;
  50 static struct wait_queue *sem_lock = NULL;
  51 static int max_semid = 0;
  52 
  53 static unsigned short sem_seq = 0;
  54 
  55 void sem_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  56 {
  57         int i;
  58 
  59         sem_lock = NULL;
  60         used_sems = used_semids = max_semid = sem_seq = 0;
  61         for (i = 0; i < SEMMNI; i++)
  62                 semary[i] = (struct semid_ds *) IPC_UNUSED;
  63         return;
  64 }
  65 
  66 static int findkey (key_t key)
     /* [previous][next][first][last][top][bottom][index][help] */
  67 {
  68         int id;
  69         struct semid_ds *sma;
  70 
  71         for (id = 0; id <= max_semid; id++) {
  72                 while ((sma = semary[id]) == IPC_NOID)
  73                         interruptible_sleep_on (&sem_lock);
  74                 if (sma == IPC_UNUSED)
  75                         continue;
  76                 if (key == sma->sem_perm.key)
  77                         return id;
  78         }
  79         return -1;
  80 }
  81 
  82 static int newary (key_t key, int nsems, int semflg)
     /* [previous][next][first][last][top][bottom][index][help] */
  83 {
  84         int id;
  85         struct semid_ds *sma;
  86         struct ipc_perm *ipcp;
  87         int size;
  88 
  89         if (!nsems)
  90                 return -EINVAL;
  91         if (used_sems + nsems > SEMMNS)
  92                 return -ENOSPC;
  93         for (id = 0; id < SEMMNI; id++)
  94                 if (semary[id] == IPC_UNUSED) {
  95                         semary[id] = (struct semid_ds *) IPC_NOID;
  96                         goto found;
  97                 }
  98         return -ENOSPC;
  99 found:
 100         size = sizeof (*sma) + nsems * sizeof (struct sem);
 101         used_sems += nsems;
 102         sma = (struct semid_ds *) kmalloc (size, GFP_KERNEL);
 103         if (!sma) {
 104                 semary[id] = (struct semid_ds *) IPC_UNUSED;
 105                 used_sems -= nsems;
 106                 if (sem_lock)
 107                         wake_up (&sem_lock);
 108                 return -ENOMEM;
 109         }
 110         memset (sma, 0, size);
 111         sma->sem_base = (struct sem *) &sma[1];
 112         ipcp = &sma->sem_perm;
 113         ipcp->mode = (semflg & S_IRWXUGO);
 114         ipcp->key = key;
 115         ipcp->cuid = ipcp->uid = current->euid;
 116         ipcp->gid = ipcp->cgid = current->egid;
 117         sma->sem_perm.seq = sem_seq;
 118         /* sma->sem_pending = NULL; */
 119         sma->sem_pending_last = &sma->sem_pending;
 120         /* sma->undo = NULL; */
 121         sma->sem_nsems = nsems;
 122         sma->sem_ctime = CURRENT_TIME;
 123         if (id > max_semid)
 124                 max_semid = id;
 125         used_semids++;
 126         semary[id] = sma;
 127         if (sem_lock)
 128                 wake_up (&sem_lock);
 129         return (unsigned int) sma->sem_perm.seq * SEMMNI + id;
 130 }
 131 
 132 asmlinkage int sys_semget (key_t key, int nsems, int semflg)
     /* [previous][next][first][last][top][bottom][index][help] */
 133 {
 134         int id;
 135         struct semid_ds *sma;
 136 
 137         if (nsems < 0 || nsems > SEMMSL)
 138                 return -EINVAL;
 139         if (key == IPC_PRIVATE)
 140                 return newary(key, nsems, semflg);
 141         if ((id = findkey (key)) == -1) {  /* key not used */
 142                 if (!(semflg & IPC_CREAT))
 143                         return -ENOENT;
 144                 return newary(key, nsems, semflg);
 145         }
 146         if (semflg & IPC_CREAT && semflg & IPC_EXCL)
 147                 return -EEXIST;
 148         sma = semary[id];
 149         if (nsems > sma->sem_nsems)
 150                 return -EINVAL;
 151         if (ipcperms(&sma->sem_perm, semflg))
 152                 return -EACCES;
 153         return (unsigned int) sma->sem_perm.seq * SEMMNI + id;
 154 }
 155 
 156 /* Manage the doubly linked list sma->sem_pending as a FIFO:
 157  * insert new queue elements at the tail sma->sem_pending_last.
 158  */
 159 static inline void insert_into_queue (struct semid_ds * sma, struct sem_queue * q)
     /* [previous][next][first][last][top][bottom][index][help] */
 160 {
 161         *(q->prev = sma->sem_pending_last) = q;
 162         *(sma->sem_pending_last = &q->next) = NULL;
 163 }
 164 static inline void remove_from_queue (struct semid_ds * sma, struct sem_queue * q)
     /* [previous][next][first][last][top][bottom][index][help] */
 165 {
 166         *(q->prev) = q->next;
 167         if (q->next)
 168                 q->next->prev = q->prev;
 169         else /* sma->sem_pending_last == &q->next */
 170                 sma->sem_pending_last = q->prev;
 171         q->prev = NULL; /* mark as removed */
 172 }
 173 
 174 /* Determine whether a sequence of semaphore operations would succeed
 175  * all at once. Return 0 if yes, 1 if need to sleep, else return error code.
 176  */
 177 static int try_semop (struct semid_ds * sma, struct sembuf * sops, int nsops)
     /* [previous][next][first][last][top][bottom][index][help] */
 178 {
 179         int result = 0;
 180         int i = 0;
 181 
 182         while (i < nsops) {
 183                 struct sembuf * sop = &sops[i];
 184                 struct sem * curr = &sma->sem_base[sop->sem_num];
 185                 if (sop->sem_op + curr->semval > SEMVMX) {
 186                         result = -ERANGE;
 187                         break;
 188                 }
 189                 if (!sop->sem_op && curr->semval) {
 190                         if (sop->sem_flg & IPC_NOWAIT)
 191                                 result = -EAGAIN;
 192                         else
 193                                 result = 1;
 194                         break;
 195                 }
 196                 i++;
 197                 curr->semval += sop->sem_op;
 198                 if (curr->semval < 0) {
 199                         if (sop->sem_flg & IPC_NOWAIT)
 200                                 result = -EAGAIN;
 201                         else
 202                                 result = 1;
 203                         break;
 204                 }
 205         }
 206         while (--i >= 0) {
 207                 struct sembuf * sop = &sops[i];
 208                 struct sem * curr = &sma->sem_base[sop->sem_num];
 209                 curr->semval -= sop->sem_op;
 210         }
 211         return result;
 212 }
 213 
 214 /* Actually perform a sequence of semaphore operations. Atomically. */
 215 /* This assumes that try_semop() already returned 0. */
 216 static int do_semop (struct semid_ds * sma, struct sembuf * sops, int nsops,
     /* [previous][next][first][last][top][bottom][index][help] */
 217                      struct sem_undo * un, int pid)
 218 {
 219         int i;
 220 
 221         for (i = 0; i < nsops; i++) {
 222                 struct sembuf * sop = &sops[i];
 223                 struct sem * curr = &sma->sem_base[sop->sem_num];
 224                 if (sop->sem_op + curr->semval > SEMVMX) {
 225                         printk("do_semop: race\n");
 226                         break;
 227                 }
 228                 if (!sop->sem_op) {
 229                         if (curr->semval) {
 230                                 printk("do_semop: race\n");
 231                                 break;
 232                         }
 233                 } else {
 234                         curr->semval += sop->sem_op;
 235                         if (curr->semval < 0) {
 236                                 printk("do_semop: race\n");
 237                                 break;
 238                         }
 239                         if (sop->sem_flg & SEM_UNDO)
 240                                 un->semadj[sop->sem_num] -= sop->sem_op;
 241                 }
 242                 curr->sempid = pid;
 243         }
 244         sma->sem_otime = CURRENT_TIME;
 245 
 246         /* Previous implementation returned the last semaphore's semval.
 247          * This is wrong because we may not have checked read permission,
 248          * only write permission.
 249          */
 250         return 0;
 251 }
 252 
 253 /* Go through the pending queue for the indicated semaphore
 254  * looking for tasks that can be completed. Keep cycling through
 255  * the queue until a pass is made in which no process is woken up.
 256  */
 257 static void update_queue (struct semid_ds * sma)
     /* [previous][next][first][last][top][bottom][index][help] */
 258 {
 259         int wokeup, error;
 260         struct sem_queue * q;
 261 
 262         do {
 263                 wokeup = 0;
 264                 for (q = sma->sem_pending; q; q = q->next) {
 265                         error = try_semop(sma, q->sops, q->nsops);
 266                         /* Does q->sleeper still need to sleep? */
 267                         if (error > 0)
 268                                 continue;
 269                         /* Perform the operations the sleeper was waiting for */
 270                         if (!error)
 271                                 error = do_semop(sma, q->sops, q->nsops, q->undo, q->pid);
 272                         q->status = error;
 273                         /* Remove it from the queue */
 274                         remove_from_queue(sma,q);
 275                         /* Wake it up */
 276                         wake_up_interruptible(&q->sleeper); /* doesn't sleep! */
 277                         wokeup++;
 278                 }
 279         } while (wokeup);
 280 }
 281 
 282 /* The following counts are associated to each semaphore:
 283  *   semncnt        number of tasks waiting on semval being nonzero
 284  *   semzcnt        number of tasks waiting on semval being zero
 285  * This model assumes that a task waits on exactly one semaphore.
 286  * Since semaphore operations are to be performed atomically, tasks actually
 287  * wait on a whole sequence of semaphores simultaneously.
 288  * The counts we return here are a rough approximation, but still
 289  * warrant that semncnt+semzcnt>0 if the task is on the pending queue.
 290  */
 291 static int count_semncnt (struct semid_ds * sma, ushort semnum)
     /* [previous][next][first][last][top][bottom][index][help] */
 292 {
 293         int semncnt;
 294         struct sem_queue * q;
 295 
 296         semncnt = 0;
 297         for (q = sma->sem_pending; q; q = q->next) {
 298                 struct sembuf * sops = q->sops;
 299                 int nsops = q->nsops;
 300                 int i;
 301                 for (i = 0; i < nsops; i++)
 302                         if (sops[i].sem_num == semnum
 303                             && (sops[i].sem_op < 0)
 304                             && !(sops[i].sem_flg & IPC_NOWAIT))
 305                                 semncnt++;
 306         }
 307         return semncnt;
 308 }
 309 static int count_semzcnt (struct semid_ds * sma, ushort semnum)
     /* [previous][next][first][last][top][bottom][index][help] */
 310 {
 311         int semzcnt;
 312         struct sem_queue * q;
 313 
 314         semzcnt = 0;
 315         for (q = sma->sem_pending; q; q = q->next) {
 316                 struct sembuf * sops = q->sops;
 317                 int nsops = q->nsops;
 318                 int i;
 319                 for (i = 0; i < nsops; i++)
 320                         if (sops[i].sem_num == semnum
 321                             && (sops[i].sem_op == 0)
 322                             && !(sops[i].sem_flg & IPC_NOWAIT))
 323                                 semzcnt++;
 324         }
 325         return semzcnt;
 326 }
 327 
 328 /* Free a semaphore set. */
 329 static void freeary (int id)
     /* [previous][next][first][last][top][bottom][index][help] */
 330 {
 331         struct semid_ds *sma = semary[id];
 332         struct sem_undo *un;
 333         struct sem_queue *q;
 334 
 335         /* Invalidate this semaphore set */
 336         sma->sem_perm.seq++;
 337         sem_seq = (sem_seq+1) % ((unsigned)(1<<31)/SEMMNI); /* increment, but avoid overflow */
 338         used_sems -= sma->sem_nsems;
 339         if (id == max_semid)
 340                 while (max_semid && (semary[--max_semid] == IPC_UNUSED));
 341         semary[id] = (struct semid_ds *) IPC_UNUSED;
 342         used_semids--;
 343 
 344         /* Invalidate the existing undo structures for this semaphore set.
 345          * (They will be freed without any further action in sem_exit().)
 346          */
 347         for (un = sma->undo; un; un = un->id_next)
 348                 un->semid = -1;
 349 
 350         /* Wake up all pending processes and let them fail with EIDRM. */
 351         for (q = sma->sem_pending; q; q = q->next) {
 352                 q->status = -EIDRM;
 353                 q->prev = NULL;
 354                 wake_up_interruptible(&q->sleeper); /* doesn't sleep! */
 355         }
 356 
 357         kfree(sma);
 358 }
 359 
 360 asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 361 {
 362         struct semid_ds *buf = NULL;
 363         struct semid_ds tbuf;
 364         int i, id, val = 0;
 365         struct semid_ds *sma;
 366         struct ipc_perm *ipcp;
 367         struct sem *curr = NULL;
 368         struct sem_undo *un;
 369         unsigned int nsems;
 370         ushort *array = NULL;
 371         ushort sem_io[SEMMSL];
 372 
 373         if (semid < 0 || semnum < 0 || cmd < 0)
 374                 return -EINVAL;
 375 
 376         switch (cmd) {
 377         case IPC_INFO:
 378         case SEM_INFO:
 379         {
 380                 struct seminfo seminfo, *tmp = arg.__buf;
 381                 seminfo.semmni = SEMMNI;
 382                 seminfo.semmns = SEMMNS;
 383                 seminfo.semmsl = SEMMSL;
 384                 seminfo.semopm = SEMOPM;
 385                 seminfo.semvmx = SEMVMX;
 386                 seminfo.semmnu = SEMMNU;
 387                 seminfo.semmap = SEMMAP;
 388                 seminfo.semume = SEMUME;
 389                 seminfo.semusz = SEMUSZ;
 390                 seminfo.semaem = SEMAEM;
 391                 if (cmd == SEM_INFO) {
 392                         seminfo.semusz = used_semids;
 393                         seminfo.semaem = used_sems;
 394                 }
 395                 i = verify_area(VERIFY_WRITE, tmp, sizeof(struct seminfo));
 396                 if (i)
 397                         return i;
 398                 memcpy_tofs (tmp, &seminfo, sizeof(struct seminfo));
 399                 return max_semid;
 400         }
 401 
 402         case SEM_STAT:
 403                 buf = arg.buf;
 404                 i = verify_area (VERIFY_WRITE, buf, sizeof (*buf));
 405                 if (i)
 406                         return i;
 407                 if (semid > max_semid)
 408                         return -EINVAL;
 409                 sma = semary[semid];
 410                 if (sma == IPC_UNUSED || sma == IPC_NOID)
 411                         return -EINVAL;
 412                 if (ipcperms (&sma->sem_perm, S_IRUGO))
 413                         return -EACCES;
 414                 id = (unsigned int) sma->sem_perm.seq * SEMMNI + semid;
 415                 tbuf.sem_perm   = sma->sem_perm;
 416                 tbuf.sem_otime  = sma->sem_otime;
 417                 tbuf.sem_ctime  = sma->sem_ctime;
 418                 tbuf.sem_nsems  = sma->sem_nsems;
 419                 memcpy_tofs (buf, &tbuf, sizeof(*buf));
 420                 return id;
 421         }
 422 
 423         id = (unsigned int) semid % SEMMNI;
 424         sma = semary [id];
 425         if (sma == IPC_UNUSED || sma == IPC_NOID)
 426                 return -EINVAL;
 427         ipcp = &sma->sem_perm;
 428         nsems = sma->sem_nsems;
 429         if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI)
 430                 return -EIDRM;
 431 
 432         switch (cmd) {
 433         case GETVAL:
 434         case GETPID:
 435         case GETNCNT:
 436         case GETZCNT:
 437         case SETVAL:
 438                 if (semnum >= nsems)
 439                         return -EINVAL;
 440                 curr = &sma->sem_base[semnum];
 441                 break;
 442         }
 443 
 444         switch (cmd) {
 445         case GETVAL:
 446         case GETPID:
 447         case GETNCNT:
 448         case GETZCNT:
 449         case GETALL:
 450                 if (ipcperms (ipcp, S_IRUGO))
 451                         return -EACCES;
 452                 switch (cmd) {
 453                 case GETVAL : return curr->semval;
 454                 case GETPID : return curr->sempid;
 455                 case GETNCNT: return count_semncnt(sma,semnum);
 456                 case GETZCNT: return count_semzcnt(sma,semnum);
 457                 case GETALL:
 458                         array = arg.array;
 459                         i = verify_area (VERIFY_WRITE, array, nsems*sizeof(ushort));
 460                         if (i)
 461                                 return i;
 462                 }
 463                 break;
 464         case SETVAL:
 465                 val = arg.val;
 466                 if (val > SEMVMX || val < 0)
 467                         return -ERANGE;
 468                 break;
 469         case IPC_RMID:
 470                 if (suser() || current->euid == ipcp->cuid || current->euid == ipcp->uid) {
 471                         freeary (id);
 472                         return 0;
 473                 }
 474                 return -EPERM;
 475         case SETALL: /* arg is a pointer to an array of ushort */
 476                 array = arg.array;
 477                 if ((i = verify_area (VERIFY_READ, array, nsems*sizeof(ushort))))
 478                         return i;
 479                 memcpy_fromfs (sem_io, array, nsems*sizeof(ushort));
 480                 for (i = 0; i < nsems; i++)
 481                         if (sem_io[i] > SEMVMX)
 482                                 return -ERANGE;
 483                 break;
 484         case IPC_STAT:
 485                 buf = arg.buf;
 486                 if ((i = verify_area (VERIFY_WRITE, buf, sizeof(*buf))))
 487                         return i;
 488                 break;
 489         case IPC_SET:
 490                 buf = arg.buf;
 491                 if ((i = verify_area (VERIFY_READ, buf, sizeof (*buf))))
 492                         return i;
 493                 memcpy_fromfs (&tbuf, buf, sizeof (*buf));
 494                 break;
 495         }
 496 
 497         if (semary[id] == IPC_UNUSED || semary[id] == IPC_NOID)
 498                 return -EIDRM;
 499         if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI)
 500                 return -EIDRM;
 501 
 502         switch (cmd) {
 503         case GETALL:
 504                 if (ipcperms (ipcp, S_IRUGO))
 505                         return -EACCES;
 506                 for (i = 0; i < sma->sem_nsems; i++)
 507                         sem_io[i] = sma->sem_base[i].semval;
 508                 memcpy_tofs (array, sem_io, nsems*sizeof(ushort));
 509                 break;
 510         case SETVAL:
 511                 if (ipcperms (ipcp, S_IWUGO))
 512                         return -EACCES;
 513                 for (un = sma->undo; un; un = un->id_next)
 514                         un->semadj[semnum] = 0;
 515                 curr->semval = val;
 516                 sma->sem_ctime = CURRENT_TIME;
 517                 /* maybe some queued-up processes were waiting for this */
 518                 update_queue(sma);
 519                 break;
 520         case IPC_SET:
 521                 if (suser() || current->euid == ipcp->cuid || current->euid == ipcp->uid) {
 522                         ipcp->uid = tbuf.sem_perm.uid;
 523                         ipcp->gid = tbuf.sem_perm.gid;
 524                         ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
 525                                 | (tbuf.sem_perm.mode & S_IRWXUGO);
 526                         sma->sem_ctime = CURRENT_TIME;
 527                         return 0;
 528                 }
 529                 return -EPERM;
 530         case IPC_STAT:
 531                 if (ipcperms (ipcp, S_IRUGO))
 532                         return -EACCES;
 533                 tbuf.sem_perm   = sma->sem_perm;
 534                 tbuf.sem_otime  = sma->sem_otime;
 535                 tbuf.sem_ctime  = sma->sem_ctime;
 536                 tbuf.sem_nsems  = sma->sem_nsems;
 537                 memcpy_tofs (buf, &tbuf, sizeof(*buf));
 538                 break;
 539         case SETALL:
 540                 if (ipcperms (ipcp, S_IWUGO))
 541                         return -EACCES;
 542                 for (i = 0; i < nsems; i++)
 543                         sma->sem_base[i].semval = sem_io[i];
 544                 for (un = sma->undo; un; un = un->id_next)
 545                         for (i = 0; i < nsems; i++)
 546                                 un->semadj[i] = 0;
 547                 sma->sem_ctime = CURRENT_TIME;
 548                 /* maybe some queued-up processes were waiting for this */
 549                 update_queue(sma);
 550                 break;
 551         default:
 552                 return -EINVAL;
 553         }
 554         return 0;
 555 }
 556 
 557 asmlinkage int sys_semop (int semid, struct sembuf *tsops, unsigned nsops)
     /* [previous][next][first][last][top][bottom][index][help] */
 558 {
 559         int i, id, size, error;
 560         struct semid_ds *sma;
 561         struct sembuf sops[SEMOPM], *sop;
 562         struct sem_undo *un;
 563         int undos = 0, alter = 0;
 564 
 565         if (nsops < 1 || semid < 0)
 566                 return -EINVAL;
 567         if (nsops > SEMOPM)
 568                 return -E2BIG;
 569         if (!tsops)
 570                 return -EFAULT;
 571         if ((i = verify_area (VERIFY_READ, tsops, nsops * sizeof(*tsops))))
 572                 return i;
 573         memcpy_fromfs (sops, tsops, nsops * sizeof(*tsops));
 574         id = (unsigned int) semid % SEMMNI;
 575         if ((sma = semary[id]) == IPC_UNUSED || sma == IPC_NOID)
 576                 return -EINVAL;
 577         if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI)
 578                 return -EIDRM;
 579         for (i = 0; i < nsops; i++) {
 580                 sop = &sops[i];
 581                 if (sop->sem_num >= sma->sem_nsems)
 582                         return -EFBIG;
 583                 if (sop->sem_flg & SEM_UNDO)
 584                         undos++;
 585                 if (sop->sem_op)
 586                         alter++;
 587         }
 588         if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
 589                 return -EACCES;
 590         error = try_semop(sma, sops, nsops);
 591         if (error < 0)
 592                 return error;
 593         if (undos) {
 594                 /* Make sure we have an undo structure
 595                  * for this process and this semaphore set.
 596                  */
 597                 for (un = current->semundo; un; un = un->proc_next)
 598                         if (un->semid == semid)
 599                                 break;
 600                 if (!un) {
 601                         size = sizeof(struct sem_undo) + sizeof(short)*sma->sem_nsems;
 602                         un = (struct sem_undo *) kmalloc(size, GFP_ATOMIC);
 603                         if (!un)
 604                                 return -ENOMEM;
 605                         memset(un, 0, size);
 606                         un->semadj = (short *) &un[1];
 607                         un->semid = semid;
 608                         un->proc_next = current->semundo;
 609                         current->semundo = un;
 610                         un->id_next = sma->undo;
 611                         sma->undo = un;
 612                 }
 613         } else
 614                 un = NULL;
 615         if (error == 0) {
 616                 /* the operations go through immediately */
 617                 error = do_semop(sma, sops, nsops, un, current->pid);
 618                 /* maybe some queued-up processes were waiting for this */
 619                 update_queue(sma);
 620                 return error;
 621         } else {
 622                 /* We need to sleep on this operation, so we put the current
 623                  * task into the pending queue and go to sleep.
 624                  */
 625                 struct sem_queue queue;
 626 
 627                 queue.sma = sma;
 628                 queue.sops = sops;
 629                 queue.nsops = nsops;
 630                 queue.undo = un;
 631                 queue.pid = current->pid;
 632                 queue.status = 0;
 633                 insert_into_queue(sma,&queue);
 634                 queue.sleeper = NULL;
 635                 current->semsleeping = &queue;
 636                 interruptible_sleep_on(&queue.sleeper);
 637                 current->semsleeping = NULL;
 638                 /* When we wake up, either the operation is finished,
 639                  * or some kind of error happened.
 640                  */
 641                 if (!queue.prev) {
 642                         /* operation is finished, update_queue() removed us */
 643                         return queue.status;
 644                 } else {
 645                         remove_from_queue(sma,&queue);
 646                         return -EINTR;
 647                 }
 648         }
 649 }
 650 
 651 /*
 652  * add semadj values to semaphores, free undo structures.
 653  * undo structures are not freed when semaphore arrays are destroyed
 654  * so some of them may be out of date.
 655  * IMPLEMENTATION NOTE: There is some confusion over whether the
 656  * set of adjustments that needs to be done should be done in an atomic
 657  * manner or not. That is, if we are attempting to decrement the semval
 658  * should we queue up and wait until we can do so legally?
 659  * The original implementation attempted to do this (queue and wait).
 660  * The current implementation does not do so. The POSIX standard
 661  * and SVID should be consulted to determine what behavior is mandated.
 662  */
 663 void sem_exit (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 664 {
 665         struct sem_queue *q;
 666         struct sem_undo *u, *un = NULL, **up, **unp;
 667         struct semid_ds *sma;
 668         int nsems, i;
 669 
 670         /* If the current process was sleeping for a semaphore,
 671          * remove it from the queue.
 672          */
 673         if ((q = current->semsleeping)) {
 674                 if (q->prev)
 675                         remove_from_queue(q->sma,q);
 676                 current->semsleeping = NULL;
 677         }
 678 
 679         for (up = &current->semundo; (u = *up); *up = u->proc_next, kfree(u)) {
 680                 if (u->semid == -1)
 681                         continue;
 682                 sma = semary[(unsigned int) u->semid % SEMMNI];
 683                 if (sma == IPC_UNUSED || sma == IPC_NOID)
 684                         continue;
 685                 if (sma->sem_perm.seq != (unsigned int) u->semid / SEMMNI)
 686                         continue;
 687                 /* remove u from the sma->undo list */
 688                 for (unp = &sma->undo; (un = *unp); unp = &un->id_next) {
 689                         if (u == un)
 690                                 goto found;
 691                 }
 692                 printk ("sem_exit undo list error id=%d\n", u->semid);
 693                 break;
 694 found:
 695                 *unp = un->id_next;
 696                 /* perform adjustments registered in u */
 697                 nsems = sma->sem_nsems;
 698                 for (i = 0; i < nsems; i++) {
 699                         struct sem * sem = &sma->sem_base[i];
 700                         sem->semval += u->semadj[i];
 701                         if (sem->semval < 0)
 702                                 sem->semval = 0; /* shouldn't happen */
 703                         sem->sempid = current->pid;
 704                 }
 705                 sma->sem_otime = CURRENT_TIME;
 706                 /* maybe some queued-up processes were waiting for this */
 707                 update_queue(sma);
 708         }
 709         current->semundo = NULL;
 710 }

/* [previous][next][first][last][top][bottom][index][help] */