Merge tag 'locking-urgent-2024-09-29' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull locking updates from Ingo Molnar:
"lockdep:
- Fix potential deadlock between lockdep and RCU (Zhiguo Niu)
- Use str_plural() to address Coccinelle warning (Thorsten Blum)
- Add debuggability enhancement (Luis Claudio R. Goncalves)
static keys & calls:
- Fix static_key_slow_dec() yet again (Peter Zijlstra)
- Handle module init failure correctly in static_call_del_module()
(Thomas Gleixner)
- Replace pointless WARN_ON() in static_call_module_notify() (Thomas
Gleixner)
<linux/cleanup.h>:
- Add usage and style documentation (Dan Williams)
rwsems:
- Move is_rwsem_reader_owned() and rwsem_owner() under
CONFIG_DEBUG_RWSEMS (Waiman Long)
atomic ops, x86:
- Redeclare x86_32 arch_atomic64_{add,sub}() as void (Uros Bizjak)
- Introduce the read64_nonatomic macro to x86_32 with cx8 (Uros
Bizjak)"
Signed-off-by: Ingo Molnar <mingo@kernel.org>
* tag 'locking-urgent-2024-09-29' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
locking/rwsem: Move is_rwsem_reader_owned() and rwsem_owner() under CONFIG_DEBUG_RWSEMS
jump_label: Fix static_key_slow_dec() yet again
static_call: Replace pointless WARN_ON() in static_call_module_notify()
static_call: Handle module init failure correctly in static_call_del_module()
locking/lockdep: Simplify character output in seq_line()
lockdep: fix deadlock issue between lockdep and rcu
lockdep: Use str_plural() to fix Coccinelle warning
cleanup: Add usage and style documentation
lockdep: suggest the fix for "lockdep bfs error:-1" on print_bfs_bug
locking/atomic/x86: Redeclare x86_32 arch_atomic64_{add,sub}() as void
locking/atomic/x86: Introduce the read64_nonatomic macro to x86_32 with cx8
This commit is contained in:
+27
-7
@@ -168,7 +168,7 @@ bool static_key_slow_inc_cpuslocked(struct static_key *key)
|
||||
jump_label_update(key);
|
||||
/*
|
||||
* Ensure that when static_key_fast_inc_not_disabled() or
|
||||
* static_key_slow_try_dec() observe the positive value,
|
||||
* static_key_dec_not_one() observe the positive value,
|
||||
* they must also observe all the text changes.
|
||||
*/
|
||||
atomic_set_release(&key->enabled, 1);
|
||||
@@ -250,7 +250,7 @@ void static_key_disable(struct static_key *key)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(static_key_disable);
|
||||
|
||||
static bool static_key_slow_try_dec(struct static_key *key)
|
||||
static bool static_key_dec_not_one(struct static_key *key)
|
||||
{
|
||||
int v;
|
||||
|
||||
@@ -274,6 +274,14 @@ static bool static_key_slow_try_dec(struct static_key *key)
|
||||
* enabled. This suggests an ordering problem on the user side.
|
||||
*/
|
||||
WARN_ON_ONCE(v < 0);
|
||||
|
||||
/*
|
||||
* Warn about underflow, and lie about success in an attempt to
|
||||
* not make things worse.
|
||||
*/
|
||||
if (WARN_ON_ONCE(v == 0))
|
||||
return true;
|
||||
|
||||
if (v <= 1)
|
||||
return false;
|
||||
} while (!likely(atomic_try_cmpxchg(&key->enabled, &v, v - 1)));
|
||||
@@ -284,15 +292,27 @@ static bool static_key_slow_try_dec(struct static_key *key)
|
||||
static void __static_key_slow_dec_cpuslocked(struct static_key *key)
|
||||
{
|
||||
lockdep_assert_cpus_held();
|
||||
int val;
|
||||
|
||||
if (static_key_slow_try_dec(key))
|
||||
if (static_key_dec_not_one(key))
|
||||
return;
|
||||
|
||||
guard(mutex)(&jump_label_mutex);
|
||||
if (atomic_cmpxchg(&key->enabled, 1, 0) == 1)
|
||||
val = atomic_read(&key->enabled);
|
||||
/*
|
||||
* It should be impossible to observe -1 with jump_label_mutex held,
|
||||
* see static_key_slow_inc_cpuslocked().
|
||||
*/
|
||||
if (WARN_ON_ONCE(val == -1))
|
||||
return;
|
||||
/*
|
||||
* Cannot already be 0, something went sideways.
|
||||
*/
|
||||
if (WARN_ON_ONCE(val == 0))
|
||||
return;
|
||||
|
||||
if (atomic_dec_and_test(&key->enabled))
|
||||
jump_label_update(key);
|
||||
else
|
||||
WARN_ON_ONCE(!static_key_slow_try_dec(key));
|
||||
}
|
||||
|
||||
static void __static_key_slow_dec(struct static_key *key)
|
||||
@@ -329,7 +349,7 @@ void __static_key_slow_dec_deferred(struct static_key *key,
|
||||
{
|
||||
STATIC_KEY_CHECK_USE(key);
|
||||
|
||||
if (static_key_slow_try_dec(key))
|
||||
if (static_key_dec_not_one(key))
|
||||
return;
|
||||
|
||||
schedule_delayed_work(work, timeout);
|
||||
|
||||
+37
-18
@@ -788,7 +788,7 @@ static void lockdep_print_held_locks(struct task_struct *p)
|
||||
printk("no locks held by %s/%d.\n", p->comm, task_pid_nr(p));
|
||||
else
|
||||
printk("%d lock%s held by %s/%d:\n", depth,
|
||||
depth > 1 ? "s" : "", p->comm, task_pid_nr(p));
|
||||
str_plural(depth), p->comm, task_pid_nr(p));
|
||||
/*
|
||||
* It's not reliable to print a task's held locks if it's not sleeping
|
||||
* and it's not the current task.
|
||||
@@ -2084,6 +2084,9 @@ static noinline void print_bfs_bug(int ret)
|
||||
/*
|
||||
* Breadth-first-search failed, graph got corrupted?
|
||||
*/
|
||||
if (ret == BFS_EQUEUEFULL)
|
||||
pr_warn("Increase LOCKDEP_CIRCULAR_QUEUE_BITS to avoid this warning:\n");
|
||||
|
||||
WARN(1, "lockdep bfs error:%d\n", ret);
|
||||
}
|
||||
|
||||
@@ -6263,25 +6266,27 @@ static struct pending_free *get_pending_free(void)
|
||||
static void free_zapped_rcu(struct rcu_head *cb);
|
||||
|
||||
/*
|
||||
* Schedule an RCU callback if no RCU callback is pending. Must be called with
|
||||
* the graph lock held.
|
||||
*/
|
||||
static void call_rcu_zapped(struct pending_free *pf)
|
||||
* See if we need to queue an RCU callback, must called with
|
||||
* the lockdep lock held, returns false if either we don't have
|
||||
* any pending free or the callback is already scheduled.
|
||||
* Otherwise, a call_rcu() must follow this function call.
|
||||
*/
|
||||
static bool prepare_call_rcu_zapped(struct pending_free *pf)
|
||||
{
|
||||
WARN_ON_ONCE(inside_selftest());
|
||||
|
||||
if (list_empty(&pf->zapped))
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (delayed_free.scheduled)
|
||||
return;
|
||||
return false;
|
||||
|
||||
delayed_free.scheduled = true;
|
||||
|
||||
WARN_ON_ONCE(delayed_free.pf + delayed_free.index != pf);
|
||||
delayed_free.index ^= 1;
|
||||
|
||||
call_rcu(&delayed_free.rcu_head, free_zapped_rcu);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* The caller must hold the graph lock. May be called from RCU context. */
|
||||
@@ -6307,6 +6312,7 @@ static void free_zapped_rcu(struct rcu_head *ch)
|
||||
{
|
||||
struct pending_free *pf;
|
||||
unsigned long flags;
|
||||
bool need_callback;
|
||||
|
||||
if (WARN_ON_ONCE(ch != &delayed_free.rcu_head))
|
||||
return;
|
||||
@@ -6318,14 +6324,18 @@ static void free_zapped_rcu(struct rcu_head *ch)
|
||||
pf = delayed_free.pf + (delayed_free.index ^ 1);
|
||||
__free_zapped_classes(pf);
|
||||
delayed_free.scheduled = false;
|
||||
|
||||
/*
|
||||
* If there's anything on the open list, close and start a new callback.
|
||||
*/
|
||||
call_rcu_zapped(delayed_free.pf + delayed_free.index);
|
||||
|
||||
need_callback =
|
||||
prepare_call_rcu_zapped(delayed_free.pf + delayed_free.index);
|
||||
lockdep_unlock();
|
||||
raw_local_irq_restore(flags);
|
||||
|
||||
/*
|
||||
* If there's pending free and its callback has not been scheduled,
|
||||
* queue an RCU callback.
|
||||
*/
|
||||
if (need_callback)
|
||||
call_rcu(&delayed_free.rcu_head, free_zapped_rcu);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -6365,6 +6375,7 @@ static void lockdep_free_key_range_reg(void *start, unsigned long size)
|
||||
{
|
||||
struct pending_free *pf;
|
||||
unsigned long flags;
|
||||
bool need_callback;
|
||||
|
||||
init_data_structures_once();
|
||||
|
||||
@@ -6372,10 +6383,11 @@ static void lockdep_free_key_range_reg(void *start, unsigned long size)
|
||||
lockdep_lock();
|
||||
pf = get_pending_free();
|
||||
__lockdep_free_key_range(pf, start, size);
|
||||
call_rcu_zapped(pf);
|
||||
need_callback = prepare_call_rcu_zapped(pf);
|
||||
lockdep_unlock();
|
||||
raw_local_irq_restore(flags);
|
||||
|
||||
if (need_callback)
|
||||
call_rcu(&delayed_free.rcu_head, free_zapped_rcu);
|
||||
/*
|
||||
* Wait for any possible iterators from look_up_lock_class() to pass
|
||||
* before continuing to free the memory they refer to.
|
||||
@@ -6469,6 +6481,7 @@ static void lockdep_reset_lock_reg(struct lockdep_map *lock)
|
||||
struct pending_free *pf;
|
||||
unsigned long flags;
|
||||
int locked;
|
||||
bool need_callback = false;
|
||||
|
||||
raw_local_irq_save(flags);
|
||||
locked = graph_lock();
|
||||
@@ -6477,11 +6490,13 @@ static void lockdep_reset_lock_reg(struct lockdep_map *lock)
|
||||
|
||||
pf = get_pending_free();
|
||||
__lockdep_reset_lock(pf, lock);
|
||||
call_rcu_zapped(pf);
|
||||
need_callback = prepare_call_rcu_zapped(pf);
|
||||
|
||||
graph_unlock();
|
||||
out_irq:
|
||||
raw_local_irq_restore(flags);
|
||||
if (need_callback)
|
||||
call_rcu(&delayed_free.rcu_head, free_zapped_rcu);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -6525,6 +6540,7 @@ void lockdep_unregister_key(struct lock_class_key *key)
|
||||
struct pending_free *pf;
|
||||
unsigned long flags;
|
||||
bool found = false;
|
||||
bool need_callback = false;
|
||||
|
||||
might_sleep();
|
||||
|
||||
@@ -6545,11 +6561,14 @@ void lockdep_unregister_key(struct lock_class_key *key)
|
||||
if (found) {
|
||||
pf = get_pending_free();
|
||||
__lockdep_free_key_range(pf, key, 1);
|
||||
call_rcu_zapped(pf);
|
||||
need_callback = prepare_call_rcu_zapped(pf);
|
||||
}
|
||||
lockdep_unlock();
|
||||
raw_local_irq_restore(flags);
|
||||
|
||||
if (need_callback)
|
||||
call_rcu(&delayed_free.rcu_head, free_zapped_rcu);
|
||||
|
||||
/* Wait until is_dynamic_key() has finished accessing k->hash_entry. */
|
||||
synchronize_rcu();
|
||||
}
|
||||
|
||||
@@ -424,7 +424,7 @@ static void seq_line(struct seq_file *m, char c, int offset, int length)
|
||||
for (i = 0; i < offset; i++)
|
||||
seq_puts(m, " ");
|
||||
for (i = 0; i < length; i++)
|
||||
seq_printf(m, "%c", c);
|
||||
seq_putc(m, c);
|
||||
seq_puts(m, "\n");
|
||||
}
|
||||
|
||||
|
||||
+10
-12
@@ -181,12 +181,21 @@ static inline void rwsem_set_reader_owned(struct rw_semaphore *sem)
|
||||
__rwsem_set_reader_owned(sem, current);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_RWSEMS
|
||||
/*
|
||||
* Return just the real task structure pointer of the owner
|
||||
*/
|
||||
static inline struct task_struct *rwsem_owner(struct rw_semaphore *sem)
|
||||
{
|
||||
return (struct task_struct *)
|
||||
(atomic_long_read(&sem->owner) & ~RWSEM_OWNER_FLAGS_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if the rwsem is owned by a reader.
|
||||
*/
|
||||
static inline bool is_rwsem_reader_owned(struct rw_semaphore *sem)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_RWSEMS
|
||||
/*
|
||||
* Check the count to see if it is write-locked.
|
||||
*/
|
||||
@@ -194,11 +203,9 @@ static inline bool is_rwsem_reader_owned(struct rw_semaphore *sem)
|
||||
|
||||
if (count & RWSEM_WRITER_MASK)
|
||||
return false;
|
||||
#endif
|
||||
return rwsem_test_oflags(sem, RWSEM_READER_OWNED);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_RWSEMS
|
||||
/*
|
||||
* With CONFIG_DEBUG_RWSEMS configured, it will make sure that if there
|
||||
* is a task pointer in owner of a reader-owned rwsem, it will be the
|
||||
@@ -265,15 +272,6 @@ static inline bool rwsem_write_trylock(struct rw_semaphore *sem)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return just the real task structure pointer of the owner
|
||||
*/
|
||||
static inline struct task_struct *rwsem_owner(struct rw_semaphore *sem)
|
||||
{
|
||||
return (struct task_struct *)
|
||||
(atomic_long_read(&sem->owner) & ~RWSEM_OWNER_FLAGS_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the real task structure pointer of the owner and the embedded
|
||||
* flags in the owner. pflags must be non-NULL.
|
||||
|
||||
@@ -411,6 +411,17 @@ static void static_call_del_module(struct module *mod)
|
||||
|
||||
for (site = start; site < stop; site++) {
|
||||
key = static_call_key(site);
|
||||
|
||||
/*
|
||||
* If the key was not updated due to a memory allocation
|
||||
* failure in __static_call_init() then treating key::sites
|
||||
* as key::mods in the code below would cause random memory
|
||||
* access and #GP. In that case all subsequent sites have
|
||||
* not been touched either, so stop iterating.
|
||||
*/
|
||||
if (!static_call_key_has_mods(key))
|
||||
break;
|
||||
|
||||
if (key == prev_key)
|
||||
continue;
|
||||
|
||||
@@ -442,7 +453,7 @@ static int static_call_module_notify(struct notifier_block *nb,
|
||||
case MODULE_STATE_COMING:
|
||||
ret = static_call_add_module(mod);
|
||||
if (ret) {
|
||||
WARN(1, "Failed to allocate memory for static calls");
|
||||
pr_warn("Failed to allocate memory for static calls\n");
|
||||
static_call_del_module(mod);
|
||||
}
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user