Skip to content

Commit 166bf83

Browse files
storulfrafaeljw
authored andcommitted
cpuidle: psci: Fix error path via converting to a platform driver
The current error paths for the cpuidle-psci driver, may leak memory or possibly leave CPU devices attached to their PM domains. These are quite harmless issues, but still deserves to be taken care of. Although, rather than fixing them by keeping track of allocations that needs to be freed, which tends to become a bit messy, let's convert into a platform driver. In this way, it gets easier to fix the memory leaks as we can rely on the devm_* functions. Moreover, converting to a platform driver also enables support for deferred probe, which subsequent changes takes benefit from. Signed-off-by: Ulf Hansson <[email protected]> Reviewed-by: Lukasz Luba <[email protected]> Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent 4b072cd commit 166bf83

File tree

3 files changed

+95
-65
lines changed

3 files changed

+95
-65
lines changed

drivers/cpuidle/cpuidle-psci-domain.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ static int __init psci_idle_init_domains(void)
287287
}
288288
subsys_initcall(psci_idle_init_domains);
289289

290-
struct device __init *psci_dt_attach_cpu(int cpu)
290+
struct device *psci_dt_attach_cpu(int cpu)
291291
{
292292
struct device *dev;
293293

@@ -301,3 +301,11 @@ struct device __init *psci_dt_attach_cpu(int cpu)
301301

302302
return dev;
303303
}
304+
305+
void psci_dt_detach_cpu(struct device *dev)
306+
{
307+
if (IS_ERR_OR_NULL(dev))
308+
return;
309+
310+
dev_pm_domain_detach(dev, false);
311+
}

drivers/cpuidle/cpuidle-psci.c

Lines changed: 80 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
#include <linux/module.h>
1818
#include <linux/of.h>
1919
#include <linux/of_device.h>
20+
#include <linux/platform_device.h>
2021
#include <linux/psci.h>
2122
#include <linux/pm_runtime.h>
2223
#include <linux/slab.h>
24+
#include <linux/string.h>
2325

2426
#include <asm/cpuidle.h>
2527

@@ -33,7 +35,7 @@ struct psci_cpuidle_data {
3335

3436
static DEFINE_PER_CPU_READ_MOSTLY(struct psci_cpuidle_data, psci_cpuidle_data);
3537
static DEFINE_PER_CPU(u32, domain_state);
36-
static bool psci_cpuidle_use_cpuhp __initdata;
38+
static bool psci_cpuidle_use_cpuhp;
3739

3840
void psci_set_domain_state(u32 state)
3941
{
@@ -104,7 +106,7 @@ static int psci_idle_cpuhp_down(unsigned int cpu)
104106
return 0;
105107
}
106108

107-
static void __init psci_idle_init_cpuhp(void)
109+
static void psci_idle_init_cpuhp(void)
108110
{
109111
int err;
110112

@@ -127,30 +129,13 @@ static int psci_enter_idle_state(struct cpuidle_device *dev,
127129
return psci_enter_state(idx, state[idx]);
128130
}
129131

130-
static struct cpuidle_driver psci_idle_driver __initdata = {
131-
.name = "psci_idle",
132-
.owner = THIS_MODULE,
133-
/*
134-
* PSCI idle states relies on architectural WFI to
135-
* be represented as state index 0.
136-
*/
137-
.states[0] = {
138-
.enter = psci_enter_idle_state,
139-
.exit_latency = 1,
140-
.target_residency = 1,
141-
.power_usage = UINT_MAX,
142-
.name = "WFI",
143-
.desc = "ARM WFI",
144-
}
145-
};
146-
147-
static const struct of_device_id psci_idle_state_match[] __initconst = {
132+
static const struct of_device_id psci_idle_state_match[] = {
148133
{ .compatible = "arm,idle-state",
149134
.data = psci_enter_idle_state },
150135
{ },
151136
};
152137

153-
int __init psci_dt_parse_state_node(struct device_node *np, u32 *state)
138+
int psci_dt_parse_state_node(struct device_node *np, u32 *state)
154139
{
155140
int err = of_property_read_u32(np, "arm,psci-suspend-param", state);
156141

@@ -167,9 +152,9 @@ int __init psci_dt_parse_state_node(struct device_node *np, u32 *state)
167152
return 0;
168153
}
169154

170-
static int __init psci_dt_cpu_init_topology(struct cpuidle_driver *drv,
171-
struct psci_cpuidle_data *data,
172-
unsigned int state_count, int cpu)
155+
static int psci_dt_cpu_init_topology(struct cpuidle_driver *drv,
156+
struct psci_cpuidle_data *data,
157+
unsigned int state_count, int cpu)
173158
{
174159
/* Currently limit the hierarchical topology to be used in OSI mode. */
175160
if (!psci_has_osi_support())
@@ -190,17 +175,18 @@ static int __init psci_dt_cpu_init_topology(struct cpuidle_driver *drv,
190175
return 0;
191176
}
192177

193-
static int __init psci_dt_cpu_init_idle(struct cpuidle_driver *drv,
194-
struct device_node *cpu_node,
195-
unsigned int state_count, int cpu)
178+
static int psci_dt_cpu_init_idle(struct device *dev, struct cpuidle_driver *drv,
179+
struct device_node *cpu_node,
180+
unsigned int state_count, int cpu)
196181
{
197182
int i, ret = 0;
198183
u32 *psci_states;
199184
struct device_node *state_node;
200185
struct psci_cpuidle_data *data = per_cpu_ptr(&psci_cpuidle_data, cpu);
201186

202187
state_count++; /* Add WFI state too */
203-
psci_states = kcalloc(state_count, sizeof(*psci_states), GFP_KERNEL);
188+
psci_states = devm_kcalloc(dev, state_count, sizeof(*psci_states),
189+
GFP_KERNEL);
204190
if (!psci_states)
205191
return -ENOMEM;
206192

@@ -213,32 +199,26 @@ static int __init psci_dt_cpu_init_idle(struct cpuidle_driver *drv,
213199
of_node_put(state_node);
214200

215201
if (ret)
216-
goto free_mem;
202+
return ret;
217203

218204
pr_debug("psci-power-state %#x index %d\n", psci_states[i], i);
219205
}
220206

221-
if (i != state_count) {
222-
ret = -ENODEV;
223-
goto free_mem;
224-
}
207+
if (i != state_count)
208+
return -ENODEV;
225209

226210
/* Initialize optional data, used for the hierarchical topology. */
227211
ret = psci_dt_cpu_init_topology(drv, data, state_count, cpu);
228212
if (ret < 0)
229-
goto free_mem;
213+
return ret;
230214

231215
/* Idle states parsed correctly, store them in the per-cpu struct. */
232216
data->psci_states = psci_states;
233217
return 0;
234-
235-
free_mem:
236-
kfree(psci_states);
237-
return ret;
238218
}
239219

240-
static __init int psci_cpu_init_idle(struct cpuidle_driver *drv,
241-
unsigned int cpu, unsigned int state_count)
220+
static int psci_cpu_init_idle(struct device *dev, struct cpuidle_driver *drv,
221+
unsigned int cpu, unsigned int state_count)
242222
{
243223
struct device_node *cpu_node;
244224
int ret;
@@ -254,14 +234,22 @@ static __init int psci_cpu_init_idle(struct cpuidle_driver *drv,
254234
if (!cpu_node)
255235
return -ENODEV;
256236

257-
ret = psci_dt_cpu_init_idle(drv, cpu_node, state_count, cpu);
237+
ret = psci_dt_cpu_init_idle(dev, drv, cpu_node, state_count, cpu);
258238

259239
of_node_put(cpu_node);
260240

261241
return ret;
262242
}
263243

264-
static int __init psci_idle_init_cpu(int cpu)
244+
static void psci_cpu_deinit_idle(int cpu)
245+
{
246+
struct psci_cpuidle_data *data = per_cpu_ptr(&psci_cpuidle_data, cpu);
247+
248+
psci_dt_detach_cpu(data->dev);
249+
psci_cpuidle_use_cpuhp = false;
250+
}
251+
252+
static int psci_idle_init_cpu(struct device *dev, int cpu)
265253
{
266254
struct cpuidle_driver *drv;
267255
struct device_node *cpu_node;
@@ -284,66 +272,72 @@ static int __init psci_idle_init_cpu(int cpu)
284272
if (ret)
285273
return ret;
286274

287-
drv = kmemdup(&psci_idle_driver, sizeof(*drv), GFP_KERNEL);
275+
drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
288276
if (!drv)
289277
return -ENOMEM;
290278

279+
drv->name = "psci_idle";
280+
drv->owner = THIS_MODULE;
291281
drv->cpumask = (struct cpumask *)cpumask_of(cpu);
292282

293283
/*
294-
* Initialize idle states data, starting at index 1, since
295-
* by default idle state 0 is the quiescent state reached
296-
* by the cpu by executing the wfi instruction.
297-
*
284+
* PSCI idle states relies on architectural WFI to be represented as
285+
* state index 0.
286+
*/
287+
drv->states[0].enter = psci_enter_idle_state;
288+
drv->states[0].exit_latency = 1;
289+
drv->states[0].target_residency = 1;
290+
drv->states[0].power_usage = UINT_MAX;
291+
strcpy(drv->states[0].name, "WFI");
292+
strcpy(drv->states[0].desc, "ARM WFI");
293+
294+
/*
298295
* If no DT idle states are detected (ret == 0) let the driver
299296
* initialization fail accordingly since there is no reason to
300297
* initialize the idle driver if only wfi is supported, the
301298
* default archictectural back-end already executes wfi
302299
* on idle entry.
303300
*/
304301
ret = dt_init_idle_driver(drv, psci_idle_state_match, 1);
305-
if (ret <= 0) {
306-
ret = ret ? : -ENODEV;
307-
goto out_kfree_drv;
308-
}
302+
if (ret <= 0)
303+
return ret ? : -ENODEV;
309304

310305
/*
311306
* Initialize PSCI idle states.
312307
*/
313-
ret = psci_cpu_init_idle(drv, cpu, ret);
308+
ret = psci_cpu_init_idle(dev, drv, cpu, ret);
314309
if (ret) {
315310
pr_err("CPU %d failed to PSCI idle\n", cpu);
316-
goto out_kfree_drv;
311+
return ret;
317312
}
318313

319314
ret = cpuidle_register(drv, NULL);
320315
if (ret)
321-
goto out_kfree_drv;
316+
goto deinit;
322317

323318
cpuidle_cooling_register(drv);
324319

325320
return 0;
326-
327-
out_kfree_drv:
328-
kfree(drv);
321+
deinit:
322+
psci_cpu_deinit_idle(cpu);
329323
return ret;
330324
}
331325

332326
/*
333-
* psci_idle_init - Initializes PSCI cpuidle driver
327+
* psci_idle_probe - Initializes PSCI cpuidle driver
334328
*
335329
* Initializes PSCI cpuidle driver for all CPUs, if any CPU fails
336330
* to register cpuidle driver then rollback to cancel all CPUs
337331
* registration.
338332
*/
339-
static int __init psci_idle_init(void)
333+
static int psci_cpuidle_probe(struct platform_device *pdev)
340334
{
341335
int cpu, ret;
342336
struct cpuidle_driver *drv;
343337
struct cpuidle_device *dev;
344338

345339
for_each_possible_cpu(cpu) {
346-
ret = psci_idle_init_cpu(cpu);
340+
ret = psci_idle_init_cpu(&pdev->dev, cpu);
347341
if (ret)
348342
goto out_fail;
349343
}
@@ -356,9 +350,34 @@ static int __init psci_idle_init(void)
356350
dev = per_cpu(cpuidle_devices, cpu);
357351
drv = cpuidle_get_cpu_driver(dev);
358352
cpuidle_unregister(drv);
359-
kfree(drv);
353+
psci_cpu_deinit_idle(cpu);
360354
}
361355

362356
return ret;
363357
}
358+
359+
static struct platform_driver psci_cpuidle_driver = {
360+
.probe = psci_cpuidle_probe,
361+
.driver = {
362+
.name = "psci-cpuidle",
363+
},
364+
};
365+
366+
static int __init psci_idle_init(void)
367+
{
368+
struct platform_device *pdev;
369+
int ret;
370+
371+
ret = platform_driver_register(&psci_cpuidle_driver);
372+
if (ret)
373+
return ret;
374+
375+
pdev = platform_device_register_simple("psci-cpuidle", -1, NULL, 0);
376+
if (IS_ERR(pdev)) {
377+
platform_driver_unregister(&psci_cpuidle_driver);
378+
return PTR_ERR(pdev);
379+
}
380+
381+
return 0;
382+
}
364383
device_initcall(psci_idle_init);

drivers/cpuidle/cpuidle-psci.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@
33
#ifndef __CPUIDLE_PSCI_H
44
#define __CPUIDLE_PSCI_H
55

6+
struct device;
67
struct device_node;
78

89
void psci_set_domain_state(u32 state);
9-
int __init psci_dt_parse_state_node(struct device_node *np, u32 *state);
10+
int psci_dt_parse_state_node(struct device_node *np, u32 *state);
1011

1112
#ifdef CONFIG_ARM_PSCI_CPUIDLE_DOMAIN
12-
struct device __init *psci_dt_attach_cpu(int cpu);
13+
struct device *psci_dt_attach_cpu(int cpu);
14+
void psci_dt_detach_cpu(struct device *dev);
1315
#else
14-
static inline struct device __init *psci_dt_attach_cpu(int cpu) { return NULL; }
16+
static inline struct device *psci_dt_attach_cpu(int cpu) { return NULL; }
17+
static inline void psci_dt_detach_cpu(struct device *dev) { }
1518
#endif
1619

1720
#endif /* __CPUIDLE_PSCI_H */

0 commit comments

Comments
 (0)