mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 22:51:42 +00:00
Merge branch 'locking-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull locking updates from Ingo Molnar: "The main changes in this cycle were: - a big round of FUTEX_UNLOCK_PI improvements, fixes, cleanups and general restructuring - lockdep updates such as new checks for lock_downgrade() - introduce the new atomic_try_cmpxchg() locking API and use it to optimize refcount code generation - ... plus misc fixes, updates and cleanups" * 'locking-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (38 commits) MAINTAINERS: Add FUTEX SUBSYSTEM futex: Clarify mark_wake_futex memory barrier usage futex: Fix small (and harmless looking) inconsistencies futex: Avoid freeing an active timer rtmutex: Plug preempt count leak in rt_mutex_futex_unlock() rtmutex: Fix more prio comparisons rtmutex: Fix PI chain order integrity sched,tracing: Update trace_sched_pi_setprio() sched/rtmutex: Refactor rt_mutex_setprio() rtmutex: Clean up sched/deadline/rtmutex: Dont miss the dl_runtime/dl_period update sched/rtmutex/deadline: Fix a PI crash for deadline tasks rtmutex: Deboost before waking up the top waiter locking/ww-mutex: Limit stress test to 2 seconds locking/atomic: Fix atomic_try_cmpxchg() semantics lockdep: Fix per-cpu static objects futex: Drop hb->lock before enqueueing on the rtmutex futex: Futex_unlock_pi() determinism futex: Rework futex_lock_pi() to use rt_mutex_*_proxy_lock() futex,rt_mutex: Restructure rt_mutex_finish_proxy_lock() ...
This commit is contained in:
commit
207fb8c304
17
MAINTAINERS
17
MAINTAINERS
@ -5415,6 +5415,23 @@ F: fs/fuse/
|
|||||||
F: include/uapi/linux/fuse.h
|
F: include/uapi/linux/fuse.h
|
||||||
F: Documentation/filesystems/fuse.txt
|
F: Documentation/filesystems/fuse.txt
|
||||||
|
|
||||||
|
FUTEX SUBSYSTEM
|
||||||
|
M: Thomas Gleixner <tglx@linutronix.de>
|
||||||
|
M: Ingo Molnar <mingo@redhat.com>
|
||||||
|
R: Peter Zijlstra <peterz@infradead.org>
|
||||||
|
R: Darren Hart <dvhart@infradead.org>
|
||||||
|
L: linux-kernel@vger.kernel.org
|
||||||
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking/core
|
||||||
|
S: Maintained
|
||||||
|
F: kernel/futex.c
|
||||||
|
F: kernel/futex_compat.c
|
||||||
|
F: include/asm-generic/futex.h
|
||||||
|
F: include/linux/futex.h
|
||||||
|
F: include/uapi/linux/futex.h
|
||||||
|
F: tools/testing/selftests/futex/
|
||||||
|
F: tools/perf/bench/futex*
|
||||||
|
F: Documentation/*futex*
|
||||||
|
|
||||||
FUTURE DOMAIN TMC-16x0 SCSI DRIVER (16-bit)
|
FUTURE DOMAIN TMC-16x0 SCSI DRIVER (16-bit)
|
||||||
M: Rik Faith <faith@cs.unc.edu>
|
M: Rik Faith <faith@cs.unc.edu>
|
||||||
L: linux-scsi@vger.kernel.org
|
L: linux-scsi@vger.kernel.org
|
||||||
|
@ -186,6 +186,12 @@ static __always_inline int atomic_cmpxchg(atomic_t *v, int old, int new)
|
|||||||
return cmpxchg(&v->counter, old, new);
|
return cmpxchg(&v->counter, old, new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define atomic_try_cmpxchg atomic_try_cmpxchg
|
||||||
|
static __always_inline bool atomic_try_cmpxchg(atomic_t *v, int *old, int new)
|
||||||
|
{
|
||||||
|
return try_cmpxchg(&v->counter, old, new);
|
||||||
|
}
|
||||||
|
|
||||||
static inline int atomic_xchg(atomic_t *v, int new)
|
static inline int atomic_xchg(atomic_t *v, int new)
|
||||||
{
|
{
|
||||||
return xchg(&v->counter, new);
|
return xchg(&v->counter, new);
|
||||||
@ -203,14 +209,10 @@ static inline void atomic_##op(int i, atomic_t *v) \
|
|||||||
#define ATOMIC_FETCH_OP(op, c_op) \
|
#define ATOMIC_FETCH_OP(op, c_op) \
|
||||||
static inline int atomic_fetch_##op(int i, atomic_t *v) \
|
static inline int atomic_fetch_##op(int i, atomic_t *v) \
|
||||||
{ \
|
{ \
|
||||||
int old, val = atomic_read(v); \
|
int val = atomic_read(v); \
|
||||||
for (;;) { \
|
do { \
|
||||||
old = atomic_cmpxchg(v, val, val c_op i); \
|
} while (!atomic_try_cmpxchg(v, &val, val c_op i)); \
|
||||||
if (old == val) \
|
return val; \
|
||||||
break; \
|
|
||||||
val = old; \
|
|
||||||
} \
|
|
||||||
return old; \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ATOMIC_OPS(op, c_op) \
|
#define ATOMIC_OPS(op, c_op) \
|
||||||
@ -236,16 +238,11 @@ ATOMIC_OPS(xor, ^)
|
|||||||
*/
|
*/
|
||||||
static __always_inline int __atomic_add_unless(atomic_t *v, int a, int u)
|
static __always_inline int __atomic_add_unless(atomic_t *v, int a, int u)
|
||||||
{
|
{
|
||||||
int c, old;
|
int c = atomic_read(v);
|
||||||
c = atomic_read(v);
|
do {
|
||||||
for (;;) {
|
if (unlikely(c == u))
|
||||||
if (unlikely(c == (u)))
|
|
||||||
break;
|
break;
|
||||||
old = atomic_cmpxchg((v), c, c + (a));
|
} while (!atomic_try_cmpxchg(v, &c, c + a));
|
||||||
if (likely(old == c))
|
|
||||||
break;
|
|
||||||
c = old;
|
|
||||||
}
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,6 +176,12 @@ static inline long atomic64_cmpxchg(atomic64_t *v, long old, long new)
|
|||||||
return cmpxchg(&v->counter, old, new);
|
return cmpxchg(&v->counter, old, new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define atomic64_try_cmpxchg atomic64_try_cmpxchg
|
||||||
|
static __always_inline bool atomic64_try_cmpxchg(atomic64_t *v, long *old, long new)
|
||||||
|
{
|
||||||
|
return try_cmpxchg(&v->counter, old, new);
|
||||||
|
}
|
||||||
|
|
||||||
static inline long atomic64_xchg(atomic64_t *v, long new)
|
static inline long atomic64_xchg(atomic64_t *v, long new)
|
||||||
{
|
{
|
||||||
return xchg(&v->counter, new);
|
return xchg(&v->counter, new);
|
||||||
@ -192,17 +198,12 @@ static inline long atomic64_xchg(atomic64_t *v, long new)
|
|||||||
*/
|
*/
|
||||||
static inline bool atomic64_add_unless(atomic64_t *v, long a, long u)
|
static inline bool atomic64_add_unless(atomic64_t *v, long a, long u)
|
||||||
{
|
{
|
||||||
long c, old;
|
long c = atomic64_read(v);
|
||||||
c = atomic64_read(v);
|
do {
|
||||||
for (;;) {
|
if (unlikely(c == u))
|
||||||
if (unlikely(c == (u)))
|
return false;
|
||||||
break;
|
} while (!atomic64_try_cmpxchg(v, &c, c + a));
|
||||||
old = atomic64_cmpxchg((v), c, c + (a));
|
return true;
|
||||||
if (likely(old == c))
|
|
||||||
break;
|
|
||||||
c = old;
|
|
||||||
}
|
|
||||||
return c != (u);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1, 0)
|
#define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1, 0)
|
||||||
@ -216,17 +217,12 @@ static inline bool atomic64_add_unless(atomic64_t *v, long a, long u)
|
|||||||
*/
|
*/
|
||||||
static inline long atomic64_dec_if_positive(atomic64_t *v)
|
static inline long atomic64_dec_if_positive(atomic64_t *v)
|
||||||
{
|
{
|
||||||
long c, old, dec;
|
long dec, c = atomic64_read(v);
|
||||||
c = atomic64_read(v);
|
do {
|
||||||
for (;;) {
|
|
||||||
dec = c - 1;
|
dec = c - 1;
|
||||||
if (unlikely(dec < 0))
|
if (unlikely(dec < 0))
|
||||||
break;
|
break;
|
||||||
old = atomic64_cmpxchg((v), c, dec);
|
} while (!atomic64_try_cmpxchg(v, &c, dec));
|
||||||
if (likely(old == c))
|
|
||||||
break;
|
|
||||||
c = old;
|
|
||||||
}
|
|
||||||
return dec;
|
return dec;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,14 +238,10 @@ static inline void atomic64_##op(long i, atomic64_t *v) \
|
|||||||
#define ATOMIC64_FETCH_OP(op, c_op) \
|
#define ATOMIC64_FETCH_OP(op, c_op) \
|
||||||
static inline long atomic64_fetch_##op(long i, atomic64_t *v) \
|
static inline long atomic64_fetch_##op(long i, atomic64_t *v) \
|
||||||
{ \
|
{ \
|
||||||
long old, val = atomic64_read(v); \
|
long val = atomic64_read(v); \
|
||||||
for (;;) { \
|
do { \
|
||||||
old = atomic64_cmpxchg(v, val, val c_op i); \
|
} while (!atomic64_try_cmpxchg(v, &val, val c_op i)); \
|
||||||
if (old == val) \
|
return val; \
|
||||||
break; \
|
|
||||||
val = old; \
|
|
||||||
} \
|
|
||||||
return old; \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ATOMIC64_OPS(op, c_op) \
|
#define ATOMIC64_OPS(op, c_op) \
|
||||||
|
@ -153,6 +153,76 @@ extern void __add_wrong_size(void)
|
|||||||
#define cmpxchg_local(ptr, old, new) \
|
#define cmpxchg_local(ptr, old, new) \
|
||||||
__cmpxchg_local(ptr, old, new, sizeof(*(ptr)))
|
__cmpxchg_local(ptr, old, new, sizeof(*(ptr)))
|
||||||
|
|
||||||
|
|
||||||
|
#define __raw_try_cmpxchg(_ptr, _pold, _new, size, lock) \
|
||||||
|
({ \
|
||||||
|
bool success; \
|
||||||
|
__typeof__(_ptr) _old = (_pold); \
|
||||||
|
__typeof__(*(_ptr)) __old = *_old; \
|
||||||
|
__typeof__(*(_ptr)) __new = (_new); \
|
||||||
|
switch (size) { \
|
||||||
|
case __X86_CASE_B: \
|
||||||
|
{ \
|
||||||
|
volatile u8 *__ptr = (volatile u8 *)(_ptr); \
|
||||||
|
asm volatile(lock "cmpxchgb %[new], %[ptr]" \
|
||||||
|
CC_SET(z) \
|
||||||
|
: CC_OUT(z) (success), \
|
||||||
|
[ptr] "+m" (*__ptr), \
|
||||||
|
[old] "+a" (__old) \
|
||||||
|
: [new] "q" (__new) \
|
||||||
|
: "memory"); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
case __X86_CASE_W: \
|
||||||
|
{ \
|
||||||
|
volatile u16 *__ptr = (volatile u16 *)(_ptr); \
|
||||||
|
asm volatile(lock "cmpxchgw %[new], %[ptr]" \
|
||||||
|
CC_SET(z) \
|
||||||
|
: CC_OUT(z) (success), \
|
||||||
|
[ptr] "+m" (*__ptr), \
|
||||||
|
[old] "+a" (__old) \
|
||||||
|
: [new] "r" (__new) \
|
||||||
|
: "memory"); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
case __X86_CASE_L: \
|
||||||
|
{ \
|
||||||
|
volatile u32 *__ptr = (volatile u32 *)(_ptr); \
|
||||||
|
asm volatile(lock "cmpxchgl %[new], %[ptr]" \
|
||||||
|
CC_SET(z) \
|
||||||
|
: CC_OUT(z) (success), \
|
||||||
|
[ptr] "+m" (*__ptr), \
|
||||||
|
[old] "+a" (__old) \
|
||||||
|
: [new] "r" (__new) \
|
||||||
|
: "memory"); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
case __X86_CASE_Q: \
|
||||||
|
{ \
|
||||||
|
volatile u64 *__ptr = (volatile u64 *)(_ptr); \
|
||||||
|
asm volatile(lock "cmpxchgq %[new], %[ptr]" \
|
||||||
|
CC_SET(z) \
|
||||||
|
: CC_OUT(z) (success), \
|
||||||
|
[ptr] "+m" (*__ptr), \
|
||||||
|
[old] "+a" (__old) \
|
||||||
|
: [new] "r" (__new) \
|
||||||
|
: "memory"); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
default: \
|
||||||
|
__cmpxchg_wrong_size(); \
|
||||||
|
} \
|
||||||
|
if (unlikely(!success)) \
|
||||||
|
*_old = __old; \
|
||||||
|
likely(success); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define __try_cmpxchg(ptr, pold, new, size) \
|
||||||
|
__raw_try_cmpxchg((ptr), (pold), (new), (size), LOCK_PREFIX)
|
||||||
|
|
||||||
|
#define try_cmpxchg(ptr, pold, new) \
|
||||||
|
__try_cmpxchg((ptr), (pold), (new), sizeof(*(ptr)))
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* xadd() adds "inc" to "*ptr" and atomically returns the previous
|
* xadd() adds "inc" to "*ptr" and atomically returns the previous
|
||||||
* value of "*ptr".
|
* value of "*ptr".
|
||||||
|
@ -423,6 +423,29 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif /* atomic_cmpxchg_relaxed */
|
#endif /* atomic_cmpxchg_relaxed */
|
||||||
|
|
||||||
|
#ifndef atomic_try_cmpxchg
|
||||||
|
|
||||||
|
#define __atomic_try_cmpxchg(type, _p, _po, _n) \
|
||||||
|
({ \
|
||||||
|
typeof(_po) __po = (_po); \
|
||||||
|
typeof(*(_po)) __r, __o = *__po; \
|
||||||
|
__r = atomic_cmpxchg##type((_p), __o, (_n)); \
|
||||||
|
if (unlikely(__r != __o)) \
|
||||||
|
*__po = __r; \
|
||||||
|
likely(__r == __o); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define atomic_try_cmpxchg(_p, _po, _n) __atomic_try_cmpxchg(, _p, _po, _n)
|
||||||
|
#define atomic_try_cmpxchg_relaxed(_p, _po, _n) __atomic_try_cmpxchg(_relaxed, _p, _po, _n)
|
||||||
|
#define atomic_try_cmpxchg_acquire(_p, _po, _n) __atomic_try_cmpxchg(_acquire, _p, _po, _n)
|
||||||
|
#define atomic_try_cmpxchg_release(_p, _po, _n) __atomic_try_cmpxchg(_release, _p, _po, _n)
|
||||||
|
|
||||||
|
#else /* atomic_try_cmpxchg */
|
||||||
|
#define atomic_try_cmpxchg_relaxed atomic_try_cmpxchg
|
||||||
|
#define atomic_try_cmpxchg_acquire atomic_try_cmpxchg
|
||||||
|
#define atomic_try_cmpxchg_release atomic_try_cmpxchg
|
||||||
|
#endif /* atomic_try_cmpxchg */
|
||||||
|
|
||||||
/* cmpxchg_relaxed */
|
/* cmpxchg_relaxed */
|
||||||
#ifndef cmpxchg_relaxed
|
#ifndef cmpxchg_relaxed
|
||||||
#define cmpxchg_relaxed cmpxchg
|
#define cmpxchg_relaxed cmpxchg
|
||||||
@ -996,6 +1019,29 @@ static inline int atomic_dec_if_positive(atomic_t *v)
|
|||||||
#endif
|
#endif
|
||||||
#endif /* atomic64_cmpxchg_relaxed */
|
#endif /* atomic64_cmpxchg_relaxed */
|
||||||
|
|
||||||
|
#ifndef atomic64_try_cmpxchg
|
||||||
|
|
||||||
|
#define __atomic64_try_cmpxchg(type, _p, _po, _n) \
|
||||||
|
({ \
|
||||||
|
typeof(_po) __po = (_po); \
|
||||||
|
typeof(*(_po)) __r, __o = *__po; \
|
||||||
|
__r = atomic64_cmpxchg##type((_p), __o, (_n)); \
|
||||||
|
if (unlikely(__r != __o)) \
|
||||||
|
*__po = __r; \
|
||||||
|
likely(__r == __o); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define atomic64_try_cmpxchg(_p, _po, _n) __atomic64_try_cmpxchg(, _p, _po, _n)
|
||||||
|
#define atomic64_try_cmpxchg_relaxed(_p, _po, _n) __atomic64_try_cmpxchg(_relaxed, _p, _po, _n)
|
||||||
|
#define atomic64_try_cmpxchg_acquire(_p, _po, _n) __atomic64_try_cmpxchg(_acquire, _p, _po, _n)
|
||||||
|
#define atomic64_try_cmpxchg_release(_p, _po, _n) __atomic64_try_cmpxchg(_release, _p, _po, _n)
|
||||||
|
|
||||||
|
#else /* atomic64_try_cmpxchg */
|
||||||
|
#define atomic64_try_cmpxchg_relaxed atomic64_try_cmpxchg
|
||||||
|
#define atomic64_try_cmpxchg_acquire atomic64_try_cmpxchg
|
||||||
|
#define atomic64_try_cmpxchg_release atomic64_try_cmpxchg
|
||||||
|
#endif /* atomic64_try_cmpxchg */
|
||||||
|
|
||||||
#ifndef atomic64_andnot
|
#ifndef atomic64_andnot
|
||||||
static inline void atomic64_andnot(long long i, atomic64_t *v)
|
static inline void atomic64_andnot(long long i, atomic64_t *v)
|
||||||
{
|
{
|
||||||
|
@ -181,6 +181,7 @@ extern struct cred init_cred;
|
|||||||
#ifdef CONFIG_RT_MUTEXES
|
#ifdef CONFIG_RT_MUTEXES
|
||||||
# define INIT_RT_MUTEXES(tsk) \
|
# define INIT_RT_MUTEXES(tsk) \
|
||||||
.pi_waiters = RB_ROOT, \
|
.pi_waiters = RB_ROOT, \
|
||||||
|
.pi_top_task = NULL, \
|
||||||
.pi_waiters_leftmost = NULL,
|
.pi_waiters_leftmost = NULL,
|
||||||
#else
|
#else
|
||||||
# define INIT_RT_MUTEXES(tsk)
|
# define INIT_RT_MUTEXES(tsk)
|
||||||
|
@ -361,6 +361,8 @@ static inline void lock_set_subclass(struct lockdep_map *lock,
|
|||||||
lock_set_class(lock, lock->name, lock->key, subclass, ip);
|
lock_set_class(lock, lock->name, lock->key, subclass, ip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void lock_downgrade(struct lockdep_map *lock, unsigned long ip);
|
||||||
|
|
||||||
extern void lockdep_set_current_reclaim_state(gfp_t gfp_mask);
|
extern void lockdep_set_current_reclaim_state(gfp_t gfp_mask);
|
||||||
extern void lockdep_clear_current_reclaim_state(void);
|
extern void lockdep_clear_current_reclaim_state(void);
|
||||||
extern void lockdep_trace_alloc(gfp_t mask);
|
extern void lockdep_trace_alloc(gfp_t mask);
|
||||||
@ -411,6 +413,7 @@ static inline void lockdep_on(void)
|
|||||||
|
|
||||||
# define lock_acquire(l, s, t, r, c, n, i) do { } while (0)
|
# define lock_acquire(l, s, t, r, c, n, i) do { } while (0)
|
||||||
# define lock_release(l, n, i) do { } while (0)
|
# define lock_release(l, n, i) do { } while (0)
|
||||||
|
# define lock_downgrade(l, i) do { } while (0)
|
||||||
# define lock_set_class(l, n, k, s, i) do { } while (0)
|
# define lock_set_class(l, n, k, s, i) do { } while (0)
|
||||||
# define lock_set_subclass(l, s, i) do { } while (0)
|
# define lock_set_subclass(l, s, i) do { } while (0)
|
||||||
# define lockdep_set_current_reclaim_state(g) do { } while (0)
|
# define lockdep_set_current_reclaim_state(g) do { } while (0)
|
||||||
|
@ -493,6 +493,7 @@ static inline int module_is_live(struct module *mod)
|
|||||||
struct module *__module_text_address(unsigned long addr);
|
struct module *__module_text_address(unsigned long addr);
|
||||||
struct module *__module_address(unsigned long addr);
|
struct module *__module_address(unsigned long addr);
|
||||||
bool is_module_address(unsigned long addr);
|
bool is_module_address(unsigned long addr);
|
||||||
|
bool __is_module_percpu_address(unsigned long addr, unsigned long *can_addr);
|
||||||
bool is_module_percpu_address(unsigned long addr);
|
bool is_module_percpu_address(unsigned long addr);
|
||||||
bool is_module_text_address(unsigned long addr);
|
bool is_module_text_address(unsigned long addr);
|
||||||
|
|
||||||
@ -660,6 +661,11 @@ static inline bool is_module_percpu_address(unsigned long addr)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool __is_module_percpu_address(unsigned long addr, unsigned long *can_addr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool is_module_text_address(unsigned long addr)
|
static inline bool is_module_text_address(unsigned long addr)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -110,6 +110,7 @@ extern int __init pcpu_page_first_chunk(size_t reserved_size,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern void __percpu *__alloc_reserved_percpu(size_t size, size_t align);
|
extern void __percpu *__alloc_reserved_percpu(size_t size, size_t align);
|
||||||
|
extern bool __is_kernel_percpu_address(unsigned long addr, unsigned long *can_addr);
|
||||||
extern bool is_kernel_percpu_address(unsigned long addr);
|
extern bool is_kernel_percpu_address(unsigned long addr);
|
||||||
|
|
||||||
#if !defined(CONFIG_SMP) || !defined(CONFIG_HAVE_SETUP_PER_CPU_AREA)
|
#if !defined(CONFIG_SMP) || !defined(CONFIG_HAVE_SETUP_PER_CPU_AREA)
|
||||||
|
@ -6,17 +6,36 @@
|
|||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* refcount_t - variant of atomic_t specialized for reference counts
|
||||||
|
* @refs: atomic_t counter field
|
||||||
|
*
|
||||||
|
* The counter saturates at UINT_MAX and will not move once
|
||||||
|
* there. This avoids wrapping the counter and causing 'spurious'
|
||||||
|
* use-after-free bugs.
|
||||||
|
*/
|
||||||
typedef struct refcount_struct {
|
typedef struct refcount_struct {
|
||||||
atomic_t refs;
|
atomic_t refs;
|
||||||
} refcount_t;
|
} refcount_t;
|
||||||
|
|
||||||
#define REFCOUNT_INIT(n) { .refs = ATOMIC_INIT(n), }
|
#define REFCOUNT_INIT(n) { .refs = ATOMIC_INIT(n), }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* refcount_set - set a refcount's value
|
||||||
|
* @r: the refcount
|
||||||
|
* @n: value to which the refcount will be set
|
||||||
|
*/
|
||||||
static inline void refcount_set(refcount_t *r, unsigned int n)
|
static inline void refcount_set(refcount_t *r, unsigned int n)
|
||||||
{
|
{
|
||||||
atomic_set(&r->refs, n);
|
atomic_set(&r->refs, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* refcount_read - get a refcount's value
|
||||||
|
* @r: the refcount
|
||||||
|
*
|
||||||
|
* Return: the refcount's value
|
||||||
|
*/
|
||||||
static inline unsigned int refcount_read(const refcount_t *r)
|
static inline unsigned int refcount_read(const refcount_t *r)
|
||||||
{
|
{
|
||||||
return atomic_read(&r->refs);
|
return atomic_read(&r->refs);
|
||||||
|
@ -779,6 +779,8 @@ struct task_struct {
|
|||||||
/* PI waiters blocked on a rt_mutex held by this task: */
|
/* PI waiters blocked on a rt_mutex held by this task: */
|
||||||
struct rb_root pi_waiters;
|
struct rb_root pi_waiters;
|
||||||
struct rb_node *pi_waiters_leftmost;
|
struct rb_node *pi_waiters_leftmost;
|
||||||
|
/* Updated under owner's pi_lock and rq lock */
|
||||||
|
struct task_struct *pi_top_task;
|
||||||
/* Deadlock detection and priority inheritance handling: */
|
/* Deadlock detection and priority inheritance handling: */
|
||||||
struct rt_mutex_waiter *pi_blocked_on;
|
struct rt_mutex_waiter *pi_blocked_on;
|
||||||
#endif
|
#endif
|
||||||
|
@ -18,27 +18,20 @@ static inline int rt_task(struct task_struct *p)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_RT_MUTEXES
|
#ifdef CONFIG_RT_MUTEXES
|
||||||
extern int rt_mutex_getprio(struct task_struct *p);
|
/*
|
||||||
extern void rt_mutex_setprio(struct task_struct *p, int prio);
|
* Must hold either p->pi_lock or task_rq(p)->lock.
|
||||||
extern int rt_mutex_get_effective_prio(struct task_struct *task, int newprio);
|
*/
|
||||||
extern struct task_struct *rt_mutex_get_top_task(struct task_struct *task);
|
static inline struct task_struct *rt_mutex_get_top_task(struct task_struct *p)
|
||||||
|
{
|
||||||
|
return p->pi_top_task;
|
||||||
|
}
|
||||||
|
extern void rt_mutex_setprio(struct task_struct *p, struct task_struct *pi_task);
|
||||||
extern void rt_mutex_adjust_pi(struct task_struct *p);
|
extern void rt_mutex_adjust_pi(struct task_struct *p);
|
||||||
static inline bool tsk_is_pi_blocked(struct task_struct *tsk)
|
static inline bool tsk_is_pi_blocked(struct task_struct *tsk)
|
||||||
{
|
{
|
||||||
return tsk->pi_blocked_on != NULL;
|
return tsk->pi_blocked_on != NULL;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static inline int rt_mutex_getprio(struct task_struct *p)
|
|
||||||
{
|
|
||||||
return p->normal_prio;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int rt_mutex_get_effective_prio(struct task_struct *task,
|
|
||||||
int newprio)
|
|
||||||
{
|
|
||||||
return newprio;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline struct task_struct *rt_mutex_get_top_task(struct task_struct *task)
|
static inline struct task_struct *rt_mutex_get_top_task(struct task_struct *task)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -120,6 +120,13 @@ extern unsigned int setup_max_cpus;
|
|||||||
extern void __init setup_nr_cpu_ids(void);
|
extern void __init setup_nr_cpu_ids(void);
|
||||||
extern void __init smp_init(void);
|
extern void __init smp_init(void);
|
||||||
|
|
||||||
|
extern int __boot_cpu_id;
|
||||||
|
|
||||||
|
static inline int get_boot_cpu_id(void)
|
||||||
|
{
|
||||||
|
return __boot_cpu_id;
|
||||||
|
}
|
||||||
|
|
||||||
#else /* !SMP */
|
#else /* !SMP */
|
||||||
|
|
||||||
static inline void smp_send_stop(void) { }
|
static inline void smp_send_stop(void) { }
|
||||||
@ -158,6 +165,11 @@ static inline void smp_init(void) { up_late_init(); }
|
|||||||
static inline void smp_init(void) { }
|
static inline void smp_init(void) { }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static inline int get_boot_cpu_id(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* !SMP */
|
#endif /* !SMP */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -70,7 +70,7 @@ DECLARE_EVENT_CLASS(sched_wakeup_template,
|
|||||||
TP_fast_assign(
|
TP_fast_assign(
|
||||||
memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
|
memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
|
||||||
__entry->pid = p->pid;
|
__entry->pid = p->pid;
|
||||||
__entry->prio = p->prio;
|
__entry->prio = p->prio; /* XXX SCHED_DEADLINE */
|
||||||
__entry->success = 1; /* rudiment, kill when possible */
|
__entry->success = 1; /* rudiment, kill when possible */
|
||||||
__entry->target_cpu = task_cpu(p);
|
__entry->target_cpu = task_cpu(p);
|
||||||
),
|
),
|
||||||
@ -147,6 +147,7 @@ TRACE_EVENT(sched_switch,
|
|||||||
memcpy(__entry->prev_comm, prev->comm, TASK_COMM_LEN);
|
memcpy(__entry->prev_comm, prev->comm, TASK_COMM_LEN);
|
||||||
__entry->next_pid = next->pid;
|
__entry->next_pid = next->pid;
|
||||||
__entry->next_prio = next->prio;
|
__entry->next_prio = next->prio;
|
||||||
|
/* XXX SCHED_DEADLINE */
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_printk("prev_comm=%s prev_pid=%d prev_prio=%d prev_state=%s%s ==> next_comm=%s next_pid=%d next_prio=%d",
|
TP_printk("prev_comm=%s prev_pid=%d prev_prio=%d prev_state=%s%s ==> next_comm=%s next_pid=%d next_prio=%d",
|
||||||
@ -181,7 +182,7 @@ TRACE_EVENT(sched_migrate_task,
|
|||||||
TP_fast_assign(
|
TP_fast_assign(
|
||||||
memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
|
memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
|
||||||
__entry->pid = p->pid;
|
__entry->pid = p->pid;
|
||||||
__entry->prio = p->prio;
|
__entry->prio = p->prio; /* XXX SCHED_DEADLINE */
|
||||||
__entry->orig_cpu = task_cpu(p);
|
__entry->orig_cpu = task_cpu(p);
|
||||||
__entry->dest_cpu = dest_cpu;
|
__entry->dest_cpu = dest_cpu;
|
||||||
),
|
),
|
||||||
@ -206,7 +207,7 @@ DECLARE_EVENT_CLASS(sched_process_template,
|
|||||||
TP_fast_assign(
|
TP_fast_assign(
|
||||||
memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
|
memcpy(__entry->comm, p->comm, TASK_COMM_LEN);
|
||||||
__entry->pid = p->pid;
|
__entry->pid = p->pid;
|
||||||
__entry->prio = p->prio;
|
__entry->prio = p->prio; /* XXX SCHED_DEADLINE */
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_printk("comm=%s pid=%d prio=%d",
|
TP_printk("comm=%s pid=%d prio=%d",
|
||||||
@ -253,7 +254,7 @@ TRACE_EVENT(sched_process_wait,
|
|||||||
TP_fast_assign(
|
TP_fast_assign(
|
||||||
memcpy(__entry->comm, current->comm, TASK_COMM_LEN);
|
memcpy(__entry->comm, current->comm, TASK_COMM_LEN);
|
||||||
__entry->pid = pid_nr(pid);
|
__entry->pid = pid_nr(pid);
|
||||||
__entry->prio = current->prio;
|
__entry->prio = current->prio; /* XXX SCHED_DEADLINE */
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_printk("comm=%s pid=%d prio=%d",
|
TP_printk("comm=%s pid=%d prio=%d",
|
||||||
@ -413,9 +414,9 @@ DEFINE_EVENT(sched_stat_runtime, sched_stat_runtime,
|
|||||||
*/
|
*/
|
||||||
TRACE_EVENT(sched_pi_setprio,
|
TRACE_EVENT(sched_pi_setprio,
|
||||||
|
|
||||||
TP_PROTO(struct task_struct *tsk, int newprio),
|
TP_PROTO(struct task_struct *tsk, struct task_struct *pi_task),
|
||||||
|
|
||||||
TP_ARGS(tsk, newprio),
|
TP_ARGS(tsk, pi_task),
|
||||||
|
|
||||||
TP_STRUCT__entry(
|
TP_STRUCT__entry(
|
||||||
__array( char, comm, TASK_COMM_LEN )
|
__array( char, comm, TASK_COMM_LEN )
|
||||||
@ -428,7 +429,8 @@ TRACE_EVENT(sched_pi_setprio,
|
|||||||
memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN);
|
memcpy(__entry->comm, tsk->comm, TASK_COMM_LEN);
|
||||||
__entry->pid = tsk->pid;
|
__entry->pid = tsk->pid;
|
||||||
__entry->oldprio = tsk->prio;
|
__entry->oldprio = tsk->prio;
|
||||||
__entry->newprio = newprio;
|
__entry->newprio = pi_task ? pi_task->prio : tsk->prio;
|
||||||
|
/* XXX SCHED_DEADLINE bits missing */
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_printk("comm=%s pid=%d oldprio=%d newprio=%d",
|
TP_printk("comm=%s pid=%d oldprio=%d newprio=%d",
|
||||||
|
@ -1125,6 +1125,8 @@ core_initcall(cpu_hotplug_pm_sync_init);
|
|||||||
|
|
||||||
#endif /* CONFIG_PM_SLEEP_SMP */
|
#endif /* CONFIG_PM_SLEEP_SMP */
|
||||||
|
|
||||||
|
int __boot_cpu_id;
|
||||||
|
|
||||||
#endif /* CONFIG_SMP */
|
#endif /* CONFIG_SMP */
|
||||||
|
|
||||||
/* Boot processor state steps */
|
/* Boot processor state steps */
|
||||||
@ -1815,6 +1817,10 @@ void __init boot_cpu_init(void)
|
|||||||
set_cpu_active(cpu, true);
|
set_cpu_active(cpu, true);
|
||||||
set_cpu_present(cpu, true);
|
set_cpu_present(cpu, true);
|
||||||
set_cpu_possible(cpu, true);
|
set_cpu_possible(cpu, true);
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
__boot_cpu_id = cpu;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1438,6 +1438,7 @@ static void rt_mutex_init_task(struct task_struct *p)
|
|||||||
#ifdef CONFIG_RT_MUTEXES
|
#ifdef CONFIG_RT_MUTEXES
|
||||||
p->pi_waiters = RB_ROOT;
|
p->pi_waiters = RB_ROOT;
|
||||||
p->pi_waiters_leftmost = NULL;
|
p->pi_waiters_leftmost = NULL;
|
||||||
|
p->pi_top_task = NULL;
|
||||||
p->pi_blocked_on = NULL;
|
p->pi_blocked_on = NULL;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
514
kernel/futex.c
514
kernel/futex.c
@ -802,7 +802,7 @@ static int refill_pi_state_cache(void)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct futex_pi_state * alloc_pi_state(void)
|
static struct futex_pi_state *alloc_pi_state(void)
|
||||||
{
|
{
|
||||||
struct futex_pi_state *pi_state = current->pi_state_cache;
|
struct futex_pi_state *pi_state = current->pi_state_cache;
|
||||||
|
|
||||||
@ -812,6 +812,11 @@ static struct futex_pi_state * alloc_pi_state(void)
|
|||||||
return pi_state;
|
return pi_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void get_pi_state(struct futex_pi_state *pi_state)
|
||||||
|
{
|
||||||
|
WARN_ON_ONCE(!atomic_inc_not_zero(&pi_state->refcount));
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Drops a reference to the pi_state object and frees or caches it
|
* Drops a reference to the pi_state object and frees or caches it
|
||||||
* when the last reference is gone.
|
* when the last reference is gone.
|
||||||
@ -856,7 +861,7 @@ static void put_pi_state(struct futex_pi_state *pi_state)
|
|||||||
* Look up the task based on what TID userspace gave us.
|
* Look up the task based on what TID userspace gave us.
|
||||||
* We dont trust it.
|
* We dont trust it.
|
||||||
*/
|
*/
|
||||||
static struct task_struct * futex_find_get_task(pid_t pid)
|
static struct task_struct *futex_find_get_task(pid_t pid)
|
||||||
{
|
{
|
||||||
struct task_struct *p;
|
struct task_struct *p;
|
||||||
|
|
||||||
@ -916,10 +921,12 @@ void exit_pi_state_list(struct task_struct *curr)
|
|||||||
pi_state->owner = NULL;
|
pi_state->owner = NULL;
|
||||||
raw_spin_unlock_irq(&curr->pi_lock);
|
raw_spin_unlock_irq(&curr->pi_lock);
|
||||||
|
|
||||||
rt_mutex_unlock(&pi_state->pi_mutex);
|
get_pi_state(pi_state);
|
||||||
|
|
||||||
spin_unlock(&hb->lock);
|
spin_unlock(&hb->lock);
|
||||||
|
|
||||||
|
rt_mutex_futex_unlock(&pi_state->pi_mutex);
|
||||||
|
put_pi_state(pi_state);
|
||||||
|
|
||||||
raw_spin_lock_irq(&curr->pi_lock);
|
raw_spin_lock_irq(&curr->pi_lock);
|
||||||
}
|
}
|
||||||
raw_spin_unlock_irq(&curr->pi_lock);
|
raw_spin_unlock_irq(&curr->pi_lock);
|
||||||
@ -973,6 +980,39 @@ void exit_pi_state_list(struct task_struct *curr)
|
|||||||
*
|
*
|
||||||
* [10] There is no transient state which leaves owner and user space
|
* [10] There is no transient state which leaves owner and user space
|
||||||
* TID out of sync.
|
* TID out of sync.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Serialization and lifetime rules:
|
||||||
|
*
|
||||||
|
* hb->lock:
|
||||||
|
*
|
||||||
|
* hb -> futex_q, relation
|
||||||
|
* futex_q -> pi_state, relation
|
||||||
|
*
|
||||||
|
* (cannot be raw because hb can contain arbitrary amount
|
||||||
|
* of futex_q's)
|
||||||
|
*
|
||||||
|
* pi_mutex->wait_lock:
|
||||||
|
*
|
||||||
|
* {uval, pi_state}
|
||||||
|
*
|
||||||
|
* (and pi_mutex 'obviously')
|
||||||
|
*
|
||||||
|
* p->pi_lock:
|
||||||
|
*
|
||||||
|
* p->pi_state_list -> pi_state->list, relation
|
||||||
|
*
|
||||||
|
* pi_state->refcount:
|
||||||
|
*
|
||||||
|
* pi_state lifetime
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Lock order:
|
||||||
|
*
|
||||||
|
* hb->lock
|
||||||
|
* pi_mutex->wait_lock
|
||||||
|
* p->pi_lock
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -980,10 +1020,13 @@ void exit_pi_state_list(struct task_struct *curr)
|
|||||||
* the pi_state against the user space value. If correct, attach to
|
* the pi_state against the user space value. If correct, attach to
|
||||||
* it.
|
* it.
|
||||||
*/
|
*/
|
||||||
static int attach_to_pi_state(u32 uval, struct futex_pi_state *pi_state,
|
static int attach_to_pi_state(u32 __user *uaddr, u32 uval,
|
||||||
|
struct futex_pi_state *pi_state,
|
||||||
struct futex_pi_state **ps)
|
struct futex_pi_state **ps)
|
||||||
{
|
{
|
||||||
pid_t pid = uval & FUTEX_TID_MASK;
|
pid_t pid = uval & FUTEX_TID_MASK;
|
||||||
|
u32 uval2;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Userspace might have messed up non-PI and PI futexes [3]
|
* Userspace might have messed up non-PI and PI futexes [3]
|
||||||
@ -991,8 +1034,38 @@ static int attach_to_pi_state(u32 uval, struct futex_pi_state *pi_state,
|
|||||||
if (unlikely(!pi_state))
|
if (unlikely(!pi_state))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We get here with hb->lock held, and having found a
|
||||||
|
* futex_top_waiter(). This means that futex_lock_pi() of said futex_q
|
||||||
|
* has dropped the hb->lock in between queue_me() and unqueue_me_pi(),
|
||||||
|
* which in turn means that futex_lock_pi() still has a reference on
|
||||||
|
* our pi_state.
|
||||||
|
*
|
||||||
|
* The waiter holding a reference on @pi_state also protects against
|
||||||
|
* the unlocked put_pi_state() in futex_unlock_pi(), futex_lock_pi()
|
||||||
|
* and futex_wait_requeue_pi() as it cannot go to 0 and consequently
|
||||||
|
* free pi_state before we can take a reference ourselves.
|
||||||
|
*/
|
||||||
WARN_ON(!atomic_read(&pi_state->refcount));
|
WARN_ON(!atomic_read(&pi_state->refcount));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now that we have a pi_state, we can acquire wait_lock
|
||||||
|
* and do the state validation.
|
||||||
|
*/
|
||||||
|
raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since {uval, pi_state} is serialized by wait_lock, and our current
|
||||||
|
* uval was read without holding it, it can have changed. Verify it
|
||||||
|
* still is what we expect it to be, otherwise retry the entire
|
||||||
|
* operation.
|
||||||
|
*/
|
||||||
|
if (get_futex_value_locked(&uval2, uaddr))
|
||||||
|
goto out_efault;
|
||||||
|
|
||||||
|
if (uval != uval2)
|
||||||
|
goto out_eagain;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle the owner died case:
|
* Handle the owner died case:
|
||||||
*/
|
*/
|
||||||
@ -1008,11 +1081,11 @@ static int attach_to_pi_state(u32 uval, struct futex_pi_state *pi_state,
|
|||||||
* is not 0. Inconsistent state. [5]
|
* is not 0. Inconsistent state. [5]
|
||||||
*/
|
*/
|
||||||
if (pid)
|
if (pid)
|
||||||
return -EINVAL;
|
goto out_einval;
|
||||||
/*
|
/*
|
||||||
* Take a ref on the state and return success. [4]
|
* Take a ref on the state and return success. [4]
|
||||||
*/
|
*/
|
||||||
goto out_state;
|
goto out_attach;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1024,14 +1097,14 @@ static int attach_to_pi_state(u32 uval, struct futex_pi_state *pi_state,
|
|||||||
* Take a ref on the state and return success. [6]
|
* Take a ref on the state and return success. [6]
|
||||||
*/
|
*/
|
||||||
if (!pid)
|
if (!pid)
|
||||||
goto out_state;
|
goto out_attach;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* If the owner died bit is not set, then the pi_state
|
* If the owner died bit is not set, then the pi_state
|
||||||
* must have an owner. [7]
|
* must have an owner. [7]
|
||||||
*/
|
*/
|
||||||
if (!pi_state->owner)
|
if (!pi_state->owner)
|
||||||
return -EINVAL;
|
goto out_einval;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1040,11 +1113,29 @@ static int attach_to_pi_state(u32 uval, struct futex_pi_state *pi_state,
|
|||||||
* user space TID. [9/10]
|
* user space TID. [9/10]
|
||||||
*/
|
*/
|
||||||
if (pid != task_pid_vnr(pi_state->owner))
|
if (pid != task_pid_vnr(pi_state->owner))
|
||||||
return -EINVAL;
|
goto out_einval;
|
||||||
out_state:
|
|
||||||
atomic_inc(&pi_state->refcount);
|
out_attach:
|
||||||
|
get_pi_state(pi_state);
|
||||||
|
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
|
||||||
*ps = pi_state;
|
*ps = pi_state;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_einval:
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out_error;
|
||||||
|
|
||||||
|
out_eagain:
|
||||||
|
ret = -EAGAIN;
|
||||||
|
goto out_error;
|
||||||
|
|
||||||
|
out_efault:
|
||||||
|
ret = -EFAULT;
|
||||||
|
goto out_error;
|
||||||
|
|
||||||
|
out_error:
|
||||||
|
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1095,6 +1186,9 @@ static int attach_to_pi_owner(u32 uval, union futex_key *key,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* No existing pi state. First waiter. [2]
|
* No existing pi state. First waiter. [2]
|
||||||
|
*
|
||||||
|
* This creates pi_state, we have hb->lock held, this means nothing can
|
||||||
|
* observe this state, wait_lock is irrelevant.
|
||||||
*/
|
*/
|
||||||
pi_state = alloc_pi_state();
|
pi_state = alloc_pi_state();
|
||||||
|
|
||||||
@ -1119,17 +1213,18 @@ static int attach_to_pi_owner(u32 uval, union futex_key *key,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lookup_pi_state(u32 uval, struct futex_hash_bucket *hb,
|
static int lookup_pi_state(u32 __user *uaddr, u32 uval,
|
||||||
|
struct futex_hash_bucket *hb,
|
||||||
union futex_key *key, struct futex_pi_state **ps)
|
union futex_key *key, struct futex_pi_state **ps)
|
||||||
{
|
{
|
||||||
struct futex_q *match = futex_top_waiter(hb, key);
|
struct futex_q *top_waiter = futex_top_waiter(hb, key);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If there is a waiter on that futex, validate it and
|
* If there is a waiter on that futex, validate it and
|
||||||
* attach to the pi_state when the validation succeeds.
|
* attach to the pi_state when the validation succeeds.
|
||||||
*/
|
*/
|
||||||
if (match)
|
if (top_waiter)
|
||||||
return attach_to_pi_state(uval, match->pi_state, ps);
|
return attach_to_pi_state(uaddr, uval, top_waiter->pi_state, ps);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We are the first waiter - try to look up the owner based on
|
* We are the first waiter - try to look up the owner based on
|
||||||
@ -1148,7 +1243,7 @@ static int lock_pi_update_atomic(u32 __user *uaddr, u32 uval, u32 newval)
|
|||||||
if (unlikely(cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)))
|
if (unlikely(cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
/*If user space value changed, let the caller retry */
|
/* If user space value changed, let the caller retry */
|
||||||
return curval != uval ? -EAGAIN : 0;
|
return curval != uval ? -EAGAIN : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1176,7 +1271,7 @@ static int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb,
|
|||||||
struct task_struct *task, int set_waiters)
|
struct task_struct *task, int set_waiters)
|
||||||
{
|
{
|
||||||
u32 uval, newval, vpid = task_pid_vnr(task);
|
u32 uval, newval, vpid = task_pid_vnr(task);
|
||||||
struct futex_q *match;
|
struct futex_q *top_waiter;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1202,9 +1297,9 @@ static int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb,
|
|||||||
* Lookup existing state first. If it exists, try to attach to
|
* Lookup existing state first. If it exists, try to attach to
|
||||||
* its pi_state.
|
* its pi_state.
|
||||||
*/
|
*/
|
||||||
match = futex_top_waiter(hb, key);
|
top_waiter = futex_top_waiter(hb, key);
|
||||||
if (match)
|
if (top_waiter)
|
||||||
return attach_to_pi_state(uval, match->pi_state, ps);
|
return attach_to_pi_state(uaddr, uval, top_waiter->pi_state, ps);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* No waiter and user TID is 0. We are here because the
|
* No waiter and user TID is 0. We are here because the
|
||||||
@ -1285,50 +1380,44 @@ static void mark_wake_futex(struct wake_q_head *wake_q, struct futex_q *q)
|
|||||||
wake_q_add(wake_q, p);
|
wake_q_add(wake_q, p);
|
||||||
__unqueue_futex(q);
|
__unqueue_futex(q);
|
||||||
/*
|
/*
|
||||||
* The waiting task can free the futex_q as soon as
|
* The waiting task can free the futex_q as soon as q->lock_ptr = NULL
|
||||||
* q->lock_ptr = NULL is written, without taking any locks. A
|
* is written, without taking any locks. This is possible in the event
|
||||||
* memory barrier is required here to prevent the following
|
* of a spurious wakeup, for example. A memory barrier is required here
|
||||||
* store to lock_ptr from getting ahead of the plist_del.
|
* to prevent the following store to lock_ptr from getting ahead of the
|
||||||
|
* plist_del in __unqueue_futex().
|
||||||
*/
|
*/
|
||||||
smp_wmb();
|
smp_store_release(&q->lock_ptr, NULL);
|
||||||
q->lock_ptr = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this,
|
/*
|
||||||
struct futex_hash_bucket *hb)
|
* Caller must hold a reference on @pi_state.
|
||||||
|
*/
|
||||||
|
static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_pi_state *pi_state)
|
||||||
{
|
{
|
||||||
struct task_struct *new_owner;
|
|
||||||
struct futex_pi_state *pi_state = this->pi_state;
|
|
||||||
u32 uninitialized_var(curval), newval;
|
u32 uninitialized_var(curval), newval;
|
||||||
|
struct task_struct *new_owner;
|
||||||
|
bool postunlock = false;
|
||||||
DEFINE_WAKE_Q(wake_q);
|
DEFINE_WAKE_Q(wake_q);
|
||||||
bool deboost;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (!pi_state)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If current does not own the pi_state then the futex is
|
|
||||||
* inconsistent and user space fiddled with the futex value.
|
|
||||||
*/
|
|
||||||
if (pi_state->owner != current)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock);
|
|
||||||
new_owner = rt_mutex_next_owner(&pi_state->pi_mutex);
|
new_owner = rt_mutex_next_owner(&pi_state->pi_mutex);
|
||||||
|
if (WARN_ON_ONCE(!new_owner)) {
|
||||||
/*
|
/*
|
||||||
* It is possible that the next waiter (the one that brought
|
* As per the comment in futex_unlock_pi() this should not happen.
|
||||||
* this owner to the kernel) timed out and is no longer
|
*
|
||||||
* waiting on the lock.
|
* When this happens, give up our locks and try again, giving
|
||||||
|
* the futex_lock_pi() instance time to complete, either by
|
||||||
|
* waiting on the rtmutex or removing itself from the futex
|
||||||
|
* queue.
|
||||||
*/
|
*/
|
||||||
if (!new_owner)
|
ret = -EAGAIN;
|
||||||
new_owner = this->task;
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We pass it to the next owner. The WAITERS bit is always
|
* We pass it to the next owner. The WAITERS bit is always kept
|
||||||
* kept enabled while there is PI state around. We cleanup the
|
* enabled while there is PI state around. We cleanup the owner
|
||||||
* owner died bit, because we are the owner.
|
* died bit, because we are the owner.
|
||||||
*/
|
*/
|
||||||
newval = FUTEX_WAITERS | task_pid_vnr(new_owner);
|
newval = FUTEX_WAITERS | task_pid_vnr(new_owner);
|
||||||
|
|
||||||
@ -1337,6 +1426,7 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this,
|
|||||||
|
|
||||||
if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) {
|
if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval)) {
|
||||||
ret = -EFAULT;
|
ret = -EFAULT;
|
||||||
|
|
||||||
} else if (curval != uval) {
|
} else if (curval != uval) {
|
||||||
/*
|
/*
|
||||||
* If a unconditional UNLOCK_PI operation (user space did not
|
* If a unconditional UNLOCK_PI operation (user space did not
|
||||||
@ -1349,10 +1439,14 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this,
|
|||||||
else
|
else
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
}
|
}
|
||||||
if (ret) {
|
|
||||||
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
|
if (ret)
|
||||||
return ret;
|
goto out_unlock;
|
||||||
}
|
|
||||||
|
/*
|
||||||
|
* This is a point of no return; once we modify the uval there is no
|
||||||
|
* going back and subsequent operations must not fail.
|
||||||
|
*/
|
||||||
|
|
||||||
raw_spin_lock(&pi_state->owner->pi_lock);
|
raw_spin_lock(&pi_state->owner->pi_lock);
|
||||||
WARN_ON(list_empty(&pi_state->list));
|
WARN_ON(list_empty(&pi_state->list));
|
||||||
@ -1365,22 +1459,15 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_q *this,
|
|||||||
pi_state->owner = new_owner;
|
pi_state->owner = new_owner;
|
||||||
raw_spin_unlock(&new_owner->pi_lock);
|
raw_spin_unlock(&new_owner->pi_lock);
|
||||||
|
|
||||||
|
postunlock = __rt_mutex_futex_unlock(&pi_state->pi_mutex, &wake_q);
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
|
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
|
||||||
|
|
||||||
deboost = rt_mutex_futex_unlock(&pi_state->pi_mutex, &wake_q);
|
if (postunlock)
|
||||||
|
rt_mutex_postunlock(&wake_q);
|
||||||
|
|
||||||
/*
|
return ret;
|
||||||
* First unlock HB so the waiter does not spin on it once he got woken
|
|
||||||
* up. Second wake up the waiter before the priority is adjusted. If we
|
|
||||||
* deboost first (and lose our higher priority), then the task might get
|
|
||||||
* scheduled away before the wake up can take place.
|
|
||||||
*/
|
|
||||||
spin_unlock(&hb->lock);
|
|
||||||
wake_up_q(&wake_q);
|
|
||||||
if (deboost)
|
|
||||||
rt_mutex_adjust_prio(current);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1826,7 +1913,7 @@ retry_private:
|
|||||||
* If that call succeeds then we have pi_state and an
|
* If that call succeeds then we have pi_state and an
|
||||||
* initial refcount on it.
|
* initial refcount on it.
|
||||||
*/
|
*/
|
||||||
ret = lookup_pi_state(ret, hb2, &key2, &pi_state);
|
ret = lookup_pi_state(uaddr2, ret, hb2, &key2, &pi_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
@ -1909,7 +1996,7 @@ retry_private:
|
|||||||
* refcount on the pi_state and store the pointer in
|
* refcount on the pi_state and store the pointer in
|
||||||
* the futex_q object of the waiter.
|
* the futex_q object of the waiter.
|
||||||
*/
|
*/
|
||||||
atomic_inc(&pi_state->refcount);
|
get_pi_state(pi_state);
|
||||||
this->pi_state = pi_state;
|
this->pi_state = pi_state;
|
||||||
ret = rt_mutex_start_proxy_lock(&pi_state->pi_mutex,
|
ret = rt_mutex_start_proxy_lock(&pi_state->pi_mutex,
|
||||||
this->rt_waiter,
|
this->rt_waiter,
|
||||||
@ -2009,20 +2096,7 @@ queue_unlock(struct futex_hash_bucket *hb)
|
|||||||
hb_waiters_dec(hb);
|
hb_waiters_dec(hb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static inline void __queue_me(struct futex_q *q, struct futex_hash_bucket *hb)
|
||||||
* queue_me() - Enqueue the futex_q on the futex_hash_bucket
|
|
||||||
* @q: The futex_q to enqueue
|
|
||||||
* @hb: The destination hash bucket
|
|
||||||
*
|
|
||||||
* The hb->lock must be held by the caller, and is released here. A call to
|
|
||||||
* queue_me() is typically paired with exactly one call to unqueue_me(). The
|
|
||||||
* exceptions involve the PI related operations, which may use unqueue_me_pi()
|
|
||||||
* or nothing if the unqueue is done as part of the wake process and the unqueue
|
|
||||||
* state is implicit in the state of woken task (see futex_wait_requeue_pi() for
|
|
||||||
* an example).
|
|
||||||
*/
|
|
||||||
static inline void queue_me(struct futex_q *q, struct futex_hash_bucket *hb)
|
|
||||||
__releases(&hb->lock)
|
|
||||||
{
|
{
|
||||||
int prio;
|
int prio;
|
||||||
|
|
||||||
@ -2039,6 +2113,24 @@ static inline void queue_me(struct futex_q *q, struct futex_hash_bucket *hb)
|
|||||||
plist_node_init(&q->list, prio);
|
plist_node_init(&q->list, prio);
|
||||||
plist_add(&q->list, &hb->chain);
|
plist_add(&q->list, &hb->chain);
|
||||||
q->task = current;
|
q->task = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* queue_me() - Enqueue the futex_q on the futex_hash_bucket
|
||||||
|
* @q: The futex_q to enqueue
|
||||||
|
* @hb: The destination hash bucket
|
||||||
|
*
|
||||||
|
* The hb->lock must be held by the caller, and is released here. A call to
|
||||||
|
* queue_me() is typically paired with exactly one call to unqueue_me(). The
|
||||||
|
* exceptions involve the PI related operations, which may use unqueue_me_pi()
|
||||||
|
* or nothing if the unqueue is done as part of the wake process and the unqueue
|
||||||
|
* state is implicit in the state of woken task (see futex_wait_requeue_pi() for
|
||||||
|
* an example).
|
||||||
|
*/
|
||||||
|
static inline void queue_me(struct futex_q *q, struct futex_hash_bucket *hb)
|
||||||
|
__releases(&hb->lock)
|
||||||
|
{
|
||||||
|
__queue_me(q, hb);
|
||||||
spin_unlock(&hb->lock);
|
spin_unlock(&hb->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2125,10 +2217,13 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
|
|||||||
{
|
{
|
||||||
u32 newtid = task_pid_vnr(newowner) | FUTEX_WAITERS;
|
u32 newtid = task_pid_vnr(newowner) | FUTEX_WAITERS;
|
||||||
struct futex_pi_state *pi_state = q->pi_state;
|
struct futex_pi_state *pi_state = q->pi_state;
|
||||||
struct task_struct *oldowner = pi_state->owner;
|
|
||||||
u32 uval, uninitialized_var(curval), newval;
|
u32 uval, uninitialized_var(curval), newval;
|
||||||
|
struct task_struct *oldowner;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock);
|
||||||
|
|
||||||
|
oldowner = pi_state->owner;
|
||||||
/* Owner died? */
|
/* Owner died? */
|
||||||
if (!pi_state->owner)
|
if (!pi_state->owner)
|
||||||
newtid |= FUTEX_OWNER_DIED;
|
newtid |= FUTEX_OWNER_DIED;
|
||||||
@ -2136,7 +2231,8 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
|
|||||||
/*
|
/*
|
||||||
* We are here either because we stole the rtmutex from the
|
* We are here either because we stole the rtmutex from the
|
||||||
* previous highest priority waiter or we are the highest priority
|
* previous highest priority waiter or we are the highest priority
|
||||||
* waiter but failed to get the rtmutex the first time.
|
* waiter but have failed to get the rtmutex the first time.
|
||||||
|
*
|
||||||
* We have to replace the newowner TID in the user space variable.
|
* We have to replace the newowner TID in the user space variable.
|
||||||
* This must be atomic as we have to preserve the owner died bit here.
|
* This must be atomic as we have to preserve the owner died bit here.
|
||||||
*
|
*
|
||||||
@ -2144,17 +2240,16 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
|
|||||||
* because we can fault here. Imagine swapped out pages or a fork
|
* because we can fault here. Imagine swapped out pages or a fork
|
||||||
* that marked all the anonymous memory readonly for cow.
|
* that marked all the anonymous memory readonly for cow.
|
||||||
*
|
*
|
||||||
* Modifying pi_state _before_ the user space value would
|
* Modifying pi_state _before_ the user space value would leave the
|
||||||
* leave the pi_state in an inconsistent state when we fault
|
* pi_state in an inconsistent state when we fault here, because we
|
||||||
* here, because we need to drop the hash bucket lock to
|
* need to drop the locks to handle the fault. This might be observed
|
||||||
* handle the fault. This might be observed in the PID check
|
* in the PID check in lookup_pi_state.
|
||||||
* in lookup_pi_state.
|
|
||||||
*/
|
*/
|
||||||
retry:
|
retry:
|
||||||
if (get_futex_value_locked(&uval, uaddr))
|
if (get_futex_value_locked(&uval, uaddr))
|
||||||
goto handle_fault;
|
goto handle_fault;
|
||||||
|
|
||||||
while (1) {
|
for (;;) {
|
||||||
newval = (uval & FUTEX_OWNER_DIED) | newtid;
|
newval = (uval & FUTEX_OWNER_DIED) | newtid;
|
||||||
|
|
||||||
if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))
|
if (cmpxchg_futex_value_locked(&curval, uaddr, uval, newval))
|
||||||
@ -2169,47 +2264,60 @@ retry:
|
|||||||
* itself.
|
* itself.
|
||||||
*/
|
*/
|
||||||
if (pi_state->owner != NULL) {
|
if (pi_state->owner != NULL) {
|
||||||
raw_spin_lock_irq(&pi_state->owner->pi_lock);
|
raw_spin_lock(&pi_state->owner->pi_lock);
|
||||||
WARN_ON(list_empty(&pi_state->list));
|
WARN_ON(list_empty(&pi_state->list));
|
||||||
list_del_init(&pi_state->list);
|
list_del_init(&pi_state->list);
|
||||||
raw_spin_unlock_irq(&pi_state->owner->pi_lock);
|
raw_spin_unlock(&pi_state->owner->pi_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
pi_state->owner = newowner;
|
pi_state->owner = newowner;
|
||||||
|
|
||||||
raw_spin_lock_irq(&newowner->pi_lock);
|
raw_spin_lock(&newowner->pi_lock);
|
||||||
WARN_ON(!list_empty(&pi_state->list));
|
WARN_ON(!list_empty(&pi_state->list));
|
||||||
list_add(&pi_state->list, &newowner->pi_state_list);
|
list_add(&pi_state->list, &newowner->pi_state_list);
|
||||||
raw_spin_unlock_irq(&newowner->pi_lock);
|
raw_spin_unlock(&newowner->pi_lock);
|
||||||
|
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To handle the page fault we need to drop the hash bucket
|
* To handle the page fault we need to drop the locks here. That gives
|
||||||
* lock here. That gives the other task (either the highest priority
|
* the other task (either the highest priority waiter itself or the
|
||||||
* waiter itself or the task which stole the rtmutex) the
|
* task which stole the rtmutex) the chance to try the fixup of the
|
||||||
* chance to try the fixup of the pi_state. So once we are
|
* pi_state. So once we are back from handling the fault we need to
|
||||||
* back from handling the fault we need to check the pi_state
|
* check the pi_state after reacquiring the locks and before trying to
|
||||||
* after reacquiring the hash bucket lock and before trying to
|
* do another fixup. When the fixup has been done already we simply
|
||||||
* do another fixup. When the fixup has been done already we
|
* return.
|
||||||
* simply return.
|
*
|
||||||
|
* Note: we hold both hb->lock and pi_mutex->wait_lock. We can safely
|
||||||
|
* drop hb->lock since the caller owns the hb -> futex_q relation.
|
||||||
|
* Dropping the pi_mutex->wait_lock requires the state revalidate.
|
||||||
*/
|
*/
|
||||||
handle_fault:
|
handle_fault:
|
||||||
|
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
|
||||||
spin_unlock(q->lock_ptr);
|
spin_unlock(q->lock_ptr);
|
||||||
|
|
||||||
ret = fault_in_user_writeable(uaddr);
|
ret = fault_in_user_writeable(uaddr);
|
||||||
|
|
||||||
spin_lock(q->lock_ptr);
|
spin_lock(q->lock_ptr);
|
||||||
|
raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if someone else fixed it for us:
|
* Check if someone else fixed it for us:
|
||||||
*/
|
*/
|
||||||
if (pi_state->owner != oldowner)
|
if (pi_state->owner != oldowner) {
|
||||||
return 0;
|
ret = 0;
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto out_unlock;
|
||||||
|
|
||||||
goto retry;
|
goto retry;
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
|
raw_spin_unlock_irq(&pi_state->pi_mutex.wait_lock);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long futex_wait_restart(struct restart_block *restart);
|
static long futex_wait_restart(struct restart_block *restart);
|
||||||
@ -2231,57 +2339,32 @@ static long futex_wait_restart(struct restart_block *restart);
|
|||||||
*/
|
*/
|
||||||
static int fixup_owner(u32 __user *uaddr, struct futex_q *q, int locked)
|
static int fixup_owner(u32 __user *uaddr, struct futex_q *q, int locked)
|
||||||
{
|
{
|
||||||
struct task_struct *owner;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (locked) {
|
if (locked) {
|
||||||
/*
|
/*
|
||||||
* Got the lock. We might not be the anticipated owner if we
|
* Got the lock. We might not be the anticipated owner if we
|
||||||
* did a lock-steal - fix up the PI-state in that case:
|
* did a lock-steal - fix up the PI-state in that case:
|
||||||
|
*
|
||||||
|
* We can safely read pi_state->owner without holding wait_lock
|
||||||
|
* because we now own the rt_mutex, only the owner will attempt
|
||||||
|
* to change it.
|
||||||
*/
|
*/
|
||||||
if (q->pi_state->owner != current)
|
if (q->pi_state->owner != current)
|
||||||
ret = fixup_pi_state_owner(uaddr, q, current);
|
ret = fixup_pi_state_owner(uaddr, q, current);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Catch the rare case, where the lock was released when we were on the
|
|
||||||
* way back before we locked the hash bucket.
|
|
||||||
*/
|
|
||||||
if (q->pi_state->owner == current) {
|
|
||||||
/*
|
|
||||||
* Try to get the rt_mutex now. This might fail as some other
|
|
||||||
* task acquired the rt_mutex after we removed ourself from the
|
|
||||||
* rt_mutex waiters list.
|
|
||||||
*/
|
|
||||||
if (rt_mutex_trylock(&q->pi_state->pi_mutex)) {
|
|
||||||
locked = 1;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* pi_state is incorrect, some other task did a lock steal and
|
|
||||||
* we returned due to timeout or signal without taking the
|
|
||||||
* rt_mutex. Too late.
|
|
||||||
*/
|
|
||||||
raw_spin_lock_irq(&q->pi_state->pi_mutex.wait_lock);
|
|
||||||
owner = rt_mutex_owner(&q->pi_state->pi_mutex);
|
|
||||||
if (!owner)
|
|
||||||
owner = rt_mutex_next_owner(&q->pi_state->pi_mutex);
|
|
||||||
raw_spin_unlock_irq(&q->pi_state->pi_mutex.wait_lock);
|
|
||||||
ret = fixup_pi_state_owner(uaddr, q, owner);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Paranoia check. If we did not take the lock, then we should not be
|
* Paranoia check. If we did not take the lock, then we should not be
|
||||||
* the owner of the rt_mutex.
|
* the owner of the rt_mutex.
|
||||||
*/
|
*/
|
||||||
if (rt_mutex_owner(&q->pi_state->pi_mutex) == current)
|
if (rt_mutex_owner(&q->pi_state->pi_mutex) == current) {
|
||||||
printk(KERN_ERR "fixup_owner: ret = %d pi-mutex: %p "
|
printk(KERN_ERR "fixup_owner: ret = %d pi-mutex: %p "
|
||||||
"pi-state %p\n", ret,
|
"pi-state %p\n", ret,
|
||||||
q->pi_state->pi_mutex.owner,
|
q->pi_state->pi_mutex.owner,
|
||||||
q->pi_state->owner);
|
q->pi_state->owner);
|
||||||
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return ret ? ret : locked;
|
return ret ? ret : locked;
|
||||||
@ -2505,6 +2588,8 @@ static int futex_lock_pi(u32 __user *uaddr, unsigned int flags,
|
|||||||
ktime_t *time, int trylock)
|
ktime_t *time, int trylock)
|
||||||
{
|
{
|
||||||
struct hrtimer_sleeper timeout, *to = NULL;
|
struct hrtimer_sleeper timeout, *to = NULL;
|
||||||
|
struct futex_pi_state *pi_state = NULL;
|
||||||
|
struct rt_mutex_waiter rt_waiter;
|
||||||
struct futex_hash_bucket *hb;
|
struct futex_hash_bucket *hb;
|
||||||
struct futex_q q = futex_q_init;
|
struct futex_q q = futex_q_init;
|
||||||
int res, ret;
|
int res, ret;
|
||||||
@ -2557,24 +2642,67 @@ retry_private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WARN_ON(!q.pi_state);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Only actually queue now that the atomic ops are done:
|
* Only actually queue now that the atomic ops are done:
|
||||||
*/
|
*/
|
||||||
queue_me(&q, hb);
|
__queue_me(&q, hb);
|
||||||
|
|
||||||
WARN_ON(!q.pi_state);
|
if (trylock) {
|
||||||
/*
|
ret = rt_mutex_futex_trylock(&q.pi_state->pi_mutex);
|
||||||
* Block on the PI mutex:
|
|
||||||
*/
|
|
||||||
if (!trylock) {
|
|
||||||
ret = rt_mutex_timed_futex_lock(&q.pi_state->pi_mutex, to);
|
|
||||||
} else {
|
|
||||||
ret = rt_mutex_trylock(&q.pi_state->pi_mutex);
|
|
||||||
/* Fixup the trylock return value: */
|
/* Fixup the trylock return value: */
|
||||||
ret = ret ? 0 : -EWOULDBLOCK;
|
ret = ret ? 0 : -EWOULDBLOCK;
|
||||||
|
goto no_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rt_mutex_init_waiter(&rt_waiter);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* On PREEMPT_RT_FULL, when hb->lock becomes an rt_mutex, we must not
|
||||||
|
* hold it while doing rt_mutex_start_proxy(), because then it will
|
||||||
|
* include hb->lock in the blocking chain, even through we'll not in
|
||||||
|
* fact hold it while blocking. This will lead it to report -EDEADLK
|
||||||
|
* and BUG when futex_unlock_pi() interleaves with this.
|
||||||
|
*
|
||||||
|
* Therefore acquire wait_lock while holding hb->lock, but drop the
|
||||||
|
* latter before calling rt_mutex_start_proxy_lock(). This still fully
|
||||||
|
* serializes against futex_unlock_pi() as that does the exact same
|
||||||
|
* lock handoff sequence.
|
||||||
|
*/
|
||||||
|
raw_spin_lock_irq(&q.pi_state->pi_mutex.wait_lock);
|
||||||
|
spin_unlock(q.lock_ptr);
|
||||||
|
ret = __rt_mutex_start_proxy_lock(&q.pi_state->pi_mutex, &rt_waiter, current);
|
||||||
|
raw_spin_unlock_irq(&q.pi_state->pi_mutex.wait_lock);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
if (ret == 1)
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
spin_lock(q.lock_ptr);
|
spin_lock(q.lock_ptr);
|
||||||
|
goto no_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (unlikely(to))
|
||||||
|
hrtimer_start_expires(&to->timer, HRTIMER_MODE_ABS);
|
||||||
|
|
||||||
|
ret = rt_mutex_wait_proxy_lock(&q.pi_state->pi_mutex, to, &rt_waiter);
|
||||||
|
|
||||||
|
spin_lock(q.lock_ptr);
|
||||||
|
/*
|
||||||
|
* If we failed to acquire the lock (signal/timeout), we must
|
||||||
|
* first acquire the hb->lock before removing the lock from the
|
||||||
|
* rt_mutex waitqueue, such that we can keep the hb and rt_mutex
|
||||||
|
* wait lists consistent.
|
||||||
|
*
|
||||||
|
* In particular; it is important that futex_unlock_pi() can not
|
||||||
|
* observe this inconsistency.
|
||||||
|
*/
|
||||||
|
if (ret && !rt_mutex_cleanup_proxy_lock(&q.pi_state->pi_mutex, &rt_waiter))
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
no_block:
|
||||||
/*
|
/*
|
||||||
* Fixup the pi_state owner and possibly acquire the lock if we
|
* Fixup the pi_state owner and possibly acquire the lock if we
|
||||||
* haven't already.
|
* haven't already.
|
||||||
@ -2591,12 +2719,19 @@ retry_private:
|
|||||||
* If fixup_owner() faulted and was unable to handle the fault, unlock
|
* If fixup_owner() faulted and was unable to handle the fault, unlock
|
||||||
* it and return the fault to userspace.
|
* it and return the fault to userspace.
|
||||||
*/
|
*/
|
||||||
if (ret && (rt_mutex_owner(&q.pi_state->pi_mutex) == current))
|
if (ret && (rt_mutex_owner(&q.pi_state->pi_mutex) == current)) {
|
||||||
rt_mutex_unlock(&q.pi_state->pi_mutex);
|
pi_state = q.pi_state;
|
||||||
|
get_pi_state(pi_state);
|
||||||
|
}
|
||||||
|
|
||||||
/* Unqueue and drop the lock */
|
/* Unqueue and drop the lock */
|
||||||
unqueue_me_pi(&q);
|
unqueue_me_pi(&q);
|
||||||
|
|
||||||
|
if (pi_state) {
|
||||||
|
rt_mutex_futex_unlock(&pi_state->pi_mutex);
|
||||||
|
put_pi_state(pi_state);
|
||||||
|
}
|
||||||
|
|
||||||
goto out_put_key;
|
goto out_put_key;
|
||||||
|
|
||||||
out_unlock_put_key:
|
out_unlock_put_key:
|
||||||
@ -2605,8 +2740,10 @@ out_unlock_put_key:
|
|||||||
out_put_key:
|
out_put_key:
|
||||||
put_futex_key(&q.key);
|
put_futex_key(&q.key);
|
||||||
out:
|
out:
|
||||||
if (to)
|
if (to) {
|
||||||
|
hrtimer_cancel(&to->timer);
|
||||||
destroy_hrtimer_on_stack(&to->timer);
|
destroy_hrtimer_on_stack(&to->timer);
|
||||||
|
}
|
||||||
return ret != -EINTR ? ret : -ERESTARTNOINTR;
|
return ret != -EINTR ? ret : -ERESTARTNOINTR;
|
||||||
|
|
||||||
uaddr_faulted:
|
uaddr_faulted:
|
||||||
@ -2633,7 +2770,7 @@ static int futex_unlock_pi(u32 __user *uaddr, unsigned int flags)
|
|||||||
u32 uninitialized_var(curval), uval, vpid = task_pid_vnr(current);
|
u32 uninitialized_var(curval), uval, vpid = task_pid_vnr(current);
|
||||||
union futex_key key = FUTEX_KEY_INIT;
|
union futex_key key = FUTEX_KEY_INIT;
|
||||||
struct futex_hash_bucket *hb;
|
struct futex_hash_bucket *hb;
|
||||||
struct futex_q *match;
|
struct futex_q *top_waiter;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
retry:
|
retry:
|
||||||
@ -2657,12 +2794,37 @@ retry:
|
|||||||
* all and we at least want to know if user space fiddled
|
* all and we at least want to know if user space fiddled
|
||||||
* with the futex value instead of blindly unlocking.
|
* with the futex value instead of blindly unlocking.
|
||||||
*/
|
*/
|
||||||
match = futex_top_waiter(hb, &key);
|
top_waiter = futex_top_waiter(hb, &key);
|
||||||
if (match) {
|
if (top_waiter) {
|
||||||
ret = wake_futex_pi(uaddr, uval, match, hb);
|
struct futex_pi_state *pi_state = top_waiter->pi_state;
|
||||||
|
|
||||||
|
ret = -EINVAL;
|
||||||
|
if (!pi_state)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In case of success wake_futex_pi dropped the hash
|
* If current does not own the pi_state then the futex is
|
||||||
* bucket lock.
|
* inconsistent and user space fiddled with the futex value.
|
||||||
|
*/
|
||||||
|
if (pi_state->owner != current)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
get_pi_state(pi_state);
|
||||||
|
/*
|
||||||
|
* By taking wait_lock while still holding hb->lock, we ensure
|
||||||
|
* there is no point where we hold neither; and therefore
|
||||||
|
* wake_futex_pi() must observe a state consistent with what we
|
||||||
|
* observed.
|
||||||
|
*/
|
||||||
|
raw_spin_lock_irq(&pi_state->pi_mutex.wait_lock);
|
||||||
|
spin_unlock(&hb->lock);
|
||||||
|
|
||||||
|
ret = wake_futex_pi(uaddr, uval, pi_state);
|
||||||
|
|
||||||
|
put_pi_state(pi_state);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Success, we're done! No tricky corner cases.
|
||||||
*/
|
*/
|
||||||
if (!ret)
|
if (!ret)
|
||||||
goto out_putkey;
|
goto out_putkey;
|
||||||
@ -2677,7 +2839,6 @@ retry:
|
|||||||
* setting the FUTEX_WAITERS bit. Try again.
|
* setting the FUTEX_WAITERS bit. Try again.
|
||||||
*/
|
*/
|
||||||
if (ret == -EAGAIN) {
|
if (ret == -EAGAIN) {
|
||||||
spin_unlock(&hb->lock);
|
|
||||||
put_futex_key(&key);
|
put_futex_key(&key);
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
@ -2685,7 +2846,7 @@ retry:
|
|||||||
* wake_futex_pi has detected invalid state. Tell user
|
* wake_futex_pi has detected invalid state. Tell user
|
||||||
* space.
|
* space.
|
||||||
*/
|
*/
|
||||||
goto out_unlock;
|
goto out_putkey;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2695,8 +2856,10 @@ retry:
|
|||||||
* preserve the WAITERS bit not the OWNER_DIED one. We are the
|
* preserve the WAITERS bit not the OWNER_DIED one. We are the
|
||||||
* owner.
|
* owner.
|
||||||
*/
|
*/
|
||||||
if (cmpxchg_futex_value_locked(&curval, uaddr, uval, 0))
|
if (cmpxchg_futex_value_locked(&curval, uaddr, uval, 0)) {
|
||||||
|
spin_unlock(&hb->lock);
|
||||||
goto pi_faulted;
|
goto pi_faulted;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If uval has changed, let user space handle it.
|
* If uval has changed, let user space handle it.
|
||||||
@ -2710,7 +2873,6 @@ out_putkey:
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
pi_faulted:
|
pi_faulted:
|
||||||
spin_unlock(&hb->lock);
|
|
||||||
put_futex_key(&key);
|
put_futex_key(&key);
|
||||||
|
|
||||||
ret = fault_in_user_writeable(uaddr);
|
ret = fault_in_user_writeable(uaddr);
|
||||||
@ -2814,6 +2976,7 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
|
|||||||
u32 __user *uaddr2)
|
u32 __user *uaddr2)
|
||||||
{
|
{
|
||||||
struct hrtimer_sleeper timeout, *to = NULL;
|
struct hrtimer_sleeper timeout, *to = NULL;
|
||||||
|
struct futex_pi_state *pi_state = NULL;
|
||||||
struct rt_mutex_waiter rt_waiter;
|
struct rt_mutex_waiter rt_waiter;
|
||||||
struct futex_hash_bucket *hb;
|
struct futex_hash_bucket *hb;
|
||||||
union futex_key key2 = FUTEX_KEY_INIT;
|
union futex_key key2 = FUTEX_KEY_INIT;
|
||||||
@ -2840,10 +3003,7 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
|
|||||||
* The waiter is allocated on our stack, manipulated by the requeue
|
* The waiter is allocated on our stack, manipulated by the requeue
|
||||||
* code while we sleep on uaddr.
|
* code while we sleep on uaddr.
|
||||||
*/
|
*/
|
||||||
debug_rt_mutex_init_waiter(&rt_waiter);
|
rt_mutex_init_waiter(&rt_waiter);
|
||||||
RB_CLEAR_NODE(&rt_waiter.pi_tree_entry);
|
|
||||||
RB_CLEAR_NODE(&rt_waiter.tree_entry);
|
|
||||||
rt_waiter.task = NULL;
|
|
||||||
|
|
||||||
ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, VERIFY_WRITE);
|
ret = get_futex_key(uaddr2, flags & FLAGS_SHARED, &key2, VERIFY_WRITE);
|
||||||
if (unlikely(ret != 0))
|
if (unlikely(ret != 0))
|
||||||
@ -2898,8 +3058,10 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
|
|||||||
if (q.pi_state && (q.pi_state->owner != current)) {
|
if (q.pi_state && (q.pi_state->owner != current)) {
|
||||||
spin_lock(q.lock_ptr);
|
spin_lock(q.lock_ptr);
|
||||||
ret = fixup_pi_state_owner(uaddr2, &q, current);
|
ret = fixup_pi_state_owner(uaddr2, &q, current);
|
||||||
if (ret && rt_mutex_owner(&q.pi_state->pi_mutex) == current)
|
if (ret && rt_mutex_owner(&q.pi_state->pi_mutex) == current) {
|
||||||
rt_mutex_unlock(&q.pi_state->pi_mutex);
|
pi_state = q.pi_state;
|
||||||
|
get_pi_state(pi_state);
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* Drop the reference to the pi state which
|
* Drop the reference to the pi state which
|
||||||
* the requeue_pi() code acquired for us.
|
* the requeue_pi() code acquired for us.
|
||||||
@ -2917,10 +3079,13 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
|
|||||||
*/
|
*/
|
||||||
WARN_ON(!q.pi_state);
|
WARN_ON(!q.pi_state);
|
||||||
pi_mutex = &q.pi_state->pi_mutex;
|
pi_mutex = &q.pi_state->pi_mutex;
|
||||||
ret = rt_mutex_finish_proxy_lock(pi_mutex, to, &rt_waiter);
|
ret = rt_mutex_wait_proxy_lock(pi_mutex, to, &rt_waiter);
|
||||||
debug_rt_mutex_free_waiter(&rt_waiter);
|
|
||||||
|
|
||||||
spin_lock(q.lock_ptr);
|
spin_lock(q.lock_ptr);
|
||||||
|
if (ret && !rt_mutex_cleanup_proxy_lock(pi_mutex, &rt_waiter))
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
debug_rt_mutex_free_waiter(&rt_waiter);
|
||||||
/*
|
/*
|
||||||
* Fixup the pi_state owner and possibly acquire the lock if we
|
* Fixup the pi_state owner and possibly acquire the lock if we
|
||||||
* haven't already.
|
* haven't already.
|
||||||
@ -2938,13 +3103,20 @@ static int futex_wait_requeue_pi(u32 __user *uaddr, unsigned int flags,
|
|||||||
* the fault, unlock the rt_mutex and return the fault to
|
* the fault, unlock the rt_mutex and return the fault to
|
||||||
* userspace.
|
* userspace.
|
||||||
*/
|
*/
|
||||||
if (ret && rt_mutex_owner(pi_mutex) == current)
|
if (ret && rt_mutex_owner(&q.pi_state->pi_mutex) == current) {
|
||||||
rt_mutex_unlock(pi_mutex);
|
pi_state = q.pi_state;
|
||||||
|
get_pi_state(pi_state);
|
||||||
|
}
|
||||||
|
|
||||||
/* Unqueue and drop the lock. */
|
/* Unqueue and drop the lock. */
|
||||||
unqueue_me_pi(&q);
|
unqueue_me_pi(&q);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pi_state) {
|
||||||
|
rt_mutex_futex_unlock(&pi_state->pi_mutex);
|
||||||
|
put_pi_state(pi_state);
|
||||||
|
}
|
||||||
|
|
||||||
if (ret == -EINTR) {
|
if (ret == -EINTR) {
|
||||||
/*
|
/*
|
||||||
* We've already been requeued, but cannot restart by calling
|
* We've already been requeued, but cannot restart by calling
|
||||||
|
@ -660,6 +660,7 @@ look_up_lock_class(struct lockdep_map *lock, unsigned int subclass)
|
|||||||
struct lockdep_subclass_key *key;
|
struct lockdep_subclass_key *key;
|
||||||
struct hlist_head *hash_head;
|
struct hlist_head *hash_head;
|
||||||
struct lock_class *class;
|
struct lock_class *class;
|
||||||
|
bool is_static = false;
|
||||||
|
|
||||||
if (unlikely(subclass >= MAX_LOCKDEP_SUBCLASSES)) {
|
if (unlikely(subclass >= MAX_LOCKDEP_SUBCLASSES)) {
|
||||||
debug_locks_off();
|
debug_locks_off();
|
||||||
@ -673,10 +674,23 @@ look_up_lock_class(struct lockdep_map *lock, unsigned int subclass)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Static locks do not have their class-keys yet - for them the key
|
* Static locks do not have their class-keys yet - for them the key
|
||||||
* is the lock object itself:
|
* is the lock object itself. If the lock is in the per cpu area,
|
||||||
|
* the canonical address of the lock (per cpu offset removed) is
|
||||||
|
* used.
|
||||||
*/
|
*/
|
||||||
if (unlikely(!lock->key))
|
if (unlikely(!lock->key)) {
|
||||||
|
unsigned long can_addr, addr = (unsigned long)lock;
|
||||||
|
|
||||||
|
if (__is_kernel_percpu_address(addr, &can_addr))
|
||||||
|
lock->key = (void *)can_addr;
|
||||||
|
else if (__is_module_percpu_address(addr, &can_addr))
|
||||||
|
lock->key = (void *)can_addr;
|
||||||
|
else if (static_obj(lock))
|
||||||
lock->key = (void *)lock;
|
lock->key = (void *)lock;
|
||||||
|
else
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
is_static = true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTE: the class-key must be unique. For dynamic locks, a static
|
* NOTE: the class-key must be unique. For dynamic locks, a static
|
||||||
@ -708,7 +722,7 @@ look_up_lock_class(struct lockdep_map *lock, unsigned int subclass)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return is_static || static_obj(lock->key) ? NULL : ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -726,19 +740,18 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force)
|
|||||||
DEBUG_LOCKS_WARN_ON(!irqs_disabled());
|
DEBUG_LOCKS_WARN_ON(!irqs_disabled());
|
||||||
|
|
||||||
class = look_up_lock_class(lock, subclass);
|
class = look_up_lock_class(lock, subclass);
|
||||||
if (likely(class))
|
if (likely(!IS_ERR_OR_NULL(class)))
|
||||||
goto out_set_class_cache;
|
goto out_set_class_cache;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Debug-check: all keys must be persistent!
|
* Debug-check: all keys must be persistent!
|
||||||
*/
|
*/
|
||||||
if (!static_obj(lock->key)) {
|
if (IS_ERR(class)) {
|
||||||
debug_locks_off();
|
debug_locks_off();
|
||||||
printk("INFO: trying to register non-static key.\n");
|
printk("INFO: trying to register non-static key.\n");
|
||||||
printk("the code is fine but needs lockdep annotation.\n");
|
printk("the code is fine but needs lockdep annotation.\n");
|
||||||
printk("turning off the locking correctness validator.\n");
|
printk("turning off the locking correctness validator.\n");
|
||||||
dump_stack();
|
dump_stack();
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3419,7 +3432,7 @@ static int match_held_lock(struct held_lock *hlock, struct lockdep_map *lock)
|
|||||||
* Clearly if the lock hasn't been acquired _ever_, we're not
|
* Clearly if the lock hasn't been acquired _ever_, we're not
|
||||||
* holding it either, so report failure.
|
* holding it either, so report failure.
|
||||||
*/
|
*/
|
||||||
if (!class)
|
if (IS_ERR_OR_NULL(class))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3437,13 +3450,67 @@ static int match_held_lock(struct held_lock *hlock, struct lockdep_map *lock)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* @depth must not be zero */
|
||||||
|
static struct held_lock *find_held_lock(struct task_struct *curr,
|
||||||
|
struct lockdep_map *lock,
|
||||||
|
unsigned int depth, int *idx)
|
||||||
|
{
|
||||||
|
struct held_lock *ret, *hlock, *prev_hlock;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
i = depth - 1;
|
||||||
|
hlock = curr->held_locks + i;
|
||||||
|
ret = hlock;
|
||||||
|
if (match_held_lock(hlock, lock))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = NULL;
|
||||||
|
for (i--, prev_hlock = hlock--;
|
||||||
|
i >= 0;
|
||||||
|
i--, prev_hlock = hlock--) {
|
||||||
|
/*
|
||||||
|
* We must not cross into another context:
|
||||||
|
*/
|
||||||
|
if (prev_hlock->irq_context != hlock->irq_context) {
|
||||||
|
ret = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (match_held_lock(hlock, lock)) {
|
||||||
|
ret = hlock;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
*idx = i;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reacquire_held_locks(struct task_struct *curr, unsigned int depth,
|
||||||
|
int idx)
|
||||||
|
{
|
||||||
|
struct held_lock *hlock;
|
||||||
|
|
||||||
|
for (hlock = curr->held_locks + idx; idx < depth; idx++, hlock++) {
|
||||||
|
if (!__lock_acquire(hlock->instance,
|
||||||
|
hlock_class(hlock)->subclass,
|
||||||
|
hlock->trylock,
|
||||||
|
hlock->read, hlock->check,
|
||||||
|
hlock->hardirqs_off,
|
||||||
|
hlock->nest_lock, hlock->acquire_ip,
|
||||||
|
hlock->references, hlock->pin_count))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
__lock_set_class(struct lockdep_map *lock, const char *name,
|
__lock_set_class(struct lockdep_map *lock, const char *name,
|
||||||
struct lock_class_key *key, unsigned int subclass,
|
struct lock_class_key *key, unsigned int subclass,
|
||||||
unsigned long ip)
|
unsigned long ip)
|
||||||
{
|
{
|
||||||
struct task_struct *curr = current;
|
struct task_struct *curr = current;
|
||||||
struct held_lock *hlock, *prev_hlock;
|
struct held_lock *hlock;
|
||||||
struct lock_class *class;
|
struct lock_class *class;
|
||||||
unsigned int depth;
|
unsigned int depth;
|
||||||
int i;
|
int i;
|
||||||
@ -3456,21 +3523,10 @@ __lock_set_class(struct lockdep_map *lock, const char *name,
|
|||||||
if (DEBUG_LOCKS_WARN_ON(!depth))
|
if (DEBUG_LOCKS_WARN_ON(!depth))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
prev_hlock = NULL;
|
hlock = find_held_lock(curr, lock, depth, &i);
|
||||||
for (i = depth-1; i >= 0; i--) {
|
if (!hlock)
|
||||||
hlock = curr->held_locks + i;
|
|
||||||
/*
|
|
||||||
* We must not cross into another context:
|
|
||||||
*/
|
|
||||||
if (prev_hlock && prev_hlock->irq_context != hlock->irq_context)
|
|
||||||
break;
|
|
||||||
if (match_held_lock(hlock, lock))
|
|
||||||
goto found_it;
|
|
||||||
prev_hlock = hlock;
|
|
||||||
}
|
|
||||||
return print_unlock_imbalance_bug(curr, lock, ip);
|
return print_unlock_imbalance_bug(curr, lock, ip);
|
||||||
|
|
||||||
found_it:
|
|
||||||
lockdep_init_map(lock, name, key, 0);
|
lockdep_init_map(lock, name, key, 0);
|
||||||
class = register_lock_class(lock, subclass, 0);
|
class = register_lock_class(lock, subclass, 0);
|
||||||
hlock->class_idx = class - lock_classes + 1;
|
hlock->class_idx = class - lock_classes + 1;
|
||||||
@ -3478,15 +3534,46 @@ found_it:
|
|||||||
curr->lockdep_depth = i;
|
curr->lockdep_depth = i;
|
||||||
curr->curr_chain_key = hlock->prev_chain_key;
|
curr->curr_chain_key = hlock->prev_chain_key;
|
||||||
|
|
||||||
for (; i < depth; i++) {
|
if (reacquire_held_locks(curr, depth, i))
|
||||||
hlock = curr->held_locks + i;
|
return 0;
|
||||||
if (!__lock_acquire(hlock->instance,
|
|
||||||
hlock_class(hlock)->subclass, hlock->trylock,
|
/*
|
||||||
hlock->read, hlock->check, hlock->hardirqs_off,
|
* I took it apart and put it back together again, except now I have
|
||||||
hlock->nest_lock, hlock->acquire_ip,
|
* these 'spare' parts.. where shall I put them.
|
||||||
hlock->references, hlock->pin_count))
|
*/
|
||||||
|
if (DEBUG_LOCKS_WARN_ON(curr->lockdep_depth != depth))
|
||||||
|
return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __lock_downgrade(struct lockdep_map *lock, unsigned long ip)
|
||||||
|
{
|
||||||
|
struct task_struct *curr = current;
|
||||||
|
struct held_lock *hlock;
|
||||||
|
unsigned int depth;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
depth = curr->lockdep_depth;
|
||||||
|
/*
|
||||||
|
* This function is about (re)setting the class of a held lock,
|
||||||
|
* yet we're not actually holding any locks. Naughty user!
|
||||||
|
*/
|
||||||
|
if (DEBUG_LOCKS_WARN_ON(!depth))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
hlock = find_held_lock(curr, lock, depth, &i);
|
||||||
|
if (!hlock)
|
||||||
|
return print_unlock_imbalance_bug(curr, lock, ip);
|
||||||
|
|
||||||
|
curr->lockdep_depth = i;
|
||||||
|
curr->curr_chain_key = hlock->prev_chain_key;
|
||||||
|
|
||||||
|
WARN(hlock->read, "downgrading a read lock");
|
||||||
|
hlock->read = 1;
|
||||||
|
hlock->acquire_ip = ip;
|
||||||
|
|
||||||
|
if (reacquire_held_locks(curr, depth, i))
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* I took it apart and put it back together again, except now I have
|
* I took it apart and put it back together again, except now I have
|
||||||
@ -3508,7 +3595,7 @@ static int
|
|||||||
__lock_release(struct lockdep_map *lock, int nested, unsigned long ip)
|
__lock_release(struct lockdep_map *lock, int nested, unsigned long ip)
|
||||||
{
|
{
|
||||||
struct task_struct *curr = current;
|
struct task_struct *curr = current;
|
||||||
struct held_lock *hlock, *prev_hlock;
|
struct held_lock *hlock;
|
||||||
unsigned int depth;
|
unsigned int depth;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -3527,21 +3614,10 @@ __lock_release(struct lockdep_map *lock, int nested, unsigned long ip)
|
|||||||
* Check whether the lock exists in the current stack
|
* Check whether the lock exists in the current stack
|
||||||
* of held locks:
|
* of held locks:
|
||||||
*/
|
*/
|
||||||
prev_hlock = NULL;
|
hlock = find_held_lock(curr, lock, depth, &i);
|
||||||
for (i = depth-1; i >= 0; i--) {
|
if (!hlock)
|
||||||
hlock = curr->held_locks + i;
|
|
||||||
/*
|
|
||||||
* We must not cross into another context:
|
|
||||||
*/
|
|
||||||
if (prev_hlock && prev_hlock->irq_context != hlock->irq_context)
|
|
||||||
break;
|
|
||||||
if (match_held_lock(hlock, lock))
|
|
||||||
goto found_it;
|
|
||||||
prev_hlock = hlock;
|
|
||||||
}
|
|
||||||
return print_unlock_imbalance_bug(curr, lock, ip);
|
return print_unlock_imbalance_bug(curr, lock, ip);
|
||||||
|
|
||||||
found_it:
|
|
||||||
if (hlock->instance == lock)
|
if (hlock->instance == lock)
|
||||||
lock_release_holdtime(hlock);
|
lock_release_holdtime(hlock);
|
||||||
|
|
||||||
@ -3568,15 +3644,8 @@ found_it:
|
|||||||
curr->lockdep_depth = i;
|
curr->lockdep_depth = i;
|
||||||
curr->curr_chain_key = hlock->prev_chain_key;
|
curr->curr_chain_key = hlock->prev_chain_key;
|
||||||
|
|
||||||
for (i++; i < depth; i++) {
|
if (reacquire_held_locks(curr, depth, i + 1))
|
||||||
hlock = curr->held_locks + i;
|
|
||||||
if (!__lock_acquire(hlock->instance,
|
|
||||||
hlock_class(hlock)->subclass, hlock->trylock,
|
|
||||||
hlock->read, hlock->check, hlock->hardirqs_off,
|
|
||||||
hlock->nest_lock, hlock->acquire_ip,
|
|
||||||
hlock->references, hlock->pin_count))
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We had N bottles of beer on the wall, we drank one, but now
|
* We had N bottles of beer on the wall, we drank one, but now
|
||||||
@ -3741,6 +3810,23 @@ void lock_set_class(struct lockdep_map *lock, const char *name,
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(lock_set_class);
|
EXPORT_SYMBOL_GPL(lock_set_class);
|
||||||
|
|
||||||
|
void lock_downgrade(struct lockdep_map *lock, unsigned long ip)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
if (unlikely(current->lockdep_recursion))
|
||||||
|
return;
|
||||||
|
|
||||||
|
raw_local_irq_save(flags);
|
||||||
|
current->lockdep_recursion = 1;
|
||||||
|
check_flags(flags);
|
||||||
|
if (__lock_downgrade(lock, ip))
|
||||||
|
check_chain_key(current);
|
||||||
|
current->lockdep_recursion = 0;
|
||||||
|
raw_local_irq_restore(flags);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(lock_downgrade);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We are not always called with irqs disabled - do that here,
|
* We are not always called with irqs disabled - do that here,
|
||||||
* and also avoid lockdep recursion:
|
* and also avoid lockdep recursion:
|
||||||
@ -3903,7 +3989,7 @@ static void
|
|||||||
__lock_contended(struct lockdep_map *lock, unsigned long ip)
|
__lock_contended(struct lockdep_map *lock, unsigned long ip)
|
||||||
{
|
{
|
||||||
struct task_struct *curr = current;
|
struct task_struct *curr = current;
|
||||||
struct held_lock *hlock, *prev_hlock;
|
struct held_lock *hlock;
|
||||||
struct lock_class_stats *stats;
|
struct lock_class_stats *stats;
|
||||||
unsigned int depth;
|
unsigned int depth;
|
||||||
int i, contention_point, contending_point;
|
int i, contention_point, contending_point;
|
||||||
@ -3916,22 +4002,12 @@ __lock_contended(struct lockdep_map *lock, unsigned long ip)
|
|||||||
if (DEBUG_LOCKS_WARN_ON(!depth))
|
if (DEBUG_LOCKS_WARN_ON(!depth))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
prev_hlock = NULL;
|
hlock = find_held_lock(curr, lock, depth, &i);
|
||||||
for (i = depth-1; i >= 0; i--) {
|
if (!hlock) {
|
||||||
hlock = curr->held_locks + i;
|
|
||||||
/*
|
|
||||||
* We must not cross into another context:
|
|
||||||
*/
|
|
||||||
if (prev_hlock && prev_hlock->irq_context != hlock->irq_context)
|
|
||||||
break;
|
|
||||||
if (match_held_lock(hlock, lock))
|
|
||||||
goto found_it;
|
|
||||||
prev_hlock = hlock;
|
|
||||||
}
|
|
||||||
print_lock_contention_bug(curr, lock, ip);
|
print_lock_contention_bug(curr, lock, ip);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
found_it:
|
|
||||||
if (hlock->instance != lock)
|
if (hlock->instance != lock)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -3955,7 +4031,7 @@ static void
|
|||||||
__lock_acquired(struct lockdep_map *lock, unsigned long ip)
|
__lock_acquired(struct lockdep_map *lock, unsigned long ip)
|
||||||
{
|
{
|
||||||
struct task_struct *curr = current;
|
struct task_struct *curr = current;
|
||||||
struct held_lock *hlock, *prev_hlock;
|
struct held_lock *hlock;
|
||||||
struct lock_class_stats *stats;
|
struct lock_class_stats *stats;
|
||||||
unsigned int depth;
|
unsigned int depth;
|
||||||
u64 now, waittime = 0;
|
u64 now, waittime = 0;
|
||||||
@ -3969,22 +4045,12 @@ __lock_acquired(struct lockdep_map *lock, unsigned long ip)
|
|||||||
if (DEBUG_LOCKS_WARN_ON(!depth))
|
if (DEBUG_LOCKS_WARN_ON(!depth))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
prev_hlock = NULL;
|
hlock = find_held_lock(curr, lock, depth, &i);
|
||||||
for (i = depth-1; i >= 0; i--) {
|
if (!hlock) {
|
||||||
hlock = curr->held_locks + i;
|
|
||||||
/*
|
|
||||||
* We must not cross into another context:
|
|
||||||
*/
|
|
||||||
if (prev_hlock && prev_hlock->irq_context != hlock->irq_context)
|
|
||||||
break;
|
|
||||||
if (match_held_lock(hlock, lock))
|
|
||||||
goto found_it;
|
|
||||||
prev_hlock = hlock;
|
|
||||||
}
|
|
||||||
print_lock_contention_bug(curr, lock, _RET_IP_);
|
print_lock_contention_bug(curr, lock, _RET_IP_);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
found_it:
|
|
||||||
if (hlock->instance != lock)
|
if (hlock->instance != lock)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -4172,7 +4238,7 @@ void lockdep_reset_lock(struct lockdep_map *lock)
|
|||||||
* If the class exists we look it up and zap it:
|
* If the class exists we look it up and zap it:
|
||||||
*/
|
*/
|
||||||
class = look_up_lock_class(lock, j);
|
class = look_up_lock_class(lock, j);
|
||||||
if (class)
|
if (!IS_ERR_OR_NULL(class))
|
||||||
zap_class(class);
|
zap_class(class);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -174,12 +174,3 @@ void debug_rt_mutex_init(struct rt_mutex *lock, const char *name)
|
|||||||
lock->name = name;
|
lock->name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
rt_mutex_deadlock_account_lock(struct rt_mutex *lock, struct task_struct *task)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void rt_mutex_deadlock_account_unlock(struct task_struct *task)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -9,9 +9,6 @@
|
|||||||
* This file contains macros used solely by rtmutex.c. Debug version.
|
* This file contains macros used solely by rtmutex.c. Debug version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern void
|
|
||||||
rt_mutex_deadlock_account_lock(struct rt_mutex *lock, struct task_struct *task);
|
|
||||||
extern void rt_mutex_deadlock_account_unlock(struct task_struct *task);
|
|
||||||
extern void debug_rt_mutex_init_waiter(struct rt_mutex_waiter *waiter);
|
extern void debug_rt_mutex_init_waiter(struct rt_mutex_waiter *waiter);
|
||||||
extern void debug_rt_mutex_free_waiter(struct rt_mutex_waiter *waiter);
|
extern void debug_rt_mutex_free_waiter(struct rt_mutex_waiter *waiter);
|
||||||
extern void debug_rt_mutex_init(struct rt_mutex *lock, const char *name);
|
extern void debug_rt_mutex_init(struct rt_mutex *lock, const char *name);
|
||||||
|
@ -224,6 +224,12 @@ static inline bool unlock_rt_mutex_safe(struct rt_mutex *lock,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only use with rt_mutex_waiter_{less,equal}()
|
||||||
|
*/
|
||||||
|
#define task_to_waiter(p) \
|
||||||
|
&(struct rt_mutex_waiter){ .prio = (p)->prio, .deadline = (p)->dl.deadline }
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
rt_mutex_waiter_less(struct rt_mutex_waiter *left,
|
rt_mutex_waiter_less(struct rt_mutex_waiter *left,
|
||||||
struct rt_mutex_waiter *right)
|
struct rt_mutex_waiter *right)
|
||||||
@ -238,12 +244,30 @@ rt_mutex_waiter_less(struct rt_mutex_waiter *left,
|
|||||||
* then right waiter has a dl_prio() too.
|
* then right waiter has a dl_prio() too.
|
||||||
*/
|
*/
|
||||||
if (dl_prio(left->prio))
|
if (dl_prio(left->prio))
|
||||||
return dl_time_before(left->task->dl.deadline,
|
return dl_time_before(left->deadline, right->deadline);
|
||||||
right->task->dl.deadline);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
rt_mutex_waiter_equal(struct rt_mutex_waiter *left,
|
||||||
|
struct rt_mutex_waiter *right)
|
||||||
|
{
|
||||||
|
if (left->prio != right->prio)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If both waiters have dl_prio(), we check the deadlines of the
|
||||||
|
* associated tasks.
|
||||||
|
* If left waiter has a dl_prio(), and we didn't return 0 above,
|
||||||
|
* then right waiter has a dl_prio() too.
|
||||||
|
*/
|
||||||
|
if (dl_prio(left->prio))
|
||||||
|
return left->deadline == right->deadline;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
rt_mutex_enqueue(struct rt_mutex *lock, struct rt_mutex_waiter *waiter)
|
rt_mutex_enqueue(struct rt_mutex *lock, struct rt_mutex_waiter *waiter)
|
||||||
{
|
{
|
||||||
@ -322,72 +346,16 @@ rt_mutex_dequeue_pi(struct task_struct *task, struct rt_mutex_waiter *waiter)
|
|||||||
RB_CLEAR_NODE(&waiter->pi_tree_entry);
|
RB_CLEAR_NODE(&waiter->pi_tree_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void rt_mutex_adjust_prio(struct task_struct *p)
|
||||||
* Calculate task priority from the waiter tree priority
|
|
||||||
*
|
|
||||||
* Return task->normal_prio when the waiter tree is empty or when
|
|
||||||
* the waiter is not allowed to do priority boosting
|
|
||||||
*/
|
|
||||||
int rt_mutex_getprio(struct task_struct *task)
|
|
||||||
{
|
{
|
||||||
if (likely(!task_has_pi_waiters(task)))
|
struct task_struct *pi_task = NULL;
|
||||||
return task->normal_prio;
|
|
||||||
|
|
||||||
return min(task_top_pi_waiter(task)->prio,
|
lockdep_assert_held(&p->pi_lock);
|
||||||
task->normal_prio);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct task_struct *rt_mutex_get_top_task(struct task_struct *task)
|
if (task_has_pi_waiters(p))
|
||||||
{
|
pi_task = task_top_pi_waiter(p)->task;
|
||||||
if (likely(!task_has_pi_waiters(task)))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return task_top_pi_waiter(task)->task;
|
rt_mutex_setprio(p, pi_task);
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Called by sched_setscheduler() to get the priority which will be
|
|
||||||
* effective after the change.
|
|
||||||
*/
|
|
||||||
int rt_mutex_get_effective_prio(struct task_struct *task, int newprio)
|
|
||||||
{
|
|
||||||
if (!task_has_pi_waiters(task))
|
|
||||||
return newprio;
|
|
||||||
|
|
||||||
if (task_top_pi_waiter(task)->task->prio <= newprio)
|
|
||||||
return task_top_pi_waiter(task)->task->prio;
|
|
||||||
return newprio;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Adjust the priority of a task, after its pi_waiters got modified.
|
|
||||||
*
|
|
||||||
* This can be both boosting and unboosting. task->pi_lock must be held.
|
|
||||||
*/
|
|
||||||
static void __rt_mutex_adjust_prio(struct task_struct *task)
|
|
||||||
{
|
|
||||||
int prio = rt_mutex_getprio(task);
|
|
||||||
|
|
||||||
if (task->prio != prio || dl_prio(prio))
|
|
||||||
rt_mutex_setprio(task, prio);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Adjust task priority (undo boosting). Called from the exit path of
|
|
||||||
* rt_mutex_slowunlock() and rt_mutex_slowlock().
|
|
||||||
*
|
|
||||||
* (Note: We do this outside of the protection of lock->wait_lock to
|
|
||||||
* allow the lock to be taken while or before we readjust the priority
|
|
||||||
* of task. We do not use the spin_xx_mutex() variants here as we are
|
|
||||||
* outside of the debug path.)
|
|
||||||
*/
|
|
||||||
void rt_mutex_adjust_prio(struct task_struct *task)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&task->pi_lock, flags);
|
|
||||||
__rt_mutex_adjust_prio(task);
|
|
||||||
raw_spin_unlock_irqrestore(&task->pi_lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -610,7 +578,7 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,
|
|||||||
* enabled we continue, but stop the requeueing in the chain
|
* enabled we continue, but stop the requeueing in the chain
|
||||||
* walk.
|
* walk.
|
||||||
*/
|
*/
|
||||||
if (waiter->prio == task->prio) {
|
if (rt_mutex_waiter_equal(waiter, task_to_waiter(task))) {
|
||||||
if (!detect_deadlock)
|
if (!detect_deadlock)
|
||||||
goto out_unlock_pi;
|
goto out_unlock_pi;
|
||||||
else
|
else
|
||||||
@ -706,7 +674,26 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,
|
|||||||
|
|
||||||
/* [7] Requeue the waiter in the lock waiter tree. */
|
/* [7] Requeue the waiter in the lock waiter tree. */
|
||||||
rt_mutex_dequeue(lock, waiter);
|
rt_mutex_dequeue(lock, waiter);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the waiter prio fields now that we're dequeued.
|
||||||
|
*
|
||||||
|
* These values can have changed through either:
|
||||||
|
*
|
||||||
|
* sys_sched_set_scheduler() / sys_sched_setattr()
|
||||||
|
*
|
||||||
|
* or
|
||||||
|
*
|
||||||
|
* DL CBS enforcement advancing the effective deadline.
|
||||||
|
*
|
||||||
|
* Even though pi_waiters also uses these fields, and that tree is only
|
||||||
|
* updated in [11], we can do this here, since we hold [L], which
|
||||||
|
* serializes all pi_waiters access and rb_erase() does not care about
|
||||||
|
* the values of the node being removed.
|
||||||
|
*/
|
||||||
waiter->prio = task->prio;
|
waiter->prio = task->prio;
|
||||||
|
waiter->deadline = task->dl.deadline;
|
||||||
|
|
||||||
rt_mutex_enqueue(lock, waiter);
|
rt_mutex_enqueue(lock, waiter);
|
||||||
|
|
||||||
/* [8] Release the task */
|
/* [8] Release the task */
|
||||||
@ -747,7 +734,7 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,
|
|||||||
*/
|
*/
|
||||||
rt_mutex_dequeue_pi(task, prerequeue_top_waiter);
|
rt_mutex_dequeue_pi(task, prerequeue_top_waiter);
|
||||||
rt_mutex_enqueue_pi(task, waiter);
|
rt_mutex_enqueue_pi(task, waiter);
|
||||||
__rt_mutex_adjust_prio(task);
|
rt_mutex_adjust_prio(task);
|
||||||
|
|
||||||
} else if (prerequeue_top_waiter == waiter) {
|
} else if (prerequeue_top_waiter == waiter) {
|
||||||
/*
|
/*
|
||||||
@ -763,7 +750,7 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,
|
|||||||
rt_mutex_dequeue_pi(task, waiter);
|
rt_mutex_dequeue_pi(task, waiter);
|
||||||
waiter = rt_mutex_top_waiter(lock);
|
waiter = rt_mutex_top_waiter(lock);
|
||||||
rt_mutex_enqueue_pi(task, waiter);
|
rt_mutex_enqueue_pi(task, waiter);
|
||||||
__rt_mutex_adjust_prio(task);
|
rt_mutex_adjust_prio(task);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* Nothing changed. No need to do any priority
|
* Nothing changed. No need to do any priority
|
||||||
@ -833,6 +820,8 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,
|
|||||||
static int try_to_take_rt_mutex(struct rt_mutex *lock, struct task_struct *task,
|
static int try_to_take_rt_mutex(struct rt_mutex *lock, struct task_struct *task,
|
||||||
struct rt_mutex_waiter *waiter)
|
struct rt_mutex_waiter *waiter)
|
||||||
{
|
{
|
||||||
|
lockdep_assert_held(&lock->wait_lock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Before testing whether we can acquire @lock, we set the
|
* Before testing whether we can acquire @lock, we set the
|
||||||
* RT_MUTEX_HAS_WAITERS bit in @lock->owner. This forces all
|
* RT_MUTEX_HAS_WAITERS bit in @lock->owner. This forces all
|
||||||
@ -892,7 +881,8 @@ static int try_to_take_rt_mutex(struct rt_mutex *lock, struct task_struct *task,
|
|||||||
* the top waiter priority (kernel view),
|
* the top waiter priority (kernel view),
|
||||||
* @task lost.
|
* @task lost.
|
||||||
*/
|
*/
|
||||||
if (task->prio >= rt_mutex_top_waiter(lock)->prio)
|
if (!rt_mutex_waiter_less(task_to_waiter(task),
|
||||||
|
rt_mutex_top_waiter(lock)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -938,8 +928,6 @@ takeit:
|
|||||||
*/
|
*/
|
||||||
rt_mutex_set_owner(lock, task);
|
rt_mutex_set_owner(lock, task);
|
||||||
|
|
||||||
rt_mutex_deadlock_account_lock(lock, task);
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -960,6 +948,8 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,
|
|||||||
struct rt_mutex *next_lock;
|
struct rt_mutex *next_lock;
|
||||||
int chain_walk = 0, res;
|
int chain_walk = 0, res;
|
||||||
|
|
||||||
|
lockdep_assert_held(&lock->wait_lock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Early deadlock detection. We really don't want the task to
|
* Early deadlock detection. We really don't want the task to
|
||||||
* enqueue on itself just to untangle the mess later. It's not
|
* enqueue on itself just to untangle the mess later. It's not
|
||||||
@ -973,10 +963,11 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,
|
|||||||
return -EDEADLK;
|
return -EDEADLK;
|
||||||
|
|
||||||
raw_spin_lock(&task->pi_lock);
|
raw_spin_lock(&task->pi_lock);
|
||||||
__rt_mutex_adjust_prio(task);
|
rt_mutex_adjust_prio(task);
|
||||||
waiter->task = task;
|
waiter->task = task;
|
||||||
waiter->lock = lock;
|
waiter->lock = lock;
|
||||||
waiter->prio = task->prio;
|
waiter->prio = task->prio;
|
||||||
|
waiter->deadline = task->dl.deadline;
|
||||||
|
|
||||||
/* Get the top priority waiter on the lock */
|
/* Get the top priority waiter on the lock */
|
||||||
if (rt_mutex_has_waiters(lock))
|
if (rt_mutex_has_waiters(lock))
|
||||||
@ -995,7 +986,7 @@ static int task_blocks_on_rt_mutex(struct rt_mutex *lock,
|
|||||||
rt_mutex_dequeue_pi(owner, top_waiter);
|
rt_mutex_dequeue_pi(owner, top_waiter);
|
||||||
rt_mutex_enqueue_pi(owner, waiter);
|
rt_mutex_enqueue_pi(owner, waiter);
|
||||||
|
|
||||||
__rt_mutex_adjust_prio(owner);
|
rt_mutex_adjust_prio(owner);
|
||||||
if (owner->pi_blocked_on)
|
if (owner->pi_blocked_on)
|
||||||
chain_walk = 1;
|
chain_walk = 1;
|
||||||
} else if (rt_mutex_cond_detect_deadlock(waiter, chwalk)) {
|
} else if (rt_mutex_cond_detect_deadlock(waiter, chwalk)) {
|
||||||
@ -1047,12 +1038,14 @@ static void mark_wakeup_next_waiter(struct wake_q_head *wake_q,
|
|||||||
waiter = rt_mutex_top_waiter(lock);
|
waiter = rt_mutex_top_waiter(lock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove it from current->pi_waiters. We do not adjust a
|
* Remove it from current->pi_waiters and deboost.
|
||||||
* possible priority boost right now. We execute wakeup in the
|
*
|
||||||
* boosted mode and go back to normal after releasing
|
* We must in fact deboost here in order to ensure we call
|
||||||
* lock->wait_lock.
|
* rt_mutex_setprio() to update p->pi_top_task before the
|
||||||
|
* task unblocks.
|
||||||
*/
|
*/
|
||||||
rt_mutex_dequeue_pi(current, waiter);
|
rt_mutex_dequeue_pi(current, waiter);
|
||||||
|
rt_mutex_adjust_prio(current);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* As we are waking up the top waiter, and the waiter stays
|
* As we are waking up the top waiter, and the waiter stays
|
||||||
@ -1064,9 +1057,19 @@ static void mark_wakeup_next_waiter(struct wake_q_head *wake_q,
|
|||||||
*/
|
*/
|
||||||
lock->owner = (void *) RT_MUTEX_HAS_WAITERS;
|
lock->owner = (void *) RT_MUTEX_HAS_WAITERS;
|
||||||
|
|
||||||
raw_spin_unlock(¤t->pi_lock);
|
/*
|
||||||
|
* We deboosted before waking the top waiter task such that we don't
|
||||||
|
* run two tasks with the 'same' priority (and ensure the
|
||||||
|
* p->pi_top_task pointer points to a blocked task). This however can
|
||||||
|
* lead to priority inversion if we would get preempted after the
|
||||||
|
* deboost but before waking our donor task, hence the preempt_disable()
|
||||||
|
* before unlock.
|
||||||
|
*
|
||||||
|
* Pairs with preempt_enable() in rt_mutex_postunlock();
|
||||||
|
*/
|
||||||
|
preempt_disable();
|
||||||
wake_q_add(wake_q, waiter->task);
|
wake_q_add(wake_q, waiter->task);
|
||||||
|
raw_spin_unlock(¤t->pi_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1082,6 +1085,8 @@ static void remove_waiter(struct rt_mutex *lock,
|
|||||||
struct task_struct *owner = rt_mutex_owner(lock);
|
struct task_struct *owner = rt_mutex_owner(lock);
|
||||||
struct rt_mutex *next_lock;
|
struct rt_mutex *next_lock;
|
||||||
|
|
||||||
|
lockdep_assert_held(&lock->wait_lock);
|
||||||
|
|
||||||
raw_spin_lock(¤t->pi_lock);
|
raw_spin_lock(¤t->pi_lock);
|
||||||
rt_mutex_dequeue(lock, waiter);
|
rt_mutex_dequeue(lock, waiter);
|
||||||
current->pi_blocked_on = NULL;
|
current->pi_blocked_on = NULL;
|
||||||
@ -1101,7 +1106,7 @@ static void remove_waiter(struct rt_mutex *lock,
|
|||||||
if (rt_mutex_has_waiters(lock))
|
if (rt_mutex_has_waiters(lock))
|
||||||
rt_mutex_enqueue_pi(owner, rt_mutex_top_waiter(lock));
|
rt_mutex_enqueue_pi(owner, rt_mutex_top_waiter(lock));
|
||||||
|
|
||||||
__rt_mutex_adjust_prio(owner);
|
rt_mutex_adjust_prio(owner);
|
||||||
|
|
||||||
/* Store the lock on which owner is blocked or NULL */
|
/* Store the lock on which owner is blocked or NULL */
|
||||||
next_lock = task_blocked_on_lock(owner);
|
next_lock = task_blocked_on_lock(owner);
|
||||||
@ -1140,8 +1145,7 @@ void rt_mutex_adjust_pi(struct task_struct *task)
|
|||||||
raw_spin_lock_irqsave(&task->pi_lock, flags);
|
raw_spin_lock_irqsave(&task->pi_lock, flags);
|
||||||
|
|
||||||
waiter = task->pi_blocked_on;
|
waiter = task->pi_blocked_on;
|
||||||
if (!waiter || (waiter->prio == task->prio &&
|
if (!waiter || rt_mutex_waiter_equal(waiter, task_to_waiter(task))) {
|
||||||
!dl_prio(task->prio))) {
|
|
||||||
raw_spin_unlock_irqrestore(&task->pi_lock, flags);
|
raw_spin_unlock_irqrestore(&task->pi_lock, flags);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1155,6 +1159,14 @@ void rt_mutex_adjust_pi(struct task_struct *task)
|
|||||||
next_lock, NULL, task);
|
next_lock, NULL, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rt_mutex_init_waiter(struct rt_mutex_waiter *waiter)
|
||||||
|
{
|
||||||
|
debug_rt_mutex_init_waiter(waiter);
|
||||||
|
RB_CLEAR_NODE(&waiter->pi_tree_entry);
|
||||||
|
RB_CLEAR_NODE(&waiter->tree_entry);
|
||||||
|
waiter->task = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __rt_mutex_slowlock() - Perform the wait-wake-try-to-take loop
|
* __rt_mutex_slowlock() - Perform the wait-wake-try-to-take loop
|
||||||
* @lock: the rt_mutex to take
|
* @lock: the rt_mutex to take
|
||||||
@ -1237,9 +1249,7 @@ rt_mutex_slowlock(struct rt_mutex *lock, int state,
|
|||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
debug_rt_mutex_init_waiter(&waiter);
|
rt_mutex_init_waiter(&waiter);
|
||||||
RB_CLEAR_NODE(&waiter.pi_tree_entry);
|
|
||||||
RB_CLEAR_NODE(&waiter.tree_entry);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Technically we could use raw_spin_[un]lock_irq() here, but this can
|
* Technically we could use raw_spin_[un]lock_irq() here, but this can
|
||||||
@ -1330,7 +1340,8 @@ static inline int rt_mutex_slowtrylock(struct rt_mutex *lock)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Slow path to release a rt-mutex.
|
* Slow path to release a rt-mutex.
|
||||||
* Return whether the current task needs to undo a potential priority boosting.
|
*
|
||||||
|
* Return whether the current task needs to call rt_mutex_postunlock().
|
||||||
*/
|
*/
|
||||||
static bool __sched rt_mutex_slowunlock(struct rt_mutex *lock,
|
static bool __sched rt_mutex_slowunlock(struct rt_mutex *lock,
|
||||||
struct wake_q_head *wake_q)
|
struct wake_q_head *wake_q)
|
||||||
@ -1342,8 +1353,6 @@ static bool __sched rt_mutex_slowunlock(struct rt_mutex *lock,
|
|||||||
|
|
||||||
debug_rt_mutex_unlock(lock);
|
debug_rt_mutex_unlock(lock);
|
||||||
|
|
||||||
rt_mutex_deadlock_account_unlock(current);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We must be careful here if the fast path is enabled. If we
|
* We must be careful here if the fast path is enabled. If we
|
||||||
* have no waiters queued we cannot set owner to NULL here
|
* have no waiters queued we cannot set owner to NULL here
|
||||||
@ -1390,11 +1399,9 @@ static bool __sched rt_mutex_slowunlock(struct rt_mutex *lock,
|
|||||||
* Queue the next waiter for wakeup once we release the wait_lock.
|
* Queue the next waiter for wakeup once we release the wait_lock.
|
||||||
*/
|
*/
|
||||||
mark_wakeup_next_waiter(wake_q, lock);
|
mark_wakeup_next_waiter(wake_q, lock);
|
||||||
|
|
||||||
raw_spin_unlock_irqrestore(&lock->wait_lock, flags);
|
raw_spin_unlock_irqrestore(&lock->wait_lock, flags);
|
||||||
|
|
||||||
/* check PI boosting */
|
return true; /* call rt_mutex_postunlock() */
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1409,10 +1416,9 @@ rt_mutex_fastlock(struct rt_mutex *lock, int state,
|
|||||||
struct hrtimer_sleeper *timeout,
|
struct hrtimer_sleeper *timeout,
|
||||||
enum rtmutex_chainwalk chwalk))
|
enum rtmutex_chainwalk chwalk))
|
||||||
{
|
{
|
||||||
if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current))) {
|
if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current)))
|
||||||
rt_mutex_deadlock_account_lock(lock, current);
|
|
||||||
return 0;
|
return 0;
|
||||||
} else
|
|
||||||
return slowfn(lock, state, NULL, RT_MUTEX_MIN_CHAINWALK);
|
return slowfn(lock, state, NULL, RT_MUTEX_MIN_CHAINWALK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1425,10 +1431,9 @@ rt_mutex_timed_fastlock(struct rt_mutex *lock, int state,
|
|||||||
enum rtmutex_chainwalk chwalk))
|
enum rtmutex_chainwalk chwalk))
|
||||||
{
|
{
|
||||||
if (chwalk == RT_MUTEX_MIN_CHAINWALK &&
|
if (chwalk == RT_MUTEX_MIN_CHAINWALK &&
|
||||||
likely(rt_mutex_cmpxchg_acquire(lock, NULL, current))) {
|
likely(rt_mutex_cmpxchg_acquire(lock, NULL, current)))
|
||||||
rt_mutex_deadlock_account_lock(lock, current);
|
|
||||||
return 0;
|
return 0;
|
||||||
} else
|
|
||||||
return slowfn(lock, state, timeout, chwalk);
|
return slowfn(lock, state, timeout, chwalk);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1436,13 +1441,23 @@ static inline int
|
|||||||
rt_mutex_fasttrylock(struct rt_mutex *lock,
|
rt_mutex_fasttrylock(struct rt_mutex *lock,
|
||||||
int (*slowfn)(struct rt_mutex *lock))
|
int (*slowfn)(struct rt_mutex *lock))
|
||||||
{
|
{
|
||||||
if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current))) {
|
if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current)))
|
||||||
rt_mutex_deadlock_account_lock(lock, current);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
|
||||||
return slowfn(lock);
|
return slowfn(lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Performs the wakeup of the the top-waiter and re-enables preemption.
|
||||||
|
*/
|
||||||
|
void rt_mutex_postunlock(struct wake_q_head *wake_q)
|
||||||
|
{
|
||||||
|
wake_up_q(wake_q);
|
||||||
|
|
||||||
|
/* Pairs with preempt_disable() in rt_mutex_slowunlock() */
|
||||||
|
preempt_enable();
|
||||||
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
rt_mutex_fastunlock(struct rt_mutex *lock,
|
rt_mutex_fastunlock(struct rt_mutex *lock,
|
||||||
bool (*slowfn)(struct rt_mutex *lock,
|
bool (*slowfn)(struct rt_mutex *lock,
|
||||||
@ -1450,18 +1465,11 @@ rt_mutex_fastunlock(struct rt_mutex *lock,
|
|||||||
{
|
{
|
||||||
DEFINE_WAKE_Q(wake_q);
|
DEFINE_WAKE_Q(wake_q);
|
||||||
|
|
||||||
if (likely(rt_mutex_cmpxchg_release(lock, current, NULL))) {
|
if (likely(rt_mutex_cmpxchg_release(lock, current, NULL)))
|
||||||
rt_mutex_deadlock_account_unlock(current);
|
return;
|
||||||
|
|
||||||
} else {
|
if (slowfn(lock, &wake_q))
|
||||||
bool deboost = slowfn(lock, &wake_q);
|
rt_mutex_postunlock(&wake_q);
|
||||||
|
|
||||||
wake_up_q(&wake_q);
|
|
||||||
|
|
||||||
/* Undo pi boosting if necessary: */
|
|
||||||
if (deboost)
|
|
||||||
rt_mutex_adjust_prio(current);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1495,16 +1503,11 @@ int __sched rt_mutex_lock_interruptible(struct rt_mutex *lock)
|
|||||||
EXPORT_SYMBOL_GPL(rt_mutex_lock_interruptible);
|
EXPORT_SYMBOL_GPL(rt_mutex_lock_interruptible);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Futex variant with full deadlock detection.
|
* Futex variant, must not use fastpath.
|
||||||
*/
|
*/
|
||||||
int rt_mutex_timed_futex_lock(struct rt_mutex *lock,
|
int __sched rt_mutex_futex_trylock(struct rt_mutex *lock)
|
||||||
struct hrtimer_sleeper *timeout)
|
|
||||||
{
|
{
|
||||||
might_sleep();
|
return rt_mutex_slowtrylock(lock);
|
||||||
|
|
||||||
return rt_mutex_timed_fastlock(lock, TASK_INTERRUPTIBLE, timeout,
|
|
||||||
RT_MUTEX_FULL_CHAINWALK,
|
|
||||||
rt_mutex_slowlock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1563,20 +1566,43 @@ void __sched rt_mutex_unlock(struct rt_mutex *lock)
|
|||||||
EXPORT_SYMBOL_GPL(rt_mutex_unlock);
|
EXPORT_SYMBOL_GPL(rt_mutex_unlock);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* rt_mutex_futex_unlock - Futex variant of rt_mutex_unlock
|
* Futex variant, that since futex variants do not use the fast-path, can be
|
||||||
* @lock: the rt_mutex to be unlocked
|
* simple and will not need to retry.
|
||||||
*
|
|
||||||
* Returns: true/false indicating whether priority adjustment is
|
|
||||||
* required or not.
|
|
||||||
*/
|
*/
|
||||||
bool __sched rt_mutex_futex_unlock(struct rt_mutex *lock,
|
bool __sched __rt_mutex_futex_unlock(struct rt_mutex *lock,
|
||||||
struct wake_q_head *wqh)
|
struct wake_q_head *wake_q)
|
||||||
{
|
{
|
||||||
if (likely(rt_mutex_cmpxchg_release(lock, current, NULL))) {
|
lockdep_assert_held(&lock->wait_lock);
|
||||||
rt_mutex_deadlock_account_unlock(current);
|
|
||||||
return false;
|
debug_rt_mutex_unlock(lock);
|
||||||
|
|
||||||
|
if (!rt_mutex_has_waiters(lock)) {
|
||||||
|
lock->owner = NULL;
|
||||||
|
return false; /* done */
|
||||||
}
|
}
|
||||||
return rt_mutex_slowunlock(lock, wqh);
|
|
||||||
|
/*
|
||||||
|
* We've already deboosted, mark_wakeup_next_waiter() will
|
||||||
|
* retain preempt_disabled when we drop the wait_lock, to
|
||||||
|
* avoid inversion prior to the wakeup. preempt_disable()
|
||||||
|
* therein pairs with rt_mutex_postunlock().
|
||||||
|
*/
|
||||||
|
mark_wakeup_next_waiter(wake_q, lock);
|
||||||
|
|
||||||
|
return true; /* call postunlock() */
|
||||||
|
}
|
||||||
|
|
||||||
|
void __sched rt_mutex_futex_unlock(struct rt_mutex *lock)
|
||||||
|
{
|
||||||
|
DEFINE_WAKE_Q(wake_q);
|
||||||
|
bool postunlock;
|
||||||
|
|
||||||
|
raw_spin_lock_irq(&lock->wait_lock);
|
||||||
|
postunlock = __rt_mutex_futex_unlock(lock, &wake_q);
|
||||||
|
raw_spin_unlock_irq(&lock->wait_lock);
|
||||||
|
|
||||||
|
if (postunlock)
|
||||||
|
rt_mutex_postunlock(&wake_q);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1637,7 +1663,6 @@ void rt_mutex_init_proxy_locked(struct rt_mutex *lock,
|
|||||||
__rt_mutex_init(lock, NULL);
|
__rt_mutex_init(lock, NULL);
|
||||||
debug_rt_mutex_proxy_lock(lock, proxy_owner);
|
debug_rt_mutex_proxy_lock(lock, proxy_owner);
|
||||||
rt_mutex_set_owner(lock, proxy_owner);
|
rt_mutex_set_owner(lock, proxy_owner);
|
||||||
rt_mutex_deadlock_account_lock(lock, proxy_owner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1657,7 +1682,37 @@ void rt_mutex_proxy_unlock(struct rt_mutex *lock,
|
|||||||
{
|
{
|
||||||
debug_rt_mutex_proxy_unlock(lock);
|
debug_rt_mutex_proxy_unlock(lock);
|
||||||
rt_mutex_set_owner(lock, NULL);
|
rt_mutex_set_owner(lock, NULL);
|
||||||
rt_mutex_deadlock_account_unlock(proxy_owner);
|
}
|
||||||
|
|
||||||
|
int __rt_mutex_start_proxy_lock(struct rt_mutex *lock,
|
||||||
|
struct rt_mutex_waiter *waiter,
|
||||||
|
struct task_struct *task)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (try_to_take_rt_mutex(lock, task, NULL))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
/* We enforce deadlock detection for futexes */
|
||||||
|
ret = task_blocks_on_rt_mutex(lock, waiter, task,
|
||||||
|
RT_MUTEX_FULL_CHAINWALK);
|
||||||
|
|
||||||
|
if (ret && !rt_mutex_owner(lock)) {
|
||||||
|
/*
|
||||||
|
* Reset the return value. We might have
|
||||||
|
* returned with -EDEADLK and the owner
|
||||||
|
* released the lock while we were walking the
|
||||||
|
* pi chain. Let the waiter sort it out.
|
||||||
|
*/
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(ret))
|
||||||
|
remove_waiter(lock, waiter);
|
||||||
|
|
||||||
|
debug_rt_mutex_print_deadlock(waiter);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1680,32 +1735,8 @@ int rt_mutex_start_proxy_lock(struct rt_mutex *lock,
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
raw_spin_lock_irq(&lock->wait_lock);
|
raw_spin_lock_irq(&lock->wait_lock);
|
||||||
|
ret = __rt_mutex_start_proxy_lock(lock, waiter, task);
|
||||||
if (try_to_take_rt_mutex(lock, task, NULL)) {
|
|
||||||
raw_spin_unlock_irq(&lock->wait_lock);
|
raw_spin_unlock_irq(&lock->wait_lock);
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We enforce deadlock detection for futexes */
|
|
||||||
ret = task_blocks_on_rt_mutex(lock, waiter, task,
|
|
||||||
RT_MUTEX_FULL_CHAINWALK);
|
|
||||||
|
|
||||||
if (ret && !rt_mutex_owner(lock)) {
|
|
||||||
/*
|
|
||||||
* Reset the return value. We might have
|
|
||||||
* returned with -EDEADLK and the owner
|
|
||||||
* released the lock while we were walking the
|
|
||||||
* pi chain. Let the waiter sort it out.
|
|
||||||
*/
|
|
||||||
ret = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unlikely(ret))
|
|
||||||
remove_waiter(lock, waiter);
|
|
||||||
|
|
||||||
raw_spin_unlock_irq(&lock->wait_lock);
|
|
||||||
|
|
||||||
debug_rt_mutex_print_deadlock(waiter);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1731,21 +1762,23 @@ struct task_struct *rt_mutex_next_owner(struct rt_mutex *lock)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* rt_mutex_finish_proxy_lock() - Complete lock acquisition
|
* rt_mutex_wait_proxy_lock() - Wait for lock acquisition
|
||||||
* @lock: the rt_mutex we were woken on
|
* @lock: the rt_mutex we were woken on
|
||||||
* @to: the timeout, null if none. hrtimer should already have
|
* @to: the timeout, null if none. hrtimer should already have
|
||||||
* been started.
|
* been started.
|
||||||
* @waiter: the pre-initialized rt_mutex_waiter
|
* @waiter: the pre-initialized rt_mutex_waiter
|
||||||
*
|
*
|
||||||
* Complete the lock acquisition started our behalf by another thread.
|
* Wait for the the lock acquisition started on our behalf by
|
||||||
|
* rt_mutex_start_proxy_lock(). Upon failure, the caller must call
|
||||||
|
* rt_mutex_cleanup_proxy_lock().
|
||||||
*
|
*
|
||||||
* Returns:
|
* Returns:
|
||||||
* 0 - success
|
* 0 - success
|
||||||
* <0 - error, one of -EINTR, -ETIMEDOUT
|
* <0 - error, one of -EINTR, -ETIMEDOUT
|
||||||
*
|
*
|
||||||
* Special API call for PI-futex requeue support
|
* Special API call for PI-futex support
|
||||||
*/
|
*/
|
||||||
int rt_mutex_finish_proxy_lock(struct rt_mutex *lock,
|
int rt_mutex_wait_proxy_lock(struct rt_mutex *lock,
|
||||||
struct hrtimer_sleeper *to,
|
struct hrtimer_sleeper *to,
|
||||||
struct rt_mutex_waiter *waiter)
|
struct rt_mutex_waiter *waiter)
|
||||||
{
|
{
|
||||||
@ -1758,8 +1791,45 @@ int rt_mutex_finish_proxy_lock(struct rt_mutex *lock,
|
|||||||
/* sleep on the mutex */
|
/* sleep on the mutex */
|
||||||
ret = __rt_mutex_slowlock(lock, TASK_INTERRUPTIBLE, to, waiter);
|
ret = __rt_mutex_slowlock(lock, TASK_INTERRUPTIBLE, to, waiter);
|
||||||
|
|
||||||
if (unlikely(ret))
|
raw_spin_unlock_irq(&lock->wait_lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rt_mutex_cleanup_proxy_lock() - Cleanup failed lock acquisition
|
||||||
|
* @lock: the rt_mutex we were woken on
|
||||||
|
* @waiter: the pre-initialized rt_mutex_waiter
|
||||||
|
*
|
||||||
|
* Attempt to clean up after a failed rt_mutex_wait_proxy_lock().
|
||||||
|
*
|
||||||
|
* Unless we acquired the lock; we're still enqueued on the wait-list and can
|
||||||
|
* in fact still be granted ownership until we're removed. Therefore we can
|
||||||
|
* find we are in fact the owner and must disregard the
|
||||||
|
* rt_mutex_wait_proxy_lock() failure.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* true - did the cleanup, we done.
|
||||||
|
* false - we acquired the lock after rt_mutex_wait_proxy_lock() returned,
|
||||||
|
* caller should disregards its return value.
|
||||||
|
*
|
||||||
|
* Special API call for PI-futex support
|
||||||
|
*/
|
||||||
|
bool rt_mutex_cleanup_proxy_lock(struct rt_mutex *lock,
|
||||||
|
struct rt_mutex_waiter *waiter)
|
||||||
|
{
|
||||||
|
bool cleanup = false;
|
||||||
|
|
||||||
|
raw_spin_lock_irq(&lock->wait_lock);
|
||||||
|
/*
|
||||||
|
* Unless we're the owner; we're still enqueued on the wait_list.
|
||||||
|
* So check if we became owner, if not, take us off the wait_list.
|
||||||
|
*/
|
||||||
|
if (rt_mutex_owner(lock) != current) {
|
||||||
remove_waiter(lock, waiter);
|
remove_waiter(lock, waiter);
|
||||||
|
fixup_rt_mutex_waiters(lock);
|
||||||
|
cleanup = true;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* try_to_take_rt_mutex() sets the waiter bit unconditionally. We might
|
* try_to_take_rt_mutex() sets the waiter bit unconditionally. We might
|
||||||
@ -1769,5 +1839,5 @@ int rt_mutex_finish_proxy_lock(struct rt_mutex *lock,
|
|||||||
|
|
||||||
raw_spin_unlock_irq(&lock->wait_lock);
|
raw_spin_unlock_irq(&lock->wait_lock);
|
||||||
|
|
||||||
return ret;
|
return cleanup;
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define rt_mutex_deadlock_check(l) (0)
|
#define rt_mutex_deadlock_check(l) (0)
|
||||||
#define rt_mutex_deadlock_account_lock(m, t) do { } while (0)
|
|
||||||
#define rt_mutex_deadlock_account_unlock(l) do { } while (0)
|
|
||||||
#define debug_rt_mutex_init_waiter(w) do { } while (0)
|
#define debug_rt_mutex_init_waiter(w) do { } while (0)
|
||||||
#define debug_rt_mutex_free_waiter(w) do { } while (0)
|
#define debug_rt_mutex_free_waiter(w) do { } while (0)
|
||||||
#define debug_rt_mutex_lock(l) do { } while (0)
|
#define debug_rt_mutex_lock(l) do { } while (0)
|
||||||
|
@ -34,6 +34,7 @@ struct rt_mutex_waiter {
|
|||||||
struct rt_mutex *deadlock_lock;
|
struct rt_mutex *deadlock_lock;
|
||||||
#endif
|
#endif
|
||||||
int prio;
|
int prio;
|
||||||
|
u64 deadline;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -103,16 +104,26 @@ extern void rt_mutex_init_proxy_locked(struct rt_mutex *lock,
|
|||||||
struct task_struct *proxy_owner);
|
struct task_struct *proxy_owner);
|
||||||
extern void rt_mutex_proxy_unlock(struct rt_mutex *lock,
|
extern void rt_mutex_proxy_unlock(struct rt_mutex *lock,
|
||||||
struct task_struct *proxy_owner);
|
struct task_struct *proxy_owner);
|
||||||
|
extern void rt_mutex_init_waiter(struct rt_mutex_waiter *waiter);
|
||||||
|
extern int __rt_mutex_start_proxy_lock(struct rt_mutex *lock,
|
||||||
|
struct rt_mutex_waiter *waiter,
|
||||||
|
struct task_struct *task);
|
||||||
extern int rt_mutex_start_proxy_lock(struct rt_mutex *lock,
|
extern int rt_mutex_start_proxy_lock(struct rt_mutex *lock,
|
||||||
struct rt_mutex_waiter *waiter,
|
struct rt_mutex_waiter *waiter,
|
||||||
struct task_struct *task);
|
struct task_struct *task);
|
||||||
extern int rt_mutex_finish_proxy_lock(struct rt_mutex *lock,
|
extern int rt_mutex_wait_proxy_lock(struct rt_mutex *lock,
|
||||||
struct hrtimer_sleeper *to,
|
struct hrtimer_sleeper *to,
|
||||||
struct rt_mutex_waiter *waiter);
|
struct rt_mutex_waiter *waiter);
|
||||||
extern int rt_mutex_timed_futex_lock(struct rt_mutex *l, struct hrtimer_sleeper *to);
|
extern bool rt_mutex_cleanup_proxy_lock(struct rt_mutex *lock,
|
||||||
extern bool rt_mutex_futex_unlock(struct rt_mutex *lock,
|
struct rt_mutex_waiter *waiter);
|
||||||
|
|
||||||
|
extern int rt_mutex_futex_trylock(struct rt_mutex *l);
|
||||||
|
|
||||||
|
extern void rt_mutex_futex_unlock(struct rt_mutex *lock);
|
||||||
|
extern bool __rt_mutex_futex_unlock(struct rt_mutex *lock,
|
||||||
struct wake_q_head *wqh);
|
struct wake_q_head *wqh);
|
||||||
extern void rt_mutex_adjust_prio(struct task_struct *task);
|
|
||||||
|
extern void rt_mutex_postunlock(struct wake_q_head *wake_q);
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_RT_MUTEXES
|
#ifdef CONFIG_DEBUG_RT_MUTEXES
|
||||||
# include "rtmutex-debug.h"
|
# include "rtmutex-debug.h"
|
||||||
|
@ -124,10 +124,8 @@ EXPORT_SYMBOL(up_write);
|
|||||||
*/
|
*/
|
||||||
void downgrade_write(struct rw_semaphore *sem)
|
void downgrade_write(struct rw_semaphore *sem)
|
||||||
{
|
{
|
||||||
/*
|
lock_downgrade(&sem->dep_map, _RET_IP_);
|
||||||
* lockdep: a downgraded write will live on as a write
|
|
||||||
* dependency.
|
|
||||||
*/
|
|
||||||
rwsem_set_reader_owned(sem);
|
rwsem_set_reader_owned(sem);
|
||||||
__downgrade_write(sem);
|
__downgrade_write(sem);
|
||||||
}
|
}
|
||||||
|
@ -353,8 +353,8 @@ static int test_cycle(unsigned int ncpus)
|
|||||||
struct stress {
|
struct stress {
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
struct ww_mutex *locks;
|
struct ww_mutex *locks;
|
||||||
|
unsigned long timeout;
|
||||||
int nlocks;
|
int nlocks;
|
||||||
int nloops;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int *get_random_order(int count)
|
static int *get_random_order(int count)
|
||||||
@ -398,12 +398,11 @@ static void stress_inorder_work(struct work_struct *work)
|
|||||||
if (!order)
|
if (!order)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ww_acquire_init(&ctx, &ww_class);
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int contended = -1;
|
int contended = -1;
|
||||||
int n, err;
|
int n, err;
|
||||||
|
|
||||||
|
ww_acquire_init(&ctx, &ww_class);
|
||||||
retry:
|
retry:
|
||||||
err = 0;
|
err = 0;
|
||||||
for (n = 0; n < nlocks; n++) {
|
for (n = 0; n < nlocks; n++) {
|
||||||
@ -433,9 +432,9 @@ retry:
|
|||||||
__func__, err);
|
__func__, err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (--stress->nloops);
|
|
||||||
|
|
||||||
ww_acquire_fini(&ctx);
|
ww_acquire_fini(&ctx);
|
||||||
|
} while (!time_after(jiffies, stress->timeout));
|
||||||
|
|
||||||
kfree(order);
|
kfree(order);
|
||||||
kfree(stress);
|
kfree(stress);
|
||||||
@ -470,9 +469,9 @@ static void stress_reorder_work(struct work_struct *work)
|
|||||||
kfree(order);
|
kfree(order);
|
||||||
order = NULL;
|
order = NULL;
|
||||||
|
|
||||||
|
do {
|
||||||
ww_acquire_init(&ctx, &ww_class);
|
ww_acquire_init(&ctx, &ww_class);
|
||||||
|
|
||||||
do {
|
|
||||||
list_for_each_entry(ll, &locks, link) {
|
list_for_each_entry(ll, &locks, link) {
|
||||||
err = ww_mutex_lock(ll->lock, &ctx);
|
err = ww_mutex_lock(ll->lock, &ctx);
|
||||||
if (!err)
|
if (!err)
|
||||||
@ -495,9 +494,9 @@ static void stress_reorder_work(struct work_struct *work)
|
|||||||
dummy_load(stress);
|
dummy_load(stress);
|
||||||
list_for_each_entry(ll, &locks, link)
|
list_for_each_entry(ll, &locks, link)
|
||||||
ww_mutex_unlock(ll->lock);
|
ww_mutex_unlock(ll->lock);
|
||||||
} while (--stress->nloops);
|
|
||||||
|
|
||||||
ww_acquire_fini(&ctx);
|
ww_acquire_fini(&ctx);
|
||||||
|
} while (!time_after(jiffies, stress->timeout));
|
||||||
|
|
||||||
out:
|
out:
|
||||||
list_for_each_entry_safe(ll, ln, &locks, link)
|
list_for_each_entry_safe(ll, ln, &locks, link)
|
||||||
@ -523,7 +522,7 @@ static void stress_one_work(struct work_struct *work)
|
|||||||
__func__, err);
|
__func__, err);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (--stress->nloops);
|
} while (!time_after(jiffies, stress->timeout));
|
||||||
|
|
||||||
kfree(stress);
|
kfree(stress);
|
||||||
}
|
}
|
||||||
@ -533,7 +532,7 @@ static void stress_one_work(struct work_struct *work)
|
|||||||
#define STRESS_ONE BIT(2)
|
#define STRESS_ONE BIT(2)
|
||||||
#define STRESS_ALL (STRESS_INORDER | STRESS_REORDER | STRESS_ONE)
|
#define STRESS_ALL (STRESS_INORDER | STRESS_REORDER | STRESS_ONE)
|
||||||
|
|
||||||
static int stress(int nlocks, int nthreads, int nloops, unsigned int flags)
|
static int stress(int nlocks, int nthreads, unsigned int flags)
|
||||||
{
|
{
|
||||||
struct ww_mutex *locks;
|
struct ww_mutex *locks;
|
||||||
int n;
|
int n;
|
||||||
@ -575,7 +574,7 @@ static int stress(int nlocks, int nthreads, int nloops, unsigned int flags)
|
|||||||
INIT_WORK(&stress->work, fn);
|
INIT_WORK(&stress->work, fn);
|
||||||
stress->locks = locks;
|
stress->locks = locks;
|
||||||
stress->nlocks = nlocks;
|
stress->nlocks = nlocks;
|
||||||
stress->nloops = nloops;
|
stress->timeout = jiffies + 2*HZ;
|
||||||
|
|
||||||
queue_work(wq, &stress->work);
|
queue_work(wq, &stress->work);
|
||||||
nthreads--;
|
nthreads--;
|
||||||
@ -619,15 +618,15 @@ static int __init test_ww_mutex_init(void)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = stress(16, 2*ncpus, 1<<10, STRESS_INORDER);
|
ret = stress(16, 2*ncpus, STRESS_INORDER);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = stress(16, 2*ncpus, 1<<10, STRESS_REORDER);
|
ret = stress(16, 2*ncpus, STRESS_REORDER);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = stress(4095, hweight32(STRESS_ALL)*ncpus, 1<<12, STRESS_ALL);
|
ret = stress(4095, hweight32(STRESS_ALL)*ncpus, STRESS_ALL);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -665,16 +665,7 @@ static void percpu_modcopy(struct module *mod,
|
|||||||
memcpy(per_cpu_ptr(mod->percpu, cpu), from, size);
|
memcpy(per_cpu_ptr(mod->percpu, cpu), from, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
bool __is_module_percpu_address(unsigned long addr, unsigned long *can_addr)
|
||||||
* is_module_percpu_address - test whether address is from module static percpu
|
|
||||||
* @addr: address to test
|
|
||||||
*
|
|
||||||
* Test whether @addr belongs to module static percpu area.
|
|
||||||
*
|
|
||||||
* RETURNS:
|
|
||||||
* %true if @addr is from module static percpu area
|
|
||||||
*/
|
|
||||||
bool is_module_percpu_address(unsigned long addr)
|
|
||||||
{
|
{
|
||||||
struct module *mod;
|
struct module *mod;
|
||||||
unsigned int cpu;
|
unsigned int cpu;
|
||||||
@ -688,9 +679,15 @@ bool is_module_percpu_address(unsigned long addr)
|
|||||||
continue;
|
continue;
|
||||||
for_each_possible_cpu(cpu) {
|
for_each_possible_cpu(cpu) {
|
||||||
void *start = per_cpu_ptr(mod->percpu, cpu);
|
void *start = per_cpu_ptr(mod->percpu, cpu);
|
||||||
|
void *va = (void *)addr;
|
||||||
|
|
||||||
if ((void *)addr >= start &&
|
if (va >= start && va < start + mod->percpu_size) {
|
||||||
(void *)addr < start + mod->percpu_size) {
|
if (can_addr) {
|
||||||
|
*can_addr = (unsigned long) (va - start);
|
||||||
|
*can_addr += (unsigned long)
|
||||||
|
per_cpu_ptr(mod->percpu,
|
||||||
|
get_boot_cpu_id());
|
||||||
|
}
|
||||||
preempt_enable();
|
preempt_enable();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -701,6 +698,20 @@ bool is_module_percpu_address(unsigned long addr)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* is_module_percpu_address - test whether address is from module static percpu
|
||||||
|
* @addr: address to test
|
||||||
|
*
|
||||||
|
* Test whether @addr belongs to module static percpu area.
|
||||||
|
*
|
||||||
|
* RETURNS:
|
||||||
|
* %true if @addr is from module static percpu area
|
||||||
|
*/
|
||||||
|
bool is_module_percpu_address(unsigned long addr)
|
||||||
|
{
|
||||||
|
return __is_module_percpu_address(addr, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
#else /* ... !CONFIG_SMP */
|
#else /* ... !CONFIG_SMP */
|
||||||
|
|
||||||
static inline void __percpu *mod_percpu(struct module *mod)
|
static inline void __percpu *mod_percpu(struct module *mod)
|
||||||
@ -732,6 +743,11 @@ bool is_module_percpu_address(unsigned long addr)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool __is_module_percpu_address(unsigned long addr, unsigned long *can_addr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_SMP */
|
#endif /* CONFIG_SMP */
|
||||||
|
|
||||||
#define MODINFO_ATTR(field) \
|
#define MODINFO_ATTR(field) \
|
||||||
|
@ -3671,10 +3671,25 @@ EXPORT_SYMBOL(default_wake_function);
|
|||||||
|
|
||||||
#ifdef CONFIG_RT_MUTEXES
|
#ifdef CONFIG_RT_MUTEXES
|
||||||
|
|
||||||
|
static inline int __rt_effective_prio(struct task_struct *pi_task, int prio)
|
||||||
|
{
|
||||||
|
if (pi_task)
|
||||||
|
prio = min(prio, pi_task->prio);
|
||||||
|
|
||||||
|
return prio;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int rt_effective_prio(struct task_struct *p, int prio)
|
||||||
|
{
|
||||||
|
struct task_struct *pi_task = rt_mutex_get_top_task(p);
|
||||||
|
|
||||||
|
return __rt_effective_prio(pi_task, prio);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* rt_mutex_setprio - set the current priority of a task
|
* rt_mutex_setprio - set the current priority of a task
|
||||||
* @p: task
|
* @p: task to boost
|
||||||
* @prio: prio value (kernel-internal form)
|
* @pi_task: donor task
|
||||||
*
|
*
|
||||||
* This function changes the 'effective' priority of a task. It does
|
* This function changes the 'effective' priority of a task. It does
|
||||||
* not touch ->normal_prio like __setscheduler().
|
* not touch ->normal_prio like __setscheduler().
|
||||||
@ -3682,18 +3697,42 @@ EXPORT_SYMBOL(default_wake_function);
|
|||||||
* Used by the rt_mutex code to implement priority inheritance
|
* Used by the rt_mutex code to implement priority inheritance
|
||||||
* logic. Call site only calls if the priority of the task changed.
|
* logic. Call site only calls if the priority of the task changed.
|
||||||
*/
|
*/
|
||||||
void rt_mutex_setprio(struct task_struct *p, int prio)
|
void rt_mutex_setprio(struct task_struct *p, struct task_struct *pi_task)
|
||||||
{
|
{
|
||||||
int oldprio, queued, running, queue_flag =
|
int prio, oldprio, queued, running, queue_flag =
|
||||||
DEQUEUE_SAVE | DEQUEUE_MOVE | DEQUEUE_NOCLOCK;
|
DEQUEUE_SAVE | DEQUEUE_MOVE | DEQUEUE_NOCLOCK;
|
||||||
const struct sched_class *prev_class;
|
const struct sched_class *prev_class;
|
||||||
struct rq_flags rf;
|
struct rq_flags rf;
|
||||||
struct rq *rq;
|
struct rq *rq;
|
||||||
|
|
||||||
BUG_ON(prio > MAX_PRIO);
|
/* XXX used to be waiter->prio, not waiter->task->prio */
|
||||||
|
prio = __rt_effective_prio(pi_task, p->normal_prio);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If nothing changed; bail early.
|
||||||
|
*/
|
||||||
|
if (p->pi_top_task == pi_task && prio == p->prio && !dl_prio(prio))
|
||||||
|
return;
|
||||||
|
|
||||||
rq = __task_rq_lock(p, &rf);
|
rq = __task_rq_lock(p, &rf);
|
||||||
update_rq_clock(rq);
|
update_rq_clock(rq);
|
||||||
|
/*
|
||||||
|
* Set under pi_lock && rq->lock, such that the value can be used under
|
||||||
|
* either lock.
|
||||||
|
*
|
||||||
|
* Note that there is loads of tricky to make this pointer cache work
|
||||||
|
* right. rt_mutex_slowunlock()+rt_mutex_postunlock() work together to
|
||||||
|
* ensure a task is de-boosted (pi_task is set to NULL) before the
|
||||||
|
* task is allowed to run again (and can exit). This ensures the pointer
|
||||||
|
* points to a blocked task -- which guaratees the task is present.
|
||||||
|
*/
|
||||||
|
p->pi_top_task = pi_task;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For FIFO/RR we only need to set prio, if that matches we're done.
|
||||||
|
*/
|
||||||
|
if (prio == p->prio && !dl_prio(prio))
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Idle task boosting is a nono in general. There is one
|
* Idle task boosting is a nono in general. There is one
|
||||||
@ -3713,7 +3752,7 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
|
|||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
trace_sched_pi_setprio(p, prio);
|
trace_sched_pi_setprio(p, pi_task);
|
||||||
oldprio = p->prio;
|
oldprio = p->prio;
|
||||||
|
|
||||||
if (oldprio == prio)
|
if (oldprio == prio)
|
||||||
@ -3737,7 +3776,6 @@ void rt_mutex_setprio(struct task_struct *p, int prio)
|
|||||||
* running task
|
* running task
|
||||||
*/
|
*/
|
||||||
if (dl_prio(prio)) {
|
if (dl_prio(prio)) {
|
||||||
struct task_struct *pi_task = rt_mutex_get_top_task(p);
|
|
||||||
if (!dl_prio(p->normal_prio) ||
|
if (!dl_prio(p->normal_prio) ||
|
||||||
(pi_task && dl_entity_preempt(&pi_task->dl, &p->dl))) {
|
(pi_task && dl_entity_preempt(&pi_task->dl, &p->dl))) {
|
||||||
p->dl.dl_boosted = 1;
|
p->dl.dl_boosted = 1;
|
||||||
@ -3775,6 +3813,11 @@ out_unlock:
|
|||||||
balance_callback(rq);
|
balance_callback(rq);
|
||||||
preempt_enable();
|
preempt_enable();
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
static inline int rt_effective_prio(struct task_struct *p, int prio)
|
||||||
|
{
|
||||||
|
return prio;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void set_user_nice(struct task_struct *p, long nice)
|
void set_user_nice(struct task_struct *p, long nice)
|
||||||
@ -4021,10 +4064,9 @@ static void __setscheduler(struct rq *rq, struct task_struct *p,
|
|||||||
* Keep a potential priority boosting if called from
|
* Keep a potential priority boosting if called from
|
||||||
* sched_setscheduler().
|
* sched_setscheduler().
|
||||||
*/
|
*/
|
||||||
if (keep_boost)
|
|
||||||
p->prio = rt_mutex_get_effective_prio(p, normal_prio(p));
|
|
||||||
else
|
|
||||||
p->prio = normal_prio(p);
|
p->prio = normal_prio(p);
|
||||||
|
if (keep_boost)
|
||||||
|
p->prio = rt_effective_prio(p, p->prio);
|
||||||
|
|
||||||
if (dl_prio(p->prio))
|
if (dl_prio(p->prio))
|
||||||
p->sched_class = &dl_sched_class;
|
p->sched_class = &dl_sched_class;
|
||||||
@ -4311,7 +4353,7 @@ change:
|
|||||||
* the runqueue. This will be done when the task deboost
|
* the runqueue. This will be done when the task deboost
|
||||||
* itself.
|
* itself.
|
||||||
*/
|
*/
|
||||||
new_effective_prio = rt_mutex_get_effective_prio(p, newprio);
|
new_effective_prio = rt_effective_prio(p, newprio);
|
||||||
if (new_effective_prio == oldprio)
|
if (new_effective_prio == oldprio)
|
||||||
queue_flags &= ~DEQUEUE_MOVE;
|
queue_flags &= ~DEQUEUE_MOVE;
|
||||||
}
|
}
|
||||||
|
169
lib/refcount.c
169
lib/refcount.c
@ -37,11 +37,29 @@
|
|||||||
#include <linux/refcount.h>
|
#include <linux/refcount.h>
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* refcount_add_not_zero - add a value to a refcount unless it is 0
|
||||||
|
* @i: the value to add to the refcount
|
||||||
|
* @r: the refcount
|
||||||
|
*
|
||||||
|
* Will saturate at UINT_MAX and WARN.
|
||||||
|
*
|
||||||
|
* Provides no memory ordering, it is assumed the caller has guaranteed the
|
||||||
|
* object memory to be stable (RCU, etc.). It does provide a control dependency
|
||||||
|
* and thereby orders future stores. See the comment on top.
|
||||||
|
*
|
||||||
|
* Use of this function is not recommended for the normal reference counting
|
||||||
|
* use case in which references are taken and released one at a time. In these
|
||||||
|
* cases, refcount_inc(), or one of its variants, should instead be used to
|
||||||
|
* increment a reference count.
|
||||||
|
*
|
||||||
|
* Return: false if the passed refcount is 0, true otherwise
|
||||||
|
*/
|
||||||
bool refcount_add_not_zero(unsigned int i, refcount_t *r)
|
bool refcount_add_not_zero(unsigned int i, refcount_t *r)
|
||||||
{
|
{
|
||||||
unsigned int old, new, val = atomic_read(&r->refs);
|
unsigned int new, val = atomic_read(&r->refs);
|
||||||
|
|
||||||
for (;;) {
|
do {
|
||||||
if (!val)
|
if (!val)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -51,12 +69,8 @@ bool refcount_add_not_zero(unsigned int i, refcount_t *r)
|
|||||||
new = val + i;
|
new = val + i;
|
||||||
if (new < val)
|
if (new < val)
|
||||||
new = UINT_MAX;
|
new = UINT_MAX;
|
||||||
old = atomic_cmpxchg_relaxed(&r->refs, val, new);
|
|
||||||
if (old == val)
|
|
||||||
break;
|
|
||||||
|
|
||||||
val = old;
|
} while (!atomic_try_cmpxchg_relaxed(&r->refs, &val, new));
|
||||||
}
|
|
||||||
|
|
||||||
WARN_ONCE(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
|
WARN_ONCE(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
|
||||||
|
|
||||||
@ -64,24 +78,45 @@ bool refcount_add_not_zero(unsigned int i, refcount_t *r)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(refcount_add_not_zero);
|
EXPORT_SYMBOL_GPL(refcount_add_not_zero);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* refcount_add - add a value to a refcount
|
||||||
|
* @i: the value to add to the refcount
|
||||||
|
* @r: the refcount
|
||||||
|
*
|
||||||
|
* Similar to atomic_add(), but will saturate at UINT_MAX and WARN.
|
||||||
|
*
|
||||||
|
* Provides no memory ordering, it is assumed the caller has guaranteed the
|
||||||
|
* object memory to be stable (RCU, etc.). It does provide a control dependency
|
||||||
|
* and thereby orders future stores. See the comment on top.
|
||||||
|
*
|
||||||
|
* Use of this function is not recommended for the normal reference counting
|
||||||
|
* use case in which references are taken and released one at a time. In these
|
||||||
|
* cases, refcount_inc(), or one of its variants, should instead be used to
|
||||||
|
* increment a reference count.
|
||||||
|
*/
|
||||||
void refcount_add(unsigned int i, refcount_t *r)
|
void refcount_add(unsigned int i, refcount_t *r)
|
||||||
{
|
{
|
||||||
WARN_ONCE(!refcount_add_not_zero(i, r), "refcount_t: addition on 0; use-after-free.\n");
|
WARN_ONCE(!refcount_add_not_zero(i, r), "refcount_t: addition on 0; use-after-free.\n");
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(refcount_add);
|
EXPORT_SYMBOL_GPL(refcount_add);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Similar to atomic_inc_not_zero(), will saturate at UINT_MAX and WARN.
|
* refcount_inc_not_zero - increment a refcount unless it is 0
|
||||||
|
* @r: the refcount to increment
|
||||||
|
*
|
||||||
|
* Similar to atomic_inc_not_zero(), but will saturate at UINT_MAX and WARN.
|
||||||
*
|
*
|
||||||
* Provides no memory ordering, it is assumed the caller has guaranteed the
|
* Provides no memory ordering, it is assumed the caller has guaranteed the
|
||||||
* object memory to be stable (RCU, etc.). It does provide a control dependency
|
* object memory to be stable (RCU, etc.). It does provide a control dependency
|
||||||
* and thereby orders future stores. See the comment on top.
|
* and thereby orders future stores. See the comment on top.
|
||||||
|
*
|
||||||
|
* Return: true if the increment was successful, false otherwise
|
||||||
*/
|
*/
|
||||||
bool refcount_inc_not_zero(refcount_t *r)
|
bool refcount_inc_not_zero(refcount_t *r)
|
||||||
{
|
{
|
||||||
unsigned int old, new, val = atomic_read(&r->refs);
|
unsigned int new, val = atomic_read(&r->refs);
|
||||||
|
|
||||||
for (;;) {
|
do {
|
||||||
new = val + 1;
|
new = val + 1;
|
||||||
|
|
||||||
if (!val)
|
if (!val)
|
||||||
@ -90,12 +125,7 @@ bool refcount_inc_not_zero(refcount_t *r)
|
|||||||
if (unlikely(!new))
|
if (unlikely(!new))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
old = atomic_cmpxchg_relaxed(&r->refs, val, new);
|
} while (!atomic_try_cmpxchg_relaxed(&r->refs, &val, new));
|
||||||
if (old == val)
|
|
||||||
break;
|
|
||||||
|
|
||||||
val = old;
|
|
||||||
}
|
|
||||||
|
|
||||||
WARN_ONCE(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
|
WARN_ONCE(new == UINT_MAX, "refcount_t: saturated; leaking memory.\n");
|
||||||
|
|
||||||
@ -103,11 +133,17 @@ bool refcount_inc_not_zero(refcount_t *r)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(refcount_inc_not_zero);
|
EXPORT_SYMBOL_GPL(refcount_inc_not_zero);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Similar to atomic_inc(), will saturate at UINT_MAX and WARN.
|
* refcount_inc - increment a refcount
|
||||||
|
* @r: the refcount to increment
|
||||||
|
*
|
||||||
|
* Similar to atomic_inc(), but will saturate at UINT_MAX and WARN.
|
||||||
*
|
*
|
||||||
* Provides no memory ordering, it is assumed the caller already has a
|
* Provides no memory ordering, it is assumed the caller already has a
|
||||||
* reference on the object, will WARN when this is not so.
|
* reference on the object.
|
||||||
|
*
|
||||||
|
* Will WARN if the refcount is 0, as this represents a possible use-after-free
|
||||||
|
* condition.
|
||||||
*/
|
*/
|
||||||
void refcount_inc(refcount_t *r)
|
void refcount_inc(refcount_t *r)
|
||||||
{
|
{
|
||||||
@ -115,11 +151,31 @@ void refcount_inc(refcount_t *r)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(refcount_inc);
|
EXPORT_SYMBOL_GPL(refcount_inc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* refcount_sub_and_test - subtract from a refcount and test if it is 0
|
||||||
|
* @i: amount to subtract from the refcount
|
||||||
|
* @r: the refcount
|
||||||
|
*
|
||||||
|
* Similar to atomic_dec_and_test(), but it will WARN, return false and
|
||||||
|
* ultimately leak on underflow and will fail to decrement when saturated
|
||||||
|
* at UINT_MAX.
|
||||||
|
*
|
||||||
|
* Provides release memory ordering, such that prior loads and stores are done
|
||||||
|
* before, and provides a control dependency such that free() must come after.
|
||||||
|
* See the comment on top.
|
||||||
|
*
|
||||||
|
* Use of this function is not recommended for the normal reference counting
|
||||||
|
* use case in which references are taken and released one at a time. In these
|
||||||
|
* cases, refcount_dec(), or one of its variants, should instead be used to
|
||||||
|
* decrement a reference count.
|
||||||
|
*
|
||||||
|
* Return: true if the resulting refcount is 0, false otherwise
|
||||||
|
*/
|
||||||
bool refcount_sub_and_test(unsigned int i, refcount_t *r)
|
bool refcount_sub_and_test(unsigned int i, refcount_t *r)
|
||||||
{
|
{
|
||||||
unsigned int old, new, val = atomic_read(&r->refs);
|
unsigned int new, val = atomic_read(&r->refs);
|
||||||
|
|
||||||
for (;;) {
|
do {
|
||||||
if (unlikely(val == UINT_MAX))
|
if (unlikely(val == UINT_MAX))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -129,24 +185,24 @@ bool refcount_sub_and_test(unsigned int i, refcount_t *r)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
old = atomic_cmpxchg_release(&r->refs, val, new);
|
} while (!atomic_try_cmpxchg_release(&r->refs, &val, new));
|
||||||
if (old == val)
|
|
||||||
break;
|
|
||||||
|
|
||||||
val = old;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !new;
|
return !new;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(refcount_sub_and_test);
|
EXPORT_SYMBOL_GPL(refcount_sub_and_test);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
|
* refcount_dec_and_test - decrement a refcount and test if it is 0
|
||||||
|
* @r: the refcount
|
||||||
|
*
|
||||||
* Similar to atomic_dec_and_test(), it will WARN on underflow and fail to
|
* Similar to atomic_dec_and_test(), it will WARN on underflow and fail to
|
||||||
* decrement when saturated at UINT_MAX.
|
* decrement when saturated at UINT_MAX.
|
||||||
*
|
*
|
||||||
* Provides release memory ordering, such that prior loads and stores are done
|
* Provides release memory ordering, such that prior loads and stores are done
|
||||||
* before, and provides a control dependency such that free() must come after.
|
* before, and provides a control dependency such that free() must come after.
|
||||||
* See the comment on top.
|
* See the comment on top.
|
||||||
|
*
|
||||||
|
* Return: true if the resulting refcount is 0, false otherwise
|
||||||
*/
|
*/
|
||||||
bool refcount_dec_and_test(refcount_t *r)
|
bool refcount_dec_and_test(refcount_t *r)
|
||||||
{
|
{
|
||||||
@ -154,21 +210,26 @@ bool refcount_dec_and_test(refcount_t *r)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(refcount_dec_and_test);
|
EXPORT_SYMBOL_GPL(refcount_dec_and_test);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
|
* refcount_dec - decrement a refcount
|
||||||
|
* @r: the refcount
|
||||||
|
*
|
||||||
* Similar to atomic_dec(), it will WARN on underflow and fail to decrement
|
* Similar to atomic_dec(), it will WARN on underflow and fail to decrement
|
||||||
* when saturated at UINT_MAX.
|
* when saturated at UINT_MAX.
|
||||||
*
|
*
|
||||||
* Provides release memory ordering, such that prior loads and stores are done
|
* Provides release memory ordering, such that prior loads and stores are done
|
||||||
* before.
|
* before.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void refcount_dec(refcount_t *r)
|
void refcount_dec(refcount_t *r)
|
||||||
{
|
{
|
||||||
WARN_ONCE(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n");
|
WARN_ONCE(refcount_dec_and_test(r), "refcount_t: decrement hit 0; leaking memory.\n");
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(refcount_dec);
|
EXPORT_SYMBOL_GPL(refcount_dec);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
|
* refcount_dec_if_one - decrement a refcount if it is 1
|
||||||
|
* @r: the refcount
|
||||||
|
*
|
||||||
* No atomic_t counterpart, it attempts a 1 -> 0 transition and returns the
|
* No atomic_t counterpart, it attempts a 1 -> 0 transition and returns the
|
||||||
* success thereof.
|
* success thereof.
|
||||||
*
|
*
|
||||||
@ -178,24 +239,33 @@ EXPORT_SYMBOL_GPL(refcount_dec);
|
|||||||
* It can be used like a try-delete operator; this explicit case is provided
|
* It can be used like a try-delete operator; this explicit case is provided
|
||||||
* and not cmpxchg in generic, because that would allow implementing unsafe
|
* and not cmpxchg in generic, because that would allow implementing unsafe
|
||||||
* operations.
|
* operations.
|
||||||
|
*
|
||||||
|
* Return: true if the resulting refcount is 0, false otherwise
|
||||||
*/
|
*/
|
||||||
bool refcount_dec_if_one(refcount_t *r)
|
bool refcount_dec_if_one(refcount_t *r)
|
||||||
{
|
{
|
||||||
return atomic_cmpxchg_release(&r->refs, 1, 0) == 1;
|
int val = 1;
|
||||||
|
|
||||||
|
return atomic_try_cmpxchg_release(&r->refs, &val, 0);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(refcount_dec_if_one);
|
EXPORT_SYMBOL_GPL(refcount_dec_if_one);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
|
* refcount_dec_not_one - decrement a refcount if it is not 1
|
||||||
|
* @r: the refcount
|
||||||
|
*
|
||||||
* No atomic_t counterpart, it decrements unless the value is 1, in which case
|
* No atomic_t counterpart, it decrements unless the value is 1, in which case
|
||||||
* it will return false.
|
* it will return false.
|
||||||
*
|
*
|
||||||
* Was often done like: atomic_add_unless(&var, -1, 1)
|
* Was often done like: atomic_add_unless(&var, -1, 1)
|
||||||
|
*
|
||||||
|
* Return: true if the decrement operation was successful, false otherwise
|
||||||
*/
|
*/
|
||||||
bool refcount_dec_not_one(refcount_t *r)
|
bool refcount_dec_not_one(refcount_t *r)
|
||||||
{
|
{
|
||||||
unsigned int old, new, val = atomic_read(&r->refs);
|
unsigned int new, val = atomic_read(&r->refs);
|
||||||
|
|
||||||
for (;;) {
|
do {
|
||||||
if (unlikely(val == UINT_MAX))
|
if (unlikely(val == UINT_MAX))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
@ -208,24 +278,27 @@ bool refcount_dec_not_one(refcount_t *r)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
old = atomic_cmpxchg_release(&r->refs, val, new);
|
} while (!atomic_try_cmpxchg_release(&r->refs, &val, new));
|
||||||
if (old == val)
|
|
||||||
break;
|
|
||||||
|
|
||||||
val = old;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(refcount_dec_not_one);
|
EXPORT_SYMBOL_GPL(refcount_dec_not_one);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
|
* refcount_dec_and_mutex_lock - return holding mutex if able to decrement
|
||||||
|
* refcount to 0
|
||||||
|
* @r: the refcount
|
||||||
|
* @lock: the mutex to be locked
|
||||||
|
*
|
||||||
* Similar to atomic_dec_and_mutex_lock(), it will WARN on underflow and fail
|
* Similar to atomic_dec_and_mutex_lock(), it will WARN on underflow and fail
|
||||||
* to decrement when saturated at UINT_MAX.
|
* to decrement when saturated at UINT_MAX.
|
||||||
*
|
*
|
||||||
* Provides release memory ordering, such that prior loads and stores are done
|
* Provides release memory ordering, such that prior loads and stores are done
|
||||||
* before, and provides a control dependency such that free() must come after.
|
* before, and provides a control dependency such that free() must come after.
|
||||||
* See the comment on top.
|
* See the comment on top.
|
||||||
|
*
|
||||||
|
* Return: true and hold mutex if able to decrement refcount to 0, false
|
||||||
|
* otherwise
|
||||||
*/
|
*/
|
||||||
bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)
|
bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)
|
||||||
{
|
{
|
||||||
@ -242,13 +315,21 @@ bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(refcount_dec_and_mutex_lock);
|
EXPORT_SYMBOL_GPL(refcount_dec_and_mutex_lock);
|
||||||
|
|
||||||
/*
|
/**
|
||||||
|
* refcount_dec_and_lock - return holding spinlock if able to decrement
|
||||||
|
* refcount to 0
|
||||||
|
* @r: the refcount
|
||||||
|
* @lock: the spinlock to be locked
|
||||||
|
*
|
||||||
* Similar to atomic_dec_and_lock(), it will WARN on underflow and fail to
|
* Similar to atomic_dec_and_lock(), it will WARN on underflow and fail to
|
||||||
* decrement when saturated at UINT_MAX.
|
* decrement when saturated at UINT_MAX.
|
||||||
*
|
*
|
||||||
* Provides release memory ordering, such that prior loads and stores are done
|
* Provides release memory ordering, such that prior loads and stores are done
|
||||||
* before, and provides a control dependency such that free() must come after.
|
* before, and provides a control dependency such that free() must come after.
|
||||||
* See the comment on top.
|
* See the comment on top.
|
||||||
|
*
|
||||||
|
* Return: true and hold spinlock if able to decrement refcount to 0, false
|
||||||
|
* otherwise
|
||||||
*/
|
*/
|
||||||
bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock)
|
bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock)
|
||||||
{
|
{
|
||||||
|
40
mm/percpu.c
40
mm/percpu.c
@ -1284,6 +1284,31 @@ void free_percpu(void __percpu *ptr)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(free_percpu);
|
EXPORT_SYMBOL_GPL(free_percpu);
|
||||||
|
|
||||||
|
bool __is_kernel_percpu_address(unsigned long addr, unsigned long *can_addr)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
const size_t static_size = __per_cpu_end - __per_cpu_start;
|
||||||
|
void __percpu *base = __addr_to_pcpu_ptr(pcpu_base_addr);
|
||||||
|
unsigned int cpu;
|
||||||
|
|
||||||
|
for_each_possible_cpu(cpu) {
|
||||||
|
void *start = per_cpu_ptr(base, cpu);
|
||||||
|
void *va = (void *)addr;
|
||||||
|
|
||||||
|
if (va >= start && va < start + static_size) {
|
||||||
|
if (can_addr) {
|
||||||
|
*can_addr = (unsigned long) (va - start);
|
||||||
|
*can_addr += (unsigned long)
|
||||||
|
per_cpu_ptr(base, get_boot_cpu_id());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* on UP, can't distinguish from other static vars, always false */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* is_kernel_percpu_address - test whether address is from static percpu area
|
* is_kernel_percpu_address - test whether address is from static percpu area
|
||||||
* @addr: address to test
|
* @addr: address to test
|
||||||
@ -1297,20 +1322,7 @@ EXPORT_SYMBOL_GPL(free_percpu);
|
|||||||
*/
|
*/
|
||||||
bool is_kernel_percpu_address(unsigned long addr)
|
bool is_kernel_percpu_address(unsigned long addr)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_SMP
|
return __is_kernel_percpu_address(addr, NULL);
|
||||||
const size_t static_size = __per_cpu_end - __per_cpu_start;
|
|
||||||
void __percpu *base = __addr_to_pcpu_ptr(pcpu_base_addr);
|
|
||||||
unsigned int cpu;
|
|
||||||
|
|
||||||
for_each_possible_cpu(cpu) {
|
|
||||||
void *start = per_cpu_ptr(base, cpu);
|
|
||||||
|
|
||||||
if ((void *)addr >= start && (void *)addr < start + static_size)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
/* on UP, can't distinguish from other static vars, always false */
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user