summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/semaphore.h3
-rw-r--r--lib/libc/gen/sem_new.c27
-rw-r--r--sys/kern/kern_umtx.c184
-rw-r--r--sys/sys/_umtx.h5
-rw-r--r--sys/sys/umtx.h13
-rw-r--r--usr.bin/truss/syscalls.c1
6 files changed, 212 insertions, 21 deletions
diff --git a/include/semaphore.h b/include/semaphore.h
index 3531353..218de99 100644
--- a/include/semaphore.h
+++ b/include/semaphore.h
@@ -38,7 +38,8 @@
struct _sem {
__uint32_t _magic;
- struct _usem _kern;
+ struct _usem2 _kern;
+ __uint32_t _padding; /* Preserve structure size */
};
typedef struct _sem sem_t;
diff --git a/lib/libc/gen/sem_new.c b/lib/libc/gen/sem_new.c
index ec1a2fd..3ee0272 100644
--- a/lib/libc/gen/sem_new.c
+++ b/lib/libc/gen/sem_new.c
@@ -61,7 +61,9 @@ __weak_reference(_sem_unlink, sem_unlink);
__weak_reference(_sem_wait, sem_wait);
#define SEM_PREFIX "/tmp/SEMD"
-#define SEM_MAGIC ((u_int32_t)0x73656d31)
+#define SEM_MAGIC ((u_int32_t)0x73656d32)
+
+_Static_assert(SEM_VALUE_MAX <= USEM_MAX_COUNT, "SEM_VALUE_MAX too large");
struct sem_nameinfo {
int open_count;
@@ -131,7 +133,6 @@ _sem_init(sem_t *sem, int pshared, unsigned int value)
bzero(sem, sizeof(sem_t));
sem->_magic = SEM_MAGIC;
sem->_kern._count = (u_int32_t)value;
- sem->_kern._has_waiters = 0;
sem->_kern._flags = pshared ? USYNC_PROCESS_SHARED : 0;
return (0);
}
@@ -212,7 +213,6 @@ _sem_open(const char *name, int flags, ...)
sem_t tmp;
tmp._magic = SEM_MAGIC;
- tmp._kern._has_waiters = 0;
tmp._kern._count = value;
tmp._kern._flags = USYNC_PROCESS_SHARED | SEM_NAMED;
if (_write(fd, &tmp, sizeof(tmp)) != sizeof(tmp))
@@ -332,18 +332,18 @@ _sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
if (sem_check_validity(sem) != 0)
return (-1);
- *sval = (int)sem->_kern._count;
+ *sval = (int)USEM_COUNT(sem->_kern._count);
return (0);
}
static __inline int
-usem_wake(struct _usem *sem)
+usem_wake(struct _usem2 *sem)
{
- return _umtx_op(sem, UMTX_OP_SEM_WAKE, 0, NULL, NULL);
+ return _umtx_op(sem, UMTX_OP_SEM2_WAKE, 0, NULL, NULL);
}
static __inline int
-usem_wait(struct _usem *sem, const struct timespec *abstime)
+usem_wait(struct _usem2 *sem, const struct timespec *abstime)
{
struct _umtx_time *tm_p, timeout;
size_t tm_size;
@@ -358,7 +358,7 @@ usem_wait(struct _usem *sem, const struct timespec *abstime)
tm_p = &timeout;
tm_size = sizeof(timeout);
}
- return _umtx_op(sem, UMTX_OP_SEM_WAIT, 0,
+ return _umtx_op(sem, UMTX_OP_SEM2_WAIT, 0,
(void *)tm_size, __DECONST(void*, tm_p));
}
@@ -370,7 +370,7 @@ _sem_trywait(sem_t *sem)
if (sem_check_validity(sem) != 0)
return (-1);
- while ((val = sem->_kern._count) > 0) {
+ while (USEM_COUNT(val = sem->_kern._count) > 0) {
if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1))
return (0);
}
@@ -390,7 +390,7 @@ _sem_timedwait(sem_t * __restrict sem,
retval = 0;
_pthread_testcancel();
for (;;) {
- while ((val = sem->_kern._count) > 0) {
+ while (USEM_COUNT(val = sem->_kern._count) > 0) {
if (atomic_cmpset_acq_int(&sem->_kern._count, val, val - 1))
return (0);
}
@@ -439,9 +439,10 @@ _sem_post(sem_t *sem)
do {
count = sem->_kern._count;
- if (count + 1 > SEM_VALUE_MAX)
+ if (USEM_COUNT(count) + 1 > SEM_VALUE_MAX)
return (EOVERFLOW);
- } while(!atomic_cmpset_rel_int(&sem->_kern._count, count, count+1));
- (void)usem_wake(&sem->_kern);
+ } while (!atomic_cmpset_rel_int(&sem->_kern._count, count, count + 1));
+ if (count & USEM_HAS_WAITERS)
+ usem_wake(&sem->_kern);
return (0);
}
diff --git a/sys/kern/kern_umtx.c b/sys/kern/kern_umtx.c
index 7cfef38..472391e 100644
--- a/sys/kern/kern_umtx.c
+++ b/sys/kern/kern_umtx.c
@@ -2710,6 +2710,7 @@ out:
return (error);
}
+#if defined(COMPAT_FREEBSD9) || defined(COMPAT_FREEBSD10)
static int
do_sem_wait(struct thread *td, struct _usem *sem, struct _umtx_time *timeout)
{
@@ -2731,7 +2732,7 @@ do_sem_wait(struct thread *td, struct _usem *sem, struct _umtx_time *timeout)
umtxq_busy(&uq->uq_key);
umtxq_insert(uq);
umtxq_unlock(&uq->uq_key);
- casuword32(__DEVOLATILE(uint32_t *, &sem->_has_waiters), 0, 1);
+ casuword32(&sem->_has_waiters, 0, 1);
count = fuword32(__DEVOLATILE(uint32_t *, &sem->_count));
if (count != 0) {
umtxq_lock(&uq->uq_key);
@@ -2761,7 +2762,7 @@ do_sem_wait(struct thread *td, struct _usem *sem, struct _umtx_time *timeout)
}
/*
- * Signal a userland condition variable.
+ * Signal a userland semaphore.
*/
static int
do_sem_wake(struct thread *td, struct _usem *sem)
@@ -2795,6 +2796,119 @@ do_sem_wake(struct thread *td, struct _usem *sem)
umtx_key_release(&key);
return (error);
}
+#endif
+
+static int
+do_sem2_wait(struct thread *td, struct _usem2 *sem, struct _umtx_time *timeout)
+{
+ struct abs_timeout timo;
+ struct umtx_q *uq;
+ uint32_t count, flags;
+ int error;
+
+ uq = td->td_umtxq;
+ flags = fuword32(&sem->_flags);
+ error = umtx_key_get(sem, TYPE_SEM, GET_SHARE(flags), &uq->uq_key);
+ if (error != 0)
+ return (error);
+
+ if (timeout != NULL)
+ abs_timeout_init2(&timo, timeout);
+
+ umtxq_lock(&uq->uq_key);
+ umtxq_busy(&uq->uq_key);
+ umtxq_insert(uq);
+ umtxq_unlock(&uq->uq_key);
+ count = fuword32(__DEVOLATILE(uint32_t *, &sem->_count));
+ if (count == -1) {
+ umtxq_lock(&uq->uq_key);
+ umtxq_unbusy(&uq->uq_key);
+ umtxq_remove(uq);
+ umtxq_unlock(&uq->uq_key);
+ umtx_key_release(&uq->uq_key);
+ return (EFAULT);
+ }
+ for (;;) {
+ if (USEM_COUNT(count) != 0) {
+ umtxq_lock(&uq->uq_key);
+ umtxq_unbusy(&uq->uq_key);
+ umtxq_remove(uq);
+ umtxq_unlock(&uq->uq_key);
+ umtx_key_release(&uq->uq_key);
+ return (0);
+ }
+ if (count == USEM_HAS_WAITERS)
+ break;
+ count = casuword32(&sem->_count, 0, USEM_HAS_WAITERS);
+ if (count == -1) {
+ umtxq_lock(&uq->uq_key);
+ umtxq_unbusy(&uq->uq_key);
+ umtxq_remove(uq);
+ umtxq_unlock(&uq->uq_key);
+ umtx_key_release(&uq->uq_key);
+ return (EFAULT);
+ }
+ if (count == 0)
+ break;
+ }
+ umtxq_lock(&uq->uq_key);
+ umtxq_unbusy(&uq->uq_key);
+
+ error = umtxq_sleep(uq, "usem", timeout == NULL ? NULL : &timo);
+
+ if ((uq->uq_flags & UQF_UMTXQ) == 0)
+ error = 0;
+ else {
+ umtxq_remove(uq);
+ /* A relative timeout cannot be restarted. */
+ if (error == ERESTART && timeout != NULL &&
+ (timeout->_flags & UMTX_ABSTIME) == 0)
+ error = EINTR;
+ }
+ umtxq_unlock(&uq->uq_key);
+ umtx_key_release(&uq->uq_key);
+ return (error);
+}
+
+/*
+ * Signal a userland semaphore.
+ */
+static int
+do_sem2_wake(struct thread *td, struct _usem2 *sem)
+{
+ struct umtx_key key;
+ int error, cnt;
+ uint32_t count, flags;
+
+ flags = fuword32(&sem->_flags);
+ if ((error = umtx_key_get(sem, TYPE_SEM, GET_SHARE(flags), &key)) != 0)
+ return (error);
+ umtxq_lock(&key);
+ umtxq_busy(&key);
+ cnt = umtxq_count(&key);
+ if (cnt > 0) {
+ umtxq_signal(&key, 1);
+
+ /*
+ * If this was the last sleeping thread, clear the waiters
+ * flag in _count.
+ */
+ if (cnt == 1) {
+ umtxq_unlock(&key);
+ count = fuword32(&sem->_count);
+ while (count != -1 && count & USEM_HAS_WAITERS)
+ count = casuword32(&sem->_count, count,
+ count & ~USEM_HAS_WAITERS);
+ if (count == -1)
+ error = EFAULT;
+ umtxq_lock(&key);
+ }
+ }
+ umtxq_unbusy(&key);
+ umtxq_unlock(&key);
+ umtx_key_release(&key);
+ return (error);
+}
inline int
umtx_copyin_timeout(const void *addr, struct timespec *tsp)
@@ -3066,6 +3180,7 @@ __umtx_op_rw_unlock(struct thread *td, struct _umtx_op_args *uap)
return do_rw_unlock(td, uap->obj);
}
+#if defined(COMPAT_FREEBSD9) || defined(COMPAT_FREEBSD10)
static int
__umtx_op_sem_wait(struct thread *td, struct _umtx_op_args *uap)
{
@@ -3090,6 +3205,7 @@ __umtx_op_sem_wake(struct thread *td, struct _umtx_op_args *uap)
{
return do_sem_wake(td, uap->obj);
}
+#endif
static int
__umtx_op_wake2_umutex(struct thread *td, struct _umtx_op_args *uap)
@@ -3097,6 +3213,31 @@ __umtx_op_wake2_umutex(struct thread *td, struct _umtx_op_args *uap)
return do_wake2_umutex(td, uap->obj, uap->val);
}
+static int
+__umtx_op_sem2_wait(struct thread *td, struct _umtx_op_args *uap)
+{
+ struct _umtx_time *tm_p, timeout;
+ int error;
+
+ /* Allow a null timespec (wait forever). */
+ if (uap->uaddr2 == NULL)
+ tm_p = NULL;
+ else {
+ error = umtx_copyin_umtx_time(
+ uap->uaddr2, (size_t)uap->uaddr1, &timeout);
+ if (error != 0)
+ return (error);
+ tm_p = &timeout;
+ }
+ return (do_sem2_wait(td, uap->obj, tm_p));
+}
+
+static int
+__umtx_op_sem2_wake(struct thread *td, struct _umtx_op_args *uap)
+{
+ return do_sem2_wake(td, uap->obj);
+}
+
typedef int (*_umtx_op_func)(struct thread *td, struct _umtx_op_args *uap);
static _umtx_op_func op_table[] = {
@@ -3119,10 +3260,17 @@ static _umtx_op_func op_table[] = {
__umtx_op_wake_private, /* UMTX_OP_WAKE_PRIVATE */
__umtx_op_wait_umutex, /* UMTX_OP_MUTEX_WAIT */
__umtx_op_wake_umutex, /* UMTX_OP_MUTEX_WAKE */
+#if defined(COMPAT_FREEBSD9) || defined(COMPAT_FREEBSD10)
__umtx_op_sem_wait, /* UMTX_OP_SEM_WAIT */
__umtx_op_sem_wake, /* UMTX_OP_SEM_WAKE */
+#else
+ __umtx_op_unimpl, /* UMTX_OP_SEM_WAIT */
+ __umtx_op_unimpl, /* UMTX_OP_SEM_WAKE */
+#endif
__umtx_op_nwake_private, /* UMTX_OP_NWAKE_PRIVATE */
- __umtx_op_wake2_umutex /* UMTX_OP_MUTEX_WAKE2 */
+ __umtx_op_wake2_umutex, /* UMTX_OP_MUTEX_WAKE2 */
+ __umtx_op_sem2_wait, /* UMTX_OP_SEM2_WAIT */
+ __umtx_op_sem2_wake, /* UMTX_OP_SEM2_WAKE */
};
int
@@ -3320,6 +3468,7 @@ __umtx_op_wait_uint_private_compat32(struct thread *td, struct _umtx_op_args *ua
return do_wait(td, uap->obj, uap->val, tm_p, 1, 1);
}
+#if defined(COMPAT_FREEBSD9) || defined(COMPAT_FREEBSD10)
static int
__umtx_op_sem_wait_compat32(struct thread *td, struct _umtx_op_args *uap)
{
@@ -3338,6 +3487,26 @@ __umtx_op_sem_wait_compat32(struct thread *td, struct _umtx_op_args *uap)
}
return (do_sem_wait(td, uap->obj, tm_p));
}
+#endif
+
+static int
+__umtx_op_sem2_wait_compat32(struct thread *td, struct _umtx_op_args *uap)
+{
+ struct _umtx_time *tm_p, timeout;
+ int error;
+
+ /* Allow a null timespec (wait forever). */
+ if (uap->uaddr2 == NULL)
+ tm_p = NULL;
+ else {
+ error = umtx_copyin_umtx_time32(uap->uaddr2,
+ (size_t)uap->uaddr1, &timeout);
+ if (error != 0)
+ return (error);
+ tm_p = &timeout;
+ }
+ return (do_sem2_wait(td, uap->obj, tm_p));
+}
static int
__umtx_op_nwake_private32(struct thread *td, struct _umtx_op_args *uap)
@@ -3385,10 +3554,17 @@ static _umtx_op_func op_table_compat32[] = {
__umtx_op_wake_private, /* UMTX_OP_WAKE_PRIVATE */
__umtx_op_wait_umutex_compat32, /* UMTX_OP_MUTEX_WAIT */
__umtx_op_wake_umutex, /* UMTX_OP_MUTEX_WAKE */
+#if defined(COMPAT_FREEBSD9) || defined(COMPAT_FREEBSD10)
__umtx_op_sem_wait_compat32, /* UMTX_OP_SEM_WAIT */
__umtx_op_sem_wake, /* UMTX_OP_SEM_WAKE */
+#else
+ __umtx_op_unimpl, /* UMTX_OP_SEM_WAIT */
+ __umtx_op_unimpl, /* UMTX_OP_SEM_WAKE */
+#endif
__umtx_op_nwake_private32, /* UMTX_OP_NWAKE_PRIVATE */
- __umtx_op_wake2_umutex /* UMTX_OP_MUTEX_WAKE2 */
+ __umtx_op_wake2_umutex, /* UMTX_OP_MUTEX_WAKE2 */
+ __umtx_op_sem2_wait_compat32, /* UMTX_OP_SEM2_WAIT */
+ __umtx_op_sem2_wake, /* UMTX_OP_SEM2_WAKE */
};
int
diff --git a/sys/sys/_umtx.h b/sys/sys/_umtx.h
index dbfaa65..e8a400f 100644
--- a/sys/sys/_umtx.h
+++ b/sys/sys/_umtx.h
@@ -61,6 +61,11 @@ struct _usem {
__uint32_t _flags;
};
+struct _usem2 {
+ volatile __uint32_t _count; /* Waiters flag in high bit. */
+ __uint32_t _flags;
+};
+
struct _umtx_time {
struct timespec _timeout;
__uint32_t _flags;
diff --git a/sys/sys/umtx.h b/sys/sys/umtx.h
index 40740b5..07794df 100644
--- a/sys/sys/umtx.h
+++ b/sys/sys/umtx.h
@@ -52,6 +52,11 @@
/* _usem flags */
#define SEM_NAMED 0x0002
+/* _usem2 count field */
+#define USEM_HAS_WAITERS 0x80000000U
+#define USEM_MAX_COUNT 0x7fffffffU
+#define USEM_COUNT(c) ((c) & USEM_MAX_COUNT)
+
/* op code for _umtx_op */
#define UMTX_OP_RESERVED0 0
#define UMTX_OP_RESERVED1 1
@@ -72,11 +77,13 @@
#define UMTX_OP_WAKE_PRIVATE 16
#define UMTX_OP_MUTEX_WAIT 17
#define UMTX_OP_MUTEX_WAKE 18 /* deprecated */
-#define UMTX_OP_SEM_WAIT 19
-#define UMTX_OP_SEM_WAKE 20
+#define UMTX_OP_SEM_WAIT 19 /* deprecated */
+#define UMTX_OP_SEM_WAKE 20 /* deprecated */
#define UMTX_OP_NWAKE_PRIVATE 21
#define UMTX_OP_MUTEX_WAKE2 22
-#define UMTX_OP_MAX 23
+#define UMTX_OP_SEM2_WAIT 23
+#define UMTX_OP_SEM2_WAKE 24
+#define UMTX_OP_MAX 25
/* Flags for UMTX_OP_CV_WAIT */
#define CVWAIT_CHECK_UNPARKING 0x01
diff --git a/usr.bin/truss/syscalls.c b/usr.bin/truss/syscalls.c
index ff170f1..efd27e9 100644
--- a/usr.bin/truss/syscalls.c
+++ b/usr.bin/truss/syscalls.c
@@ -424,6 +424,7 @@ static struct xlat umtx_ops[] = {
X(UMTX_OP_WAIT_UINT_PRIVATE) X(UMTX_OP_WAKE_PRIVATE)
X(UMTX_OP_MUTEX_WAIT) X(UMTX_OP_MUTEX_WAKE) X(UMTX_OP_SEM_WAIT)
X(UMTX_OP_SEM_WAKE) X(UMTX_OP_NWAKE_PRIVATE) X(UMTX_OP_MUTEX_WAKE2)
+ X(UMTX_OP_SEM2_WAIT) X(UMTX_OP_SEM2_WAKE)
XEND
};
OpenPOWER on IntegriCloud