x86/resctrl: Separate arch and fs resctrl locks
[linux-2.6-microblaze.git] / arch / x86 / kernel / cpu / resctrl / core.c
index b03a6c6..8a4ef4f 100644 (file)
@@ -16,6 +16,7 @@
 
 #define pr_fmt(fmt)    "resctrl: " fmt
 
+#include <linux/cpu.h>
 #include <linux/slab.h>
 #include <linux/err.h>
 #include <linux/cacheinfo.h>
 #include <asm/resctrl.h>
 #include "internal.h"
 
-/* Mutex to protect rdtgroup access. */
-DEFINE_MUTEX(rdtgroup_mutex);
+/*
+ * rdt_domain structures are kfree()d when their last CPU goes offline,
+ * and allocated when the first CPU in a new domain comes online.
+ * The rdt_resource's domain list is updated when this happens. Readers of
+ * the domain list must either take cpus_read_lock(), or rely on an RCU
+ * read-side critical section, to avoid observing concurrent modification.
+ * All writers take this mutex:
+ */
+static DEFINE_MUTEX(domain_list_lock);
 
 /*
  * The cached resctrl_pqr_state is strictly per CPU and can never be
@@ -354,6 +362,15 @@ struct rdt_domain *get_domain_from_cpu(int cpu, struct rdt_resource *r)
 {
        struct rdt_domain *d;
 
+       /*
+        * Walking r->domains, ensure it can't race with cpuhp.
+        * Because this is called via IPI by rdt_ctrl_update(), assertions
+        * about locks this thread holds will lead to false positives. Check
+        * someone is holding the CPUs lock.
+        */
+       if (IS_ENABLED(CONFIG_HOTPLUG_CPU) && IS_ENABLED(CONFIG_LOCKDEP))
+               WARN_ON_ONCE(!lockdep_is_cpus_held());
+
        list_for_each_entry(d, &r->domains, list) {
                /* Find the domain that contains this CPU */
                if (cpumask_test_cpu(cpu, &d->cpu_mask))
@@ -510,6 +527,8 @@ static void domain_add_cpu(int cpu, struct rdt_resource *r)
        struct rdt_domain *d;
        int err;
 
+       lockdep_assert_held(&domain_list_lock);
+
        d = rdt_find_domain(r, id, &add_pos);
        if (IS_ERR(d)) {
                pr_warn("Couldn't find cache id for CPU %d\n", cpu);
@@ -543,11 +562,12 @@ static void domain_add_cpu(int cpu, struct rdt_resource *r)
                return;
        }
 
-       list_add_tail(&d->list, add_pos);
+       list_add_tail_rcu(&d->list, add_pos);
 
        err = resctrl_online_domain(r, d);
        if (err) {
-               list_del(&d->list);
+               list_del_rcu(&d->list);
+               synchronize_rcu();
                domain_free(hw_dom);
        }
 }
@@ -558,6 +578,8 @@ static void domain_remove_cpu(int cpu, struct rdt_resource *r)
        struct rdt_hw_domain *hw_dom;
        struct rdt_domain *d;
 
+       lockdep_assert_held(&domain_list_lock);
+
        d = rdt_find_domain(r, id, NULL);
        if (IS_ERR_OR_NULL(d)) {
                pr_warn("Couldn't find cache id for CPU %d\n", cpu);
@@ -568,7 +590,8 @@ static void domain_remove_cpu(int cpu, struct rdt_resource *r)
        cpumask_clear_cpu(cpu, &d->cpu_mask);
        if (cpumask_empty(&d->cpu_mask)) {
                resctrl_offline_domain(r, d);
-               list_del(&d->list);
+               list_del_rcu(&d->list);
+               synchronize_rcu();
 
                /*
                 * rdt_domain "d" is going to be freed below, so clear
@@ -598,13 +621,13 @@ static int resctrl_arch_online_cpu(unsigned int cpu)
 {
        struct rdt_resource *r;
 
-       mutex_lock(&rdtgroup_mutex);
+       mutex_lock(&domain_list_lock);
        for_each_capable_rdt_resource(r)
                domain_add_cpu(cpu, r);
-       clear_closid_rmid(cpu);
+       mutex_unlock(&domain_list_lock);
 
+       clear_closid_rmid(cpu);
        resctrl_online_cpu(cpu);
-       mutex_unlock(&rdtgroup_mutex);
 
        return 0;
 }
@@ -613,13 +636,14 @@ static int resctrl_arch_offline_cpu(unsigned int cpu)
 {
        struct rdt_resource *r;
 
-       mutex_lock(&rdtgroup_mutex);
        resctrl_offline_cpu(cpu);
 
+       mutex_lock(&domain_list_lock);
        for_each_capable_rdt_resource(r)
                domain_remove_cpu(cpu, r);
+       mutex_unlock(&domain_list_lock);
+
        clear_closid_rmid(cpu);
-       mutex_unlock(&rdtgroup_mutex);
 
        return 0;
 }