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

coresight: etm4x: Fix etm4_count race by moving cpuhp callbacks to init

etm4_count keeps track of number of ETMv4 registered and on some systems,
a race is observed on etm4_count variable which can lead to multiple calls
to cpuhp_setup_state_nocalls_cpuslocked(). This function internally calls
cpuhp_store_callbacks() which prevents multiple registrations of callbacks
for a given state and due to this race, it returns -EBUSY leading to ETM
probe failures like below.

coresight-etm4x: probe of 7040000.etm failed with error -16

This race can easily be triggered with async probe by setting probe type
as PROBE_PREFER_ASYNCHRONOUS and with ETM power management property
"arm,coresight-loses-context-with-cpu".

Prevent this race by moving cpuhp callbacks to etm driver init since the
cpuhp callbacks doesn't have to depend on the etm4_count and can be once
setup during driver init. Similarly we move cpu_pm notifier registration
to driver init and completely remove etm4_count usage. Also now we can
use non cpuslocked version of cpuhp callbacks with this movement.

Fixes: 9b6a3f3633a5 ("coresight: etmv4: Fix CPU power management setup in probe() function")
Fixes: 58eb457be028 ("hwtracing/coresight-etm4x: Convert to hotplug state machine")
Suggested-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Tested-by: Stephen Boyd <swboyd@chromium.org>
Reviewed-by: Stephen Boyd <swboyd@chromium.org>
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Signed-off-by: Sai Prakash Ranjan <saiprakash.ranjan@codeaurora.org>
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
Link: https://lore.kernel.org/r/20200916191737.4001561-2-mathieu.poirier@linaro.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Sai Prakash Ranjan and committed by
Greg Kroah-Hartman
2d1a8bfb 7c33e3c4

+31 -34
+31 -34
drivers/hwtracing/coresight/coresight-etm4x.c
··· 48 48 MODULE_PARM_DESC(pm_save_enable, 49 49 "Save/restore state on power down: 1 = never, 2 = self-hosted"); 50 50 51 - /* The number of ETMv4 currently registered */ 52 - static int etm4_count; 53 51 static struct etmv4_drvdata *etmdrvdata[NR_CPUS]; 54 52 static void etm4_set_default_config(struct etmv4_config *config); 55 53 static int etm4_set_event_filters(struct etmv4_drvdata *drvdata, ··· 1395 1397 .notifier_call = etm4_cpu_pm_notify, 1396 1398 }; 1397 1399 1398 - /* Setup PM. Called with cpus locked. Deals with error conditions and counts */ 1399 - static int etm4_pm_setup_cpuslocked(void) 1400 + /* Setup PM. Deals with error conditions and counts */ 1401 + static int __init etm4_pm_setup(void) 1400 1402 { 1401 1403 int ret; 1402 1404 1403 - if (etm4_count++) 1404 - return 0; 1405 - 1406 1405 ret = cpu_pm_register_notifier(&etm4_cpu_pm_nb); 1407 1406 if (ret) 1408 - goto reduce_count; 1407 + return ret; 1409 1408 1410 - ret = cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ARM_CORESIGHT_STARTING, 1411 - "arm/coresight4:starting", 1412 - etm4_starting_cpu, etm4_dying_cpu); 1409 + ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING, 1410 + "arm/coresight4:starting", 1411 + etm4_starting_cpu, etm4_dying_cpu); 1413 1412 1414 1413 if (ret) 1415 1414 goto unregister_notifier; 1416 1415 1417 - ret = cpuhp_setup_state_nocalls_cpuslocked(CPUHP_AP_ONLINE_DYN, 1418 - "arm/coresight4:online", 1419 - etm4_online_cpu, NULL); 1416 + ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, 1417 + "arm/coresight4:online", 1418 + etm4_online_cpu, NULL); 1420 1419 1421 1420 /* HP dyn state ID returned in ret on success */ 1422 1421 if (ret > 0) { ··· 1422 1427 } 1423 1428 1424 1429 /* failed dyn state - remove others */ 1425 - cpuhp_remove_state_nocalls_cpuslocked(CPUHP_AP_ARM_CORESIGHT_STARTING); 1430 + cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); 1426 1431 1427 1432 unregister_notifier: 1428 1433 cpu_pm_unregister_notifier(&etm4_cpu_pm_nb); 1429 - 1430 - reduce_count: 1431 - --etm4_count; 1432 1434 return ret; 1433 1435 } 1434 1436 1435 - static void etm4_pm_clear(void) 1437 + static void __init etm4_pm_clear(void) 1436 1438 { 1437 - if (--etm4_count != 0) 1438 - return; 1439 - 1440 1439 cpu_pm_unregister_notifier(&etm4_cpu_pm_nb); 1441 1440 cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING); 1442 1441 if (hp_online) { ··· 1486 1497 if (!desc.name) 1487 1498 return -ENOMEM; 1488 1499 1489 - cpus_read_lock(); 1490 1500 etmdrvdata[drvdata->cpu] = drvdata; 1491 1501 1492 1502 if (smp_call_function_single(drvdata->cpu, 1493 1503 etm4_init_arch_data, drvdata, 1)) 1494 1504 dev_err(dev, "ETM arch init failed\n"); 1495 - 1496 - ret = etm4_pm_setup_cpuslocked(); 1497 - cpus_read_unlock(); 1498 - 1499 - /* etm4_pm_setup_cpuslocked() does its own cleanup - exit on error */ 1500 - if (ret) { 1501 - etmdrvdata[drvdata->cpu] = NULL; 1502 - return ret; 1503 - } 1504 1505 1505 1506 if (etm4_arch_supported(drvdata->arch) == false) { 1506 1507 ret = -EINVAL; ··· 1538 1559 1539 1560 err_arch_supported: 1540 1561 etmdrvdata[drvdata->cpu] = NULL; 1541 - etm4_pm_clear(); 1542 1562 return ret; 1543 1563 } 1544 1564 ··· 1575 1597 .probe = etm4_probe, 1576 1598 .id_table = etm4_ids, 1577 1599 }; 1578 - builtin_amba_driver(etm4x_driver); 1600 + 1601 + static int __init etm4x_init(void) 1602 + { 1603 + int ret; 1604 + 1605 + ret = etm4_pm_setup(); 1606 + 1607 + /* etm4_pm_setup() does its own cleanup - exit on error */ 1608 + if (ret) 1609 + return ret; 1610 + 1611 + ret = amba_driver_register(&etm4x_driver); 1612 + if (ret) { 1613 + pr_err("Error registering etm4x driver\n"); 1614 + etm4_pm_clear(); 1615 + } 1616 + 1617 + return ret; 1618 + } 1619 + device_initcall(etm4x_init);