Skip to content

Commit edf6fa1

Browse files
Vikas ShivappaKAGA-KOKO
authored andcommitted
x86/intel_rdt/cqm: Add RMID (Resource monitoring ID) management
Hardware uses RMID(Resource monitoring ID) to keep track of each of the RDT events associated with tasks. The number of RMIDs is dependent on the SKU and is enumerated via CPUID. We add support to manage the RMIDs which include managing the RMID allocation and reading LLC occupancy for an RMID. RMID allocation is managed by keeping a free list which is initialized to all available RMIDs except for RMID 0 which is always reserved for root group. RMIDs goto a limbo list once they are freed since the RMIDs are still tagged to cache lines of the tasks which were using them - thereby still having some occupancy. They continue to be in limbo list until the occupancy < threshold_occupancy. The threshold_occupancy is a user configurable value. OS uses IA32_QM_CTR MSR to read the occupancy associated with an RMID after programming the IA32_EVENTSEL MSR with the RMID. [Tony: Improved limbo search] Signed-off-by: Vikas Shivappa <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Cc: [email protected] Link: http://lkml.kernel.org/r/1501017287-28083-10-git-send-email-vikas.shivappa@linux.intel.com
1 parent 6a445ed commit edf6fa1

File tree

3 files changed

+251
-0
lines changed

3 files changed

+251
-0
lines changed

arch/x86/kernel/cpu/intel_rdt.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,19 @@ cat_wrmsr(struct rdt_domain *d, struct msr_param *m, struct rdt_resource *r)
320320
wrmsrl(r->msr_base + cbm_idx(r, i), d->ctrl_val[i]);
321321
}
322322

323+
struct rdt_domain *get_domain_from_cpu(int cpu, struct rdt_resource *r)
324+
{
325+
struct rdt_domain *d;
326+
327+
list_for_each_entry(d, &r->domains, list) {
328+
/* Find the domain that contains this CPU */
329+
if (cpumask_test_cpu(cpu, &d->cpu_mask))
330+
return d;
331+
}
332+
333+
return NULL;
334+
}
335+
323336
void rdt_ctrl_update(void *arg)
324337
{
325338
struct msr_param *m = arg;
@@ -397,6 +410,19 @@ static int domain_setup_ctrlval(struct rdt_resource *r, struct rdt_domain *d)
397410
return 0;
398411
}
399412

413+
static int domain_setup_mon_state(struct rdt_resource *r, struct rdt_domain *d)
414+
{
415+
if (is_llc_occupancy_enabled()) {
416+
d->rmid_busy_llc = kcalloc(BITS_TO_LONGS(r->num_rmid),
417+
sizeof(unsigned long),
418+
GFP_KERNEL);
419+
if (!d->rmid_busy_llc)
420+
return -ENOMEM;
421+
}
422+
423+
return 0;
424+
}
425+
400426
/*
401427
* domain_add_cpu - Add a cpu to a resource's domain list.
402428
*
@@ -438,6 +464,11 @@ static void domain_add_cpu(int cpu, struct rdt_resource *r)
438464
return;
439465
}
440466

467+
if (r->mon_capable && domain_setup_mon_state(r, d)) {
468+
kfree(d);
469+
return;
470+
}
471+
441472
cpumask_set_cpu(cpu, &d->cpu_mask);
442473
list_add_tail(&d->list, add_pos);
443474
}
@@ -456,6 +487,7 @@ static void domain_remove_cpu(int cpu, struct rdt_resource *r)
456487
cpumask_clear_cpu(cpu, &d->cpu_mask);
457488
if (cpumask_empty(&d->cpu_mask)) {
458489
kfree(d->ctrl_val);
490+
kfree(d->rmid_busy_llc);
459491
list_del(&d->list);
460492
kfree(d);
461493
}

arch/x86/kernel/cpu/intel_rdt.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
#define QOS_L3_OCCUP_EVENT_ID 0x01
2020
#define QOS_L3_MBM_TOTAL_EVENT_ID 0x02
2121
#define QOS_L3_MBM_LOCAL_EVENT_ID 0x03
22+
#define RMID_VAL_ERROR BIT_ULL(63)
23+
#define RMID_VAL_UNAVAIL BIT_ULL(62)
2224

2325
/**
2426
* struct mon_evt - Entry in the event list of a resource
@@ -98,6 +100,8 @@ struct rftype {
98100
* @list: all instances of this resource
99101
* @id: unique id for this instance
100102
* @cpu_mask: which cpus share this resource
103+
* @rmid_busy_llc:
104+
* bitmap of which limbo RMIDs are above threshold
101105
* @ctrl_val: array of cache or mem ctrl values (indexed by CLOSID)
102106
* @new_ctrl: new ctrl value to be loaded
103107
* @have_new_ctrl: did user provide new_ctrl for this domain
@@ -106,6 +110,7 @@ struct rdt_domain {
106110
struct list_head list;
107111
int id;
108112
struct cpumask cpu_mask;
113+
unsigned long *rmid_busy_llc;
109114
u32 *ctrl_val;
110115
u32 new_ctrl;
111116
bool have_new_ctrl;
@@ -282,6 +287,7 @@ ssize_t rdtgroup_schemata_write(struct kernfs_open_file *of,
282287
char *buf, size_t nbytes, loff_t off);
283288
int rdtgroup_schemata_show(struct kernfs_open_file *of,
284289
struct seq_file *s, void *v);
290+
struct rdt_domain *get_domain_from_cpu(int cpu, struct rdt_resource *r);
285291
int rdt_get_mon_l3_config(struct rdt_resource *r);
286292

287293
#endif /* _ASM_X86_INTEL_RDT_H */

arch/x86/kernel/cpu/intel_rdt_monitor.c

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,12 @@
2828
#include <asm/cpu_device_id.h>
2929
#include "intel_rdt.h"
3030

31+
#define MSR_IA32_QM_CTR 0x0c8e
32+
#define MSR_IA32_QM_EVTSEL 0x0c8d
33+
3134
struct rmid_entry {
3235
u32 rmid;
36+
atomic_t busy;
3337
struct list_head list;
3438
};
3539

@@ -81,6 +85,215 @@ static inline struct rmid_entry *__rmid_entry(u32 rmid)
8185
return entry;
8286
}
8387

88+
static u64 __rmid_read(u32 rmid, u32 eventid)
89+
{
90+
u64 val;
91+
92+
/*
93+
* As per the SDM, when IA32_QM_EVTSEL.EvtID (bits 7:0) is configured
94+
* with a valid event code for supported resource type and the bits
95+
* IA32_QM_EVTSEL.RMID (bits 41:32) are configured with valid RMID,
96+
* IA32_QM_CTR.data (bits 61:0) reports the monitored data.
97+
* IA32_QM_CTR.Error (bit 63) and IA32_QM_CTR.Unavailable (bit 62)
98+
* are error bits.
99+
*/
100+
wrmsr(MSR_IA32_QM_EVTSEL, eventid, rmid);
101+
rdmsrl(MSR_IA32_QM_CTR, val);
102+
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+
/*
227+
* As of now the RMIDs allocation is global.
228+
* However we keep track of which packages the RMIDs
229+
* are used to optimize the limbo list management.
230+
*/
231+
int alloc_rmid(void)
232+
{
233+
struct rmid_entry *entry;
234+
bool ret;
235+
236+
lockdep_assert_held(&rdtgroup_mutex);
237+
238+
if (list_empty(&rmid_free_lru)) {
239+
ret = try_freeing_limbo_rmid();
240+
if (list_empty(&rmid_free_lru))
241+
return ret ? -ENOSPC : -EBUSY;
242+
}
243+
244+
entry = list_first_entry(&rmid_free_lru,
245+
struct rmid_entry, list);
246+
list_del(&entry->list);
247+
248+
return entry->rmid;
249+
}
250+
251+
static void add_rmid_to_limbo(struct rmid_entry *entry)
252+
{
253+
struct rdt_resource *r;
254+
struct rdt_domain *d;
255+
int cpu, nbusy = 0;
256+
u64 val;
257+
258+
r = &rdt_resources_all[RDT_RESOURCE_L3];
259+
260+
cpu = get_cpu();
261+
list_for_each_entry(d, &r->domains, list) {
262+
if (cpumask_test_cpu(cpu, &d->cpu_mask)) {
263+
val = __rmid_read(entry->rmid, QOS_L3_OCCUP_EVENT_ID);
264+
if (val <= intel_cqm_threshold)
265+
continue;
266+
}
267+
set_bit(entry->rmid, d->rmid_busy_llc);
268+
nbusy++;
269+
}
270+
put_cpu();
271+
272+
if (nbusy) {
273+
atomic_set(&entry->busy, nbusy);
274+
list_add_tail(&entry->list, &rmid_limbo_lru);
275+
} else {
276+
list_add_tail(&entry->list, &rmid_free_lru);
277+
}
278+
}
279+
280+
void free_rmid(u32 rmid)
281+
{
282+
struct rmid_entry *entry;
283+
284+
if (!rmid)
285+
return;
286+
287+
lockdep_assert_held(&rdtgroup_mutex);
288+
289+
entry = __rmid_entry(rmid);
290+
291+
if (is_llc_occupancy_enabled())
292+
add_rmid_to_limbo(entry);
293+
else
294+
list_add_tail(&entry->list, &rmid_free_lru);
295+
}
296+
84297
static int dom_data_init(struct rdt_resource *r)
85298
{
86299
struct rmid_entry *entry = NULL;

0 commit comments

Comments
 (0)