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