[2/2] _umtx_op: introduce 32-bit/i386 flags for operations
This patch takes advantage of the consolidation that happened to provide two flags that can be used with the native _umtx_op(2): UMTX_OP___32BIT and UMTX_OP__I386. UMTX_OP__32BIT iindicates that we are being provided with 32-bit structures. Note that this flag alone indicates a 64bit time_t, since this is the majority case. UMTX_OP__I386 has been provided so that we can emulate i386 as well, regardless of whether the host is amd64 or not. Both imply a different set of copyops in sysumtx_op. freebsd32__umtx_op simply ignores the flags, since it's already doing a 32-bit operation and it's unlikely we'll be running an emulator under compat32. Future work could consider it, but the author sees little benefit. This will be used by qemu-bsd-user to pass on all _umtx_op calls to the native interface as long as the host/target endianness matches, effectively eliminating most if not all of the remaining unresolved deadlocks for most. This version changed a fair amount from what was under review, mostly in response to refactoring of the prereq reorganization and battle-testing it with qemu-bsd-user. The main changes are as follows: 1.) The i386 flag got renamed to omit '32BIT' since this is redundant. 2.) The flags are now properly handled on 32-bit platforms to emulate other 32-bit platforms. 3.) Robust list handling was fixed, and the 32-bit functionality that was previously gated by COMPAT_FREEBSD32 is now unconditional. 4.) Robust list handling was also improved, including the error reported when a process has already registered 32-bit ABI lists and also detecting if native robust lists have already been registered. Both scenarios now return EBUSY rather than EINVAL, because the input is technically valid but we're too busy with another ABI's lists. libsysdecode/kdump/truss support will go into review soon-ish, along with the associated manpage update. Reviewed by: kib (earlier version) MFC after: 3 weeks
This commit is contained in:
parent
cbf33b3654
commit
e0cb5b2a77
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=367930
@ -75,8 +75,8 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/atomic.h>
|
||||
#include <machine/cpu.h>
|
||||
|
||||
#ifdef COMPAT_FREEBSD32
|
||||
#include <compat/freebsd32/freebsd32.h>
|
||||
#ifdef COMPAT_FREEBSD32
|
||||
#include <compat/freebsd32/freebsd32_proto.h>
|
||||
#endif
|
||||
|
||||
@ -232,7 +232,6 @@ struct umtx_copyops {
|
||||
const bool compat32;
|
||||
};
|
||||
|
||||
#ifdef COMPAT_FREEBSD32
|
||||
struct umtx_time32 {
|
||||
struct timespec32 _timeout;
|
||||
uint32_t _flags;
|
||||
@ -257,7 +256,6 @@ struct umutex32 {
|
||||
_Static_assert(sizeof(struct umutex) == sizeof(struct umutex32), "umutex32");
|
||||
_Static_assert(__offsetof(struct umutex, m_spare[0]) ==
|
||||
__offsetof(struct umutex32, m_spare[0]), "m_spare32");
|
||||
#endif
|
||||
|
||||
int umtx_shm_vnobj_persistent = 0;
|
||||
SYSCTL_INT(_kern_ipc, OID_AUTO, umtx_vnode_persistent, CTLFLAG_RWTUN,
|
||||
@ -4167,6 +4165,15 @@ __umtx_op_robust_lists(struct thread *td, struct _umtx_op_args *uap,
|
||||
struct umtx_robust_lists_params rb;
|
||||
int error;
|
||||
|
||||
if (ops->compat32) {
|
||||
if ((td->td_pflags2 & TDP2_COMPAT32RB) == 0 &&
|
||||
(td->td_rb_list != 0 || td->td_rbp_list != 0 ||
|
||||
td->td_rb_inact != 0))
|
||||
return (EBUSY);
|
||||
} else if ((td->td_pflags2 & TDP2_COMPAT32RB) != 0) {
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
bzero(&rb, sizeof(rb));
|
||||
error = ops->copyin_robust_lists(uap->uaddr1, uap->val, &rb);
|
||||
if (error != 0)
|
||||
@ -4174,8 +4181,6 @@ __umtx_op_robust_lists(struct thread *td, struct _umtx_op_args *uap,
|
||||
|
||||
if (ops->compat32)
|
||||
td->td_pflags2 |= TDP2_COMPAT32RB;
|
||||
else if ((td->td_pflags2 & TDP2_COMPAT32RB) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
td->td_rb_list = rb.robust_list_offset;
|
||||
td->td_rbp_list = rb.robust_priv_list_offset;
|
||||
@ -4183,49 +4188,45 @@ __umtx_op_robust_lists(struct thread *td, struct _umtx_op_args *uap,
|
||||
return (0);
|
||||
}
|
||||
|
||||
#ifdef COMPAT_FREEBSD32
|
||||
static inline int
|
||||
umtx_copyin_timeout32(const void *uaddr, struct timespec *tsp)
|
||||
{
|
||||
struct timespec32 ts32;
|
||||
int error;
|
||||
#if defined(__i386__) || defined(__amd64__)
|
||||
/*
|
||||
* Provide the standard 32-bit definitions for x86, since native/compat32 use a
|
||||
* 32-bit time_t there. Other architectures just need the i386 definitions
|
||||
* along with their standard compat32.
|
||||
*/
|
||||
struct timespecx32 {
|
||||
int64_t tv_sec;
|
||||
int32_t tv_nsec;
|
||||
};
|
||||
|
||||
error = copyin(uaddr, &ts32, sizeof(ts32));
|
||||
if (error == 0) {
|
||||
if (ts32.tv_sec < 0 ||
|
||||
ts32.tv_nsec >= 1000000000 ||
|
||||
ts32.tv_nsec < 0)
|
||||
error = EINVAL;
|
||||
else {
|
||||
CP(ts32, *tsp, tv_sec);
|
||||
CP(ts32, *tsp, tv_nsec);
|
||||
}
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
struct umtx_timex32 {
|
||||
struct timespecx32 _timeout;
|
||||
uint32_t _flags;
|
||||
uint32_t _clockid;
|
||||
};
|
||||
|
||||
static inline int
|
||||
umtx_copyin_umtx_time32(const void *uaddr, size_t size, struct _umtx_time *tp)
|
||||
{
|
||||
struct umtx_time32 t32;
|
||||
int error;
|
||||
#ifndef __i386__
|
||||
#define timespeci386 timespec32
|
||||
#define umtx_timei386 umtx_time32
|
||||
#endif
|
||||
#else /* !__i386__ && !__amd64__ */
|
||||
/* 32-bit architectures can emulate i386, so define these almost everywhere. */
|
||||
struct timespeci386 {
|
||||
int32_t tv_sec;
|
||||
int32_t tv_nsec;
|
||||
};
|
||||
|
||||
t32._clockid = CLOCK_REALTIME;
|
||||
t32._flags = 0;
|
||||
if (size <= sizeof(t32._timeout))
|
||||
error = copyin(uaddr, &t32._timeout, sizeof(t32._timeout));
|
||||
else
|
||||
error = copyin(uaddr, &t32, sizeof(t32));
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (t32._timeout.tv_sec < 0 ||
|
||||
t32._timeout.tv_nsec >= 1000000000 || t32._timeout.tv_nsec < 0)
|
||||
return (EINVAL);
|
||||
TS_CP(t32, *tp, _timeout);
|
||||
CP(t32, *tp, _flags);
|
||||
CP(t32, *tp, _clockid);
|
||||
return (0);
|
||||
}
|
||||
struct umtx_timei386 {
|
||||
struct timespeci386 _timeout;
|
||||
uint32_t _flags;
|
||||
uint32_t _clockid;
|
||||
};
|
||||
|
||||
#if defined(__LP64__)
|
||||
#define timespecx32 timespec32
|
||||
#define umtx_timex32 umtx_time32
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static int
|
||||
umtx_copyin_robust_lists32(const void *uaddr, size_t size,
|
||||
@ -4246,10 +4247,54 @@ umtx_copyin_robust_lists32(const void *uaddr, size_t size,
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
umtx_copyout_timeout32(void *uaddr, size_t sz, struct timespec *tsp)
|
||||
#ifndef __i386__
|
||||
static inline int
|
||||
umtx_copyin_timeouti386(const void *uaddr, struct timespec *tsp)
|
||||
{
|
||||
struct timespec32 remain32 = {
|
||||
struct timespeci386 ts32;
|
||||
int error;
|
||||
|
||||
error = copyin(uaddr, &ts32, sizeof(ts32));
|
||||
if (error == 0) {
|
||||
if (ts32.tv_sec < 0 ||
|
||||
ts32.tv_nsec >= 1000000000 ||
|
||||
ts32.tv_nsec < 0)
|
||||
error = EINVAL;
|
||||
else {
|
||||
CP(ts32, *tsp, tv_sec);
|
||||
CP(ts32, *tsp, tv_nsec);
|
||||
}
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
static inline int
|
||||
umtx_copyin_umtx_timei386(const void *uaddr, size_t size, struct _umtx_time *tp)
|
||||
{
|
||||
struct umtx_timei386 t32;
|
||||
int error;
|
||||
|
||||
t32._clockid = CLOCK_REALTIME;
|
||||
t32._flags = 0;
|
||||
if (size <= sizeof(t32._timeout))
|
||||
error = copyin(uaddr, &t32._timeout, sizeof(t32._timeout));
|
||||
else
|
||||
error = copyin(uaddr, &t32, sizeof(t32));
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (t32._timeout.tv_sec < 0 ||
|
||||
t32._timeout.tv_nsec >= 1000000000 || t32._timeout.tv_nsec < 0)
|
||||
return (EINVAL);
|
||||
TS_CP(t32, *tp, _timeout);
|
||||
CP(t32, *tp, _flags);
|
||||
CP(t32, *tp, _clockid);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
umtx_copyout_timeouti386(void *uaddr, size_t sz, struct timespec *tsp)
|
||||
{
|
||||
struct timespeci386 remain32 = {
|
||||
.tv_sec = tsp->tv_sec,
|
||||
.tv_nsec = tsp->tv_nsec,
|
||||
};
|
||||
@ -4264,7 +4309,71 @@ umtx_copyout_timeout32(void *uaddr, size_t sz, struct timespec *tsp)
|
||||
|
||||
return (copyout(&remain32, uaddr, sizeof(remain32)));
|
||||
}
|
||||
#endif /* COMPAT_FREEBSD32 */
|
||||
#endif /* !__i386__ */
|
||||
|
||||
#if defined(__i386__) || defined(__LP64__)
|
||||
static inline int
|
||||
umtx_copyin_timeoutx32(const void *uaddr, struct timespec *tsp)
|
||||
{
|
||||
struct timespecx32 ts32;
|
||||
int error;
|
||||
|
||||
error = copyin(uaddr, &ts32, sizeof(ts32));
|
||||
if (error == 0) {
|
||||
if (ts32.tv_sec < 0 ||
|
||||
ts32.tv_nsec >= 1000000000 ||
|
||||
ts32.tv_nsec < 0)
|
||||
error = EINVAL;
|
||||
else {
|
||||
CP(ts32, *tsp, tv_sec);
|
||||
CP(ts32, *tsp, tv_nsec);
|
||||
}
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
static inline int
|
||||
umtx_copyin_umtx_timex32(const void *uaddr, size_t size, struct _umtx_time *tp)
|
||||
{
|
||||
struct umtx_timex32 t32;
|
||||
int error;
|
||||
|
||||
t32._clockid = CLOCK_REALTIME;
|
||||
t32._flags = 0;
|
||||
if (size <= sizeof(t32._timeout))
|
||||
error = copyin(uaddr, &t32._timeout, sizeof(t32._timeout));
|
||||
else
|
||||
error = copyin(uaddr, &t32, sizeof(t32));
|
||||
if (error != 0)
|
||||
return (error);
|
||||
if (t32._timeout.tv_sec < 0 ||
|
||||
t32._timeout.tv_nsec >= 1000000000 || t32._timeout.tv_nsec < 0)
|
||||
return (EINVAL);
|
||||
TS_CP(t32, *tp, _timeout);
|
||||
CP(t32, *tp, _flags);
|
||||
CP(t32, *tp, _clockid);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
umtx_copyout_timeoutx32(void *uaddr, size_t sz, struct timespec *tsp)
|
||||
{
|
||||
struct timespecx32 remain32 = {
|
||||
.tv_sec = tsp->tv_sec,
|
||||
.tv_nsec = tsp->tv_nsec,
|
||||
};
|
||||
|
||||
/*
|
||||
* Should be guaranteed by the caller, sz == uaddr1 - sizeof(_umtx_time)
|
||||
* and we're only called if sz >= sizeof(timespec) as supplied in the
|
||||
* copyops.
|
||||
*/
|
||||
KASSERT(sz >= sizeof(remain32),
|
||||
("umtx_copyops specifies incorrect sizes"));
|
||||
|
||||
return (copyout(&remain32, uaddr, sizeof(remain32)));
|
||||
}
|
||||
#endif /* __i386__ || __LP64__ */
|
||||
|
||||
typedef int (*_umtx_op_func)(struct thread *td, struct _umtx_op_args *uap,
|
||||
const struct umtx_copyops *umtx_ops);
|
||||
@ -4313,25 +4422,48 @@ static const struct umtx_copyops umtx_native_ops = {
|
||||
.umtx_time_sz = sizeof(struct _umtx_time),
|
||||
};
|
||||
|
||||
#ifdef COMPAT_FREEBSD32
|
||||
const struct umtx_copyops umtx_native_ops32 = {
|
||||
.copyin_timeout = umtx_copyin_timeout32,
|
||||
.copyin_umtx_time = umtx_copyin_umtx_time32,
|
||||
#ifndef __i386__
|
||||
static const struct umtx_copyops umtx_native_opsi386 = {
|
||||
.copyin_timeout = umtx_copyin_timeouti386,
|
||||
.copyin_umtx_time = umtx_copyin_umtx_timei386,
|
||||
.copyin_robust_lists = umtx_copyin_robust_lists32,
|
||||
.copyout_timeout = umtx_copyout_timeout32,
|
||||
.timespec_sz = sizeof(struct timespec32),
|
||||
.umtx_time_sz = sizeof(struct umtx_time32),
|
||||
.copyout_timeout = umtx_copyout_timeouti386,
|
||||
.timespec_sz = sizeof(struct timespeci386),
|
||||
.umtx_time_sz = sizeof(struct umtx_timei386),
|
||||
.compat32 = true,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) || defined(__LP64__)
|
||||
/* i386 can emulate other 32-bit archs, too! */
|
||||
static const struct umtx_copyops umtx_native_opsx32 = {
|
||||
.copyin_timeout = umtx_copyin_timeoutx32,
|
||||
.copyin_umtx_time = umtx_copyin_umtx_timex32,
|
||||
.copyin_robust_lists = umtx_copyin_robust_lists32,
|
||||
.copyout_timeout = umtx_copyout_timeoutx32,
|
||||
.timespec_sz = sizeof(struct timespecx32),
|
||||
.umtx_time_sz = sizeof(struct umtx_timex32),
|
||||
.compat32 = true,
|
||||
};
|
||||
|
||||
#ifdef COMPAT_FREEBSD32
|
||||
#ifdef __amd64__
|
||||
#define umtx_native_ops32 umtx_native_opsi386
|
||||
#else
|
||||
#define umtx_native_ops32 umtx_native_opsx32
|
||||
#endif
|
||||
#endif /* COMPAT_FREEBSD32 */
|
||||
#endif /* __i386__ || __LP64__ */
|
||||
|
||||
#define UMTX_OP__FLAGS (UMTX_OP__32BIT | UMTX_OP__I386)
|
||||
|
||||
static int
|
||||
kern__umtx_op(struct thread *td, void *obj, int op, unsigned long val,
|
||||
void *uaddr1, void *uaddr2, const struct umtx_copyops *ops)
|
||||
{
|
||||
struct _umtx_op_args uap = {
|
||||
.obj = obj,
|
||||
.op = op,
|
||||
.op = op & ~UMTX_OP__FLAGS,
|
||||
.val = val,
|
||||
.uaddr1 = uaddr1,
|
||||
.uaddr2 = uaddr2
|
||||
@ -4345,9 +4477,27 @@ kern__umtx_op(struct thread *td, void *obj, int op, unsigned long val,
|
||||
int
|
||||
sys__umtx_op(struct thread *td, struct _umtx_op_args *uap)
|
||||
{
|
||||
static const struct umtx_copyops *umtx_ops;
|
||||
|
||||
umtx_ops = &umtx_native_ops;
|
||||
#ifdef __LP64__
|
||||
if ((uap->op & (UMTX_OP__32BIT | UMTX_OP__I386)) != 0) {
|
||||
if ((uap->op & UMTX_OP__I386) != 0)
|
||||
umtx_ops = &umtx_native_opsi386;
|
||||
else
|
||||
umtx_ops = &umtx_native_opsx32;
|
||||
}
|
||||
#elif !defined(__i386__)
|
||||
/* We consider UMTX_OP__32BIT a nop on !i386 ILP32. */
|
||||
if ((uap->op & UMTX_OP__I386) != 0)
|
||||
umtx_ops = &umtx_native_opsi386;
|
||||
#else
|
||||
/* Likewise, UMTX_OP__I386 is a nop on i386. */
|
||||
if ((uap->op & UMTX_OP__32BIT) != 0)
|
||||
umtx_ops = &umtx_native_opsx32;
|
||||
#endif
|
||||
return (kern__umtx_op(td, uap->obj, uap->op, uap->val, uap->uaddr1,
|
||||
uap->uaddr2, &umtx_native_ops));
|
||||
uap->uaddr2, umtx_ops));
|
||||
}
|
||||
|
||||
#ifdef COMPAT_FREEBSD32
|
||||
@ -4435,19 +4585,14 @@ static int
|
||||
umtx_read_uptr(struct thread *td, uintptr_t ptr, uintptr_t *res, bool compat32)
|
||||
{
|
||||
u_long res1;
|
||||
#ifdef COMPAT_FREEBSD32
|
||||
uint32_t res32;
|
||||
#endif
|
||||
int error;
|
||||
|
||||
#ifdef COMPAT_FREEBSD32
|
||||
if (compat32) {
|
||||
error = fueword32((void *)ptr, &res32);
|
||||
if (error == 0)
|
||||
res1 = res32;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
} else {
|
||||
error = fueword((void *)ptr, &res1);
|
||||
}
|
||||
if (error == 0)
|
||||
@ -4461,15 +4606,14 @@ static void
|
||||
umtx_read_rb_list(struct thread *td, struct umutex *m, uintptr_t *rb_list,
|
||||
bool compat32)
|
||||
{
|
||||
#ifdef COMPAT_FREEBSD32
|
||||
struct umutex32 m32;
|
||||
|
||||
if (compat32) {
|
||||
memcpy(&m32, m, sizeof(m32));
|
||||
*rb_list = m32.m_rb_lnk;
|
||||
} else
|
||||
#endif
|
||||
} else {
|
||||
*rb_list = m->m_rb_lnk;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -101,6 +101,13 @@
|
||||
#define UMTX_OP_SHM 25
|
||||
#define UMTX_OP_ROBUST_LISTS 26
|
||||
|
||||
/*
|
||||
* Flags for ops; the double-underbar convention must be maintained for future
|
||||
* additions for the sake of libsysdecode.
|
||||
*/
|
||||
#define UMTX_OP__I386 0x40000000
|
||||
#define UMTX_OP__32BIT 0x80000000
|
||||
|
||||
/* Flags for UMTX_OP_CV_WAIT */
|
||||
#define CVWAIT_CHECK_UNPARKING 0x01
|
||||
#define CVWAIT_ABSTIME 0x02
|
||||
|
Loading…
Reference in New Issue
Block a user