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