mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 14:04:27 -04:00
maple_tree: add rcu locking check when LOCKDEP is enabled
Patch series "maple_tree: lock checking and clean ups", v3. In this series: 1. Try to detect lock issues A number of syzbot reports are incorrectly pointing to the mm exit as a source of the locking error. The first three patches attempt to help users detect errors in their locking - but they still have to use LOCKDEP. I guess it's still down to hope and prayers. 2. Documentation fixes The documentation was lacking clarity, there are updates to try and help the users, especially around the erase() cases. 3. Two benign issues The cyclic allocator may have a race, although no in-kernel user can hit it. The erase functions may cause allocation issues if used with the incorrect locking type, but none are present in-tree. 4. The erase gfp uses mas_erase() and mtree_erase() do not take a gfp argument. To improve reliability of the erase, the first attempt to allocate will be GFP_NOWAIT, followed by a retry (if necessary of GFP_KERNEL | GFP_NOFAIL. This will ensure the data is gone. I've updated the documentation to make it more clear as well. mas_store() is not addressed in the same way, but may need to be updated at a later date, but that may require changing callers so it is out of scope here. Beyond these goals there are some test fixes, some general speed-up patches targeting extra work and cycles, and dropping dead code. This patch (of 19): When CONFIG_LOCKDEP and CONFIG_RCU_STRICT_GRACE_PERIOD is enabled, check for rcu locking issues by recording the grace period in the maple state and checking the rcu window is still valid whenever the maple state is reused with a state that is not MA_START or MA_PAUSED. Link: https://lore.kernel.org/20260821192627.4085470-1-liam@infradead.org Link: https://lore.kernel.org/20260821192627.4085470-2-liam@infradead.org Signed-off-by: Liam R. Howlett (Oracle) <liam@infradead.org> Cc: Boqun Feng <boqun.feng@gmail.com> Cc: Breno Leitao <leitao@debian.org> Cc: Chris Mason <clm@meta.com> Cc: Chuck Lever <cel@kernel.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Jason Gunthorpe <jgg@ziepe.ca> Cc: Joe Perches <joe@perches.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Rik van Riel <riel@surriel.com> Cc: Waiman Long <longman@redhat.com> Cc: Will Deacon <will@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
committed by
Andrew Morton
parent
d16e52a9ba
commit
9e32ec53b1
@@ -484,6 +484,9 @@ struct ma_state {
|
||||
unsigned char mas_flags;
|
||||
unsigned char end; /* The end of the node */
|
||||
enum store_type store_type; /* The type of store needed for this operation */
|
||||
#if IS_ENABLED(CONFIG_LOCKDEP) && IS_ENABLED(CONFIG_RCU_STRICT_GRACE_PERIOD)
|
||||
unsigned long rcu_gp;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct ma_wr_state {
|
||||
|
||||
@@ -1148,6 +1148,42 @@ static inline void mas_free(struct ma_state *mas, struct maple_enode *used)
|
||||
ma_free_rcu(mte_to_node(used));
|
||||
}
|
||||
|
||||
void mas_lock_check(struct ma_state *mas)
|
||||
{
|
||||
|
||||
#if IS_ENABLED(CONFIG_LOCKDEP) && IS_ENABLED(CONFIG_RCU_STRICT_GRACE_PERIOD)
|
||||
if (!mas_is_active(mas))
|
||||
return;
|
||||
|
||||
if (!mt_locked(mas->tree)) {
|
||||
if (mt_in_rcu(mas->tree))
|
||||
WARN_ON_ONCE(poll_state_synchronize_rcu(mas->rcu_gp));
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void mas_init_lock_check(struct ma_state *mas)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_LOCKDEP) && IS_ENABLED(CONFIG_RCU_STRICT_GRACE_PERIOD)
|
||||
if (!mt_locked(mas->tree)) {
|
||||
if (mt_in_rcu(mas->tree))
|
||||
mas->rcu_gp = get_state_synchronize_rcu();
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
static void mas_may_init_lock_check(struct ma_state *mas)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_LOCKDEP) && IS_ENABLED(CONFIG_RCU_STRICT_GRACE_PERIOD)
|
||||
if (mas_is_start(mas) || mas_is_paused(mas))
|
||||
mas_init_lock_check(mas);
|
||||
else
|
||||
mas_lock_check(mas);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* mas_start() - Sets up maple state for operations.
|
||||
* @mas: The maple state.
|
||||
@@ -1166,6 +1202,7 @@ static inline struct maple_enode *mas_start(struct ma_state *mas)
|
||||
if (likely(mas_is_start(mas))) {
|
||||
struct maple_enode *root;
|
||||
|
||||
mas_init_lock_check(mas);
|
||||
mas->min = 0;
|
||||
mas->max = ULONG_MAX;
|
||||
|
||||
@@ -4355,6 +4392,7 @@ void *mas_walk(struct ma_state *mas)
|
||||
{
|
||||
void *entry;
|
||||
|
||||
mas_may_init_lock_check(mas);
|
||||
if (!mas_is_active(mas) && !mas_is_start(mas))
|
||||
mas->status = ma_start;
|
||||
retry:
|
||||
@@ -4992,6 +5030,7 @@ static void mas_may_activate(struct ma_state *mas)
|
||||
mas->status = ma_start;
|
||||
} else {
|
||||
mas->status = ma_active;
|
||||
mas_lock_check(mas);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5069,6 +5108,7 @@ void *mas_next(struct ma_state *mas, unsigned long max)
|
||||
{
|
||||
void *entry = NULL;
|
||||
|
||||
mas_may_init_lock_check(mas);
|
||||
if (mas_next_setup(mas, max, &entry))
|
||||
return entry;
|
||||
|
||||
@@ -5092,6 +5132,7 @@ void *mas_next_range(struct ma_state *mas, unsigned long max)
|
||||
{
|
||||
void *entry = NULL;
|
||||
|
||||
mas_may_init_lock_check(mas);
|
||||
if (mas_next_setup(mas, max, &entry))
|
||||
return entry;
|
||||
|
||||
@@ -5200,6 +5241,7 @@ void *mas_prev(struct ma_state *mas, unsigned long min)
|
||||
{
|
||||
void *entry = NULL;
|
||||
|
||||
mas_may_init_lock_check(mas);
|
||||
if (mas_prev_setup(mas, min, &entry))
|
||||
return entry;
|
||||
|
||||
@@ -5223,6 +5265,7 @@ void *mas_prev_range(struct ma_state *mas, unsigned long min)
|
||||
{
|
||||
void *entry = NULL;
|
||||
|
||||
mas_may_init_lock_check(mas);
|
||||
if (mas_prev_setup(mas, min, &entry))
|
||||
return entry;
|
||||
|
||||
@@ -5269,6 +5312,7 @@ EXPORT_SYMBOL_GPL(mt_prev);
|
||||
*/
|
||||
void mas_pause(struct ma_state *mas)
|
||||
{
|
||||
mas_lock_check(mas);
|
||||
mas->status = ma_pause;
|
||||
mas->node = NULL;
|
||||
}
|
||||
@@ -5377,6 +5421,7 @@ void *mas_find(struct ma_state *mas, unsigned long max)
|
||||
{
|
||||
void *entry = NULL;
|
||||
|
||||
mas_may_init_lock_check(mas);
|
||||
if (mas_find_setup(mas, max, &entry))
|
||||
return entry;
|
||||
|
||||
@@ -5404,6 +5449,7 @@ void *mas_find_range(struct ma_state *mas, unsigned long max)
|
||||
{
|
||||
void *entry = NULL;
|
||||
|
||||
mas_may_init_lock_check(mas);
|
||||
if (mas_find_setup(mas, max, &entry))
|
||||
return entry;
|
||||
|
||||
@@ -5516,6 +5562,7 @@ void *mas_find_rev(struct ma_state *mas, unsigned long min)
|
||||
{
|
||||
void *entry = NULL;
|
||||
|
||||
mas_may_init_lock_check(mas);
|
||||
if (mas_find_rev_setup(mas, min, &entry))
|
||||
return entry;
|
||||
|
||||
@@ -5542,6 +5589,7 @@ void *mas_find_range_rev(struct ma_state *mas, unsigned long min)
|
||||
{
|
||||
void *entry = NULL;
|
||||
|
||||
mas_may_init_lock_check(mas);
|
||||
if (mas_find_rev_setup(mas, min, &entry))
|
||||
return entry;
|
||||
|
||||
@@ -5618,7 +5666,7 @@ bool mas_nomem(struct ma_state *mas, gfp_t gfp)
|
||||
if (!mas->sheaf && !mas->alloc)
|
||||
return false;
|
||||
|
||||
mas->status = ma_start;
|
||||
mas_reset(mas);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user