netlib.narod.ru< Назад | Оглавление | Далее >

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 }

netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz