Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only
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 *
12 * More information about RDT be found in the Intel (R) x86 Architecture
13 * Software Developer Manual June 2016, volume 3, section 17.17.
14 */
15
16#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17
18#include <linux/cpu.h>
19#include <linux/kernfs.h>
20#include <linux/seq_file.h>
21#include <linux/slab.h>
22#include <linux/tick.h>
23
24#include "internal.h"
25
26struct rdt_parse_data {
27 u32 closid;
28 enum rdtgrp_mode mode;
29 char *buf;
30};
31
32typedef int (ctrlval_parser_t)(struct rdt_parse_data *data,
33 struct resctrl_schema *s,
34 struct rdt_ctrl_domain *d);
35
36/*
37 * Check whether MBA bandwidth percentage value is correct. The value is
38 * checked against the minimum and max bandwidth values specified by the
39 * hardware. The allocated bandwidth percentage is rounded to the next
40 * control step available on the hardware.
41 */
42static bool bw_validate(char *buf, u32 *data, struct rdt_resource *r)
43{
44 int ret;
45 u32 bw;
46
47 /*
48 * Only linear delay values is supported for current Intel SKUs.
49 */
50 if (!r->membw.delay_linear && r->membw.arch_needs_linear) {
51 rdt_last_cmd_puts("No support for non-linear MB domains\n");
52 return false;
53 }
54
55 ret = kstrtou32(buf, 10, &bw);
56 if (ret) {
57 rdt_last_cmd_printf("Invalid MB value %s\n", buf);
58 return false;
59 }
60
61 /* Nothing else to do if software controller is enabled. */
62 if (is_mba_sc(r)) {
63 *data = bw;
64 return true;
65 }
66
67 if (bw < r->membw.min_bw || bw > r->membw.max_bw) {
68 rdt_last_cmd_printf("MB value %u out of range [%d,%d]\n",
69 bw, r->membw.min_bw, r->membw.max_bw);
70 return false;
71 }
72
73 *data = roundup(bw, (unsigned long)r->membw.bw_gran);
74 return true;
75}
76
77static int parse_bw(struct rdt_parse_data *data, struct resctrl_schema *s,
78 struct rdt_ctrl_domain *d)
79{
80 struct resctrl_staged_config *cfg;
81 struct rdt_resource *r = s->res;
82 u32 closid = data->closid;
83 u32 bw_val;
84
85 cfg = &d->staged_config[s->conf_type];
86 if (cfg->have_new_ctrl) {
87 rdt_last_cmd_printf("Duplicate domain %d\n", d->hdr.id);
88 return -EINVAL;
89 }
90
91 if (!bw_validate(data->buf, &bw_val, r))
92 return -EINVAL;
93
94 if (is_mba_sc(r)) {
95 d->mbps_val[closid] = bw_val;
96 return 0;
97 }
98
99 cfg->new_ctrl = bw_val;
100 cfg->have_new_ctrl = true;
101
102 return 0;
103}
104
105/*
106 * Check whether a cache bit mask is valid.
107 * On Intel CPUs, non-contiguous 1s value support is indicated by CPUID:
108 * - CPUID.0x10.1:ECX[3]: L3 non-contiguous 1s value supported if 1
109 * - CPUID.0x10.2:ECX[3]: L2 non-contiguous 1s value supported if 1
110 *
111 * Haswell does not support a non-contiguous 1s value and additionally
112 * requires at least two bits set.
113 * AMD allows non-contiguous bitmasks.
114 */
115static bool cbm_validate(char *buf, u32 *data, struct rdt_resource *r)
116{
117 u32 supported_bits = BIT_MASK(r->cache.cbm_len) - 1;
118 unsigned int cbm_len = r->cache.cbm_len;
119 unsigned long first_bit, zero_bit, val;
120 int ret;
121
122 ret = kstrtoul(buf, 16, &val);
123 if (ret) {
124 rdt_last_cmd_printf("Non-hex character in the mask %s\n", buf);
125 return false;
126 }
127
128 if ((r->cache.min_cbm_bits > 0 && val == 0) || val > supported_bits) {
129 rdt_last_cmd_puts("Mask out of range\n");
130 return false;
131 }
132
133 first_bit = find_first_bit(&val, cbm_len);
134 zero_bit = find_next_zero_bit(&val, cbm_len, first_bit);
135
136 /* Are non-contiguous bitmasks allowed? */
137 if (!r->cache.arch_has_sparse_bitmasks &&
138 (find_next_bit(&val, cbm_len, zero_bit) < cbm_len)) {
139 rdt_last_cmd_printf("The mask %lx has non-consecutive 1-bits\n", val);
140 return false;
141 }
142
143 if ((zero_bit - first_bit) < r->cache.min_cbm_bits) {
144 rdt_last_cmd_printf("Need at least %d bits in the mask\n",
145 r->cache.min_cbm_bits);
146 return false;
147 }
148
149 *data = val;
150 return true;
151}
152
153/*
154 * Read one cache bit mask (hex). Check that it is valid for the current
155 * resource type.
156 */
157static int parse_cbm(struct rdt_parse_data *data, struct resctrl_schema *s,
158 struct rdt_ctrl_domain *d)
159{
160 enum rdtgrp_mode mode = data->mode;
161 struct resctrl_staged_config *cfg;
162 struct rdt_resource *r = s->res;
163 u32 closid = data->closid;
164 u32 cbm_val;
165
166 cfg = &d->staged_config[s->conf_type];
167 if (cfg->have_new_ctrl) {
168 rdt_last_cmd_printf("Duplicate domain %d\n", d->hdr.id);
169 return -EINVAL;
170 }
171
172 /*
173 * Cannot set up more than one pseudo-locked region in a cache
174 * hierarchy.
175 */
176 if (mode == RDT_MODE_PSEUDO_LOCKSETUP &&
177 rdtgroup_pseudo_locked_in_hierarchy(d)) {
178 rdt_last_cmd_puts("Pseudo-locked region in hierarchy\n");
179 return -EINVAL;
180 }
181
182 if (!cbm_validate(data->buf, &cbm_val, r))
183 return -EINVAL;
184
185 if ((mode == RDT_MODE_EXCLUSIVE || mode == RDT_MODE_SHAREABLE) &&
186 rdtgroup_cbm_overlaps_pseudo_locked(d, cbm_val)) {
187 rdt_last_cmd_puts("CBM overlaps with pseudo-locked region\n");
188 return -EINVAL;
189 }
190
191 /*
192 * The CBM may not overlap with the CBM of another closid if
193 * either is exclusive.
194 */
195 if (rdtgroup_cbm_overlaps(s, d, cbm_val, closid, true)) {
196 rdt_last_cmd_puts("Overlaps with exclusive group\n");
197 return -EINVAL;
198 }
199
200 if (rdtgroup_cbm_overlaps(s, d, cbm_val, closid, false)) {
201 if (mode == RDT_MODE_EXCLUSIVE ||
202 mode == RDT_MODE_PSEUDO_LOCKSETUP) {
203 rdt_last_cmd_puts("Overlaps with other group\n");
204 return -EINVAL;
205 }
206 }
207
208 cfg->new_ctrl = cbm_val;
209 cfg->have_new_ctrl = true;
210
211 return 0;
212}
213
214/*
215 * For each domain in this resource we expect to find a series of:
216 * id=mask
217 * separated by ";". The "id" is in decimal, and must match one of
218 * the "id"s for this resource.
219 */
220static int parse_line(char *line, struct resctrl_schema *s,
221 struct rdtgroup *rdtgrp)
222{
223 enum resctrl_conf_type t = s->conf_type;
224 ctrlval_parser_t *parse_ctrlval = NULL;
225 struct resctrl_staged_config *cfg;
226 struct rdt_resource *r = s->res;
227 struct rdt_parse_data data;
228 struct rdt_ctrl_domain *d;
229 char *dom = NULL, *id;
230 unsigned long dom_id;
231
232 /* Walking r->domains, ensure it can't race with cpuhp */
233 lockdep_assert_cpus_held();
234
235 switch (r->schema_fmt) {
236 case RESCTRL_SCHEMA_BITMAP:
237 parse_ctrlval = &parse_cbm;
238 break;
239 case RESCTRL_SCHEMA_RANGE:
240 parse_ctrlval = &parse_bw;
241 break;
242 }
243
244 if (WARN_ON_ONCE(!parse_ctrlval))
245 return -EINVAL;
246
247 if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP &&
248 (r->rid == RDT_RESOURCE_MBA || r->rid == RDT_RESOURCE_SMBA)) {
249 rdt_last_cmd_puts("Cannot pseudo-lock MBA resource\n");
250 return -EINVAL;
251 }
252
253next:
254 if (!line || line[0] == '\0')
255 return 0;
256 dom = strsep(&line, ";");
257 id = strsep(&dom, "=");
258 if (!dom || kstrtoul(id, 10, &dom_id)) {
259 rdt_last_cmd_puts("Missing '=' or non-numeric domain\n");
260 return -EINVAL;
261 }
262 dom = strim(dom);
263 list_for_each_entry(d, &r->ctrl_domains, hdr.list) {
264 if (d->hdr.id == dom_id) {
265 data.buf = dom;
266 data.closid = rdtgrp->closid;
267 data.mode = rdtgrp->mode;
268 if (parse_ctrlval(&data, s, d))
269 return -EINVAL;
270 if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
271 cfg = &d->staged_config[t];
272 /*
273 * In pseudo-locking setup mode and just
274 * parsed a valid CBM that should be
275 * pseudo-locked. Only one locked region per
276 * resource group and domain so just do
277 * the required initialization for single
278 * region and return.
279 */
280 rdtgrp->plr->s = s;
281 rdtgrp->plr->d = d;
282 rdtgrp->plr->cbm = cfg->new_ctrl;
283 d->plr = rdtgrp->plr;
284 return 0;
285 }
286 goto next;
287 }
288 }
289 return -EINVAL;
290}
291
292static int rdtgroup_parse_resource(char *resname, char *tok,
293 struct rdtgroup *rdtgrp)
294{
295 struct resctrl_schema *s;
296
297 list_for_each_entry(s, &resctrl_schema_all, list) {
298 if (!strcmp(resname, s->name) && rdtgrp->closid < s->num_closid)
299 return parse_line(tok, s, rdtgrp);
300 }
301 rdt_last_cmd_printf("Unknown or unsupported resource name '%s'\n", resname);
302 return -EINVAL;
303}
304
305ssize_t rdtgroup_schemata_write(struct kernfs_open_file *of,
306 char *buf, size_t nbytes, loff_t off)
307{
308 struct resctrl_schema *s;
309 struct rdtgroup *rdtgrp;
310 struct rdt_resource *r;
311 char *tok, *resname;
312 int ret = 0;
313
314 /* Valid input requires a trailing newline */
315 if (nbytes == 0 || buf[nbytes - 1] != '\n')
316 return -EINVAL;
317 buf[nbytes - 1] = '\0';
318
319 rdtgrp = rdtgroup_kn_lock_live(of->kn);
320 if (!rdtgrp) {
321 rdtgroup_kn_unlock(of->kn);
322 return -ENOENT;
323 }
324 rdt_last_cmd_clear();
325
326 /*
327 * No changes to pseudo-locked region allowed. It has to be removed
328 * and re-created instead.
329 */
330 if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
331 ret = -EINVAL;
332 rdt_last_cmd_puts("Resource group is pseudo-locked\n");
333 goto out;
334 }
335
336 rdt_staged_configs_clear();
337
338 while ((tok = strsep(&buf, "\n")) != NULL) {
339 resname = strim(strsep(&tok, ":"));
340 if (!tok) {
341 rdt_last_cmd_puts("Missing ':'\n");
342 ret = -EINVAL;
343 goto out;
344 }
345 if (tok[0] == '\0') {
346 rdt_last_cmd_printf("Missing '%s' value\n", resname);
347 ret = -EINVAL;
348 goto out;
349 }
350 ret = rdtgroup_parse_resource(resname, tok, rdtgrp);
351 if (ret)
352 goto out;
353 }
354
355 list_for_each_entry(s, &resctrl_schema_all, list) {
356 r = s->res;
357
358 /*
359 * Writes to mba_sc resources update the software controller,
360 * not the control MSR.
361 */
362 if (is_mba_sc(r))
363 continue;
364
365 ret = resctrl_arch_update_domains(r, rdtgrp->closid);
366 if (ret)
367 goto out;
368 }
369
370 if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
371 /*
372 * If pseudo-locking fails we keep the resource group in
373 * mode RDT_MODE_PSEUDO_LOCKSETUP with its class of service
374 * active and updated for just the domain the pseudo-locked
375 * region was requested for.
376 */
377 ret = rdtgroup_pseudo_lock_create(rdtgrp);
378 }
379
380out:
381 rdt_staged_configs_clear();
382 rdtgroup_kn_unlock(of->kn);
383 return ret ?: nbytes;
384}
385
386static void show_doms(struct seq_file *s, struct resctrl_schema *schema,
387 char *resource_name, int closid)
388{
389 struct rdt_resource *r = schema->res;
390 struct rdt_ctrl_domain *dom;
391 bool sep = false;
392 u32 ctrl_val;
393
394 /* Walking r->domains, ensure it can't race with cpuhp */
395 lockdep_assert_cpus_held();
396
397 if (resource_name)
398 seq_printf(s, "%*s:", max_name_width, resource_name);
399 list_for_each_entry(dom, &r->ctrl_domains, hdr.list) {
400 if (sep)
401 seq_puts(s, ";");
402
403 if (is_mba_sc(r))
404 ctrl_val = dom->mbps_val[closid];
405 else
406 ctrl_val = resctrl_arch_get_config(r, dom, closid,
407 schema->conf_type);
408
409 seq_printf(s, schema->fmt_str, dom->hdr.id, ctrl_val);
410 sep = true;
411 }
412 seq_puts(s, "\n");
413}
414
415int rdtgroup_schemata_show(struct kernfs_open_file *of,
416 struct seq_file *s, void *v)
417{
418 struct resctrl_schema *schema;
419 struct rdtgroup *rdtgrp;
420 int ret = 0;
421 u32 closid;
422
423 rdtgrp = rdtgroup_kn_lock_live(of->kn);
424 if (rdtgrp) {
425 if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
426 list_for_each_entry(schema, &resctrl_schema_all, list) {
427 seq_printf(s, "%s:uninitialized\n", schema->name);
428 }
429 } else if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) {
430 if (!rdtgrp->plr->d) {
431 rdt_last_cmd_clear();
432 rdt_last_cmd_puts("Cache domain offline\n");
433 ret = -ENODEV;
434 } else {
435 seq_printf(s, "%s:%d=%x\n",
436 rdtgrp->plr->s->res->name,
437 rdtgrp->plr->d->hdr.id,
438 rdtgrp->plr->cbm);
439 }
440 } else {
441 closid = rdtgrp->closid;
442 list_for_each_entry(schema, &resctrl_schema_all, list) {
443 if (closid < schema->num_closid)
444 show_doms(s, schema, schema->name, closid);
445 }
446 }
447 } else {
448 ret = -ENOENT;
449 }
450 rdtgroup_kn_unlock(of->kn);
451 return ret;
452}
453
454static int smp_mon_event_count(void *arg)
455{
456 mon_event_count(arg);
457
458 return 0;
459}
460
461ssize_t rdtgroup_mba_mbps_event_write(struct kernfs_open_file *of,
462 char *buf, size_t nbytes, loff_t off)
463{
464 struct rdtgroup *rdtgrp;
465 int ret = 0;
466
467 /* Valid input requires a trailing newline */
468 if (nbytes == 0 || buf[nbytes - 1] != '\n')
469 return -EINVAL;
470 buf[nbytes - 1] = '\0';
471
472 rdtgrp = rdtgroup_kn_lock_live(of->kn);
473 if (!rdtgrp) {
474 rdtgroup_kn_unlock(of->kn);
475 return -ENOENT;
476 }
477 rdt_last_cmd_clear();
478
479 if (!strcmp(buf, "mbm_local_bytes")) {
480 if (resctrl_is_mon_event_enabled(QOS_L3_MBM_LOCAL_EVENT_ID))
481 rdtgrp->mba_mbps_event = QOS_L3_MBM_LOCAL_EVENT_ID;
482 else
483 ret = -EINVAL;
484 } else if (!strcmp(buf, "mbm_total_bytes")) {
485 if (resctrl_is_mon_event_enabled(QOS_L3_MBM_TOTAL_EVENT_ID))
486 rdtgrp->mba_mbps_event = QOS_L3_MBM_TOTAL_EVENT_ID;
487 else
488 ret = -EINVAL;
489 } else {
490 ret = -EINVAL;
491 }
492
493 if (ret)
494 rdt_last_cmd_printf("Unsupported event id '%s'\n", buf);
495
496 rdtgroup_kn_unlock(of->kn);
497
498 return ret ?: nbytes;
499}
500
501int rdtgroup_mba_mbps_event_show(struct kernfs_open_file *of,
502 struct seq_file *s, void *v)
503{
504 struct rdtgroup *rdtgrp;
505 int ret = 0;
506
507 rdtgrp = rdtgroup_kn_lock_live(of->kn);
508
509 if (rdtgrp) {
510 switch (rdtgrp->mba_mbps_event) {
511 case QOS_L3_MBM_LOCAL_EVENT_ID:
512 seq_puts(s, "mbm_local_bytes\n");
513 break;
514 case QOS_L3_MBM_TOTAL_EVENT_ID:
515 seq_puts(s, "mbm_total_bytes\n");
516 break;
517 default:
518 pr_warn_once("Bad event %d\n", rdtgrp->mba_mbps_event);
519 ret = -EINVAL;
520 break;
521 }
522 } else {
523 ret = -ENOENT;
524 }
525
526 rdtgroup_kn_unlock(of->kn);
527
528 return ret;
529}
530
531struct rdt_domain_hdr *resctrl_find_domain(struct list_head *h, int id,
532 struct list_head **pos)
533{
534 struct rdt_domain_hdr *d;
535 struct list_head *l;
536
537 list_for_each(l, h) {
538 d = list_entry(l, struct rdt_domain_hdr, list);
539 /* When id is found, return its domain. */
540 if (id == d->id)
541 return d;
542 /* Stop searching when finding id's position in sorted list. */
543 if (id < d->id)
544 break;
545 }
546
547 if (pos)
548 *pos = l;
549
550 return NULL;
551}
552
553void mon_event_read(struct rmid_read *rr, struct rdt_resource *r,
554 struct rdt_mon_domain *d, struct rdtgroup *rdtgrp,
555 cpumask_t *cpumask, int evtid, int first)
556{
557 int cpu;
558
559 /* When picking a CPU from cpu_mask, ensure it can't race with cpuhp */
560 lockdep_assert_cpus_held();
561
562 /*
563 * Setup the parameters to pass to mon_event_count() to read the data.
564 */
565 rr->rgrp = rdtgrp;
566 rr->evtid = evtid;
567 rr->r = r;
568 rr->d = d;
569 rr->first = first;
570 if (resctrl_arch_mbm_cntr_assign_enabled(r) &&
571 resctrl_is_mbm_event(evtid)) {
572 rr->is_mbm_cntr = true;
573 } else {
574 rr->arch_mon_ctx = resctrl_arch_mon_ctx_alloc(r, evtid);
575 if (IS_ERR(rr->arch_mon_ctx)) {
576 rr->err = -EINVAL;
577 return;
578 }
579 }
580
581 cpu = cpumask_any_housekeeping(cpumask, RESCTRL_PICK_ANY_CPU);
582
583 /*
584 * cpumask_any_housekeeping() prefers housekeeping CPUs, but
585 * are all the CPUs nohz_full? If yes, pick a CPU to IPI.
586 * MPAM's resctrl_arch_rmid_read() is unable to read the
587 * counters on some platforms if its called in IRQ context.
588 */
589 if (tick_nohz_full_cpu(cpu))
590 smp_call_function_any(cpumask, mon_event_count, rr, 1);
591 else
592 smp_call_on_cpu(cpu, smp_mon_event_count, rr, false);
593
594 if (rr->arch_mon_ctx)
595 resctrl_arch_mon_ctx_free(r, evtid, rr->arch_mon_ctx);
596}
597
598int rdtgroup_mondata_show(struct seq_file *m, void *arg)
599{
600 struct kernfs_open_file *of = m->private;
601 enum resctrl_res_level resid;
602 enum resctrl_event_id evtid;
603 struct rdt_domain_hdr *hdr;
604 struct rmid_read rr = {0};
605 struct rdt_mon_domain *d;
606 struct rdtgroup *rdtgrp;
607 int domid, cpu, ret = 0;
608 struct rdt_resource *r;
609 struct cacheinfo *ci;
610 struct mon_data *md;
611
612 rdtgrp = rdtgroup_kn_lock_live(of->kn);
613 if (!rdtgrp) {
614 ret = -ENOENT;
615 goto out;
616 }
617
618 md = of->kn->priv;
619 if (WARN_ON_ONCE(!md)) {
620 ret = -EIO;
621 goto out;
622 }
623
624 resid = md->rid;
625 domid = md->domid;
626 evtid = md->evtid;
627 r = resctrl_arch_get_resource(resid);
628
629 if (md->sum) {
630 /*
631 * This file requires summing across all domains that share
632 * the L3 cache id that was provided in the "domid" field of the
633 * struct mon_data. Search all domains in the resource for
634 * one that matches this cache id.
635 */
636 list_for_each_entry(d, &r->mon_domains, hdr.list) {
637 if (d->ci_id == domid) {
638 cpu = cpumask_any(&d->hdr.cpu_mask);
639 ci = get_cpu_cacheinfo_level(cpu, RESCTRL_L3_CACHE);
640 if (!ci)
641 continue;
642 rr.ci = ci;
643 mon_event_read(&rr, r, NULL, rdtgrp,
644 &ci->shared_cpu_map, evtid, false);
645 goto checkresult;
646 }
647 }
648 ret = -ENOENT;
649 goto out;
650 } else {
651 /*
652 * This file provides data from a single domain. Search
653 * the resource to find the domain with "domid".
654 */
655 hdr = resctrl_find_domain(&r->mon_domains, domid, NULL);
656 if (!hdr || WARN_ON_ONCE(hdr->type != RESCTRL_MON_DOMAIN)) {
657 ret = -ENOENT;
658 goto out;
659 }
660 d = container_of(hdr, struct rdt_mon_domain, hdr);
661 mon_event_read(&rr, r, d, rdtgrp, &d->hdr.cpu_mask, evtid, false);
662 }
663
664checkresult:
665
666 /*
667 * -ENOENT is a special case, set only when "mbm_event" counter assignment
668 * mode is enabled and no counter has been assigned.
669 */
670 if (rr.err == -EIO)
671 seq_puts(m, "Error\n");
672 else if (rr.err == -EINVAL)
673 seq_puts(m, "Unavailable\n");
674 else if (rr.err == -ENOENT)
675 seq_puts(m, "Unassigned\n");
676 else
677 seq_printf(m, "%llu\n", rr.val);
678
679out:
680 rdtgroup_kn_unlock(of->kn);
681 return ret;
682}
683
684int resctrl_io_alloc_show(struct kernfs_open_file *of, struct seq_file *seq, void *v)
685{
686 struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
687 struct rdt_resource *r = s->res;
688
689 mutex_lock(&rdtgroup_mutex);
690
691 if (r->cache.io_alloc_capable) {
692 if (resctrl_arch_get_io_alloc_enabled(r))
693 seq_puts(seq, "enabled\n");
694 else
695 seq_puts(seq, "disabled\n");
696 } else {
697 seq_puts(seq, "not supported\n");
698 }
699
700 mutex_unlock(&rdtgroup_mutex);
701
702 return 0;
703}
704
705/*
706 * resctrl_io_alloc_closid_supported() - io_alloc feature utilizes the
707 * highest CLOSID value to direct I/O traffic. Ensure that io_alloc_closid
708 * is in the supported range.
709 */
710static bool resctrl_io_alloc_closid_supported(u32 io_alloc_closid)
711{
712 return io_alloc_closid < closids_supported();
713}
714
715/*
716 * Initialize io_alloc CLOSID cache resource CBM with all usable (shared
717 * and unused) cache portions.
718 */
719static int resctrl_io_alloc_init_cbm(struct resctrl_schema *s, u32 closid)
720{
721 enum resctrl_conf_type peer_type;
722 struct rdt_resource *r = s->res;
723 struct rdt_ctrl_domain *d;
724 int ret;
725
726 rdt_staged_configs_clear();
727
728 ret = rdtgroup_init_cat(s, closid);
729 if (ret < 0)
730 goto out;
731
732 /* Keep CDP_CODE and CDP_DATA of io_alloc CLOSID's CBM in sync. */
733 if (resctrl_arch_get_cdp_enabled(r->rid)) {
734 peer_type = resctrl_peer_type(s->conf_type);
735 list_for_each_entry(d, &s->res->ctrl_domains, hdr.list)
736 memcpy(&d->staged_config[peer_type],
737 &d->staged_config[s->conf_type],
738 sizeof(d->staged_config[0]));
739 }
740
741 ret = resctrl_arch_update_domains(r, closid);
742out:
743 rdt_staged_configs_clear();
744 return ret;
745}
746
747/*
748 * resctrl_io_alloc_closid() - io_alloc feature routes I/O traffic using
749 * the highest available CLOSID. Retrieve the maximum CLOSID supported by the
750 * resource. Note that if Code Data Prioritization (CDP) is enabled, the number
751 * of available CLOSIDs is reduced by half.
752 */
753u32 resctrl_io_alloc_closid(struct rdt_resource *r)
754{
755 if (resctrl_arch_get_cdp_enabled(r->rid))
756 return resctrl_arch_get_num_closid(r) / 2 - 1;
757 else
758 return resctrl_arch_get_num_closid(r) - 1;
759}
760
761ssize_t resctrl_io_alloc_write(struct kernfs_open_file *of, char *buf,
762 size_t nbytes, loff_t off)
763{
764 struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
765 struct rdt_resource *r = s->res;
766 char const *grp_name;
767 u32 io_alloc_closid;
768 bool enable;
769 int ret;
770
771 ret = kstrtobool(buf, &enable);
772 if (ret)
773 return ret;
774
775 cpus_read_lock();
776 mutex_lock(&rdtgroup_mutex);
777
778 rdt_last_cmd_clear();
779
780 if (!r->cache.io_alloc_capable) {
781 rdt_last_cmd_printf("io_alloc is not supported on %s\n", s->name);
782 ret = -ENODEV;
783 goto out_unlock;
784 }
785
786 /* If the feature is already up to date, no action is needed. */
787 if (resctrl_arch_get_io_alloc_enabled(r) == enable)
788 goto out_unlock;
789
790 io_alloc_closid = resctrl_io_alloc_closid(r);
791 if (!resctrl_io_alloc_closid_supported(io_alloc_closid)) {
792 rdt_last_cmd_printf("io_alloc CLOSID (ctrl_hw_id) %u is not available\n",
793 io_alloc_closid);
794 ret = -EINVAL;
795 goto out_unlock;
796 }
797
798 if (enable) {
799 if (!closid_alloc_fixed(io_alloc_closid)) {
800 grp_name = rdtgroup_name_by_closid(io_alloc_closid);
801 WARN_ON_ONCE(!grp_name);
802 rdt_last_cmd_printf("CLOSID (ctrl_hw_id) %u for io_alloc is used by %s group\n",
803 io_alloc_closid, grp_name ? grp_name : "another");
804 ret = -ENOSPC;
805 goto out_unlock;
806 }
807
808 ret = resctrl_io_alloc_init_cbm(s, io_alloc_closid);
809 if (ret) {
810 rdt_last_cmd_puts("Failed to initialize io_alloc allocations\n");
811 closid_free(io_alloc_closid);
812 goto out_unlock;
813 }
814 } else {
815 closid_free(io_alloc_closid);
816 }
817
818 ret = resctrl_arch_io_alloc_enable(r, enable);
819 if (enable && ret) {
820 rdt_last_cmd_puts("Failed to enable io_alloc feature\n");
821 closid_free(io_alloc_closid);
822 }
823
824out_unlock:
825 mutex_unlock(&rdtgroup_mutex);
826 cpus_read_unlock();
827
828 return ret ?: nbytes;
829}
830
831int resctrl_io_alloc_cbm_show(struct kernfs_open_file *of, struct seq_file *seq, void *v)
832{
833 struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
834 struct rdt_resource *r = s->res;
835 int ret = 0;
836
837 cpus_read_lock();
838 mutex_lock(&rdtgroup_mutex);
839
840 rdt_last_cmd_clear();
841
842 if (!r->cache.io_alloc_capable) {
843 rdt_last_cmd_printf("io_alloc is not supported on %s\n", s->name);
844 ret = -ENODEV;
845 goto out_unlock;
846 }
847
848 if (!resctrl_arch_get_io_alloc_enabled(r)) {
849 rdt_last_cmd_printf("io_alloc is not enabled on %s\n", s->name);
850 ret = -EINVAL;
851 goto out_unlock;
852 }
853
854 /*
855 * When CDP is enabled, the CBMs of the highest CLOSID of CDP_CODE and
856 * CDP_DATA are kept in sync. As a result, the io_alloc CBMs shown for
857 * either CDP resource are identical and accurately represent the CBMs
858 * used for I/O.
859 */
860 show_doms(seq, s, NULL, resctrl_io_alloc_closid(r));
861
862out_unlock:
863 mutex_unlock(&rdtgroup_mutex);
864 cpus_read_unlock();
865 return ret;
866}
867
868static int resctrl_io_alloc_parse_line(char *line, struct rdt_resource *r,
869 struct resctrl_schema *s, u32 closid)
870{
871 enum resctrl_conf_type peer_type;
872 struct rdt_parse_data data;
873 struct rdt_ctrl_domain *d;
874 char *dom = NULL, *id;
875 unsigned long dom_id;
876
877next:
878 if (!line || line[0] == '\0')
879 return 0;
880
881 dom = strsep(&line, ";");
882 id = strsep(&dom, "=");
883 if (!dom || kstrtoul(id, 10, &dom_id)) {
884 rdt_last_cmd_puts("Missing '=' or non-numeric domain\n");
885 return -EINVAL;
886 }
887
888 dom = strim(dom);
889 list_for_each_entry(d, &r->ctrl_domains, hdr.list) {
890 if (d->hdr.id == dom_id) {
891 data.buf = dom;
892 data.mode = RDT_MODE_SHAREABLE;
893 data.closid = closid;
894 if (parse_cbm(&data, s, d))
895 return -EINVAL;
896 /*
897 * Keep io_alloc CLOSID's CBM of CDP_CODE and CDP_DATA
898 * in sync.
899 */
900 if (resctrl_arch_get_cdp_enabled(r->rid)) {
901 peer_type = resctrl_peer_type(s->conf_type);
902 memcpy(&d->staged_config[peer_type],
903 &d->staged_config[s->conf_type],
904 sizeof(d->staged_config[0]));
905 }
906 goto next;
907 }
908 }
909
910 return -EINVAL;
911}
912
913ssize_t resctrl_io_alloc_cbm_write(struct kernfs_open_file *of, char *buf,
914 size_t nbytes, loff_t off)
915{
916 struct resctrl_schema *s = rdt_kn_parent_priv(of->kn);
917 struct rdt_resource *r = s->res;
918 u32 io_alloc_closid;
919 int ret = 0;
920
921 /* Valid input requires a trailing newline */
922 if (nbytes == 0 || buf[nbytes - 1] != '\n')
923 return -EINVAL;
924
925 buf[nbytes - 1] = '\0';
926
927 cpus_read_lock();
928 mutex_lock(&rdtgroup_mutex);
929 rdt_last_cmd_clear();
930
931 if (!r->cache.io_alloc_capable) {
932 rdt_last_cmd_printf("io_alloc is not supported on %s\n", s->name);
933 ret = -ENODEV;
934 goto out_unlock;
935 }
936
937 if (!resctrl_arch_get_io_alloc_enabled(r)) {
938 rdt_last_cmd_printf("io_alloc is not enabled on %s\n", s->name);
939 ret = -EINVAL;
940 goto out_unlock;
941 }
942
943 io_alloc_closid = resctrl_io_alloc_closid(r);
944
945 rdt_staged_configs_clear();
946 ret = resctrl_io_alloc_parse_line(buf, r, s, io_alloc_closid);
947 if (ret)
948 goto out_clear_configs;
949
950 ret = resctrl_arch_update_domains(r, io_alloc_closid);
951
952out_clear_configs:
953 rdt_staged_configs_clear();
954out_unlock:
955 mutex_unlock(&rdtgroup_mutex);
956 cpus_read_unlock();
957
958 return ret ?: nbytes;
959}