x86/intel_rdt/cqm: Improve limbo list processing

During a mkdir, the entire limbo list is synchronously checked on each
package for free RMIDs by sending IPIs. With a large number of RMIDs (SKL
has 192) this creates a intolerable amount of work in IPIs.

Replace the IPI based checking of the limbo list with asynchronous worker
threads on each package which periodically scan the limbo list and move the
RMIDs that have:

llc_occupancy < threshold_occupancy

on all packages to the free list.

mkdir now returns -ENOSPC if the free list and the limbo list ere empty or
returns -EBUSY if there are RMIDs on the limbo list and the free list is
empty.

Getting rid of the IPIs also simplifies the data structures and the
serialization required for handling the lists.

[ tglx: Rewrote changelog ... ]

Signed-off-by: Vikas Shivappa <vikas.shivappa@linux.intel.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: ravi.v.shankar@intel.com
Cc: tony.luck@intel.com
Cc: fenghua.yu@intel.com
Cc: peterz@infradead.org
Cc: eranian@google.com
Cc: vikas.shivappa@intel.com
Cc: ak@linux.intel.com
Cc: davidcc@google.com
Link: http://lkml.kernel.org/r/1502845243-20454-3-git-send-email-vikas.shivappa@linux.intel.com

authored by

Vikas Shivappa and committed by
Thomas Gleixner
24247aee bbc4615e

+136 -125
+27 -4
arch/x86/kernel/cpu/intel_rdt.c
··· 426 GFP_KERNEL); 427 if (!d->rmid_busy_llc) 428 return -ENOMEM; 429 } 430 if (is_mbm_total_enabled()) { 431 tsize = sizeof(*d->mbm_total); ··· 537 list_del(&d->list); 538 if (is_mbm_enabled()) 539 cancel_delayed_work(&d->mbm_over); 540 kfree(d); 541 - } else if (r == &rdt_resources_all[RDT_RESOURCE_L3] && 542 - cpu == d->mbm_work_cpu && is_mbm_enabled()) { 543 - cancel_delayed_work(&d->mbm_over); 544 - mbm_setup_overflow_handler(d, 0); 545 } 546 } 547
··· 426 GFP_KERNEL); 427 if (!d->rmid_busy_llc) 428 return -ENOMEM; 429 + INIT_DELAYED_WORK(&d->cqm_limbo, cqm_handle_limbo); 430 } 431 if (is_mbm_total_enabled()) { 432 tsize = sizeof(*d->mbm_total); ··· 536 list_del(&d->list); 537 if (is_mbm_enabled()) 538 cancel_delayed_work(&d->mbm_over); 539 + if (is_llc_occupancy_enabled() && has_busy_rmid(r, d)) { 540 + /* 541 + * When a package is going down, forcefully 542 + * decrement rmid->ebusy. There is no way to know 543 + * that the L3 was flushed and hence may lead to 544 + * incorrect counts in rare scenarios, but leaving 545 + * the RMID as busy creates RMID leaks if the 546 + * package never comes back. 547 + */ 548 + __check_limbo(d, true); 549 + cancel_delayed_work(&d->cqm_limbo); 550 + } 551 + 552 kfree(d); 553 + return; 554 + } 555 + 556 + if (r == &rdt_resources_all[RDT_RESOURCE_L3]) { 557 + if (is_mbm_enabled() && cpu == d->mbm_work_cpu) { 558 + cancel_delayed_work(&d->mbm_over); 559 + mbm_setup_overflow_handler(d, 0); 560 + } 561 + if (is_llc_occupancy_enabled() && cpu == d->cqm_work_cpu && 562 + has_busy_rmid(r, d)) { 563 + cancel_delayed_work(&d->cqm_limbo); 564 + cqm_setup_limbo_handler(d, 0); 565 + } 566 } 567 } 568
+13 -1
arch/x86/kernel/cpu/intel_rdt.h
··· 20 #define QOS_L3_MBM_TOTAL_EVENT_ID 0x02 21 #define QOS_L3_MBM_LOCAL_EVENT_ID 0x03 22 23 #define MBM_CNTR_WIDTH 24 24 #define MBM_OVERFLOW_INTERVAL 1000 25 ··· 189 * @mbm_total: saved state for MBM total bandwidth 190 * @mbm_local: saved state for MBM local bandwidth 191 * @mbm_over: worker to periodically read MBM h/w counters 192 * @mbm_work_cpu: 193 * worker cpu for MBM h/w counters 194 * @ctrl_val: array of cache or mem ctrl values (indexed by CLOSID) 195 * @new_ctrl: new ctrl value to be loaded 196 * @have_new_ctrl: did user provide new_ctrl for this domain ··· 206 struct mbm_state *mbm_total; 207 struct mbm_state *mbm_local; 208 struct delayed_work mbm_over; 209 int mbm_work_cpu; 210 u32 *ctrl_val; 211 u32 new_ctrl; 212 bool have_new_ctrl; ··· 429 struct rdt_domain *d); 430 void mon_event_read(struct rmid_read *rr, struct rdt_domain *d, 431 struct rdtgroup *rdtgrp, int evtid, int first); 432 - void mbm_setup_overflow_handler(struct rdt_domain *dom, unsigned long delay_ms); 433 void mbm_handle_overflow(struct work_struct *work); 434 435 #endif /* _ASM_X86_INTEL_RDT_H */
··· 20 #define QOS_L3_MBM_TOTAL_EVENT_ID 0x02 21 #define QOS_L3_MBM_LOCAL_EVENT_ID 0x03 22 23 + #define CQM_LIMBOCHECK_INTERVAL 1000 24 + 25 #define MBM_CNTR_WIDTH 24 26 #define MBM_OVERFLOW_INTERVAL 1000 27 ··· 187 * @mbm_total: saved state for MBM total bandwidth 188 * @mbm_local: saved state for MBM local bandwidth 189 * @mbm_over: worker to periodically read MBM h/w counters 190 + * @cqm_limbo: worker to periodically read CQM h/w counters 191 * @mbm_work_cpu: 192 * worker cpu for MBM h/w counters 193 + * @cqm_work_cpu: 194 + * worker cpu for CQM h/w counters 195 * @ctrl_val: array of cache or mem ctrl values (indexed by CLOSID) 196 * @new_ctrl: new ctrl value to be loaded 197 * @have_new_ctrl: did user provide new_ctrl for this domain ··· 201 struct mbm_state *mbm_total; 202 struct mbm_state *mbm_local; 203 struct delayed_work mbm_over; 204 + struct delayed_work cqm_limbo; 205 int mbm_work_cpu; 206 + int cqm_work_cpu; 207 u32 *ctrl_val; 208 u32 new_ctrl; 209 bool have_new_ctrl; ··· 422 struct rdt_domain *d); 423 void mon_event_read(struct rmid_read *rr, struct rdt_domain *d, 424 struct rdtgroup *rdtgrp, int evtid, int first); 425 + void mbm_setup_overflow_handler(struct rdt_domain *dom, 426 + unsigned long delay_ms); 427 void mbm_handle_overflow(struct work_struct *work); 428 + void cqm_setup_limbo_handler(struct rdt_domain *dom, unsigned long delay_ms); 429 + void cqm_handle_limbo(struct work_struct *work); 430 + bool has_busy_rmid(struct rdt_resource *r, struct rdt_domain *d); 431 + void __check_limbo(struct rdt_domain *d, bool force_free); 432 433 #endif /* _ASM_X86_INTEL_RDT_H */
+96 -120
arch/x86/kernel/cpu/intel_rdt_monitor.c
··· 33 34 struct rmid_entry { 35 u32 rmid; 36 - atomic_t busy; 37 struct list_head list; 38 }; 39 ··· 45 static LIST_HEAD(rmid_free_lru); 46 47 /** 48 - * @rmid_limbo_lru list of currently unused but (potentially) 49 * dirty RMIDs. 50 - * This list contains RMIDs that no one is currently using but that 51 * may have a occupancy value > intel_cqm_threshold. User can change 52 * the threshold occupancy value. 53 */ 54 - static LIST_HEAD(rmid_limbo_lru); 55 56 /** 57 * @rmid_entry - The entry in the limbo and free lists. ··· 103 return val; 104 } 105 106 /* 107 - * Walk the limbo list looking at any RMIDs that are flagged in the 108 - * domain rmid_busy_llc bitmap as busy. If the reported LLC occupancy 109 - * is below the threshold clear the busy bit and decrement the count. 110 - * If the busy count gets to zero on an RMID we stop looking. 111 - * This can be called from an IPI. 112 - * We need an atomic for the busy count because multiple CPUs may check 113 - * the same RMID at the same time. 114 */ 115 - static bool __check_limbo(struct rdt_domain *d) 116 { 117 struct rmid_entry *entry; 118 - u64 val; 119 - 120 - list_for_each_entry(entry, &rmid_limbo_lru, list) { 121 - if (!test_bit(entry->rmid, d->rmid_busy_llc)) 122 - continue; 123 - val = __rmid_read(entry->rmid, QOS_L3_OCCUP_EVENT_ID); 124 - if (val <= intel_cqm_threshold) { 125 - clear_bit(entry->rmid, d->rmid_busy_llc); 126 - if (atomic_dec_and_test(&entry->busy)) 127 - return true; 128 - } 129 - } 130 - return false; 131 - } 132 - 133 - static void check_limbo(void *arg) 134 - { 135 - struct rdt_domain *d; 136 - 137 - d = get_domain_from_cpu(smp_processor_id(), 138 - &rdt_resources_all[RDT_RESOURCE_L3]); 139 - 140 - if (d) 141 - __check_limbo(d); 142 - } 143 - 144 - static bool has_busy_rmid(struct rdt_resource *r, struct rdt_domain *d) 145 - { 146 - return find_first_bit(d->rmid_busy_llc, r->num_rmid) != r->num_rmid; 147 - } 148 - 149 - /* 150 - * Scan the limbo list and move all entries that are below the 151 - * intel_cqm_threshold to the free list. 152 - * Return "true" if the limbo list is empty, "false" if there are 153 - * still some RMIDs there. 154 - */ 155 - static bool try_freeing_limbo_rmid(void) 156 - { 157 - struct rmid_entry *entry, *tmp; 158 struct rdt_resource *r; 159 - cpumask_var_t cpu_mask; 160 - struct rdt_domain *d; 161 - bool ret = true; 162 - int cpu; 163 - 164 - if (list_empty(&rmid_limbo_lru)) 165 - return ret; 166 167 r = &rdt_resources_all[RDT_RESOURCE_L3]; 168 169 - cpu = get_cpu(); 170 - 171 /* 172 - * First see if we can free up an RMID by checking busy values 173 - * on the local package. 174 */ 175 - d = get_domain_from_cpu(cpu, r); 176 - if (d && has_busy_rmid(r, d) && __check_limbo(d)) { 177 - list_for_each_entry_safe(entry, tmp, &rmid_limbo_lru, list) { 178 - if (atomic_read(&entry->busy) == 0) { 179 - list_del(&entry->list); 180 list_add_tail(&entry->list, &rmid_free_lru); 181 - goto done; 182 } 183 } 184 } 185 186 - if (!zalloc_cpumask_var(&cpu_mask, GFP_KERNEL)) { 187 - ret = false; 188 - goto done; 189 - } 190 - 191 - /* 192 - * Build a mask of other domains that have busy RMIDs 193 - */ 194 - list_for_each_entry(d, &r->domains, list) { 195 - if (!cpumask_test_cpu(cpu, &d->cpu_mask) && 196 - has_busy_rmid(r, d)) 197 - cpumask_set_cpu(cpumask_any(&d->cpu_mask), cpu_mask); 198 - } 199 - if (cpumask_empty(cpu_mask)) { 200 - ret = false; 201 - goto free_mask; 202 - } 203 - 204 - /* 205 - * Scan domains with busy RMIDs to check if they still are busy 206 - */ 207 - on_each_cpu_mask(cpu_mask, check_limbo, NULL, true); 208 - 209 - /* Walk limbo list moving all free RMIDs to the &rmid_free_lru list */ 210 - list_for_each_entry_safe(entry, tmp, &rmid_limbo_lru, list) { 211 - if (atomic_read(&entry->busy) != 0) { 212 - ret = false; 213 - continue; 214 - } 215 - list_del(&entry->list); 216 - list_add_tail(&entry->list, &rmid_free_lru); 217 - } 218 - 219 - free_mask: 220 - free_cpumask_var(cpu_mask); 221 - done: 222 - put_cpu(); 223 - return ret; 224 } 225 226 /* ··· 160 int alloc_rmid(void) 161 { 162 struct rmid_entry *entry; 163 - bool ret; 164 165 lockdep_assert_held(&rdtgroup_mutex); 166 167 - if (list_empty(&rmid_free_lru)) { 168 - ret = try_freeing_limbo_rmid(); 169 - if (list_empty(&rmid_free_lru)) 170 - return ret ? -ENOSPC : -EBUSY; 171 - } 172 173 entry = list_first_entry(&rmid_free_lru, 174 struct rmid_entry, list); ··· 177 { 178 struct rdt_resource *r; 179 struct rdt_domain *d; 180 - int cpu, nbusy = 0; 181 u64 val; 182 183 r = &rdt_resources_all[RDT_RESOURCE_L3]; 184 185 cpu = get_cpu(); 186 list_for_each_entry(d, &r->domains, list) { 187 if (cpumask_test_cpu(cpu, &d->cpu_mask)) { ··· 190 if (val <= intel_cqm_threshold) 191 continue; 192 } 193 set_bit(entry->rmid, d->rmid_busy_llc); 194 - nbusy++; 195 } 196 put_cpu(); 197 198 - if (nbusy) { 199 - atomic_set(&entry->busy, nbusy); 200 - list_add_tail(&entry->list, &rmid_limbo_lru); 201 - } else { 202 list_add_tail(&entry->list, &rmid_free_lru); 203 - } 204 } 205 206 void free_rmid(u32 rmid) ··· 318 } 319 } 320 321 void mbm_handle_overflow(struct work_struct *work) 322 { 323 unsigned long delay = msecs_to_jiffies(MBM_OVERFLOW_INTERVAL); ··· 388 } 389 390 schedule_delayed_work_on(cpu, &d->mbm_over, delay); 391 out_unlock: 392 mutex_unlock(&rdtgroup_mutex); 393 }
··· 33 34 struct rmid_entry { 35 u32 rmid; 36 + int busy; 37 struct list_head list; 38 }; 39 ··· 45 static LIST_HEAD(rmid_free_lru); 46 47 /** 48 + * @rmid_limbo_count count of currently unused but (potentially) 49 * dirty RMIDs. 50 + * This counts RMIDs that no one is currently using but that 51 * may have a occupancy value > intel_cqm_threshold. User can change 52 * the threshold occupancy value. 53 */ 54 + unsigned int rmid_limbo_count; 55 56 /** 57 * @rmid_entry - The entry in the limbo and free lists. ··· 103 return val; 104 } 105 106 + static bool rmid_dirty(struct rmid_entry *entry) 107 + { 108 + u64 val = __rmid_read(entry->rmid, QOS_L3_OCCUP_EVENT_ID); 109 + 110 + return val >= intel_cqm_threshold; 111 + } 112 + 113 /* 114 + * Check the RMIDs that are marked as busy for this domain. If the 115 + * reported LLC occupancy is below the threshold clear the busy bit and 116 + * decrement the count. If the busy count gets to zero on an RMID, we 117 + * free the RMID 118 */ 119 + void __check_limbo(struct rdt_domain *d, bool force_free) 120 { 121 struct rmid_entry *entry; 122 struct rdt_resource *r; 123 + u32 crmid = 1, nrmid; 124 125 r = &rdt_resources_all[RDT_RESOURCE_L3]; 126 127 /* 128 + * Skip RMID 0 and start from RMID 1 and check all the RMIDs that 129 + * are marked as busy for occupancy < threshold. If the occupancy 130 + * is less than the threshold decrement the busy counter of the 131 + * RMID and move it to the free list when the counter reaches 0. 132 */ 133 + for (;;) { 134 + nrmid = find_next_bit(d->rmid_busy_llc, r->num_rmid, crmid); 135 + if (nrmid >= r->num_rmid) 136 + break; 137 + 138 + entry = __rmid_entry(nrmid); 139 + if (force_free || !rmid_dirty(entry)) { 140 + clear_bit(entry->rmid, d->rmid_busy_llc); 141 + if (!--entry->busy) { 142 + rmid_limbo_count--; 143 list_add_tail(&entry->list, &rmid_free_lru); 144 } 145 } 146 + crmid = nrmid + 1; 147 } 148 + } 149 150 + bool has_busy_rmid(struct rdt_resource *r, struct rdt_domain *d) 151 + { 152 + return find_first_bit(d->rmid_busy_llc, r->num_rmid) != r->num_rmid; 153 } 154 155 /* ··· 231 int alloc_rmid(void) 232 { 233 struct rmid_entry *entry; 234 235 lockdep_assert_held(&rdtgroup_mutex); 236 237 + if (list_empty(&rmid_free_lru)) 238 + return rmid_limbo_count ? -EBUSY : -ENOSPC; 239 240 entry = list_first_entry(&rmid_free_lru, 241 struct rmid_entry, list); ··· 252 { 253 struct rdt_resource *r; 254 struct rdt_domain *d; 255 + int cpu; 256 u64 val; 257 258 r = &rdt_resources_all[RDT_RESOURCE_L3]; 259 260 + entry->busy = 0; 261 cpu = get_cpu(); 262 list_for_each_entry(d, &r->domains, list) { 263 if (cpumask_test_cpu(cpu, &d->cpu_mask)) { ··· 264 if (val <= intel_cqm_threshold) 265 continue; 266 } 267 + 268 + /* 269 + * For the first limbo RMID in the domain, 270 + * setup up the limbo worker. 271 + */ 272 + if (!has_busy_rmid(r, d)) 273 + cqm_setup_limbo_handler(d, CQM_LIMBOCHECK_INTERVAL); 274 set_bit(entry->rmid, d->rmid_busy_llc); 275 + entry->busy++; 276 } 277 put_cpu(); 278 279 + if (entry->busy) 280 + rmid_limbo_count++; 281 + else 282 list_add_tail(&entry->list, &rmid_free_lru); 283 } 284 285 void free_rmid(u32 rmid) ··· 387 } 388 } 389 390 + /* 391 + * Handler to scan the limbo list and move the RMIDs 392 + * to free list whose occupancy < threshold_occupancy. 393 + */ 394 + void cqm_handle_limbo(struct work_struct *work) 395 + { 396 + unsigned long delay = msecs_to_jiffies(CQM_LIMBOCHECK_INTERVAL); 397 + int cpu = smp_processor_id(); 398 + struct rdt_resource *r; 399 + struct rdt_domain *d; 400 + 401 + mutex_lock(&rdtgroup_mutex); 402 + 403 + r = &rdt_resources_all[RDT_RESOURCE_L3]; 404 + d = get_domain_from_cpu(cpu, r); 405 + 406 + if (!d) { 407 + pr_warn_once("Failure to get domain for limbo worker\n"); 408 + goto out_unlock; 409 + } 410 + 411 + __check_limbo(d, false); 412 + 413 + if (has_busy_rmid(r, d)) 414 + schedule_delayed_work_on(cpu, &d->cqm_limbo, delay); 415 + 416 + out_unlock: 417 + mutex_unlock(&rdtgroup_mutex); 418 + } 419 + 420 + void cqm_setup_limbo_handler(struct rdt_domain *dom, unsigned long delay_ms) 421 + { 422 + unsigned long delay = msecs_to_jiffies(delay_ms); 423 + struct rdt_resource *r; 424 + int cpu; 425 + 426 + r = &rdt_resources_all[RDT_RESOURCE_L3]; 427 + 428 + cpu = cpumask_any(&dom->cpu_mask); 429 + dom->cqm_work_cpu = cpu; 430 + 431 + schedule_delayed_work_on(cpu, &dom->cqm_limbo, delay); 432 + } 433 + 434 void mbm_handle_overflow(struct work_struct *work) 435 { 436 unsigned long delay = msecs_to_jiffies(MBM_OVERFLOW_INTERVAL); ··· 413 } 414 415 schedule_delayed_work_on(cpu, &d->mbm_over, delay); 416 + 417 out_unlock: 418 mutex_unlock(&rdtgroup_mutex); 419 }