CPUs associated with a pseudo-locked cache region are prevented
from entering C6 and deeper C-states to ensure that the
power savings associated with those C-states cannot impact
the pseudo-locked region by forcing the pseudo-locked memory to
be evicted.

When supporting pseudo-locked regions that span L2 and L3 cache
levels it is not necessary to prevent all CPUs associated with both
cache levels from entering deeper C-states. Instead, only the
CPUs associated with the L2 cache need to be limited. This would
potentially result in more power savings since another L2 cache
that does not have a pseudo-locked region but share the L3 cache
would be able to enter power savings.

In preparation for limiting the C-states only where required the code to
do so is moved to earlier in the pseudo-lock region initialization.
Moving this code has the consequence that its actions need to be undone
in more error paths. This is accommodated by moving the C-state cleanup
code to the generic cleanup code (pseudo_lock_region_clear()) and
ensuring that the C-state cleanup code can handle the case when C-states
have not yet been constrained.

Also in preparation for limiting the C-states only on required CPUs the
function now accepts a parameter that specifies which CPUs should have
their C-states constrained - at this time the parameter is still used
for all CPUs associated with the pseudo-locked region.

Signed-off-by: Reinette Chatre <reinette.cha...@intel.com>
---
 arch/x86/kernel/cpu/resctrl/pseudo_lock.c | 30 ++++++++++++-----------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/arch/x86/kernel/cpu/resctrl/pseudo_lock.c 
b/arch/x86/kernel/cpu/resctrl/pseudo_lock.c
index d7623e1b927d..110ae4b4f2e4 100644
--- a/arch/x86/kernel/cpu/resctrl/pseudo_lock.c
+++ b/arch/x86/kernel/cpu/resctrl/pseudo_lock.c
@@ -175,6 +175,9 @@ static void pseudo_lock_cstates_relax(struct 
pseudo_lock_region *plr)
 {
        struct pseudo_lock_pm_req *pm_req, *next;
 
+       if (list_empty(&plr->pm_reqs))
+               return;
+
        list_for_each_entry_safe(pm_req, next, &plr->pm_reqs, list) {
                dev_pm_qos_remove_request(&pm_req->req);
                list_del(&pm_req->list);
@@ -184,6 +187,8 @@ static void pseudo_lock_cstates_relax(struct 
pseudo_lock_region *plr)
 
 /**
  * pseudo_lock_cstates_constrain - Restrict cores from entering C6
+ * @plr: pseudo-lock region requiring the C-states to be restricted
+ * @cpu_mask: the CPUs that should have their C-states restricted
  *
  * To prevent the cache from being affected by power management entering
  * C6 has to be avoided. This is accomplished by requesting a latency
@@ -197,13 +202,14 @@ static void pseudo_lock_cstates_relax(struct 
pseudo_lock_region *plr)
  * may be set to map to deeper sleep states. In this case the latency
  * requirement needs to prevent entering C2 also.
  */
-static int pseudo_lock_cstates_constrain(struct pseudo_lock_region *plr)
+static int pseudo_lock_cstates_constrain(struct pseudo_lock_region *plr,
+                                        struct cpumask *cpu_mask)
 {
        struct pseudo_lock_pm_req *pm_req;
        int cpu;
        int ret;
 
-       for_each_cpu(cpu, &plr->d->cpu_mask) {
+       for_each_cpu(cpu, cpu_mask) {
                pm_req = kzalloc(sizeof(*pm_req), GFP_KERNEL);
                if (!pm_req) {
                        rdt_last_cmd_puts("Failure to allocate memory for PM 
QoS\n");
@@ -251,6 +257,7 @@ static void pseudo_lock_region_clear(struct 
pseudo_lock_region *plr)
                plr->d->plr = NULL;
        plr->d = NULL;
        plr->cbm = 0;
+       pseudo_lock_cstates_relax(plr);
        plr->debugfs_dir = NULL;
 }
 
@@ -292,6 +299,10 @@ static int pseudo_lock_region_init(struct 
pseudo_lock_region *plr)
 
        plr->size = rdtgroup_cbm_to_size(plr->r, plr->d, plr->cbm);
 
+       ret = pseudo_lock_cstates_constrain(plr, &plr->d->cpu_mask);
+       if (ret < 0)
+               goto out_region;
+
        for (i = 0; i < ci->num_leaves; i++) {
                if (ci->info_list[i].level == plr->r->cache_level) {
                        plr->line_size = ci->info_list[i].coherency_line_size;
@@ -1280,12 +1291,6 @@ int rdtgroup_pseudo_lock_create(struct rdtgroup *rdtgrp)
        if (ret < 0)
                return ret;
 
-       ret = pseudo_lock_cstates_constrain(plr);
-       if (ret < 0) {
-               ret = -EINVAL;
-               goto out_region;
-       }
-
        plr->thread_done = 0;
 
        thread = kthread_create_on_node(pseudo_lock_fn, rdtgrp,
@@ -1294,7 +1299,7 @@ int rdtgroup_pseudo_lock_create(struct rdtgroup *rdtgrp)
        if (IS_ERR(thread)) {
                ret = PTR_ERR(thread);
                rdt_last_cmd_printf("Locking thread returned error %d\n", ret);
-               goto out_cstates;
+               goto out_region;
        }
 
        kthread_bind(thread, plr->cpu);
@@ -1312,13 +1317,13 @@ int rdtgroup_pseudo_lock_create(struct rdtgroup *rdtgrp)
                 * empty pseudo-locking loop.
                 */
                rdt_last_cmd_puts("Locking thread interrupted\n");
-               goto out_cstates;
+               goto out_region;
        }
 
        ret = pseudo_lock_minor_get(&new_minor);
        if (ret < 0) {
                rdt_last_cmd_puts("Unable to obtain a new minor number\n");
-               goto out_cstates;
+               goto out_region;
        }
 
        /*
@@ -1375,8 +1380,6 @@ int rdtgroup_pseudo_lock_create(struct rdtgroup *rdtgrp)
 out_debugfs:
        debugfs_remove_recursive(plr->debugfs_dir);
        pseudo_lock_minor_release(new_minor);
-out_cstates:
-       pseudo_lock_cstates_relax(plr);
 out_region:
        pseudo_lock_region_clear(plr);
 out:
@@ -1410,7 +1413,6 @@ void rdtgroup_pseudo_lock_remove(struct rdtgroup *rdtgrp)
                goto free;
        }
 
-       pseudo_lock_cstates_relax(plr);
        debugfs_remove_recursive(rdtgrp->plr->debugfs_dir);
        device_destroy(pseudo_lock_class, MKDEV(pseudo_lock_major, plr->minor));
        pseudo_lock_minor_release(plr->minor);
-- 
2.17.2

Reply via email to