x86/resctrl: Add cpumask_any_housekeeping() for limbo/overflow
[linux-2.6-microblaze.git] / arch / x86 / kernel / cpu / resctrl / monitor.c
index c49f2e8..38f85e5 100644 (file)
@@ -50,6 +50,13 @@ struct rmid_entry {
  */
 static LIST_HEAD(rmid_free_lru);
 
+/*
+ * @closid_num_dirty_rmid    The number of dirty RMID each CLOSID has.
+ *     Only allocated when CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID is defined.
+ *     Indexed by CLOSID. Protected by rdtgroup_mutex.
+ */
+static u32 *closid_num_dirty_rmid;
+
 /*
  * @rmid_limbo_count - count of currently unused but (potentially)
  *     dirty RMIDs.
@@ -292,6 +299,17 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_domain *d,
        return 0;
 }
 
+static void limbo_release_entry(struct rmid_entry *entry)
+{
+       lockdep_assert_held(&rdtgroup_mutex);
+
+       rmid_limbo_count--;
+       list_add_tail(&entry->list, &rmid_free_lru);
+
+       if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID))
+               closid_num_dirty_rmid[entry->closid]--;
+}
+
 /*
  * Check the RMIDs that are marked as busy for this domain. If the
  * reported LLC occupancy is below the threshold clear the busy bit and
@@ -328,10 +346,8 @@ void __check_limbo(struct rdt_domain *d, bool force_free)
 
                if (force_free || !rmid_dirty) {
                        clear_bit(idx, d->rmid_busy_llc);
-                       if (!--entry->busy) {
-                               rmid_limbo_count--;
-                               list_add_tail(&entry->list, &rmid_free_lru);
-                       }
+                       if (!--entry->busy)
+                               limbo_release_entry(entry);
                }
                cur_idx = idx + 1;
        }
@@ -370,6 +386,51 @@ static struct rmid_entry *resctrl_find_free_rmid(u32 closid)
        return ERR_PTR(-ENOSPC);
 }
 
+/**
+ * resctrl_find_cleanest_closid() - Find a CLOSID where all the associated
+ *                                  RMID are clean, or the CLOSID that has
+ *                                  the most clean RMID.
+ *
+ * MPAM's equivalent of RMID are per-CLOSID, meaning a freshly allocated CLOSID
+ * may not be able to allocate clean RMID. To avoid this the allocator will
+ * choose the CLOSID with the most clean RMID.
+ *
+ * When the CLOSID and RMID are independent numbers, the first free CLOSID will
+ * be returned.
+ */
+int resctrl_find_cleanest_closid(void)
+{
+       u32 cleanest_closid = ~0;
+       int i = 0;
+
+       lockdep_assert_held(&rdtgroup_mutex);
+
+       if (!IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID))
+               return -EIO;
+
+       for (i = 0; i < closids_supported(); i++) {
+               int num_dirty;
+
+               if (closid_allocated(i))
+                       continue;
+
+               num_dirty = closid_num_dirty_rmid[i];
+               if (num_dirty == 0)
+                       return i;
+
+               if (cleanest_closid == ~0)
+                       cleanest_closid = i;
+
+               if (num_dirty < closid_num_dirty_rmid[cleanest_closid])
+                       cleanest_closid = i;
+       }
+
+       if (cleanest_closid == ~0)
+               return -ENOSPC;
+
+       return cleanest_closid;
+}
+
 /*
  * For MPAM the RMID value is not unique, and has to be considered with
  * the CLOSID. The (CLOSID, RMID) pair is allocated on all domains, which
@@ -398,6 +459,8 @@ static void add_rmid_to_limbo(struct rmid_entry *entry)
        u64 val = 0;
        u32 idx;
 
+       lockdep_assert_held(&rdtgroup_mutex);
+
        idx = resctrl_arch_rmid_idx_encode(entry->closid, entry->rmid);
 
        entry->busy = 0;
@@ -423,10 +486,13 @@ static void add_rmid_to_limbo(struct rmid_entry *entry)
        }
        put_cpu();
 
-       if (entry->busy)
+       if (entry->busy) {
                rmid_limbo_count++;
-       else
+               if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID))
+                       closid_num_dirty_rmid[entry->closid]++;
+       } else {
                list_add_tail(&entry->list, &rmid_free_lru);
+       }
 }
 
 void free_rmid(u32 closid, u32 rmid)
@@ -695,7 +761,6 @@ static void mbm_update(struct rdt_resource *r, struct rdt_domain *d,
 void cqm_handle_limbo(struct work_struct *work)
 {
        unsigned long delay = msecs_to_jiffies(CQM_LIMBOCHECK_INTERVAL);
-       int cpu = smp_processor_id();
        struct rdt_domain *d;
 
        mutex_lock(&rdtgroup_mutex);
@@ -704,8 +769,11 @@ void cqm_handle_limbo(struct work_struct *work)
 
        __check_limbo(d, false);
 
-       if (has_busy_rmid(d))
-               schedule_delayed_work_on(cpu, &d->cqm_limbo, delay);
+       if (has_busy_rmid(d)) {
+               d->cqm_work_cpu = cpumask_any_housekeeping(&d->cpu_mask);
+               schedule_delayed_work_on(d->cqm_work_cpu, &d->cqm_limbo,
+                                        delay);
+       }
 
        mutex_unlock(&rdtgroup_mutex);
 }
@@ -715,7 +783,7 @@ void cqm_setup_limbo_handler(struct rdt_domain *dom, unsigned long delay_ms)
        unsigned long delay = msecs_to_jiffies(delay_ms);
        int cpu;
 
-       cpu = cpumask_any(&dom->cpu_mask);
+       cpu = cpumask_any_housekeeping(&dom->cpu_mask);
        dom->cqm_work_cpu = cpu;
 
        schedule_delayed_work_on(cpu, &dom->cqm_limbo, delay);
@@ -725,7 +793,6 @@ void mbm_handle_overflow(struct work_struct *work)
 {
        unsigned long delay = msecs_to_jiffies(MBM_OVERFLOW_INTERVAL);
        struct rdtgroup *prgrp, *crgrp;
-       int cpu = smp_processor_id();
        struct list_head *head;
        struct rdt_resource *r;
        struct rdt_domain *d;
@@ -749,7 +816,12 @@ void mbm_handle_overflow(struct work_struct *work)
                        update_mba_bw(prgrp, d);
        }
 
-       schedule_delayed_work_on(cpu, &d->mbm_over, delay);
+       /*
+        * Re-check for housekeeping CPUs. This allows the overflow handler to
+        * move off a nohz_full CPU quickly.
+        */
+       d->mbm_work_cpu = cpumask_any_housekeeping(&d->cpu_mask);
+       schedule_delayed_work_on(d->mbm_work_cpu, &d->mbm_over, delay);
 
 out_unlock:
        mutex_unlock(&rdtgroup_mutex);
@@ -762,7 +834,7 @@ void mbm_setup_overflow_handler(struct rdt_domain *dom, unsigned long delay_ms)
 
        if (!static_branch_likely(&rdt_mon_enable_key))
                return;
-       cpu = cpumask_any(&dom->cpu_mask);
+       cpu = cpumask_any_housekeeping(&dom->cpu_mask);
        dom->mbm_work_cpu = cpu;
        schedule_delayed_work_on(cpu, &dom->mbm_over, delay);
 }
@@ -770,13 +842,39 @@ void mbm_setup_overflow_handler(struct rdt_domain *dom, unsigned long delay_ms)
 static int dom_data_init(struct rdt_resource *r)
 {
        u32 idx_limit = resctrl_arch_system_num_rmid_idx();
+       u32 num_closid = resctrl_arch_get_num_closid(r);
        struct rmid_entry *entry = NULL;
+       int err = 0, i;
        u32 idx;
-       int i;
+
+       mutex_lock(&rdtgroup_mutex);
+       if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID)) {
+               u32 *tmp;
+
+               /*
+                * If the architecture hasn't provided a sanitised value here,
+                * this may result in larger arrays than necessary. Resctrl will
+                * use a smaller system wide value based on the resources in
+                * use.
+                */
+               tmp = kcalloc(num_closid, sizeof(*tmp), GFP_KERNEL);
+               if (!tmp) {
+                       err = -ENOMEM;
+                       goto out_unlock;
+               }
+
+               closid_num_dirty_rmid = tmp;
+       }
 
        rmid_ptrs = kcalloc(idx_limit, sizeof(struct rmid_entry), GFP_KERNEL);
-       if (!rmid_ptrs)
-               return -ENOMEM;
+       if (!rmid_ptrs) {
+               if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID)) {
+                       kfree(closid_num_dirty_rmid);
+                       closid_num_dirty_rmid = NULL;
+               }
+               err = -ENOMEM;
+               goto out_unlock;
+       }
 
        for (i = 0; i < idx_limit; i++) {
                entry = &rmid_ptrs[i];
@@ -796,13 +894,21 @@ static int dom_data_init(struct rdt_resource *r)
        entry = __rmid_entry(idx);
        list_del(&entry->list);
 
-       return 0;
+out_unlock:
+       mutex_unlock(&rdtgroup_mutex);
+
+       return err;
 }
 
 static void __exit dom_data_exit(void)
 {
        mutex_lock(&rdtgroup_mutex);
 
+       if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID)) {
+               kfree(closid_num_dirty_rmid);
+               closid_num_dirty_rmid = NULL;
+       }
+
        kfree(rmid_ptrs);
        rmid_ptrs = NULL;