Silence lockdep false positive for expedited grace periods
PATCH 1/1 Silence lockdep false positive for expedited grace periods
In a CONFIG_PREEMPT=y kernel, synchronize_rcu_expedited() acquires the ->exp_funnel_mutex in rcu_preempt_state, then invokes synchronize_sched_expedited, which acquires the ->exp_funnel_mutex in rcu_sched_state. There can be no deadlock because rcu_preempt_state ->exp_funnel_mutex acquisition always precedes that of rcu_sched_state. But lockdep does not know that, so it gives false-positive splats.
This commit therefore associates a separate lock_class_key structure with the rcu_sched_state structure's ->exp_funnel_mutex, allowing lockdep to see the lock ordering, avoiding the false positives.
Later changes break the dependency between synchronize_rcu_expedited() and synchronize_sched_expedited(), after which this commit will be reverted. However, those later changes are not ready for mainline, hence this commit.
kernel/rcu/tree.c +10 -2
@@ -71,6 +71,7 @@ MODULE_ALIAS("rcutree");static struct lock_class_key rcu_node_class[RCU_NUM_LVLS ];static struct lock_class_key rcu_fqs_class[RCU_NUM_LVLS ];static struct lock_class_key rcu_exp_class[RCU_NUM_LVLS ]; +static struct lock_class_key rcu_exp_sched_class[RCU_NUM_LVLS ];/* * In order to export the rcu_state name to the tracing tools, it
@@ -4049,6 +4050,7 @@ static void __init rcu_init_one(struct rcu_state *rsp,static const char *const buf[] =RCU_NODE_NAME_INIT ;static const char *const fqs[] =RCU_FQS_NAME_INIT ;static const char *const exp[] =RCU_EXP_NAME_INIT ; +static const char *const exp_sched[] =RCU_EXP_SCHED_NAME_INIT ;static u8 fl_mask =0x1 ;int levelcnt[RCU_NUM_LVLS ];/* # nodes in each level. */
@@ -4108,8 +4110,14 @@ static void __init rcu_init_one(struct rcu_state *rsp,INIT_LIST_HEAD (&rnp->blkd_tasks);rcu_init_one_nocb (rnp);mutex_init (&rnp->exp_funnel_mutex); -lockdep_set_class_and_name (&rnp->exp_funnel_mutex, - &rcu_exp_class[i], exp[i]); +if (rsp == &rcu_sched_state) +lockdep_set_class_and_name ( + &rnp->exp_funnel_mutex, + &rcu_exp_sched_class[i], exp_sched[i]); +else +lockdep_set_class_and_name ( + &rnp->exp_funnel_mutex, + &rcu_exp_class[i], exp[i]); } }
kernel/rcu/tree.h +8 -0
@@ -70,6 +70,8 @@# define RCU_NODE_NAME_INIT { "rcu_node_0" }# define RCU_FQS_NAME_INIT { "rcu_node_fqs_0" }# define RCU_EXP_NAME_INIT { "rcu_node_exp_0" } +# define RCU_EXP_SCHED_NAME_INIT \ + { "rcu_node_exp_sched_0" }#elif NR_CPUS <=RCU_FANOUT_2 # define RCU_NUM_LVLS 2# define NUM_RCU_LVL_0 1
@@ -79,6 +81,8 @@# define RCU_NODE_NAME_INIT { "rcu_node_0", "rcu_node_1" }# define RCU_FQS_NAME_INIT { "rcu_node_fqs_0", "rcu_node_fqs_1" }# define RCU_EXP_NAME_INIT { "rcu_node_exp_0", "rcu_node_exp_1" } +# define RCU_EXP_SCHED_NAME_INIT \ + { "rcu_node_exp_sched_0", "rcu_node_exp_sched_1" }#elif NR_CPUS <=RCU_FANOUT_3 # define RCU_NUM_LVLS 3# define NUM_RCU_LVL_0 1
@@ -89,6 +93,8 @@# define RCU_NODE_NAME_INIT { "rcu_node_0", "rcu_node_1", "rcu_node_2" }# define RCU_FQS_NAME_INIT { "rcu_node_fqs_0", "rcu_node_fqs_1", "rcu_node_fqs_2" }# define RCU_EXP_NAME_INIT { "rcu_node_exp_0", "rcu_node_exp_1", "rcu_node_exp_2" } +# define RCU_EXP_SCHED_NAME_INIT \ + { "rcu_node_exp_sched_0", "rcu_node_exp_sched_1", "rcu_node_exp_sched_2" }#elif NR_CPUS <=RCU_FANOUT_4 # define RCU_NUM_LVLS 4# define NUM_RCU_LVL_0 1
@@ -100,6 +106,8 @@# define RCU_NODE_NAME_INIT { "rcu_node_0", "rcu_node_1", "rcu_node_2", "rcu_node_3" }# define RCU_FQS_NAME_INIT { "rcu_node_fqs_0", "rcu_node_fqs_1", "rcu_node_fqs_2", "rcu_node_fqs_3" }# define RCU_EXP_NAME_INIT { "rcu_node_exp_0", "rcu_node_exp_1", "rcu_node_exp_2", "rcu_node_exp_3" } +# define RCU_EXP_SCHED_NAME_INIT \ + { "rcu_node_exp_sched_0", "rcu_node_exp_sched_1", "rcu_node_exp_sched_2", "rcu_node_exp_sched_3" }#else # error "CONFIG_RCU_FANOUT insufficient for NR_CPUS"#endif /* #if (NR_CPUS) <= RCU_FANOUT_1 */