Revert most of ce42e79310
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
This commit is contained in:
parent
c007408c24
commit
9b6b793bd7
@ -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, \
|
||||
|
@ -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,
|
||||
__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)
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user