xref: /dflybsd-src/lib/libc/sysvipc/sem.c (revision 4a41674f196a6d9ae8b94e4fc1177a05123e3319)
182657471SMarkus Pfeiffer /* $FreeBSD: src/sys/kern/sysv_sem.c,v 1.69 2004/03/17 09:37:13 cperciva Exp $ */
282657471SMarkus Pfeiffer 
382657471SMarkus Pfeiffer /*
482657471SMarkus Pfeiffer  * Implementation of SVID semaphores
582657471SMarkus Pfeiffer  *
682657471SMarkus Pfeiffer  * Author:  Daniel Boulet
782657471SMarkus Pfeiffer  * Copyright (c) 2013 Larisa Grigore <larisagrigore@gmail.com>
882657471SMarkus Pfeiffer  *
982657471SMarkus Pfeiffer  * This software is provided ``AS IS'' without any warranties of any kind.
1082657471SMarkus Pfeiffer  */
1182657471SMarkus Pfeiffer 
1282657471SMarkus Pfeiffer #include "namespace.h"
1382657471SMarkus Pfeiffer #include <stdio.h>
1482657471SMarkus Pfeiffer #include <stdlib.h>
1582657471SMarkus Pfeiffer #include <errno.h>
1682657471SMarkus Pfeiffer #include <err.h>
1782657471SMarkus Pfeiffer #include <pthread.h>
1882657471SMarkus Pfeiffer #include <string.h>
1982657471SMarkus Pfeiffer #include <stdarg.h>
2082657471SMarkus Pfeiffer #include <sys/param.h>
2182657471SMarkus Pfeiffer #include <sys/queue.h>
2282657471SMarkus Pfeiffer #include <sys/mman.h>
2382657471SMarkus Pfeiffer #include <sys/sem.h>
2482657471SMarkus Pfeiffer #include "un-namespace.h"
2582657471SMarkus Pfeiffer 
2682657471SMarkus Pfeiffer #include "sysvipc_lock.h"
2782657471SMarkus Pfeiffer #include "sysvipc_ipc.h"
2882657471SMarkus Pfeiffer #include "sysvipc_shm.h"
2982657471SMarkus Pfeiffer #include "sysvipc_sem.h"
3082657471SMarkus Pfeiffer #include "sysvipc_hash.h"
3182657471SMarkus Pfeiffer 
3282657471SMarkus Pfeiffer 
3382657471SMarkus Pfeiffer #define SYSV_MUTEX_LOCK(x)		if (__isthreaded) _pthread_mutex_lock(x)
3482657471SMarkus Pfeiffer #define SYSV_MUTEX_UNLOCK(x)	if (__isthreaded) _pthread_mutex_unlock(x)
3582657471SMarkus Pfeiffer #define SYSV_MUTEX_DESTROY(x)	if (__isthreaded) _pthread_mutex_destroy(x)
3682657471SMarkus Pfeiffer 
3782657471SMarkus Pfeiffer extern struct hashtable *shmaddrs;
3882657471SMarkus Pfeiffer extern struct hashtable *shmres;
3982657471SMarkus Pfeiffer extern pthread_mutex_t lock_resources;
4082657471SMarkus Pfeiffer 
4182657471SMarkus Pfeiffer struct sem_undo *undos = NULL;
4282657471SMarkus Pfeiffer pthread_mutex_t lock_undo = PTHREAD_MUTEX_INITIALIZER;
4382657471SMarkus Pfeiffer 
4482657471SMarkus Pfeiffer static int semundo_clear(int, int);
4582657471SMarkus Pfeiffer 
4682657471SMarkus Pfeiffer static int
4782657471SMarkus Pfeiffer put_shmdata(int id) {
4882657471SMarkus Pfeiffer 	struct shm_data *data;
4982657471SMarkus Pfeiffer 	int ret = -1;
5082657471SMarkus Pfeiffer 
5182657471SMarkus Pfeiffer 	SYSV_MUTEX_LOCK(&lock_resources);
5282657471SMarkus Pfeiffer 	data = _hash_lookup(shmres, id);
5382657471SMarkus Pfeiffer 	if (!data) {
5482657471SMarkus Pfeiffer 		sysv_print_err("something wrong put_shmdata\n");
5582657471SMarkus Pfeiffer 		goto done; /* It should not reach here. */
5682657471SMarkus Pfeiffer 	}
5782657471SMarkus Pfeiffer 
5882657471SMarkus Pfeiffer 	data->used--;
5982657471SMarkus Pfeiffer 	if (data->used == 0 && data->removed) {
6082657471SMarkus Pfeiffer 		sysv_print("really remove the sem\n");
6182657471SMarkus Pfeiffer 		SYSV_MUTEX_UNLOCK(&lock_resources);
6282657471SMarkus Pfeiffer 		/* OBS: Even if the shmctl fails (the thread doesn't
6382657471SMarkus Pfeiffer 		 * have IPC_M permissions), all structures associated
6482657471SMarkus Pfeiffer 		 * with it will be removed in the current process.*/
6582657471SMarkus Pfeiffer 		sysvipc_shmdt(data->internal);
6682657471SMarkus Pfeiffer 		semundo_clear(id, -1);
6782657471SMarkus Pfeiffer 		if (data->removed == SEG_ALREADY_REMOVED)
6882657471SMarkus Pfeiffer 			return 1; /* The semaphore was removed
6982657471SMarkus Pfeiffer 			by another process so there is nothing else
7082657471SMarkus Pfeiffer 			we must do. */
7182657471SMarkus Pfeiffer 		/* Else inform the daemon that the segment is removed. */
7282657471SMarkus Pfeiffer 		return (sysvipc_shmctl(id, IPC_RMID, NULL));
7382657471SMarkus Pfeiffer 	}
7482657471SMarkus Pfeiffer 
7582657471SMarkus Pfeiffer 	ret = 0;
7682657471SMarkus Pfeiffer done:
7782657471SMarkus Pfeiffer 	SYSV_MUTEX_UNLOCK(&lock_resources);
7882657471SMarkus Pfeiffer 	return (ret);
7982657471SMarkus Pfeiffer }
8082657471SMarkus Pfeiffer 
8182657471SMarkus Pfeiffer static struct semid_pool*
8282657471SMarkus Pfeiffer get_semaptr(int semid, int to_remove, int shm_access) {
8382657471SMarkus Pfeiffer 	struct semid_pool *semaptr;
8482657471SMarkus Pfeiffer 
8582657471SMarkus Pfeiffer 	struct shm_data *shmdata = get_shmdata(semid, to_remove, shm_access);
8682657471SMarkus Pfeiffer 	if (!shmdata) {
8782657471SMarkus Pfeiffer 		/* Error is set in get_shmdata. */
8882657471SMarkus Pfeiffer 		return (NULL);
8982657471SMarkus Pfeiffer 	}
9082657471SMarkus Pfeiffer 
9182657471SMarkus Pfeiffer 	semaptr = (struct semid_pool *)shmdata->internal;
9282657471SMarkus Pfeiffer 	if (!semaptr) {
9382657471SMarkus Pfeiffer 		put_shmdata(semid);
9482657471SMarkus Pfeiffer 		errno = EINVAL;
9582657471SMarkus Pfeiffer 		return (NULL);
9682657471SMarkus Pfeiffer 	}
9782657471SMarkus Pfeiffer 
9882657471SMarkus Pfeiffer 	return (semaptr);
9982657471SMarkus Pfeiffer }
10082657471SMarkus Pfeiffer 
10182657471SMarkus Pfeiffer static int
10282657471SMarkus Pfeiffer sema_exist(int semid, struct semid_pool *semaptr) {
10382657471SMarkus Pfeiffer 	/* Was it removed? */
10482657471SMarkus Pfeiffer 	if (semaptr->gen == -1 ||
10582657471SMarkus Pfeiffer 			semaptr->ds.sem_perm.seq != IPCID_TO_SEQ(semid))
10682657471SMarkus Pfeiffer 		return (0);
10782657471SMarkus Pfeiffer 
10882657471SMarkus Pfeiffer 	return (1);
10982657471SMarkus Pfeiffer }
11082657471SMarkus Pfeiffer 
11182657471SMarkus Pfeiffer /* This is the function called when a the semaphore
11282657471SMarkus Pfeiffer  * is descovered as removed. It marks the process
11382657471SMarkus Pfeiffer  * internal data and munmap the */
11482657471SMarkus Pfeiffer static void
11582657471SMarkus Pfeiffer mark_for_removal(int shmid) {
11682657471SMarkus Pfeiffer 	sysv_print("Mark that the segment was removed\n");
11782657471SMarkus Pfeiffer 	get_shmdata(shmid, SEG_ALREADY_REMOVED, 0);
11882657471SMarkus Pfeiffer 	 /* Setting SEG_ALREADY_REMOVED parameter, when put_shmdata
11982657471SMarkus Pfeiffer 	  * is called, the internal resources will be freed.
12082657471SMarkus Pfeiffer 	  */
12182657471SMarkus Pfeiffer 	/* Decrement the "usage" field. */
12282657471SMarkus Pfeiffer 	put_shmdata(shmid);
12382657471SMarkus Pfeiffer }
12482657471SMarkus Pfeiffer 
12582657471SMarkus Pfeiffer static int
12682657471SMarkus Pfeiffer try_rwlock_rdlock(int semid, struct semid_pool *semaptr) {
12782657471SMarkus Pfeiffer 	sysv_print(" before rd lock id = %d %x\n", semid, semaptr);
12882657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
12982657471SMarkus Pfeiffer 	sysv_rwlock_rdlock(&semaptr->rwlock);
13082657471SMarkus Pfeiffer 	sysv_print("rd lock id = %d\n", semid);
13182657471SMarkus Pfeiffer #else
13282657471SMarkus Pfeiffer 	sysv_mutex_lock(&semaptr->mutex);
13382657471SMarkus Pfeiffer 	sysv_print("lock id = %d\n", semid);
13482657471SMarkus Pfeiffer #endif
13582657471SMarkus Pfeiffer 	if (!sema_exist(semid, semaptr)) {
13682657471SMarkus Pfeiffer 		errno = EINVAL;
13782657471SMarkus Pfeiffer 		sysv_print("error sema %d doesn't exist\n", semid);
13882657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
13982657471SMarkus Pfeiffer 		sysv_rwlock_unlock(&semaptr->rwlock);
14082657471SMarkus Pfeiffer #else
14182657471SMarkus Pfeiffer 		sysv_mutex_unlock(&semaptr->mutex);
14282657471SMarkus Pfeiffer #endif
14382657471SMarkus Pfeiffer 		/* Internal resources must be freed. */
14482657471SMarkus Pfeiffer 		mark_for_removal(semid);
14582657471SMarkus Pfeiffer 		return (-1);
14682657471SMarkus Pfeiffer 	}
14782657471SMarkus Pfeiffer 	return (0);
14882657471SMarkus Pfeiffer }
14982657471SMarkus Pfeiffer 
15082657471SMarkus Pfeiffer static int
15182657471SMarkus Pfeiffer try_rwlock_wrlock(int semid, struct semid_pool *semaptr) {
15282657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
15382657471SMarkus Pfeiffer 	sysv_print("before wrlock id = %d %x\n", semid, semaptr);
15482657471SMarkus Pfeiffer 	sysv_rwlock_wrlock(&semaptr->rwlock);
15582657471SMarkus Pfeiffer #else
15682657471SMarkus Pfeiffer 	sysv_print("before lock id = %d %x\n", semid, semaptr);
15782657471SMarkus Pfeiffer 	sysv_mutex_lock(&semaptr->mutex);
15882657471SMarkus Pfeiffer #endif
15982657471SMarkus Pfeiffer 	sysv_print("lock id = %d\n", semid);
16082657471SMarkus Pfeiffer 	if (!sema_exist(semid, semaptr)) {
16182657471SMarkus Pfeiffer 		errno = EINVAL;
16282657471SMarkus Pfeiffer 		sysv_print("error sema %d doesn't exist\n", semid);
16382657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
16482657471SMarkus Pfeiffer 		sysv_rwlock_unlock(&semaptr->rwlock);
16582657471SMarkus Pfeiffer #else
16682657471SMarkus Pfeiffer 		sysv_mutex_unlock(&semaptr->mutex);
16782657471SMarkus Pfeiffer #endif
16882657471SMarkus Pfeiffer 		/* Internal resources must be freed. */
16982657471SMarkus Pfeiffer 		mark_for_removal(semid);
17082657471SMarkus Pfeiffer 		return (-1);
17182657471SMarkus Pfeiffer 	}
17282657471SMarkus Pfeiffer 	return (0);
17382657471SMarkus Pfeiffer }
17482657471SMarkus Pfeiffer 
17582657471SMarkus Pfeiffer static int
17682657471SMarkus Pfeiffer rwlock_unlock(int semid, struct semid_pool *semaptr) {
17782657471SMarkus Pfeiffer 	sysv_print("unlock id = %d %x\n", semid, semaptr);
17882657471SMarkus Pfeiffer 	if (!sema_exist(semid, semaptr)) {
17982657471SMarkus Pfeiffer 		/* Internal resources must be freed. */
18082657471SMarkus Pfeiffer 		mark_for_removal(semid);
18182657471SMarkus Pfeiffer 		errno = EINVAL;
18282657471SMarkus Pfeiffer 		return (-1);
18382657471SMarkus Pfeiffer 	}
18482657471SMarkus Pfeiffer #ifdef SYSV_RWLOCK
18582657471SMarkus Pfeiffer 	sysv_rwlock_unlock(&semaptr->rwlock);
18682657471SMarkus Pfeiffer #else
18782657471SMarkus Pfeiffer 	sysv_mutex_unlock(&semaptr->mutex);
18882657471SMarkus Pfeiffer #endif
18982657471SMarkus Pfeiffer 	return (0);
19082657471SMarkus Pfeiffer }
19182657471SMarkus Pfeiffer 
19282657471SMarkus Pfeiffer int
19382657471SMarkus Pfeiffer sysvipc_semget(key_t key, int nsems, int semflg) {
19482657471SMarkus Pfeiffer 	int semid;
19582657471SMarkus Pfeiffer 	void *shmaddr;
19682657471SMarkus Pfeiffer 	//int shm_access;
19782657471SMarkus Pfeiffer 	int size = sizeof(struct semid_pool) + nsems * sizeof(struct sem);
19882657471SMarkus Pfeiffer 
19982657471SMarkus Pfeiffer 	//TODO resources limits
20082657471SMarkus Pfeiffer 	sysv_print("handle semget\n");
20182657471SMarkus Pfeiffer 
20282657471SMarkus Pfeiffer 	semid = _shmget(key, size, semflg, SEMGET);
20382657471SMarkus Pfeiffer 	if (semid == -1) {
20482657471SMarkus Pfeiffer 		/* errno already set. */
20582657471SMarkus Pfeiffer 		goto done;
20682657471SMarkus Pfeiffer 	}
20782657471SMarkus Pfeiffer 
20882657471SMarkus Pfeiffer 	/* If the semaphore is in process of being removed there are two cases:
20982657471SMarkus Pfeiffer 	 * - the daemon knows that and it will handle this situation.
21082657471SMarkus Pfeiffer 	 * - one of the threads from this address space remove it and the daemon
21182657471SMarkus Pfeiffer 	 *   wasn't announced yet; in this scenario, the semaphore is marked
21282657471SMarkus Pfeiffer 	 *   using "removed" field of shm_data and future calls will return
21382657471SMarkus Pfeiffer 	 *   EIDRM error.
21482657471SMarkus Pfeiffer 	 */
21582657471SMarkus Pfeiffer 
21682657471SMarkus Pfeiffer #if 0
21782657471SMarkus Pfeiffer 	/* Set access type. */
21882657471SMarkus Pfeiffer 	shm_access = semflg & (IPC_W | IPC_R);
21982657471SMarkus Pfeiffer 	if(set_shmdata_access(semid, shm_access) != 0) {
22082657471SMarkus Pfeiffer 		/* errno already set. */
22182657471SMarkus Pfeiffer 		goto done;
22282657471SMarkus Pfeiffer 	}
22382657471SMarkus Pfeiffer #endif
22482657471SMarkus Pfeiffer 	shmaddr = sysvipc_shmat(semid, NULL, 0);
22582657471SMarkus Pfeiffer 	if (!shmaddr) {
22682657471SMarkus Pfeiffer 		semid = -1;
22782657471SMarkus Pfeiffer 		sysvipc_shmctl(semid, IPC_RMID, NULL);
22882657471SMarkus Pfeiffer 		goto done;
22982657471SMarkus Pfeiffer 	}
23082657471SMarkus Pfeiffer 
23182657471SMarkus Pfeiffer 	//TODO more semaphores in a single file
23282657471SMarkus Pfeiffer 
23382657471SMarkus Pfeiffer done:
23482657471SMarkus Pfeiffer 	sysv_print("end handle semget %d\n", semid);
23582657471SMarkus Pfeiffer 	return (semid);
23682657471SMarkus Pfeiffer }
23782657471SMarkus Pfeiffer 
23882657471SMarkus Pfeiffer static int
23982657471SMarkus Pfeiffer semundo_clear(int semid, int semnum)
24082657471SMarkus Pfeiffer {
24182657471SMarkus Pfeiffer 	struct undo *sunptr;
24282657471SMarkus Pfeiffer 	int i;
24382657471SMarkus Pfeiffer 
24482657471SMarkus Pfeiffer 	sysv_print("semundo clear\n");
24582657471SMarkus Pfeiffer 
24682657471SMarkus Pfeiffer 	SYSV_MUTEX_LOCK(&lock_undo);
24782657471SMarkus Pfeiffer 	if (!undos)
24882657471SMarkus Pfeiffer 		goto done;
24982657471SMarkus Pfeiffer 
25082657471SMarkus Pfeiffer 	sunptr = &undos->un_ent[0];
25182657471SMarkus Pfeiffer 	i = 0;
25282657471SMarkus Pfeiffer 
25382657471SMarkus Pfeiffer 	while (i < undos->un_cnt) {
25482657471SMarkus Pfeiffer 		if (sunptr->un_id == semid) {
25582657471SMarkus Pfeiffer 			if (semnum == -1 || sunptr->un_num == semnum) {
25682657471SMarkus Pfeiffer 				undos->un_cnt--;
25782657471SMarkus Pfeiffer 				if (i < undos->un_cnt) {
25882657471SMarkus Pfeiffer 					undos->un_ent[i] =
25982657471SMarkus Pfeiffer 					  undos->un_ent[undos->un_cnt];
26082657471SMarkus Pfeiffer 					continue;
26182657471SMarkus Pfeiffer 				}
26282657471SMarkus Pfeiffer 			}
26382657471SMarkus Pfeiffer 			if (semnum != -1)
26482657471SMarkus Pfeiffer 				break;
26582657471SMarkus Pfeiffer 		}
26682657471SMarkus Pfeiffer 		++i;
26782657471SMarkus Pfeiffer 		++sunptr;
26882657471SMarkus Pfeiffer 	}
26982657471SMarkus Pfeiffer 
27082657471SMarkus Pfeiffer 	//TODO Shrink memory if case; not sure if necessary
27182657471SMarkus Pfeiffer done:
27282657471SMarkus Pfeiffer 	SYSV_MUTEX_UNLOCK(&lock_undo);
27382657471SMarkus Pfeiffer 	sysv_print("end semundo clear\n");
27482657471SMarkus Pfeiffer 	return (0);
27582657471SMarkus Pfeiffer }
27682657471SMarkus Pfeiffer 
27782657471SMarkus Pfeiffer int
278*4a41674fSSascha Wildner sysvipc___semctl(int semid, int semnum , int cmd, union semun *arg)
279*4a41674fSSascha Wildner {
28082657471SMarkus Pfeiffer 	int i, error;
28182657471SMarkus Pfeiffer 	struct semid_pool *semaptr = NULL;
28282657471SMarkus Pfeiffer 	struct sem *semptr = NULL;
28382657471SMarkus Pfeiffer 	struct shmid_ds shmds;
28482657471SMarkus Pfeiffer 	int shm_access = 0;
28582657471SMarkus Pfeiffer 
28682657471SMarkus Pfeiffer 	/*if (!jail_sysvipc_allowed && cred->cr_prison != NULL)
28782657471SMarkus Pfeiffer 		return (ENOSYS);
28882657471SMarkus Pfeiffer */
28982657471SMarkus Pfeiffer 
29082657471SMarkus Pfeiffer 	sysv_print("semctl cmd = %d\n", cmd);
29182657471SMarkus Pfeiffer 
29282657471SMarkus Pfeiffer 	error = 0;
29382657471SMarkus Pfeiffer 
29482657471SMarkus Pfeiffer 	switch (cmd) {
29582657471SMarkus Pfeiffer 		case IPC_SET: /* Originally was IPC_M but this is checked
29682657471SMarkus Pfeiffer 				 by daemon. */
29782657471SMarkus Pfeiffer 		case SETVAL:
29882657471SMarkus Pfeiffer 		case SETALL:
29982657471SMarkus Pfeiffer 			shm_access = IPC_W;
30082657471SMarkus Pfeiffer 			break;
30182657471SMarkus Pfeiffer 		case IPC_STAT:
30282657471SMarkus Pfeiffer 		case GETNCNT:
30382657471SMarkus Pfeiffer 		case GETPID:
30482657471SMarkus Pfeiffer 		case GETVAL:
30582657471SMarkus Pfeiffer 		case GETALL:
30682657471SMarkus Pfeiffer 		case GETZCNT:
30782657471SMarkus Pfeiffer 			shm_access = IPC_R;
30882657471SMarkus Pfeiffer 			break;
30982657471SMarkus Pfeiffer 		default:
31082657471SMarkus Pfeiffer 			break;
31182657471SMarkus Pfeiffer 	}
31282657471SMarkus Pfeiffer 
31382657471SMarkus Pfeiffer 	semaptr = get_semaptr(semid, cmd==IPC_RMID, shm_access);
31482657471SMarkus Pfeiffer 	if (!semaptr) {
31582657471SMarkus Pfeiffer 		/* errno already set. */
31682657471SMarkus Pfeiffer 		return (-1);
31782657471SMarkus Pfeiffer 	}
31882657471SMarkus Pfeiffer 
31982657471SMarkus Pfeiffer 	switch (cmd) {
32082657471SMarkus Pfeiffer 	case IPC_RMID:
32182657471SMarkus Pfeiffer 		/* Mark that the segment is removed. This is done in
32282657471SMarkus Pfeiffer 		 * get_semaptr call in order to announce other processes.
32382657471SMarkus Pfeiffer 		 * It will be actually removed after put_shmdata call and
32482657471SMarkus Pfeiffer 		 * not other thread from this address space use shm_data
32582657471SMarkus Pfeiffer 		 * structure.
32682657471SMarkus Pfeiffer 		 */
32782657471SMarkus Pfeiffer 		break;
32882657471SMarkus Pfeiffer 
32982657471SMarkus Pfeiffer 	case IPC_SET:
330*4a41674fSSascha Wildner 		if (!arg->buf) {
33182657471SMarkus Pfeiffer 			error = EFAULT;
33282657471SMarkus Pfeiffer 			break;
33382657471SMarkus Pfeiffer 		}
33482657471SMarkus Pfeiffer 
33582657471SMarkus Pfeiffer 		memset(&shmds, 0, sizeof(shmds)/sizeof(unsigned char));
336*4a41674fSSascha Wildner 		memcpy(&shmds.shm_perm, &arg->buf->sem_perm,
33782657471SMarkus Pfeiffer 				sizeof(struct ipc_perm));
33882657471SMarkus Pfeiffer 		error = sysvipc_shmctl(semid, cmd, &shmds);
33982657471SMarkus Pfeiffer 		/* OBS: didn't update ctime and mode as in kernel implementation
34082657471SMarkus Pfeiffer 		 * it is done. Those fields are already updated for shmid_ds
34182657471SMarkus Pfeiffer 		 * struct when calling shmctl
34282657471SMarkus Pfeiffer 		 */
34382657471SMarkus Pfeiffer 		break;
34482657471SMarkus Pfeiffer 
34582657471SMarkus Pfeiffer 	case IPC_STAT:
346*4a41674fSSascha Wildner 		if (!arg->buf) {
34782657471SMarkus Pfeiffer 			error = EFAULT;
34882657471SMarkus Pfeiffer 			break;
34982657471SMarkus Pfeiffer 		}
35082657471SMarkus Pfeiffer 
35182657471SMarkus Pfeiffer 		error = sysvipc_shmctl(semid, cmd, &shmds);
35282657471SMarkus Pfeiffer 		if (error)
35382657471SMarkus Pfeiffer 			break;
35482657471SMarkus Pfeiffer 
355*4a41674fSSascha Wildner 		memcpy(&arg->buf->sem_perm, &shmds.shm_perm,
35682657471SMarkus Pfeiffer 				sizeof(struct ipc_perm));
357*4a41674fSSascha Wildner 		arg->buf->sem_nsems = (shmds.shm_segsz - sizeof(struct semid_pool)) /
35882657471SMarkus Pfeiffer 			sizeof(struct sem);
359*4a41674fSSascha Wildner 		arg->buf->sem_ctime = shmds.shm_ctime;
36082657471SMarkus Pfeiffer 
36182657471SMarkus Pfeiffer 		/* otime is semaphore specific so read it from
36282657471SMarkus Pfeiffer 		 * semaptr
36382657471SMarkus Pfeiffer 		 */
36482657471SMarkus Pfeiffer 		error = try_rwlock_rdlock(semid, semaptr);
36582657471SMarkus Pfeiffer 		if (error)
36682657471SMarkus Pfeiffer 			break;
367*4a41674fSSascha Wildner 		arg->buf->sem_otime = semaptr->ds.sem_otime;
36882657471SMarkus Pfeiffer 		rwlock_unlock(semid, semaptr);
36982657471SMarkus Pfeiffer 		break;
37082657471SMarkus Pfeiffer 
37182657471SMarkus Pfeiffer 	case GETNCNT:
37282657471SMarkus Pfeiffer 		if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) {
37382657471SMarkus Pfeiffer 			errno = EINVAL;
37482657471SMarkus Pfeiffer 			break;
37582657471SMarkus Pfeiffer 		}
37682657471SMarkus Pfeiffer 
37782657471SMarkus Pfeiffer 		error = try_rwlock_rdlock(semid, semaptr);
37882657471SMarkus Pfeiffer 		if (error)
37982657471SMarkus Pfeiffer 			break;
38082657471SMarkus Pfeiffer 		error = semaptr->ds.sem_base[semnum].semncnt;
38182657471SMarkus Pfeiffer 		rwlock_unlock(semid, semaptr);
38282657471SMarkus Pfeiffer 		break;
38382657471SMarkus Pfeiffer 
38482657471SMarkus Pfeiffer 	case GETPID:
38582657471SMarkus Pfeiffer 		if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) {
38682657471SMarkus Pfeiffer 			errno = EINVAL;
38782657471SMarkus Pfeiffer 			break;
38882657471SMarkus Pfeiffer 		}
38982657471SMarkus Pfeiffer 
39082657471SMarkus Pfeiffer 		error = try_rwlock_rdlock(semid, semaptr);
39182657471SMarkus Pfeiffer 		if (error)
39282657471SMarkus Pfeiffer 			break;
39382657471SMarkus Pfeiffer 		error = semaptr->ds.sem_base[semnum].sempid;
39482657471SMarkus Pfeiffer 		rwlock_unlock(semid, semaptr);
39582657471SMarkus Pfeiffer 		break;
39682657471SMarkus Pfeiffer 
39782657471SMarkus Pfeiffer 	case GETVAL:
39882657471SMarkus Pfeiffer 		if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) {
39982657471SMarkus Pfeiffer 			errno = EINVAL;
40082657471SMarkus Pfeiffer 			break;
40182657471SMarkus Pfeiffer 		}
40282657471SMarkus Pfeiffer 
40382657471SMarkus Pfeiffer 		error = try_rwlock_rdlock(semid, semaptr);
40482657471SMarkus Pfeiffer 		if (error)
40582657471SMarkus Pfeiffer 			break;
40682657471SMarkus Pfeiffer 		error = semaptr->ds.sem_base[semnum].semval;
40782657471SMarkus Pfeiffer 		rwlock_unlock(semid, semaptr);
40882657471SMarkus Pfeiffer 		break;
40982657471SMarkus Pfeiffer 
41082657471SMarkus Pfeiffer 	case GETALL:
411*4a41674fSSascha Wildner 		if (!arg->array) {
41282657471SMarkus Pfeiffer 			error = EFAULT;
41382657471SMarkus Pfeiffer 			break;
41482657471SMarkus Pfeiffer 		}
41582657471SMarkus Pfeiffer 
41682657471SMarkus Pfeiffer 		error = try_rwlock_rdlock(semid, semaptr);
41782657471SMarkus Pfeiffer 		if (error)
41882657471SMarkus Pfeiffer 			break;
41982657471SMarkus Pfeiffer 		for (i = 0; i < semaptr->ds.sem_nsems; i++) {
420*4a41674fSSascha Wildner 			arg->array[i] = semaptr->ds.sem_base[i].semval;
42182657471SMarkus Pfeiffer 		}
42282657471SMarkus Pfeiffer 		rwlock_unlock(semid, semaptr);
42382657471SMarkus Pfeiffer 		break;
42482657471SMarkus Pfeiffer 
42582657471SMarkus Pfeiffer 	case GETZCNT:
42682657471SMarkus Pfeiffer 		if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) {
42782657471SMarkus Pfeiffer 			errno = EINVAL;
42882657471SMarkus Pfeiffer 			break;
42982657471SMarkus Pfeiffer 		}
43082657471SMarkus Pfeiffer 
43182657471SMarkus Pfeiffer 		error = try_rwlock_rdlock(semid, semaptr);
43282657471SMarkus Pfeiffer 		if (error)
43382657471SMarkus Pfeiffer 			break;
43482657471SMarkus Pfeiffer 		error = semaptr->ds.sem_base[semnum].semzcnt;
43582657471SMarkus Pfeiffer 		rwlock_unlock(semid, semaptr);
43682657471SMarkus Pfeiffer 		break;
43782657471SMarkus Pfeiffer 
43882657471SMarkus Pfeiffer 	case SETVAL:
43982657471SMarkus Pfeiffer 		if (semnum < 0 || semnum >= semaptr->ds.sem_nsems) {
44082657471SMarkus Pfeiffer 			errno = EINVAL;
44182657471SMarkus Pfeiffer 			break;
44282657471SMarkus Pfeiffer 		}
44382657471SMarkus Pfeiffer 
44482657471SMarkus Pfeiffer 		error = try_rwlock_wrlock(semid, semaptr);
44582657471SMarkus Pfeiffer 		if (error)
44682657471SMarkus Pfeiffer 			break;
44782657471SMarkus Pfeiffer 		semptr = &semaptr->ds.sem_base[semnum];
448*4a41674fSSascha Wildner 		semptr->semval = arg->val;
44982657471SMarkus Pfeiffer 		semundo_clear(semid, semnum);
45082657471SMarkus Pfeiffer 		if (semptr->semzcnt || semptr->semncnt)
45182657471SMarkus Pfeiffer 			umtx_wakeup((int *)&semptr->semval, 0);
45282657471SMarkus Pfeiffer 		rwlock_unlock(semid, semaptr);
45382657471SMarkus Pfeiffer 		break;
45482657471SMarkus Pfeiffer 
45582657471SMarkus Pfeiffer 	case SETALL:
456*4a41674fSSascha Wildner 		if (!arg->array) {
45782657471SMarkus Pfeiffer 			error = EFAULT;
45882657471SMarkus Pfeiffer 			break;
45982657471SMarkus Pfeiffer 		}
46082657471SMarkus Pfeiffer 
46182657471SMarkus Pfeiffer 		error = try_rwlock_wrlock(semid, semaptr);
46282657471SMarkus Pfeiffer 		if (error)
46382657471SMarkus Pfeiffer 			break;
46482657471SMarkus Pfeiffer 		for (i = 0; i < semaptr->ds.sem_nsems; i++) {
46582657471SMarkus Pfeiffer 			semptr = &semaptr->ds.sem_base[i];
466*4a41674fSSascha Wildner 			semptr->semval = arg->array[i];
46782657471SMarkus Pfeiffer 			if (semptr->semzcnt || semptr->semncnt)
46882657471SMarkus Pfeiffer 				umtx_wakeup((int *)&semptr->semval, 0);
46982657471SMarkus Pfeiffer 		}
47082657471SMarkus Pfeiffer 		semundo_clear(semid, -1);
47182657471SMarkus Pfeiffer 		rwlock_unlock(semid, semaptr);
47282657471SMarkus Pfeiffer 		break;
47382657471SMarkus Pfeiffer 
47482657471SMarkus Pfeiffer 	default:
47582657471SMarkus Pfeiffer 		errno = EINVAL;
47682657471SMarkus Pfeiffer 		break;
47782657471SMarkus Pfeiffer 	}
47882657471SMarkus Pfeiffer 
47982657471SMarkus Pfeiffer 	put_shmdata(semid);
48082657471SMarkus Pfeiffer 
48182657471SMarkus Pfeiffer 	sysv_print("end semctl\n");
48282657471SMarkus Pfeiffer 	return (error);
48382657471SMarkus Pfeiffer }
48482657471SMarkus Pfeiffer 
48582657471SMarkus Pfeiffer /*
48682657471SMarkus Pfeiffer  * Adjust a particular entry for a particular proc
48782657471SMarkus Pfeiffer  */
48882657471SMarkus Pfeiffer static int
48982657471SMarkus Pfeiffer semundo_adjust(int semid, int semnum, int adjval)
49082657471SMarkus Pfeiffer {
49182657471SMarkus Pfeiffer 	struct undo *sunptr;
49282657471SMarkus Pfeiffer 	int i;
49382657471SMarkus Pfeiffer 	int error = 0;
49482657471SMarkus Pfeiffer 	size_t size;
49582657471SMarkus Pfeiffer 	int undoid;
49682657471SMarkus Pfeiffer 	void *addr;
49782657471SMarkus Pfeiffer 	struct shm_data *data;
49882657471SMarkus Pfeiffer 
49982657471SMarkus Pfeiffer 	sysv_print("semundo adjust\n");
50082657471SMarkus Pfeiffer 	if (!adjval)
50182657471SMarkus Pfeiffer 		goto done;
50282657471SMarkus Pfeiffer 
50382657471SMarkus Pfeiffer 	SYSV_MUTEX_LOCK(&lock_undo);
50482657471SMarkus Pfeiffer 	if (!undos) {
50582657471SMarkus Pfeiffer 		sysv_print("get undo segment\n");
50682657471SMarkus Pfeiffer 		undoid = _shmget(IPC_PRIVATE, PAGE_SIZE, IPC_CREAT | IPC_EXCL | 0600,
50782657471SMarkus Pfeiffer 				UNDOGET);
50882657471SMarkus Pfeiffer 		if (undoid == -1) {
50982657471SMarkus Pfeiffer 			sysv_print_err("no undo segment\n");
51082657471SMarkus Pfeiffer 			return (-1);
51182657471SMarkus Pfeiffer 		}
51282657471SMarkus Pfeiffer 
51382657471SMarkus Pfeiffer 		addr = sysvipc_shmat(undoid, NULL, 0);
51482657471SMarkus Pfeiffer 		if (!addr) {
51582657471SMarkus Pfeiffer 			sysv_print_err("can not map undo segment\n");
51682657471SMarkus Pfeiffer 			sysvipc_shmctl(undoid, IPC_RMID, NULL);
51782657471SMarkus Pfeiffer 			return (-1);
51882657471SMarkus Pfeiffer 		}
51982657471SMarkus Pfeiffer 
52082657471SMarkus Pfeiffer 		undos = (struct sem_undo *)addr;
52182657471SMarkus Pfeiffer 		undos->un_pages = 1;
52282657471SMarkus Pfeiffer 		undos->un_cnt = 0;
52382657471SMarkus Pfeiffer 	}
52482657471SMarkus Pfeiffer 
52582657471SMarkus Pfeiffer 	/*
52682657471SMarkus Pfeiffer 	 * Look for the requested entry and adjust it (delete if adjval becomes
52782657471SMarkus Pfeiffer 	 * 0).
52882657471SMarkus Pfeiffer 	 */
52982657471SMarkus Pfeiffer 	sunptr = &undos->un_ent[0];
53082657471SMarkus Pfeiffer 	for (i = 0; i < undos->un_cnt; i++, sunptr++) {
53182657471SMarkus Pfeiffer 		if (sunptr->un_id != semid && sunptr->un_num != semnum)
53282657471SMarkus Pfeiffer 			continue;
53382657471SMarkus Pfeiffer 		sunptr->un_adjval += adjval;
53482657471SMarkus Pfeiffer 		if (sunptr->un_adjval == 0) {
53582657471SMarkus Pfeiffer 			undos->un_cnt--;
53682657471SMarkus Pfeiffer 			if (i < undos->un_cnt)
53782657471SMarkus Pfeiffer 				undos->un_ent[i] = undos->un_ent[undos->un_cnt];
53882657471SMarkus Pfeiffer 		}
53982657471SMarkus Pfeiffer 		goto done;
54082657471SMarkus Pfeiffer 	}
54182657471SMarkus Pfeiffer 
54282657471SMarkus Pfeiffer 	/* Didn't find the right entry - create it */
54382657471SMarkus Pfeiffer 	size = sizeof(struct sem_undo) + (undos->un_cnt + 1) *
54482657471SMarkus Pfeiffer 		sizeof(struct sem_undo);
54582657471SMarkus Pfeiffer 	if (size > (unsigned int)(undos->un_pages * PAGE_SIZE)) {
54682657471SMarkus Pfeiffer 		sysv_print("need more undo space\n");
54782657471SMarkus Pfeiffer 		sysvipc_shmdt(undos);
54882657471SMarkus Pfeiffer 		undos->un_pages++;
54982657471SMarkus Pfeiffer 
55082657471SMarkus Pfeiffer 		SYSV_MUTEX_LOCK(&lock_resources);
55182657471SMarkus Pfeiffer 		data = _hash_lookup(shmaddrs, (u_long)undos);
55282657471SMarkus Pfeiffer 		SYSV_MUTEX_UNLOCK(&lock_resources);
55382657471SMarkus Pfeiffer 
55482657471SMarkus Pfeiffer 		/* It is not necessary any lock on "size" because it is used
55582657471SMarkus Pfeiffer 		 * only by shmat and shmdt.
55682657471SMarkus Pfeiffer 		 * shmat for undoid is called only from this function and it
55782657471SMarkus Pfeiffer 		 * is protected by undo_lock.
55882657471SMarkus Pfeiffer 		 * shmdt for undoid is not called anywhere because the segment
55982657471SMarkus Pfeiffer 		 * is destroyed by the daemon when the client dies.
56082657471SMarkus Pfeiffer 		 */
56182657471SMarkus Pfeiffer 		data->size = undos->un_pages * PAGE_SIZE;
56282657471SMarkus Pfeiffer 		undos = sysvipc_shmat(data->shmid, NULL, 0);
56382657471SMarkus Pfeiffer 	}
56482657471SMarkus Pfeiffer 
56582657471SMarkus Pfeiffer 	sunptr = &undos->un_ent[undos->un_cnt];
56682657471SMarkus Pfeiffer 	undos->un_cnt++;
56782657471SMarkus Pfeiffer 	sunptr->un_adjval = adjval;
56882657471SMarkus Pfeiffer 	sunptr->un_id = semid;
56982657471SMarkus Pfeiffer 	sunptr->un_num = semnum;
57082657471SMarkus Pfeiffer 	//if (suptr->un_cnt == seminfo.semume) TODO move it in daemon
57182657471SMarkus Pfeiffer 	/*} else {
57282657471SMarkus Pfeiffer 	  error = EINVAL; //se face prin notificare
57382657471SMarkus Pfeiffer 	  }*/
57482657471SMarkus Pfeiffer done:
57582657471SMarkus Pfeiffer 	SYSV_MUTEX_UNLOCK(&lock_undo);
57682657471SMarkus Pfeiffer 
57782657471SMarkus Pfeiffer 	sysv_print("semundo adjust end\n");
57882657471SMarkus Pfeiffer 	return (error);
57982657471SMarkus Pfeiffer }
58082657471SMarkus Pfeiffer 
58182657471SMarkus Pfeiffer int sysvipc_semop (int semid, struct sembuf *sops, unsigned nsops) {
58282657471SMarkus Pfeiffer 	struct semid_pool *semaptr = NULL, *auxsemaptr = NULL;
58382657471SMarkus Pfeiffer 	struct sembuf *sopptr;
58482657471SMarkus Pfeiffer 	struct sem *semptr = NULL;
58582657471SMarkus Pfeiffer 	struct sem *xsemptr = NULL;
58682657471SMarkus Pfeiffer 	int eval = 0;
58782657471SMarkus Pfeiffer 	int i, j;
58882657471SMarkus Pfeiffer 	int do_undos;
58982657471SMarkus Pfeiffer 	int val_to_sleep;
59082657471SMarkus Pfeiffer 
59182657471SMarkus Pfeiffer 	sysv_print("[client %d] call to semop(%d, %u)\n",
59282657471SMarkus Pfeiffer 			getpid(), semid, nsops);
59382657471SMarkus Pfeiffer //TODO
59482657471SMarkus Pfeiffer 	/*if (!jail_sysvipc_allowed && td->td_ucred->cr_prison != NULL)
59582657471SMarkus Pfeiffer 	  return (ENOSYS);
59682657471SMarkus Pfeiffer 	  */
59782657471SMarkus Pfeiffer 
59882657471SMarkus Pfeiffer 	semaptr = get_semaptr(semid, 0, IPC_W);
59982657471SMarkus Pfeiffer 	if (!semaptr) {
60082657471SMarkus Pfeiffer 		errno = EINVAL;
60182657471SMarkus Pfeiffer 		return (-1);
60282657471SMarkus Pfeiffer 	}
60382657471SMarkus Pfeiffer 
60482657471SMarkus Pfeiffer #ifdef SYSV_SEMS
60582657471SMarkus Pfeiffer 	if (try_rwlock_rdlock(semid, semaptr) == -1) {
60682657471SMarkus Pfeiffer #else
60782657471SMarkus Pfeiffer 	if (try_rwlock_wrlock(semid, semaptr) == -1) {
60882657471SMarkus Pfeiffer #endif
60982657471SMarkus Pfeiffer 		sysv_print("sema removed\n");
61082657471SMarkus Pfeiffer 		errno = EIDRM;
61182657471SMarkus Pfeiffer 		goto done2;
61282657471SMarkus Pfeiffer 	}
61382657471SMarkus Pfeiffer 
61482657471SMarkus Pfeiffer 	if (nsops > MAX_SOPS) {
61582657471SMarkus Pfeiffer 		sysv_print("too many sops (max=%d, nsops=%u)\n",
61682657471SMarkus Pfeiffer 				getpid(), MAX_SOPS, nsops);
61782657471SMarkus Pfeiffer 		eval = E2BIG;
61882657471SMarkus Pfeiffer 		goto done;
61982657471SMarkus Pfeiffer 	}
62082657471SMarkus Pfeiffer 
62182657471SMarkus Pfeiffer 	/*
62282657471SMarkus Pfeiffer 	* Loop trying to satisfy the vector of requests.
62382657471SMarkus Pfeiffer 	* If we reach a point where we must wait, any requests already
62482657471SMarkus Pfeiffer 	* performed are rolled back and we go to sleep until some other
62582657471SMarkus Pfeiffer 	* process wakes us up.  At this point, we start all over again.
62682657471SMarkus Pfeiffer 	*
62782657471SMarkus Pfeiffer 	* This ensures that from the perspective of other tasks, a set
62882657471SMarkus Pfeiffer 	* of requests is atomic (never partially satisfied).
62982657471SMarkus Pfeiffer 	*/
63082657471SMarkus Pfeiffer 	do_undos = 0;
63182657471SMarkus Pfeiffer 
63282657471SMarkus Pfeiffer 	for (;;) {
63382657471SMarkus Pfeiffer 
63482657471SMarkus Pfeiffer 		semptr = NULL;
63582657471SMarkus Pfeiffer 
63682657471SMarkus Pfeiffer 		for (i = 0; i < (int)nsops; i++) {
63782657471SMarkus Pfeiffer 			sopptr = &sops[i];
63882657471SMarkus Pfeiffer 
63982657471SMarkus Pfeiffer 			if (sopptr->sem_num >= semaptr->ds.sem_nsems) {
64082657471SMarkus Pfeiffer 				eval = EFBIG;
64182657471SMarkus Pfeiffer 				goto done;
64282657471SMarkus Pfeiffer 			}
64382657471SMarkus Pfeiffer 
64482657471SMarkus Pfeiffer 			semptr = &semaptr->ds.sem_base[sopptr->sem_num];
64582657471SMarkus Pfeiffer #ifdef SYSV_SEMS
64682657471SMarkus Pfeiffer 			sysv_mutex_lock(&semptr->sem_mutex);
64782657471SMarkus Pfeiffer #endif
64882657471SMarkus Pfeiffer 			sysv_print("semop: sem[%d]=%d : op=%d, flag=%s\n",
64982657471SMarkus Pfeiffer 				sopptr->sem_num, semptr->semval, sopptr->sem_op,
65082657471SMarkus Pfeiffer 				(sopptr->sem_flg & IPC_NOWAIT) ? "nowait" : "wait");
65182657471SMarkus Pfeiffer 
65282657471SMarkus Pfeiffer 			if (sopptr->sem_op < 0) {
65382657471SMarkus Pfeiffer 				if (semptr->semval + sopptr->sem_op < 0) {
65482657471SMarkus Pfeiffer 					sysv_print("semop:  can't do it now\n");
65582657471SMarkus Pfeiffer 					break;
65682657471SMarkus Pfeiffer 				} else {
65782657471SMarkus Pfeiffer 					semptr->semval += sopptr->sem_op;
65882657471SMarkus Pfeiffer 					if (semptr->semval == 0 &&
65982657471SMarkus Pfeiffer 						semptr->semzcnt > 0)
66082657471SMarkus Pfeiffer 						umtx_wakeup((int *)&semptr->semval, 0);
66182657471SMarkus Pfeiffer 				}
66282657471SMarkus Pfeiffer 				if (sopptr->sem_flg & SEM_UNDO)
66382657471SMarkus Pfeiffer 					do_undos = 1;
66482657471SMarkus Pfeiffer 			} else if (sopptr->sem_op == 0) {
66582657471SMarkus Pfeiffer 				if (semptr->semval > 0) {
66682657471SMarkus Pfeiffer 					sysv_print("semop:  not zero now\n");
66782657471SMarkus Pfeiffer 					break;
66882657471SMarkus Pfeiffer 				}
66982657471SMarkus Pfeiffer 			} else {
67082657471SMarkus Pfeiffer 				semptr->semval += sopptr->sem_op;
67182657471SMarkus Pfeiffer 				if (sopptr->sem_flg & SEM_UNDO)
67282657471SMarkus Pfeiffer 					do_undos = 1;
67382657471SMarkus Pfeiffer 				if (semptr->semncnt > 0)
67482657471SMarkus Pfeiffer 					umtx_wakeup((int *)&semptr->semval, 0);
67582657471SMarkus Pfeiffer 			}
67682657471SMarkus Pfeiffer #ifdef SYSV_SEMS
67782657471SMarkus Pfeiffer 			sysv_mutex_unlock(&semptr->sem_mutex);
67882657471SMarkus Pfeiffer #endif
67982657471SMarkus Pfeiffer 		}
68082657471SMarkus Pfeiffer 
68182657471SMarkus Pfeiffer 		/*
68282657471SMarkus Pfeiffer 		 * Did we get through the entire vector?
68382657471SMarkus Pfeiffer 		 */
68482657471SMarkus Pfeiffer 		if (i >= (int)nsops)
68582657471SMarkus Pfeiffer 			goto donex;
68682657471SMarkus Pfeiffer 
68782657471SMarkus Pfeiffer 		if (sopptr->sem_op == 0)
68882657471SMarkus Pfeiffer 			semptr->semzcnt++;
68982657471SMarkus Pfeiffer 		else
69082657471SMarkus Pfeiffer 			semptr->semncnt++;
69182657471SMarkus Pfeiffer #ifdef SYSV_SEMS
69282657471SMarkus Pfeiffer 		sysv_mutex_unlock(&semptr->sem_mutex);
69382657471SMarkus Pfeiffer #endif
69482657471SMarkus Pfeiffer 		/*
69582657471SMarkus Pfeiffer 		 * Rollback the semaphores we had acquired.
69682657471SMarkus Pfeiffer 		 */
69782657471SMarkus Pfeiffer 		sysv_print("semop:  rollback 0 through %d\n", i-1);
69882657471SMarkus Pfeiffer 		for (j = 0; j < i; j++) {
69982657471SMarkus Pfeiffer 			xsemptr = &semaptr->ds.sem_base[sops[j].sem_num];
70082657471SMarkus Pfeiffer #ifdef SYSV_SEMS
70182657471SMarkus Pfeiffer 			sysv_mutex_lock(&semptr->sem_mutex);
70282657471SMarkus Pfeiffer #endif
70382657471SMarkus Pfeiffer 			xsemptr->semval -= sops[j].sem_op;
70482657471SMarkus Pfeiffer 			if (xsemptr->semval == 0 && xsemptr->semzcnt > 0)
70582657471SMarkus Pfeiffer 				umtx_wakeup((int *)&xsemptr->semval, 0);
70682657471SMarkus Pfeiffer 			if (xsemptr->semval <= 0 && xsemptr->semncnt > 0)
70782657471SMarkus Pfeiffer 				umtx_wakeup((int *)&xsemptr->semval, 0); //?!
70882657471SMarkus Pfeiffer #ifdef SYSV_SEMS
70982657471SMarkus Pfeiffer 			sysv_mutex_unlock(&semptr->sem_mutex);
71082657471SMarkus Pfeiffer #endif
71182657471SMarkus Pfeiffer 		}
71282657471SMarkus Pfeiffer 
71382657471SMarkus Pfeiffer 		/*
71482657471SMarkus Pfeiffer 		 * If the request that we couldn't satisfy has the
71582657471SMarkus Pfeiffer 		 * NOWAIT flag set then return with EAGAIN.
71682657471SMarkus Pfeiffer 		 */
71782657471SMarkus Pfeiffer 		if (sopptr->sem_flg & IPC_NOWAIT) {
71882657471SMarkus Pfeiffer 			eval = EAGAIN;
71982657471SMarkus Pfeiffer 			goto done;
72082657471SMarkus Pfeiffer 		}
72182657471SMarkus Pfeiffer 
72282657471SMarkus Pfeiffer 		/*
72382657471SMarkus Pfeiffer 		 * Release semaptr->lock while sleeping, allowing other
72482657471SMarkus Pfeiffer 		 * semops (like SETVAL, SETALL, etc), which require an
72582657471SMarkus Pfeiffer 		 * exclusive lock and might wake us up.
72682657471SMarkus Pfeiffer 		 *
72782657471SMarkus Pfeiffer 		 * Reload and recheck the validity of semaptr on return.
72882657471SMarkus Pfeiffer 		 * Note that semptr itself might have changed too, but
72982657471SMarkus Pfeiffer 		 * we've already interlocked for semptr and that is what
73082657471SMarkus Pfeiffer 		 * will be woken up if it wakes up the tsleep on a MP
73182657471SMarkus Pfeiffer 		 * race.
73282657471SMarkus Pfeiffer 		 *
73382657471SMarkus Pfeiffer 		 */
73482657471SMarkus Pfeiffer 
73582657471SMarkus Pfeiffer 		sysv_print("semop:  good night!\n");
73682657471SMarkus Pfeiffer 		val_to_sleep = semptr->semval;
73782657471SMarkus Pfeiffer 		rwlock_unlock(semid, semaptr);
73882657471SMarkus Pfeiffer 		put_shmdata(semid);
73982657471SMarkus Pfeiffer 
74082657471SMarkus Pfeiffer 		/* We don't sleep more than SYSV_TIMEOUT because we could
74182657471SMarkus Pfeiffer 		 * go to sleep after another process calls wakeup and remain
74282657471SMarkus Pfeiffer 		 * blocked.
74382657471SMarkus Pfeiffer 		 */
74482657471SMarkus Pfeiffer 		eval = umtx_sleep((int *)&semptr->semval, val_to_sleep, SYSV_TIMEOUT);
74582657471SMarkus Pfeiffer 		/* return code is checked below, after sem[nz]cnt-- */
74682657471SMarkus Pfeiffer 
74782657471SMarkus Pfeiffer 		/*
74882657471SMarkus Pfeiffer 		 * Make sure that the semaphore still exists
74982657471SMarkus Pfeiffer 		 */
75082657471SMarkus Pfeiffer 
75182657471SMarkus Pfeiffer 		/* Check if another thread didn't remove the semaphore. */
75282657471SMarkus Pfeiffer 		auxsemaptr = get_semaptr(semid, 0, IPC_W); /* Redundant access check. */
75382657471SMarkus Pfeiffer 		if (!auxsemaptr) {
75482657471SMarkus Pfeiffer 			errno = EIDRM;
75582657471SMarkus Pfeiffer 			return (-1);
75682657471SMarkus Pfeiffer 		}
75782657471SMarkus Pfeiffer 
75882657471SMarkus Pfeiffer 		if (auxsemaptr != semaptr) {
75982657471SMarkus Pfeiffer 			errno = EIDRM;
76082657471SMarkus Pfeiffer 			goto done;
76182657471SMarkus Pfeiffer 		}
76282657471SMarkus Pfeiffer 
76382657471SMarkus Pfeiffer 		/* Check if another process didn't remove the semaphore. */
76482657471SMarkus Pfeiffer #ifdef SYSV_SEMS
76582657471SMarkus Pfeiffer 		if (try_rwlock_rdlock(semid, semaptr) == -1) {
76682657471SMarkus Pfeiffer #else
76782657471SMarkus Pfeiffer 		if (try_rwlock_wrlock(semid, semaptr) == -1) {
76882657471SMarkus Pfeiffer #endif
76982657471SMarkus Pfeiffer 			errno = EIDRM;
77082657471SMarkus Pfeiffer 			goto done;
77182657471SMarkus Pfeiffer 		}
77282657471SMarkus Pfeiffer 		sysv_print("semop:  good morning (eval=%d)!\n", eval);
77382657471SMarkus Pfeiffer 
77482657471SMarkus Pfeiffer 		/* The semaphore is still alive.  Readjust the count of
77582657471SMarkus Pfeiffer 		 * waiting processes.
77682657471SMarkus Pfeiffer 		 */
77782657471SMarkus Pfeiffer 		semptr = &semaptr->ds.sem_base[sopptr->sem_num];
77882657471SMarkus Pfeiffer #ifdef SYSV_SEMS
77982657471SMarkus Pfeiffer 		sysv_mutex_lock(&semptr->sem_mutex);
78082657471SMarkus Pfeiffer #endif
78182657471SMarkus Pfeiffer 		if (sopptr->sem_op == 0)
78282657471SMarkus Pfeiffer 			semptr->semzcnt--;
78382657471SMarkus Pfeiffer 		else
78482657471SMarkus Pfeiffer 			semptr->semncnt--;
78582657471SMarkus Pfeiffer #ifdef SYSV_SEMS
78682657471SMarkus Pfeiffer 		sysv_mutex_unlock(&semptr->sem_mutex);
78782657471SMarkus Pfeiffer #endif
78882657471SMarkus Pfeiffer 
78982657471SMarkus Pfeiffer 		/*
79082657471SMarkus Pfeiffer 		 * Is it really morning, or was our sleep interrupted?
79182657471SMarkus Pfeiffer 		 * (Delayed check of tsleep() return code because we
79282657471SMarkus Pfeiffer 		 * need to decrement sem[nz]cnt either way.)
79382657471SMarkus Pfeiffer 		 */
79482657471SMarkus Pfeiffer 		if (eval) {
79582657471SMarkus Pfeiffer 			eval = EINTR;
79682657471SMarkus Pfeiffer 			goto done;
79782657471SMarkus Pfeiffer 		}
79882657471SMarkus Pfeiffer 
79982657471SMarkus Pfeiffer 		sysv_print("semop:  good morning!\n");
80082657471SMarkus Pfeiffer 		/* RETRY LOOP */
80182657471SMarkus Pfeiffer }
80282657471SMarkus Pfeiffer 
80382657471SMarkus Pfeiffer donex:
80482657471SMarkus Pfeiffer 	/*
80582657471SMarkus Pfeiffer 	* Process any SEM_UNDO requests.
80682657471SMarkus Pfeiffer 	*/
80782657471SMarkus Pfeiffer 	if (do_undos) {
80882657471SMarkus Pfeiffer 		for (i = 0; i < (int)nsops; i++) {
80982657471SMarkus Pfeiffer 			/*
81082657471SMarkus Pfeiffer 			 * We only need to deal with SEM_UNDO's for non-zero
81182657471SMarkus Pfeiffer 			 * op's.
81282657471SMarkus Pfeiffer 			 */
81382657471SMarkus Pfeiffer 			int adjval;
81482657471SMarkus Pfeiffer 
81582657471SMarkus Pfeiffer 			if ((sops[i].sem_flg & SEM_UNDO) == 0)
81682657471SMarkus Pfeiffer 				continue;
81782657471SMarkus Pfeiffer 			adjval = sops[i].sem_op;
81882657471SMarkus Pfeiffer 			if (adjval == 0)
81982657471SMarkus Pfeiffer 				continue;
82082657471SMarkus Pfeiffer 			eval = semundo_adjust(semid, sops[i].sem_num, -adjval);
82182657471SMarkus Pfeiffer 			if (eval == 0)
82282657471SMarkus Pfeiffer 				continue;
82382657471SMarkus Pfeiffer 
82482657471SMarkus Pfeiffer 			/*
82582657471SMarkus Pfeiffer 			 * Oh-Oh!  We ran out of either sem_undo's or undo's.
82682657471SMarkus Pfeiffer 			 * Rollback the adjustments to this point and then
82782657471SMarkus Pfeiffer 			 * rollback the semaphore ups and down so we can return
82882657471SMarkus Pfeiffer 			 * with an error with all structures restored.  We
82982657471SMarkus Pfeiffer 			 * rollback the undo's in the exact reverse order that
83082657471SMarkus Pfeiffer 			 * we applied them.  This guarantees that we won't run
83182657471SMarkus Pfeiffer 			 * out of space as we roll things back out.
83282657471SMarkus Pfeiffer 			 */
83382657471SMarkus Pfeiffer 			for (j = i - 1; j >= 0; j--) {
83482657471SMarkus Pfeiffer 				if ((sops[j].sem_flg & SEM_UNDO) == 0)
83582657471SMarkus Pfeiffer 					continue;
83682657471SMarkus Pfeiffer 				adjval = sops[j].sem_op;
83782657471SMarkus Pfeiffer 				if (adjval == 0)
83882657471SMarkus Pfeiffer 					continue;
83982657471SMarkus Pfeiffer 				if (semundo_adjust(semid, sops[j].sem_num,
84082657471SMarkus Pfeiffer 							adjval) != 0)
84182657471SMarkus Pfeiffer 					sysv_print("semop - can't undo undos");
84282657471SMarkus Pfeiffer 			}
84382657471SMarkus Pfeiffer 
84482657471SMarkus Pfeiffer 			for (j = 0; j < (int)nsops; j++) {
84582657471SMarkus Pfeiffer 				xsemptr = &semaptr->ds.sem_base[
84682657471SMarkus Pfeiffer 					sops[j].sem_num];
84782657471SMarkus Pfeiffer #ifdef SYSV_SEMS
84882657471SMarkus Pfeiffer 				sysv_mutex_lock(&semptr->sem_mutex);
84982657471SMarkus Pfeiffer #endif
85082657471SMarkus Pfeiffer 				xsemptr->semval -= sops[j].sem_op;
85182657471SMarkus Pfeiffer 				if (xsemptr->semval == 0 &&
85282657471SMarkus Pfeiffer 						xsemptr->semzcnt > 0)
85382657471SMarkus Pfeiffer 					umtx_wakeup((int *)&xsemptr->semval, 0);
85482657471SMarkus Pfeiffer 				if (xsemptr->semval <= 0 &&
85582657471SMarkus Pfeiffer 						xsemptr->semncnt > 0)
85682657471SMarkus Pfeiffer 					umtx_wakeup((int *)&xsemptr->semval, 0); //?!
85782657471SMarkus Pfeiffer #ifdef SYSV_SEMS
85882657471SMarkus Pfeiffer 				sysv_mutex_unlock(&semptr->sem_mutex);
85982657471SMarkus Pfeiffer #endif
86082657471SMarkus Pfeiffer 			}
86182657471SMarkus Pfeiffer 
86282657471SMarkus Pfeiffer 			sysv_print("eval = %d from semundo_adjust\n", eval);
86382657471SMarkus Pfeiffer 			goto done;
86482657471SMarkus Pfeiffer 		}
86582657471SMarkus Pfeiffer 	}
86682657471SMarkus Pfeiffer 
86782657471SMarkus Pfeiffer 	/* Set sempid field for each semaphore. */
86882657471SMarkus Pfeiffer 	for (i = 0; i < (int)nsops; i++) {
86982657471SMarkus Pfeiffer 		sopptr = &sops[i];
87082657471SMarkus Pfeiffer 		semptr = &semaptr->ds.sem_base[sopptr->sem_num];
87182657471SMarkus Pfeiffer #ifdef SYSV_SEMS
87282657471SMarkus Pfeiffer 		sysv_mutex_lock(&semptr->sem_mutex);
87382657471SMarkus Pfeiffer #endif
87482657471SMarkus Pfeiffer 		semptr->sempid = getpid();
87582657471SMarkus Pfeiffer #ifdef SYSV_SEMS
87682657471SMarkus Pfeiffer 		sysv_mutex_unlock(&semptr->sem_mutex);
87782657471SMarkus Pfeiffer #endif
87882657471SMarkus Pfeiffer 	}
87982657471SMarkus Pfeiffer 
88082657471SMarkus Pfeiffer 	sysv_print("semop:  done\n");
88182657471SMarkus Pfeiffer 	semaptr->ds.sem_otime = time(NULL);
88282657471SMarkus Pfeiffer done:
88382657471SMarkus Pfeiffer 	rwlock_unlock(semid, semaptr);
88482657471SMarkus Pfeiffer done2:
88582657471SMarkus Pfeiffer 	put_shmdata(semid);
88682657471SMarkus Pfeiffer 
88782657471SMarkus Pfeiffer 	return (eval);
88882657471SMarkus Pfeiffer }
889