Re: [PATCH RFC v5 16/18] ACPI: RISC-V: Parse RISC-V Quality of Service Controller (RQSC) table
Sunil V L <[email protected]> Mon, 25 May 2026 13:53:09 +0530
| Newsgroups | dev.linux.lists.acpica-devel,dev.linux.lists.linux-rt-devel,org.infradead.lists.linux-riscv,org.kernel.vger.linux-acpi,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CAB19ukE9r_V=0eop3=f8zohhnCavd=9GjDEK1VMMbOb0ReOtDA@mail.gmail.com> |
Hi Drew, On Mon, May 25, 2026 at 5:32 AM Drew Fustini <[email protected]> wrote: > > Add a parser for the ACPI RQSC table, which describes the CBQRI > controllers in a system. For each table entry, populate a > cbqri_controller_info descriptor and hand it to the CBQRI driver via > riscv_cbqri_register_controller(). The driver owns all subsequent state, > including cpumask resolution at cbqri_resctrl_setup() time. > > Link: https://github.com/riscv-non-isa/riscv-rqsc/blob/main/src/ > Link: https://github.com/riscv-non-isa/riscv-cbqri/releases/tag/v1.0 > Assisted-by: Claude:claude-opus-4-7 > Signed-off-by: Drew Fustini <[email protected]> > --- > MAINTAINERS | 2 + > arch/riscv/include/asm/acpi.h | 10 +++ > drivers/acpi/riscv/Makefile | 1 + > drivers/acpi/riscv/rqsc.c | 194 ++++++++++++++++++++++++++++++++++++++++++ > drivers/acpi/riscv/rqsc.h | 63 ++++++++++++++ > 5 files changed, 270 insertions(+) > > diff --git a/MAINTAINERS b/MAINTAINERS > index 7821dd5159cb..eab31c7b5e91 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -23025,6 +23025,8 @@ S: Supported > F: arch/riscv/include/asm/qos.h > F: arch/riscv/include/asm/resctrl.h > F: arch/riscv/kernel/qos.c > +F: drivers/acpi/riscv/rqsc.c > +F: drivers/acpi/riscv/rqsc.h > F: drivers/resctrl/cbqri_devices.c > F: drivers/resctrl/cbqri_internal.h > F: drivers/resctrl/cbqri_resctrl.c > diff --git a/arch/riscv/include/asm/acpi.h b/arch/riscv/include/asm/acpi.h > index 26ab37c171bc..3cfd0102085e 100644 > --- a/arch/riscv/include/asm/acpi.h > +++ b/arch/riscv/include/asm/acpi.h > @@ -67,6 +67,16 @@ int acpi_get_riscv_isa(struct acpi_table_header *table, > > void acpi_get_cbo_block_size(struct acpi_table_header *table, u32 *cbom_size, > u32 *cboz_size, u32 *cbop_size); > + > +#ifdef CONFIG_RISCV_CBQRI_DRIVER > +int __init acpi_parse_rqsc(struct acpi_table_header *table); > +#else > +static inline int acpi_parse_rqsc(struct acpi_table_header *table) > +{ > + return -EINVAL; > +} > +#endif /* CONFIG_RISCV_CBQRI_DRIVER */ > + > #else > static inline void acpi_init_rintc_map(void) { } > static inline struct acpi_madt_rintc *acpi_cpu_get_madt_rintc(int cpu) > diff --git a/drivers/acpi/riscv/Makefile b/drivers/acpi/riscv/Makefile > index 1284a076fa88..77f8f0101b7e 100644 > --- a/drivers/acpi/riscv/Makefile > +++ b/drivers/acpi/riscv/Makefile > @@ -1,5 +1,6 @@ > # SPDX-License-Identifier: GPL-2.0-only > obj-y += rhct.o init.o irq.o > +obj-$(CONFIG_RISCV_CBQRI_DRIVER) += rqsc.o > obj-$(CONFIG_ACPI_PROCESSOR_IDLE) += cpuidle.o > obj-$(CONFIG_ACPI_CPPC_LIB) += cppc.o > obj-$(CONFIG_ACPI_RIMT) += rimt.o > diff --git a/drivers/acpi/riscv/rqsc.c b/drivers/acpi/riscv/rqsc.c > new file mode 100644 > index 000000000000..1cbc5c07e191 > --- /dev/null > +++ b/drivers/acpi/riscv/rqsc.c > @@ -0,0 +1,194 @@ > +// SPDX-License-Identifier: GPL-2.0-only > + > +#define pr_fmt(fmt) "ACPI: RQSC: " fmt > + > +#include <linux/acpi.h> > +#include <linux/bits.h> > +#include <linux/riscv_cbqri.h> > + > +#include "rqsc.h" > + > +#define CBQRI_CTRL_SIZE 0x1000 > + > +int __init acpi_parse_rqsc(struct acpi_table_header *table) > +{ > + struct acpi_table_rqsc *rqsc = (struct acpi_table_rqsc *)table; > + struct acpi_rqsc_node *end, *node; > + int num_controllers = 0; > + > + /* > + * Reject revisions newer than this parser was written against. A > + * future revision could extend the fixed RQSC header before the > + * first node, which would shift the resource subtables and cause the > + * sizeof(*node)-based offset below to point into the wrong place. > + */ > + if (rqsc->header.revision != ACPI_RQSC_REVISION) { > + pr_err("RQSC table revision %u, expected %u, aborting\n", > + rqsc->header.revision, ACPI_RQSC_REVISION); > + return -EINVAL; > + } > + > + /* Reject tables shorter than the fixed RQSC header. */ > + if (rqsc->header.length < sizeof(struct acpi_table_rqsc)) { > + pr_err("RQSC table truncated: length %u < %zu, aborting\n", > + rqsc->header.length, sizeof(struct acpi_table_rqsc)); > + return -EINVAL; > + } > + > + end = ACPI_ADD_PTR(struct acpi_rqsc_node, rqsc, rqsc->header.length); > + > + for (node = ACPI_ADD_PTR(struct acpi_rqsc_node, rqsc, > + sizeof(struct acpi_table_rqsc)); > + node < end; > + node = ACPI_ADD_PTR(struct acpi_rqsc_node, node, node->length) > + ) { > + const struct acpi_rqsc_resource *res0; > + struct cbqri_controller_info info = {}; > + int ret; > + > + if ((void *)node + sizeof(*node) > (void *)end) { > + pr_err("truncated entry at end of table, aborting\n"); > + riscv_cbqri_unregister_last(num_controllers); > + return -EINVAL; > + } > + > + if (node->length < sizeof(*node)) { > + pr_err("malformed RQSC entry: length %u < %zu, aborting\n", > + node->length, sizeof(*node)); > + riscv_cbqri_unregister_last(num_controllers); > + return -EINVAL; > + } > + > + /* > + * Without this check, a node whose length claims to extend > + * past the end of the table would advance the loop cursor > + * past `end` and silently terminate. Flag the corruption > + * explicitly so a malformed firmware table cannot truncate > + * the controller list without noise. > + */ > + if ((void *)node + node->length > (void *)end) { > + pr_err("RQSC entry length %u overruns table end, aborting\n", > + node->length); > + riscv_cbqri_unregister_last(num_controllers); > + return -EINVAL; > + } > + > + /* GAS must describe system memory. ioremap() consumes it later. */ > + if (node->reg.space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) { > + pr_warn("controller has unsupported address space_id=%u, skipping\n", > + node->reg.space_id); > + continue; > + } > + > + /* Address 0 would map page 0 (reset vectors, SBI, boot ROM). */ > + if (!node->reg.address) { > + pr_warn("controller has zero address, skipping\n"); > + continue; > + } > + > + info.type = node->type; > + /* RQSC v0.9.2 section 2 Table 2: 12-byte GAS-format register interface address */ > + info.addr = node->reg.address; > + info.size = CBQRI_CTRL_SIZE; > + info.rcid_count = node->rcid; > + info.mcid_count = node->mcid; > + > + /* See CBQRI_MAX_RCID/MCID in <linux/riscv_cbqri.h> for the rationale. */ > + if (info.rcid_count > CBQRI_MAX_RCID) { > + pr_warn("controller at %pa: rcid_count %u exceeds CBQRI_MAX_RCID %u, skipping\n", > + &info.addr, info.rcid_count, CBQRI_MAX_RCID); > + continue; > + } > + > + if (info.mcid_count > CBQRI_MAX_MCID) { > + pr_warn("controller at %pa: mcid_count %u exceeds CBQRI_MAX_MCID %u, skipping\n", > + &info.addr, info.mcid_count, CBQRI_MAX_MCID); > + continue; > + } > + Do you also want to add a check for the statement in the spec "At least one of RCID Count or MCID Count must be non-zero." ? > + if (node->nres == 0) { > + pr_warn("controller at %pa has no resource descriptors, skipping\n", > + &info.addr); > + continue; > + } > + > + /* > + * Resources follow the node header in-line. Only res[0] is > + * consumed. Bound it against end before reading its prefix so > + * a table that ends partway through a resource subtable is > + * rejected rather than read past the mapping. > + */ > + res0 = (const struct acpi_rqsc_resource *) > + ((const u8 *)node + sizeof(*node)); > + if ((void *)res0 + sizeof(*res0) > (void *)end || > + node->length < sizeof(*node) + sizeof(*res0) || > + res0->length < sizeof(*res0)) { > + pr_warn("controller at %pa: node too short for resource descriptor, skipping\n", > + &info.addr); > + continue; > + } > + > + if (node->nres > 1) > + pr_warn("controller at %pa has %u resource descriptors, using first\n", > + &info.addr, node->nres); > + > + /* > + * id1 is u64 on the wire but cache_id and prox_dom are u32 > + * downstream (PPTT cache_id, ACPI proximity domain). Reject > + * rather than truncate, so a too-large id is not silently > + * mapped to the wrong PPTT entry or NUMA node. > + */ > + if (res0->id1 > U32_MAX) { > + pr_warn("controller at %pa: id1 0x%llx exceeds u32, skipping\n", > + &info.addr, res0->id1); > + continue; > + } > + > + /* > + * Pair the QoS controller type with the resource descriptor > + * fields that index id1. RQSC v0.9.2 Table 4 defines the > + * mapping: a Capacity controller indexes a Processor Cache > + * via PPTT cache_id, a Bandwidth controller indexes a Memory > + * Range via SRAT proximity domain. Mismatched pairings > + * (e.g. a CC whose first resource is Memory) would otherwise > + * route id1 into the wrong downstream lookup. > + */ > + switch (info.type) { > + case CBQRI_CONTROLLER_TYPE_CAPACITY: > + if (res0->type != ACPI_RQSC_RESOURCE_TYPE_CACHE || > + res0->id_type != ACPI_RQSC_RESOURCE_ID_TYPE_PROCESSOR_CACHE) { > + pr_warn("CC at %pa: resource type=%u id_type=%u not (cache, processor cache), skipping\n", > + &info.addr, res0->type, res0->id_type); > + continue; > + } > + info.cache_id = (u32)res0->id1; > + break; > + case CBQRI_CONTROLLER_TYPE_BANDWIDTH: > + if (res0->type != ACPI_RQSC_RESOURCE_TYPE_MEMORY || > + res0->id_type != ACPI_RQSC_RESOURCE_ID_TYPE_MEMORY_RANGE) { > + pr_warn("BC at %pa: resource type=%u id_type=%u not (memory, memory range), skipping\n", > + &info.addr, res0->type, res0->id_type); > + continue; > + } > + info.prox_dom = (u32)res0->id1; > + break; > + default: > + pr_warn("controller at %pa: unknown type %u, skipping\n", > + &info.addr, info.type); > + continue; > + } > + > + pr_debug("registering controller type=%u addr=%pa rcid=%u mcid=%u\n", > + info.type, &info.addr, info.rcid_count, info.mcid_count); > + > + ret = riscv_cbqri_register_controller(&info); > + if (ret == 0) > + num_controllers++; > + else > + pr_warn("controller at %pa: registration failed (%d), skipping\n", > + &info.addr, ret); > + } > + > + pr_info("found %d CBQRI controllers\n", num_controllers); > + return 0; > +} > diff --git a/drivers/acpi/riscv/rqsc.h b/drivers/acpi/riscv/rqsc.h > new file mode 100644 > index 000000000000..f7b556f29e16 > --- /dev/null > +++ b/drivers/acpi/riscv/rqsc.h > @@ -0,0 +1,63 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +/* > + * Local definitions for the RISC-V Quality of Service Controller (RQSC) > + * ACPI table. Will move to ACPICA's include/acpi/actbl2.h once the spec > + * is ratified. > + */ > +#ifndef _DRIVERS_ACPI_RISCV_RQSC_H > +#define _DRIVERS_ACPI_RISCV_RQSC_H > + > +#include <linux/types.h> > +#include <acpi/actbl.h> > + > +#define ACPI_SIG_RQSC "RQSC" /* RISC-V Quality of Service Controller */ > + > +/* RQSC v0.9.2 Table 1: current revision number. */ > +#define ACPI_RQSC_REVISION 1 > + > +/* RQSC v0.9.2 Table 4: Resource Type values for acpi_rqsc_resource.type. */ > +#define ACPI_RQSC_RESOURCE_TYPE_CACHE 0 > +#define ACPI_RQSC_RESOURCE_TYPE_MEMORY 1 > + > +/* RQSC v0.9.2 Table 4: Resource ID Type values for .id_type. */ > +#define ACPI_RQSC_RESOURCE_ID_TYPE_PROCESSOR_CACHE 0 > +#define ACPI_RQSC_RESOURCE_ID_TYPE_MEMORY_RANGE 1 > + Memory-Side Cache, ACPI, PCI devices missing? Thanks, Sunil