Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PM / OPP: Add 'UNKNOWN' status for shared_opp in struct opp_table

dev_pm_opp_get_sharing_cpus() returns 0 even in the case when the OPP
core doesn't know whether or not the table is shared. It works on the
majority of platforms, where the OPP table is never created before
invoking the function and then -ENODEV is returned by it.

But in the case of one platform (Jetson TK1) at least, the situation
is a bit different. The OPP table has been created (somehow) before
dev_pm_opp_get_sharing_cpus() is called and it returns 0. Its caller
treats that as 'the CPUs don't share OPPs' and that leads to degraded
performance.

Fix this by converting 'shared_opp' in struct opp_table to an enum
and making dev_pm_opp_get_sharing_cpus() return -EINVAL in case when
the value of that field is "access unknown", so that the caller can
handle it accordingly (cpufreq-dt considers that as 'all CPUs share
the table', for example).

Fixes: 6f707daa3833 "PM / OPP: Add dev_pm_opp_get_sharing_cpus()"
Reported-and-tested-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
[ rjw : Subject & changelog ]
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

authored by

Viresh Kumar and committed by
Rafael J. Wysocki
79ee2e8f c8541203

+24 -6
+9 -3
drivers/base/power/opp/cpu.c
··· 211 211 } 212 212 213 213 /* Mark opp-table as multiple CPUs are sharing it now */ 214 - opp_table->shared_opp = true; 214 + opp_table->shared_opp = OPP_TABLE_ACCESS_SHARED; 215 215 } 216 216 unlock: 217 217 mutex_unlock(&opp_table_lock); ··· 227 227 * 228 228 * This updates the @cpumask with CPUs that are sharing OPPs with @cpu_dev. 229 229 * 230 - * Returns -ENODEV if OPP table isn't already present. 230 + * Returns -ENODEV if OPP table isn't already present and -EINVAL if the OPP 231 + * table's status is access-unknown. 231 232 * 232 233 * Locking: The internal opp_table and opp structures are RCU protected. 233 234 * Hence this function internally uses RCU updater strategy with mutex locks ··· 250 249 goto unlock; 251 250 } 252 251 252 + if (opp_table->shared_opp == OPP_TABLE_ACCESS_UNKNOWN) { 253 + ret = -EINVAL; 254 + goto unlock; 255 + } 256 + 253 257 cpumask_clear(cpumask); 254 258 255 - if (opp_table->shared_opp) { 259 + if (opp_table->shared_opp == OPP_TABLE_ACCESS_SHARED) { 256 260 list_for_each_entry(opp_dev, &opp_table->dev_list, node) 257 261 cpumask_set_cpu(opp_dev->dev->id, cpumask); 258 262 } else {
+8 -2
drivers/base/power/opp/of.c
··· 34 34 * But the OPPs will be considered as shared only if the 35 35 * OPP table contains a "opp-shared" property. 36 36 */ 37 - return opp_table->shared_opp ? opp_table : NULL; 37 + if (opp_table->shared_opp == OPP_TABLE_ACCESS_SHARED) 38 + return opp_table; 39 + 40 + return NULL; 38 41 } 39 42 } 40 43 ··· 356 353 } 357 354 358 355 opp_table->np = opp_np; 359 - opp_table->shared_opp = of_property_read_bool(opp_np, "opp-shared"); 356 + if (of_property_read_bool(opp_np, "opp-shared")) 357 + opp_table->shared_opp = OPP_TABLE_ACCESS_SHARED; 358 + else 359 + opp_table->shared_opp = OPP_TABLE_ACCESS_EXCLUSIVE; 360 360 361 361 mutex_unlock(&opp_table_lock); 362 362
+7 -1
drivers/base/power/opp/opp.h
··· 119 119 #endif 120 120 }; 121 121 122 + enum opp_table_access { 123 + OPP_TABLE_ACCESS_UNKNOWN = 0, 124 + OPP_TABLE_ACCESS_EXCLUSIVE = 1, 125 + OPP_TABLE_ACCESS_SHARED = 2, 126 + }; 127 + 122 128 /** 123 129 * struct opp_table - Device opp structure 124 130 * @node: table node - contains the devices with OPPs that ··· 172 166 /* For backward compatibility with v1 bindings */ 173 167 unsigned int voltage_tolerance_v1; 174 168 175 - bool shared_opp; 169 + enum opp_table_access shared_opp; 176 170 struct dev_pm_opp *suspend_opp; 177 171 178 172 unsigned int *supported_hw;