Skip to content

Commit 2b391c4

Browse files
stephan-ghstorulf
authored andcommitted
pmdomain: core: Scale down parent/child performance states in reverse order
Power domains might have parent domains assigned that are automatically managed by the PM domain core. In particular, parent domains are automatically powered on/off and setting performance states on child domains are propagated to parent domains (e.g. using an OPP table from the device tree). Currently the parent performance state is always adjusted before the performance state of the child domain, which is a problem for some cases when scaling down the performance state. More exactly, it may lead to that the parent domain could run in a lower performance state, than what is required by the child domain. To fix the behaviour, let's differentiate between scaling up/down and adjust the order of operations: - When scaling up, parent domains should be adjusted before the child domain. In case of an error, the rollback happens in reverse order. - When scaling down, parent domains should be adjusted after the child domain, in reverse order, just as if we would rollback scaling up. In case of an error, the rollback happens in normal order (just as if we would normally scale up). Signed-off-by: Stephan Gerhold <stephan@gerhold.net> Link: https://lore.kernel.org/r/20240103-genpd-perf-order-v2-1-eeecfc55624b@gerhold.net Tested-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
1 parent b9401b6 commit 2b391c4

File tree

1 file changed

+77
-47
lines changed

1 file changed

+77
-47
lines changed

drivers/pmdomain/core.c

Lines changed: 77 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -311,72 +311,102 @@ static int genpd_xlate_performance_state(struct generic_pm_domain *genpd,
311311
}
312312

313313
static int _genpd_set_performance_state(struct generic_pm_domain *genpd,
314-
unsigned int state, int depth)
314+
unsigned int state, int depth);
315+
316+
static void _genpd_rollback_parent_state(struct gpd_link *link, int depth)
315317
{
316-
struct generic_pm_domain *parent;
317-
struct gpd_link *link;
318-
int parent_state, ret;
318+
struct generic_pm_domain *parent = link->parent;
319+
int parent_state;
319320

320-
if (state == genpd->performance_state)
321-
return 0;
321+
genpd_lock_nested(parent, depth + 1);
322322

323-
/* Propagate to parents of genpd */
324-
list_for_each_entry(link, &genpd->child_links, child_node) {
325-
parent = link->parent;
323+
parent_state = link->prev_performance_state;
324+
link->performance_state = parent_state;
326325

327-
/* Find parent's performance state */
328-
ret = genpd_xlate_performance_state(genpd, parent, state);
329-
if (unlikely(ret < 0))
330-
goto err;
326+
parent_state = _genpd_reeval_performance_state(parent, parent_state);
327+
if (_genpd_set_performance_state(parent, parent_state, depth + 1)) {
328+
pr_err("%s: Failed to roll back to %d performance state\n",
329+
parent->name, parent_state);
330+
}
331331

332-
parent_state = ret;
332+
genpd_unlock(parent);
333+
}
333334

334-
genpd_lock_nested(parent, depth + 1);
335+
static int _genpd_set_parent_state(struct generic_pm_domain *genpd,
336+
struct gpd_link *link,
337+
unsigned int state, int depth)
338+
{
339+
struct generic_pm_domain *parent = link->parent;
340+
int parent_state, ret;
335341

336-
link->prev_performance_state = link->performance_state;
337-
link->performance_state = parent_state;
338-
parent_state = _genpd_reeval_performance_state(parent,
339-
parent_state);
340-
ret = _genpd_set_performance_state(parent, parent_state, depth + 1);
341-
if (ret)
342-
link->performance_state = link->prev_performance_state;
342+
/* Find parent's performance state */
343+
ret = genpd_xlate_performance_state(genpd, parent, state);
344+
if (unlikely(ret < 0))
345+
return ret;
343346

344-
genpd_unlock(parent);
347+
parent_state = ret;
345348

346-
if (ret)
347-
goto err;
348-
}
349+
genpd_lock_nested(parent, depth + 1);
349350

350-
if (genpd->set_performance_state) {
351-
ret = genpd->set_performance_state(genpd, state);
352-
if (ret)
353-
goto err;
354-
}
351+
link->prev_performance_state = link->performance_state;
352+
link->performance_state = parent_state;
355353

356-
genpd->performance_state = state;
357-
return 0;
354+
parent_state = _genpd_reeval_performance_state(parent, parent_state);
355+
ret = _genpd_set_performance_state(parent, parent_state, depth + 1);
356+
if (ret)
357+
link->performance_state = link->prev_performance_state;
358358

359-
err:
360-
/* Encountered an error, lets rollback */
361-
list_for_each_entry_continue_reverse(link, &genpd->child_links,
362-
child_node) {
363-
parent = link->parent;
359+
genpd_unlock(parent);
364360

365-
genpd_lock_nested(parent, depth + 1);
361+
return ret;
362+
}
363+
364+
static int _genpd_set_performance_state(struct generic_pm_domain *genpd,
365+
unsigned int state, int depth)
366+
{
367+
struct gpd_link *link = NULL;
368+
int ret;
369+
370+
if (state == genpd->performance_state)
371+
return 0;
366372

367-
parent_state = link->prev_performance_state;
368-
link->performance_state = parent_state;
373+
/* When scaling up, propagate to parents first in normal order */
374+
if (state > genpd->performance_state) {
375+
list_for_each_entry(link, &genpd->child_links, child_node) {
376+
ret = _genpd_set_parent_state(genpd, link, state, depth);
377+
if (ret)
378+
goto rollback_parents_up;
379+
}
380+
}
369381

370-
parent_state = _genpd_reeval_performance_state(parent,
371-
parent_state);
372-
if (_genpd_set_performance_state(parent, parent_state, depth + 1)) {
373-
pr_err("%s: Failed to roll back to %d performance state\n",
374-
parent->name, parent_state);
382+
if (genpd->set_performance_state) {
383+
ret = genpd->set_performance_state(genpd, state);
384+
if (ret) {
385+
if (link)
386+
goto rollback_parents_up;
387+
return ret;
375388
}
389+
}
376390

377-
genpd_unlock(parent);
391+
/* When scaling down, propagate to parents last in reverse order */
392+
if (state < genpd->performance_state) {
393+
list_for_each_entry_reverse(link, &genpd->child_links, child_node) {
394+
ret = _genpd_set_parent_state(genpd, link, state, depth);
395+
if (ret)
396+
goto rollback_parents_down;
397+
}
378398
}
379399

400+
genpd->performance_state = state;
401+
return 0;
402+
403+
rollback_parents_up:
404+
list_for_each_entry_continue_reverse(link, &genpd->child_links, child_node)
405+
_genpd_rollback_parent_state(link, depth);
406+
return ret;
407+
rollback_parents_down:
408+
list_for_each_entry_continue(link, &genpd->child_links, child_node)
409+
_genpd_rollback_parent_state(link, depth);
380410
return ret;
381411
}
382412

0 commit comments

Comments
 (0)