]>
Commit | Line | Data |
---|---|---|
2025cf9e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
78e99b4a FY |
2 | /* |
3 | * Resource Director Technology(RDT) | |
4 | * - Cache Allocation code. | |
5 | * | |
6 | * Copyright (C) 2016 Intel Corporation | |
7 | * | |
8 | * Authors: | |
9 | * Fenghua Yu <fenghua.yu@intel.com> | |
10 | * Tony Luck <tony.luck@intel.com> | |
11 | * Vikas Shivappa <vikas.shivappa@intel.com> | |
12 | * | |
78e99b4a FY |
13 | * More information about RDT be found in the Intel (R) x86 Architecture |
14 | * Software Developer Manual June 2016, volume 3, section 17.17. | |
15 | */ | |
16 | ||
352940ec | 17 | #define pr_fmt(fmt) "resctrl: " fmt |
78e99b4a FY |
18 | |
19 | #include <linux/slab.h> | |
20 | #include <linux/err.h> | |
2264d9c7 TL |
21 | #include <linux/cacheinfo.h> |
22 | #include <linux/cpuhotplug.h> | |
78e99b4a | 23 | |
113c6097 | 24 | #include <asm/intel-family.h> |
8dd97c65 | 25 | #include <asm/resctrl.h> |
fa7d9493 | 26 | #include "internal.h" |
113c6097 | 27 | |
2264d9c7 TL |
28 | /* Mutex to protect rdtgroup access. */ |
29 | DEFINE_MUTEX(rdtgroup_mutex); | |
30 | ||
c39a0e2c | 31 | /* |
352940ec | 32 | * The cached resctrl_pqr_state is strictly per CPU and can never be |
c39a0e2c VS |
33 | * updated from a remote CPU. Functions which modify the state |
34 | * are called with interrupts disabled and no preemption, which | |
35 | * is sufficient for the protection. | |
36 | */ | |
352940ec | 37 | DEFINE_PER_CPU(struct resctrl_pqr_state, pqr_state); |
c39a0e2c | 38 | |
de016df8 VS |
39 | /* |
40 | * Used to store the max resource name width and max resource data width | |
41 | * to display the schemata in a tabular format | |
42 | */ | |
43 | int max_name_width, max_data_width; | |
44 | ||
6a445edc VS |
45 | /* |
46 | * Global boolean for rdt_alloc which is true if any | |
47 | * resource allocation is enabled. | |
48 | */ | |
49 | bool rdt_alloc_capable; | |
50 | ||
05b93417 | 51 | static void |
1ad4fa41 BM |
52 | mba_wrmsr_intel(struct rdt_domain *d, struct msr_param *m, |
53 | struct rdt_resource *r); | |
0921c547 TG |
54 | static void |
55 | cat_wrmsr(struct rdt_domain *d, struct msr_param *m, struct rdt_resource *r); | |
4d05bf71 BM |
56 | static void |
57 | mba_wrmsr_amd(struct rdt_domain *d, struct msr_param *m, | |
58 | struct rdt_resource *r); | |
0921c547 | 59 | |
d3e11b4d TG |
60 | #define domain_init(id) LIST_HEAD_INIT(rdt_resources_all[id].domains) |
61 | ||
c1c7c3f9 | 62 | struct rdt_resource rdt_resources_all[] = { |
dd131853 | 63 | [RDT_RESOURCE_L3] = |
c1c7c3f9 | 64 | { |
d89b7379 | 65 | .rid = RDT_RESOURCE_L3, |
d3e11b4d TG |
66 | .name = "L3", |
67 | .domains = domain_init(RDT_RESOURCE_L3), | |
aa50453a | 68 | .msr_base = MSR_IA32_L3_CBM_BASE, |
0921c547 | 69 | .msr_update = cat_wrmsr, |
d3e11b4d TG |
70 | .cache_level = 3, |
71 | .cache = { | |
72 | .min_cbm_bits = 1, | |
73 | .cbm_idx_mult = 1, | |
74 | .cbm_idx_offset = 0, | |
75 | }, | |
c6ea67de VS |
76 | .parse_ctrlval = parse_cbm, |
77 | .format_str = "%d=%0*x", | |
5dc1d5c6 | 78 | .fflags = RFTYPE_RES_CACHE, |
c1c7c3f9 | 79 | }, |
dd131853 | 80 | [RDT_RESOURCE_L3DATA] = |
c1c7c3f9 | 81 | { |
d89b7379 | 82 | .rid = RDT_RESOURCE_L3DATA, |
d3e11b4d TG |
83 | .name = "L3DATA", |
84 | .domains = domain_init(RDT_RESOURCE_L3DATA), | |
aa50453a | 85 | .msr_base = MSR_IA32_L3_CBM_BASE, |
0921c547 | 86 | .msr_update = cat_wrmsr, |
d3e11b4d TG |
87 | .cache_level = 3, |
88 | .cache = { | |
89 | .min_cbm_bits = 1, | |
90 | .cbm_idx_mult = 2, | |
91 | .cbm_idx_offset = 0, | |
92 | }, | |
c6ea67de VS |
93 | .parse_ctrlval = parse_cbm, |
94 | .format_str = "%d=%0*x", | |
5dc1d5c6 | 95 | .fflags = RFTYPE_RES_CACHE, |
c1c7c3f9 | 96 | }, |
dd131853 | 97 | [RDT_RESOURCE_L3CODE] = |
c1c7c3f9 | 98 | { |
d89b7379 | 99 | .rid = RDT_RESOURCE_L3CODE, |
d3e11b4d TG |
100 | .name = "L3CODE", |
101 | .domains = domain_init(RDT_RESOURCE_L3CODE), | |
aa50453a | 102 | .msr_base = MSR_IA32_L3_CBM_BASE, |
0921c547 | 103 | .msr_update = cat_wrmsr, |
d3e11b4d TG |
104 | .cache_level = 3, |
105 | .cache = { | |
106 | .min_cbm_bits = 1, | |
107 | .cbm_idx_mult = 2, | |
108 | .cbm_idx_offset = 1, | |
109 | }, | |
c6ea67de VS |
110 | .parse_ctrlval = parse_cbm, |
111 | .format_str = "%d=%0*x", | |
5dc1d5c6 | 112 | .fflags = RFTYPE_RES_CACHE, |
c1c7c3f9 | 113 | }, |
dd131853 | 114 | [RDT_RESOURCE_L2] = |
c1c7c3f9 | 115 | { |
d89b7379 | 116 | .rid = RDT_RESOURCE_L2, |
d3e11b4d TG |
117 | .name = "L2", |
118 | .domains = domain_init(RDT_RESOURCE_L2), | |
aa50453a | 119 | .msr_base = MSR_IA32_L2_CBM_BASE, |
0921c547 | 120 | .msr_update = cat_wrmsr, |
d3e11b4d TG |
121 | .cache_level = 2, |
122 | .cache = { | |
123 | .min_cbm_bits = 1, | |
124 | .cbm_idx_mult = 1, | |
125 | .cbm_idx_offset = 0, | |
126 | }, | |
c6ea67de VS |
127 | .parse_ctrlval = parse_cbm, |
128 | .format_str = "%d=%0*x", | |
5dc1d5c6 | 129 | .fflags = RFTYPE_RES_CACHE, |
c1c7c3f9 | 130 | }, |
def10853 FY |
131 | [RDT_RESOURCE_L2DATA] = |
132 | { | |
133 | .rid = RDT_RESOURCE_L2DATA, | |
134 | .name = "L2DATA", | |
135 | .domains = domain_init(RDT_RESOURCE_L2DATA), | |
aa50453a | 136 | .msr_base = MSR_IA32_L2_CBM_BASE, |
def10853 FY |
137 | .msr_update = cat_wrmsr, |
138 | .cache_level = 2, | |
139 | .cache = { | |
140 | .min_cbm_bits = 1, | |
141 | .cbm_idx_mult = 2, | |
142 | .cbm_idx_offset = 0, | |
143 | }, | |
144 | .parse_ctrlval = parse_cbm, | |
145 | .format_str = "%d=%0*x", | |
146 | .fflags = RFTYPE_RES_CACHE, | |
147 | }, | |
148 | [RDT_RESOURCE_L2CODE] = | |
149 | { | |
150 | .rid = RDT_RESOURCE_L2CODE, | |
151 | .name = "L2CODE", | |
152 | .domains = domain_init(RDT_RESOURCE_L2CODE), | |
aa50453a | 153 | .msr_base = MSR_IA32_L2_CBM_BASE, |
def10853 FY |
154 | .msr_update = cat_wrmsr, |
155 | .cache_level = 2, | |
156 | .cache = { | |
157 | .min_cbm_bits = 1, | |
158 | .cbm_idx_mult = 2, | |
159 | .cbm_idx_offset = 1, | |
160 | }, | |
161 | .parse_ctrlval = parse_cbm, | |
162 | .format_str = "%d=%0*x", | |
163 | .fflags = RFTYPE_RES_CACHE, | |
164 | }, | |
dd131853 | 165 | [RDT_RESOURCE_MBA] = |
05b93417 | 166 | { |
d89b7379 | 167 | .rid = RDT_RESOURCE_MBA, |
05b93417 VS |
168 | .name = "MB", |
169 | .domains = domain_init(RDT_RESOURCE_MBA), | |
05b93417 | 170 | .cache_level = 3, |
8205a078 | 171 | .format_str = "%d=%*u", |
5dc1d5c6 | 172 | .fflags = RFTYPE_RES_MB, |
05b93417 | 173 | }, |
c1c7c3f9 FY |
174 | }; |
175 | ||
d3e11b4d | 176 | static unsigned int cbm_idx(struct rdt_resource *r, unsigned int closid) |
2264d9c7 | 177 | { |
d3e11b4d | 178 | return closid * r->cache.cbm_idx_mult + r->cache.cbm_idx_offset; |
2264d9c7 TL |
179 | } |
180 | ||
113c6097 FY |
181 | /* |
182 | * cache_alloc_hsw_probe() - Have to probe for Intel haswell server CPUs | |
183 | * as they do not have CPUID enumeration support for Cache allocation. | |
184 | * The check for Vendor/Family/Model is not enough to guarantee that | |
185 | * the MSRs won't #GP fault because only the following SKUs support | |
186 | * CAT: | |
187 | * Intel(R) Xeon(R) CPU E5-2658 v3 @ 2.20GHz | |
188 | * Intel(R) Xeon(R) CPU E5-2648L v3 @ 1.80GHz | |
189 | * Intel(R) Xeon(R) CPU E5-2628L v3 @ 2.00GHz | |
190 | * Intel(R) Xeon(R) CPU E5-2618L v3 @ 2.30GHz | |
191 | * Intel(R) Xeon(R) CPU E5-2608L v3 @ 2.00GHz | |
192 | * Intel(R) Xeon(R) CPU E5-2658A v3 @ 2.20GHz | |
193 | * | |
194 | * Probe by trying to write the first of the L3 cach mask registers | |
195 | * and checking that the bits stick. Max CLOSids is always 4 and max cbm length | |
196 | * is always 20 on hsw server parts. The minimum cache bitmask length | |
197 | * allowed for HSW server is always 2 bits. Hardcode all of them. | |
198 | */ | |
0576113a | 199 | static inline void cache_alloc_hsw_probe(void) |
113c6097 | 200 | { |
0576113a TL |
201 | struct rdt_resource *r = &rdt_resources_all[RDT_RESOURCE_L3]; |
202 | u32 l, h, max_cbm = BIT_MASK(20) - 1; | |
113c6097 | 203 | |
aa50453a | 204 | if (wrmsr_safe(MSR_IA32_L3_CBM_BASE, max_cbm, 0)) |
0576113a | 205 | return; |
aa50453a BM |
206 | |
207 | rdmsr(MSR_IA32_L3_CBM_BASE, l, h); | |
c1c7c3f9 | 208 | |
0576113a TL |
209 | /* If all the bits were set in MSR, return success */ |
210 | if (l != max_cbm) | |
211 | return; | |
c1c7c3f9 | 212 | |
0576113a TL |
213 | r->num_closid = 4; |
214 | r->default_ctrl = max_cbm; | |
215 | r->cache.cbm_len = 20; | |
216 | r->cache.shareable_bits = 0xc0000; | |
217 | r->cache.min_cbm_bits = 2; | |
218 | r->alloc_capable = true; | |
219 | r->alloc_enabled = true; | |
113c6097 | 220 | |
0576113a | 221 | rdt_alloc_capable = true; |
113c6097 FY |
222 | } |
223 | ||
19c635ab VS |
224 | bool is_mba_sc(struct rdt_resource *r) |
225 | { | |
226 | if (!r) | |
227 | return rdt_resources_all[RDT_RESOURCE_MBA].membw.mba_sc; | |
228 | ||
229 | return r->membw.mba_sc; | |
230 | } | |
231 | ||
05b93417 VS |
232 | /* |
233 | * rdt_get_mb_table() - get a mapping of bandwidth(b/w) percentage values | |
234 | * exposed to user interface and the h/w understandable delay values. | |
235 | * | |
236 | * The non-linear delay values have the granularity of power of two | |
237 | * and also the h/w does not guarantee a curve for configured delay | |
238 | * values vs. actual b/w enforced. | |
239 | * Hence we need a mapping that is pre calibrated so the user can | |
240 | * express the memory b/w as a percentage value. | |
241 | */ | |
242 | static inline bool rdt_get_mb_table(struct rdt_resource *r) | |
243 | { | |
244 | /* | |
245 | * There are no Intel SKUs as of now to support non-linear delay. | |
246 | */ | |
247 | pr_info("MBA b/w map not implemented for cpu:%d, model:%d", | |
248 | boot_cpu_data.x86, boot_cpu_data.x86_model); | |
249 | ||
250 | return false; | |
251 | } | |
252 | ||
4d05bf71 | 253 | static bool __get_mem_config_intel(struct rdt_resource *r) |
05b93417 VS |
254 | { |
255 | union cpuid_0x10_3_eax eax; | |
256 | union cpuid_0x10_x_edx edx; | |
e89f85b9 | 257 | u32 ebx, ecx, max_delay; |
05b93417 VS |
258 | |
259 | cpuid_count(0x00000010, 3, &eax.full, &ebx, &ecx, &edx.full); | |
260 | r->num_closid = edx.split.cos_max + 1; | |
e89f85b9 | 261 | max_delay = eax.split.max_delay + 1; |
05b93417 VS |
262 | r->default_ctrl = MAX_MBA_BW; |
263 | if (ecx & MBA_IS_LINEAR) { | |
264 | r->membw.delay_linear = true; | |
e89f85b9 JM |
265 | r->membw.min_bw = MAX_MBA_BW - max_delay; |
266 | r->membw.bw_gran = MAX_MBA_BW - max_delay; | |
05b93417 VS |
267 | } else { |
268 | if (!rdt_get_mb_table(r)) | |
269 | return false; | |
270 | } | |
271 | r->data_width = 3; | |
272 | ||
1b5c0b75 VS |
273 | r->alloc_capable = true; |
274 | r->alloc_enabled = true; | |
05b93417 VS |
275 | |
276 | return true; | |
277 | } | |
278 | ||
4d05bf71 BM |
279 | static bool __rdt_get_mem_config_amd(struct rdt_resource *r) |
280 | { | |
281 | union cpuid_0x10_3_eax eax; | |
282 | union cpuid_0x10_x_edx edx; | |
283 | u32 ebx, ecx; | |
284 | ||
285 | cpuid_count(0x80000020, 1, &eax.full, &ebx, &ecx, &edx.full); | |
286 | r->num_closid = edx.split.cos_max + 1; | |
287 | r->default_ctrl = MAX_MBA_BW_AMD; | |
288 | ||
289 | /* AMD does not use delay */ | |
290 | r->membw.delay_linear = false; | |
291 | ||
292 | r->membw.min_bw = 0; | |
293 | r->membw.bw_gran = 1; | |
294 | /* Max value is 2048, Data width should be 4 in decimal */ | |
295 | r->data_width = 4; | |
296 | ||
297 | r->alloc_capable = true; | |
298 | r->alloc_enabled = true; | |
299 | ||
300 | return true; | |
301 | } | |
302 | ||
6a445edc | 303 | static void rdt_get_cache_alloc_cfg(int idx, struct rdt_resource *r) |
c1c7c3f9 FY |
304 | { |
305 | union cpuid_0x10_1_eax eax; | |
2545e9f5 | 306 | union cpuid_0x10_x_edx edx; |
c1c7c3f9 FY |
307 | u32 ebx, ecx; |
308 | ||
309 | cpuid_count(0x00000010, idx, &eax.full, &ebx, &ecx, &edx.full); | |
310 | r->num_closid = edx.split.cos_max + 1; | |
d3e11b4d | 311 | r->cache.cbm_len = eax.split.cbm_len + 1; |
2545e9f5 | 312 | r->default_ctrl = BIT_MASK(eax.split.cbm_len + 1) - 1; |
0dd2d749 | 313 | r->cache.shareable_bits = ebx & r->default_ctrl; |
d3e11b4d | 314 | r->data_width = (r->cache.cbm_len + 3) / 4; |
1b5c0b75 VS |
315 | r->alloc_capable = true; |
316 | r->alloc_enabled = true; | |
c1c7c3f9 FY |
317 | } |
318 | ||
def10853 | 319 | static void rdt_get_cdp_config(int level, int type) |
c1c7c3f9 | 320 | { |
def10853 | 321 | struct rdt_resource *r_l = &rdt_resources_all[level]; |
c1c7c3f9 FY |
322 | struct rdt_resource *r = &rdt_resources_all[type]; |
323 | ||
def10853 FY |
324 | r->num_closid = r_l->num_closid / 2; |
325 | r->cache.cbm_len = r_l->cache.cbm_len; | |
326 | r->default_ctrl = r_l->default_ctrl; | |
327 | r->cache.shareable_bits = r_l->cache.shareable_bits; | |
d3e11b4d | 328 | r->data_width = (r->cache.cbm_len + 3) / 4; |
1b5c0b75 | 329 | r->alloc_capable = true; |
c1c7c3f9 FY |
330 | /* |
331 | * By default, CDP is disabled. CDP can be enabled by mount parameter | |
332 | * "cdp" during resctrl file system mount time. | |
333 | */ | |
1b5c0b75 | 334 | r->alloc_enabled = false; |
c1c7c3f9 FY |
335 | } |
336 | ||
def10853 FY |
337 | static void rdt_get_cdp_l3_config(void) |
338 | { | |
339 | rdt_get_cdp_config(RDT_RESOURCE_L3, RDT_RESOURCE_L3DATA); | |
340 | rdt_get_cdp_config(RDT_RESOURCE_L3, RDT_RESOURCE_L3CODE); | |
341 | } | |
342 | ||
343 | static void rdt_get_cdp_l2_config(void) | |
344 | { | |
345 | rdt_get_cdp_config(RDT_RESOURCE_L2, RDT_RESOURCE_L2DATA); | |
346 | rdt_get_cdp_config(RDT_RESOURCE_L2, RDT_RESOURCE_L2CODE); | |
347 | } | |
348 | ||
2264d9c7 TL |
349 | static int get_cache_id(int cpu, int level) |
350 | { | |
351 | struct cpu_cacheinfo *ci = get_cpu_cacheinfo(cpu); | |
352 | int i; | |
353 | ||
354 | for (i = 0; i < ci->num_leaves; i++) { | |
355 | if (ci->info_list[i].level == level) | |
356 | return ci->info_list[i].id; | |
357 | } | |
358 | ||
359 | return -1; | |
360 | } | |
361 | ||
4d05bf71 BM |
362 | static void |
363 | mba_wrmsr_amd(struct rdt_domain *d, struct msr_param *m, struct rdt_resource *r) | |
364 | { | |
365 | unsigned int i; | |
366 | ||
367 | for (i = m->low; i < m->high; i++) | |
368 | wrmsrl(r->msr_base + i, d->ctrl_val[i]); | |
369 | } | |
370 | ||
05b93417 VS |
371 | /* |
372 | * Map the memory b/w percentage value to delay values | |
373 | * that can be written to QOS_MSRs. | |
374 | * There are currently no SKUs which support non linear delay values. | |
375 | */ | |
de73f38f | 376 | u32 delay_bw_map(unsigned long bw, struct rdt_resource *r) |
05b93417 VS |
377 | { |
378 | if (r->membw.delay_linear) | |
379 | return MAX_MBA_BW - bw; | |
380 | ||
381 | pr_warn_once("Non Linear delay-bw map not supported but queried\n"); | |
382 | return r->default_ctrl; | |
383 | } | |
384 | ||
385 | static void | |
1ad4fa41 BM |
386 | mba_wrmsr_intel(struct rdt_domain *d, struct msr_param *m, |
387 | struct rdt_resource *r) | |
05b93417 VS |
388 | { |
389 | unsigned int i; | |
390 | ||
391 | /* Write the delay values for mba. */ | |
392 | for (i = m->low; i < m->high; i++) | |
393 | wrmsrl(r->msr_base + i, delay_bw_map(d->ctrl_val[i], r)); | |
394 | } | |
395 | ||
0921c547 TG |
396 | static void |
397 | cat_wrmsr(struct rdt_domain *d, struct msr_param *m, struct rdt_resource *r) | |
398 | { | |
399 | unsigned int i; | |
400 | ||
401 | for (i = m->low; i < m->high; i++) | |
402 | wrmsrl(r->msr_base + cbm_idx(r, i), d->ctrl_val[i]); | |
403 | } | |
404 | ||
edf6fa1c VS |
405 | struct rdt_domain *get_domain_from_cpu(int cpu, struct rdt_resource *r) |
406 | { | |
407 | struct rdt_domain *d; | |
408 | ||
409 | list_for_each_entry(d, &r->domains, list) { | |
410 | /* Find the domain that contains this CPU */ | |
411 | if (cpumask_test_cpu(cpu, &d->cpu_mask)) | |
412 | return d; | |
413 | } | |
414 | ||
415 | return NULL; | |
416 | } | |
417 | ||
2545e9f5 | 418 | void rdt_ctrl_update(void *arg) |
2264d9c7 | 419 | { |
0921c547 | 420 | struct msr_param *m = arg; |
2264d9c7 | 421 | struct rdt_resource *r = m->res; |
0921c547 | 422 | int cpu = smp_processor_id(); |
2264d9c7 TL |
423 | struct rdt_domain *d; |
424 | ||
e3302683 VS |
425 | d = get_domain_from_cpu(cpu, r); |
426 | if (d) { | |
427 | r->msr_update(d, m, r); | |
428 | return; | |
2264d9c7 | 429 | } |
0921c547 | 430 | pr_warn_once("cpu %d not found in any domain for resource %s\n", |
2264d9c7 | 431 | cpu, r->name); |
2264d9c7 TL |
432 | } |
433 | ||
434 | /* | |
435 | * rdt_find_domain - Find a domain in a resource that matches input resource id | |
436 | * | |
437 | * Search resource r's domain list to find the resource id. If the resource | |
438 | * id is found in a domain, return the domain. Otherwise, if requested by | |
439 | * caller, return the first domain whose id is bigger than the input id. | |
440 | * The domain list is sorted by id in ascending order. | |
441 | */ | |
d89b7379 VS |
442 | struct rdt_domain *rdt_find_domain(struct rdt_resource *r, int id, |
443 | struct list_head **pos) | |
2264d9c7 TL |
444 | { |
445 | struct rdt_domain *d; | |
446 | struct list_head *l; | |
447 | ||
448 | if (id < 0) | |
52eb7433 | 449 | return ERR_PTR(-ENODEV); |
2264d9c7 TL |
450 | |
451 | list_for_each(l, &r->domains) { | |
452 | d = list_entry(l, struct rdt_domain, list); | |
453 | /* When id is found, return its domain. */ | |
454 | if (id == d->id) | |
455 | return d; | |
456 | /* Stop searching when finding id's position in sorted list. */ | |
457 | if (id < d->id) | |
458 | break; | |
459 | } | |
460 | ||
461 | if (pos) | |
462 | *pos = l; | |
463 | ||
464 | return NULL; | |
465 | } | |
466 | ||
1bd2a63b VS |
467 | void setup_default_ctrlval(struct rdt_resource *r, u32 *dc, u32 *dm) |
468 | { | |
469 | int i; | |
470 | ||
471 | /* | |
472 | * Initialize the Control MSRs to having no control. | |
473 | * For Cache Allocation: Set all bits in cbm | |
474 | * For Memory Allocation: Set b/w requested to 100% | |
475 | * and the bandwidth in MBps to U32_MAX | |
476 | */ | |
477 | for (i = 0; i < r->num_closid; i++, dc++, dm++) { | |
478 | *dc = r->default_ctrl; | |
479 | *dm = MBA_MAX_MBPS; | |
480 | } | |
481 | } | |
482 | ||
0921c547 TG |
483 | static int domain_setup_ctrlval(struct rdt_resource *r, struct rdt_domain *d) |
484 | { | |
485 | struct msr_param m; | |
1bd2a63b | 486 | u32 *dc, *dm; |
0921c547 TG |
487 | |
488 | dc = kmalloc_array(r->num_closid, sizeof(*d->ctrl_val), GFP_KERNEL); | |
489 | if (!dc) | |
490 | return -ENOMEM; | |
491 | ||
1bd2a63b VS |
492 | dm = kmalloc_array(r->num_closid, sizeof(*d->mbps_val), GFP_KERNEL); |
493 | if (!dm) { | |
494 | kfree(dc); | |
495 | return -ENOMEM; | |
496 | } | |
0921c547 | 497 | |
1bd2a63b VS |
498 | d->ctrl_val = dc; |
499 | d->mbps_val = dm; | |
500 | setup_default_ctrlval(r, dc, dm); | |
0921c547 TG |
501 | |
502 | m.low = 0; | |
503 | m.high = r->num_closid; | |
504 | r->msr_update(d, &m, r); | |
505 | return 0; | |
506 | } | |
507 | ||
edf6fa1c VS |
508 | static int domain_setup_mon_state(struct rdt_resource *r, struct rdt_domain *d) |
509 | { | |
9f52425b TL |
510 | size_t tsize; |
511 | ||
edf6fa1c | 512 | if (is_llc_occupancy_enabled()) { |
82159876 | 513 | d->rmid_busy_llc = bitmap_zalloc(r->num_rmid, GFP_KERNEL); |
edf6fa1c VS |
514 | if (!d->rmid_busy_llc) |
515 | return -ENOMEM; | |
24247aee | 516 | INIT_DELAYED_WORK(&d->cqm_limbo, cqm_handle_limbo); |
edf6fa1c | 517 | } |
9f52425b TL |
518 | if (is_mbm_total_enabled()) { |
519 | tsize = sizeof(*d->mbm_total); | |
520 | d->mbm_total = kcalloc(r->num_rmid, tsize, GFP_KERNEL); | |
521 | if (!d->mbm_total) { | |
82159876 | 522 | bitmap_free(d->rmid_busy_llc); |
9f52425b TL |
523 | return -ENOMEM; |
524 | } | |
525 | } | |
526 | if (is_mbm_local_enabled()) { | |
527 | tsize = sizeof(*d->mbm_local); | |
528 | d->mbm_local = kcalloc(r->num_rmid, tsize, GFP_KERNEL); | |
529 | if (!d->mbm_local) { | |
82159876 | 530 | bitmap_free(d->rmid_busy_llc); |
9f52425b TL |
531 | kfree(d->mbm_total); |
532 | return -ENOMEM; | |
533 | } | |
534 | } | |
edf6fa1c | 535 | |
e3302683 VS |
536 | if (is_mbm_enabled()) { |
537 | INIT_DELAYED_WORK(&d->mbm_over, mbm_handle_overflow); | |
bbc4615e | 538 | mbm_setup_overflow_handler(d, MBM_OVERFLOW_INTERVAL); |
e3302683 VS |
539 | } |
540 | ||
edf6fa1c VS |
541 | return 0; |
542 | } | |
543 | ||
2264d9c7 TL |
544 | /* |
545 | * domain_add_cpu - Add a cpu to a resource's domain list. | |
546 | * | |
547 | * If an existing domain in the resource r's domain list matches the cpu's | |
548 | * resource id, add the cpu in the domain. | |
549 | * | |
550 | * Otherwise, a new domain is allocated and inserted into the right position | |
551 | * in the domain list sorted by id in ascending order. | |
552 | * | |
553 | * The order in the domain list is visible to users when we print entries | |
554 | * in the schemata file and schemata input is validated to have the same order | |
555 | * as this list. | |
556 | */ | |
557 | static void domain_add_cpu(int cpu, struct rdt_resource *r) | |
558 | { | |
0921c547 | 559 | int id = get_cache_id(cpu, r->cache_level); |
2264d9c7 TL |
560 | struct list_head *add_pos = NULL; |
561 | struct rdt_domain *d; | |
562 | ||
563 | d = rdt_find_domain(r, id, &add_pos); | |
564 | if (IS_ERR(d)) { | |
565 | pr_warn("Could't find cache id for cpu %d\n", cpu); | |
566 | return; | |
567 | } | |
568 | ||
569 | if (d) { | |
570 | cpumask_set_cpu(cpu, &d->cpu_mask); | |
571 | return; | |
572 | } | |
573 | ||
574 | d = kzalloc_node(sizeof(*d), GFP_KERNEL, cpu_to_node(cpu)); | |
575 | if (!d) | |
576 | return; | |
577 | ||
578 | d->id = id; | |
9f52425b | 579 | cpumask_set_cpu(cpu, &d->cpu_mask); |
2264d9c7 | 580 | |
9fe04507 JM |
581 | rdt_domain_reconfigure_cdp(r); |
582 | ||
1b5c0b75 | 583 | if (r->alloc_capable && domain_setup_ctrlval(r, d)) { |
2264d9c7 TL |
584 | kfree(d); |
585 | return; | |
586 | } | |
587 | ||
edf6fa1c VS |
588 | if (r->mon_capable && domain_setup_mon_state(r, d)) { |
589 | kfree(d); | |
590 | return; | |
591 | } | |
592 | ||
2264d9c7 | 593 | list_add_tail(&d->list, add_pos); |
895c663e VS |
594 | |
595 | /* | |
596 | * If resctrl is mounted, add | |
597 | * per domain monitor data directories. | |
598 | */ | |
599 | if (static_branch_unlikely(&rdt_mon_enable_key)) | |
600 | mkdir_mondata_subdir_allrdtgrp(r, d); | |
2264d9c7 TL |
601 | } |
602 | ||
603 | static void domain_remove_cpu(int cpu, struct rdt_resource *r) | |
604 | { | |
605 | int id = get_cache_id(cpu, r->cache_level); | |
606 | struct rdt_domain *d; | |
607 | ||
608 | d = rdt_find_domain(r, id, NULL); | |
609 | if (IS_ERR_OR_NULL(d)) { | |
610 | pr_warn("Could't find cache id for cpu %d\n", cpu); | |
611 | return; | |
612 | } | |
613 | ||
614 | cpumask_clear_cpu(cpu, &d->cpu_mask); | |
615 | if (cpumask_empty(&d->cpu_mask)) { | |
895c663e VS |
616 | /* |
617 | * If resctrl is mounted, remove all the | |
618 | * per domain monitor data directories. | |
619 | */ | |
620 | if (static_branch_unlikely(&rdt_mon_enable_key)) | |
621 | rmdir_mondata_subdir_allrdtgrp(r, d->id); | |
2264d9c7 | 622 | list_del(&d->list); |
e278af89 | 623 | if (r->mon_capable && is_mbm_enabled()) |
e3302683 | 624 | cancel_delayed_work(&d->mbm_over); |
24247aee VS |
625 | if (is_llc_occupancy_enabled() && has_busy_rmid(r, d)) { |
626 | /* | |
627 | * When a package is going down, forcefully | |
628 | * decrement rmid->ebusy. There is no way to know | |
629 | * that the L3 was flushed and hence may lead to | |
630 | * incorrect counts in rare scenarios, but leaving | |
631 | * the RMID as busy creates RMID leaks if the | |
632 | * package never comes back. | |
633 | */ | |
634 | __check_limbo(d, true); | |
635 | cancel_delayed_work(&d->cqm_limbo); | |
636 | } | |
637 | ||
b61b8bba JJ |
638 | /* |
639 | * rdt_domain "d" is going to be freed below, so clear | |
640 | * its pointer from pseudo_lock_region struct. | |
641 | */ | |
642 | if (d->plr) | |
643 | d->plr->d = NULL; | |
644 | ||
d4792441 | 645 | kfree(d->ctrl_val); |
1bd2a63b | 646 | kfree(d->mbps_val); |
82159876 | 647 | bitmap_free(d->rmid_busy_llc); |
d4792441 TG |
648 | kfree(d->mbm_total); |
649 | kfree(d->mbm_local); | |
2264d9c7 | 650 | kfree(d); |
24247aee VS |
651 | return; |
652 | } | |
653 | ||
654 | if (r == &rdt_resources_all[RDT_RESOURCE_L3]) { | |
655 | if (is_mbm_enabled() && cpu == d->mbm_work_cpu) { | |
656 | cancel_delayed_work(&d->mbm_over); | |
657 | mbm_setup_overflow_handler(d, 0); | |
658 | } | |
659 | if (is_llc_occupancy_enabled() && cpu == d->cqm_work_cpu && | |
660 | has_busy_rmid(r, d)) { | |
661 | cancel_delayed_work(&d->cqm_limbo); | |
662 | cqm_setup_limbo_handler(d, 0); | |
663 | } | |
2264d9c7 TL |
664 | } |
665 | } | |
666 | ||
895c663e | 667 | static void clear_closid_rmid(int cpu) |
2264d9c7 | 668 | { |
352940ec | 669 | struct resctrl_pqr_state *state = this_cpu_ptr(&pqr_state); |
12e0110c | 670 | |
a9110b55 VS |
671 | state->default_closid = 0; |
672 | state->default_rmid = 0; | |
673 | state->cur_closid = 0; | |
674 | state->cur_rmid = 0; | |
895c663e | 675 | wrmsr(IA32_PQR_ASSOC, 0, 0); |
12e0110c TL |
676 | } |
677 | ||
352940ec | 678 | static int resctrl_online_cpu(unsigned int cpu) |
12e0110c | 679 | { |
2264d9c7 TL |
680 | struct rdt_resource *r; |
681 | ||
682 | mutex_lock(&rdtgroup_mutex); | |
895c663e | 683 | for_each_capable_rdt_resource(r) |
2264d9c7 | 684 | domain_add_cpu(cpu, r); |
12e0110c TL |
685 | /* The cpu is set in default rdtgroup after online. */ |
686 | cpumask_set_cpu(cpu, &rdtgroup_default.cpu_mask); | |
895c663e | 687 | clear_closid_rmid(cpu); |
2264d9c7 TL |
688 | mutex_unlock(&rdtgroup_mutex); |
689 | ||
690 | return 0; | |
691 | } | |
692 | ||
895c663e VS |
693 | static void clear_childcpus(struct rdtgroup *r, unsigned int cpu) |
694 | { | |
695 | struct rdtgroup *cr; | |
696 | ||
697 | list_for_each_entry(cr, &r->mon.crdtgrp_list, mon.crdtgrp_list) { | |
698 | if (cpumask_test_and_clear_cpu(cpu, &cr->cpu_mask)) { | |
699 | break; | |
700 | } | |
701 | } | |
702 | } | |
703 | ||
352940ec | 704 | static int resctrl_offline_cpu(unsigned int cpu) |
2264d9c7 | 705 | { |
12e0110c | 706 | struct rdtgroup *rdtgrp; |
2264d9c7 TL |
707 | struct rdt_resource *r; |
708 | ||
709 | mutex_lock(&rdtgroup_mutex); | |
895c663e | 710 | for_each_capable_rdt_resource(r) |
2264d9c7 | 711 | domain_remove_cpu(cpu, r); |
12e0110c | 712 | list_for_each_entry(rdtgrp, &rdt_all_groups, rdtgroup_list) { |
895c663e VS |
713 | if (cpumask_test_and_clear_cpu(cpu, &rdtgrp->cpu_mask)) { |
714 | clear_childcpus(rdtgrp, cpu); | |
12e0110c | 715 | break; |
895c663e | 716 | } |
12e0110c | 717 | } |
895c663e | 718 | clear_closid_rmid(cpu); |
2264d9c7 TL |
719 | mutex_unlock(&rdtgroup_mutex); |
720 | ||
721 | return 0; | |
722 | } | |
723 | ||
70a1ee92 TG |
724 | /* |
725 | * Choose a width for the resource name and resource data based on the | |
726 | * resource that has widest name and cbm. | |
727 | */ | |
728 | static __init void rdt_init_padding(void) | |
729 | { | |
730 | struct rdt_resource *r; | |
731 | int cl; | |
732 | ||
1b5c0b75 | 733 | for_each_alloc_capable_rdt_resource(r) { |
70a1ee92 TG |
734 | cl = strlen(r->name); |
735 | if (cl > max_name_width) | |
736 | max_name_width = cl; | |
737 | ||
738 | if (r->data_width > max_data_width) | |
739 | max_data_width = r->data_width; | |
740 | } | |
741 | } | |
742 | ||
1d9807fc TL |
743 | enum { |
744 | RDT_FLAG_CMT, | |
745 | RDT_FLAG_MBM_TOTAL, | |
746 | RDT_FLAG_MBM_LOCAL, | |
747 | RDT_FLAG_L3_CAT, | |
748 | RDT_FLAG_L3_CDP, | |
749 | RDT_FLAG_L2_CAT, | |
31516de3 | 750 | RDT_FLAG_L2_CDP, |
1d9807fc TL |
751 | RDT_FLAG_MBA, |
752 | }; | |
753 | ||
754 | #define RDT_OPT(idx, n, f) \ | |
755 | [idx] = { \ | |
756 | .name = n, \ | |
757 | .flag = f \ | |
758 | } | |
759 | ||
760 | struct rdt_options { | |
761 | char *name; | |
762 | int flag; | |
763 | bool force_off, force_on; | |
764 | }; | |
765 | ||
766 | static struct rdt_options rdt_options[] __initdata = { | |
767 | RDT_OPT(RDT_FLAG_CMT, "cmt", X86_FEATURE_CQM_OCCUP_LLC), | |
768 | RDT_OPT(RDT_FLAG_MBM_TOTAL, "mbmtotal", X86_FEATURE_CQM_MBM_TOTAL), | |
769 | RDT_OPT(RDT_FLAG_MBM_LOCAL, "mbmlocal", X86_FEATURE_CQM_MBM_LOCAL), | |
770 | RDT_OPT(RDT_FLAG_L3_CAT, "l3cat", X86_FEATURE_CAT_L3), | |
771 | RDT_OPT(RDT_FLAG_L3_CDP, "l3cdp", X86_FEATURE_CDP_L3), | |
772 | RDT_OPT(RDT_FLAG_L2_CAT, "l2cat", X86_FEATURE_CAT_L2), | |
31516de3 | 773 | RDT_OPT(RDT_FLAG_L2_CDP, "l2cdp", X86_FEATURE_CDP_L2), |
1d9807fc TL |
774 | RDT_OPT(RDT_FLAG_MBA, "mba", X86_FEATURE_MBA), |
775 | }; | |
776 | #define NUM_RDT_OPTIONS ARRAY_SIZE(rdt_options) | |
777 | ||
778 | static int __init set_rdt_options(char *str) | |
779 | { | |
780 | struct rdt_options *o; | |
781 | bool force_off; | |
782 | char *tok; | |
783 | ||
784 | if (*str == '=') | |
785 | str++; | |
786 | while ((tok = strsep(&str, ",")) != NULL) { | |
787 | force_off = *tok == '!'; | |
788 | if (force_off) | |
789 | tok++; | |
790 | for (o = rdt_options; o < &rdt_options[NUM_RDT_OPTIONS]; o++) { | |
791 | if (strcmp(tok, o->name) == 0) { | |
792 | if (force_off) | |
793 | o->force_off = true; | |
794 | else | |
795 | o->force_on = true; | |
796 | break; | |
797 | } | |
798 | } | |
799 | } | |
800 | return 1; | |
801 | } | |
802 | __setup("rdt", set_rdt_options); | |
803 | ||
804 | static bool __init rdt_cpu_has(int flag) | |
805 | { | |
806 | bool ret = boot_cpu_has(flag); | |
807 | struct rdt_options *o; | |
808 | ||
809 | if (!ret) | |
810 | return ret; | |
811 | ||
812 | for (o = rdt_options; o < &rdt_options[NUM_RDT_OPTIONS]; o++) { | |
813 | if (flag == o->flag) { | |
814 | if (o->force_off) | |
815 | ret = false; | |
816 | if (o->force_on) | |
817 | ret = true; | |
818 | break; | |
819 | } | |
820 | } | |
821 | return ret; | |
822 | } | |
823 | ||
0f00717e BM |
824 | static __init bool get_mem_config(void) |
825 | { | |
4d05bf71 BM |
826 | if (!rdt_cpu_has(X86_FEATURE_MBA)) |
827 | return false; | |
828 | ||
829 | if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) | |
830 | return __get_mem_config_intel(&rdt_resources_all[RDT_RESOURCE_MBA]); | |
831 | else if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) | |
832 | return __rdt_get_mem_config_amd(&rdt_resources_all[RDT_RESOURCE_MBA]); | |
0f00717e BM |
833 | |
834 | return false; | |
835 | } | |
836 | ||
6a445edc | 837 | static __init bool get_rdt_alloc_resources(void) |
70a1ee92 TG |
838 | { |
839 | bool ret = false; | |
840 | ||
0576113a | 841 | if (rdt_alloc_capable) |
70a1ee92 TG |
842 | return true; |
843 | ||
844 | if (!boot_cpu_has(X86_FEATURE_RDT_A)) | |
845 | return false; | |
846 | ||
1d9807fc | 847 | if (rdt_cpu_has(X86_FEATURE_CAT_L3)) { |
6a445edc | 848 | rdt_get_cache_alloc_cfg(1, &rdt_resources_all[RDT_RESOURCE_L3]); |
def10853 FY |
849 | if (rdt_cpu_has(X86_FEATURE_CDP_L3)) |
850 | rdt_get_cdp_l3_config(); | |
70a1ee92 TG |
851 | ret = true; |
852 | } | |
1d9807fc | 853 | if (rdt_cpu_has(X86_FEATURE_CAT_L2)) { |
70a1ee92 | 854 | /* CPUID 0x10.2 fields are same format at 0x10.1 */ |
6a445edc | 855 | rdt_get_cache_alloc_cfg(2, &rdt_resources_all[RDT_RESOURCE_L2]); |
def10853 FY |
856 | if (rdt_cpu_has(X86_FEATURE_CDP_L2)) |
857 | rdt_get_cdp_l2_config(); | |
70a1ee92 TG |
858 | ret = true; |
859 | } | |
ab66a33b | 860 | |
0f00717e BM |
861 | if (get_mem_config()) |
862 | ret = true; | |
863 | ||
70a1ee92 TG |
864 | return ret; |
865 | } | |
866 | ||
6a445edc VS |
867 | static __init bool get_rdt_mon_resources(void) |
868 | { | |
1d9807fc | 869 | if (rdt_cpu_has(X86_FEATURE_CQM_OCCUP_LLC)) |
6a445edc | 870 | rdt_mon_features |= (1 << QOS_L3_OCCUP_EVENT_ID); |
1d9807fc | 871 | if (rdt_cpu_has(X86_FEATURE_CQM_MBM_TOTAL)) |
6a445edc | 872 | rdt_mon_features |= (1 << QOS_L3_MBM_TOTAL_EVENT_ID); |
1d9807fc | 873 | if (rdt_cpu_has(X86_FEATURE_CQM_MBM_LOCAL)) |
6a445edc VS |
874 | rdt_mon_features |= (1 << QOS_L3_MBM_LOCAL_EVENT_ID); |
875 | ||
876 | if (!rdt_mon_features) | |
877 | return false; | |
878 | ||
879 | return !rdt_get_mon_l3_config(&rdt_resources_all[RDT_RESOURCE_L3]); | |
880 | } | |
881 | ||
0f00717e | 882 | static __init void __check_quirks_intel(void) |
0576113a TL |
883 | { |
884 | switch (boot_cpu_data.x86_model) { | |
885 | case INTEL_FAM6_HASWELL_X: | |
1d9807fc TL |
886 | if (!rdt_options[RDT_FLAG_L3_CAT].force_off) |
887 | cache_alloc_hsw_probe(); | |
0576113a | 888 | break; |
d56593eb | 889 | case INTEL_FAM6_SKYLAKE_X: |
b399151c | 890 | if (boot_cpu_data.x86_stepping <= 4) |
d56593eb | 891 | set_rdt_options("!cmt,!mbmtotal,!mbmlocal,!l3cat"); |
1d9f3e20 TL |
892 | else |
893 | set_rdt_options("!l3cat"); | |
0576113a TL |
894 | } |
895 | } | |
896 | ||
0f00717e BM |
897 | static __init void check_quirks(void) |
898 | { | |
899 | if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) | |
900 | __check_quirks_intel(); | |
901 | } | |
902 | ||
6a445edc VS |
903 | static __init bool get_rdt_resources(void) |
904 | { | |
905 | rdt_alloc_capable = get_rdt_alloc_resources(); | |
906 | rdt_mon_capable = get_rdt_mon_resources(); | |
907 | ||
908 | return (rdt_mon_capable || rdt_alloc_capable); | |
909 | } | |
910 | ||
1ad4fa41 BM |
911 | static __init void rdt_init_res_defs_intel(void) |
912 | { | |
913 | struct rdt_resource *r; | |
914 | ||
915 | for_each_rdt_resource(r) { | |
a36c5ff5 BM |
916 | if (r->rid == RDT_RESOURCE_L3 || |
917 | r->rid == RDT_RESOURCE_L3DATA || | |
918 | r->rid == RDT_RESOURCE_L3CODE || | |
919 | r->rid == RDT_RESOURCE_L2 || | |
920 | r->rid == RDT_RESOURCE_L2DATA || | |
921 | r->rid == RDT_RESOURCE_L2CODE) | |
922 | r->cbm_validate = cbm_validate_intel; | |
923 | else if (r->rid == RDT_RESOURCE_MBA) { | |
1ad4fa41 BM |
924 | r->msr_base = MSR_IA32_MBA_THRTL_BASE; |
925 | r->msr_update = mba_wrmsr_intel; | |
926 | r->parse_ctrlval = parse_bw_intel; | |
927 | } | |
928 | } | |
929 | } | |
930 | ||
4d05bf71 BM |
931 | static __init void rdt_init_res_defs_amd(void) |
932 | { | |
933 | struct rdt_resource *r; | |
934 | ||
935 | for_each_rdt_resource(r) { | |
936 | if (r->rid == RDT_RESOURCE_L3 || | |
937 | r->rid == RDT_RESOURCE_L3DATA || | |
938 | r->rid == RDT_RESOURCE_L3CODE || | |
939 | r->rid == RDT_RESOURCE_L2 || | |
940 | r->rid == RDT_RESOURCE_L2DATA || | |
941 | r->rid == RDT_RESOURCE_L2CODE) | |
942 | r->cbm_validate = cbm_validate_amd; | |
943 | else if (r->rid == RDT_RESOURCE_MBA) { | |
944 | r->msr_base = MSR_IA32_MBA_BW_BASE; | |
945 | r->msr_update = mba_wrmsr_amd; | |
946 | r->parse_ctrlval = parse_bw_amd; | |
947 | } | |
948 | } | |
949 | } | |
950 | ||
1ad4fa41 BM |
951 | static __init void rdt_init_res_defs(void) |
952 | { | |
953 | if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) | |
954 | rdt_init_res_defs_intel(); | |
4d05bf71 BM |
955 | else if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD) |
956 | rdt_init_res_defs_amd(); | |
1ad4fa41 BM |
957 | } |
958 | ||
0af6a48d RC |
959 | static enum cpuhp_state rdt_online; |
960 | ||
923f3a2b | 961 | /* Runs once on the BSP during boot. */ |
0118ad82 RC |
962 | void resctrl_cpu_detect(struct cpuinfo_x86 *c) |
963 | { | |
964 | if (!cpu_has(c, X86_FEATURE_CQM_LLC)) { | |
965 | c->x86_cache_max_rmid = -1; | |
966 | c->x86_cache_occ_scale = -1; | |
f3d44f18 | 967 | c->x86_cache_mbm_width_offset = -1; |
0118ad82 RC |
968 | return; |
969 | } | |
970 | ||
971 | /* will be overridden if occupancy monitoring exists */ | |
972 | c->x86_cache_max_rmid = cpuid_ebx(0xf); | |
973 | ||
974 | if (cpu_has(c, X86_FEATURE_CQM_OCCUP_LLC) || | |
975 | cpu_has(c, X86_FEATURE_CQM_MBM_TOTAL) || | |
976 | cpu_has(c, X86_FEATURE_CQM_MBM_LOCAL)) { | |
977 | u32 eax, ebx, ecx, edx; | |
978 | ||
979 | /* QoS sub-leaf, EAX=0Fh, ECX=1 */ | |
980 | cpuid_count(0xf, 1, &eax, &ebx, &ecx, &edx); | |
981 | ||
982 | c->x86_cache_max_rmid = ecx; | |
983 | c->x86_cache_occ_scale = ebx; | |
2c18bd52 BM |
984 | c->x86_cache_mbm_width_offset = eax & 0xff; |
985 | ||
986 | if (c->x86_vendor == X86_VENDOR_AMD && !c->x86_cache_mbm_width_offset) | |
987 | c->x86_cache_mbm_width_offset = MBM_CNTR_WIDTH_OFFSET_AMD; | |
0118ad82 RC |
988 | } |
989 | } | |
990 | ||
352940ec | 991 | static int __init resctrl_late_init(void) |
78e99b4a | 992 | { |
c1c7c3f9 | 993 | struct rdt_resource *r; |
5ff193fb | 994 | int state, ret; |
c1c7c3f9 | 995 | |
1ad4fa41 BM |
996 | /* |
997 | * Initialize functions(or definitions) that are different | |
998 | * between vendors here. | |
999 | */ | |
1000 | rdt_init_res_defs(); | |
1001 | ||
0f00717e BM |
1002 | check_quirks(); |
1003 | ||
78e99b4a FY |
1004 | if (!get_rdt_resources()) |
1005 | return -ENODEV; | |
1006 | ||
06b57e45 TG |
1007 | rdt_init_padding(); |
1008 | ||
2264d9c7 | 1009 | state = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, |
352940ec BM |
1010 | "x86/resctrl/cat:online:", |
1011 | resctrl_online_cpu, resctrl_offline_cpu); | |
2264d9c7 TL |
1012 | if (state < 0) |
1013 | return state; | |
1014 | ||
5ff193fb FY |
1015 | ret = rdtgroup_init(); |
1016 | if (ret) { | |
1017 | cpuhp_remove_state(state); | |
1018 | return ret; | |
1019 | } | |
0af6a48d | 1020 | rdt_online = state; |
5ff193fb | 1021 | |
1b5c0b75 | 1022 | for_each_alloc_capable_rdt_resource(r) |
352940ec | 1023 | pr_info("%s allocation detected\n", r->name); |
78e99b4a | 1024 | |
6a445edc | 1025 | for_each_mon_capable_rdt_resource(r) |
352940ec | 1026 | pr_info("%s monitoring detected\n", r->name); |
6a445edc | 1027 | |
78e99b4a FY |
1028 | return 0; |
1029 | } | |
1030 | ||
352940ec | 1031 | late_initcall(resctrl_late_init); |
0af6a48d | 1032 | |
352940ec | 1033 | static void __exit resctrl_exit(void) |
0af6a48d RC |
1034 | { |
1035 | cpuhp_remove_state(rdt_online); | |
1036 | rdtgroup_exit(); | |
1037 | } | |
1038 | ||
352940ec | 1039 | __exitcall(resctrl_exit); |