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