Merge tag 'locking-core-2023-10-28' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull locking updates from Info Molnar:
"Futex improvements:
- Add the 'futex2' syscall ABI, which is an attempt to get away from
the multiplex syscall and adds a little room for extentions, while
lifting some limitations.
- Fix futex PI recursive rt_mutex waiter state bug
- Fix inter-process shared futexes on no-MMU systems
- Use folios instead of pages
Micro-optimizations of locking primitives:
- Improve arch_spin_value_unlocked() on asm-generic ticket spinlock
architectures, to improve lockref code generation
- Improve the x86-32 lockref_get_not_zero() main loop by adding
build-time CMPXCHG8B support detection for the relevant lockref
code, and by better interfacing the CMPXCHG8B assembly code with
the compiler
- Introduce arch_sync_try_cmpxchg() on x86 to improve
sync_try_cmpxchg() code generation. Convert some sync_cmpxchg()
users to sync_try_cmpxchg().
- Micro-optimize rcuref_put_slowpath()
Locking debuggability improvements:
- Improve CONFIG_DEBUG_RT_MUTEXES=y to have a fast-path as well
- Enforce atomicity of sched_submit_work(), which is de-facto atomic
but was un-enforced previously.
- Extend <linux/cleanup.h>'s no_free_ptr() with __must_check
semantics
- Fix ww_mutex self-tests
- Clean up const-propagation in <linux/seqlock.h> and simplify the
API-instantiation macros a bit
RT locking improvements:
- Provide the rt_mutex_*_schedule() primitives/helpers and use them
in the rtmutex code to avoid recursion vs. rtlock on the PI state.
- Add nested blocking lockdep asserts to rt_mutex_lock(),
rtlock_lock() and rwbase_read_lock()
.. plus misc fixes & cleanups"
* tag 'locking-core-2023-10-28' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (39 commits)
futex: Don't include process MM in futex key on no-MMU
locking/seqlock: Fix grammar in comment
alpha: Fix up new futex syscall numbers
locking/seqlock: Propagate 'const' pointers within read-only methods, remove forced type casts
locking/lockdep: Fix string sizing bug that triggers a format-truncation compiler-warning
locking/seqlock: Change __seqprop() to return the function pointer
locking/seqlock: Simplify SEQCOUNT_LOCKNAME()
locking/atomics: Use atomic_try_cmpxchg_release() to micro-optimize rcuref_put_slowpath()
locking/atomic, xen: Use sync_try_cmpxchg() instead of sync_cmpxchg()
locking/atomic/x86: Introduce arch_sync_try_cmpxchg()
locking/atomic: Add generic support for sync_try_cmpxchg() and its fallback
locking/seqlock: Fix typo in comment
futex/requeue: Remove unnecessary ‘NULL’ initialization from futex_proxy_trylock_atomic()
locking/local, arch: Rewrite local_add_unless() as a static inline function
locking/debug: Fix debugfs API return value checks to use IS_ERR()
locking/ww_mutex/test: Make sure we bail out instead of livelock
locking/ww_mutex/test: Fix potential workqueue corruption
locking/ww_mutex/test: Use prng instead of rng to avoid hangs at bootup
futex: Add sys_futex_requeue()
futex: Add flags2 argument to futex_requeue()
...
This commit is contained in:
@@ -146,7 +146,7 @@ static int __init init_lockevent_counts(void)
|
||||
struct dentry *d_counts = debugfs_create_dir(LOCK_EVENTS_DIR, NULL);
|
||||
int i;
|
||||
|
||||
if (!d_counts)
|
||||
if (IS_ERR(d_counts))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
@@ -159,14 +159,14 @@ static int __init init_lockevent_counts(void)
|
||||
for (i = 0; i < lockevent_num; i++) {
|
||||
if (skip_lockevent(lockevent_names[i]))
|
||||
continue;
|
||||
if (!debugfs_create_file(lockevent_names[i], 0400, d_counts,
|
||||
(void *)(long)i, &fops_lockevent))
|
||||
if (IS_ERR(debugfs_create_file(lockevent_names[i], 0400, d_counts,
|
||||
(void *)(long)i, &fops_lockevent)))
|
||||
goto fail_undo;
|
||||
}
|
||||
|
||||
if (!debugfs_create_file(lockevent_names[LOCKEVENT_reset_cnts], 0200,
|
||||
if (IS_ERR(debugfs_create_file(lockevent_names[LOCKEVENT_reset_cnts], 0200,
|
||||
d_counts, (void *)(long)LOCKEVENT_reset_cnts,
|
||||
&fops_lockevent))
|
||||
&fops_lockevent)))
|
||||
goto fail_undo;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -440,7 +440,7 @@ static void snprint_time(char *buf, size_t bufsiz, s64 nr)
|
||||
|
||||
static void seq_time(struct seq_file *m, s64 time)
|
||||
{
|
||||
char num[15];
|
||||
char num[22];
|
||||
|
||||
snprint_time(num, sizeof(num), time);
|
||||
seq_printf(m, " %14s", num);
|
||||
|
||||
@@ -218,6 +218,11 @@ static __always_inline bool rt_mutex_cmpxchg_acquire(struct rt_mutex_base *lock,
|
||||
return try_cmpxchg_acquire(&lock->owner, &old, new);
|
||||
}
|
||||
|
||||
static __always_inline bool rt_mutex_try_acquire(struct rt_mutex_base *lock)
|
||||
{
|
||||
return rt_mutex_cmpxchg_acquire(lock, NULL, current);
|
||||
}
|
||||
|
||||
static __always_inline bool rt_mutex_cmpxchg_release(struct rt_mutex_base *lock,
|
||||
struct task_struct *old,
|
||||
struct task_struct *new)
|
||||
@@ -297,6 +302,20 @@ static __always_inline bool rt_mutex_cmpxchg_acquire(struct rt_mutex_base *lock,
|
||||
|
||||
}
|
||||
|
||||
static int __sched rt_mutex_slowtrylock(struct rt_mutex_base *lock);
|
||||
|
||||
static __always_inline bool rt_mutex_try_acquire(struct rt_mutex_base *lock)
|
||||
{
|
||||
/*
|
||||
* With debug enabled rt_mutex_cmpxchg trylock() will always fail.
|
||||
*
|
||||
* Avoid unconditionally taking the slow path by using
|
||||
* rt_mutex_slow_trylock() which is covered by the debug code and can
|
||||
* acquire a non-contended rtmutex.
|
||||
*/
|
||||
return rt_mutex_slowtrylock(lock);
|
||||
}
|
||||
|
||||
static __always_inline bool rt_mutex_cmpxchg_release(struct rt_mutex_base *lock,
|
||||
struct task_struct *old,
|
||||
struct task_struct *new)
|
||||
@@ -1613,7 +1632,7 @@ static int __sched rt_mutex_slowlock_block(struct rt_mutex_base *lock,
|
||||
raw_spin_unlock_irq(&lock->wait_lock);
|
||||
|
||||
if (!owner || !rtmutex_spin_on_owner(lock, waiter, owner))
|
||||
schedule();
|
||||
rt_mutex_schedule();
|
||||
|
||||
raw_spin_lock_irq(&lock->wait_lock);
|
||||
set_current_state(state);
|
||||
@@ -1642,7 +1661,7 @@ static void __sched rt_mutex_handle_deadlock(int res, int detect_deadlock,
|
||||
WARN(1, "rtmutex deadlock detected\n");
|
||||
while (1) {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
schedule();
|
||||
rt_mutex_schedule();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1737,6 +1756,15 @@ static int __sched rt_mutex_slowlock(struct rt_mutex_base *lock,
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Do all pre-schedule work here, before we queue a waiter and invoke
|
||||
* PI -- any such work that trips on rtlock (PREEMPT_RT spinlock) would
|
||||
* otherwise recurse back into task_blocks_on_rt_mutex() through
|
||||
* rtlock_slowlock() and will then enqueue a second waiter for this
|
||||
* same task and things get really confusing real fast.
|
||||
*/
|
||||
rt_mutex_pre_schedule();
|
||||
|
||||
/*
|
||||
* Technically we could use raw_spin_[un]lock_irq() here, but this can
|
||||
* be called in early boot if the cmpxchg() fast path is disabled
|
||||
@@ -1748,6 +1776,7 @@ static int __sched rt_mutex_slowlock(struct rt_mutex_base *lock,
|
||||
raw_spin_lock_irqsave(&lock->wait_lock, flags);
|
||||
ret = __rt_mutex_slowlock_locked(lock, ww_ctx, state);
|
||||
raw_spin_unlock_irqrestore(&lock->wait_lock, flags);
|
||||
rt_mutex_post_schedule();
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1755,7 +1784,9 @@ static int __sched rt_mutex_slowlock(struct rt_mutex_base *lock,
|
||||
static __always_inline int __rt_mutex_lock(struct rt_mutex_base *lock,
|
||||
unsigned int state)
|
||||
{
|
||||
if (likely(rt_mutex_cmpxchg_acquire(lock, NULL, current)))
|
||||
lockdep_assert(!current->pi_blocked_on);
|
||||
|
||||
if (likely(rt_mutex_try_acquire(lock)))
|
||||
return 0;
|
||||
|
||||
return rt_mutex_slowlock(lock, NULL, state);
|
||||
|
||||
@@ -71,6 +71,7 @@ static int __sched __rwbase_read_lock(struct rwbase_rt *rwb,
|
||||
struct rt_mutex_base *rtm = &rwb->rtmutex;
|
||||
int ret;
|
||||
|
||||
rwbase_pre_schedule();
|
||||
raw_spin_lock_irq(&rtm->wait_lock);
|
||||
|
||||
/*
|
||||
@@ -125,12 +126,15 @@ static int __sched __rwbase_read_lock(struct rwbase_rt *rwb,
|
||||
rwbase_rtmutex_unlock(rtm);
|
||||
|
||||
trace_contention_end(rwb, ret);
|
||||
rwbase_post_schedule();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static __always_inline int rwbase_read_lock(struct rwbase_rt *rwb,
|
||||
unsigned int state)
|
||||
{
|
||||
lockdep_assert(!current->pi_blocked_on);
|
||||
|
||||
if (rwbase_read_trylock(rwb))
|
||||
return 0;
|
||||
|
||||
@@ -237,6 +241,8 @@ static int __sched rwbase_write_lock(struct rwbase_rt *rwb,
|
||||
/* Force readers into slow path */
|
||||
atomic_sub(READER_BIAS, &rwb->readers);
|
||||
|
||||
rwbase_pre_schedule();
|
||||
|
||||
raw_spin_lock_irqsave(&rtm->wait_lock, flags);
|
||||
if (__rwbase_write_trylock(rwb))
|
||||
goto out_unlock;
|
||||
@@ -248,6 +254,7 @@ static int __sched rwbase_write_lock(struct rwbase_rt *rwb,
|
||||
if (rwbase_signal_pending_state(state, current)) {
|
||||
rwbase_restore_current_state();
|
||||
__rwbase_write_unlock(rwb, 0, flags);
|
||||
rwbase_post_schedule();
|
||||
trace_contention_end(rwb, -EINTR);
|
||||
return -EINTR;
|
||||
}
|
||||
@@ -266,6 +273,7 @@ static int __sched rwbase_write_lock(struct rwbase_rt *rwb,
|
||||
|
||||
out_unlock:
|
||||
raw_spin_unlock_irqrestore(&rtm->wait_lock, flags);
|
||||
rwbase_post_schedule();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1427,8 +1427,14 @@ static inline void __downgrade_write(struct rw_semaphore *sem)
|
||||
#define rwbase_signal_pending_state(state, current) \
|
||||
signal_pending_state(state, current)
|
||||
|
||||
#define rwbase_pre_schedule() \
|
||||
rt_mutex_pre_schedule()
|
||||
|
||||
#define rwbase_schedule() \
|
||||
schedule()
|
||||
rt_mutex_schedule()
|
||||
|
||||
#define rwbase_post_schedule() \
|
||||
rt_mutex_post_schedule()
|
||||
|
||||
#include "rwbase_rt.c"
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
|
||||
static __always_inline void rtlock_lock(struct rt_mutex_base *rtm)
|
||||
{
|
||||
lockdep_assert(!current->pi_blocked_on);
|
||||
|
||||
if (unlikely(!rt_mutex_cmpxchg_acquire(rtm, NULL, current)))
|
||||
rtlock_slowlock(rtm);
|
||||
}
|
||||
@@ -184,9 +186,13 @@ static __always_inline int rwbase_rtmutex_trylock(struct rt_mutex_base *rtm)
|
||||
|
||||
#define rwbase_signal_pending_state(state, current) (0)
|
||||
|
||||
#define rwbase_pre_schedule()
|
||||
|
||||
#define rwbase_schedule() \
|
||||
schedule_rtlock()
|
||||
|
||||
#define rwbase_post_schedule()
|
||||
|
||||
#include "rwbase_rt.c"
|
||||
/*
|
||||
* The common functions which get wrapped into the rwlock API.
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/prandom.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ww_mutex.h>
|
||||
|
||||
@@ -386,6 +386,19 @@ struct stress {
|
||||
int nlocks;
|
||||
};
|
||||
|
||||
struct rnd_state rng;
|
||||
DEFINE_SPINLOCK(rng_lock);
|
||||
|
||||
static inline u32 prandom_u32_below(u32 ceil)
|
||||
{
|
||||
u32 ret;
|
||||
|
||||
spin_lock(&rng_lock);
|
||||
ret = prandom_u32_state(&rng) % ceil;
|
||||
spin_unlock(&rng_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int *get_random_order(int count)
|
||||
{
|
||||
int *order;
|
||||
@@ -399,7 +412,7 @@ static int *get_random_order(int count)
|
||||
order[n] = n;
|
||||
|
||||
for (n = count - 1; n > 1; n--) {
|
||||
r = get_random_u32_below(n + 1);
|
||||
r = prandom_u32_below(n + 1);
|
||||
if (r != n) {
|
||||
tmp = order[n];
|
||||
order[n] = order[r];
|
||||
@@ -452,21 +465,21 @@ retry:
|
||||
ww_mutex_unlock(&locks[order[n]]);
|
||||
|
||||
if (err == -EDEADLK) {
|
||||
ww_mutex_lock_slow(&locks[order[contended]], &ctx);
|
||||
goto retry;
|
||||
if (!time_after(jiffies, stress->timeout)) {
|
||||
ww_mutex_lock_slow(&locks[order[contended]], &ctx);
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
|
||||
ww_acquire_fini(&ctx);
|
||||
if (err) {
|
||||
pr_err_once("stress (%s) failed with %d\n",
|
||||
__func__, err);
|
||||
break;
|
||||
}
|
||||
|
||||
ww_acquire_fini(&ctx);
|
||||
} while (!time_after(jiffies, stress->timeout));
|
||||
|
||||
kfree(order);
|
||||
kfree(stress);
|
||||
}
|
||||
|
||||
struct reorder_lock {
|
||||
@@ -531,7 +544,6 @@ out:
|
||||
list_for_each_entry_safe(ll, ln, &locks, link)
|
||||
kfree(ll);
|
||||
kfree(order);
|
||||
kfree(stress);
|
||||
}
|
||||
|
||||
static void stress_one_work(struct work_struct *work)
|
||||
@@ -552,8 +564,6 @@ static void stress_one_work(struct work_struct *work)
|
||||
break;
|
||||
}
|
||||
} while (!time_after(jiffies, stress->timeout));
|
||||
|
||||
kfree(stress);
|
||||
}
|
||||
|
||||
#define STRESS_INORDER BIT(0)
|
||||
@@ -564,15 +574,24 @@ static void stress_one_work(struct work_struct *work)
|
||||
static int stress(int nlocks, int nthreads, unsigned int flags)
|
||||
{
|
||||
struct ww_mutex *locks;
|
||||
int n;
|
||||
struct stress *stress_array;
|
||||
int n, count;
|
||||
|
||||
locks = kmalloc_array(nlocks, sizeof(*locks), GFP_KERNEL);
|
||||
if (!locks)
|
||||
return -ENOMEM;
|
||||
|
||||
stress_array = kmalloc_array(nthreads, sizeof(*stress_array),
|
||||
GFP_KERNEL);
|
||||
if (!stress_array) {
|
||||
kfree(locks);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (n = 0; n < nlocks; n++)
|
||||
ww_mutex_init(&locks[n], &ww_class);
|
||||
|
||||
count = 0;
|
||||
for (n = 0; nthreads; n++) {
|
||||
struct stress *stress;
|
||||
void (*fn)(struct work_struct *work);
|
||||
@@ -596,9 +615,7 @@ static int stress(int nlocks, int nthreads, unsigned int flags)
|
||||
if (!fn)
|
||||
continue;
|
||||
|
||||
stress = kmalloc(sizeof(*stress), GFP_KERNEL);
|
||||
if (!stress)
|
||||
break;
|
||||
stress = &stress_array[count++];
|
||||
|
||||
INIT_WORK(&stress->work, fn);
|
||||
stress->locks = locks;
|
||||
@@ -613,6 +630,7 @@ static int stress(int nlocks, int nthreads, unsigned int flags)
|
||||
|
||||
for (n = 0; n < nlocks; n++)
|
||||
ww_mutex_destroy(&locks[n]);
|
||||
kfree(stress_array);
|
||||
kfree(locks);
|
||||
|
||||
return 0;
|
||||
@@ -625,6 +643,8 @@ static int __init test_ww_mutex_init(void)
|
||||
|
||||
printk(KERN_INFO "Beginning ww mutex selftests\n");
|
||||
|
||||
prandom_seed_state(&rng, get_random_u64());
|
||||
|
||||
wq = alloc_workqueue("test-ww_mutex", WQ_UNBOUND, 0);
|
||||
if (!wq)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -62,7 +62,7 @@ __ww_rt_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ww_ctx,
|
||||
}
|
||||
mutex_acquire_nest(&rtm->dep_map, 0, 0, nest_lock, ip);
|
||||
|
||||
if (likely(rt_mutex_cmpxchg_acquire(&rtm->rtmutex, NULL, current))) {
|
||||
if (likely(rt_mutex_try_acquire(&rtm->rtmutex))) {
|
||||
if (ww_ctx)
|
||||
ww_mutex_set_context_fastpath(lock, ww_ctx);
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user