Ядро Linux в комментариях

       

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 = &current->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 }


Содержание раздела