git: 9b6b793bd795 - main - Revert most of ce42e793100b460f597e4c85ec0da12e274f9394

Konstantin Belousov kib at FreeBSD.org
Wed Jul 28 10:42:16 UTC 2021


The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=9b6b793bd79521edc082a89b264a30881cb65e22

commit 9b6b793bd79521edc082a89b264a30881cb65e22
Author:     Konstantin Belousov <kib at FreeBSD.org>
AuthorDate: 2021-07-19 15:40:38 +0000
Commit:     Konstantin Belousov <kib at FreeBSD.org>
CommitDate: 2021-07-28 10:21:12 +0000

    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
---
 sys/compat/freebsd32/syscalls.master |   6 +-
 sys/kern/kern_umtx.c                 | 446 ++++++++++++++++++++++++++++++++++-
 sys/kern/syscalls.master             |  14 +-
 sys/sys/_umtx.h                      |   4 +
 sys/sys/umtx.h                       |   7 +-
 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 69a82d642d79..af787908451a 100644
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -2270,7 +2270,19 @@
 		    int sig
 		);
 	}
-434-435	AUE_NULL	RESERVED
+
+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


More information about the dev-commits-src-all mailing list