[RFC PATCH 2/7] x86/intel_rdt: Enable a pseudo-locked region to be restored

From: Reinette Chatre
Date: Tue Jul 24 2018 - 16:41:25 EST


Memory that has been pseudo-locked to cache could be evicted if
an instruction like WBINVD has been used on a core in the cache
hierarchy. When this happens the region of cache would remained
orphaned.

Make it possible for pseudo-locked memory that may have been evicted
to be restored to its original cache region. In preparation for
the restoration to be triggered from user space as well as automatically
within the kernel the restoration does not assume that the
last_cmd_status log facility is available, instead, if any errors do
occur during restoration, they will be written to the kernel log buffer.

Temporarily add the unused attribute to the function. It will be removed
at the time the function is used.

Signed-off-by: Reinette Chatre <reinette.chatre@xxxxxxxxx>
---
arch/x86/kernel/cpu/intel_rdt_pseudo_lock.c | 111 ++++++++++++++++++++
1 file changed, 111 insertions(+)

diff --git a/arch/x86/kernel/cpu/intel_rdt_pseudo_lock.c b/arch/x86/kernel/cpu/intel_rdt_pseudo_lock.c
index f80c58f8adc3..693f99594e9f 100644
--- a/arch/x86/kernel/cpu/intel_rdt_pseudo_lock.c
+++ b/arch/x86/kernel/cpu/intel_rdt_pseudo_lock.c
@@ -453,6 +453,16 @@ static int pseudo_lock_fn(void *_rdtgrp)
#endif /* CONFIG_X86_64 */
#endif /* CONFIG_KASAN */

+ /*
+ * In the case of a pseudo-locked region restore, if the thread
+ * does not get on the CPU and the process attempting
+ * a restore is interrupted then the thread remains in runnable
+ * state but the closid would have been freed and nothing is
+ * waiting on this restoration anymore.
+ */
+ if (!closid_allocated(rdtgrp->closid))
+ return 0;
+
/*
* Make sure none of the allocated memory is cached. If it is we
* will get a cache hit in below loop from outside of pseudo-locked
@@ -1191,6 +1201,107 @@ static const struct file_operations pseudo_measure_fops = {
.llseek = default_llseek,
};

+/**
+ * _pseudo_lock_restore - Pseudo-Lock possibly evicted memory again
+ * @rdtgrp: Resource group to which pseudo-locked region belongs
+ *
+ * Pseudo-locked memory could be evicted from the cache with an instruction
+ * like WBINVD or when the cache is affected by platform power savings. In
+ * support of recovering from these situations the pseudo-locked region can
+ * be restored by pseudo-locking the memory again.
+ *
+ * Return: 0 if region was successfully restored, <0 on failure.
+ */
+static int _pseudo_lock_restore(struct rdtgroup *rdtgrp)
+{
+ struct pseudo_lock_region *plr = rdtgrp->plr;
+ struct task_struct *thread;
+ struct rdt_resource *r;
+ struct rdt_domain *dom;
+ int closid;
+ int ret;
+
+ plr->cpu = cpumask_first(&plr->d->cpu_mask);
+
+ if (!cpu_online(plr->cpu)) {
+ pr_err("Failed to restore region %s. Associated CPU offline.\n",
+ rdtgrp->kn->name);
+ ret = -ENODEV;
+ goto out;
+ }
+
+ closid = closid_alloc();
+ if (closid < 0) {
+ pr_err("Failed to restore region %s. No free closid.\n",
+ rdtgrp->kn->name);
+ ret = -ENOSPC;
+ goto out;
+ }
+
+ rdtgrp->closid = closid;
+
+ /*
+ * Associate the newly allocated closid with the CBM of the
+ * Cache Pseudo-Locked region.
+ */
+ for_each_alloc_enabled_rdt_resource(r) {
+ list_for_each_entry(dom, &r->domains, list)
+ dom->have_new_ctrl = false;
+ }
+
+ plr->d->new_ctrl = plr->cbm;
+ plr->d->have_new_ctrl = true;
+
+ ret = update_domains(plr->r, rdtgrp->closid);
+ if (ret < 0) {
+ pr_err("Unable to program CBM of region %s\n",
+ rdtgrp->kn->name);
+ goto out_closid;
+ }
+
+ plr->thread_done = 0;
+
+ thread = kthread_create_on_node(pseudo_lock_fn, rdtgrp,
+ cpu_to_node(plr->cpu),
+ "pseudo_lock/%u", plr->cpu);
+ if (IS_ERR(thread)) {
+ ret = PTR_ERR(thread);
+ pr_err("Locking thread error %d during restore of %s\n",
+ ret, rdtgrp->kn->name);
+ goto out_closid;
+ }
+
+ kthread_bind(thread, plr->cpu);
+ wake_up_process(thread);
+
+ ret = wait_event_interruptible(plr->lock_thread_wq,
+ plr->thread_done == 1);
+ if (ret < 0) {
+ pr_err("Locking thread interrupted during restore of %s\n",
+ rdtgrp->kn->name);
+ goto out_closid;
+ }
+
+ ret = 0;
+
+out_closid:
+ closid_free(rdtgrp->closid);
+out:
+ return ret;
+}
+
+static int __attribute__ ((unused)) pseudo_lock_restore(struct rdtgroup *rdtgrp)
+{
+ int ret;
+
+ cpus_read_lock();
+ mutex_lock(&rdtgroup_mutex);
+ ret = _pseudo_lock_restore(rdtgrp);
+ mutex_unlock(&rdtgroup_mutex);
+ cpus_read_unlock();
+ return ret;
+}
+
/**
* rdtgroup_pseudo_lock_create - Create a pseudo-locked region
* @rdtgrp: resource group to which pseudo-lock region belongs
--
2.17.0