Merge tag 'locking-core-2022-03-21' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull locking updates from Ingo Molnar:
"Changes in this cycle were:
Bitops & cpumask:
- Always inline various generic helpers, to improve code generation,
but also for instrumentation, found by noinstr validation.
- Add a x86-specific cpumask_clear_cpu() helper to improve code
generation
Atomics:
- Fix atomic64_{read_acquire,set_release} fallbacks
Lockdep:
- Fix /proc/lockdep output loop iteration for classes
- Fix /proc/lockdep potential access to invalid memory
- Add Mark Rutland as reviewer for atomic primitives
- Minor cleanups
Jump labels:
- Clean up the code a bit
Misc:
- Add __sched annotations to percpu rwsem primitives
- Enable RT_MUTEXES on PREEMPT_RT by default
- Stray v8086_mode() inlining fix, result of noinstr objtool
validation"
* tag 'locking-core-2022-03-21' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
jump_label: Refactor #ifdef of struct static_key
jump_label: Avoid unneeded casts in STATIC_KEY_INIT_{TRUE,FALSE}
locking/lockdep: Iterate lock_classes directly when reading lockdep files
x86/ptrace: Always inline v8086_mode() for instrumentation
cpumask: Add a x86-specific cpumask_clear_cpu() helper
locking: Enable RT_MUTEXES by default on PREEMPT_RT.
locking/local_lock: Make the empty local_lock_*() function a macro.
atomics: Fix atomic64_{read_acquire,set_release} fallbacks
locking: Add missing __sched attributes
cpumask: Always inline helpers which use bit manipulation functions
asm-generic/bitops: Always inline all bit manipulation helpers
locking/lockdep: Avoid potential access of invalid memory in lock_class
lockdep: Use memset_startat() helper in reinit_class()
MAINTAINERS: add myself as reviewer for atomics
This commit is contained in:
+25
-18
@@ -183,11 +183,9 @@ static DECLARE_BITMAP(list_entries_in_use, MAX_LOCKDEP_ENTRIES);
|
||||
static struct hlist_head lock_keys_hash[KEYHASH_SIZE];
|
||||
unsigned long nr_lock_classes;
|
||||
unsigned long nr_zapped_classes;
|
||||
#ifndef CONFIG_DEBUG_LOCKDEP
|
||||
static
|
||||
#endif
|
||||
unsigned long max_lock_class_idx;
|
||||
struct lock_class lock_classes[MAX_LOCKDEP_KEYS];
|
||||
static DECLARE_BITMAP(lock_classes_in_use, MAX_LOCKDEP_KEYS);
|
||||
DECLARE_BITMAP(lock_classes_in_use, MAX_LOCKDEP_KEYS);
|
||||
|
||||
static inline struct lock_class *hlock_class(struct held_lock *hlock)
|
||||
{
|
||||
@@ -338,7 +336,7 @@ static inline void lock_release_holdtime(struct held_lock *hlock)
|
||||
* elements. These elements are linked together by the lock_entry member in
|
||||
* struct lock_class.
|
||||
*/
|
||||
LIST_HEAD(all_lock_classes);
|
||||
static LIST_HEAD(all_lock_classes);
|
||||
static LIST_HEAD(free_lock_classes);
|
||||
|
||||
/**
|
||||
@@ -1252,6 +1250,7 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force)
|
||||
struct lockdep_subclass_key *key;
|
||||
struct hlist_head *hash_head;
|
||||
struct lock_class *class;
|
||||
int idx;
|
||||
|
||||
DEBUG_LOCKS_WARN_ON(!irqs_disabled());
|
||||
|
||||
@@ -1317,6 +1316,9 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force)
|
||||
* of classes.
|
||||
*/
|
||||
list_move_tail(&class->lock_entry, &all_lock_classes);
|
||||
idx = class - lock_classes;
|
||||
if (idx > max_lock_class_idx)
|
||||
max_lock_class_idx = idx;
|
||||
|
||||
if (verbose(class)) {
|
||||
graph_unlock();
|
||||
@@ -6000,6 +6002,8 @@ static void zap_class(struct pending_free *pf, struct lock_class *class)
|
||||
WRITE_ONCE(class->name, NULL);
|
||||
nr_lock_classes--;
|
||||
__clear_bit(class - lock_classes, lock_classes_in_use);
|
||||
if (class - lock_classes == max_lock_class_idx)
|
||||
max_lock_class_idx--;
|
||||
} else {
|
||||
WARN_ONCE(true, "%s() failed for class %s\n", __func__,
|
||||
class->name);
|
||||
@@ -6011,13 +6015,10 @@ static void zap_class(struct pending_free *pf, struct lock_class *class)
|
||||
|
||||
static void reinit_class(struct lock_class *class)
|
||||
{
|
||||
void *const p = class;
|
||||
const unsigned int offset = offsetof(struct lock_class, key);
|
||||
|
||||
WARN_ON_ONCE(!class->lock_entry.next);
|
||||
WARN_ON_ONCE(!list_empty(&class->locks_after));
|
||||
WARN_ON_ONCE(!list_empty(&class->locks_before));
|
||||
memset(p + offset, 0, sizeof(*class) - offset);
|
||||
memset_startat(class, 0, key);
|
||||
WARN_ON_ONCE(!class->lock_entry.next);
|
||||
WARN_ON_ONCE(!list_empty(&class->locks_after));
|
||||
WARN_ON_ONCE(!list_empty(&class->locks_before));
|
||||
@@ -6290,7 +6291,13 @@ void lockdep_reset_lock(struct lockdep_map *lock)
|
||||
lockdep_reset_lock_reg(lock);
|
||||
}
|
||||
|
||||
/* Unregister a dynamically allocated key. */
|
||||
/*
|
||||
* Unregister a dynamically allocated key.
|
||||
*
|
||||
* Unlike lockdep_register_key(), a search is always done to find a matching
|
||||
* key irrespective of debug_locks to avoid potential invalid access to freed
|
||||
* memory in lock_class entry.
|
||||
*/
|
||||
void lockdep_unregister_key(struct lock_class_key *key)
|
||||
{
|
||||
struct hlist_head *hash_head = keyhashentry(key);
|
||||
@@ -6305,10 +6312,8 @@ void lockdep_unregister_key(struct lock_class_key *key)
|
||||
return;
|
||||
|
||||
raw_local_irq_save(flags);
|
||||
if (!graph_lock())
|
||||
goto out_irq;
|
||||
lockdep_lock();
|
||||
|
||||
pf = get_pending_free();
|
||||
hlist_for_each_entry_rcu(k, hash_head, hash_entry) {
|
||||
if (k == key) {
|
||||
hlist_del_rcu(&k->hash_entry);
|
||||
@@ -6316,11 +6321,13 @@ void lockdep_unregister_key(struct lock_class_key *key)
|
||||
break;
|
||||
}
|
||||
}
|
||||
WARN_ON_ONCE(!found);
|
||||
__lockdep_free_key_range(pf, key, 1);
|
||||
call_rcu_zapped(pf);
|
||||
graph_unlock();
|
||||
out_irq:
|
||||
WARN_ON_ONCE(!found && debug_locks);
|
||||
if (found) {
|
||||
pf = get_pending_free();
|
||||
__lockdep_free_key_range(pf, key, 1);
|
||||
call_rcu_zapped(pf);
|
||||
}
|
||||
lockdep_unlock();
|
||||
raw_local_irq_restore(flags);
|
||||
|
||||
/* Wait until is_dynamic_key() has finished accessing k->hash_entry. */
|
||||
|
||||
@@ -121,7 +121,6 @@ static const unsigned long LOCKF_USED_IN_IRQ_READ =
|
||||
|
||||
#define MAX_LOCKDEP_CHAIN_HLOCKS (MAX_LOCKDEP_CHAINS*5)
|
||||
|
||||
extern struct list_head all_lock_classes;
|
||||
extern struct lock_chain lock_chains[];
|
||||
|
||||
#define LOCK_USAGE_CHARS (2*XXX_LOCK_USAGE_STATES + 1)
|
||||
@@ -151,6 +150,10 @@ extern unsigned int nr_large_chain_blocks;
|
||||
|
||||
extern unsigned int max_lockdep_depth;
|
||||
extern unsigned int max_bfs_queue_depth;
|
||||
extern unsigned long max_lock_class_idx;
|
||||
|
||||
extern struct lock_class lock_classes[MAX_LOCKDEP_KEYS];
|
||||
extern unsigned long lock_classes_in_use[];
|
||||
|
||||
#ifdef CONFIG_PROVE_LOCKING
|
||||
extern unsigned long lockdep_count_forward_deps(struct lock_class *);
|
||||
@@ -205,7 +208,6 @@ struct lockdep_stats {
|
||||
};
|
||||
|
||||
DECLARE_PER_CPU(struct lockdep_stats, lockdep_stats);
|
||||
extern struct lock_class lock_classes[MAX_LOCKDEP_KEYS];
|
||||
|
||||
#define __debug_atomic_inc(ptr) \
|
||||
this_cpu_inc(lockdep_stats.ptr);
|
||||
|
||||
@@ -24,14 +24,33 @@
|
||||
|
||||
#include "lockdep_internals.h"
|
||||
|
||||
/*
|
||||
* Since iteration of lock_classes is done without holding the lockdep lock,
|
||||
* it is not safe to iterate all_lock_classes list directly as the iteration
|
||||
* may branch off to free_lock_classes or the zapped list. Iteration is done
|
||||
* directly on the lock_classes array by checking the lock_classes_in_use
|
||||
* bitmap and max_lock_class_idx.
|
||||
*/
|
||||
#define iterate_lock_classes(idx, class) \
|
||||
for (idx = 0, class = lock_classes; idx <= max_lock_class_idx; \
|
||||
idx++, class++)
|
||||
|
||||
static void *l_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
{
|
||||
return seq_list_next(v, &all_lock_classes, pos);
|
||||
struct lock_class *class = v;
|
||||
|
||||
++class;
|
||||
*pos = class - lock_classes;
|
||||
return (*pos > max_lock_class_idx) ? NULL : class;
|
||||
}
|
||||
|
||||
static void *l_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
return seq_list_start_head(&all_lock_classes, *pos);
|
||||
unsigned long idx = *pos;
|
||||
|
||||
if (idx > max_lock_class_idx)
|
||||
return NULL;
|
||||
return lock_classes + idx;
|
||||
}
|
||||
|
||||
static void l_stop(struct seq_file *m, void *v)
|
||||
@@ -57,14 +76,16 @@ static void print_name(struct seq_file *m, struct lock_class *class)
|
||||
|
||||
static int l_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct lock_class *class = list_entry(v, struct lock_class, lock_entry);
|
||||
struct lock_class *class = v;
|
||||
struct lock_list *entry;
|
||||
char usage[LOCK_USAGE_CHARS];
|
||||
int idx = class - lock_classes;
|
||||
|
||||
if (v == &all_lock_classes) {
|
||||
if (v == lock_classes)
|
||||
seq_printf(m, "all lock classes:\n");
|
||||
|
||||
if (!test_bit(idx, lock_classes_in_use))
|
||||
return 0;
|
||||
}
|
||||
|
||||
seq_printf(m, "%p", class->key);
|
||||
#ifdef CONFIG_DEBUG_LOCKDEP
|
||||
@@ -220,8 +241,11 @@ static int lockdep_stats_show(struct seq_file *m, void *v)
|
||||
|
||||
#ifdef CONFIG_PROVE_LOCKING
|
||||
struct lock_class *class;
|
||||
unsigned long idx;
|
||||
|
||||
list_for_each_entry(class, &all_lock_classes, lock_entry) {
|
||||
iterate_lock_classes(idx, class) {
|
||||
if (!test_bit(idx, lock_classes_in_use))
|
||||
continue;
|
||||
|
||||
if (class->usage_mask == 0)
|
||||
nr_unused++;
|
||||
@@ -254,6 +278,7 @@ static int lockdep_stats_show(struct seq_file *m, void *v)
|
||||
|
||||
sum_forward_deps += lockdep_count_forward_deps(class);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCKDEP
|
||||
DEBUG_LOCKS_WARN_ON(debug_atomic_read(nr_unused_locks) != nr_unused);
|
||||
#endif
|
||||
@@ -345,6 +370,8 @@ static int lockdep_stats_show(struct seq_file *m, void *v)
|
||||
seq_printf(m, " max bfs queue depth: %11u\n",
|
||||
max_bfs_queue_depth);
|
||||
#endif
|
||||
seq_printf(m, " max lock class index: %11lu\n",
|
||||
max_lock_class_idx);
|
||||
lockdep_stats_debug_show(m);
|
||||
seq_printf(m, " debug_locks: %11u\n",
|
||||
debug_locks);
|
||||
@@ -622,12 +649,16 @@ static int lock_stat_open(struct inode *inode, struct file *file)
|
||||
if (!res) {
|
||||
struct lock_stat_data *iter = data->stats;
|
||||
struct seq_file *m = file->private_data;
|
||||
unsigned long idx;
|
||||
|
||||
list_for_each_entry(class, &all_lock_classes, lock_entry) {
|
||||
iterate_lock_classes(idx, class) {
|
||||
if (!test_bit(idx, lock_classes_in_use))
|
||||
continue;
|
||||
iter->class = class;
|
||||
iter->stats = lock_stats(class);
|
||||
iter++;
|
||||
}
|
||||
|
||||
data->iter_end = iter;
|
||||
|
||||
sort(data->stats, data->iter_end - data->stats,
|
||||
@@ -645,6 +676,7 @@ static ssize_t lock_stat_write(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct lock_class *class;
|
||||
unsigned long idx;
|
||||
char c;
|
||||
|
||||
if (count) {
|
||||
@@ -654,8 +686,11 @@ static ssize_t lock_stat_write(struct file *file, const char __user *buf,
|
||||
if (c != '0')
|
||||
return count;
|
||||
|
||||
list_for_each_entry(class, &all_lock_classes, lock_entry)
|
||||
iterate_lock_classes(idx, class) {
|
||||
if (!test_bit(idx, lock_classes_in_use))
|
||||
continue;
|
||||
clear_lock_stats(class);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/task.h>
|
||||
#include <linux/sched/debug.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
int __percpu_init_rwsem(struct percpu_rw_semaphore *sem,
|
||||
@@ -162,7 +163,7 @@ static void percpu_rwsem_wait(struct percpu_rw_semaphore *sem, bool reader)
|
||||
__set_current_state(TASK_RUNNING);
|
||||
}
|
||||
|
||||
bool __percpu_down_read(struct percpu_rw_semaphore *sem, bool try)
|
||||
bool __sched __percpu_down_read(struct percpu_rw_semaphore *sem, bool try)
|
||||
{
|
||||
if (__percpu_down_read_trylock(sem))
|
||||
return true;
|
||||
@@ -211,7 +212,7 @@ static bool readers_active_check(struct percpu_rw_semaphore *sem)
|
||||
return true;
|
||||
}
|
||||
|
||||
void percpu_down_write(struct percpu_rw_semaphore *sem)
|
||||
void __sched percpu_down_write(struct percpu_rw_semaphore *sem)
|
||||
{
|
||||
might_sleep();
|
||||
rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_);
|
||||
|
||||
@@ -1048,7 +1048,7 @@ out_nolock:
|
||||
/*
|
||||
* Wait until we successfully acquire the write lock
|
||||
*/
|
||||
static struct rw_semaphore *
|
||||
static struct rw_semaphore __sched *
|
||||
rwsem_down_write_slowpath(struct rw_semaphore *sem, int state)
|
||||
{
|
||||
long count;
|
||||
|
||||
Reference in New Issue
Block a user