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