1 /* $OpenBSD: sysv_sem.c,v 1.32 2004/07/15 11:24:46 millert Exp $ */
2 /* $NetBSD: sysv_sem.c,v 1.26 1996/02/09 19:00:25 christos Exp $ */
3
4 /*
5 * Copyright (c) 2002,2003 Todd C. Miller <Todd.Miller@courtesan.com>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 *
19 * Sponsored in part by the Defense Advanced Research Projects
20 * Agency (DARPA) and Air Force Research Laboratory, Air Force
21 * Materiel Command, USAF, under agreement number F39502-99-1-0512.
22 */
23 /*
24 * Implementation of SVID semaphores
25 *
26 * Author: Daniel Boulet
27 *
28 * This software is provided ``AS IS'' without any warranties of any kind.
29 */
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/proc.h>
35 #include <sys/sem.h>
36 #include <sys/sysctl.h>
37 #include <sys/malloc.h>
38 #include <sys/pool.h>
39
40 #include <sys/mount.h>
41 #include <sys/syscallargs.h>
42
43 /* SVID defines EIDRM but BSD does not */
44 #ifndef EIDRM
45 #define EIDRM EINVAL
46 #endif
47
48 #ifdef SEM_DEBUG
49 #define DPRINTF(x) printf x
50 #else
51 #define DPRINTF(x)
52 #endif
53
54 int semtot = 0;
55 int semutot = 0;
56 struct semid_ds **sema; /* semaphore id list */
57 SLIST_HEAD(, sem_undo) semu_list; /* list of undo structures */
58 struct pool sema_pool; /* pool for struct semid_ds */
59 struct pool semu_pool; /* pool for struct sem_undo (SEMUSZ) */
60 unsigned short *semseqs; /* array of sem sequence numbers */
61
62 struct sem_undo *semu_alloc(struct proc *);
63 int semundo_adjust(struct proc *, struct sem_undo **, int, int, int);
64 void semundo_clear(int, int);
65
66 void
seminit(void)67 seminit(void)
68 {
69
70 pool_init(&sema_pool, sizeof(struct semid_ds), 0, 0, 0, "semapl",
71 &pool_allocator_nointr);
72 pool_init(&semu_pool, SEMUSZ, 0, 0, 0, "semupl",
73 &pool_allocator_nointr);
74 sema = malloc(seminfo.semmni * sizeof(struct semid_ds *),
75 M_SEM, M_WAITOK);
76 bzero(sema, seminfo.semmni * sizeof(struct semid_ds *));
77 semseqs = malloc(seminfo.semmni * sizeof(unsigned short),
78 M_SEM, M_WAITOK);
79 bzero(semseqs, seminfo.semmni * sizeof(unsigned short));
80 SLIST_INIT(&semu_list);
81 }
82
83 /*
84 * Allocate a new sem_undo structure for a process
85 * (returns ptr to structure or NULL if no more room)
86 */
87 struct sem_undo *
semu_alloc(struct proc * p)88 semu_alloc(struct proc *p)
89 {
90 struct sem_undo *suptr, *sutmp;
91
92 if (semutot == seminfo.semmnu)
93 return (NULL); /* no space */
94
95 /*
96 * Allocate a semu w/o waiting if possible.
97 * If we do have to wait, we must check to verify that a semu
98 * with un_proc == p has not been allocated in the meantime.
99 */
100 semutot++;
101 if ((suptr = pool_get(&semu_pool, 0)) == NULL) {
102 sutmp = pool_get(&semu_pool, PR_WAITOK);
103 SLIST_FOREACH(suptr, &semu_list, un_next) {
104 if (suptr->un_proc == p) {
105 pool_put(&semu_pool, sutmp);
106 semutot--;
107 return (suptr);
108 }
109 }
110 suptr = sutmp;
111 }
112 suptr->un_cnt = 0;
113 suptr->un_proc = p;
114 SLIST_INSERT_HEAD(&semu_list, suptr, un_next);
115 return (suptr);
116 }
117
118 /*
119 * Adjust a particular entry for a particular proc
120 */
121 int
semundo_adjust(struct proc * p,struct sem_undo ** supptr,int semid,int semnum,int adjval)122 semundo_adjust(struct proc *p, struct sem_undo **supptr, int semid, int semnum,
123 int adjval)
124 {
125 struct sem_undo *suptr;
126 struct undo *sunptr;
127 int i;
128
129 /*
130 * Look for and remember the sem_undo if the caller doesn't provide it.
131 */
132 suptr = *supptr;
133 if (suptr == NULL) {
134 SLIST_FOREACH(suptr, &semu_list, un_next) {
135 if (suptr->un_proc == p) {
136 *supptr = suptr;
137 break;
138 }
139 }
140 if (suptr == NULL) {
141 if (adjval == 0)
142 return (0);
143 suptr = semu_alloc(p);
144 if (suptr == NULL)
145 return (ENOSPC);
146 *supptr = suptr;
147 }
148 }
149
150 /*
151 * Look for the requested entry and adjust it
152 * (delete if adjval becomes 0).
153 */
154 sunptr = &suptr->un_ent[0];
155 for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
156 if (sunptr->un_id != semid || sunptr->un_num != semnum)
157 continue;
158 if (adjval == 0)
159 sunptr->un_adjval = 0;
160 else
161 sunptr->un_adjval += adjval;
162 if (sunptr->un_adjval != 0)
163 return (0);
164
165 if (--suptr->un_cnt == 0) {
166 SLIST_REMOVE(&semu_list, suptr, sem_undo, un_next);
167 pool_put(&semu_pool, suptr);
168 semutot--;
169 } else if (i < suptr->un_cnt)
170 suptr->un_ent[i] =
171 suptr->un_ent[suptr->un_cnt];
172 return (0);
173 }
174
175 /* Didn't find the right entry - create it */
176 if (adjval == 0)
177 return (0);
178 if (suptr->un_cnt == SEMUME)
179 return (EINVAL);
180
181 sunptr = &suptr->un_ent[suptr->un_cnt];
182 suptr->un_cnt++;
183 sunptr->un_adjval = adjval;
184 sunptr->un_id = semid;
185 sunptr->un_num = semnum;
186 return (0);
187 }
188
189 void
semundo_clear(int semid,int semnum)190 semundo_clear(int semid, int semnum)
191 {
192 struct sem_undo *suptr = SLIST_FIRST(&semu_list);
193 struct sem_undo *suprev = SLIST_END(&semu_list);
194 struct undo *sunptr;
195 int i;
196
197 while (suptr != SLIST_END(&semu_list)) {
198 sunptr = &suptr->un_ent[0];
199 for (i = 0; i < suptr->un_cnt; i++, sunptr++) {
200 if (sunptr->un_id == semid) {
201 if (semnum == -1 || sunptr->un_num == semnum) {
202 suptr->un_cnt--;
203 if (i < suptr->un_cnt) {
204 suptr->un_ent[i] =
205 suptr->un_ent[suptr->un_cnt];
206 i--, sunptr--;
207 }
208 }
209 if (semnum != -1)
210 break;
211 }
212 }
213 if (suptr->un_cnt == 0) {
214 struct sem_undo *sutmp = suptr;
215
216 if (suptr == SLIST_FIRST(&semu_list))
217 SLIST_REMOVE_HEAD(&semu_list, un_next);
218 else
219 SLIST_REMOVE_NEXT(&semu_list, suprev, un_next);
220 suptr = SLIST_NEXT(suptr, un_next);
221 pool_put(&semu_pool, sutmp);
222 semutot--;
223 } else {
224 suprev = suptr;
225 suptr = SLIST_NEXT(suptr, un_next);
226 }
227 }
228 }
229
230 int
sys___semctl(struct proc * p,void * v,register_t * retval)231 sys___semctl(struct proc *p, void *v, register_t *retval)
232 {
233 struct sys___semctl_args /* {
234 syscallarg(int) semid;
235 syscallarg(int) semnum;
236 syscallarg(int) cmd;
237 syscallarg(union semun *) arg;
238 } */ *uap = v;
239 union semun arg;
240 int error = 0, cmd = SCARG(uap, cmd);
241
242 switch (cmd) {
243 case IPC_SET:
244 case IPC_STAT:
245 case GETALL:
246 case SETVAL:
247 case SETALL:
248 error = copyin(SCARG(uap, arg), &arg, sizeof(arg));
249 break;
250 }
251 if (error == 0) {
252 error = semctl1(p, SCARG(uap, semid), SCARG(uap, semnum),
253 cmd, &arg, retval, copyin, copyout);
254 }
255 return (error);
256 }
257
258 int
semctl1(struct proc * p,int semid,int semnum,int cmd,union semun * arg,register_t * retval,int (* ds_copyin)(const void *,void *,size_t),int (* ds_copyout)(const void *,void *,size_t))259 semctl1(struct proc *p, int semid, int semnum, int cmd, union semun *arg,
260 register_t *retval, int (*ds_copyin)(const void *, void *, size_t),
261 int (*ds_copyout)(const void *, void *, size_t))
262 {
263 struct ucred *cred = p->p_ucred;
264 int i, ix, error = 0;
265 struct semid_ds sbuf;
266 struct semid_ds *semaptr;
267
268 DPRINTF(("call to semctl(%d, %d, %d, %p)\n", semid, semnum, cmd, arg));
269
270 ix = IPCID_TO_IX(semid);
271 if (ix < 0 || ix >= seminfo.semmni)
272 return (EINVAL);
273
274 if ((semaptr = sema[ix]) == NULL ||
275 semaptr->sem_perm.seq != IPCID_TO_SEQ(semid))
276 return (EINVAL);
277
278 switch (cmd) {
279 case IPC_RMID:
280 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)) != 0)
281 return (error);
282 semaptr->sem_perm.cuid = cred->cr_uid;
283 semaptr->sem_perm.uid = cred->cr_uid;
284 semtot -= semaptr->sem_nsems;
285 free(semaptr->sem_base, M_SEM);
286 pool_put(&sema_pool, semaptr);
287 sema[ix] = NULL;
288 semundo_clear(ix, -1);
289 wakeup(&sema[ix]);
290 break;
291
292 case IPC_SET:
293 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_M)))
294 return (error);
295 if ((error = ds_copyin(arg->buf, &sbuf, sizeof(sbuf))) != 0)
296 return (error);
297 semaptr->sem_perm.uid = sbuf.sem_perm.uid;
298 semaptr->sem_perm.gid = sbuf.sem_perm.gid;
299 semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) |
300 (sbuf.sem_perm.mode & 0777);
301 semaptr->sem_ctime = time.tv_sec;
302 break;
303
304 case IPC_STAT:
305 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
306 return (error);
307 error = ds_copyout(semaptr, arg->buf, sizeof(struct semid_ds));
308 break;
309
310 case GETNCNT:
311 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
312 return (error);
313 if (semnum < 0 || semnum >= semaptr->sem_nsems)
314 return (EINVAL);
315 *retval = semaptr->sem_base[semnum].semncnt;
316 break;
317
318 case GETPID:
319 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
320 return (error);
321 if (semnum < 0 || semnum >= semaptr->sem_nsems)
322 return (EINVAL);
323 *retval = semaptr->sem_base[semnum].sempid;
324 break;
325
326 case GETVAL:
327 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
328 return (error);
329 if (semnum < 0 || semnum >= semaptr->sem_nsems)
330 return (EINVAL);
331 *retval = semaptr->sem_base[semnum].semval;
332 break;
333
334 case GETALL:
335 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
336 return (error);
337 for (i = 0; i < semaptr->sem_nsems; i++) {
338 error = ds_copyout(&semaptr->sem_base[i].semval,
339 &arg->array[i], sizeof(arg->array[0]));
340 if (error != 0)
341 break;
342 }
343 break;
344
345 case GETZCNT:
346 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_R)))
347 return (error);
348 if (semnum < 0 || semnum >= semaptr->sem_nsems)
349 return (EINVAL);
350 *retval = semaptr->sem_base[semnum].semzcnt;
351 break;
352
353 case SETVAL:
354 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
355 return (error);
356 if (semnum < 0 || semnum >= semaptr->sem_nsems)
357 return (EINVAL);
358 semaptr->sem_base[semnum].semval = arg->val;
359 semundo_clear(ix, semnum);
360 wakeup(&sema[ix]);
361 break;
362
363 case SETALL:
364 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W)))
365 return (error);
366 for (i = 0; i < semaptr->sem_nsems; i++) {
367 error = ds_copyin(&arg->array[i],
368 &semaptr->sem_base[i].semval,
369 sizeof(arg->array[0]));
370 if (error != 0)
371 break;
372 }
373 semundo_clear(ix, -1);
374 wakeup(&sema[ix]);
375 break;
376
377 default:
378 return (EINVAL);
379 }
380
381 return (error);
382 }
383
384 int
sys_semget(struct proc * p,void * v,register_t * retval)385 sys_semget(struct proc *p, void *v, register_t *retval)
386 {
387 struct sys_semget_args /* {
388 syscallarg(key_t) key;
389 syscallarg(int) nsems;
390 syscallarg(int) semflg;
391 } */ *uap = v;
392 int semid, error;
393 int key = SCARG(uap, key);
394 int nsems = SCARG(uap, nsems);
395 int semflg = SCARG(uap, semflg);
396 struct semid_ds *semaptr, *semaptr_new = NULL;
397 struct ucred *cred = p->p_ucred;
398
399 DPRINTF(("semget(0x%x, %d, 0%o)\n", key, nsems, semflg));
400
401 /*
402 * Preallocate space for the new semaphore. If we are going
403 * to sleep, we want to sleep now to eliminate any race
404 * condition in allocating a semaphore with a specific key.
405 */
406 if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
407 if (nsems <= 0 || nsems > seminfo.semmsl) {
408 DPRINTF(("nsems out of range (0<%d<=%d)\n", nsems,
409 seminfo.semmsl));
410 return (EINVAL);
411 }
412 if (nsems > seminfo.semmns - semtot) {
413 DPRINTF(("not enough semaphores left (need %d, got %d)\n",
414 nsems, seminfo.semmns - semtot));
415 return (ENOSPC);
416 }
417 semaptr_new = pool_get(&sema_pool, PR_WAITOK);
418 semaptr_new->sem_base = malloc(nsems * sizeof(struct sem),
419 M_SEM, M_WAITOK);
420 bzero(semaptr_new->sem_base, nsems * sizeof(struct sem));
421 }
422
423 if (key != IPC_PRIVATE) {
424 for (semid = 0, semaptr = NULL; semid < seminfo.semmni; semid++) {
425 if ((semaptr = sema[semid]) != NULL &&
426 semaptr->sem_perm.key == key) {
427 DPRINTF(("found public key\n"));
428 if ((error = ipcperm(cred, &semaptr->sem_perm,
429 semflg & 0700)))
430 goto error;
431 if (nsems > 0 && semaptr->sem_nsems < nsems) {
432 DPRINTF(("too small\n"));
433 error = EINVAL;
434 goto error;
435 }
436 if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) {
437 DPRINTF(("not exclusive\n"));
438 error = EEXIST;
439 goto error;
440 }
441 goto found;
442 }
443 }
444 }
445
446 DPRINTF(("need to allocate the semid_ds\n"));
447 if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) {
448 for (semid = 0; semid < seminfo.semmni; semid++) {
449 if ((semaptr = sema[semid]) == NULL)
450 break;
451 }
452 if (semid == seminfo.semmni) {
453 DPRINTF(("no more semid_ds's available\n"));
454 error = ENOSPC;
455 goto error;
456 }
457 DPRINTF(("semid %d is available\n", semid));
458 semaptr_new->sem_perm.key = key;
459 semaptr_new->sem_perm.cuid = cred->cr_uid;
460 semaptr_new->sem_perm.uid = cred->cr_uid;
461 semaptr_new->sem_perm.cgid = cred->cr_gid;
462 semaptr_new->sem_perm.gid = cred->cr_gid;
463 semaptr_new->sem_perm.mode = (semflg & 0777);
464 semaptr_new->sem_perm.seq = semseqs[semid] =
465 (semseqs[semid] + 1) & 0x7fff;
466 semaptr_new->sem_nsems = nsems;
467 semaptr_new->sem_otime = 0;
468 semaptr_new->sem_ctime = time.tv_sec;
469 sema[semid] = semaptr_new;
470 semtot += nsems;
471 } else {
472 DPRINTF(("didn't find it and wasn't asked to create it\n"));
473 return (ENOENT);
474 }
475
476 found:
477 *retval = IXSEQ_TO_IPCID(semid, sema[semid]->sem_perm);
478 return (0);
479 error:
480 if (semaptr_new != NULL) {
481 free(semaptr_new->sem_base, M_SEM);
482 pool_put(&sema_pool, semaptr_new);
483 }
484 return (error);
485 }
486
487 int
sys_semop(struct proc * p,void * v,register_t * retval)488 sys_semop(struct proc *p, void *v, register_t *retval)
489 {
490 struct sys_semop_args /* {
491 syscallarg(int) semid;
492 syscallarg(struct sembuf *) sops;
493 syscallarg(size_t) nsops;
494 } */ *uap = v;
495 #define NSOPS 8
496 struct sembuf sopbuf[NSOPS];
497 int semid = SCARG(uap, semid);
498 size_t nsops = SCARG(uap, nsops);
499 struct sembuf *sops;
500 struct semid_ds *semaptr;
501 struct sembuf *sopptr = NULL;
502 struct sem *semptr = NULL;
503 struct sem_undo *suptr = NULL;
504 struct ucred *cred = p->p_ucred;
505 size_t i, j;
506 int do_wakeup, do_undos, error;
507
508 DPRINTF(("call to semop(%d, %p, %lu)\n", semid, SCARG(uap, sops),
509 (u_long)nsops));
510
511 semid = IPCID_TO_IX(semid); /* Convert back to zero origin */
512
513 if (semid < 0 || semid >= seminfo.semmni)
514 return (EINVAL);
515
516 if ((semaptr = sema[semid]) == NULL ||
517 semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid)))
518 return (EINVAL);
519
520 if ((error = ipcperm(cred, &semaptr->sem_perm, IPC_W))) {
521 DPRINTF(("error = %d from ipaccess\n", error));
522 return (error);
523 }
524
525 if (nsops == 0) {
526 *retval = 0;
527 return (0);
528 } else if (nsops > (size_t)seminfo.semopm) {
529 DPRINTF(("too many sops (max=%d, nsops=%lu)\n", seminfo.semopm,
530 (u_long)nsops));
531 return (E2BIG);
532 }
533
534 if (nsops <= NSOPS)
535 sops = sopbuf;
536 else
537 sops = malloc(nsops * sizeof(struct sembuf), M_SEM, M_WAITOK);
538 error = copyin(SCARG(uap, sops), sops, nsops * sizeof(struct sembuf));
539 if (error != 0) {
540 DPRINTF(("error = %d from copyin(%p, %p, %u)\n", error,
541 SCARG(uap, sops), &sops, nsops * sizeof(struct sembuf)));
542 goto done2;
543 }
544
545 /*
546 * Loop trying to satisfy the vector of requests.
547 * If we reach a point where we must wait, any requests already
548 * performed are rolled back and we go to sleep until some other
549 * process wakes us up. At this point, we start all over again.
550 *
551 * This ensures that from the perspective of other tasks, a set
552 * of requests is atomic (never partially satisfied).
553 */
554 do_undos = 0;
555
556 for (;;) {
557 do_wakeup = 0;
558
559 for (i = 0; i < nsops; i++) {
560 sopptr = &sops[i];
561
562 if (sopptr->sem_num >= semaptr->sem_nsems) {
563 error = EFBIG;
564 goto done2;
565 }
566
567 semptr = &semaptr->sem_base[sopptr->sem_num];
568
569 DPRINTF(("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n",
570 semaptr, semaptr->sem_base, semptr,
571 sopptr->sem_num, semptr->semval, sopptr->sem_op,
572 (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"));
573
574 if (sopptr->sem_op < 0) {
575 if ((int)(semptr->semval +
576 sopptr->sem_op) < 0) {
577 DPRINTF(("semop: can't do it now\n"));
578 break;
579 } else {
580 semptr->semval += sopptr->sem_op;
581 if (semptr->semval == 0 &&
582 semptr->semzcnt > 0)
583 do_wakeup = 1;
584 }
585 if (sopptr->sem_flg & SEM_UNDO)
586 do_undos = 1;
587 } else if (sopptr->sem_op == 0) {
588 if (semptr->semval > 0) {
589 DPRINTF(("semop: not zero now\n"));
590 break;
591 }
592 } else {
593 if (semptr->semncnt > 0)
594 do_wakeup = 1;
595 semptr->semval += sopptr->sem_op;
596 if (sopptr->sem_flg & SEM_UNDO)
597 do_undos = 1;
598 }
599 }
600
601 /*
602 * Did we get through the entire vector?
603 */
604 if (i >= nsops)
605 goto done;
606
607 /*
608 * No ... rollback anything that we've already done
609 */
610 DPRINTF(("semop: rollback 0 through %d\n", i - 1));
611 for (j = 0; j < i; j++)
612 semaptr->sem_base[sops[j].sem_num].semval -=
613 sops[j].sem_op;
614
615 /*
616 * If the request that we couldn't satisfy has the
617 * NOWAIT flag set then return with EAGAIN.
618 */
619 if (sopptr->sem_flg & IPC_NOWAIT) {
620 error = EAGAIN;
621 goto done2;
622 }
623
624 if (sopptr->sem_op == 0)
625 semptr->semzcnt++;
626 else
627 semptr->semncnt++;
628
629 DPRINTF(("semop: good night!\n"));
630 error = tsleep(&sema[semid], PLOCK | PCATCH,
631 "semwait", 0);
632 DPRINTF(("semop: good morning (error=%d)!\n", error));
633
634 suptr = NULL; /* sem_undo may have been reallocated */
635
636 /*
637 * Make sure that the semaphore still exists
638 */
639 if (sema[semid] == NULL ||
640 semaptr->sem_perm.seq != IPCID_TO_SEQ(SCARG(uap, semid))) {
641 error = EIDRM;
642 goto done2;
643 }
644
645 /*
646 * The semaphore is still alive. Readjust the count of
647 * waiting processes.
648 */
649 if (sopptr->sem_op == 0)
650 semptr->semzcnt--;
651 else
652 semptr->semncnt--;
653
654 /*
655 * Is it really morning, or was our sleep interrupted?
656 * (Delayed check of tsleep() return code because we
657 * need to decrement sem[nz]cnt either way.)
658 */
659 if (error != 0) {
660 error = EINTR;
661 goto done2;
662 }
663 DPRINTF(("semop: good morning!\n"));
664 }
665
666 done:
667 /*
668 * Process any SEM_UNDO requests.
669 */
670 if (do_undos) {
671 for (i = 0; i < nsops; i++) {
672 /*
673 * We only need to deal with SEM_UNDO's for non-zero
674 * op's.
675 */
676 int adjval;
677
678 if ((sops[i].sem_flg & SEM_UNDO) == 0)
679 continue;
680 adjval = sops[i].sem_op;
681 if (adjval == 0)
682 continue;
683 error = semundo_adjust(p, &suptr, semid,
684 sops[i].sem_num, -adjval);
685 if (error == 0)
686 continue;
687
688 /*
689 * Uh-Oh! We ran out of either sem_undo's or undo's.
690 * Rollback the adjustments to this point and then
691 * rollback the semaphore ups and down so we can return
692 * with an error with all structures restored. We
693 * rollback the undo's in the exact reverse order that
694 * we applied them. This guarantees that we won't run
695 * out of space as we roll things back out.
696 */
697 if (i != 0) {
698 for (j = i - 1; j >= 0; j--) {
699 if ((sops[j].sem_flg & SEM_UNDO) == 0)
700 continue;
701 adjval = sops[j].sem_op;
702 if (adjval == 0)
703 continue;
704 if (semundo_adjust(p, &suptr, semid,
705 sops[j].sem_num, adjval) != 0)
706 panic("semop - can't undo undos");
707 }
708 }
709
710 for (j = 0; j < nsops; j++)
711 semaptr->sem_base[sops[j].sem_num].semval -=
712 sops[j].sem_op;
713
714 DPRINTF(("error = %d from semundo_adjust\n", error));
715 goto done2;
716 } /* loop through the sops */
717 } /* if (do_undos) */
718
719 /* We're definitely done - set the sempid's */
720 for (i = 0; i < nsops; i++) {
721 sopptr = &sops[i];
722 semptr = &semaptr->sem_base[sopptr->sem_num];
723 semptr->sempid = p->p_pid;
724 }
725
726 /* Do a wakeup if any semaphore was up'd. */
727 if (do_wakeup) {
728 DPRINTF(("semop: doing wakeup\n"));
729 wakeup(&sema[semid]);
730 DPRINTF(("semop: back from wakeup\n"));
731 }
732 DPRINTF(("semop: done\n"));
733 *retval = 0;
734 done2:
735 if (sops != sopbuf)
736 free(sops, M_SEM);
737 return (error);
738 }
739
740 /*
741 * Go through the undo structures for this process and apply the adjustments to
742 * semaphores.
743 */
744 void
semexit(struct proc * p)745 semexit(struct proc *p)
746 {
747 struct sem_undo *suptr;
748 struct sem_undo **supptr;
749
750 /*
751 * Go through the chain of undo vectors looking for one associated with
752 * this process.
753 */
754 SLIST_FOREACH_PREVPTR(suptr, supptr, &semu_list, un_next) {
755 if (suptr->un_proc == p)
756 break;
757 }
758
759 /*
760 * If there is no undo vector, skip to the end.
761 */
762 if (suptr == NULL)
763 return;
764
765 /*
766 * We now have an undo vector for this process.
767 */
768 DPRINTF(("proc @%p has undo structure with %d entries\n", p,
769 suptr->un_cnt));
770
771 /*
772 * If there are any active undo elements then process them.
773 */
774 if (suptr->un_cnt > 0) {
775 int ix;
776
777 for (ix = 0; ix < suptr->un_cnt; ix++) {
778 int semid = suptr->un_ent[ix].un_id;
779 int semnum = suptr->un_ent[ix].un_num;
780 int adjval = suptr->un_ent[ix].un_adjval;
781 struct semid_ds *semaptr;
782
783 if ((semaptr = sema[semid]) == NULL)
784 panic("semexit - semid not allocated");
785 if (semnum >= semaptr->sem_nsems)
786 panic("semexit - semnum out of range");
787
788 DPRINTF(("semexit: %p id=%d num=%d(adj=%d) ; sem=%d\n",
789 suptr->un_proc, suptr->un_ent[ix].un_id,
790 suptr->un_ent[ix].un_num,
791 suptr->un_ent[ix].un_adjval,
792 semaptr->sem_base[semnum].semval));
793
794 if (adjval < 0 &&
795 semaptr->sem_base[semnum].semval < -adjval)
796 semaptr->sem_base[semnum].semval = 0;
797 else
798 semaptr->sem_base[semnum].semval += adjval;
799
800 wakeup(&sema[semid]);
801 DPRINTF(("semexit: back from wakeup\n"));
802 }
803 }
804
805 /*
806 * Deallocate the undo vector.
807 */
808 DPRINTF(("removing vector\n"));
809 *supptr = SLIST_NEXT(suptr, un_next);
810 pool_put(&semu_pool, suptr);
811 semutot--;
812 }
813
814 /*
815 * Userland access to struct seminfo.
816 */
817 int
sysctl_sysvsem(int * name,u_int namelen,void * oldp,size_t * oldlenp,void * newp,size_t newlen)818 sysctl_sysvsem(int *name, u_int namelen, void *oldp, size_t *oldlenp,
819 void *newp, size_t newlen)
820 {
821 int error, val;
822 struct semid_ds **sema_new;
823 unsigned short *newseqs;
824
825 if (namelen != 2) {
826 switch (name[0]) {
827 case KERN_SEMINFO_SEMMNI:
828 case KERN_SEMINFO_SEMMNS:
829 case KERN_SEMINFO_SEMMNU:
830 case KERN_SEMINFO_SEMMSL:
831 case KERN_SEMINFO_SEMOPM:
832 case KERN_SEMINFO_SEMUME:
833 case KERN_SEMINFO_SEMUSZ:
834 case KERN_SEMINFO_SEMVMX:
835 case KERN_SEMINFO_SEMAEM:
836 break;
837 default:
838 return (ENOTDIR); /* overloaded */
839 }
840 }
841
842 switch (name[0]) {
843 case KERN_SEMINFO_SEMMNI:
844 val = seminfo.semmni;
845 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
846 val == seminfo.semmni)
847 return (error);
848
849 if (val < seminfo.semmni || val > 0xffff)
850 return (EINVAL);
851
852 /* Expand semsegs and semseqs arrays */
853 sema_new = malloc(val * sizeof(struct semid_ds *),
854 M_SEM, M_WAITOK);
855 bcopy(sema, sema_new,
856 seminfo.semmni * sizeof(struct semid_ds *));
857 bzero(sema_new + seminfo.semmni,
858 (val - seminfo.semmni) * sizeof(struct semid_ds *));
859 newseqs = malloc(val * sizeof(unsigned short), M_SEM, M_WAITOK);
860 bcopy(semseqs, newseqs,
861 seminfo.semmni * sizeof(unsigned short));
862 bzero(newseqs + seminfo.semmni,
863 (val - seminfo.semmni) * sizeof(unsigned short));
864 free(sema, M_SEM);
865 free(semseqs, M_SEM);
866 sema = sema_new;
867 semseqs = newseqs;
868 seminfo.semmni = val;
869 return (0);
870 case KERN_SEMINFO_SEMMNS:
871 val = seminfo.semmns;
872 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
873 val == seminfo.semmns)
874 return (error);
875 if (val < seminfo.semmns || val > 0xffff)
876 return (EINVAL); /* can't decrease semmns */
877 seminfo.semmns = val;
878 return (0);
879 case KERN_SEMINFO_SEMMNU:
880 val = seminfo.semmnu;
881 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
882 val == seminfo.semmnu)
883 return (error);
884 if (val < seminfo.semmnu)
885 return (EINVAL); /* can't decrease semmnu */
886 seminfo.semmnu = val;
887 return (0);
888 case KERN_SEMINFO_SEMMSL:
889 val = seminfo.semmsl;
890 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
891 val == seminfo.semmsl)
892 return (error);
893 if (val < seminfo.semmsl || val > 0xffff)
894 return (EINVAL); /* can't decrease semmsl */
895 seminfo.semmsl = val;
896 return (0);
897 case KERN_SEMINFO_SEMOPM:
898 val = seminfo.semopm;
899 if ((error = sysctl_int(oldp, oldlenp, newp, newlen, &val)) ||
900 val == seminfo.semopm)
901 return (error);
902 if (val <= 0)
903 return (EINVAL); /* semopm must be >= 1 */
904 seminfo.semopm = val;
905 return (0);
906 case KERN_SEMINFO_SEMUME:
907 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semume));
908 case KERN_SEMINFO_SEMUSZ:
909 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semusz));
910 case KERN_SEMINFO_SEMVMX:
911 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semvmx));
912 case KERN_SEMINFO_SEMAEM:
913 return (sysctl_rdint(oldp, oldlenp, newp, seminfo.semaem));
914 default:
915 return (EOPNOTSUPP);
916 }
917 /* NOTREACHED */
918 }
919