xref: /dragonfly/lib/libc/sysvipc/sem.c (revision ec21d9fb)
1 /* $FreeBSD: src/sys/kern/sysv_sem.c,v 1.69 2004/03/17 09:37:13 cperciva Exp $ */
2 
3 /*
4  * Implementation of SVID semaphores
5  *
6  * Author:  Daniel Boulet
7  * Copyright (c) 2013 Larisa Grigore <larisagrigore@gmail.com>
8  *
9  * This software is provided ``AS IS'' without any warranties of any kind.
10  */
11 
12 #include "namespace.h"
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <errno.h>
16 #include <err.h>
17 #include <pthread.h>
18 #include <string.h>
19 #include <stdarg.h>
20 #include <sys/param.h>
21 #include <sys/queue.h>
22 #include <sys/mman.h>
23 #include <sys/sem.h>
24 #include "un-namespace.h"
25 
26 #include "sysvipc_lock.h"
27 #include "sysvipc_ipc.h"
28 #include "sysvipc_shm.h"
29 #include "sysvipc_sem.h"
30 #include "sysvipc_hash.h"
31 
32 
33 #define SYSV_MUTEX_LOCK(x)	if (__isthreaded) _pthread_mutex_lock(x)
34 #define SYSV_MUTEX_UNLOCK(x)	if (__isthreaded) _pthread_mutex_unlock(x)
35 #define SYSV_MUTEX_DESTROY(x)	if (__isthreaded) _pthread_mutex_destroy(x)
36 
37 extern struct hashtable *shmaddrs;
38 extern struct hashtable *shmres;
39 extern pthread_mutex_t lock_resources;
40 
41 struct sem_undo *undos = NULL;
42 pthread_mutex_t lock_undo = PTHREAD_MUTEX_INITIALIZER;
43 
44 static int semundo_clear(int, int);
45 
46 static int
47 put_shmdata(int id)
48 {
49 	struct shm_data *data;
50 	int ret = -1;
51 
52 	SYSV_MUTEX_LOCK(&lock_resources);
53 	data = _hash_lookup(shmres, id);
54 	if (!data) {
55 		sysv_print_err("something wrong put_shmdata\n");
56 		goto done; /* It should not reach here. */
57 	}
58 
59 	data->used--;
60 	if (data->used == 0 && data->removed) {
61 		sysv_print("really remove the sem\n");
62 		SYSV_MUTEX_UNLOCK(&lock_resources);
63 		/* OBS: Even if the shmctl fails (the thread doesn't
64 		 * have IPC_M permissions), all structures associated
65 		 * with it will be removed in the current process.*/
66 		sysvipc_shmdt(data->internal);
67 		semundo_clear(id, -1);
68 		if (data->removed == SEG_ALREADY_REMOVED)
69 			return 1; /* The semaphore was removed
70 			by another process so there is nothing else
71 			we must do. */
72 		/* Else inform the daemon that the segment is removed. */
73 		return (sysvipc_shmctl(id, IPC_RMID, NULL));
74 	}
75 
76 	ret = 0;
77 done:
78 	SYSV_MUTEX_UNLOCK(&lock_resources);
79 	return (ret);
80 }
81 
82 static struct semid_pool *
83 get_semaptr(int semid, int to_remove, int shm_access)
84 {
85 	struct semid_pool *semaptr;
86 
87 	struct shm_data *shmdata = get_shmdata(semid, to_remove, shm_access);
88 	if (!shmdata) {
89 		/* Error is set in get_shmdata. */
90 		return (NULL);
91 	}
92 
93 	semaptr = (struct semid_pool *)shmdata->internal;
94 	if (!semaptr) {
95 		put_shmdata(semid);
96 		errno = EINVAL;
97 		return (NULL);
98 	}
99 
100 	return (semaptr);
101 }
102 
103 static int
104 sema_exist(int semid, struct semid_pool *semaptr)
105 {
106 	/* Was it removed? */
107 	if (semaptr->gen == -1 ||
108 			semaptr->ds.sem_perm.seq != IPCID_TO_SEQ(semid))
109 		return (0);
110 
111 	return (1);
112 }
113 
114 /* This is the function called when a the semaphore
115  * is descovered as removed. It marks the process
116  * internal data and munmap the */
117 static void
118 mark_for_removal(int shmid)
119 {
120 	sysv_print("Mark that the segment was removed\n");
121 	get_shmdata(shmid, SEG_ALREADY_REMOVED, 0);
122 	 /* Setting SEG_ALREADY_REMOVED parameter, when put_shmdata
123 	  * is called, the internal resources will be freed.
124 	  */
125 	/* Decrement the "usage" field. */
126 	put_shmdata(shmid);
127 }
128 
129 static int
130 try_rwlock_rdlock(int semid, struct semid_pool *semaptr)
131 {
132 	sysv_print(" before rd lock id = %d %p\n", semid, semaptr);
133 #ifdef SYSV_RWLOCK
134 	sysv_rwlock_rdlock(&semaptr->rwlock);
135 	sysv_print("rd lock id = %d\n", semid);
136 #else
137 	sysv_mutex_lock(&semaptr->mutex);
138 	sysv_print("lock id = %d\n", semid);
139 #endif
140 	if (!sema_exist(semid, semaptr)) {
141 		errno = EINVAL;
142 		sysv_print("error sema %d doesn't exist\n", semid);
143 #ifdef SYSV_RWLOCK
144 		sysv_rwlock_unlock(&semaptr->rwlock);
145 #else
146 		sysv_mutex_unlock(&semaptr->mutex);
147 #endif
148 		/* Internal resources must be freed. */
149 		mark_for_removal(semid);
150 		return (-1);
151 	}
152 	return (0);
153 }
154 
155 static int
156 try_rwlock_wrlock(int semid, struct semid_pool *semaptr)
157 {
158 #ifdef SYSV_RWLOCK
159 	sysv_print("before wrlock id = %d %p\n", semid, semaptr);
160 	sysv_rwlock_wrlock(&semaptr->rwlock);
161 #else
162 	sysv_print("before lock id = %d %x\n", semid, semaptr);
163 	sysv_mutex_lock(&semaptr->mutex);
164 #endif
165 	sysv_print("lock id = %d\n", semid);
166 	if (!sema_exist(semid, semaptr)) {
167 		errno = EINVAL;
168 		sysv_print("error sema %d doesn't exist\n", semid);
169 #ifdef SYSV_RWLOCK
170 		sysv_rwlock_unlock(&semaptr->rwlock);
171 #else
172 		sysv_mutex_unlock(&semaptr->mutex);
173 #endif
174 		/* Internal resources must be freed. */
175 		mark_for_removal(semid);
176 		return (-1);
177 	}
178 	return (0);
179 }
180 
181 static int
182 rwlock_unlock(int semid, struct semid_pool *semaptr)
183 {
184 	sysv_print("unlock id = %d %p\n", semid, semaptr);
185 	if (!sema_exist(semid, semaptr)) {
186 		/* Internal resources must be freed. */
187 		mark_for_removal(semid);
188 		errno = EINVAL;
189 		return (-1);
190 	}
191 #ifdef SYSV_RWLOCK
192 	sysv_rwlock_unlock(&semaptr->rwlock);
193 #else
194 	sysv_mutex_unlock(&semaptr->mutex);
195 #endif
196 	return (0);
197 }
198 
199 int
200 sysvipc_semget(key_t key, int nsems, int semflg)
201 {
202 	int semid;
203 	void *shmaddr;
204 	//int shm_access;
205 	int size = sizeof(struct semid_pool) + nsems * sizeof(struct sem);
206 
207 	//TODO resources limits
208 	sysv_print("handle semget\n");
209 
210 	semid = _shmget(key, size, semflg, SEMGET);
211 	if (semid == -1) {
212 		/* errno already set. */
213 		goto done;
214 	}
215 
216 	/* If the semaphore is in process of being removed there are two cases:
217 	 * - the daemon knows that and it will handle this situation.
218 	 * - one of the threads from this address space remove it and the daemon
219 	 *   wasn't announced yet; in this scenario, the semaphore is marked
220 	 *   using "removed" field of shm_data and future calls will return
221 	 *   EIDRM error.
222 	 */
223 
224 #if 0
225 	/* Set access type. */
226 	shm_access = semflg & (IPC_W | IPC_R);
227 	if(set_shmdata_access(semid, shm_access) != 0) {
228 		/* errno already set. */
229 		goto done;
230 	}
231 #endif
232 	shmaddr = sysvipc_shmat(semid, NULL, 0);
233 	if (!shmaddr) {
234 		semid = -1;
235 		sysvipc_shmctl(semid, IPC_RMID, NULL);
236 		goto done;
237 	}
238 
239 	//TODO more semaphores in a single file
240 
241 done:
242 	sysv_print("end handle semget %d\n", semid);
243 	return (semid);
244 }
245 
246 static int
247 semundo_clear(int semid, int semnum)
248 {
249 	struct undo *sunptr;
250 	int i;
251 
252 	sysv_print("semundo clear\n");
253 
254 	SYSV_MUTEX_LOCK(&lock_undo);
255 	if (!undos)
256 		goto done;
257 
258 	sunptr = &undos->un_ent[0];
259 	i = 0;
260 
261 	while (i < undos->un_cnt) {
262 		if (sunptr->un_id == semid) {
263 			if (semnum == -1 || sunptr->un_num == semnum) {
264 				undos->un_cnt--;
265 				if (i < undos->un_cnt) {
266 					undos->un_ent[i] =
267 					  undos->un_ent[undos->un_cnt];
268 					continue;
269 				}
270 			}
271 			if (semnum != -1)
272 				break;
273 		}
274 		++i;
275 		++sunptr;
276 	}
277 
278 	//TODO Shrink memory if case; not sure if necessary
279 done:
280 	SYSV_MUTEX_UNLOCK(&lock_undo);
281 	sysv_print("end semundo clear\n");
282 	return (0);
283 }
284 
285 int
286 sysvipc___semctl(int semid, int semnum, int cmd, union semun *arg)
287 {
288 	int i, error;
289 	struct semid_pool *semaptr = NULL;
290 	struct sem *semptr = NULL;
291 	struct shmid_ds shmds;
292 	int shm_access = 0;
293 
294 	/*if (!jail_sysvipc_allowed && cred->cr_prison != NULL)
295 		return (ENOSYS);
296 */
297 
298 	sysv_print("semctl cmd = %d\n", cmd);
299 
300 	error = 0;
301 
302 	switch (cmd) {
303 		case IPC_SET: /* Originally was IPC_M but this is checked
304 				 by daemon. */
305 		case SETVAL:
306 		case SETALL:
307 			shm_access = IPC_W;
308 			break;
309 		case IPC_STAT:
310 		case GETNCNT:
311 		case GETPID:
312 		case GETVAL:
313 		case GETALL:
314 		case GETZCNT:
315 			shm_access = IPC_R;
316 			break;
317 		default:
318 			break;
319 	}
320 
321 	semaptr = get_semaptr(semid, cmd==IPC_RMID, shm_access);
322 	if (!semaptr) {
323 		/* errno already set. */
324 		return (-1);
325 	}
326 
327 	switch (cmd) {
328 	case IPC_RMID:
329 		/* Mark that the segment is removed. This is done in
330 		 * get_semaptr call in order to announce other processes.
331 		 * It will be actually removed after put_shmdata call and
332 		 * not other thread from this address space use shm_data
333 		 * structure.
334 		 */
335 		break;
336 
337 	case IPC_SET:
338 		if (!arg->buf) {
339 			error = EFAULT;
340 			break;
341 		}
342 
343 		memset(&shmds, 0, sizeof(shmds)/sizeof(unsigned char));
344 		memcpy(&shmds.shm_perm, &arg->buf->sem_perm,
345 				sizeof(struct ipc_perm));
346 		error = sysvipc_shmctl(semid, cmd, &shmds);
347 		/* OBS: didn't update ctime and mode as in kernel implementation
348 		 * it is done. Those fields are already updated for shmid_ds
349 		 * struct when calling shmctl
350 		 */
351 		break;
352 
353 	case IPC_STAT:
354 		if (!arg->buf) {
355 			error = EFAULT;
356 			break;
357 		}
358 
359 		error = sysvipc_shmctl(semid, cmd, &shmds);
360 		if (error)
361 			break;
362 
363 		memcpy(&arg->buf->sem_perm, &shmds.shm_perm,
364 				sizeof(struct ipc_perm));
365 		arg->buf->sem_nsems = (shmds.shm_segsz - sizeof(struct semid_pool)) /
366 			sizeof(struct sem);
367 		arg->buf->sem_ctime = shmds.shm_ctime;
368 
369 		/* otime is semaphore specific so read it from
370 		 * semaptr
371 		 */
372 		error = try_rwlock_rdlock(semid, semaptr);
373 		if (error)
374 			break;
375 		arg->buf->sem_otime = semaptr->ds.sem_otime;
376 		rwlock_unlock(semid, semaptr);
377 		break;
378 
379 	case GETNCNT:
380 		if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) {
381 			errno = EINVAL;
382 			break;
383 		}
384 
385 		error = try_rwlock_rdlock(semid, semaptr);
386 		if (error)
387 			break;
388 		error = semaptr->ds.sem_base[semnum].semncnt;
389 		rwlock_unlock(semid, semaptr);
390 		break;
391 
392 	case GETPID:
393 		if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) {
394 			errno = EINVAL;
395 			break;
396 		}
397 
398 		error = try_rwlock_rdlock(semid, semaptr);
399 		if (error)
400 			break;
401 		error = semaptr->ds.sem_base[semnum].sempid;
402 		rwlock_unlock(semid, semaptr);
403 		break;
404 
405 	case GETVAL:
406 		if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) {
407 			errno = EINVAL;
408 			break;
409 		}
410 
411 		error = try_rwlock_rdlock(semid, semaptr);
412 		if (error)
413 			break;
414 		error = semaptr->ds.sem_base[semnum].semval;
415 		rwlock_unlock(semid, semaptr);
416 		break;
417 
418 	case GETALL:
419 		if (!arg->array) {
420 			error = EFAULT;
421 			break;
422 		}
423 
424 		error = try_rwlock_rdlock(semid, semaptr);
425 		if (error)
426 			break;
427 		for (i = 0; i < semaptr->ds.sem_nsems; i++) {
428 			arg->array[i] = semaptr->ds.sem_base[i].semval;
429 		}
430 		rwlock_unlock(semid, semaptr);
431 		break;
432 
433 	case GETZCNT:
434 		if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) {
435 			errno = EINVAL;
436 			break;
437 		}
438 
439 		error = try_rwlock_rdlock(semid, semaptr);
440 		if (error)
441 			break;
442 		error = semaptr->ds.sem_base[semnum].semzcnt;
443 		rwlock_unlock(semid, semaptr);
444 		break;
445 
446 	case SETVAL:
447 		if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) {
448 			errno = EINVAL;
449 			break;
450 		}
451 
452 		error = try_rwlock_wrlock(semid, semaptr);
453 		if (error)
454 			break;
455 		semptr = &semaptr->ds.sem_base[semnum];
456 		semptr->semval = arg->val;
457 		semundo_clear(semid, semnum);
458 		if (semptr->semzcnt || semptr->semncnt)
459 			umtx_wakeup((int *)&semptr->semval, 0);
460 		rwlock_unlock(semid, semaptr);
461 		break;
462 
463 	case SETALL:
464 		if (!arg->array) {
465 			error = EFAULT;
466 			break;
467 		}
468 
469 		error = try_rwlock_wrlock(semid, semaptr);
470 		if (error)
471 			break;
472 		for (i = 0; i < semaptr->ds.sem_nsems; i++) {
473 			semptr = &semaptr->ds.sem_base[i];
474 			semptr->semval = arg->array[i];
475 			if (semptr->semzcnt || semptr->semncnt)
476 				umtx_wakeup((int *)&semptr->semval, 0);
477 		}
478 		semundo_clear(semid, -1);
479 		rwlock_unlock(semid, semaptr);
480 		break;
481 
482 	default:
483 		errno = EINVAL;
484 		break;
485 	}
486 
487 	put_shmdata(semid);
488 
489 	sysv_print("end semctl\n");
490 	return (error);
491 }
492 
493 /*
494  * Adjust a particular entry for a particular proc
495  */
496 static int
497 semundo_adjust(int semid, int semnum, int adjval)
498 {
499 	struct undo *sunptr;
500 	int i;
501 	int error = 0;
502 	size_t size;
503 	int undoid;
504 	void *addr;
505 	struct shm_data *data;
506 
507 	sysv_print("semundo adjust\n");
508 	if (!adjval)
509 		goto done;
510 
511 	SYSV_MUTEX_LOCK(&lock_undo);
512 	if (!undos) {
513 		sysv_print("get undo segment\n");
514 		undoid = _shmget(IPC_PRIVATE, PAGE_SIZE, IPC_CREAT | IPC_EXCL | 0600,
515 				UNDOGET);
516 		if (undoid == -1) {
517 			sysv_print_err("no undo segment\n");
518 			return (-1);
519 		}
520 
521 		addr = sysvipc_shmat(undoid, NULL, 0);
522 		if (!addr) {
523 			sysv_print_err("can not map undo segment\n");
524 			sysvipc_shmctl(undoid, IPC_RMID, NULL);
525 			return (-1);
526 		}
527 
528 		undos = (struct sem_undo *)addr;
529 		undos->un_pages = 1;
530 		undos->un_cnt = 0;
531 	}
532 
533 	/*
534 	 * Look for the requested entry and adjust it (delete if adjval becomes
535 	 * 0).
536 	 */
537 	sunptr = &undos->un_ent[0];
538 	for (i = 0; i < undos->un_cnt; i++, sunptr++) {
539 		if (sunptr->un_id != semid && sunptr->un_num != semnum)
540 			continue;
541 		sunptr->un_adjval += adjval;
542 		if (sunptr->un_adjval == 0) {
543 			undos->un_cnt--;
544 			if (i < undos->un_cnt)
545 				undos->un_ent[i] = undos->un_ent[undos->un_cnt];
546 		}
547 		goto done;
548 	}
549 
550 	/* Didn't find the right entry - create it */
551 	size = sizeof(struct sem_undo) + (undos->un_cnt + 1) *
552 		sizeof(struct sem_undo);
553 	if (size > (unsigned int)(undos->un_pages * PAGE_SIZE)) {
554 		sysv_print("need more undo space\n");
555 		sysvipc_shmdt(undos);
556 		undos->un_pages++;
557 
558 		SYSV_MUTEX_LOCK(&lock_resources);
559 		data = _hash_lookup(shmaddrs, (u_long)undos);
560 		SYSV_MUTEX_UNLOCK(&lock_resources);
561 
562 		/* It is not necessary any lock on "size" because it is used
563 		 * only by shmat and shmdt.
564 		 * shmat for undoid is called only from this function and it
565 		 * is protected by undo_lock.
566 		 * shmdt for undoid is not called anywhere because the segment
567 		 * is destroyed by the daemon when the client dies.
568 		 */
569 		data->size = undos->un_pages * PAGE_SIZE;
570 		undos = sysvipc_shmat(data->shmid, NULL, 0);
571 	}
572 
573 	sunptr = &undos->un_ent[undos->un_cnt];
574 	undos->un_cnt++;
575 	sunptr->un_adjval = adjval;
576 	sunptr->un_id = semid;
577 	sunptr->un_num = semnum;
578 	//if (suptr->un_cnt == seminfo.semume) TODO move it in daemon
579 	/*} else {
580 	  error = EINVAL; //se face prin notificare
581 	  }*/
582 done:
583 	SYSV_MUTEX_UNLOCK(&lock_undo);
584 
585 	sysv_print("semundo adjust end\n");
586 	return (error);
587 }
588 
589 int
590 sysvipc_semop(int semid, struct sembuf *sops, unsigned nsops)
591 {
592 	struct semid_pool *semaptr = NULL, *auxsemaptr = NULL;
593 	struct sembuf *sopptr;
594 	struct sem *semptr = NULL;
595 	struct sem *xsemptr = NULL;
596 	int eval = 0;
597 	int i, j;
598 	int do_undos;
599 	int val_to_sleep;
600 
601 	sysv_print("[client %d] call to semop(%d, %u)\n",
602 			getpid(), semid, nsops);
603 //TODO
604 	/*if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
605 	  return (ENOSYS);
606 	  */
607 
608 	semaptr = get_semaptr(semid, 0, IPC_W);
609 	if (!semaptr) {
610 		errno = EINVAL;
611 		return (-1);
612 	}
613 
614 #ifdef SYSV_SEMS
615 	if (try_rwlock_rdlock(semid, semaptr) == -1) {
616 #else
617 	if (try_rwlock_wrlock(semid, semaptr) == -1) {
618 #endif
619 		sysv_print("sema removed\n");
620 		errno = EIDRM;
621 		goto done2;
622 	}
623 
624 	if (nsops > MAX_SOPS) {
625 		sysv_print("too many sops (max=%d, nsops=%u)\n",
626 		    MAX_SOPS, nsops);
627 		eval = E2BIG;
628 		goto done;
629 	}
630 
631 	/*
632 	* Loop trying to satisfy the vector of requests.
633 	* If we reach a point where we must wait, any requests already
634 	* performed are rolled back and we go to sleep until some other
635 	* process wakes us up.  At this point, we start all over again.
636 	*
637 	* This ensures that from the perspective of other tasks, a set
638 	* of requests is atomic (never partially satisfied).
639 	*/
640 	do_undos = 0;
641 
642 	for (;;) {
643 
644 		semptr = NULL;
645 
646 		for (i = 0; i < (int)nsops; i++) {
647 			sopptr = &sops[i];
648 
649 			if (sopptr->sem_num >= semaptr->ds.sem_nsems) {
650 				eval = EFBIG;
651 				goto done;
652 			}
653 
654 			semptr = &semaptr->ds.sem_base[sopptr->sem_num];
655 #ifdef SYSV_SEMS
656 			sysv_mutex_lock(&semptr->sem_mutex);
657 #endif
658 			sysv_print("semop: sem[%d]=%d : op=%d, flag=%s\n",
659 				sopptr->sem_num, semptr->semval, sopptr->sem_op,
660 				(sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait");
661 
662 			if (sopptr->sem_op < 0) {
663 				if (semptr->semval + sopptr->sem_op < 0) {
664 					sysv_print("semop:  can't do it now\n");
665 					break;
666 				} else {
667 					semptr->semval += sopptr->sem_op;
668 					if (semptr->semval == 0 &&
669 						semptr->semzcnt > 0)
670 						umtx_wakeup((int *)&semptr->semval, 0);
671 				}
672 				if (sopptr->sem_flg & SEM_UNDO)
673 					do_undos = 1;
674 			} else if (sopptr->sem_op == 0) {
675 				if (semptr->semval > 0) {
676 					sysv_print("semop:  not zero now\n");
677 					break;
678 				}
679 			} else {
680 				semptr->semval += sopptr->sem_op;
681 				if (sopptr->sem_flg & SEM_UNDO)
682 					do_undos = 1;
683 				if (semptr->semncnt > 0)
684 					umtx_wakeup((int *)&semptr->semval, 0);
685 			}
686 #ifdef SYSV_SEMS
687 			sysv_mutex_unlock(&semptr->sem_mutex);
688 #endif
689 		}
690 
691 		/*
692 		 * Did we get through the entire vector?
693 		 */
694 		if (i >= (int)nsops)
695 			goto donex;
696 
697 		if (sopptr->sem_op == 0)
698 			semptr->semzcnt++;
699 		else
700 			semptr->semncnt++;
701 
702 		/*
703 		 * Get interlock value before rleeasing sem_mutex.
704 		 *
705 		 * XXX horrible hack until we get a umtx_sleep16() (and a umtx_sleep64())
706 		 * system call.
707 		 */
708 		val_to_sleep = *(int *)&semptr->semval;
709 #ifdef SYSV_SEMS
710 		sysv_mutex_unlock(&semptr->sem_mutex);
711 #endif
712 		/*
713 		 * Rollback the semaphores we had acquired.
714 		 */
715 		sysv_print("semop:  rollback 0 through %d\n", i-1);
716 		for (j = 0; j < i; j++) {
717 			xsemptr = &semaptr->ds.sem_base[sops[j].sem_num];
718 #ifdef SYSV_SEMS
719 			sysv_mutex_lock(&xsemptr->sem_mutex);
720 #endif
721 			xsemptr->semval -= sops[j].sem_op;
722 			if (xsemptr->semval == 0 && xsemptr->semzcnt > 0)
723 				umtx_wakeup((int *)&xsemptr->semval, 0);
724 			if (xsemptr->semval <= 0 && xsemptr->semncnt > 0)
725 				umtx_wakeup((int *)&xsemptr->semval, 0); //?!
726 #ifdef SYSV_SEMS
727 			sysv_mutex_unlock(&xsemptr->sem_mutex);
728 #endif
729 		}
730 
731 		/*
732 		 * If the request that we couldn't satisfy has the
733 		 * NOWAIT flag set then return with EAGAIN.
734 		 */
735 		if (sopptr->sem_flg & IPC_NOWAIT) {
736 			eval = EAGAIN;
737 			goto done;
738 		}
739 
740 		/*
741 		 * Release semaptr->lock while sleeping, allowing other
742 		 * semops (like SETVAL, SETALL, etc), which require an
743 		 * exclusive lock and might wake us up.
744 		 *
745 		 * Reload and recheck the validity of semaptr on return.
746 		 * Note that semptr itself might have changed too, but
747 		 * we've already interlocked for semptr and that is what
748 		 * will be woken up if it wakes up the tsleep on a MP
749 		 * race.
750 		 *
751 		 */
752 		sysv_print("semop:  good night!\n");
753 		rwlock_unlock(semid, semaptr);
754 		put_shmdata(semid);
755 
756 		/* We don't sleep more than SYSV_TIMEOUT because we could
757 		 * go to sleep after another process calls wakeup and remain
758 		 * blocked.
759 		 */
760 		eval = umtx_sleep((int *)&semptr->semval, val_to_sleep, SYSV_TIMEOUT);
761 		/* return code is checked below, after sem[nz]cnt-- */
762 
763 		/*
764 		 * Make sure that the semaphore still exists
765 		 */
766 
767 		/* Check if another thread didn't remove the semaphore. */
768 		auxsemaptr = get_semaptr(semid, 0, IPC_W); /* Redundant access check. */
769 		if (!auxsemaptr) {
770 			errno = EIDRM;
771 			return (-1);
772 		}
773 
774 		if (auxsemaptr != semaptr) {
775 			errno = EIDRM;
776 			goto done;
777 		}
778 
779 		/* Check if another process didn't remove the semaphore. */
780 #ifdef SYSV_SEMS
781 		if (try_rwlock_rdlock(semid, semaptr) == -1) {
782 #else
783 		if (try_rwlock_wrlock(semid, semaptr) == -1) {
784 #endif
785 			errno = EIDRM;
786 			goto done;
787 		}
788 		sysv_print("semop:  good morning (eval=%d)!\n", eval);
789 
790 		/* The semaphore is still alive.  Readjust the count of
791 		 * waiting processes.
792 		 */
793 		semptr = &semaptr->ds.sem_base[sopptr->sem_num];
794 #ifdef SYSV_SEMS
795 		sysv_mutex_lock(&semptr->sem_mutex);
796 #endif
797 		if (sopptr->sem_op == 0)
798 			semptr->semzcnt--;
799 		else
800 			semptr->semncnt--;
801 #ifdef SYSV_SEMS
802 		sysv_mutex_unlock(&semptr->sem_mutex);
803 #endif
804 
805 		/*
806 		 * Is it really morning, or was our sleep interrupted?
807 		 * (Delayed check of tsleep() return code because we
808 		 * need to decrement sem[nz]cnt either way.)
809 		 *
810 		 * Always retry on EBUSY
811 		 */
812 		if (eval == EAGAIN) {
813 			eval = EINTR;
814 			goto done;
815 		}
816 
817 		sysv_print("semop:  good morning!\n");
818 		/* RETRY LOOP */
819 }
820 
821 donex:
822 	/*
823 	* Process any SEM_UNDO requests.
824 	*/
825 	if (do_undos) {
826 		for (i = 0; i < (int)nsops; i++) {
827 			/*
828 			 * We only need to deal with SEM_UNDO's for non-zero
829 			 * op's.
830 			 */
831 			int adjval;
832 
833 			if ((sops[i].sem_flg & SEM_UNDO) == 0)
834 				continue;
835 			adjval = sops[i].sem_op;
836 			if (adjval == 0)
837 				continue;
838 			eval = semundo_adjust(semid, sops[i].sem_num, -adjval);
839 			if (eval == 0)
840 				continue;
841 
842 			/*
843 			 * Oh-Oh!  We ran out of either sem_undo's or undo's.
844 			 * Rollback the adjustments to this point and then
845 			 * rollback the semaphore ups and down so we can return
846 			 * with an error with all structures restored.  We
847 			 * rollback the undo's in the exact reverse order that
848 			 * we applied them.  This guarantees that we won't run
849 			 * out of space as we roll things back out.
850 			 */
851 			for (j = i - 1; j >= 0; j--) {
852 				if ((sops[j].sem_flg & SEM_UNDO) == 0)
853 					continue;
854 				adjval = sops[j].sem_op;
855 				if (adjval == 0)
856 					continue;
857 				if (semundo_adjust(semid, sops[j].sem_num,
858 							adjval) != 0)
859 					sysv_print("semop - can't undo undos");
860 			}
861 
862 			for (j = 0; j < (int)nsops; j++) {
863 				xsemptr = &semaptr->ds.sem_base[
864 					sops[j].sem_num];
865 #ifdef SYSV_SEMS
866 				sysv_mutex_lock(&semptr->sem_mutex);
867 #endif
868 				xsemptr->semval -= sops[j].sem_op;
869 				if (xsemptr->semval == 0 &&
870 						xsemptr->semzcnt > 0)
871 					umtx_wakeup((int *)&xsemptr->semval, 0);
872 				if (xsemptr->semval <= 0 &&
873 						xsemptr->semncnt > 0)
874 					umtx_wakeup((int *)&xsemptr->semval, 0); //?!
875 #ifdef SYSV_SEMS
876 				sysv_mutex_unlock(&semptr->sem_mutex);
877 #endif
878 			}
879 
880 			sysv_print("eval = %d from semundo_adjust\n", eval);
881 			goto done;
882 		}
883 	}
884 
885 	/* Set sempid field for each semaphore. */
886 	for (i = 0; i < (int)nsops; i++) {
887 		sopptr = &sops[i];
888 		semptr = &semaptr->ds.sem_base[sopptr->sem_num];
889 #ifdef SYSV_SEMS
890 		sysv_mutex_lock(&semptr->sem_mutex);
891 #endif
892 		semptr->sempid = getpid();
893 #ifdef SYSV_SEMS
894 		sysv_mutex_unlock(&semptr->sem_mutex);
895 #endif
896 	}
897 
898 	sysv_print("semop:  done\n");
899 	semaptr->ds.sem_otime = time(NULL);
900 done:
901 	rwlock_unlock(semid, semaptr);
902 done2:
903 	put_shmdata(semid);
904 
905 	return (eval);
906 }
907