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