~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/ipc/sem.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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  * Further notes on refinement (Christoph Rohland, December 1998):
 34  * - The POSIX standard says, that the undo adjustments simply should
 35  *   redo. So the current implementation is o.K.
 36  * - The previous code had two flaws:
 37  *   1) It actively gave the semaphore to the next waiting process
 38  *      sleeping on the semaphore. Since this process did not have the
 39  *      cpu this led to many unnecessary context switches and bad
 40  *      performance. Now we only check which process should be able to
 41  *      get the semaphore and if this process wants to reduce some
 42  *      semaphore value we simply wake it up without doing the
 43  *      operation. So it has to try to get it later. Thus e.g. the
 44  *      running process may reacquire the semaphore during the current
 45  *      time slice. If it only waits for zero or increases the semaphore,
 46  *      we do the operation in advance and wake it up.
 47  *   2) It did not wake up all zero waiting processes. We try to do
 48  *      better but only get the semops right which only wait for zero or
 49  *      increase. If there are decrement operations in the operations
 50  *      array we do the same as before.
 51  *
 52  * /proc/sysvipc/sem support (c) 1999 Dragos Acostachioaie <dragos@iname.com>
 53  *
 54  * SMP-threaded, sysctl's added
 55  * (c) 1999 Manfred Spraul <manfreds@colorfullife.com>
 56  */
 57 
 58 #include <linux/config.h>
 59 #include <linux/malloc.h>
 60 #include <linux/spinlock.h>
 61 #include <linux/init.h>
 62 #include <linux/proc_fs.h>
 63 #include <asm/uaccess.h>
 64 #include "util.h"
 65 
 66 
 67 #define sem_lock(id)    ((struct sem_array*)ipc_lock(&sem_ids,id))
 68 #define sem_unlock(id)  ipc_unlock(&sem_ids,id)
 69 #define sem_rmid(id)    ((struct sem_array*)ipc_rmid(&sem_ids,id))
 70 #define sem_checkid(sma, semid) \
 71         ipc_checkid(&sem_ids,&sma->sem_perm,semid)
 72 #define sem_buildid(id, seq) \
 73         ipc_buildid(&sem_ids, id, seq)
 74 static struct ipc_ids sem_ids;
 75 
 76 static int newary (key_t, int, int);
 77 static void freeary (int id);
 78 #ifdef CONFIG_PROC_FS
 79 static int sysvipc_sem_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);
 80 #endif
 81 
 82 #define SEMMSL_FAST     256 /* 512 bytes on stack */
 83 #define SEMOPM_FAST     64  /* ~ 372 bytes on stack */
 84 
 85 /*
 86  * linked list protection:
 87  *      sem_undo.id_next,
 88  *      sem_array.sem_pending{,last},
 89  *      sem_array.sem_undo: sem_lock() for read/write
 90  *      sem_undo.proc_next: only "current" is allowed to read/write that field.
 91  *      
 92  */
 93 
 94 int sem_ctls[4] = {SEMMSL, SEMMNS, SEMOPM, SEMMNI};
 95 #define sc_semmsl       (sem_ctls[0])
 96 #define sc_semmns       (sem_ctls[1])
 97 #define sc_semopm       (sem_ctls[2])
 98 #define sc_semmni       (sem_ctls[3])
 99 
100 static int used_sems;
101 
102 void __init sem_init (void)
103 {
104         used_sems = 0;
105         ipc_init_ids(&sem_ids,sc_semmni);
106 
107 #ifdef CONFIG_PROC_FS
108         create_proc_read_entry("sysvipc/sem", 0, 0, sysvipc_sem_read_proc, NULL);
109 #endif
110 }
111 
112 static int newary (key_t key, int nsems, int semflg)
113 {
114         int id;
115         struct sem_array *sma;
116         int size;
117 
118         if (!nsems)
119                 return -EINVAL;
120         if (used_sems + nsems > sc_semmns)
121                 return -ENOSPC;
122 
123         size = sizeof (*sma) + nsems * sizeof (struct sem);
124         sma = (struct sem_array *) ipc_alloc(size);
125         if (!sma) {
126                 return -ENOMEM;
127         }
128         memset (sma, 0, size);
129         id = ipc_addid(&sem_ids, &sma->sem_perm, sc_semmni);
130         if(id == -1) {
131                 ipc_free(sma, size);
132                 return -ENOSPC;
133         }
134         used_sems += nsems;
135 
136         sma->sem_perm.mode = (semflg & S_IRWXUGO);
137         sma->sem_perm.key = key;
138 
139         sma->sem_base = (struct sem *) &sma[1];
140         /* sma->sem_pending = NULL; */
141         sma->sem_pending_last = &sma->sem_pending;
142         /* sma->undo = NULL; */
143         sma->sem_nsems = nsems;
144         sma->sem_ctime = CURRENT_TIME;
145         sem_unlock(id);
146 
147         return sem_buildid(id, sma->sem_perm.seq);
148 }
149 
150 asmlinkage long sys_semget (key_t key, int nsems, int semflg)
151 {
152         int id, err = -EINVAL;
153         struct sem_array *sma;
154 
155         if (nsems < 0 || nsems > sc_semmsl)
156                 return -EINVAL;
157         down(&sem_ids.sem);
158         
159         if (key == IPC_PRIVATE) {
160                 err = newary(key, nsems, semflg);
161         } else if ((id = ipc_findkey(&sem_ids, key)) == -1) {  /* key not used */
162                 if (!(semflg & IPC_CREAT))
163                         err = -ENOENT;
164                 else
165                         err = newary(key, nsems, semflg);
166         } else if (semflg & IPC_CREAT && semflg & IPC_EXCL) {
167                 err = -EEXIST;
168         } else {
169                 sma = sem_lock(id);
170                 if(sma==NULL)
171                         BUG();
172                 if (nsems > sma->sem_nsems)
173                         err = -EINVAL;
174                 else if (ipcperms(&sma->sem_perm, semflg))
175                         err = -EACCES;
176                 else
177                         err = sem_buildid(id, sma->sem_perm.seq);
178                 sem_unlock(id);
179         }
180 
181         up(&sem_ids.sem);
182         return err;
183 }
184 
185 /* doesn't acquire the sem_lock on error! */
186 static int sem_revalidate(int semid, struct sem_array* sma, int nsems, short flg)
187 {
188         struct sem_array* smanew;
189 
190         smanew = sem_lock(semid);
191         if(smanew==NULL)
192                 return -EIDRM;
193         if(smanew != sma || sem_checkid(sma,semid) || sma->sem_nsems != nsems) {
194                 sem_unlock(semid);
195                 return -EIDRM;
196         }
197 
198         if (ipcperms(&sma->sem_perm, flg)) {
199                 sem_unlock(semid);
200                 return -EACCES;
201         }
202         return 0;
203 }
204 /* Manage the doubly linked list sma->sem_pending as a FIFO:
205  * insert new queue elements at the tail sma->sem_pending_last.
206  */
207 static inline void append_to_queue (struct sem_array * sma,
208                                     struct sem_queue * q)
209 {
210         *(q->prev = sma->sem_pending_last) = q;
211         *(sma->sem_pending_last = &q->next) = NULL;
212 }
213 
214 static inline void prepend_to_queue (struct sem_array * sma,
215                                      struct sem_queue * q)
216 {
217         q->next = sma->sem_pending;
218         *(q->prev = &sma->sem_pending) = q;
219         if (q->next)
220                 q->next->prev = &q->next;
221         else /* sma->sem_pending_last == &sma->sem_pending */
222                 sma->sem_pending_last = &q->next;
223 }
224 
225 static inline void remove_from_queue (struct sem_array * sma,
226                                       struct sem_queue * q)
227 {
228         *(q->prev) = q->next;
229         if (q->next)
230                 q->next->prev = q->prev;
231         else /* sma->sem_pending_last == &q->next */
232                 sma->sem_pending_last = q->prev;
233         q->prev = NULL; /* mark as removed */
234 }
235 
236 /*
237  * Determine whether a sequence of semaphore operations would succeed
238  * all at once. Return 0 if yes, 1 if need to sleep, else return error code.
239  */
240 
241 static int try_atomic_semop (struct sem_array * sma, struct sembuf * sops,
242                              int nsops, struct sem_undo *un, int pid,
243                              int do_undo)
244 {
245         int result, sem_op;
246         struct sembuf *sop;
247         struct sem * curr;
248 
249         for (sop = sops; sop < sops + nsops; sop++) {
250                 curr = sma->sem_base + sop->sem_num;
251                 sem_op = sop->sem_op;
252 
253                 if (!sem_op && curr->semval)
254                         goto would_block;
255 
256                 curr->sempid = (curr->sempid << 16) | pid;
257                 curr->semval += sem_op;
258                 if (sop->sem_flg & SEM_UNDO)
259                         un->semadj[sop->sem_num] -= sem_op;
260 
261                 if (curr->semval < 0)
262                         goto would_block;
263                 if (curr->semval > SEMVMX)
264                         goto out_of_range;
265         }
266 
267         if (do_undo)
268         {
269                 sop--;
270                 result = 0;
271                 goto undo;
272         }
273 
274         sma->sem_otime = CURRENT_TIME;
275         return 0;
276 
277 out_of_range:
278         result = -ERANGE;
279         goto undo;
280 
281 would_block:
282         if (sop->sem_flg & IPC_NOWAIT)
283                 result = -EAGAIN;
284         else
285                 result = 1;
286 
287 undo:
288         while (sop >= sops) {
289                 curr = sma->sem_base + sop->sem_num;
290                 curr->semval -= sop->sem_op;
291                 curr->sempid >>= 16;
292 
293                 if (sop->sem_flg & SEM_UNDO)
294                         un->semadj[sop->sem_num] += sop->sem_op;
295                 sop--;
296         }
297 
298         return result;
299 }
300 
301 /* Go through the pending queue for the indicated semaphore
302  * looking for tasks that can be completed.
303  */
304 static void update_queue (struct sem_array * sma)
305 {
306         int error;
307         struct sem_queue * q;
308 
309         for (q = sma->sem_pending; q; q = q->next) {
310                         
311                 if (q->status == 1)
312                         continue;       /* this one was woken up before */
313 
314                 error = try_atomic_semop(sma, q->sops, q->nsops,
315                                          q->undo, q->pid, q->alter);
316 
317                 /* Does q->sleeper still need to sleep? */
318                 if (error <= 0) {
319                                 /* Found one, wake it up */
320                         wake_up_process(q->sleeper);
321                         if (error == 0 && q->alter) {
322                                 /* if q-> alter let it self try */
323                                 q->status = 1;
324                                 return;
325                         }
326                         q->status = error;
327                         remove_from_queue(sma,q);
328                 }
329         }
330 }
331 
332 /* The following counts are associated to each semaphore:
333  *   semncnt        number of tasks waiting on semval being nonzero
334  *   semzcnt        number of tasks waiting on semval being zero
335  * This model assumes that a task waits on exactly one semaphore.
336  * Since semaphore operations are to be performed atomically, tasks actually
337  * wait on a whole sequence of semaphores simultaneously.
338  * The counts we return here are a rough approximation, but still
339  * warrant that semncnt+semzcnt>0 if the task is on the pending queue.
340  */
341 static int count_semncnt (struct sem_array * sma, ushort semnum)
342 {
343         int semncnt;
344         struct sem_queue * q;
345 
346         semncnt = 0;
347         for (q = sma->sem_pending; q; q = q->next) {
348                 struct sembuf * sops = q->sops;
349                 int nsops = q->nsops;
350                 int i;
351                 for (i = 0; i < nsops; i++)
352                         if (sops[i].sem_num == semnum
353                             && (sops[i].sem_op < 0)
354                             && !(sops[i].sem_flg & IPC_NOWAIT))
355                                 semncnt++;
356         }
357         return semncnt;
358 }
359 static int count_semzcnt (struct sem_array * sma, ushort semnum)
360 {
361         int semzcnt;
362         struct sem_queue * q;
363 
364         semzcnt = 0;
365         for (q = sma->sem_pending; q; q = q->next) {
366                 struct sembuf * sops = q->sops;
367                 int nsops = q->nsops;
368                 int i;
369                 for (i = 0; i < nsops; i++)
370                         if (sops[i].sem_num == semnum
371                             && (sops[i].sem_op == 0)
372                             && !(sops[i].sem_flg & IPC_NOWAIT))
373                                 semzcnt++;
374         }
375         return semzcnt;
376 }
377 
378 /* Free a semaphore set. */
379 static void freeary (int id)
380 {
381         struct sem_array *sma;
382         struct sem_undo *un;
383         struct sem_queue *q;
384         int size;
385 
386         sma = sem_rmid(id);
387 
388         /* Invalidate the existing undo structures for this semaphore set.
389          * (They will be freed without any further action in sem_exit()
390          * or during the next semop.)
391          */
392         for (un = sma->undo; un; un = un->id_next)
393                 un->semid = -1;
394 
395         /* Wake up all pending processes and let them fail with EIDRM. */
396         for (q = sma->sem_pending; q; q = q->next) {
397                 q->status = -EIDRM;
398                 q->prev = NULL;
399                 wake_up_process(q->sleeper); /* doesn't sleep */
400         }
401         sem_unlock(id);
402 
403         used_sems -= sma->sem_nsems;
404         size = sizeof (*sma) + sma->sem_nsems * sizeof (struct sem);
405         ipc_free(sma, size);
406 }
407 
408 static unsigned long copy_semid_to_user(void *buf, struct semid64_ds *in, int version)
409 {
410         switch(version) {
411         case IPC_64:
412                 return copy_to_user(buf, in, sizeof(*in));
413         case IPC_OLD:
414             {
415                 struct semid_ds out;
416 
417                 ipc64_perm_to_ipc_perm(&in->sem_perm, &out.sem_perm);
418 
419                 out.sem_otime   = in->sem_otime;
420                 out.sem_ctime   = in->sem_ctime;
421                 out.sem_nsems   = in->sem_nsems;
422 
423                 return copy_to_user(buf, &out, sizeof(out));
424             }
425         default:
426                 return -EINVAL;
427         }
428 }
429 
430 int semctl_nolock(int semid, int semnum, int cmd, int version, union semun arg)
431 {
432         int err = -EINVAL;
433 
434         switch(cmd) {
435         case IPC_INFO:
436         case SEM_INFO:
437         {
438                 struct seminfo seminfo;
439                 int max_id;
440 
441                 memset(&seminfo,0,sizeof(seminfo));
442                 seminfo.semmni = sc_semmni;
443                 seminfo.semmns = sc_semmns;
444                 seminfo.semmsl = sc_semmsl;
445                 seminfo.semopm = sc_semopm;
446                 seminfo.semvmx = SEMVMX;
447                 seminfo.semmnu = SEMMNU;
448                 seminfo.semmap = SEMMAP;
449                 seminfo.semume = SEMUME;
450                 down(&sem_ids.sem);
451                 if (cmd == SEM_INFO) {
452                         seminfo.semusz = sem_ids.in_use;
453                         seminfo.semaem = used_sems;
454                 } else {
455                         seminfo.semusz = SEMUSZ;
456                         seminfo.semaem = SEMAEM;
457                 }
458                 max_id = sem_ids.max_id;
459                 up(&sem_ids.sem);
460                 if (copy_to_user (arg.__buf, &seminfo, sizeof(struct seminfo))) 
461                         return -EFAULT;
462                 return (max_id < 0) ? 0: max_id;
463         }
464         case SEM_STAT:
465         {
466                 struct sem_array *sma;
467                 struct semid64_ds tbuf;
468                 int id;
469 
470                 if(semid > sem_ids.size)
471                         return -EINVAL;
472 
473                 memset(&tbuf,0,sizeof(tbuf));
474 
475                 sma = sem_lock(semid);
476                 if(sma == NULL)
477                         return -EINVAL;
478 
479                 err = -EACCES;
480                 if (ipcperms (&sma->sem_perm, S_IRUGO))
481                         goto out_unlock;
482                 id = sem_buildid(semid, sma->sem_perm.seq);
483 
484                 kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
485                 tbuf.sem_otime  = sma->sem_otime;
486                 tbuf.sem_ctime  = sma->sem_ctime;
487                 tbuf.sem_nsems  = sma->sem_nsems;
488                 sem_unlock(semid);
489                 if (copy_semid_to_user (arg.buf, &tbuf, version))
490                         return -EFAULT;
491                 return id;
492         }
493         default:
494                 return -EINVAL;
495         }
496         return err;
497 out_unlock:
498         sem_unlock(semid);
499         return err;
500 }
501 
502 int semctl_main(int semid, int semnum, int cmd, int version, union semun arg)
503 {
504         struct sem_array *sma;
505         struct sem* curr;
506         int err;
507         ushort fast_sem_io[SEMMSL_FAST];
508         ushort* sem_io = fast_sem_io;
509         int nsems;
510 
511         sma = sem_lock(semid);
512         if(sma==NULL)
513                 return -EINVAL;
514 
515         nsems = sma->sem_nsems;
516 
517         err=-EIDRM;
518         if (sem_checkid(sma,semid))
519                 goto out_unlock;
520 
521         err = -EACCES;
522         if (ipcperms (&sma->sem_perm, (cmd==SETVAL||cmd==SETALL)?S_IWUGO:S_IRUGO))
523                 goto out_unlock;
524 
525         switch (cmd) {
526         case GETALL:
527         {
528                 ushort *array = arg.array;
529                 int i;
530 
531                 if(nsems > SEMMSL_FAST) {
532                         sem_unlock(semid);                      
533                         sem_io = ipc_alloc(sizeof(ushort)*nsems);
534                         if(sem_io == NULL)
535                                 return -ENOMEM;
536                         err = sem_revalidate(semid, sma, nsems, S_IRUGO);
537                         if(err)
538                                 goto out_free;
539                 }
540 
541                 for (i = 0; i < sma->sem_nsems; i++)
542                         sem_io[i] = sma->sem_base[i].semval;
543                 sem_unlock(semid);
544                 err = 0;
545                 if(copy_to_user(array, sem_io, nsems*sizeof(ushort)))
546                         err = -EFAULT;
547                 goto out_free;
548         }
549         case SETALL:
550         {
551                 int i;
552                 struct sem_undo *un;
553 
554                 sem_unlock(semid);
555 
556                 if(nsems > SEMMSL_FAST) {
557                         sem_io = ipc_alloc(sizeof(ushort)*nsems);
558                         if(sem_io == NULL)
559                                 return -ENOMEM;
560                 }
561 
562                 if (copy_from_user (sem_io, arg.array, nsems*sizeof(ushort))) {
563                         err = -EFAULT;
564                         goto out_free;
565                 }
566 
567                 for (i = 0; i < nsems; i++) {
568                         if (sem_io[i] > SEMVMX) {
569                                 err = -ERANGE;
570                                 goto out_free;
571                         }
572                 }
573                 err = sem_revalidate(semid, sma, nsems, S_IWUGO);
574                 if(err)
575                         goto out_free;
576 
577                 for (i = 0; i < nsems; i++)
578                         sma->sem_base[i].semval = sem_io[i];
579                 for (un = sma->undo; un; un = un->id_next)
580                         for (i = 0; i < nsems; i++)
581                                 un->semadj[i] = 0;
582                 sma->sem_ctime = CURRENT_TIME;
583                 /* maybe some queued-up processes were waiting for this */
584                 update_queue(sma);
585                 err = 0;
586                 goto out_unlock;
587         }
588         case IPC_STAT:
589         {
590                 struct semid64_ds tbuf;
591                 memset(&tbuf,0,sizeof(tbuf));
592                 kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm);
593                 tbuf.sem_otime  = sma->sem_otime;
594                 tbuf.sem_ctime  = sma->sem_ctime;
595                 tbuf.sem_nsems  = sma->sem_nsems;
596                 sem_unlock(semid);
597                 if (copy_semid_to_user (arg.buf, &tbuf, version))
598                         return -EFAULT;
599                 return 0;
600         }
601         /* GETVAL, GETPID, GETNCTN, GETZCNT, SETVAL: fall-through */
602         }
603         err = -EINVAL;
604         if(semnum < 0 || semnum >= nsems)
605                 goto out_unlock;
606 
607         curr = &sma->sem_base[semnum];
608 
609         switch (cmd) {
610         case GETVAL:
611                 err = curr->semval;
612                 goto out_unlock;
613         case GETPID:
614                 err = curr->sempid & 0xffff;
615                 goto out_unlock;
616         case GETNCNT:
617                 err = count_semncnt(sma,semnum);
618                 goto out_unlock;
619         case GETZCNT:
620                 err = count_semzcnt(sma,semnum);
621                 goto out_unlock;
622         case SETVAL:
623         {
624                 int val = arg.val;
625                 struct sem_undo *un;
626                 err = -ERANGE;
627                 if (val > SEMVMX || val < 0)
628                         goto out_unlock;
629 
630                 for (un = sma->undo; un; un = un->id_next)
631                         un->semadj[semnum] = 0;
632                 curr->semval = val;
633                 sma->sem_ctime = CURRENT_TIME;
634                 /* maybe some queued-up processes were waiting for this */
635                 update_queue(sma);
636                 err = 0;
637                 goto out_unlock;
638         }
639         }
640 out_unlock:
641         sem_unlock(semid);
642 out_free:
643         if(sem_io != fast_sem_io)
644                 ipc_free(sem_io, sizeof(ushort)*nsems);
645         return err;
646 }
647 
648 struct sem_setbuf {
649         uid_t   uid;
650         gid_t   gid;
651         mode_t  mode;
652 };
653 
654 static inline unsigned long copy_semid_from_user(struct sem_setbuf *out, void *buf, int version)
655 {
656         switch(version) {
657         case IPC_64:
658             {
659                 struct semid64_ds tbuf;
660 
661                 if(copy_from_user(&tbuf, buf, sizeof(tbuf)))
662                         return -EFAULT;
663 
664                 out->uid        = tbuf.sem_perm.uid;
665                 out->gid        = tbuf.sem_perm.gid;
666                 out->mode       = tbuf.sem_perm.mode;
667 
668                 return 0;
669             }
670         case IPC_OLD:
671             {
672                 struct semid_ds tbuf_old;
673 
674                 if(copy_from_user(&tbuf_old, buf, sizeof(tbuf_old)))
675                         return -EFAULT;
676 
677                 out->uid        = tbuf_old.sem_perm.uid;
678                 out->gid        = tbuf_old.sem_perm.gid;
679                 out->mode       = tbuf_old.sem_perm.mode;
680 
681                 return 0;
682             }
683         default:
684                 return -EINVAL;
685         }
686 }
687 
688 int semctl_down(int semid, int semnum, int cmd, int version, union semun arg)
689 {
690         struct sem_array *sma;
691         int err;
692         struct sem_setbuf setbuf;
693         struct kern_ipc_perm *ipcp;
694 
695         if(cmd == IPC_SET) {
696                 if(copy_semid_from_user (&setbuf, arg.buf, version))
697                         return -EFAULT;
698         }
699         sma = sem_lock(semid);
700         if(sma==NULL)
701                 return -EINVAL;
702 
703         if (sem_checkid(sma,semid)) {
704                 err=-EIDRM;
705                 goto out_unlock;
706         }       
707         ipcp = &sma->sem_perm;
708         
709         if (current->euid != ipcp->cuid && 
710             current->euid != ipcp->uid && !capable(CAP_SYS_ADMIN)) {
711                 err=-EPERM;
712                 goto out_unlock;
713         }
714 
715         switch(cmd){
716         case IPC_RMID:
717                 freeary(semid);
718                 err = 0;
719                 break;
720         case IPC_SET:
721                 ipcp->uid = setbuf.uid;
722                 ipcp->gid = setbuf.gid;
723                 ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
724                                 | (setbuf.mode & S_IRWXUGO);
725                 sma->sem_ctime = CURRENT_TIME;
726                 sem_unlock(semid);
727                 err = 0;
728                 break;
729         default:
730                 sem_unlock(semid);
731                 err = -EINVAL;
732                 break;
733         }
734         return err;
735 
736 out_unlock:
737         sem_unlock(semid);
738         return err;
739 }
740 
741 asmlinkage long sys_semctl (int semid, int semnum, int cmd, union semun arg)
742 {
743         int err = -EINVAL;
744         int version;
745 
746         if (semid < 0)
747                 return -EINVAL;
748 
749         version = ipc_parse_version(&cmd);
750 
751         switch(cmd) {
752         case IPC_INFO:
753         case SEM_INFO:
754         case SEM_STAT:
755                 err = semctl_nolock(semid,semnum,cmd,version,arg);
756                 return err;
757         case GETALL:
758         case GETVAL:
759         case GETPID:
760         case GETNCNT:
761         case GETZCNT:
762         case IPC_STAT:
763         case SETVAL:
764         case SETALL:
765                 err = semctl_main(semid,semnum,cmd,version,arg);
766                 return err;
767         case IPC_RMID:
768         case IPC_SET:
769                 down(&sem_ids.sem);
770                 err = semctl_down(semid,semnum,cmd,version,arg);
771                 up(&sem_ids.sem);
772                 return err;
773         default:
774                 return -EINVAL;
775         }
776 }
777 
778 static struct sem_undo* freeundos(struct sem_array *sma, struct sem_undo* un)
779 {
780         struct sem_undo* u;
781         struct sem_undo** up;
782 
783         for(up = &current->semundo;(u=*up);up=&u->proc_next) {
784                 if(un==u) {
785                         un=u->proc_next;
786                         *up=un;
787                         kfree(u);
788                         return un;
789                 }
790         }
791         printk ("freeundos undo list error id=%d\n", un->semid);
792         return un->proc_next;
793 }
794 
795 /* returns without sem_lock on error! */
796 static int alloc_undo(struct sem_array *sma, struct sem_undo** unp, int semid, int alter)
797 {
798         int size, nsems, error;
799         struct sem_undo *un;
800 
801         nsems = sma->sem_nsems;
802         size = sizeof(struct sem_undo) + sizeof(short)*nsems;
803         sem_unlock(semid);
804 
805         un = (struct sem_undo *) kmalloc(size, GFP_KERNEL);
806         if (!un)
807                 return -ENOMEM;
808 
809         memset(un, 0, size);
810         error = sem_revalidate(semid, sma, nsems, alter ? S_IWUGO : S_IRUGO);
811         if(error) {
812                 kfree(un);
813                 return error;
814         }
815 
816         un->semadj = (short *) &un[1];
817         un->semid = semid;
818         un->proc_next = current->semundo;
819         current->semundo = un;
820         un->id_next = sma->undo;
821         sma->undo = un;
822         *unp = un;
823         return 0;
824 }
825 
826 asmlinkage long sys_semop (int semid, struct sembuf *tsops, unsigned nsops)
827 {
828         int error = -EINVAL;
829         struct sem_array *sma;
830         struct sembuf fast_sops[SEMOPM_FAST];
831         struct sembuf* sops = fast_sops, *sop;
832         struct sem_undo *un;
833         int undos = 0, decrease = 0, alter = 0;
834         struct sem_queue queue;
835 
836         if (nsops < 1 || semid < 0)
837                 return -EINVAL;
838         if (nsops > sc_semopm)
839                 return -E2BIG;
840         if(nsops > SEMOPM_FAST) {
841                 sops = kmalloc(sizeof(*sops)*nsops,GFP_KERNEL);
842                 if(sops==NULL)
843                         return -ENOMEM;
844         }
845         if (copy_from_user (sops, tsops, nsops * sizeof(*tsops))) {
846                 error=-EFAULT;
847                 goto out_free;
848         }
849         sma = sem_lock(semid);
850         error=-EINVAL;
851         if(sma==NULL)
852                 goto out_free;
853         error = -EIDRM;
854         if (sem_checkid(sma,semid))
855                 goto out_unlock_free;
856         error = -EFBIG;
857         for (sop = sops; sop < sops + nsops; sop++) {
858                 if (sop->sem_num >= sma->sem_nsems)
859                         goto out_unlock_free;
860                 if (sop->sem_flg & SEM_UNDO)
861                         undos++;
862                 if (sop->sem_op < 0)
863                         decrease = 1;
864                 if (sop->sem_op > 0)
865                         alter = 1;
866         }
867         alter |= decrease;
868 
869         error = -EACCES;
870         if (ipcperms(&sma->sem_perm, alter ? S_IWUGO : S_IRUGO))
871                 goto out_unlock_free;
872         if (undos) {
873                 /* Make sure we have an undo structure
874                  * for this process and this semaphore set.
875                  */
876                 un=current->semundo;
877                 while(un != NULL) {
878                         if(un->semid==semid)
879                                 break;
880                         if(un->semid==-1)
881                                 un=freeundos(sma,un);
882                          else
883                                 un=un->proc_next;
884                 }
885                 if (!un) {
886                         error = alloc_undo(sma,&un,semid,alter);
887                         if(error)
888                                 goto out_free;
889                 }
890         } else
891                 un = NULL;
892 
893         error = try_atomic_semop (sma, sops, nsops, un, current->pid, 0);
894         if (error <= 0)
895                 goto update;
896 
897         /* We need to sleep on this operation, so we put the current
898          * task into the pending queue and go to sleep.
899          */
900                 
901         queue.sma = sma;
902         queue.sops = sops;
903         queue.nsops = nsops;
904         queue.undo = un;
905         queue.pid = current->pid;
906         queue.alter = decrease;
907         queue.id = semid;
908         if (alter)
909                 append_to_queue(sma ,&queue);
910         else
911                 prepend_to_queue(sma ,&queue);
912         current->semsleeping = &queue;
913 
914         for (;;) {
915                 struct sem_array* tmp;
916                 queue.status = -EINTR;
917                 queue.sleeper = current;
918                 current->state = TASK_INTERRUPTIBLE;
919                 sem_unlock(semid);
920 
921                 schedule();
922 
923                 tmp = sem_lock(semid);
924                 if(tmp==NULL) {
925                         if(queue.status != -EIDRM)
926                                 BUG();
927                         current->semsleeping = NULL;
928                         error = -EIDRM;
929                         goto out_free;
930                 }
931                 /*
932                  * If queue.status == 1 we where woken up and
933                  * have to retry else we simply return.
934                  * If an interrupt occurred we have to clean up the
935                  * queue
936                  *
937                  */
938                 if (queue.status == 1)
939                 {
940                         error = try_atomic_semop (sma, sops, nsops, un,
941                                                   current->pid,0);
942                         if (error <= 0) 
943                                 break;
944                 } else {
945                         error = queue.status;
946                         if (queue.prev) /* got Interrupt */
947                                 break;
948                         /* Everything done by update_queue */
949                         current->semsleeping = NULL;
950                         goto out_unlock_free;
951                 }
952         }
953         current->semsleeping = NULL;
954         remove_from_queue(sma,&queue);
955 update:
956         if (alter)
957                 update_queue (sma);
958 out_unlock_free:
959         sem_unlock(semid);
960 out_free:
961         if(sops != fast_sops)
962                 kfree(sops);
963         return error;
964 }
965 
966 /*
967  * add semadj values to semaphores, free undo structures.
968  * undo structures are not freed when semaphore arrays are destroyed
969  * so some of them may be out of date.
970  * IMPLEMENTATION NOTE: There is some confusion over whether the
971  * set of adjustments that needs to be done should be done in an atomic
972  * manner or not. That is, if we are attempting to decrement the semval
973  * should we queue up and wait until we can do so legally?
974  * The original implementation attempted to do this (queue and wait).
975  * The current implementation does not do so. The POSIX standard
976  * and SVID should be consulted to determine what behavior is mandated.
977  */
978 void sem_exit (void)
979 {
980         struct sem_queue *q;
981         struct sem_undo *u, *un = NULL, **up, **unp;
982         struct sem_array *sma;
983         int nsems, i;
984 
985         /* If the current process was sleeping for a semaphore,
986          * remove it from the queue.
987          */
988         if ((q = current->semsleeping)) {
989                 int semid = q->id;
990                 sma = sem_lock(semid);
991                 current->semsleeping = NULL;
992 
993                 if (q->prev) {
994                         if(sma==NULL)
995                                 BUG();
996                         remove_from_queue(q->sma,q);
997                 }
998                 if(sma!=NULL)
999                         sem_unlock(semid);
1000         }
1001 
1002         for (up = &current->semundo; (u = *up); *up = u->proc_next, kfree(u)) {
1003                 int semid = u->semid;
1004                 if(semid == -1)
1005                         continue;
1006                 sma = sem_lock(semid);
1007                 if (sma == NULL)
1008                         continue;
1009 
1010                 if (u->semid == -1)
1011                         goto next_entry;
1012 
1013                 if (sem_checkid(sma,u->semid))
1014                         goto next_entry;
1015 
1016                 /* remove u from the sma->undo list */
1017                 for (unp = &sma->undo; (un = *unp); unp = &un->id_next) {
1018                         if (u == un)
1019                                 goto found;
1020                 }
1021                 printk ("sem_exit undo list error id=%d\n", u->semid);
1022                 goto next_entry;
1023 found:
1024                 *unp = un->id_next;
1025                 /* perform adjustments registered in u */
1026                 nsems = sma->sem_nsems;
1027                 for (i = 0; i < nsems; i++) {
1028                         struct sem * sem = &sma->sem_base[i];
1029                         sem->semval += u->semadj[i];
1030                         if (sem->semval < 0)
1031                                 sem->semval = 0; /* shouldn't happen */
1032                         sem->sempid = current->pid;
1033                 }
1034                 sma->sem_otime = CURRENT_TIME;
1035                 /* maybe some queued-up processes were waiting for this */
1036                 update_queue(sma);
1037 next_entry:
1038                 sem_unlock(semid);
1039         }
1040         current->semundo = NULL;
1041 }
1042 
1043 #ifdef CONFIG_PROC_FS
1044 static int sysvipc_sem_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
1045 {
1046         off_t pos = 0;
1047         off_t begin = 0;
1048         int i, len = 0;
1049 
1050         len += sprintf(buffer, "       key      semid perms      nsems   uid   gid  cuid  cgid      otime      ctime\n");
1051         down(&sem_ids.sem);
1052 
1053         for(i = 0; i <= sem_ids.max_id; i++) {
1054                 struct sem_array *sma;
1055                 sma = sem_lock(i);
1056                 if(sma) {
1057                         len += sprintf(buffer + len, "%10d %10d  %4o %10lu %5u %5u %5u %5u %10lu %10lu\n",
1058                                 sma->sem_perm.key,
1059                                 sem_buildid(i,sma->sem_perm.seq),
1060                                 sma->sem_perm.mode,
1061                                 sma->sem_nsems,
1062                                 sma->sem_perm.uid,
1063                                 sma->sem_perm.gid,
1064                                 sma->sem_perm.cuid,
1065                                 sma->sem_perm.cgid,
1066                                 sma->sem_otime,
1067                                 sma->sem_ctime);
1068                         sem_unlock(i);
1069 
1070                         pos += len;
1071                         if(pos < offset) {
1072                                 len = 0;
1073                                 begin = pos;
1074                         }
1075                         if(pos > offset + length)
1076                                 goto done;
1077                 }
1078         }
1079         *eof = 1;
1080 done:
1081         up(&sem_ids.sem);
1082         *start = buffer + (offset - begin);
1083         len -= (offset - begin);
1084         if(len > length)
1085                 len = length;
1086         if(len < 0)
1087                 len = 0;
1088         return len;
1089 }
1090 #endif
1091 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.