ipc/sem.c
20614 /*
20615 * linux/ipc/sem.c
20616 * Copyright (C) 1992 Krishna Balasubramanian
20617 * Copyright (C) 1995 Eric Schenk, Bruno Haible
20618 *
20619 * IMPLEMENTATION NOTES ON CODE REWRITE (Eric Schenk,
20620 * January 1995): This code underwent a massive rewrite
20621 * in order to solve some problems with the original
20622 * code. In particular the original code failed to wake
20623 * up processes that were waiting for semval to go to 0
20624 * if the value went to 0 and was then incremented
20625 * rapidly enough. In solving this problem I have also
20626 * modified the implementation so that it processes
20627 * pending operations in a FIFO manner, thus give a
20628 * guarantee that processes waiting for a lock on the
20629 * semaphore won't starve unless another locking process
20630 * fails to unlock.
20631 * In addition the following two changes in behavior have
20632 * been introduced:
20633 * - The original implementation of semop returned the
20634 * value last semaphore element examined on
20635 * success. This does not match the manual page
20636 * specifications, and effectively allows the user to
20637 * read the semaphore even if they do not have read
20638 * permissions. The implementation now returns 0 on
20639 * success as stated in the manual page.
20640 * - There is some confusion over whether the set of undo
20641 * adjustments to be performed at exit should be done in
20642 * an atomic manner. That is, if we are attempting to
20643 * decrement the semval should we queue up and wait until
20644 * we can do so legally? The original implementation
20645 * attempted to do this. The current implementation does
20646 * not do so. This is because I don't think it is the
20647 * right thing (TM) to do, and because I couldn't see a
20648 * clean way to get the old behavior with the new design.
20649 * The POSIX standard and SVID should be consulted to
20650 * determine what behavior is mandated.
20651 *
20652 * Further notes on refinement (Christoph Rohland,
20653 * December 1998):
20654 * - The POSIX standard says, that the undo adjustments
20655 * simply should redo. So the current implementation is
20656 * o.K.
20657 * - The previous code had two flaws:
20658 * 1) It actively gave the semaphore to the next waiting
20659 * process sleeping on the semaphore. Since this
20660 * process did not have the cpu this led to many
20661 * unnecessary context switches and bad
20662 * performance. Now we only check which process
20663 * should be able to get the semaphore and if this
20664 * process wants to reduce some semaphore value we
20665 * simply wake it up without doing the operation. So
20666 * it has to try to get it later. Thus e.g. the
20667 * running process may reaquire the semaphore during
20668 * the current time slice. If it only waits for zero
20669 * or increases the semaphore, we do the operation
20670 * in advance and wake it up.
20671 * 2) It did not wake up all zero waiting processes. We
20672 * try to do better but only get the semops right
20673 * which only wait for zero or increase. If there
20674 * are decrement operations in the operations array
20675 * we do the same as before. */
20676
20677 #include <linux/malloc.h>
20678 #include <linux/smp_lock.h>
20679 #include <linux/init.h>
20680
20681 #include <asm/uaccess.h>
20682
20683 extern int ipcperms(struct ipc_perm *ipcp, short semflg);
20684 static int newary (key_t, int, int);
20685 static int findkey (key_t key);
20686 static void freeary (int id);
20687
20688 static struct semid_ds *semary[SEMMNI];
20689 static int used_sems = 0, used_semids = 0;
20690 static struct wait_queue *sem_lock = NULL;
20691 static int max_semid = 0;
20692
20693 static unsigned short sem_seq = 0;
20694
20695 void __init sem_init (void)
20696 {
20697 int i;
20698
20699 sem_lock = NULL;
20700 used_sems = used_semids = max_semid = sem_seq = 0;
20701 for (i = 0; i < SEMMNI; i++)
20702 semary[i] = (struct semid_ds *) IPC_UNUSED;
20703 return;
20704 }
20705
20706 static int findkey (key_t key)
20707 {
20708 int id;
20709 struct semid_ds *sma;
20710
20711 for (id = 0; id <= max_semid; id++) {
20712 while ((sma = semary[id]) == IPC_NOID)
20713 interruptible_sleep_on (&sem_lock);
20714 if (sma == IPC_UNUSED)
20715 continue;
20716 if (key == sma->sem_perm.key)
20717 return id;
20718 }
20719 return -1;
20720 }
20721
20722 static int newary (key_t key, int nsems, int semflg)
20723 {
20724 int id;
20725 struct semid_ds *sma;
20726 struct ipc_perm *ipcp;
20727 int size;
20728
20729 if (!nsems)
20730 return -EINVAL;
20731 if (used_sems + nsems > SEMMNS)
20732 return -ENOSPC;
20733 for (id = 0; id < SEMMNI; id++)
20734 if (semary[id] == IPC_UNUSED) {
20735 semary[id] = (struct semid_ds *) IPC_NOID;
20736 goto found;
20737 }
20738 return -ENOSPC;
20739 found:
20740 size = sizeof (*sma) + nsems * sizeof (struct sem);
20741 used_sems += nsems;
20742 sma = (struct semid_ds *) kmalloc (size, GFP_KERNEL);
20743 if (!sma) {
20744 semary[id] = (struct semid_ds *) IPC_UNUSED;
20745 used_sems -= nsems;
20746 wake_up (&sem_lock);
20747 return -ENOMEM;
20748 }
20749 memset (sma, 0, size);
20750 sma->sem_base = (struct sem *) &sma[1];
20751 ipcp = &sma->sem_perm;
20752 ipcp->mode = (semflg & S_IRWXUGO);
20753 ipcp->key = key;
20754 ipcp->cuid = ipcp->uid = current->euid;
20755 ipcp->gid = ipcp->cgid = current->egid;
20756 sma->sem_perm.seq = sem_seq;
20757 /* sma->sem_pending = NULL; */
20758 sma->sem_pending_last = &sma->sem_pending;
20759 /* sma->undo = NULL; */
20760 sma->sem_nsems = nsems;
20761 sma->sem_ctime = CURRENT_TIME;
20762 if (id > max_semid)
20763 max_semid = id;
20764 used_semids++;
20765 semary[id] = sma;
20766 wake_up (&sem_lock);
20767 return (unsigned int) sma->sem_perm.seq * SEMMNI + id;
20768 }
20769
20770 asmlinkage int sys_semget(key_t key,int nsems,int semflg)
20771 {
20772 int id, err = -EINVAL;
20773 struct semid_ds *sma;
20774
20775 lock_kernel();
20776 if (nsems < 0 || nsems > SEMMSL)
20777 goto out;
20778 if (key == IPC_PRIVATE) {
20779 err = newary(key, nsems, semflg);
20780 } else if ((id = findkey (key)) == -1) {
20781 /* key not used */
20782 if (!(semflg & IPC_CREAT))
20783 err = -ENOENT;
20784 else
20785 err = newary(key, nsems, semflg);
20786 } else if (semflg & IPC_CREAT && semflg & IPC_EXCL) {
20787 err = -EEXIST;
20788 } else {
20789 sma = semary[id];
20790 if (nsems > sma->sem_nsems)
20791 err = -EINVAL;
20792 else if (ipcperms(&sma->sem_perm, semflg))
20793 err = -EACCES;
20794 else
20795 err = (int) sma->sem_perm.seq * SEMMNI + id;
20796 }
20797 out:
20798 unlock_kernel();
20799 return err;
20800 }
20801
20802 /* Manage the doubly linked list sma->sem_pending as a
20803 * FIFO: insert new queue elements at the tail
20804 * sma->sem_pending_last. */
20805 static inline void append_to_queue(struct semid_ds * sma,
20806 struct sem_queue * q)
20807 {
20808 *(q->prev = sma->sem_pending_last) = q;
20809 *(sma->sem_pending_last = &q->next) = NULL;
20810 }
20811
20812 static inline void prepend_to_queue(struct semid_ds *sma,
20813 struct sem_queue * q)
20814 {
20815 q->next = sma->sem_pending;
20816 *(q->prev = &sma->sem_pending) = q;
20817 if (q->next)
20818 q->next->prev = &q->next;
20819 else /* sma->sem_pending_last == &sma->sem_pending */
20820 sma->sem_pending_last = &q->next;
20821 }
20822
20823 static inline void remove_from_queue(
20824 struct semid_ds * sma, struct sem_queue * q)
20825 {
20826 *(q->prev) = q->next;
20827 if (q->next)
20828 q->next->prev = q->prev;
20829 else /* sma->sem_pending_last == &q->next */
20830 sma->sem_pending_last = q->prev;
20831 q->prev = NULL; /* mark as removed */
20832 }
20833
20834 /* Determine whether a sequence of semaphore operations
20835 * would succeed all at once. Return 0 if yes, 1 if need
20836 * to sleep, else return error code. */
20837
20838 static int try_atomic_semop(struct semid_ds * sma,
20839 struct sembuf * sops, int nsops, struct sem_undo *un,
20840 int pid, int do_undo)
20841 {
20842 int result, sem_op;
20843 struct sembuf *sop;
20844 struct sem * curr;
20845
20846 for (sop = sops; sop < sops + nsops; sop++) {
20847 curr = sma->sem_base + sop->sem_num;
20848 sem_op = sop->sem_op;
20849
20850 if (!sem_op && curr->semval)
20851 goto would_block;
20852
20853 curr->sempid = (curr->sempid << 16) | pid;
20854 curr->semval += sem_op;
20855 if (sop->sem_flg & SEM_UNDO)
20856 un->semadj[sop->sem_num] -= sem_op;
20857
20858 if (curr->semval < 0)
20859 goto would_block;
20860 if (curr->semval > SEMVMX)
20861 goto out_of_range;
20862 }
20863
20864 if (do_undo)
20865 {
20866 sop--;
20867 result = 0;
20868 goto undo;
20869 }
20870
20871 sma->sem_otime = CURRENT_TIME;
20872 return 0;
20873
20874 out_of_range:
20875 result = -ERANGE;
20876 goto undo;
20877
20878 would_block:
20879 if (sop->sem_flg & IPC_NOWAIT)
20880 result = -EAGAIN;
20881 else
20882 result = 1;
20883
20884 undo:
20885 while (sop >= sops) {
20886 curr = sma->sem_base + sop->sem_num;
20887 curr->semval -= sop->sem_op;
20888 curr->sempid >>= 16;
20889
20890 if (sop->sem_flg & SEM_UNDO)
20891 un->semadj[sop->sem_num] += sop->sem_op;
20892 sop--;
20893 }
20894
20895 return result;
20896 }
20897
20898 /* Go through the pending queue for the indicated
20899 * semaphore looking for tasks that can be completed. */
20900 static void update_queue (struct semid_ds * sma)
20901 {
20902 int error;
20903 struct sem_queue * q;
20904
20905 for (q = sma->sem_pending; q; q = q->next) {
20906
20907 if (q->status == 1)
20908 return; /* wait for other process */
20909
20910 error = try_atomic_semop(sma, q->sops, q->nsops,
20911 q->undo, q->pid, q->alter);
20912
20913 /* Does q->sleeper still need to sleep? */
20914 if (error <= 0) {
20915 /* Found one, wake it up */
20916 wake_up_interruptible(&q->sleeper);
20917 if (error == 0 && q->alter) {
20918 /* if q-> alter let it self try */
20919 q->status = 1;
20920 return;
20921 }
20922 q->status = error;
20923 remove_from_queue(sma,q);
20924 }
20925 }
20926 }
20927
20928 /* The following counts are associated to each semaphore:
20929 * semncnt: # tasks waiting on semval being != 0.
20930 * semzcnt: # tasks waiting on semval being == 0.
20931 * This model assumes that a task waits on exactly one
20932 * semaphore. Since semaphore operations are to be
20933 * performed atomically, tasks actually wait on a whole
20934 * sequence of semaphores simultaneously. The counts we
20935 * return here are a rough approximation, but still
20936 * warrant that semncnt+semzcnt>0 if the task is on the
20937 * pending queue. */
20938 static int count_semncnt (struct semid_ds * sma,
20939 ushort semnum)
20940 {
20941 int semncnt;
20942 struct sem_queue * q;
20943
20944 semncnt = 0;
20945 for (q = sma->sem_pending; q; q = q->next) {
20946 struct sembuf * sops = q->sops;
20947 int nsops = q->nsops;
20948 int i;
20949 for (i = 0; i < nsops; i++)
20950 if (sops[i].sem_num == semnum
20951 && (sops[i].sem_op < 0)
20952 && !(sops[i].sem_flg & IPC_NOWAIT))
20953 semncnt++;
20954 }
20955 return semncnt;
20956 }
20957 static int count_semzcnt (struct semid_ds * sma,
20958 ushort semnum)
20959 {
20960 int semzcnt;
20961 struct sem_queue * q;
20962
20963 semzcnt = 0;
20964 for (q = sma->sem_pending; q; q = q->next) {
20965 struct sembuf * sops = q->sops;
20966 int nsops = q->nsops;
20967 int i;
20968 for (i = 0; i < nsops; i++)
20969 if (sops[i].sem_num == semnum
20970 && (sops[i].sem_op == 0)
20971 && !(sops[i].sem_flg & IPC_NOWAIT))
20972 semzcnt++;
20973 }
20974 return semzcnt;
20975 }
20976
20977 /* Free a semaphore set. */
20978 static void freeary (int id)
20979 {
20980 struct semid_ds *sma = semary[id];
20981 struct sem_undo *un;
20982 struct sem_queue *q;
20983
20984 /* Invalidate this semaphore set */
20985 sma->sem_perm.seq++;
20986 /* increment, but avoid overflow */
20987 sem_seq = (sem_seq+1) % ((unsigned)(1<<31)/SEMMNI);
20988 used_sems -= sma->sem_nsems;
20989 if (id == max_semid)
20990 while (max_semid &&
20991 (semary[--max_semid] == IPC_UNUSED));
20992 semary[id] = (struct semid_ds *) IPC_UNUSED;
20993 used_semids--;
20994
20995 /* Invalidate the existing undo structures for this
20996 * semaphore set. (They will be freed without any
20997 * further action in sem_exit().) */
20998 for (un = sma->undo; un; un = un->id_next)
20999 un->semid = -1;
21000
21001 /* Wake up all pending processes and let them fail with
21002 * EIDRM. */
21003 for (q = sma->sem_pending; q; q = q->next) {
21004 q->status = -EIDRM;
21005 q->prev = NULL;
21006 /* doesn't sleep! */
21007 wake_up_interruptible(&q->sleeper);
21008 }
21009
21010 kfree(sma);
21011 }
21012
21013 asmlinkage int sys_semctl(int semid, int semnum, int cmd,
21014 union semun arg)
21015 {
21016 struct semid_ds *buf = NULL;
21017 struct semid_ds tbuf;
21018 int i, id, val = 0;
21019 struct semid_ds *sma;
21020 struct ipc_perm *ipcp;
21021 struct sem *curr = NULL;
21022 struct sem_undo *un;
21023 unsigned int nsems;
21024 ushort *array = NULL;
21025 ushort sem_io[SEMMSL];
21026 int err = -EINVAL;
21027
21028 lock_kernel();
21029 if (semid < 0 || semnum < 0 || cmd < 0)
21030 goto out;
21031
21032 switch (cmd) {
21033 case IPC_INFO:
21034 case SEM_INFO:
21035 {
21036 struct seminfo seminfo, *tmp = arg.__buf;
21037 seminfo.semmni = SEMMNI;
21038 seminfo.semmns = SEMMNS;
21039 seminfo.semmsl = SEMMSL;
21040 seminfo.semopm = SEMOPM;
21041 seminfo.semvmx = SEMVMX;
21042 seminfo.semmnu = SEMMNU;
21043 seminfo.semmap = SEMMAP;
21044 seminfo.semume = SEMUME;
21045 seminfo.semusz = SEMUSZ;
21046 seminfo.semaem = SEMAEM;
21047 if (cmd == SEM_INFO) {
21048 seminfo.semusz = used_semids;
21049 seminfo.semaem = used_sems;
21050 }
21051 err = -EFAULT;
21052 if (copy_to_user(tmp, &seminfo,
21053 sizeof(struct seminfo)))
21054 goto out;
21055 err = max_semid;
21056 goto out;
21057 }
21058
21059 case SEM_STAT:
21060 buf = arg.buf;
21061 err = -EINVAL;
21062 if (semid > max_semid)
21063 goto out;
21064 sma = semary[semid];
21065 if (sma == IPC_UNUSED || sma == IPC_NOID)
21066 goto out;
21067 err = -EACCES;
21068 if (ipcperms (&sma->sem_perm, S_IRUGO))
21069 goto out;
21070 id = (unsigned int) sma->sem_perm.seq*SEMMNI + semid;
21071 tbuf.sem_perm = sma->sem_perm;
21072 tbuf.sem_otime = sma->sem_otime;
21073 tbuf.sem_ctime = sma->sem_ctime;
21074 tbuf.sem_nsems = sma->sem_nsems;
21075 err = -EFAULT;
21076 if (copy_to_user (buf, &tbuf, sizeof(*buf)) == 0)
21077 err = id;
21078 goto out;
21079 }
21080
21081 id = (unsigned int) semid % SEMMNI;
21082 sma = semary [id];
21083 err = -EINVAL;
21084 if (sma == IPC_UNUSED || sma == IPC_NOID)
21085 goto out;
21086 ipcp = &sma->sem_perm;
21087 nsems = sma->sem_nsems;
21088 err = -EIDRM;
21089 if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI)
21090 goto out;
21091
21092 switch (cmd) {
21093 case GETVAL:
21094 case GETPID:
21095 case GETNCNT:
21096 case GETZCNT:
21097 case SETVAL:
21098 err = -EINVAL;
21099 if (semnum >= nsems)
21100 goto out;
21101 curr = &sma->sem_base[semnum];
21102 break;
21103 }
21104
21105 switch (cmd) {
21106 case GETVAL:
21107 case GETPID:
21108 case GETNCNT:
21109 case GETZCNT:
21110 case GETALL:
21111 err = -EACCES;
21112 if (ipcperms (ipcp, S_IRUGO))
21113 goto out;
21114 switch (cmd) {
21115 case GETVAL : err = curr->semval; goto out;
21116 case GETPID : err = curr->sempid & 0xffff; goto out;
21117 case GETNCNT: err = count_semncnt(sma,semnum);
21118 goto out;
21119 case GETZCNT: err = count_semzcnt(sma,semnum);
21120 goto out;
21121 case GETALL:
21122 array = arg.array;
21123 break;
21124 }
21125 break;
21126 case SETVAL:
21127 val = arg.val;
21128 err = -ERANGE;
21129 if (val > SEMVMX || val < 0)
21130 goto out;
21131 break;
21132 case IPC_RMID:
21133 if (current->euid == ipcp->cuid ||
21134 current->euid == ipcp->uid ||
21135 capable(CAP_SYS_ADMIN)) {
21136 freeary (id);
21137 err = 0;
21138 goto out;
21139 }
21140 err = -EPERM;
21141 goto out;
21142 case SETALL: /* arg is a pTR to an array of ushort */
21143 array = arg.array;
21144 err = -EFAULT;
21145 if (copy_from_user(sem_io, array,
21146 nsems * sizeof(ushort)))
21147 goto out;
21148 err = 0;
21149 for (i = 0; i < nsems; i++)
21150 if (sem_io[i] > SEMVMX) {
21151 err = -ERANGE;
21152 goto out;
21153 }
21154 break;
21155 case IPC_STAT:
21156 buf = arg.buf;
21157 break;
21158 case IPC_SET:
21159 buf = arg.buf;
21160 err = copy_from_user (&tbuf, buf, sizeof (*buf));
21161 if (err)
21162 err = -EFAULT;
21163 break;
21164 }
21165
21166 err = -EIDRM;
21167 if (semary[id] == IPC_UNUSED || semary[id] == IPC_NOID)
21168 goto out;
21169 if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI)
21170 goto out;
21171
21172 switch (cmd) {
21173 case GETALL:
21174 err = -EACCES;
21175 if (ipcperms (ipcp, S_IRUGO))
21176 goto out;
21177 for (i = 0; i < sma->sem_nsems; i++)
21178 sem_io[i] = sma->sem_base[i].semval;
21179 if (copy_to_user(array, sem_io,
21180 nsems * sizeof(ushort)))
21181 err = -EFAULT;
21182 break;
21183 case SETVAL:
21184 err = -EACCES;
21185 if (ipcperms (ipcp, S_IWUGO))
21186 goto out;
21187 for (un = sma->undo; un; un = un->id_next)
21188 un->semadj[semnum] = 0;
21189 curr->semval = val;
21190 sma->sem_ctime = CURRENT_TIME;
21191 /* maybe some queued-up processes were waiting for
21192 * this */
21193 update_queue(sma);
21194 break;
21195 case IPC_SET:
21196 if (current->euid == ipcp->cuid ||
21197 current->euid == ipcp->uid ||
21198 capable(CAP_SYS_ADMIN)) {
21199 ipcp->uid = tbuf.sem_perm.uid;
21200 ipcp->gid = tbuf.sem_perm.gid;
21201 ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
21202 | (tbuf.sem_perm.mode & S_IRWXUGO);
21203 sma->sem_ctime = CURRENT_TIME;
21204 err = 0;
21205 goto out;
21206 }
21207 err = -EPERM;
21208 goto out;
21209 case IPC_STAT:
21210 err = -EACCES;
21211 if (ipcperms (ipcp, S_IRUGO))
21212 goto out;
21213 tbuf.sem_perm = sma->sem_perm;
21214 tbuf.sem_otime = sma->sem_otime;
21215 tbuf.sem_ctime = sma->sem_ctime;
21216 tbuf.sem_nsems = sma->sem_nsems;
21217 if (copy_to_user (buf, &tbuf, sizeof(*buf)))
21218 err = -EFAULT;
21219 break;
21220 case SETALL:
21221 err = -EACCES;
21222 if (ipcperms (ipcp, S_IWUGO))
21223 goto out;
21224 for (i = 0; i < nsems; i++)
21225 sma->sem_base[i].semval = sem_io[i];
21226 for (un = sma->undo; un; un = un->id_next)
21227 for (i = 0; i < nsems; i++)
21228 un->semadj[i] = 0;
21229 sma->sem_ctime = CURRENT_TIME;
21230 /* maybe some queued-up processes were waiting for
21231 * this */
21232 update_queue(sma);
21233 break;
21234 default:
21235 err = -EINVAL;
21236 goto out;
21237 }
21238 err = 0;
21239 out:
21240 unlock_kernel();
21241 return err;
21242 }
21243
21244 asmlinkage int sys_semop(int semid, struct sembuf *tsops,
21245 unsigned nsops)
21246 {
21247 int id, size, error = -EINVAL;
21248 struct semid_ds *sma;
21249 struct sembuf sops[SEMOPM], *sop;
21250 struct sem_undo *un;
21251 int undos = 0, decrease = 0, alter = 0;
21252 struct sem_queue queue;
21253
21254 lock_kernel();
21255 if (nsops < 1 || semid < 0)
21256 goto out;
21257 error = -E2BIG;
21258 if (nsops > SEMOPM)
21259 goto out;
21260 error = -EFAULT;
21261 if(copy_from_user(sops, tsops, nsops * sizeof(*tsops)))
21262 goto out;
21263 id = (unsigned int) semid % SEMMNI;
21264 error = -EINVAL;
21265 if((sma = semary[id]) == IPC_UNUSED || sma == IPC_NOID)
21266 goto out;
21267 error = -EIDRM;
21268 if (sma->sem_perm.seq != (unsigned int) semid / SEMMNI)
21269 goto out;
21270
21271 error = -EFBIG;
21272 for (sop = sops; sop < sops + nsops; sop++) {
21273 if (sop->sem_num >= sma->sem_nsems)
21274 goto out;
21275 if (sop->sem_flg & SEM_UNDO)
21276 undos++;
21277 if (sop->sem_op < 0)
21278 decrease = 1;
21279 if (sop->sem_op > 0)
21280 alter = 1;
21281 }
21282 alter |= decrease;
21283
21284 error = -EACCES;
21285 if(ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
21286 goto out;
21287 if (undos) {
21288 /* Make sure we have an undo structure
21289 * for this process and this semaphore set.
21290 */
21291 for (un = current->semundo; un; un = un->proc_next)
21292 if (un->semid == semid)
21293 break;
21294 if (!un) {
21295 size = sizeof(struct sem_undo) +
21296 sizeof(short) * sma->sem_nsems;
21297 un = (struct sem_undo *) kmalloc(size, GFP_ATOMIC);
21298 if (!un) {
21299 error = -ENOMEM;
21300 goto out;
21301 }
21302 memset(un, 0, size);
21303 un->semadj = (short *) &un[1];
21304 un->semid = semid;
21305 un->proc_next = current->semundo;
21306 current->semundo = un;
21307 un->id_next = sma->undo;
21308 sma->undo = un;
21309 }
21310 } else
21311 un = NULL;
21312
21313 error = try_atomic_semop(sma, sops, nsops, un,
21314 current->pid, 0);
21315 if (error <= 0)
21316 goto update;
21317
21318 /* We need to sleep on this operation, so we put the
21319 * current task into the pending queue and go to sleep.
21320 */
21321 queue.sma = sma;
21322 queue.sops = sops;
21323 queue.nsops = nsops;
21324 queue.undo = un;
21325 queue.pid = current->pid;
21326 queue.alter = decrease;
21327 current->semsleeping = &queue;
21328 if (alter)
21329 append_to_queue(sma ,&queue);
21330 else
21331 prepend_to_queue(sma ,&queue);
21332
21333 for (;;) {
21334 queue.status = -EINTR;
21335 queue.sleeper = NULL;
21336 interruptible_sleep_on(&queue.sleeper);
21337
21338 /* If queue.status == 1 we where woken up and have to
21339 * retry else we simply return. If an interrupt
21340 * occurred we have to clean up the queue
21341 */
21342 if (queue.status == 1)
21343 {
21344 error = try_atomic_semop (sma, sops, nsops, un,
21345 current->pid,0);
21346 if (error <= 0)
21347 break;
21348 } else {
21349 error = queue.status;;
21350 if (queue.prev) /* got Interrupt */
21351 break;
21352 /* Everything done by update_queue */
21353 current->semsleeping = NULL;
21354 goto out;
21355 }
21356 }
21357 current->semsleeping = NULL;
21358 remove_from_queue(sma,&queue);
21359 update:
21360 if (alter)
21361 update_queue (sma);
21362 out:
21363 unlock_kernel();
21364 return error;
21365 }
21366
21367 /* add semadj values to semaphores, free undo structures.
21368 * undo structures are not freed when semaphore arrays
21369 * are destroyed so some of them may be out of date.
21370 * IMPLEMENTATION NOTE: There is some confusion over
21371 * whether the set of adjustments that needs to be done
21372 * should be done in an atomic manner or not. That is, if
21373 * we are attempting to decrement the semval should we
21374 * queue up and wait until we can do so legally? The
21375 * original implementation attempted to do this (queue
21376 * and wait). The current implementation does not do
21377 * so. The POSIX standard and SVID should be consulted to
21378 * determine what behavior is mandated. */
21379 void sem_exit (void)
21380 {
21381 struct sem_queue *q;
21382 struct sem_undo *u, *un = NULL, **up, **unp;
21383 struct semid_ds *sma;
21384 int nsems, i;
21385
21386 /* If the current process was sleeping for a semaphore,
21387 * remove it from the queue.
21388 */
21389 if ((q = current->semsleeping)) {
21390 if (q->prev)
21391 remove_from_queue(q->sma,q);
21392 current->semsleeping = NULL;
21393 }
21394
21395 for (up = ¤t->semundo; (u = *up);
21396 *up = u->proc_next, kfree(u)) {
21397 if (u->semid == -1)
21398 continue;
21399 sma = semary[(unsigned int) u->semid % SEMMNI];
21400 if (sma == IPC_UNUSED || sma == IPC_NOID)
21401 continue;
21402 if (sma->sem_perm.seq !=
21403 (unsigned int) u->semid / SEMMNI)
21404 continue;
21405 /* remove u from the sma->undo list */
21406 for (unp = &sma->undo; (un = *unp);
21407 unp = &un->id_next) {
21408 if (u == un)
21409 goto found;
21410 }
21411 printk("sem_exit undo list error id=%d\n", u->semid);
21412 break;
21413 found:
21414 *unp = un->id_next;
21415 /* perform adjustments registered in u */
21416 nsems = sma->sem_nsems;
21417 for (i = 0; i < nsems; i++) {
21418 struct sem * sem = &sma->sem_base[i];
21419 sem->semval += u->semadj[i];
21420 if (sem->semval < 0)
21421 sem->semval = 0; /* shouldn't happen */
21422 sem->sempid = current->pid;
21423 }
21424 sma->sem_otime = CURRENT_TIME;
21425 /* maybe some queued-up processes were waiting for
21426 * this */
21427 update_queue(sma);
21428 }
21429 current->semundo = NULL;
21430 }
Сайт управляется системой
uCoz