Message ID | 5885405.DvuYhMxLoT@rjwysocki.net (mailing list archive) |
---|---|
State | Queued |
Delegated to: | Rafael Wysocki |
Headers | show |
Series | [v2] PM: EM: Address RCU-related sparse warnings | expand |
On 3/6/25 16:49, Rafael J. Wysocki wrote: > From: Rafael J. Wysocki <rafael.j.wysocki@intel.com> > > The usage of __rcu in the Energy Model code is quite inconsistent > which causes the following sparse warnings to trigger: > > kernel/power/energy_model.c:169:15: warning: incorrect type in assignment (different address spaces) > kernel/power/energy_model.c:169:15: expected struct em_perf_table [noderef] __rcu *table > kernel/power/energy_model.c:169:15: got struct em_perf_table * > kernel/power/energy_model.c:171:9: warning: incorrect type in argument 1 (different address spaces) > kernel/power/energy_model.c:171:9: expected struct callback_head *head > kernel/power/energy_model.c:171:9: got struct callback_head [noderef] __rcu * > kernel/power/energy_model.c:171:9: warning: cast removes address space '__rcu' of expression > kernel/power/energy_model.c:182:19: warning: incorrect type in argument 1 (different address spaces) > kernel/power/energy_model.c:182:19: expected struct kref *kref > kernel/power/energy_model.c:182:19: got struct kref [noderef] __rcu * > kernel/power/energy_model.c:200:15: warning: incorrect type in assignment (different address spaces) > kernel/power/energy_model.c:200:15: expected struct em_perf_table [noderef] __rcu *table > kernel/power/energy_model.c:200:15: got void *[assigned] _res > kernel/power/energy_model.c:204:20: warning: incorrect type in argument 1 (different address spaces) > kernel/power/energy_model.c:204:20: expected struct kref *kref > kernel/power/energy_model.c:204:20: got struct kref [noderef] __rcu * > kernel/power/energy_model.c:320:19: warning: incorrect type in argument 1 (different address spaces) > kernel/power/energy_model.c:320:19: expected struct kref *kref > kernel/power/energy_model.c:320:19: got struct kref [noderef] __rcu * > kernel/power/energy_model.c:325:45: warning: incorrect type in argument 2 (different address spaces) > kernel/power/energy_model.c:325:45: expected struct em_perf_state *table > kernel/power/energy_model.c:325:45: got struct em_perf_state [noderef] __rcu * > kernel/power/energy_model.c:425:45: warning: incorrect type in argument 3 (different address spaces) > kernel/power/energy_model.c:425:45: expected struct em_perf_state *table > kernel/power/energy_model.c:425:45: got struct em_perf_state [noderef] __rcu * > kernel/power/energy_model.c:442:15: warning: incorrect type in argument 1 (different address spaces) > kernel/power/energy_model.c:442:15: expected void const *objp > kernel/power/energy_model.c:442:15: got struct em_perf_table [noderef] __rcu *[assigned] em_table > kernel/power/energy_model.c:626:55: warning: incorrect type in argument 2 (different address spaces) > kernel/power/energy_model.c:626:55: expected struct em_perf_state *table > kernel/power/energy_model.c:626:55: got struct em_perf_state [noderef] __rcu * > kernel/power/energy_model.c:681:16: warning: incorrect type in assignment (different address spaces) > kernel/power/energy_model.c:681:16: expected struct em_perf_state *new_ps > kernel/power/energy_model.c:681:16: got struct em_perf_state [noderef] __rcu * > kernel/power/energy_model.c:699:37: warning: incorrect type in argument 2 (different address spaces) > kernel/power/energy_model.c:699:37: expected struct em_perf_state *table > kernel/power/energy_model.c:699:37: got struct em_perf_state [noderef] __rcu * > kernel/power/energy_model.c:733:38: warning: incorrect type in argument 3 (different address spaces) > kernel/power/energy_model.c:733:38: expected struct em_perf_state *table > kernel/power/energy_model.c:733:38: got struct em_perf_state [noderef] __rcu * > kernel/power/energy_model.c:855:53: warning: dereference of noderef expression > kernel/power/energy_model.c:864:32: warning: dereference of noderef expression > > This is because the __rcu annotation for sparse is only applicable to > pointers that need rcu_dereference() or equivalent for protection, which > basically means pointers assigned with rcu_assign_pointer(). > > Make all of the above sparse warnings go away by cleaning up the usage > of __rcu and using rcu_dereference_protected() where applicable. > > Cc: All applicable <stable@vger.kernel.org> > Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> > --- > > This replaces > > https://lore.kernel.org/linux-pm/1929404.tdWV9SEqCh@rjwysocki.net/ > > and > > https://lore.kernel.org/linux-pm/13728396.uLZWGnKmhe@rjwysocki.net/ > > --- > include/linux/energy_model.h | 12 ++++++------ > kernel/power/energy_model.c | 39 ++++++++++++++++++++------------------- > 2 files changed, 26 insertions(+), 25 deletions(-) > LGTM, Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
--- a/include/linux/energy_model.h +++ b/include/linux/energy_model.h @@ -167,13 +167,13 @@ struct em_perf_domain *em_cpu_get(int cpu); struct em_perf_domain *em_pd_get(struct device *dev); int em_dev_update_perf_domain(struct device *dev, - struct em_perf_table __rcu *new_table); + struct em_perf_table *new_table); int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states, const struct em_data_callback *cb, const cpumask_t *cpus, bool microwatts); void em_dev_unregister_perf_domain(struct device *dev); -struct em_perf_table __rcu *em_table_alloc(struct em_perf_domain *pd); -void em_table_free(struct em_perf_table __rcu *table); +struct em_perf_table *em_table_alloc(struct em_perf_domain *pd); +void em_table_free(struct em_perf_table *table); int em_dev_compute_costs(struct device *dev, struct em_perf_state *table, int nr_states); int em_dev_update_chip_binning(struct device *dev); @@ -373,14 +373,14 @@ return 0; } static inline -struct em_perf_table __rcu *em_table_alloc(struct em_perf_domain *pd) +struct em_perf_table *em_table_alloc(struct em_perf_domain *pd) { return NULL; } -static inline void em_table_free(struct em_perf_table __rcu *table) {} +static inline void em_table_free(struct em_perf_table *table) {} static inline int em_dev_update_perf_domain(struct device *dev, - struct em_perf_table __rcu *new_table) + struct em_perf_table *new_table) { return -EINVAL; } --- a/kernel/power/energy_model.c +++ b/kernel/power/energy_model.c @@ -163,12 +163,8 @@ static void em_release_table_kref(struct kref *kref) { - struct em_perf_table __rcu *table; - /* It was the last owner of this table so we can free */ - table = container_of(kref, struct em_perf_table, kref); - - kfree_rcu(table, rcu); + kfree_rcu(container_of(kref, struct em_perf_table, kref), rcu); } /** @@ -177,7 +173,7 @@ * * No return values. */ -void em_table_free(struct em_perf_table __rcu *table) +void em_table_free(struct em_perf_table *table) { kref_put(&table->kref, em_release_table_kref); } @@ -190,9 +186,9 @@ * has a user. * Returns allocated table or NULL. */ -struct em_perf_table __rcu *em_table_alloc(struct em_perf_domain *pd) +struct em_perf_table *em_table_alloc(struct em_perf_domain *pd) { - struct em_perf_table __rcu *table; + struct em_perf_table *table; int table_size; table_size = sizeof(struct em_perf_state) * pd->nr_perf_states; @@ -300,9 +296,9 @@ * Return 0 on success or an error code on failure. */ int em_dev_update_perf_domain(struct device *dev, - struct em_perf_table __rcu *new_table) + struct em_perf_table *new_table) { - struct em_perf_table __rcu *old_table; + struct em_perf_table *old_table; struct em_perf_domain *pd; if (!dev) @@ -319,7 +315,8 @@ kref_get(&new_table->kref); - old_table = pd->em_table; + old_table = rcu_dereference_protected(pd->em_table, + lockdep_is_held(&em_pd_mutex)); rcu_assign_pointer(pd->em_table, new_table); em_cpufreq_update_efficiencies(dev, new_table->state); @@ -392,7 +389,7 @@ const cpumask_t *cpus, unsigned long flags) { - struct em_perf_table __rcu *em_table; + struct em_perf_table *em_table; struct em_perf_domain *pd; struct device *cpu_dev; int cpu, ret, num_cpus; @@ -552,6 +549,7 @@ const struct em_data_callback *cb, const cpumask_t *cpus, bool microwatts) { + struct em_perf_table *em_table; unsigned long cap, prev_cap = 0; unsigned long flags = 0; int cpu, ret; @@ -624,7 +622,9 @@ dev->em_pd->min_perf_state = 0; dev->em_pd->max_perf_state = nr_states - 1; - em_cpufreq_update_efficiencies(dev, dev->em_pd->em_table->state); + em_table = rcu_dereference_protected(dev->em_pd->em_table, + lockdep_is_held(&em_pd_mutex)); + em_cpufreq_update_efficiencies(dev, em_table->state); em_debug_create_pd(dev); dev_info(dev, "EM: created perf domain\n"); @@ -661,7 +661,8 @@ mutex_lock(&em_pd_mutex); em_debug_remove_pd(dev); - em_table_free(dev->em_pd->em_table); + em_table_free(rcu_dereference_protected(dev->em_pd->em_table, + lockdep_is_held(&em_pd_mutex))); kfree(dev->em_pd); dev->em_pd = NULL; @@ -669,9 +670,9 @@ } EXPORT_SYMBOL_GPL(em_dev_unregister_perf_domain); -static struct em_perf_table __rcu *em_table_dup(struct em_perf_domain *pd) +static struct em_perf_table *em_table_dup(struct em_perf_domain *pd) { - struct em_perf_table __rcu *em_table; + struct em_perf_table *em_table; struct em_perf_state *ps, *new_ps; int ps_size; @@ -693,7 +694,7 @@ } static int em_recalc_and_update(struct device *dev, struct em_perf_domain *pd, - struct em_perf_table __rcu *em_table) + struct em_perf_table *em_table) { int ret; @@ -723,7 +724,7 @@ static void em_adjust_new_capacity(struct device *dev, struct em_perf_domain *pd) { - struct em_perf_table __rcu *em_table; + struct em_perf_table *em_table; em_table = em_table_dup(pd); if (!em_table) { @@ -814,7 +815,7 @@ */ int em_dev_update_chip_binning(struct device *dev) { - struct em_perf_table __rcu *em_table; + struct em_perf_table *em_table; struct em_perf_domain *pd; int i, ret;