libc/sysvipc: Fix some style issues (no functional change).
[dragonfly.git] / lib / libc / sysvipc / sem.c
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 }