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