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