aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2021-07-19 15:40:38 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2021-08-03 09:52:36 +0000
commit2fb281fa02d34b31b25e949415cfda4549b5aa6b (patch)
treed1581f60d8da10da511876e1b10d110e486a39fc
parent7f5a4aff9b4826c1b9852f949ef97fe7ecf3efb0 (diff)
downloadsrc-2fb281fa02d34b31b25e949415cfda4549b5aa6b.tar.gz
src-2fb281fa02d34b31b25e949415cfda4549b5aa6b.zip
Revert most of ce42e793100b460f597e4c85ec0da12e274f9394
to restore ABI compatibility for pre-10.x binaries. It restores _umtx_lock() and _umtx_unlock() syscalls, and UMTX_OP_LOCK/ UMTX_OP_UNLOCK umtx_op(2) operations. UMUTEX_ERROR_CHECK flag is left out for now, I do not think it makes a difference. PR: 218571 Reviewed by: brooks (previous version) Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D31220 (cherry picked from commit 9b6b793bd79521edc082a89b264a30881cb65e22)
-rw-r--r--sys/compat/freebsd32/syscalls.master6
-rw-r--r--sys/kern/kern_umtx.c446
-rw-r--r--sys/kern/syscalls.master14
-rw-r--r--sys/sys/_umtx.h4
-rw-r--r--sys/sys/umtx.h7
5 files changed, 467 insertions, 10 deletions
diff --git a/sys/compat/freebsd32/syscalls.master b/sys/compat/freebsd32/syscalls.master
index 93d5a251d8ab..aac788bf3956 100644
--- a/sys/compat/freebsd32/syscalls.master
+++ b/sys/compat/freebsd32/syscalls.master
@@ -789,8 +789,10 @@
431 AUE_THR_EXIT NOPROTO { void thr_exit(long *state); }
432 AUE_NULL NOPROTO { int thr_self(long *id); }
433 AUE_THR_KILL NOPROTO { int thr_kill(long id, int sig); }
-434 AUE_NULL UNIMPL nosys
-435 AUE_NULL UNIMPL nosys
+434 AUE_NULL COMPAT10 { int freebsd32_umtx_lock( \
+ struct umtx *umtx); }
+435 AUE_NULL COMPAT10 { int freebsd32_umtx_unlock( \
+ struct umtx *umtx); }
436 AUE_JAIL_ATTACH NOPROTO { int jail_attach(int jid); }
437 AUE_EXTATTR_LIST_FD NOPROTO { ssize_t extattr_list_fd(int fd, \
int attrnamespace, void *data, \
diff --git a/sys/kern/kern_umtx.c b/sys/kern/kern_umtx.c
index a1dca77fe991..b76d080b8e06 100644
--- a/sys/kern/kern_umtx.c
+++ b/sys/kern/kern_umtx.c
@@ -908,6 +908,369 @@ umtx_key_release(struct umtx_key *key)
vm_object_deallocate(key->info.shared.object);
}
+#ifdef COMPAT_FREEBSD10
+/*
+ * Lock a umtx object.
+ */
+static int
+do_lock_umtx(struct thread *td, struct umtx *umtx, u_long id,
+ const struct timespec *timeout)
+{
+ struct abs_timeout timo;
+ struct umtx_q *uq;
+ u_long owner;
+ u_long old;
+ int error = 0;
+
+ uq = td->td_umtxq;
+ if (timeout != NULL)
+ abs_timeout_init(&timo, CLOCK_REALTIME, 0, timeout);
+
+ /*
+ * Care must be exercised when dealing with umtx structure. It
+ * can fault on any access.
+ */
+ for (;;) {
+ /*
+ * Try the uncontested case. This should be done in userland.
+ */
+ owner = casuword(&umtx->u_owner, UMTX_UNOWNED, id);
+
+ /* The acquire succeeded. */
+ if (owner == UMTX_UNOWNED)
+ return (0);
+
+ /* The address was invalid. */
+ if (owner == -1)
+ return (EFAULT);
+
+ /* If no one owns it but it is contested try to acquire it. */
+ if (owner == UMTX_CONTESTED) {
+ owner = casuword(&umtx->u_owner,
+ UMTX_CONTESTED, id | UMTX_CONTESTED);
+
+ if (owner == UMTX_CONTESTED)
+ return (0);
+
+ /* The address was invalid. */
+ if (owner == -1)
+ return (EFAULT);
+
+ error = thread_check_susp(td, false);
+ if (error != 0)
+ break;
+
+ /* If this failed the lock has changed, restart. */
+ continue;
+ }
+
+ /*
+ * If we caught a signal, we have retried and now
+ * exit immediately.
+ */
+ if (error != 0)
+ break;
+
+ if ((error = umtx_key_get(umtx, TYPE_SIMPLE_LOCK,
+ AUTO_SHARE, &uq->uq_key)) != 0)
+ return (error);
+
+ umtxq_lock(&uq->uq_key);
+ umtxq_busy(&uq->uq_key);
+ umtxq_insert(uq);
+ umtxq_unbusy(&uq->uq_key);
+ umtxq_unlock(&uq->uq_key);
+
+ /*
+ * Set the contested bit so that a release in user space
+ * knows to use the system call for unlock. If this fails
+ * either some one else has acquired the lock or it has been
+ * released.
+ */
+ old = casuword(&umtx->u_owner, owner, owner | UMTX_CONTESTED);
+
+ /* The address was invalid. */
+ if (old == -1) {
+ umtxq_lock(&uq->uq_key);
+ umtxq_remove(uq);
+ umtxq_unlock(&uq->uq_key);
+ umtx_key_release(&uq->uq_key);
+ return (EFAULT);
+ }
+
+ /*
+ * We set the contested bit, sleep. Otherwise the lock changed
+ * and we need to retry or we lost a race to the thread
+ * unlocking the umtx.
+ */
+ umtxq_lock(&uq->uq_key);
+ if (old == owner)
+ error = umtxq_sleep(uq, "umtx", timeout == NULL ? NULL :
+ &timo);
+ umtxq_remove(uq);
+ umtxq_unlock(&uq->uq_key);
+ umtx_key_release(&uq->uq_key);
+
+ if (error == 0)
+ error = thread_check_susp(td, false);
+ }
+
+ if (timeout == NULL) {
+ /* Mutex locking is restarted if it is interrupted. */
+ if (error == EINTR)
+ error = ERESTART;
+ } else {
+ /* Timed-locking is not restarted. */
+ if (error == ERESTART)
+ error = EINTR;
+ }
+ return (error);
+}
+
+/*
+ * Unlock a umtx object.
+ */
+static int
+do_unlock_umtx(struct thread *td, struct umtx *umtx, u_long id)
+{
+ struct umtx_key key;
+ u_long owner;
+ u_long old;
+ int error;
+ int count;
+
+ /*
+ * Make sure we own this mtx.
+ */
+ owner = fuword(__DEVOLATILE(u_long *, &umtx->u_owner));
+ if (owner == -1)
+ return (EFAULT);
+
+ if ((owner & ~UMTX_CONTESTED) != id)
+ return (EPERM);
+
+ /* This should be done in userland */
+ if ((owner & UMTX_CONTESTED) == 0) {
+ old = casuword(&umtx->u_owner, owner, UMTX_UNOWNED);
+ if (old == -1)
+ return (EFAULT);
+ if (old == owner)
+ return (0);
+ owner = old;
+ }
+
+ /* We should only ever be in here for contested locks */
+ if ((error = umtx_key_get(umtx, TYPE_SIMPLE_LOCK, AUTO_SHARE,
+ &key)) != 0)
+ return (error);
+
+ umtxq_lock(&key);
+ umtxq_busy(&key);
+ count = umtxq_count(&key);
+ umtxq_unlock(&key);
+
+ /*
+ * When unlocking the umtx, it must be marked as unowned if
+ * there is zero or one thread only waiting for it.
+ * Otherwise, it must be marked as contested.
+ */
+ old = casuword(&umtx->u_owner, owner,
+ count <= 1 ? UMTX_UNOWNED : UMTX_CONTESTED);
+ umtxq_lock(&key);
+ umtxq_signal(&key,1);
+ umtxq_unbusy(&key);
+ umtxq_unlock(&key);
+ umtx_key_release(&key);
+ if (old == -1)
+ return (EFAULT);
+ if (old != owner)
+ return (EINVAL);
+ return (0);
+}
+
+#ifdef COMPAT_FREEBSD32
+
+/*
+ * Lock a umtx object.
+ */
+static int
+do_lock_umtx32(struct thread *td, uint32_t *m, uint32_t id,
+ const struct timespec *timeout)
+{
+ struct abs_timeout timo;
+ struct umtx_q *uq;
+ uint32_t owner;
+ uint32_t old;
+ int error = 0;
+
+ uq = td->td_umtxq;
+
+ if (timeout != NULL)
+ abs_timeout_init(&timo, CLOCK_REALTIME, 0, timeout);
+
+ /*
+ * Care must be exercised when dealing with umtx structure. It
+ * can fault on any access.
+ */
+ for (;;) {
+ /*
+ * Try the uncontested case. This should be done in userland.
+ */
+ owner = casuword32(m, UMUTEX_UNOWNED, id);
+
+ /* The acquire succeeded. */
+ if (owner == UMUTEX_UNOWNED)
+ return (0);
+
+ /* The address was invalid. */
+ if (owner == -1)
+ return (EFAULT);
+
+ /* If no one owns it but it is contested try to acquire it. */
+ if (owner == UMUTEX_CONTESTED) {
+ owner = casuword32(m,
+ UMUTEX_CONTESTED, id | UMUTEX_CONTESTED);
+ if (owner == UMUTEX_CONTESTED)
+ return (0);
+
+ /* The address was invalid. */
+ if (owner == -1)
+ return (EFAULT);
+
+ error = thread_check_susp(td, false);
+ if (error != 0)
+ break;
+
+ /* If this failed the lock has changed, restart. */
+ continue;
+ }
+
+ /*
+ * If we caught a signal, we have retried and now
+ * exit immediately.
+ */
+ if (error != 0)
+ return (error);
+
+ if ((error = umtx_key_get(m, TYPE_SIMPLE_LOCK,
+ AUTO_SHARE, &uq->uq_key)) != 0)
+ return (error);
+
+ umtxq_lock(&uq->uq_key);
+ umtxq_busy(&uq->uq_key);
+ umtxq_insert(uq);
+ umtxq_unbusy(&uq->uq_key);
+ umtxq_unlock(&uq->uq_key);
+
+ /*
+ * Set the contested bit so that a release in user space
+ * knows to use the system call for unlock. If this fails
+ * either some one else has acquired the lock or it has been
+ * released.
+ */
+ old = casuword32(m, owner, owner | UMUTEX_CONTESTED);
+
+ /* The address was invalid. */
+ if (old == -1) {
+ umtxq_lock(&uq->uq_key);
+ umtxq_remove(uq);
+ umtxq_unlock(&uq->uq_key);
+ umtx_key_release(&uq->uq_key);
+ return (EFAULT);
+ }
+
+ /*
+ * We set the contested bit, sleep. Otherwise the lock changed
+ * and we need to retry or we lost a race to the thread
+ * unlocking the umtx.
+ */
+ umtxq_lock(&uq->uq_key);
+ if (old == owner)
+ error = umtxq_sleep(uq, "umtx", timeout == NULL ?
+ NULL : &timo);
+ umtxq_remove(uq);
+ umtxq_unlock(&uq->uq_key);
+ umtx_key_release(&uq->uq_key);
+
+ if (error == 0)
+ error = thread_check_susp(td, false);
+ }
+
+ if (timeout == NULL) {
+ /* Mutex locking is restarted if it is interrupted. */
+ if (error == EINTR)
+ error = ERESTART;
+ } else {
+ /* Timed-locking is not restarted. */
+ if (error == ERESTART)
+ error = EINTR;
+ }
+ return (error);
+}
+
+/*
+ * Unlock a umtx object.
+ */
+static int
+do_unlock_umtx32(struct thread *td, uint32_t *m, uint32_t id)
+{
+ struct umtx_key key;
+ uint32_t owner;
+ uint32_t old;
+ int error;
+ int count;
+
+ /*
+ * Make sure we own this mtx.
+ */
+ owner = fuword32(m);
+ if (owner == -1)
+ return (EFAULT);
+
+ if ((owner & ~UMUTEX_CONTESTED) != id)
+ return (EPERM);
+
+ /* This should be done in userland */
+ if ((owner & UMUTEX_CONTESTED) == 0) {
+ old = casuword32(m, owner, UMUTEX_UNOWNED);
+ if (old == -1)
+ return (EFAULT);
+ if (old == owner)
+ return (0);
+ owner = old;
+ }
+
+ /* We should only ever be in here for contested locks */
+ if ((error = umtx_key_get(m, TYPE_SIMPLE_LOCK, AUTO_SHARE,
+ &key)) != 0)
+ return (error);
+
+ umtxq_lock(&key);
+ umtxq_busy(&key);
+ count = umtxq_count(&key);
+ umtxq_unlock(&key);
+
+ /*
+ * When unlocking the umtx, it must be marked as unowned if
+ * there is zero or one thread only waiting for it.
+ * Otherwise, it must be marked as contested.
+ */
+ old = casuword32(m, owner,
+ count <= 1 ? UMUTEX_UNOWNED : UMUTEX_CONTESTED);
+ umtxq_lock(&key);
+ umtxq_signal(&key,1);
+ umtxq_unbusy(&key);
+ umtxq_unlock(&key);
+ umtx_key_release(&key);
+ if (old == -1)
+ return (EFAULT);
+ if (old != owner)
+ return (EINVAL);
+ return (0);
+}
+#endif /* COMPAT_FREEBSD32 */
+#endif /* COMPAT_FREEBSD10 */
+
/*
* Fetch and compare value, sleep on the address if value is not changed.
*/
@@ -3397,6 +3760,21 @@ do_sem2_wake(struct thread *td, struct _usem2 *sem)
return (error);
}
+#ifdef COMPAT_FREEBSD10
+int
+freebsd10__umtx_lock(struct thread *td, struct freebsd10__umtx_lock_args *uap)
+{
+ return (do_lock_umtx(td, uap->umtx, td->td_tid, 0));
+}
+
+int
+freebsd10__umtx_unlock(struct thread *td,
+ struct freebsd10__umtx_unlock_args *uap)
+{
+ return (do_unlock_umtx(td, uap->umtx, td->td_tid));
+}
+#endif
+
inline int
umtx_copyin_timeout(const void *uaddr, struct timespec *tsp)
{
@@ -3456,13 +3834,50 @@ umtx_copyout_timeout(void *uaddr, size_t sz, struct timespec *tsp)
return (copyout(tsp, uaddr, sizeof(*tsp)));
}
+#ifdef COMPAT_FREEBSD10
static int
-__umtx_op_unimpl(struct thread *td, struct _umtx_op_args *uap,
- const struct umtx_copyops *ops __unused)
+__umtx_op_lock_umtx(struct thread *td, struct _umtx_op_args *uap,
+ const struct umtx_copyops *ops)
+{
+ struct timespec *ts, timeout;
+ int error;
+
+ /* Allow a null timespec (wait forever). */
+ if (uap->uaddr2 == NULL)
+ ts = NULL;
+ else {
+ error = ops->copyin_timeout(uap->uaddr2, &timeout);
+ if (error != 0)
+ return (error);
+ ts = &timeout;
+ }
+#ifdef COMPAT_FREEBSD32
+ if (ops->compat32)
+ return (do_lock_umtx32(td, uap->obj, uap->val, ts));
+#endif
+ return (do_lock_umtx(td, uap->obj, uap->val, ts));
+}
+
+static int
+__umtx_op_unlock_umtx(struct thread *td, struct _umtx_op_args *uap,
+ const struct umtx_copyops *ops)
{
+#ifdef COMPAT_FREEBSD32
+ if (ops->compat32)
+ return (do_unlock_umtx32(td, uap->obj, uap->val));
+#endif
+ return (do_unlock_umtx(td, uap->obj, uap->val));
+}
+#endif /* COMPAT_FREEBSD10 */
+#if !defined(COMPAT_FREEBSD10)
+static int
+__umtx_op_unimpl(struct thread *td __unused, struct _umtx_op_args *uap __unused,
+ const struct umtx_copyops *ops __unused)
+{
return (EOPNOTSUPP);
}
+#endif /* COMPAT_FREEBSD10 */
static int
__umtx_op_wait(struct thread *td, struct _umtx_op_args *uap,
@@ -4358,8 +4773,13 @@ typedef int (*_umtx_op_func)(struct thread *td, struct _umtx_op_args *uap,
const struct umtx_copyops *umtx_ops);
static const _umtx_op_func op_table[] = {
- [UMTX_OP_RESERVED0] = __umtx_op_unimpl,
- [UMTX_OP_RESERVED1] = __umtx_op_unimpl,
+#ifdef COMPAT_FREEBSD10
+ [UMTX_OP_LOCK] = __umtx_op_lock_umtx,
+ [UMTX_OP_UNLOCK] = __umtx_op_unlock_umtx,
+#else
+ [UMTX_OP_LOCK] = __umtx_op_unimpl,
+ [UMTX_OP_UNLOCK] = __umtx_op_unimpl,
+#endif
[UMTX_OP_WAIT] = __umtx_op_wait,
[UMTX_OP_WAKE] = __umtx_op_wake,
[UMTX_OP_MUTEX_TRYLOCK] = __umtx_op_trylock_umutex,
@@ -4480,6 +4900,22 @@ sys__umtx_op(struct thread *td, struct _umtx_op_args *uap)
}
#ifdef COMPAT_FREEBSD32
+#ifdef COMPAT_FREEBSD10
+int
+freebsd10_freebsd32_umtx_lock(struct thread *td,
+ struct freebsd10_freebsd32_umtx_lock_args *uap)
+{
+ return (do_lock_umtx32(td, (uint32_t *)uap->umtx, td->td_tid, NULL));
+}
+
+int
+freebsd10_freebsd32_umtx_unlock(struct thread *td,
+ struct freebsd10_freebsd32_umtx_unlock_args *uap)
+{
+ return (do_unlock_umtx32(td, (uint32_t *)uap->umtx, td->td_tid));
+}
+#endif /* COMPAT_FREEBSD10 */
+
int
freebsd32__umtx_op(struct thread *td, struct freebsd32__umtx_op_args *uap)
{
@@ -4487,7 +4923,7 @@ freebsd32__umtx_op(struct thread *td, struct freebsd32__umtx_op_args *uap)
return (kern__umtx_op(td, uap->obj, uap->op, uap->val, uap->uaddr,
uap->uaddr2, &umtx_native_ops32));
}
-#endif
+#endif /* COMPAT_FREEBSD32 */
void
umtx_thread_init(struct thread *td)
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
index 5b8e8049927c..d3ec771aac6f 100644
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -2288,7 +2288,19 @@
int sig
);
}
-434-435 AUE_NULL UNIMPL nosys
+
+434 AUE_NULL COMPAT10 {
+ int _umtx_lock(
+ _Inout_ struct umtx *umtx
+ );
+ }
+
+435 AUE_NULL COMPAT10 {
+ int _umtx_unlock(
+ _Inout_ struct umtx *umtx
+ );
+ }
+
436 AUE_JAIL_ATTACH STD {
int jail_attach(
int jid
diff --git a/sys/sys/_umtx.h b/sys/sys/_umtx.h
index b9d10b756a0c..d280c7d3db19 100644
--- a/sys/sys/_umtx.h
+++ b/sys/sys/_umtx.h
@@ -35,6 +35,10 @@
#include <sys/_types.h>
#include <sys/_timespec.h>
+struct umtx {
+ volatile unsigned long u_owner; /* Owner of the mutex. */
+};
+
struct umutex {
volatile __lwpid_t m_owner; /* Owner of the mutex */
__uint32_t m_flags; /* Flags of the mutex */
diff --git a/sys/sys/umtx.h b/sys/sys/umtx.h
index 6753a6217688..60e9dccdad91 100644
--- a/sys/sys/umtx.h
+++ b/sys/sys/umtx.h
@@ -34,6 +34,9 @@
#include <sys/_umtx.h>
+#define UMTX_UNOWNED 0x0
+#define UMTX_CONTESTED LONG_MIN
+
/* Common lock flags */
#define USYNC_PROCESS_SHARED 0x0001 /* Process shared sync objs */
@@ -73,8 +76,8 @@
#define USEM_COUNT(c) ((c) & USEM_MAX_COUNT)
/* op code for _umtx_op */
-#define UMTX_OP_RESERVED0 0
-#define UMTX_OP_RESERVED1 1
+#define UMTX_OP_LOCK 0 /* COMPAT10 */
+#define UMTX_OP_UNLOCK 1 /* COMPAT10 */
#define UMTX_OP_WAIT 2
#define UMTX_OP_WAKE 3
#define UMTX_OP_MUTEX_TRYLOCK 4