home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.2 / LINUX-1.2 / LINUX-1 / linux / ipc / sem.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-06  |  19.0 KB  |  711 lines

  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)
  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)
  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)
  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. int sys_semget (key_t key, int nsems, int semflg)
  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)
  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)
  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)
  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,
  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)
  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)
  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)
  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)
  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. int sys_semctl (int semid, int semnum, int cmd, union semun arg)
  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. int sys_semop (int semid, struct sembuf *tsops, unsigned nsops)
  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)
  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 = ¤t->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. }
  711.