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

ARM: cpuidle: Register per cpuidle device

If the cpuidle init cpu operation returns -ENXIO, therefore reporting HW
failure or misconfiguration, the CPUidle driver skips the respective
cpuidle device initialization because the associated platform back-end HW
is not operational.

That prevents the system to crash and allows to handle the error gracefully.

For example, on Qcom's platform, each core has a SPM. The device associated
with this SPM is initialized before the cpuidle framework. If there is an error
in the initialization (eg. error in the DT), the system continues to boot but
in degraded mode as some SPM may not be correctly initialized.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>

+43 -2
+43 -2
drivers/cpuidle/cpuidle-arm.c
··· 17 17 #include <linux/kernel.h> 18 18 #include <linux/module.h> 19 19 #include <linux/of.h> 20 + #include <linux/slab.h> 20 21 21 22 #include <asm/cpuidle.h> 22 23 ··· 95 94 { 96 95 int cpu, ret; 97 96 struct cpuidle_driver *drv = &arm_idle_driver; 97 + struct cpuidle_device *dev; 98 98 99 99 /* 100 100 * Initialize idle states data, starting at index 1. ··· 107 105 if (ret <= 0) 108 106 return ret ? : -ENODEV; 109 107 108 + ret = cpuidle_register_driver(drv); 109 + if (ret) { 110 + pr_err("Failed to register cpuidle driver\n"); 111 + return ret; 112 + } 113 + 110 114 /* 111 115 * Call arch CPU operations in order to initialize 112 116 * idle states suspend back-end specific data 113 117 */ 114 118 for_each_possible_cpu(cpu) { 115 119 ret = arm_cpuidle_init(cpu); 120 + 121 + /* 122 + * Skip the cpuidle device initialization if the reported 123 + * failure is a HW misconfiguration/breakage (-ENXIO). 124 + */ 125 + if (ret == -ENXIO) 126 + continue; 127 + 116 128 if (ret) { 117 129 pr_err("CPU %d failed to init idle CPU ops\n", cpu); 118 - return ret; 130 + goto out_fail; 131 + } 132 + 133 + dev = kzalloc(sizeof(*dev), GFP_KERNEL); 134 + if (!dev) { 135 + pr_err("Failed to allocate cpuidle device\n"); 136 + goto out_fail; 137 + } 138 + dev->cpu = cpu; 139 + 140 + ret = cpuidle_register_device(dev); 141 + if (ret) { 142 + pr_err("Failed to register cpuidle device for CPU %d\n", 143 + cpu); 144 + kfree(dev); 145 + goto out_fail; 119 146 } 120 147 } 121 148 122 - return cpuidle_register(drv, NULL); 149 + return 0; 150 + out_fail: 151 + while (--cpu >= 0) { 152 + dev = per_cpu(cpuidle_devices, cpu); 153 + cpuidle_unregister_device(dev); 154 + kfree(dev); 155 + } 156 + 157 + cpuidle_unregister_driver(drv); 158 + 159 + return ret; 123 160 } 124 161 device_initcall(arm_idle_init);