]>
Commit | Line | Data |
---|---|---|
1c79356b A |
1 | /* |
2 | * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * The contents of this file constitute Original Code as defined in and | |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
11 | * | |
12 | * This Original Code and all software distributed under the License are | |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* | |
23 | * Implementation of SVID semaphores | |
24 | * | |
25 | * Author: Daniel Boulet | |
26 | * | |
27 | * This software is provided ``AS IS'' without any warranties of any kind. | |
28 | */ | |
29 | ||
30 | #include <sys/param.h> | |
31 | #include <sys/systm.h> | |
32 | #include <sys/sysproto.h> | |
33 | #include <sys/kernel.h> | |
34 | #include <sys/proc.h> | |
35 | #include <sys/sem.h> | |
36 | #include <sys/sysent.h> | |
37 | ||
38 | static void seminit __P((void *)); | |
39 | SYSINIT(sysv_sem, SI_SUB_SYSV_SEM, SI_ORDER_FIRST, seminit, NULL) | |
40 | ||
41 | #ifndef _SYS_SYSPROTO_H_ | |
42 | struct __semctl_args; | |
43 | int __semctl __P((struct proc *p, struct __semctl_args *uap)); | |
44 | struct semget_args; | |
45 | int semget __P((struct proc *p, struct semget_args *uap)); | |
46 | struct semop_args; | |
47 | int semop __P((struct proc *p, struct semop_args *uap)); | |
48 | struct semconfig_args; | |
49 | int semconfig __P((struct proc *p, struct semconfig_args *uap)); | |
50 | #endif | |
51 | ||
52 | static struct sem_undo *semu_alloc __P((struct proc *p)); | |
53 | static int semundo_adjust __P((struct proc *p, struct sem_undo **supptr, | |
54 | int semid, int semnum, int adjval)); | |
55 | static void semundo_clear __P((int semid, int semnum)); | |
56 | ||
57 | /* XXX casting to (sy_call_t *) is bogus, as usual. */ | |
58 | static sy_call_t *semcalls[] = { | |
59 | (sy_call_t *)__semctl, (sy_call_t *)semget, | |
60 | (sy_call_t *)semop, (sy_call_t *)semconfig | |
61 | }; | |
62 | ||
63 | static int semtot = 0; | |
64 | struct semid_ds *sema; /* semaphore id pool */ | |
65 | struct sem *sem; /* semaphore pool */ | |
66 | static struct sem_undo *semu_list; /* list of active undo structures */ | |
67 | int *semu; /* undo structure pool */ | |
68 | ||
69 | static struct proc *semlock_holder = NULL; | |
70 | ||
71 | void | |
72 | seminit(dummy) | |
73 | void *dummy; | |
74 | { | |
75 | register int i; | |
76 | ||
77 | if (sema == NULL) | |
78 | panic("sema is NULL"); | |
79 | if (semu == NULL) | |
80 | panic("semu is NULL"); | |
81 | ||
82 | for (i = 0; i < seminfo.semmni; i++) { | |
83 | sema[i].sem_base = 0; | |
84 | sema[i].sem_perm.mode = 0; | |
85 | } | |
86 | for (i = 0; i < seminfo.semmnu; i++) { | |
87 | register struct sem_undo *suptr = SEMU(i); | |
88 | suptr->un_proc = NULL; | |
89 | } | |
90 | semu_list = NULL; | |
91 | } | |
92 | ||
93 | /* | |
94 | * Entry point for all SEM calls | |
95 | */ | |
96 | int | |
97 | semsys(p, uap) | |
98 | struct proc *p; | |
99 | /* XXX actually varargs. */ | |
100 | struct semsys_args /* { | |
101 | u_int which; | |
102 | int a2; | |
103 | int a3; | |
104 | int a4; | |
105 | int a5; | |
106 | } */ *uap; | |
107 | { | |
108 | ||
109 | while (semlock_holder != NULL && semlock_holder != p) | |
110 | (void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "semsys", 0); | |
111 | ||
112 | if (uap->which >= sizeof(semcalls)/sizeof(semcalls[0])) | |
113 | return (EINVAL); | |
114 | return ((*semcalls[uap->which])(p, &uap->a2)); | |
115 | } | |
116 | ||
117 | /* | |
118 | * Lock or unlock the entire semaphore facility. | |
119 | * | |
120 | * This will probably eventually evolve into a general purpose semaphore | |
121 | * facility status enquiry mechanism (I don't like the "read /dev/kmem" | |
122 | * approach currently taken by ipcs and the amount of info that we want | |
123 | * to be able to extract for ipcs is probably beyond what the capability | |
124 | * of the getkerninfo facility. | |
125 | * | |
126 | * At the time that the current version of semconfig was written, ipcs is | |
127 | * the only user of the semconfig facility. It uses it to ensure that the | |
128 | * semaphore facility data structures remain static while it fishes around | |
129 | * in /dev/kmem. | |
130 | */ | |
131 | ||
132 | #ifndef _SYS_SYSPROTO_H_ | |
133 | struct semconfig_args { | |
134 | semconfig_ctl_t flag; | |
135 | }; | |
136 | #endif | |
137 | ||
138 | int | |
139 | semconfig(p, uap) | |
140 | struct proc *p; | |
141 | struct semconfig_args *uap; | |
142 | { | |
143 | int eval = 0; | |
144 | ||
145 | switch (uap->flag) { | |
146 | case SEM_CONFIG_FREEZE: | |
147 | semlock_holder = p; | |
148 | break; | |
149 | ||
150 | case SEM_CONFIG_THAW: | |
151 | semlock_holder = NULL; | |
152 | wakeup((caddr_t)&semlock_holder); | |
153 | break; | |
154 | ||
155 | default: | |
156 | printf("semconfig: unknown flag parameter value (%d) - ignored\n", | |
157 | uap->flag); | |
158 | eval = EINVAL; | |
159 | break; | |
160 | } | |
161 | ||
162 | p->p_retval[0] = 0; | |
163 | return(eval); | |
164 | } | |
165 | ||
166 | /* | |
167 | * Allocate a new sem_undo structure for a process | |
168 | * (returns ptr to structure or NULL if no more room) | |
169 | */ | |
170 | ||
171 | static struct sem_undo * | |
172 | semu_alloc(p) | |
173 | struct proc *p; | |
174 | { | |
175 | register int i; | |
176 | register struct sem_undo *suptr; | |
177 | register struct sem_undo **supptr; | |
178 | int attempt; | |
179 | ||
180 | /* | |
181 | * Try twice to allocate something. | |
182 | * (we'll purge any empty structures after the first pass so | |
183 | * two passes are always enough) | |
184 | */ | |
185 | ||
186 | for (attempt = 0; attempt < 2; attempt++) { | |
187 | /* | |
188 | * Look for a free structure. | |
189 | * Fill it in and return it if we find one. | |
190 | */ | |
191 | ||
192 | for (i = 0; i < seminfo.semmnu; i++) { | |
193 | suptr = SEMU(i); | |
194 | if (suptr->un_proc == NULL) { | |
195 | suptr->un_next = semu_list; | |
196 | semu_list = suptr; | |
197 | suptr->un_cnt = 0; | |
198 | suptr->un_proc = p; | |
199 | return(suptr); | |
200 | } | |
201 | } | |
202 | ||
203 | /* | |
204 | * We didn't find a free one, if this is the first attempt | |
205 | * then try to free some structures. | |
206 | */ | |
207 | ||
208 | if (attempt == 0) { | |
209 | /* All the structures are in use - try to free some */ | |
210 | int did_something = 0; | |
211 | ||
212 | supptr = &semu_list; | |
213 | while ((suptr = *supptr) != NULL) { | |
214 | if (suptr->un_cnt == 0) { | |
215 | suptr->un_proc = NULL; | |
216 | *supptr = suptr->un_next; | |
217 | did_something = 1; | |
218 | } else | |
219 | supptr = &(suptr->un_next); | |
220 | } | |
221 | ||
222 | /* If we didn't free anything then just give-up */ | |
223 | if (!did_something) | |
224 | return(NULL); | |
225 | } else { | |
226 | /* | |
227 | * The second pass failed even though we freed | |
228 | * something after the first pass! | |
229 | * This is IMPOSSIBLE! | |
230 | */ | |
231 | panic("semu_alloc - second attempt failed"); | |
232 | } | |
233 | } | |
234 | return (NULL); | |
235 | } | |
236 | ||
237 | /* | |
238 | * Adjust a particular entry for a particular proc | |
239 | */ | |
240 | ||
241 | static int | |
242 | semundo_adjust(p, supptr, semid, semnum, adjval) | |
243 | register struct proc *p; | |
244 | struct sem_undo **supptr; | |
245 | int semid, semnum; | |
246 | int adjval; | |
247 | { | |
248 | register struct sem_undo *suptr; | |
249 | register struct undo *sunptr; | |
250 | int i; | |
251 | ||
252 | /* Look for and remember the sem_undo if the caller doesn't provide | |
253 | it */ | |
254 | ||
255 | suptr = *supptr; | |
256 | if (suptr == NULL) { | |
257 | for (suptr = semu_list; suptr != NULL; | |
258 | suptr = suptr->un_next) { | |
259 | if (suptr->un_proc == p) { | |
260 | *supptr = suptr; | |
261 | break; | |
262 | } | |
263 | } | |
264 | if (suptr == NULL) { | |
265 | if (adjval == 0) | |
266 | return(0); | |
267 | suptr = semu_alloc(p); | |
268 | if (suptr == NULL) | |
269 | return(ENOSPC); | |
270 | *supptr = suptr; | |
271 | } | |
272 | } | |
273 | ||
274 | /* | |
275 | * Look for the requested entry and adjust it (delete if adjval becomes | |
276 | * 0). | |
277 | */ | |
278 | sunptr = &suptr->un_ent[0]; | |
279 | for (i = 0; i < suptr->un_cnt; i++, sunptr++) { | |
280 | if (sunptr->un_id != semid || sunptr->un_num != semnum) | |
281 | continue; | |
282 | if (adjval == 0) | |
283 | sunptr->un_adjval = 0; | |
284 | else | |
285 | sunptr->un_adjval += adjval; | |
286 | if (sunptr->un_adjval == 0) { | |
287 | suptr->un_cnt--; | |
288 | if (i < suptr->un_cnt) | |
289 | suptr->un_ent[i] = | |
290 | suptr->un_ent[suptr->un_cnt]; | |
291 | } | |
292 | return(0); | |
293 | } | |
294 | ||
295 | /* Didn't find the right entry - create it */ | |
296 | if (adjval == 0) | |
297 | return(0); | |
298 | if (suptr->un_cnt != seminfo.semume) { | |
299 | sunptr = &suptr->un_ent[suptr->un_cnt]; | |
300 | suptr->un_cnt++; | |
301 | sunptr->un_adjval = adjval; | |
302 | sunptr->un_id = semid; sunptr->un_num = semnum; | |
303 | } else | |
304 | return(EINVAL); | |
305 | return(0); | |
306 | } | |
307 | ||
308 | static void | |
309 | semundo_clear(semid, semnum) | |
310 | int semid, semnum; | |
311 | { | |
312 | register struct sem_undo *suptr; | |
313 | ||
314 | for (suptr = semu_list; suptr != NULL; suptr = suptr->un_next) { | |
315 | register struct undo *sunptr = &suptr->un_ent[0]; | |
316 | register int i = 0; | |
317 | ||
318 | while (i < suptr->un_cnt) { | |
319 | if (sunptr->un_id == semid) { | |
320 | if (semnum == -1 || sunptr->un_num == semnum) { | |
321 | suptr->un_cnt--; | |
322 | if (i < suptr->un_cnt) { | |
323 | suptr->un_ent[i] = | |
324 | suptr->un_ent[suptr->un_cnt]; | |
325 | continue; | |
326 | } | |
327 | } | |
328 | if (semnum != -1) | |
329 | break; | |
330 | } | |
331 | i++, sunptr++; | |
332 | } | |
333 | } | |
334 | } | |
335 | ||
336 | /* | |
337 | * Note that the user-mode half of this passes a union, not a pointer | |
338 | */ | |
339 | #ifndef _SYS_SYSPROTO_H_ | |
340 | struct __semctl_args { | |
341 | int semid; | |
342 | int semnum; | |
343 | int cmd; | |
344 | union semun *arg; | |
345 | }; | |
346 | #endif | |
347 | ||
348 | int | |
349 | __semctl(p, uap) | |
350 | struct proc *p; | |
351 | register struct __semctl_args *uap; | |
352 | { | |
353 | int semid = uap->semid; | |
354 | int semnum = uap->semnum; | |
355 | int cmd = uap->cmd; | |
356 | union semun *arg = uap->arg; | |
357 | union semun real_arg; | |
358 | struct ucred *cred = p->p_ucred; | |
359 | int i, rval, eval; | |
360 | struct semid_ds sbuf; | |
361 | register struct semid_ds *semaptr; | |
362 | ||
363 | #ifdef SEM_DEBUG | |
364 | printf("call to semctl(%d, %d, %d, 0x%x)\n", semid, semnum, cmd, arg); | |
365 | #endif | |
366 | ||
367 | semid = IPCID_TO_IX(semid); | |
368 | if (semid < 0 || semid >= seminfo.semmsl) | |
369 | return(EINVAL); | |
370 | ||
371 | semaptr = &sema[semid]; | |
372 | if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || | |
373 | semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) | |
374 | return(EINVAL); | |
375 | ||
376 | eval = 0; | |
377 | rval = 0; | |
378 | ||
379 | switch (cmd) { | |
380 | case IPC_RMID: | |
381 | if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M))) | |
382 | return(eval); | |
383 | semaptr->sem_perm.cuid = cred->cr_uid; | |
384 | semaptr->sem_perm.uid = cred->cr_uid; | |
385 | semtot -= semaptr->sem_nsems; | |
386 | for (i = semaptr->sem_base - sem; i < semtot; i++) | |
387 | sem[i] = sem[i + semaptr->sem_nsems]; | |
388 | for (i = 0; i < seminfo.semmni; i++) { | |
389 | if ((sema[i].sem_perm.mode & SEM_ALLOC) && | |
390 | sema[i].sem_base > semaptr->sem_base) | |
391 | sema[i].sem_base -= semaptr->sem_nsems; | |
392 | } | |
393 | semaptr->sem_perm.mode = 0; | |
394 | semundo_clear(semid, -1); | |
395 | wakeup((caddr_t)semaptr); | |
396 | break; | |
397 | ||
398 | case IPC_SET: | |
399 | if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_M))) | |
400 | return(eval); | |
401 | if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) | |
402 | return(eval); | |
403 | if ((eval = copyin(real_arg.buf, (caddr_t)&sbuf, | |
404 | sizeof(sbuf))) != 0) | |
405 | return(eval); | |
406 | semaptr->sem_perm.uid = sbuf.sem_perm.uid; | |
407 | semaptr->sem_perm.gid = sbuf.sem_perm.gid; | |
408 | semaptr->sem_perm.mode = (semaptr->sem_perm.mode & ~0777) | | |
409 | (sbuf.sem_perm.mode & 0777); | |
410 | semaptr->sem_ctime = time_second; | |
411 | break; | |
412 | ||
413 | case IPC_STAT: | |
414 | if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) | |
415 | return(eval); | |
416 | if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) | |
417 | return(eval); | |
418 | eval = copyout((caddr_t)semaptr, real_arg.buf, | |
419 | sizeof(struct semid_ds)); | |
420 | break; | |
421 | ||
422 | case GETNCNT: | |
423 | if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) | |
424 | return(eval); | |
425 | if (semnum < 0 || semnum >= semaptr->sem_nsems) | |
426 | return(EINVAL); | |
427 | rval = semaptr->sem_base[semnum].semncnt; | |
428 | break; | |
429 | ||
430 | case GETPID: | |
431 | if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) | |
432 | return(eval); | |
433 | if (semnum < 0 || semnum >= semaptr->sem_nsems) | |
434 | return(EINVAL); | |
435 | rval = semaptr->sem_base[semnum].sempid; | |
436 | break; | |
437 | ||
438 | case GETVAL: | |
439 | if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) | |
440 | return(eval); | |
441 | if (semnum < 0 || semnum >= semaptr->sem_nsems) | |
442 | return(EINVAL); | |
443 | rval = semaptr->sem_base[semnum].semval; | |
444 | break; | |
445 | ||
446 | case GETALL: | |
447 | if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) | |
448 | return(eval); | |
449 | if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) | |
450 | return(eval); | |
451 | for (i = 0; i < semaptr->sem_nsems; i++) { | |
452 | eval = copyout((caddr_t)&semaptr->sem_base[i].semval, | |
453 | &real_arg.array[i], sizeof(real_arg.array[0])); | |
454 | if (eval != 0) | |
455 | break; | |
456 | } | |
457 | break; | |
458 | ||
459 | case GETZCNT: | |
460 | if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_R))) | |
461 | return(eval); | |
462 | if (semnum < 0 || semnum >= semaptr->sem_nsems) | |
463 | return(EINVAL); | |
464 | rval = semaptr->sem_base[semnum].semzcnt; | |
465 | break; | |
466 | ||
467 | case SETVAL: | |
468 | if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) | |
469 | return(eval); | |
470 | if (semnum < 0 || semnum >= semaptr->sem_nsems) | |
471 | return(EINVAL); | |
472 | if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) | |
473 | return(eval); | |
474 | semaptr->sem_base[semnum].semval = real_arg.val; | |
475 | semundo_clear(semid, semnum); | |
476 | wakeup((caddr_t)semaptr); | |
477 | break; | |
478 | ||
479 | case SETALL: | |
480 | if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) | |
481 | return(eval); | |
482 | if ((eval = copyin(arg, &real_arg, sizeof(real_arg))) != 0) | |
483 | return(eval); | |
484 | for (i = 0; i < semaptr->sem_nsems; i++) { | |
485 | eval = copyin(&real_arg.array[i], | |
486 | (caddr_t)&semaptr->sem_base[i].semval, | |
487 | sizeof(real_arg.array[0])); | |
488 | if (eval != 0) | |
489 | break; | |
490 | } | |
491 | semundo_clear(semid, -1); | |
492 | wakeup((caddr_t)semaptr); | |
493 | break; | |
494 | ||
495 | default: | |
496 | return(EINVAL); | |
497 | } | |
498 | ||
499 | if (eval == 0) | |
500 | p->p_retval[0] = rval; | |
501 | return(eval); | |
502 | } | |
503 | ||
504 | #ifndef _SYS_SYSPROTO_H_ | |
505 | struct semget_args { | |
506 | key_t key; | |
507 | int nsems; | |
508 | int semflg; | |
509 | }; | |
510 | #endif | |
511 | ||
512 | int | |
513 | semget(p, uap) | |
514 | struct proc *p; | |
515 | register struct semget_args *uap; | |
516 | { | |
517 | int semid, eval; | |
518 | int key = uap->key; | |
519 | int nsems = uap->nsems; | |
520 | int semflg = uap->semflg; | |
521 | struct ucred *cred = p->p_ucred; | |
522 | ||
523 | #ifdef SEM_DEBUG | |
524 | printf("semget(0x%x, %d, 0%o)\n", key, nsems, semflg); | |
525 | #endif | |
526 | ||
527 | if (key != IPC_PRIVATE) { | |
528 | for (semid = 0; semid < seminfo.semmni; semid++) { | |
529 | if ((sema[semid].sem_perm.mode & SEM_ALLOC) && | |
530 | sema[semid].sem_perm.key == key) | |
531 | break; | |
532 | } | |
533 | if (semid < seminfo.semmni) { | |
534 | #ifdef SEM_DEBUG | |
535 | printf("found public key\n"); | |
536 | #endif | |
537 | if ((eval = ipcperm(cred, &sema[semid].sem_perm, | |
538 | semflg & 0700))) | |
539 | return(eval); | |
540 | if (nsems > 0 && sema[semid].sem_nsems < nsems) { | |
541 | #ifdef SEM_DEBUG | |
542 | printf("too small\n"); | |
543 | #endif | |
544 | return(EINVAL); | |
545 | } | |
546 | if ((semflg & IPC_CREAT) && (semflg & IPC_EXCL)) { | |
547 | #ifdef SEM_DEBUG | |
548 | printf("not exclusive\n"); | |
549 | #endif | |
550 | return(EEXIST); | |
551 | } | |
552 | goto found; | |
553 | } | |
554 | } | |
555 | ||
556 | #ifdef SEM_DEBUG | |
557 | printf("need to allocate the semid_ds\n"); | |
558 | #endif | |
559 | if (key == IPC_PRIVATE || (semflg & IPC_CREAT)) { | |
560 | if (nsems <= 0 || nsems > seminfo.semmsl) { | |
561 | #ifdef SEM_DEBUG | |
562 | printf("nsems out of range (0<%d<=%d)\n", nsems, | |
563 | seminfo.semmsl); | |
564 | #endif | |
565 | return(EINVAL); | |
566 | } | |
567 | if (nsems > seminfo.semmns - semtot) { | |
568 | #ifdef SEM_DEBUG | |
569 | printf("not enough semaphores left (need %d, got %d)\n", | |
570 | nsems, seminfo.semmns - semtot); | |
571 | #endif | |
572 | return(ENOSPC); | |
573 | } | |
574 | for (semid = 0; semid < seminfo.semmni; semid++) { | |
575 | if ((sema[semid].sem_perm.mode & SEM_ALLOC) == 0) | |
576 | break; | |
577 | } | |
578 | if (semid == seminfo.semmni) { | |
579 | #ifdef SEM_DEBUG | |
580 | printf("no more semid_ds's available\n"); | |
581 | #endif | |
582 | return(ENOSPC); | |
583 | } | |
584 | #ifdef SEM_DEBUG | |
585 | printf("semid %d is available\n", semid); | |
586 | #endif | |
587 | sema[semid].sem_perm.key = key; | |
588 | sema[semid].sem_perm.cuid = cred->cr_uid; | |
589 | sema[semid].sem_perm.uid = cred->cr_uid; | |
590 | sema[semid].sem_perm.cgid = cred->cr_gid; | |
591 | sema[semid].sem_perm.gid = cred->cr_gid; | |
592 | sema[semid].sem_perm.mode = (semflg & 0777) | SEM_ALLOC; | |
593 | sema[semid].sem_perm.seq = | |
594 | (sema[semid].sem_perm.seq + 1) & 0x7fff; | |
595 | sema[semid].sem_nsems = nsems; | |
596 | sema[semid].sem_otime = 0; | |
597 | sema[semid].sem_ctime = time_second; | |
598 | sema[semid].sem_base = &sem[semtot]; | |
599 | semtot += nsems; | |
600 | bzero(sema[semid].sem_base, | |
601 | sizeof(sema[semid].sem_base[0])*nsems); | |
602 | #ifdef SEM_DEBUG | |
603 | printf("sembase = 0x%x, next = 0x%x\n", sema[semid].sem_base, | |
604 | &sem[semtot]); | |
605 | #endif | |
606 | } else { | |
607 | #ifdef SEM_DEBUG | |
608 | printf("didn't find it and wasn't asked to create it\n"); | |
609 | #endif | |
610 | return(ENOENT); | |
611 | } | |
612 | ||
613 | found: | |
614 | p->p_retval[0] = IXSEQ_TO_IPCID(semid, sema[semid].sem_perm); | |
615 | return(0); | |
616 | } | |
617 | ||
618 | #ifndef _SYS_SYSPROTO_H_ | |
619 | struct semop_args { | |
620 | int semid; | |
621 | struct sembuf *sops; | |
622 | int nsops; | |
623 | }; | |
624 | #endif | |
625 | ||
626 | int | |
627 | semop(p, uap) | |
628 | struct proc *p; | |
629 | register struct semop_args *uap; | |
630 | { | |
631 | int semid = uap->semid; | |
632 | int nsops = uap->nsops; | |
633 | struct sembuf sops[MAX_SOPS]; | |
634 | register struct semid_ds *semaptr; | |
635 | register struct sembuf *sopptr; | |
636 | register struct sem *semptr; | |
637 | struct sem_undo *suptr = NULL; | |
638 | struct ucred *cred = p->p_ucred; | |
639 | int i, j, eval; | |
640 | int do_wakeup, do_undos; | |
641 | ||
642 | #ifdef SEM_DEBUG | |
643 | printf("call to semop(%d, 0x%x, %d)\n", semid, sops, nsops); | |
644 | #endif | |
645 | ||
646 | semid = IPCID_TO_IX(semid); /* Convert back to zero origin */ | |
647 | ||
648 | if (semid < 0 || semid >= seminfo.semmsl) | |
649 | return(EINVAL); | |
650 | ||
651 | semaptr = &sema[semid]; | |
652 | if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) | |
653 | return(EINVAL); | |
654 | if (semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) | |
655 | return(EINVAL); | |
656 | ||
657 | if ((eval = ipcperm(cred, &semaptr->sem_perm, IPC_W))) { | |
658 | #ifdef SEM_DEBUG | |
659 | printf("eval = %d from ipaccess\n", eval); | |
660 | #endif | |
661 | return(eval); | |
662 | } | |
663 | ||
664 | if (nsops > MAX_SOPS) { | |
665 | #ifdef SEM_DEBUG | |
666 | printf("too many sops (max=%d, nsops=%d)\n", MAX_SOPS, nsops); | |
667 | #endif | |
668 | return(E2BIG); | |
669 | } | |
670 | ||
671 | if ((eval = copyin(uap->sops, &sops, nsops * sizeof(sops[0]))) != 0) { | |
672 | #ifdef SEM_DEBUG | |
673 | printf("eval = %d from copyin(%08x, %08x, %d)\n", eval, | |
674 | uap->sops, &sops, nsops * sizeof(sops[0])); | |
675 | #endif | |
676 | return(eval); | |
677 | } | |
678 | ||
679 | /* | |
680 | * Loop trying to satisfy the vector of requests. | |
681 | * If we reach a point where we must wait, any requests already | |
682 | * performed are rolled back and we go to sleep until some other | |
683 | * process wakes us up. At this point, we start all over again. | |
684 | * | |
685 | * This ensures that from the perspective of other tasks, a set | |
686 | * of requests is atomic (never partially satisfied). | |
687 | */ | |
688 | do_undos = 0; | |
689 | ||
690 | for (;;) { | |
691 | do_wakeup = 0; | |
692 | ||
693 | for (i = 0; i < nsops; i++) { | |
694 | sopptr = &sops[i]; | |
695 | ||
696 | if (sopptr->sem_num >= semaptr->sem_nsems) | |
697 | return(EFBIG); | |
698 | ||
699 | semptr = &semaptr->sem_base[sopptr->sem_num]; | |
700 | ||
701 | #ifdef SEM_DEBUG | |
702 | printf("semop: semaptr=%x, sem_base=%x, semptr=%x, sem[%d]=%d : op=%d, flag=%s\n", | |
703 | semaptr, semaptr->sem_base, semptr, | |
704 | sopptr->sem_num, semptr->semval, sopptr->sem_op, | |
705 | (sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait"); | |
706 | #endif | |
707 | ||
708 | if (sopptr->sem_op < 0) { | |
709 | if (semptr->semval + sopptr->sem_op < 0) { | |
710 | #ifdef SEM_DEBUG | |
711 | printf("semop: can't do it now\n"); | |
712 | #endif | |
713 | break; | |
714 | } else { | |
715 | semptr->semval += sopptr->sem_op; | |
716 | if (semptr->semval == 0 && | |
717 | semptr->semzcnt > 0) | |
718 | do_wakeup = 1; | |
719 | } | |
720 | if (sopptr->sem_flg & SEM_UNDO) | |
721 | do_undos = 1; | |
722 | } else if (sopptr->sem_op == 0) { | |
723 | if (semptr->semval > 0) { | |
724 | #ifdef SEM_DEBUG | |
725 | printf("semop: not zero now\n"); | |
726 | #endif | |
727 | break; | |
728 | } | |
729 | } else { | |
730 | if (semptr->semncnt > 0) | |
731 | do_wakeup = 1; | |
732 | semptr->semval += sopptr->sem_op; | |
733 | if (sopptr->sem_flg & SEM_UNDO) | |
734 | do_undos = 1; | |
735 | } | |
736 | } | |
737 | ||
738 | /* | |
739 | * Did we get through the entire vector? | |
740 | */ | |
741 | if (i >= nsops) | |
742 | goto done; | |
743 | ||
744 | /* | |
745 | * No ... rollback anything that we've already done | |
746 | */ | |
747 | #ifdef SEM_DEBUG | |
748 | printf("semop: rollback 0 through %d\n", i-1); | |
749 | #endif | |
750 | for (j = 0; j < i; j++) | |
751 | semaptr->sem_base[sops[j].sem_num].semval -= | |
752 | sops[j].sem_op; | |
753 | ||
754 | /* | |
755 | * If the request that we couldn't satisfy has the | |
756 | * NOWAIT flag set then return with EAGAIN. | |
757 | */ | |
758 | if (sopptr->sem_flg & IPC_NOWAIT) | |
759 | return(EAGAIN); | |
760 | ||
761 | if (sopptr->sem_op == 0) | |
762 | semptr->semzcnt++; | |
763 | else | |
764 | semptr->semncnt++; | |
765 | ||
766 | #ifdef SEM_DEBUG | |
767 | printf("semop: good night!\n"); | |
768 | #endif | |
769 | eval = tsleep((caddr_t)semaptr, (PZERO - 4) | PCATCH, | |
770 | "semwait", 0); | |
771 | #ifdef SEM_DEBUG | |
772 | printf("semop: good morning (eval=%d)!\n", eval); | |
773 | #endif | |
774 | ||
775 | suptr = NULL; /* sem_undo may have been reallocated */ | |
776 | ||
777 | if (eval != 0) | |
778 | return(EINTR); | |
779 | #ifdef SEM_DEBUG | |
780 | printf("semop: good morning!\n"); | |
781 | #endif | |
782 | ||
783 | /* | |
784 | * Make sure that the semaphore still exists | |
785 | */ | |
786 | if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0 || | |
787 | semaptr->sem_perm.seq != IPCID_TO_SEQ(uap->semid)) { | |
788 | /* The man page says to return EIDRM. */ | |
789 | /* Unfortunately, BSD doesn't define that code! */ | |
790 | #ifdef EIDRM | |
791 | return(EIDRM); | |
792 | #else | |
793 | return(EINVAL); | |
794 | #endif | |
795 | } | |
796 | ||
797 | /* | |
798 | * The semaphore is still alive. Readjust the count of | |
799 | * waiting processes. | |
800 | */ | |
801 | if (sopptr->sem_op == 0) | |
802 | semptr->semzcnt--; | |
803 | else | |
804 | semptr->semncnt--; | |
805 | } | |
806 | ||
807 | done: | |
808 | /* | |
809 | * Process any SEM_UNDO requests. | |
810 | */ | |
811 | if (do_undos) { | |
812 | for (i = 0; i < nsops; i++) { | |
813 | /* | |
814 | * We only need to deal with SEM_UNDO's for non-zero | |
815 | * op's. | |
816 | */ | |
817 | int adjval; | |
818 | ||
819 | if ((sops[i].sem_flg & SEM_UNDO) == 0) | |
820 | continue; | |
821 | adjval = sops[i].sem_op; | |
822 | if (adjval == 0) | |
823 | continue; | |
824 | eval = semundo_adjust(p, &suptr, semid, | |
825 | sops[i].sem_num, -adjval); | |
826 | if (eval == 0) | |
827 | continue; | |
828 | ||
829 | /* | |
830 | * Oh-Oh! We ran out of either sem_undo's or undo's. | |
831 | * Rollback the adjustments to this point and then | |
832 | * rollback the semaphore ups and down so we can return | |
833 | * with an error with all structures restored. We | |
834 | * rollback the undo's in the exact reverse order that | |
835 | * we applied them. This guarantees that we won't run | |
836 | * out of space as we roll things back out. | |
837 | */ | |
838 | for (j = i - 1; j >= 0; j--) { | |
839 | if ((sops[j].sem_flg & SEM_UNDO) == 0) | |
840 | continue; | |
841 | adjval = sops[j].sem_op; | |
842 | if (adjval == 0) | |
843 | continue; | |
844 | if (semundo_adjust(p, &suptr, semid, | |
845 | sops[j].sem_num, adjval) != 0) | |
846 | panic("semop - can't undo undos"); | |
847 | } | |
848 | ||
849 | for (j = 0; j < nsops; j++) | |
850 | semaptr->sem_base[sops[j].sem_num].semval -= | |
851 | sops[j].sem_op; | |
852 | ||
853 | #ifdef SEM_DEBUG | |
854 | printf("eval = %d from semundo_adjust\n", eval); | |
855 | #endif | |
856 | return(eval); | |
857 | } /* loop through the sops */ | |
858 | } /* if (do_undos) */ | |
859 | ||
860 | /* We're definitely done - set the sempid's */ | |
861 | for (i = 0; i < nsops; i++) { | |
862 | sopptr = &sops[i]; | |
863 | semptr = &semaptr->sem_base[sopptr->sem_num]; | |
864 | semptr->sempid = p->p_pid; | |
865 | } | |
866 | ||
867 | /* Do a wakeup if any semaphore was up'd. */ | |
868 | if (do_wakeup) { | |
869 | #ifdef SEM_DEBUG | |
870 | printf("semop: doing wakeup\n"); | |
871 | #ifdef SEM_WAKEUP | |
872 | sem_wakeup((caddr_t)semaptr); | |
873 | #else | |
874 | wakeup((caddr_t)semaptr); | |
875 | #endif | |
876 | printf("semop: back from wakeup\n"); | |
877 | #else | |
878 | wakeup((caddr_t)semaptr); | |
879 | #endif | |
880 | } | |
881 | #ifdef SEM_DEBUG | |
882 | printf("semop: done\n"); | |
883 | #endif | |
884 | p->p_retval[0] = 0; | |
885 | return(0); | |
886 | } | |
887 | ||
888 | /* | |
889 | * Go through the undo structures for this process and apply the adjustments to | |
890 | * semaphores. | |
891 | */ | |
892 | void | |
893 | semexit(p) | |
894 | struct proc *p; | |
895 | { | |
896 | register struct sem_undo *suptr; | |
897 | register struct sem_undo **supptr; | |
898 | int did_something; | |
899 | ||
900 | /* | |
901 | * If somebody else is holding the global semaphore facility lock | |
902 | * then sleep until it is released. | |
903 | */ | |
904 | while (semlock_holder != NULL && semlock_holder != p) { | |
905 | #ifdef SEM_DEBUG | |
906 | printf("semaphore facility locked - sleeping ...\n"); | |
907 | #endif | |
908 | (void) tsleep((caddr_t)&semlock_holder, (PZERO - 4), "semext", 0); | |
909 | } | |
910 | ||
911 | did_something = 0; | |
912 | ||
913 | /* | |
914 | * Go through the chain of undo vectors looking for one | |
915 | * associated with this process. | |
916 | */ | |
917 | ||
918 | for (supptr = &semu_list; (suptr = *supptr) != NULL; | |
919 | supptr = &suptr->un_next) { | |
920 | if (suptr->un_proc == p) | |
921 | break; | |
922 | } | |
923 | ||
924 | if (suptr == NULL) | |
925 | goto unlock; | |
926 | ||
927 | #ifdef SEM_DEBUG | |
928 | printf("proc @%08x has undo structure with %d entries\n", p, | |
929 | suptr->un_cnt); | |
930 | #endif | |
931 | ||
932 | /* | |
933 | * If there are any active undo elements then process them. | |
934 | */ | |
935 | if (suptr->un_cnt > 0) { | |
936 | int ix; | |
937 | ||
938 | for (ix = 0; ix < suptr->un_cnt; ix++) { | |
939 | int semid = suptr->un_ent[ix].un_id; | |
940 | int semnum = suptr->un_ent[ix].un_num; | |
941 | int adjval = suptr->un_ent[ix].un_adjval; | |
942 | struct semid_ds *semaptr; | |
943 | ||
944 | semaptr = &sema[semid]; | |
945 | if ((semaptr->sem_perm.mode & SEM_ALLOC) == 0) | |
946 | panic("semexit - semid not allocated"); | |
947 | if (semnum >= semaptr->sem_nsems) | |
948 | panic("semexit - semnum out of range"); | |
949 | ||
950 | #ifdef SEM_DEBUG | |
951 | printf("semexit: %08x id=%d num=%d(adj=%d) ; sem=%d\n", | |
952 | suptr->un_proc, suptr->un_ent[ix].un_id, | |
953 | suptr->un_ent[ix].un_num, | |
954 | suptr->un_ent[ix].un_adjval, | |
955 | semaptr->sem_base[semnum].semval); | |
956 | #endif | |
957 | ||
958 | if (adjval < 0) { | |
959 | if (semaptr->sem_base[semnum].semval < -adjval) | |
960 | semaptr->sem_base[semnum].semval = 0; | |
961 | else | |
962 | semaptr->sem_base[semnum].semval += | |
963 | adjval; | |
964 | } else | |
965 | semaptr->sem_base[semnum].semval += adjval; | |
966 | ||
967 | #ifdef SEM_WAKEUP | |
968 | sem_wakeup((caddr_t)semaptr); | |
969 | #else | |
970 | wakeup((caddr_t)semaptr); | |
971 | #endif | |
972 | #ifdef SEM_DEBUG | |
973 | printf("semexit: back from wakeup\n"); | |
974 | #endif | |
975 | } | |
976 | } | |
977 | ||
978 | /* | |
979 | * Deallocate the undo vector. | |
980 | */ | |
981 | #ifdef SEM_DEBUG | |
982 | printf("removing vector\n"); | |
983 | #endif | |
984 | suptr->un_proc = NULL; | |
985 | *supptr = suptr->un_next; | |
986 | ||
987 | unlock: | |
988 | /* | |
989 | * If the exiting process is holding the global semaphore facility | |
990 | * lock then release it. | |
991 | */ | |
992 | if (semlock_holder == p) { | |
993 | semlock_holder = NULL; | |
994 | wakeup((caddr_t)&semlock_holder); | |
995 | } | |
996 | } |