From nobody Fri Sep 25 01:22:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 8554B53D9DC; Thu, 17 Sep 2026 16:40:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789663250; cv=none; b=DhI9S68a0UnCG01NIdkjUs/oZCvNMSogKDBQxKEoxkHO8sSEcX0R1MmY3sHtVviS6EnWNKSobjT1CtTtG3JHTUKRxursVGDT8cwEuahmMWqgyimO86yz4edyYi69aXkoMM3UtXOtmCfCjf+qGkGumwKw10mt35490itI7jhquhI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789663250; c=relaxed/simple; bh=n1oNxYGfRcoalPfX9zdnossg9cc0L6I1D+UkL3guyNw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=QVY/Kv9S5ML/MYMfIm1Gl4ZrlCJ3eFQlUt3xuTL/MvHsjADbsYAXhmkZL53k3nmStRJ8hio9mqXkQnSjippHED18OXvI75U6BVRvjaTMAVSTJReaEXTox9JF1e1RYrH8LZsd78Df/61iB3Apm10LlTyyRtAj8v/micjUTG0JeZA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=fcobTGYY; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="fcobTGYY" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B00351F008A0; Thu, 17 Sep 2026 16:40:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789663247; bh=gkQV6uIygahwb1xChVEc3Iot4Tsj9Co3eDwLqaA/YzM=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=fcobTGYYxN5JNgZkfMkVb9p8w1puh9jl9qqB8/Y//hPCIVoE0Wda3wlfDQZU/otbQ GVShK2kJXf5sPwt+seh9azaeZVXLspMnh50HwJKKFwlHtOy/9Deu2F20h5y49Okm7c vUftpNF6BBzWqt0hZuoYpAywtlkO3n0yCU77XLrzXEs+AU+2e2pKZok9bdlMRCUeol kQrKPWMxAm+fZ6OgX8JkqC/1j/PUaHbqm2HB7hrSK1DqGx/nMTb3ppB2dAGlayLS66 MeykaAwV7c6JUY5Zb2eWmMtv19dhN7VKZgbg8ccRaq+UTW8rpszprMp1Eg8y7ZQGZg 2b4IqWYZGpA1Q== From: Drew Fustini Date: Thu, 17 Sep 2026 09:39:37 -0700 Subject: [PATCH v8 1/5] riscv_cbqri: Add capacity controller probe and allocation device ops Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260917-dfustini-atl-sc-cbqri-dt-v8-1-7964e8d73fe8@kernel.org> References: <20260917-dfustini-atl-sc-cbqri-dt-v8-0-7964e8d73fe8@kernel.org> In-Reply-To: <20260917-dfustini-atl-sc-cbqri-dt-v8-0-7964e8d73fe8@kernel.org> To: Adrien Ricciardi , Alexandre Ghiti , Albert Ou , Atish Kumar Patra , Atish Patra , Babu Moger , Ben Horgan , Borislav Petkov , Chen Pei , Conor Dooley , Conor Dooley , Dave Hansen , Dave Martin , Fenghua Yu , Gong Shuai , Gong Shuai , guo.wenjia23@zte.com.cn, James Morse , =?utf-8?q?Kornel_Dul=C4=99ba?= , Krzysztof Kozlowski , liu.qingtao2@zte.com.cn, Liu Zhiwei , Palmer Dabbelt , Paul Walmsley , Peter Newman , =?utf-8?q?Radim_Kr=C4=8Dm=C3=A1=C5=99?= , Reinette Chatre , Rob Herring , Samuel Holland , Sebastian Andrzej Siewior , Clark Williams , Steven Rostedt , Tony Luck , Vasudevan Srinivasan , Ved Shanbhogue , Weiwei Li , yunhui cui , Zhanpeng Zhang , Drew Fustini Cc: linux-kernel@vger.kernel.org, linux-riscv@lists.infradead.org, x86@kernel.org, devicetree@vger.kernel.org, linux-rt-devel@lists.linux.dev, linux-doc@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=26493; i=fustini@kernel.org; h=from:subject:message-id; bh=n1oNxYGfRcoalPfX9zdnossg9cc0L6I1D+UkL3guyNw=; b=owGbwMvMwCV2+43O4ZsaG3kYT6slMWStkeCN7bQoey34+KJXOce6lsZegRLu04zF13Q/vT51z 6f03s4THaUsDGJcDLJiiiybPuRdWOIV+nXB/BfbYOawMoEMYeDiFICJvLdgZLh+71H08unOf6WP Ls2bECLpq7/iXFQO+y9mWyUhS8Z51msY/lec7yhcfTJZ4c7yGRMS7X5bxPXHTP1aZH/dq+lM9Nm iGzwA X-Developer-Key: i=fustini@kernel.org; a=openpgp; fpr=1B6F948213EA489734F3997035D5CD577C1E6010 Add support for the RISC-V CBQRI capacity controller. A platform driver passes a cbqri_controller_info descriptor together with the cache level to riscv_cbqri_register_cc_dt(), which probes the controller and adds it to the controller list. Assisted-by: LLM Co-developed-by: Adrien Ricciardi Signed-off-by: Adrien Ricciardi Signed-off-by: Drew Fustini --- MAINTAINERS | 3 + drivers/resctrl/Kconfig | 9 + drivers/resctrl/Makefile | 3 + drivers/resctrl/cbqri_devices.c | 556 +++++++++++++++++++++++++++++++++++= ++++ drivers/resctrl/cbqri_internal.h | 122 +++++++++ include/linux/riscv_cbqri.h | 45 ++++ 6 files changed, 738 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 3a19da74d00c..92b111cd25c0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23571,6 +23571,9 @@ L: linux-riscv@lists.infradead.org S: Supported F: arch/riscv/include/asm/qos.h F: arch/riscv/kernel/qos.c +F: drivers/resctrl/cbqri_devices.c +F: drivers/resctrl/cbqri_internal.h +F: include/linux/riscv_cbqri.h =20 RISC-V RPMI AND MPXY DRIVERS M: Rahul Pathak diff --git a/drivers/resctrl/Kconfig b/drivers/resctrl/Kconfig index 672abea3b03c..1219fb29aa2e 100644 --- a/drivers/resctrl/Kconfig +++ b/drivers/resctrl/Kconfig @@ -29,3 +29,12 @@ config ARM64_MPAM_RESCTRL_FS default y if ARM64_MPAM_DRIVER && RESCTRL_FS select RESCTRL_RMID_DEPENDS_ON_CLOSID select RESCTRL_ASSIGN_FIXED + +menuconfig RISCV_CBQRI + bool "RISC-V CBQRI support" + depends on RISCV && RISCV_ISA_SSQOSID + help + Capacity and Bandwidth QoS Register Interface (CBQRI) support for + RISC-V cache QoS resources. CBQRI exposes cache capacity + allocation through the resctrl filesystem at /sys/fs/resctrl when + RESCTRL_FS is also enabled. diff --git a/drivers/resctrl/Makefile b/drivers/resctrl/Makefile index 4f6d0e81f9b8..4d8a2c4b5627 100644 --- a/drivers/resctrl/Makefile +++ b/drivers/resctrl/Makefile @@ -3,3 +3,6 @@ mpam-y +=3D mpam_devices.o mpam-$(CONFIG_ARM64_MPAM_RESCTRL_FS) +=3D mpam_resctrl.o =20 ccflags-$(CONFIG_ARM64_MPAM_DRIVER_DEBUG) +=3D -DDEBUG + +obj-$(CONFIG_RISCV_CBQRI) +=3D cbqri.o +cbqri-y +=3D cbqri_devices.o diff --git a/drivers/resctrl/cbqri_devices.c b/drivers/resctrl/cbqri_device= s.c new file mode 100644 index 000000000000..778189799178 --- /dev/null +++ b/drivers/resctrl/cbqri_devices.c @@ -0,0 +1,556 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "cbqri_internal.h" + +LIST_HEAD(cbqri_controllers); + +/* + * Serializes cbqri_controllers mutations against a concurrent insert under + * asynchronous driver probing, and against the boot-time walk in the resc= trl + * glue. Runtime cpuhp walks happen after registration has settled. + */ +DEFINE_MUTEX(cbqri_controllers_lock); + +/* + * CBQRI registers are 64-bit, but the spec only guarantees single-copy + * atomicity for naturally aligned 4-byte accesses. Read the two halves and + * reconstruct, so the driver does not rely on native 64-bit MMIO. + * + * The halves are not sampled atomically, but a tear is harmless. The fiel= ds + * that change while an operation runs, BUSY (bit 39) and STATUS (bits + * 38:32), are in the high half and read by the single high readl(). The l= ow + * half (OP/AT/RCID) is software-written and constant for the operation. + */ +static u64 cbqri_readq(void __iomem *addr) +{ + u32 lo =3D readl(addr); + u32 hi =3D readl(addr + 4); + + return (u64)hi << 32 | lo; +} + +/* Set capacity block mask (cc_block_mask) */ +static void cbqri_set_cbm(struct cbqri_controller *ctrl, u64 cbm) +{ + /* + * cbqri_probe_cc() rejects ncblks > 32, so the mask fits the low word. + * Per CBQRI 3.5 the cc_block_mask bits above NCBLKS are read-only zero, + * so the upper word needs no write. + */ + writel(lower_32_bits(cbm), ctrl->base + CBQRI_CC_BLOCK_MASK_OFF); +} + +/* + * Clear cc_cunits so a CONFIG_LIMIT on a CUNITS-capable controller impose= s no + * capacity-unit limit. resctrl models only the block mask. On controllers + * with cc_capabilities.CUNITS =3D=3D 0 the register is read-only zero, so= there is + * nothing to do. + */ +static void cbqri_clear_cunits(struct cbqri_controller *ctrl) +{ + if (ctrl->cc.cunits) { + writel(0, ctrl->base + CBQRI_CC_CUNITS_OFF); + writel(0, ctrl->base + CBQRI_CC_CUNITS_OFF + 4); + } +} + +static int cbqri_wait_busy_flag(struct cbqri_controller *ctrl, int reg_off= set, + u64 *regp) +{ + u64 reg; + int ret; + + /* + * Sleeping poll: caller holds ctrl->lock as a sleeping mutex, so + * 10us/1ms is safe under PREEMPT_RT. + */ + ret =3D read_poll_timeout(cbqri_readq, reg, + !FIELD_GET(CBQRI_CONTROL_REGISTERS_BUSY_MASK, reg), + 10, 1000, false, ctrl->base + reg_offset); + if (ret) + return ret; + if (regp) + *regp =3D reg; + return 0; +} + +/* + * Perform capacity allocation control operation on capacity controller. + * cbm is staged in cc_block_mask and cc_cunits is cleared only after BUSY + * reads 0, since CBQRI 3.4 leaves writes to those registers UNSPECIFIED + * while an operation is in flight. READ_LIMIT overwrites both on success, + * so staging 0 before a read makes a silent firmware no-op visible. + * Caller must hold ctrl->lock. + */ +static int cbqri_cc_alloc_op(struct cbqri_controller *ctrl, int operation, + int rcid, u32 at, u64 cbm) +{ + int reg_offset =3D CBQRI_CC_ALLOC_CTL_OFF; + int status; + u64 reg; + + lockdep_assert_held(&ctrl->lock); + + if (cbqri_wait_busy_flag(ctrl, reg_offset, ®) < 0) { + pr_err_ratelimited("BUSY timeout before starting operation\n"); + return -EIO; + } + + cbqri_set_cbm(ctrl, cbm); + cbqri_clear_cunits(ctrl); + + FIELD_MODIFY(CBQRI_CONTROL_REGISTERS_OP_MASK, ®, operation); + FIELD_MODIFY(CBQRI_CONTROL_REGISTERS_RCID_MASK, ®, rcid); + + /* + * CBQRI Table 1: AT 0=3DData, 1=3DCode. Program AT on controllers + * that report supports_alloc_at_code. On controllers that don't, + * AT is reserved-zero and the op acts on both halves. + */ + reg &=3D ~CBQRI_CONTROL_REGISTERS_AT_MASK; + if (ctrl->cc.supports_alloc_at_code) + reg |=3D FIELD_PREP(CBQRI_CONTROL_REGISTERS_AT_MASK, at); + + writel(lower_32_bits(reg), ctrl->base + reg_offset); + + if (cbqri_wait_busy_flag(ctrl, reg_offset, ®) < 0) { + pr_err_ratelimited("BUSY timeout during operation\n"); + return -EIO; + } + + status =3D FIELD_GET(CBQRI_CONTROL_REGISTERS_STATUS_MASK, reg); + if (status !=3D CBQRI_CC_ALLOC_CTL_STATUS_SUCCESS) { + pr_err_ratelimited("operation %d failed: status=3D%d\n", operation, stat= us); + return -EIO; + } + + return 0; +} + +/* + * Apply a capacity block mask and verify via CONFIG_LIMIT + READ_LIMIT. + * + * AT-capable controllers with CDP off need a second CONFIG_LIMIT on the + * other AT half (the spec encodes AT only as 0=3DData / 1=3DCode, there is + * no "both halves" value). CDP-on issues separate per-type writes from + * resctrl, so a single CONFIG_LIMIT per call is correct. + */ +int cbqri_apply_cache_config(struct cbqri_controller *ctrl, u32 closid, + const struct cbqri_cc_config *cfg) +{ + bool need_at_mirror; + u64 saved_cbm =3D 0; + int err =3D 0; + u64 reg; + + mutex_lock(&ctrl->lock); + + need_at_mirror =3D ctrl->cc.supports_alloc_at_code && !cfg->cdp_enabled; + + /* + * Capture the cfg->at half CBM before any write so a partial + * AT-mirror failure can revert and keep the two halves consistent. + */ + if (need_at_mirror) { + err =3D cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT, + closid, cfg->at, 0); + if (err < 0) + goto out; + saved_cbm =3D cbqri_readq(ctrl->base + CBQRI_CC_BLOCK_MASK_OFF); + } + + /* Capacity config limit operation for the AT half implied by cfg->at */ + err =3D cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT, + closid, cfg->at, cfg->cbm); + if (err < 0) + goto out; + + /* + * CDP-off mirror: on AT-capable controllers, also program the + * other AT half with the same mask so the two halves stay in sync. + */ + if (need_at_mirror) { + u32 other =3D (cfg->at =3D=3D CBQRI_CONTROL_REGISTERS_AT_CODE) ? + CBQRI_CONTROL_REGISTERS_AT_DATA : + CBQRI_CONTROL_REGISTERS_AT_CODE; + + err =3D cbqri_cc_alloc_op(ctrl, + CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT, + closid, other, cfg->cbm); + if (err < 0) { + int rerr; + + /* + * Best-effort revert of the cfg->at half so the two + * halves stay in sync. A schemata read sees only one + * half, so silent divergence would otherwise report + * the new value as if the write had succeeded. + */ + rerr =3D cbqri_cc_alloc_op(ctrl, + CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT, + closid, cfg->at, saved_cbm); + if (rerr < 0) + pr_err_ratelimited("AT-mirror revert failed (err=3D%d), AT halves dive= rged\n", + rerr); + goto out; + } + } + + /* Read the limit back to verify the block mask landed */ + err =3D cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT, + closid, cfg->at, 0); + if (err < 0) + goto out; + + /* + * Read capacity blockmask and narrow to u32 to match resctrl's CBM + * width. cbqri_probe_cc() rejects ncblks > 32 so the upper bits are + * reserved zero. + */ + reg =3D cbqri_readq(ctrl->base + CBQRI_CC_BLOCK_MASK_OFF); + if (lower_32_bits(reg) !=3D cfg->cbm) { + pr_err_ratelimited("CBM verify mismatch (reg=3D%llx !=3D cbm=3D%llx)\n", + reg, cfg->cbm); + err =3D -EIO; + } + +out: + mutex_unlock(&ctrl->lock); + return err; +} + +/* Read the configured CBM for closid on the at half via READ_LIMIT. */ +int cbqri_read_cache_config(struct cbqri_controller *ctrl, u32 closid, + u32 at, u32 *cbm_out) +{ + int err; + + mutex_lock(&ctrl->lock); + err =3D cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT, closid,= at, 0); + if (err =3D=3D 0) { + /* + * resctrl exposes the CBM as a u32 and cbqri_probe_cc() rejects + * ncblks > 32, so the mask fits the low 32-bit word of the + * cc_block_mask register. + */ + *cbm_out =3D readl(ctrl->base + CBQRI_CC_BLOCK_MASK_OFF); + } + mutex_unlock(&ctrl->lock); + return err; +} + +static int cbqri_probe_feature(struct cbqri_controller *ctrl, int reg_offs= et, + int operation, int *status, bool *access_type_supported) +{ + const u64 active_mask =3D CBQRI_CONTROL_REGISTERS_OP_MASK | + CBQRI_CONTROL_REGISTERS_AT_MASK | + CBQRI_CONTROL_REGISTERS_RCID_MASK; + u64 reg, saved_reg; + int at; + + /* + * Default the output to false so the status=3D=3D0 (feature not + * implemented) path returns a deterministic value to the caller + * rather than leaving an uninitialized bool. + */ + *access_type_supported =3D false; + + /* Keep the initial register value to preserve the WPRI fields */ + reg =3D cbqri_readq(ctrl->base + reg_offset); + saved_reg =3D reg; + + /* Drain any in-flight firmware op before issuing our own write. */ + if (cbqri_wait_busy_flag(ctrl, reg_offset, &saved_reg) < 0) { + pr_err("BUSY timeout before probe operation\n"); + return -EIO; + } + + /* + * Execute the requested operation with all active fields + * (OP/AT/RCID) zeroed except OP itself. Every bit not in + * active_mask is WPRI and gets carried over from saved_reg. + */ + reg =3D (saved_reg & ~active_mask) | + FIELD_PREP(CBQRI_CONTROL_REGISTERS_OP_MASK, operation); + writel(lower_32_bits(reg), ctrl->base + reg_offset); + if (cbqri_wait_busy_flag(ctrl, reg_offset, ®) < 0) { + pr_err_ratelimited("BUSY timeout during operation\n"); + return -EIO; + } + + /* Get the operation status */ + *status =3D FIELD_GET(CBQRI_CONTROL_REGISTERS_STATUS_MASK, reg); + + /* + * Check for the AT support if the register is implemented + * (if not, the status value will remain 0) + */ + if (*status !=3D 0) { + /* + * Re-issue operation with AT=3DCODE so the controller + * latches AT=3DCODE on supported hardware (or resets it to 0 + * on hardware that doesn't). OP must be a defined CBQRI op + * here. OP=3D0 is a no-op and would silently disable CDP. + */ + reg =3D (saved_reg & ~active_mask) | + FIELD_PREP(CBQRI_CONTROL_REGISTERS_OP_MASK, operation) | + FIELD_PREP(CBQRI_CONTROL_REGISTERS_AT_MASK, + CBQRI_CONTROL_REGISTERS_AT_CODE); + writel(lower_32_bits(reg), ctrl->base + reg_offset); + if (cbqri_wait_busy_flag(ctrl, reg_offset, ®) < 0) { + pr_err("BUSY timeout setting AT field\n"); + return -EIO; + } + + /* + * If the AT field value has been reset to zero, + * then the AT support is not present + */ + at =3D FIELD_GET(CBQRI_CONTROL_REGISTERS_AT_MASK, reg); + if (at =3D=3D CBQRI_CONTROL_REGISTERS_AT_CODE) + *access_type_supported =3D true; + } + + /* + * Restore the original register value. + * Clear OP to avoid re-triggering the probe op. + */ + saved_reg &=3D ~CBQRI_CONTROL_REGISTERS_OP_MASK; + writel(lower_32_bits(saved_reg), ctrl->base + reg_offset); + if (cbqri_wait_busy_flag(ctrl, reg_offset, NULL) < 0) { + pr_err("BUSY timeout restoring register value\n"); + return -EIO; + } + + return 0; +} + +static int cbqri_probe_cc(struct cbqri_controller *ctrl) +{ + int err, status; + int ver_major, ver_minor; + u64 reg; + + reg =3D cbqri_readq(ctrl->base + CBQRI_CC_CAPABILITIES_OFF); + if (reg =3D=3D 0) + return -ENODEV; + + ver_minor =3D FIELD_GET(CBQRI_CC_CAPABILITIES_VER_MINOR_MASK, reg); + ver_major =3D FIELD_GET(CBQRI_CC_CAPABILITIES_VER_MAJOR_MASK, reg); + ctrl->cc.ncblks =3D FIELD_GET(CBQRI_CC_CAPABILITIES_NCBLKS_MASK, reg); + + pr_debug("version=3D%d.%d ncblks=3D%d cache_level=3D%d\n", + ver_major, ver_minor, + ctrl->cc.ncblks, ctrl->cache.cache_level); + + /* + * NCBLKS =3D=3D 0 would divide-by-zero in the schemata math while + * ctrl->lock is held. + */ + if (!ctrl->cc.ncblks) { + pr_warn("CC at %pa has 0 capacity blocks, skipping\n", + &ctrl->addr); + return -ENODEV; + } + + /* + * resctrl represents the capacity bitmask (CBM) as a u32, so a + * controller with more than 32 capacity blocks cannot be expressed + * through the schemata interface. Skip it rather than truncate. + */ + if (ctrl->cc.ncblks > 32) { + pr_warn("CC at %pa has ncblks=3D%u > 32 (resctrl CBM is u32), skipping\n= ", + &ctrl->addr, ctrl->cc.ncblks); + return -ENODEV; + } + + /* + * On a CUNITS-capable controller, CONFIG_LIMIT also consumes cc_cunits, + * whose reset value is unspecified. The driver clears it to 0 (no limit) + * on every CONFIG_LIMIT, so cc_cunits at 0x28 must be within the mapping. + */ + ctrl->cc.cunits =3D FIELD_GET(CBQRI_CC_CAPABILITIES_CUNITS_MASK, reg); + if (ctrl->cc.cunits && ctrl->size < CBQRI_CC_CUNITS_OFF + 8) { + pr_warn("CC at %pa supports CUNITS but maps only %pa, skipping\n", + &ctrl->addr, &ctrl->size); + return -ENODEV; + } + + /* Probe allocation features */ + err =3D cbqri_probe_feature(ctrl, CBQRI_CC_ALLOC_CTL_OFF, + CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT, + &status, &ctrl->cc.supports_alloc_at_code); + if (err) + return err; + + if (status =3D=3D CBQRI_CC_ALLOC_CTL_STATUS_SUCCESS) + ctrl->alloc_capable =3D true; + + return 0; +} + +static int cbqri_probe_controller(struct cbqri_controller *ctrl) +{ + int err; + + pr_debug("controller info: type=3D%d addr=3D%pa size=3D%pa max-rcid=3D%u\= n", + ctrl->type, &ctrl->addr, &ctrl->size, ctrl->rcid_count); + + if (!ctrl->addr) { + pr_warn("controller has invalid addr=3D0x0, skipping\n"); + return -EINVAL; + } + + if (ctrl->size < CBQRI_CTRL_MIN_REG_SPAN) { + pr_warn("controller at %pa: size %pa < minimum 0x%x, skipping\n", + &ctrl->addr, &ctrl->size, CBQRI_CTRL_MIN_REG_SPAN); + return -EINVAL; + } + + if (!request_mem_region(ctrl->addr, ctrl->size, "cbqri_controller")) { + pr_err("request_mem_region failed for %pa\n", &ctrl->addr); + return -EBUSY; + } + + ctrl->base =3D ioremap(ctrl->addr, ctrl->size); + if (!ctrl->base) { + pr_err("ioremap failed for %pa\n", &ctrl->addr); + err =3D -ENOMEM; + goto err_release; + } + + switch (ctrl->type) { + case CBQRI_CONTROLLER_TYPE_CAPACITY: + err =3D cbqri_probe_cc(ctrl); + break; + default: + pr_err("unknown controller type %d\n", ctrl->type); + err =3D -ENODEV; + break; + } + + if (err) + goto err_iounmap; + + return 0; + +err_iounmap: + iounmap(ctrl->base); + ctrl->base =3D NULL; +err_release: + release_mem_region(ctrl->addr, ctrl->size); + return err; +} + +void cbqri_controller_destroy(struct cbqri_controller *ctrl) +{ + /* + * cbqri_probe_controller() clears ctrl->base on its error paths and + * releases the mem region itself, so reach into both only when + * destroy is rolling back a successful probe. + */ + if (ctrl->base) { + iounmap(ctrl->base); + release_mem_region(ctrl->addr, ctrl->size); + } + kfree(ctrl); +} + +/** + * riscv_cbqri_register_cc_dt() - register a DT-described capacity control= ler + * @info: registration descriptor. info->cache_id is used as the + * resctrl domain id. info->type must be CAPACITY. + * @cache_level: cache level (2 or 3) the controller backs, mapped to the + * resctrl L2/L3 resource by the resctrl glue. + * + * The cache level and id (info->cache_id) are supplied directly by the + * caller, which resolves them from cacheinfo. Domain membership is resolv= ed + * per CPU at hotplug, so no cpu mask is stored. There is no firmware table + * to resolve any of this from. + * + * Return: 0 on success, or a negative errno on failure. + */ +int riscv_cbqri_register_cc_dt(const struct cbqri_controller_info *info, + u32 cache_level) +{ + struct cbqri_controller *ctrl; + int err; + + if (!info->addr) { + pr_warn("skipping controller with invalid addr=3D0x0\n"); + return -EINVAL; + } + + if (info->type !=3D CBQRI_CONTROLLER_TYPE_CAPACITY) { + pr_warn("register_cc_dt called with non-capacity type %u\n", + info->type); + return -EINVAL; + } + + ctrl =3D kzalloc(sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + mutex_init(&ctrl->lock); + + ctrl->addr =3D info->addr; + ctrl->size =3D info->size; + ctrl->type =3D info->type; + ctrl->rcid_count =3D info->rcid_count; + + /* + * SRMCFG encodes RCID in 12 bits. Reject an out-of-range count rather + * than silently truncating in every FIELD_PREP(SRMCFG_RCID_MASK, closid) + * on the schedule-in fast path. + */ + if (ctrl->rcid_count > FIELD_MAX(SRMCFG_RCID_MASK) + 1) { + pr_warn("CC at %pa has RCID count %u beyond the 12-bit SRMCFG field, ski= pping\n", + &ctrl->addr, ctrl->rcid_count); + cbqri_controller_destroy(ctrl); + return -EINVAL; + } + + ctrl->cache.cache_id =3D info->cache_id; + ctrl->cache.cache_level =3D cache_level; + + err =3D cbqri_probe_controller(ctrl); + if (err) { + cbqri_controller_destroy(ctrl); + return err; + } + + /* + * Allocation capability comes from the capabilities register probed + * above, not from device tree. rcid_count only bounds the RCID range, + * so a controller the hardware reports as alloc-capable but described + * with no RCID count cannot be driven. Reject that inconsistency. A + * monitoring-only controller (not alloc_capable) needs no RCID count. + */ + if (ctrl->alloc_capable && !ctrl->rcid_count) { + pr_warn("CC at %pa is alloc-capable but has no RCID count, skipping\n", + &ctrl->addr); + cbqri_controller_destroy(ctrl); + return -EINVAL; + } + + guard(mutex)(&cbqri_controllers_lock); + list_add_tail(&ctrl->list, &cbqri_controllers); + return 0; +} diff --git a/drivers/resctrl/cbqri_internal.h b/drivers/resctrl/cbqri_inter= nal.h new file mode 100644 index 000000000000..77429f39f874 --- /dev/null +++ b/drivers/resctrl/cbqri_internal.h @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _DRIVERS_RESCTRL_CBQRI_INTERNAL_H +#define _DRIVERS_RESCTRL_CBQRI_INTERNAL_H + +#include +#include +#include +#include +#include + +/* Capacity Controller (CC) MMIO register offsets. */ +#define CBQRI_CC_CAPABILITIES_OFF 0 +#define CBQRI_CC_ALLOC_CTL_OFF 24 +#define CBQRI_CC_BLOCK_MASK_OFF 32 + +/* + * Per CBQRI 3.5 the block-mask width BMW is the smallest multiple of 64 b= its + * that holds NCBLKS, so cc_cunits sits at 32 + BMW/8 bytes. This constant= is + * valid only while NCBLKS <=3D 64. cbqri_probe_cc() rejects ncblks > 32 b= efore + * it reads cc_capabilities.CUNITS, forcing BMW to 64 bits, one 8-byte blo= ck + * mask, and cc_cunits at 0x28. Raising that cap above 64 would require + * computing 32 + roundup(ncblks, 64) / 8 instead of a constant. + */ +#define CBQRI_CC_CUNITS_OFF 40 + +/* + * Highest base register offset (cc_block_mask at 0x20) plus its 8-byte wi= dth. + * cbqri_probe_controller() rejects smaller mappings. cc_cunits at 0x28 is + * optional and only required when cc_capabilities.CUNITS is set, which + * cbqri_probe_cc() checks against the mapping size separately. + */ +#define CBQRI_CTRL_MIN_REG_SPAN 0x28u + +#define CBQRI_CC_CAPABILITIES_VER_MINOR_MASK GENMASK_ULL(3, 0) +#define CBQRI_CC_CAPABILITIES_VER_MAJOR_MASK GENMASK_ULL(7, 4) +#define CBQRI_CC_CAPABILITIES_NCBLKS_MASK GENMASK_ULL(23, 8) +#define CBQRI_CC_CAPABILITIES_CUNITS_MASK BIT_ULL(25) + +/* + * CC control registers are 64-bit, but the CBQRI spec only guarantees + * single-copy atomicity for naturally aligned 4-byte accesses. They are r= ead + * as two 32-bit halves (cbqri_readq) reconstructed into a u64 for field + * extraction, and written via the low 32-bit half, so the driver does not + * depend on native 64-bit MMIO. Keep every field mask GENMASK_ULL so + * FIELD_MODIFY() or ~mask on the reconstructed u64 never zero-extends a + * 32-bit mask and clobbers STATUS/BUSY/WPRI in bits 63:32. + */ +#define CBQRI_CONTROL_REGISTERS_OP_MASK GENMASK_ULL(4, 0) +#define CBQRI_CONTROL_REGISTERS_AT_MASK GENMASK_ULL(7, 5) +/* AT field values (CBQRI Table 1): data vs code half for CDP */ +#define CBQRI_CONTROL_REGISTERS_AT_DATA 0 +#define CBQRI_CONTROL_REGISTERS_AT_CODE 1 +#define CBQRI_CONTROL_REGISTERS_RCID_MASK GENMASK_ULL(19, 8) +#define CBQRI_CONTROL_REGISTERS_STATUS_MASK GENMASK_ULL(38, 32) +#define CBQRI_CONTROL_REGISTERS_BUSY_MASK GENMASK_ULL(39, 39) + +#define CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT 1 +#define CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT 2 +#define CBQRI_CC_ALLOC_CTL_STATUS_SUCCESS 1 + +/* Capacity Controller hardware capabilities */ +struct riscv_cbqri_capacity_caps { + u16 ncblks; + bool supports_alloc_at_code; + /* cc_capabilities.CUNITS: controller enforces a capacity-unit limit */ + bool cunits; +}; + +/** + * struct cbqri_cc_config - desired capacity allocation state for one rcid + * @cbm: capacity block mask + * @at: AT half the @cbm applies to (CBQRI_CONTROL_REGISTERS_AT_D= ATA + * or CBQRI_CONTROL_REGISTERS_AT_CODE) + * @cdp_enabled: when false and the controller supports AT, mirror @cbm + * into the other AT half so both stay in sync + */ +struct cbqri_cc_config { + u64 cbm; + u32 at; + bool cdp_enabled; +}; + +struct cbqri_controller { + void __iomem *base; + /* + * Serializes the write-then-poll-busy MMIO sequences on this + * controller. Each CBQRI op may busy-wait up to 1 ms on slow + * firmware, so use a sleeping mutex to keep preemption enabled. + * All resctrl-arch entry points run in process context. + */ + struct mutex lock; + + struct riscv_cbqri_capacity_caps cc; + + bool alloc_capable; + + phys_addr_t addr; + phys_addr_t size; + enum cbqri_controller_type type; + u32 rcid_count; + + struct list_head list; + + struct cache_controller { + u32 cache_level; + /* Cache id used as the resctrl domain id */ + u32 cache_id; + } cache; +}; + +extern struct list_head cbqri_controllers; +extern struct mutex cbqri_controllers_lock; + +void cbqri_controller_destroy(struct cbqri_controller *ctrl); + +int cbqri_apply_cache_config(struct cbqri_controller *ctrl, u32 closid, + const struct cbqri_cc_config *cfg); + +int cbqri_read_cache_config(struct cbqri_controller *ctrl, u32 closid, + u32 at, u32 *cbm_out); + +#endif /* _DRIVERS_RESCTRL_CBQRI_INTERNAL_H */ diff --git a/include/linux/riscv_cbqri.h b/include/linux/riscv_cbqri.h new file mode 100644 index 000000000000..e6b3febbed89 --- /dev/null +++ b/include/linux/riscv_cbqri.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Public registration API for the RISC-V Capacity and Bandwidth QoS + * Register Interface (CBQRI) core. Discovery layers (device tree + * platform drivers) call riscv_cbqri_register_cc_dt() to hand a capacity + * controller descriptor to the core, which owns all subsequent state. + */ +#ifndef _LINUX_RISCV_CBQRI_H +#define _LINUX_RISCV_CBQRI_H + +#include + +enum cbqri_controller_type { + CBQRI_CONTROLLER_TYPE_CAPACITY, +}; + +/** + * struct cbqri_controller_info - registration descriptor + * @addr: MMIO base address of the controller's register interface + * @size: size of the MMIO region + * @type: controller type (capacity) + * @rcid_count: number of supported RCIDs + * @cache_id: cache id used as the resctrl domain id + */ +struct cbqri_controller_info { + phys_addr_t addr; + phys_addr_t size; + enum cbqri_controller_type type; + u32 rcid_count; + u32 cache_id; +}; + +#if IS_ENABLED(CONFIG_RISCV_CBQRI) +int riscv_cbqri_register_cc_dt(const struct cbqri_controller_info *info, + u32 cache_level); +#else +static inline int +riscv_cbqri_register_cc_dt(const struct cbqri_controller_info *info, + u32 cache_level) +{ + return -ENODEV; +} +#endif + +#endif /* _LINUX_RISCV_CBQRI_H */ --=20 2.43.0 From nobody Fri Sep 25 01:22:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 450A253D9EF; Thu, 17 Sep 2026 16:40:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789663252; cv=none; b=sZYUyIa7K9YBwO7kFtMOxwD6Af835NpciosoA65LVyM77M0uVbApz2RbxZUYH/GmHM3xj0oC7f8zGLw5OJnr2FZ+nlSmkDV7Vcdx3CBs2SgZrL/2o2usSIRujE6yK17c+RhqkV+C0XucXb6CNwhx090ffjDFaMOVEd/v40Z7KTc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789663252; c=relaxed/simple; bh=2r2zMoo6D0FxoFMPWho9bUp/X4gpPqHNnE61BEgq2hA=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=AFqTtZHLl5TQoC4opLkiqRFPtaA8Ezdcu6ZgL1253kTMYCQ53A5t2DICiY2bLiGdfTfN6YTjbF12HPU561YRAypKHmAMQCFxwwbMAWvsATFy3Y93iXDmNVDGcEVxRAroz75O3f+Y7KFMa7jfvpQeDfR8wMi9qv4AKazwkSkeCpw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Lyyf79oh; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Lyyf79oh" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 56A601F0089F; Thu, 17 Sep 2026 16:40:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789663247; bh=LLsZwqIm0qoL1p5LR999Cn3WEBRuaXQVbskZE/UOkws=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=Lyyf79oh41wglpoIsLIjJoxB82pezzfOon8P/s8ySb6vi1R4Kod/USxMWf6k0Ecxo xJ+afI3EjwIYa50bZHKh/uXwrlWjupoNRsmAJwfjxJe9wBCc67U5oA1MWNcQpmP34e N8KPxJPv4HOLH3x08rgiFrCM57q7ssqxhKwS9v+TwU6UmONz08IjvSe34fK2uNEG/W ntid6jmyLDTBQGBTr7FDj0Q69VOAohE93bN24/7fQqaATpLXFhnYYXvjhpWaaoOhjO Fzky8qAPpHs6hFUUmZu5t/gKhEoue3xb17aLtHg8nU9DCD13jgF4OUJDf1QEROGas3 Y2lY532fVj3nQ== From: Drew Fustini Date: Thu, 17 Sep 2026 09:39:38 -0700 Subject: [PATCH v8 2/5] riscv_cbqri: resctrl: Add cache allocation via capacity block mask Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260917-dfustini-atl-sc-cbqri-dt-v8-2-7964e8d73fe8@kernel.org> References: <20260917-dfustini-atl-sc-cbqri-dt-v8-0-7964e8d73fe8@kernel.org> In-Reply-To: <20260917-dfustini-atl-sc-cbqri-dt-v8-0-7964e8d73fe8@kernel.org> To: Adrien Ricciardi , Alexandre Ghiti , Albert Ou , Atish Kumar Patra , Atish Patra , Babu Moger , Ben Horgan , Borislav Petkov , Chen Pei , Conor Dooley , Conor Dooley , Dave Hansen , Dave Martin , Fenghua Yu , Gong Shuai , Gong Shuai , guo.wenjia23@zte.com.cn, James Morse , =?utf-8?q?Kornel_Dul=C4=99ba?= , Krzysztof Kozlowski , liu.qingtao2@zte.com.cn, Liu Zhiwei , Palmer Dabbelt , Paul Walmsley , Peter Newman , =?utf-8?q?Radim_Kr=C4=8Dm=C3=A1=C5=99?= , Reinette Chatre , Rob Herring , Samuel Holland , Sebastian Andrzej Siewior , Clark Williams , Steven Rostedt , Tony Luck , Vasudevan Srinivasan , Ved Shanbhogue , Weiwei Li , yunhui cui , Zhanpeng Zhang , Drew Fustini Cc: linux-kernel@vger.kernel.org, linux-riscv@lists.infradead.org, x86@kernel.org, devicetree@vger.kernel.org, linux-rt-devel@lists.linux.dev, linux-doc@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=29445; i=fustini@kernel.org; h=from:subject:message-id; bh=2r2zMoo6D0FxoFMPWho9bUp/X4gpPqHNnE61BEgq2hA=; b=owGbwMvMwCV2+43O4ZsaG3kYT6slMWStkeBl8VrRq2rgUpGxy+vAd7/XPTfYj9glVEy8GtJp/ Dl012bejlIWBjEuBlkxRZZNH/IuLPEK/bpg/ottMHNYmUCGMHBxCsBEtmcz/K9dU3m2uHCD/ObQ oqOtG8Xy5P7XhfN4fF7YqLL9Z+AyjgRGhi1PJ+4MqD3euzsicNKB+1ODRNg9X3W/X94srSBdvmo BKzcA X-Developer-Key: i=fustini@kernel.org; a=openpgp; fpr=1B6F948213EA489734F3997035D5CD577C1E6010 Wire CBQRI capacity controllers into resctrl as RDT_RESOURCE_L2 and RDT_RESOURCE_L3 schemata. resctrl exposes a single capacity mask per resource, so when more than one capacity controller backs a cache level they must report the same number of capacity blocks. The same holds for the other capabilities resctrl exposes once per resource. A level whose controllers disagree is left unexposed. Other levels are unaffected. Control domains are created in the cpuhp online callback and keyed on the onlining CPU's cache id from cacheinfo, so a CPU that is offline at probe time still joins its domain once it comes online. A domain is published on the ctrl_domains RCU list only after it is fully initialized and brought online, and removed from the list before it is taken offline, following the documented safe traversal rules. Assisted-by: LLM Tested-by: Chen Pei Co-developed-by: Adrien Ricciardi Signed-off-by: Adrien Ricciardi Signed-off-by: Drew Fustini --- MAINTAINERS | 2 + arch/riscv/include/asm/resctrl.h | 147 ++++++++ drivers/resctrl/Kconfig | 4 + drivers/resctrl/Makefile | 1 + drivers/resctrl/cbqri_resctrl.c | 785 +++++++++++++++++++++++++++++++++++= ++++ 5 files changed, 939 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 92b111cd25c0..db0799d8cbf8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23570,9 +23570,11 @@ R: yunhui cui L: linux-riscv@lists.infradead.org S: Supported F: arch/riscv/include/asm/qos.h +F: arch/riscv/include/asm/resctrl.h F: arch/riscv/kernel/qos.c F: drivers/resctrl/cbqri_devices.c F: drivers/resctrl/cbqri_internal.h +F: drivers/resctrl/cbqri_resctrl.c F: include/linux/riscv_cbqri.h =20 RISC-V RPMI AND MPXY DRIVERS diff --git a/arch/riscv/include/asm/resctrl.h b/arch/riscv/include/asm/resc= trl.h new file mode 100644 index 000000000000..b08f4e12f7aa --- /dev/null +++ b/arch/riscv/include/asm/resctrl.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _ASM_RISCV_RESCTRL_H +#define _ASM_RISCV_RESCTRL_H + +#include +#include +#include + +#include + +struct rdt_resource; + +/* + * Sentinel "no CLOSID assigned" used by resctrl_arch_rmid_idx_decode(). + * fs/resctrl treats this opaquely. CBQRI uses MCID directly as the linear + * rmid index, so closid is unused on decode. + */ +#define RISCV_RESCTRL_EMPTY_CLOSID ((u32)~0) + +/* + * Terminology mapping between x86 (Intel RDT/AMD QoS) and RISC-V: + * + * CLOSID on x86 is RCID on RISC-V + * RMID on x86 is MCID on RISC-V + * CDP on x86 is AT (access type) on RISC-V + */ + +/** + * resctrl_arch_alloc_capable() - any CBQRI controller exposes resctrl all= oc + * + * Returns true once at least one CBQRI controller has successfully probed= for + * a resctrl-exposed cache capacity allocation feature. Only meaningful af= ter + * cbqri_resctrl_setup() runs at late_initcall. + */ +bool resctrl_arch_alloc_capable(void); + +/** + * resctrl_arch_mon_capable() - any CBQRI controller exposes resctrl monit= oring + * + * The CBQRI driver implements capacity allocation only and wires up no + * monitoring events, so this always returns false. fs/resctrl references = it + * unconditionally, hence the stub. + */ +bool resctrl_arch_mon_capable(void); + +/** + * resctrl_arch_rmid_idx_encode() - encode (RCID, MCID) into a linear index + * @closid: RCID (resource control id) + * @rmid: MCID (monitoring counter id) + * + * RISC-V uses MCID directly as the linear index into per-RMID arrays + * managed by fs/resctrl, since CBQRI controllers admit any MCID for any + * RCID. closid is unused here. CDP is encoded via the AT field on each + * CBQRI op rather than via the index. + */ +u32 resctrl_arch_rmid_idx_encode(u32 closid, u32 rmid); + +/** + * resctrl_arch_rmid_idx_decode() - inverse of resctrl_arch_rmid_idx_encod= e() + * @idx: linear index + * @closid: out: always RISCV_RESCTRL_EMPTY_CLOSID + * @rmid: out: the MCID that @idx encodes + */ +void resctrl_arch_rmid_idx_decode(u32 idx, u32 *closid, u32 *rmid); + +/** + * resctrl_arch_set_cpu_default_closid_rmid() - install per-CPU srmcfg def= ault + * @cpu: CPU number + * @closid: RCID to use when no task is matched + * @rmid: MCID to use when no task is matched + * + * Sets the per-CPU cpu_srmcfg_default so __switch_to_srmcfg() can fall ba= ck + * to the CPU's default RCID/MCID for default-group tasks (those whose + * thread.srmcfg encodes to 0, i.e. closid =3D=3D RESCTRL_RESERVED_CLOSID = and + * rmid =3D=3D RESCTRL_RESERVED_RMID). Implements resctrl allocation rule 2 + * ("CPU default") on RISC-V. + */ +void resctrl_arch_set_cpu_default_closid_rmid(int cpu, u32 closid, u32 rmi= d); + +/** + * resctrl_arch_sched_in() - context-switch hook to install task RCID/MCID + * @tsk: the task being scheduled in + * + * Called from finish_task_switch() to write tsk->thread.srmcfg into the + * srmcfg CSR. Tasks tagged with RISCV_RESCTRL_EMPTY_CLOSID inherit the + * per-CPU default set via resctrl_arch_set_cpu_default_closid_rmid(). + */ +void resctrl_arch_sched_in(struct task_struct *tsk); + +/** + * resctrl_arch_set_closid_rmid() - tag a task with an RCID/MCID + * @tsk: task to tag + * @closid: RCID to install + * @rmid: MCID to install + * + * Updates tsk->thread.srmcfg with the encoded (RCID, MCID) pair. The new + * value takes effect on the next resctrl_arch_sched_in() for this task. + */ +void resctrl_arch_set_closid_rmid(struct task_struct *tsk, u32 closid, u32= rmid); + +/** + * resctrl_arch_match_closid() - test whether a task carries a given RCID + * @tsk: task + * @closid: RCID + */ +bool resctrl_arch_match_closid(struct task_struct *tsk, u32 closid); + +/** + * resctrl_arch_match_rmid() - test whether a task carries a given (RCID, = MCID) + * @tsk: task + * @closid: RCID + * @rmid: MCID + */ +bool resctrl_arch_match_rmid(struct task_struct *tsk, u32 closid, u32 rmid= ); + +/** + * resctrl_arch_mon_ctx_alloc() - allocate per-monitor-event arch context + * @r: resctrl resource being monitored + * @evtid: which monitor event needs context + * + * The CBQRI driver implements no monitoring events, so there is no per-ev= ent + * context to allocate and the stub returns NULL. fs/resctrl references it + * unconditionally before checking resctrl_arch_mon_capable(). + */ +void *resctrl_arch_mon_ctx_alloc(struct rdt_resource *r, enum resctrl_even= t_id evtid); + +/** + * resctrl_arch_mon_ctx_free() - release context returned by mon_ctx_alloc= () + * @r: resctrl resource + * @evtid: monitor event id + * @arch_mon_ctx: pointer returned by resctrl_arch_mon_ctx_alloc() + */ +void resctrl_arch_mon_ctx_free(struct rdt_resource *r, enum resctrl_event_= id evtid, + void *arch_mon_ctx); + +static inline unsigned int resctrl_arch_round_mon_val(unsigned int val) +{ + return val; +} + +/* Not needed for RISC-V */ +static inline void resctrl_arch_enable_mon(void) { } +static inline void resctrl_arch_disable_mon(void) { } +static inline void resctrl_arch_enable_alloc(void) { } +static inline void resctrl_arch_disable_alloc(void) { } + +#endif /* _ASM_RISCV_RESCTRL_H */ diff --git a/drivers/resctrl/Kconfig b/drivers/resctrl/Kconfig index 1219fb29aa2e..cac23d20124b 100644 --- a/drivers/resctrl/Kconfig +++ b/drivers/resctrl/Kconfig @@ -38,3 +38,7 @@ menuconfig RISCV_CBQRI RISC-V cache QoS resources. CBQRI exposes cache capacity allocation through the resctrl filesystem at /sys/fs/resctrl when RESCTRL_FS is also enabled. + +config RISCV_CBQRI_RESCTRL_FS + bool + default y if RISCV_CBQRI && RESCTRL_FS diff --git a/drivers/resctrl/Makefile b/drivers/resctrl/Makefile index 4d8a2c4b5627..a7631712dba9 100644 --- a/drivers/resctrl/Makefile +++ b/drivers/resctrl/Makefile @@ -6,3 +6,4 @@ ccflags-$(CONFIG_ARM64_MPAM_DRIVER_DEBUG) +=3D -DDEBUG =20 obj-$(CONFIG_RISCV_CBQRI) +=3D cbqri.o cbqri-y +=3D cbqri_devices.o +cbqri-$(CONFIG_RISCV_CBQRI_RESCTRL_FS) +=3D cbqri_resctrl.o diff --git a/drivers/resctrl/cbqri_resctrl.c b/drivers/resctrl/cbqri_resctr= l.c new file mode 100644 index 000000000000..0c4bfa2a7f43 --- /dev/null +++ b/drivers/resctrl/cbqri_resctrl.c @@ -0,0 +1,785 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "cbqri_internal.h" + +struct cbqri_resctrl_res { + struct cbqri_controller *ctrl; + struct rdt_resource resctrl_res; + bool cdp_enabled; +}; + +struct cbqri_resctrl_dom { + struct rdt_ctrl_domain resctrl_ctrl_dom; + struct cbqri_controller *hw_ctrl; +}; + +static struct cbqri_resctrl_res cbqri_resctrl_resources[RDT_NUM_RESOURCES]; + +static bool exposed_alloc_capable; + +/* Protects ctrl_domain list mutations across CPU hotplug. */ +static DEFINE_MUTEX(cbqri_domain_list_lock); + +static struct rdt_ctrl_domain * +cbqri_find_ctrl_domain(struct list_head *h, int id) +{ + struct rdt_domain_hdr *hdr =3D resctrl_find_domain(h, id, NULL); + + return hdr ? container_of(hdr, struct rdt_ctrl_domain, hdr) : NULL; +} + +/* Map a hardware cache level to its resctrl resource id, or -ENODEV. */ +static int cbqri_cache_level_to_rid(u32 cache_level) +{ + switch (cache_level) { + case 2: + return RDT_RESOURCE_L2; + case 3: + return RDT_RESOURCE_L3; + default: + return -ENODEV; + } +} + +static int cbqri_apply_cache_config_dom(struct cbqri_resctrl_dom *hw_dom, + struct rdt_resource *r, + u32 closid, enum resctrl_conf_type t, + u64 cbm) +{ + struct cbqri_resctrl_res *hw_res =3D + container_of(r, struct cbqri_resctrl_res, resctrl_res); + struct cbqri_cc_config cfg =3D { + .cbm =3D cbm, + .at =3D (t =3D=3D CDP_CODE) ? CBQRI_CONTROL_REGISTERS_AT_CODE : + CBQRI_CONTROL_REGISTERS_AT_DATA, + .cdp_enabled =3D hw_res->cdp_enabled, + }; + + return cbqri_apply_cache_config(hw_dom->hw_ctrl, closid, &cfg); +} + +bool resctrl_arch_alloc_capable(void) +{ + return exposed_alloc_capable; +} + +bool resctrl_arch_mon_capable(void) +{ + return false; +} + +bool resctrl_arch_get_cdp_enabled(enum resctrl_res_level rid) +{ + if (rid !=3D RDT_RESOURCE_L2 && rid !=3D RDT_RESOURCE_L3) + return false; + return cbqri_resctrl_resources[rid].cdp_enabled; +} + +int resctrl_arch_set_cdp_enabled(enum resctrl_res_level rid, bool enable) +{ + struct cbqri_resctrl_res *cbqri_res; + + if (rid !=3D RDT_RESOURCE_L2 && rid !=3D RDT_RESOURCE_L3) + return -ENODEV; + + cbqri_res =3D &cbqri_resctrl_resources[rid]; + if (!cbqri_res->resctrl_res.cdp_capable) + return -ENODEV; + + cbqri_res->cdp_enabled =3D enable; + return 0; +} + +struct rdt_resource *resctrl_arch_get_resource(enum resctrl_res_level l) +{ + if (l >=3D RDT_NUM_RESOURCES) + return NULL; + + return &cbqri_resctrl_resources[l].resctrl_res; +} + +bool resctrl_arch_is_evt_configurable(enum resctrl_event_id evt) +{ + return false; +} + +void *resctrl_arch_mon_ctx_alloc(struct rdt_resource *r, + enum resctrl_event_id evtid) +{ + return NULL; +} + +void resctrl_arch_mon_ctx_free(struct rdt_resource *r, + enum resctrl_event_id evtid, void *arch_mon_ctx) +{ +} + +void resctrl_arch_config_cntr(struct rdt_resource *r, struct rdt_l3_mon_do= main *d, + enum resctrl_event_id evtid, u32 rmid, u32 closid, + u32 cntr_id, bool assign) +{ +} + +int resctrl_arch_cntr_read(struct rdt_resource *r, struct rdt_l3_mon_domai= n *d, + u32 unused, u32 rmid, int cntr_id, + enum resctrl_event_id eventid, u64 *val) +{ + return -EOPNOTSUPP; +} + +bool resctrl_arch_mbm_cntr_assign_enabled(struct rdt_resource *r) +{ + return false; +} + +int resctrl_arch_mbm_cntr_assign_set(struct rdt_resource *r, bool enable) +{ + return -EOPNOTSUPP; +} + +void resctrl_arch_reset_cntr(struct rdt_resource *r, struct rdt_l3_mon_dom= ain *d, + u32 unused, u32 rmid, int cntr_id, + enum resctrl_event_id eventid) +{ +} + +bool resctrl_arch_get_io_alloc_enabled(struct rdt_resource *r) +{ + return false; +} + +int resctrl_arch_io_alloc_enable(struct rdt_resource *r, bool enable) +{ + return -EOPNOTSUPP; +} + +void resctrl_arch_mon_event_config_read(void *info) +{ +} + +void resctrl_arch_mon_event_config_write(void *info) +{ +} + +void resctrl_arch_reset_rmid_all(struct rdt_resource *r, struct rdt_l3_mon= _domain *d) +{ +} + +void resctrl_arch_reset_rmid(struct rdt_resource *r, struct rdt_l3_mon_dom= ain *d, + u32 unused, u32 rmid, enum resctrl_event_id eventid) +{ +} + +int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_domain_hdr *= hdr, + u32 closid, u32 rmid, enum resctrl_event_id eventid, + void *arch_priv, u64 *val, void *arch_mon_ctx) +{ + return -ENODATA; +} + +/* + * Note about terminology between x86 (Intel RDT/AMD QoS) and RISC-V: + * CLOSID on x86 is RCID on RISC-V + * RMID on x86 is MCID on RISC-V + */ +u32 resctrl_arch_get_num_closid(struct rdt_resource *res) +{ + struct cbqri_resctrl_res *hw_res; + + hw_res =3D container_of(res, struct cbqri_resctrl_res, resctrl_res); + + if (!hw_res->ctrl) + return 0; + + return hw_res->ctrl->rcid_count; +} + +u32 resctrl_arch_system_num_rmid_idx(void) +{ + return 1; +} + +u32 resctrl_arch_rmid_idx_encode(u32 closid, u32 rmid) +{ + return rmid; +} + +void resctrl_arch_rmid_idx_decode(u32 idx, u32 *closid, u32 *rmid) +{ + *closid =3D RISCV_RESCTRL_EMPTY_CLOSID; + *rmid =3D idx; +} + +void resctrl_arch_set_cpu_default_closid_rmid(int cpu, u32 closid, u32 rmi= d) +{ + u32 srmcfg =3D FIELD_PREP(SRMCFG_RCID_MASK, closid) | + FIELD_PREP(SRMCFG_MCID_MASK, rmid); + + WRITE_ONCE(per_cpu(cpu_srmcfg_default, cpu), srmcfg); +} + +void resctrl_arch_sched_in(struct task_struct *tsk) +{ + __switch_to_srmcfg(tsk); +} + +void resctrl_arch_set_closid_rmid(struct task_struct *tsk, u32 closid, u32= rmid) +{ + u32 srmcfg =3D FIELD_PREP(SRMCFG_RCID_MASK, closid) | + FIELD_PREP(SRMCFG_MCID_MASK, rmid); + + WRITE_ONCE(tsk->thread.srmcfg, srmcfg); +} + +void resctrl_arch_sync_cpu_closid_rmid(void *info) +{ + struct resctrl_cpu_defaults *r =3D info; + + lockdep_assert_preemption_disabled(); + + if (r) { + resctrl_arch_set_cpu_default_closid_rmid(smp_processor_id(), + r->closid, r->rmid); + } + + resctrl_arch_sched_in(current); +} + +bool resctrl_arch_match_closid(struct task_struct *tsk, u32 closid) +{ + return FIELD_GET(SRMCFG_RCID_MASK, READ_ONCE(tsk->thread.srmcfg)) =3D=3D = closid; +} + +bool resctrl_arch_match_rmid(struct task_struct *tsk, u32 closid, u32 rmid) +{ + return FIELD_GET(SRMCFG_MCID_MASK, READ_ONCE(tsk->thread.srmcfg)) =3D=3D = rmid; +} + +void resctrl_arch_pre_mount(void) +{ + /* All controllers discovered at boot via late_initcall. Nothing to do. */ +} + +int resctrl_arch_update_one(struct rdt_resource *r, struct rdt_ctrl_domain= *d, + u32 closid, enum resctrl_conf_type t, u32 cfg_val) +{ + struct cbqri_resctrl_dom *dom; + + dom =3D container_of(d, struct cbqri_resctrl_dom, resctrl_ctrl_dom); + + if (!r->alloc_capable) + return -EINVAL; + + switch (r->rid) { + case RDT_RESOURCE_L2: + case RDT_RESOURCE_L3: + return cbqri_apply_cache_config_dom(dom, r, closid, t, cfg_val); + default: + return -EINVAL; + } +} + +int resctrl_arch_update_domains(struct rdt_resource *r, u32 closid) +{ + struct resctrl_staged_config *cfg; + enum resctrl_conf_type t; + struct rdt_ctrl_domain *d; + int err =3D 0; + + /* Walking r->ctrl_domains, ensure it can't race with cpuhp */ + lockdep_assert_cpus_held(); + + list_for_each_entry(d, &r->ctrl_domains, hdr.list) { + for (t =3D 0; t < CDP_NUM_TYPES; t++) { + cfg =3D &d->staged_config[t]; + if (!cfg->have_new_ctrl) + continue; + err =3D resctrl_arch_update_one(r, d, closid, t, cfg->new_ctrl); + if (err) + return err; + } + } + return err; +} + +u32 resctrl_arch_get_config(struct rdt_resource *r, struct rdt_ctrl_domain= *d, + u32 closid, enum resctrl_conf_type type) +{ + struct cbqri_resctrl_dom *hw_dom; + struct cbqri_controller *ctrl; + u32 at; + u32 val; + int err; + + hw_dom =3D container_of(d, struct cbqri_resctrl_dom, resctrl_ctrl_dom); + ctrl =3D hw_dom->hw_ctrl; + val =3D resctrl_get_default_ctrl(r); + + if (!r->alloc_capable) + return val; + + switch (r->rid) { + case RDT_RESOURCE_L2: + case RDT_RESOURCE_L3: + at =3D (type =3D=3D CDP_CODE) ? CBQRI_CONTROL_REGISTERS_AT_CODE : + CBQRI_CONTROL_REGISTERS_AT_DATA; + err =3D cbqri_read_cache_config(ctrl, closid, at, &val); + if (err < 0) + val =3D resctrl_get_default_ctrl(r); + break; + default: + break; + } + + return val; +} + +void resctrl_arch_reset_all_ctrls(struct rdt_resource *r) +{ + struct cbqri_resctrl_res *hw_res; + struct rdt_ctrl_domain *d; + enum resctrl_conf_type t; + u32 default_ctrl; + int i; + + lockdep_assert_cpus_held(); + + hw_res =3D container_of(r, struct cbqri_resctrl_res, resctrl_res); + default_ctrl =3D resctrl_get_default_ctrl(r); + + if (!hw_res->ctrl) + return; + + list_for_each_entry(d, &r->ctrl_domains, hdr.list) { + for (i =3D 0; i < hw_res->ctrl->rcid_count; i++) { + for (t =3D 0; t < CDP_NUM_TYPES; t++) { + int rerr; + + rerr =3D resctrl_arch_update_one(r, d, i, t, default_ctrl); + if (rerr) + pr_err_ratelimited("rid=3D%d reset RCID %u type %u failed (%d)\n", + r->rid, i, t, rerr); + } + } + } +} + +static struct rdt_ctrl_domain *cbqri_new_domain(struct cbqri_controller *c= trl) +{ + struct cbqri_resctrl_dom *hw_dom; + struct rdt_ctrl_domain *domain; + + hw_dom =3D kzalloc_obj(*hw_dom, GFP_KERNEL); + if (!hw_dom) + return NULL; + + hw_dom->hw_ctrl =3D ctrl; + domain =3D &hw_dom->resctrl_ctrl_dom; + + INIT_LIST_HEAD(&domain->hdr.list); + + return domain; +} + +static int cbqri_init_domain_ctrlval(struct rdt_resource *r, struct rdt_ct= rl_domain *d) +{ + struct cbqri_resctrl_dom *hw_dom; + enum resctrl_conf_type t; + int err =3D 0; + int i; + + hw_dom =3D container_of(d, struct cbqri_resctrl_dom, resctrl_ctrl_dom); + + for (i =3D 0; i < hw_dom->hw_ctrl->rcid_count; i++) { + /* + * Seed both DATA and CODE staged slots so a later mount + * with -o cdp does not see stale CODE values. + * On non-AT controllers cbqri_cc_alloc_op() masks AT to 0 + * so all three iterations land on the same hardware state. + * The redundant writes are harmless. + */ + for (t =3D 0; t < CDP_NUM_TYPES; t++) { + err =3D resctrl_arch_update_one(r, d, i, t, + resctrl_get_default_ctrl(r)); + if (err) + return err; + } + } + return 0; +} + +/* + * Two capacity controllers at the same cache level are interchangeable to + * resctrl only if they expose identical caps, since resctrl publishes one + * set of caps per rid but any of the level's controllers may service a + * given RCID. + */ +static bool cbqri_cc_caps_agree(const struct cbqri_controller *a, + const struct cbqri_controller *b) +{ + return a->rcid_count =3D=3D b->rcid_count && + a->cc.ncblks =3D=3D b->cc.ncblks && + a->cc.supports_alloc_at_code =3D=3D b->cc.supports_alloc_at_code && + a->alloc_capable =3D=3D b->alloc_capable; +} + +/* + * Walk cbqri_controllers and pick one capacity controller (CC) per cache + * level (L2/L3) to back the corresponding RDT_RESOURCE_L*. When more than + * one CC sits at the same level (e.g. one per socket), they must agree on + * rcid_count / ncblks / supports_alloc_at_code / alloc_capable. A level + * whose controllers disagree is dropped, since resctrl exposes a single + * set of caps per rid, but the other level is still picked. The first + * matching controller wins. + */ +static void cbqri_resctrl_pick_caches(void) +{ + struct cbqri_controller *ctrl, *pick; + int rid; + + guard(mutex)(&cbqri_controllers_lock); + + for (rid =3D 0; rid < RDT_NUM_RESOURCES; rid++) { + pick =3D NULL; + + list_for_each_entry(ctrl, &cbqri_controllers, list) { + if (ctrl->type !=3D CBQRI_CONTROLLER_TYPE_CAPACITY || + !ctrl->alloc_capable || + cbqri_cache_level_to_rid(ctrl->cache.cache_level) !=3D rid) + continue; + + if (!pick) { + pick =3D ctrl; + } else if (!cbqri_cc_caps_agree(pick, ctrl)) { + pr_err("L%u controllers have mismatched capabilities, skipping this le= vel\n", + ctrl->cache.cache_level); + pick =3D NULL; + break; + } + } + + cbqri_resctrl_resources[rid].ctrl =3D pick; + } +} + +/* + * Fill the rdt_resource fields for one picked rid. An rid with no picked + * controller is left untouched so it stays out of resctrl_arch_get_resour= ce(). + */ +static void cbqri_resctrl_control_init(struct cbqri_resctrl_res *cbqri_res) +{ + struct cbqri_controller *ctrl =3D cbqri_res->ctrl; + struct rdt_resource *res =3D &cbqri_res->resctrl_res; + + if (!ctrl) + return; + + switch (res->rid) { + case RDT_RESOURCE_L2: + case RDT_RESOURCE_L3: + res->name =3D (res->rid =3D=3D RDT_RESOURCE_L2) ? "L2" : "L3"; + res->schema_fmt =3D RESCTRL_SCHEMA_BITMAP; + res->ctrl_scope =3D (res->rid =3D=3D RDT_RESOURCE_L2) ? + RESCTRL_L2_CACHE : RESCTRL_L3_CACHE; + res->cache.cbm_len =3D ctrl->cc.ncblks; + res->cache.shareable_bits =3D 0; + res->cache.min_cbm_bits =3D 1; + res->cache.arch_has_sparse_bitmasks =3D false; + res->cdp_capable =3D ctrl->cc.supports_alloc_at_code; + res->alloc_capable =3D ctrl->alloc_capable; + INIT_LIST_HEAD(&res->ctrl_domains); + INIT_LIST_HEAD(&res->mon_domains); + break; + default: + break; + } +} + +static void cbqri_resctrl_accumulate_caps(void) +{ + int rid; + + for (rid =3D 0; rid < RDT_NUM_RESOURCES; rid++) { + struct cbqri_resctrl_res *hw_res =3D &cbqri_resctrl_resources[rid]; + + if (!hw_res->ctrl) + continue; + if (hw_res->ctrl->alloc_capable) + exposed_alloc_capable =3D true; + } +} + +/* + * Create, list-insert, and online a fresh ctrl_domain backing ctrl on + * resource res, seeded with cpu and identified by dom_id. Caller must + * hold cbqri_domain_list_lock and must have already verified that no + * existing ctrl_domain on res carries this id. + */ +static struct rdt_ctrl_domain *cbqri_create_ctrl_domain(struct cbqri_contr= oller *ctrl, + struct rdt_resource *res, + unsigned int cpu, int dom_id) +{ + struct rdt_ctrl_domain *domain; + struct list_head *pos =3D NULL; + int err; + + domain =3D cbqri_new_domain(ctrl); + if (!domain) + return ERR_PTR(-ENOMEM); + + cpumask_set_cpu(cpu, &domain->hdr.cpu_mask); + domain->hdr.id =3D dom_id; + domain->hdr.type =3D RESCTRL_CTRL_DOMAIN; + domain->hdr.rid =3D res->rid; + + err =3D cbqri_init_domain_ctrlval(res, domain); + if (err) + goto free; + + err =3D resctrl_online_ctrl_domain(res, domain); + if (err) + goto free; + + /* + * Publish only after the domain is fully initialized and online, so a + * reader walking the RCU list never sees a half-built domain. + */ + resctrl_find_domain(&res->ctrl_domains, dom_id, &pos); + list_add_tail_rcu(&domain->hdr.list, pos); + + return domain; +free: + kfree(container_of(domain, struct cbqri_resctrl_dom, resctrl_ctrl_dom)); + return ERR_PTR(err); +} + +/* + * Remove a CPU from the domain at each level whose cache it shares. The + * domain is looked up by the CPU's own cache id and the lookup returns NU= LL + * if it is already gone, so this is idempotent and undoes a partial online + * attach as well as a full offline. Caller holds cbqri_domain_list_lock. + */ +static void cbqri_detach_cpu_from_all_ctrls(unsigned int cpu) +{ + static const u32 levels[] =3D { 2, 3 }; + struct cbqri_resctrl_res *hw_res; + struct rdt_ctrl_domain *d; + struct rdt_resource *res; + struct cacheinfo *ci; + int i, rid; + + lockdep_assert_held(&cbqri_domain_list_lock); + + for (i =3D 0; i < ARRAY_SIZE(levels); i++) { + ci =3D get_cpu_cacheinfo_level(cpu, levels[i]); + if (!ci) + continue; + + rid =3D cbqri_cache_level_to_rid(levels[i]); + hw_res =3D &cbqri_resctrl_resources[rid]; + if (!hw_res->ctrl) + continue; + + res =3D &hw_res->resctrl_res; + d =3D cbqri_find_ctrl_domain(&res->ctrl_domains, ci->id); + if (!d) + continue; + + cpumask_clear_cpu(cpu, &d->hdr.cpu_mask); + if (cpumask_empty(&d->hdr.cpu_mask)) { + list_del_rcu(&d->hdr.list); + synchronize_rcu(); + resctrl_offline_ctrl_domain(res, d); + kfree(container_of(d, struct cbqri_resctrl_dom, + resctrl_ctrl_dom)); + } + } +} + +/* + * Attach a CPU to the capacity controller at each cache level whose cache + * the CPU shares. On failure, detach the CPU from everything attached so + * far: the cpuhp core does not run this state's offline teardown when its + * startup fails, so a partial attach would otherwise leak into the domain + * cpu_masks. Caller holds cbqri_domain_list_lock. + */ +static int cbqri_attach_cpu_to_all_ctrls(unsigned int cpu) +{ + static const u32 levels[] =3D { 2, 3 }; + struct cbqri_controller *ctrl, *c; + struct cbqri_resctrl_res *hw_res; + struct rdt_ctrl_domain *d; + struct cacheinfo *ci; + int i, rid; + + lockdep_assert_held(&cbqri_domain_list_lock); + + /* + * Hold cbqri_controllers_lock across the walk so a controller + * registered after boot cannot corrupt it. The register path takes + * it as a leaf and never cbqri_domain_list_lock, so this nesting + * cannot invert. + */ + guard(mutex)(&cbqri_controllers_lock); + + for (i =3D 0; i < ARRAY_SIZE(levels); i++) { + ci =3D get_cpu_cacheinfo_level(cpu, levels[i]); + if (!ci) + continue; + + rid =3D cbqri_cache_level_to_rid(levels[i]); + hw_res =3D &cbqri_resctrl_resources[rid]; + if (!hw_res->ctrl) + continue; + + /* The controller backing this CPU's cache at this level. */ + ctrl =3D NULL; + list_for_each_entry(c, &cbqri_controllers, list) { + if (c->type =3D=3D CBQRI_CONTROLLER_TYPE_CAPACITY && + c->alloc_capable && + c->cache.cache_level =3D=3D levels[i] && + c->cache.cache_id =3D=3D ci->id) { + ctrl =3D c; + break; + } + } + if (!ctrl) + continue; + + d =3D cbqri_find_ctrl_domain(&hw_res->resctrl_res.ctrl_domains, + ci->id); + if (d) { + cpumask_set_cpu(cpu, &d->hdr.cpu_mask); + continue; + } + + d =3D cbqri_create_ctrl_domain(ctrl, &hw_res->resctrl_res, cpu, + ci->id); + if (IS_ERR(d)) { + cbqri_detach_cpu_from_all_ctrls(cpu); + return PTR_ERR(d); + } + } + + return 0; +} + +static bool cbqri_resctrl_inited; + +static void cbqri_resctrl_teardown(void) +{ + int rid; + + if (!cbqri_resctrl_inited) + return; + + resctrl_exit(); + + for (rid =3D 0; rid < RDT_NUM_RESOURCES; rid++) { + struct cbqri_resctrl_res *hw_res =3D &cbqri_resctrl_resources[rid]; + + hw_res->ctrl =3D NULL; + hw_res->cdp_enabled =3D false; + } + exposed_alloc_capable =3D false; + cbqri_resctrl_inited =3D false; +} + +static int cbqri_resctrl_setup(void) +{ + int rid; + int err; + + for (rid =3D 0; rid < RDT_NUM_RESOURCES; rid++) + cbqri_resctrl_resources[rid].resctrl_res.rid =3D rid; + + cbqri_resctrl_pick_caches(); + + for (rid =3D 0; rid < RDT_NUM_RESOURCES; rid++) + cbqri_resctrl_control_init(&cbqri_resctrl_resources[rid]); + + cbqri_resctrl_accumulate_caps(); + + if (!exposed_alloc_capable) { + pr_debug("no resctrl-capable CBQRI controllers found\n"); + return -ENODEV; + } + + err =3D resctrl_init(); + if (err) + return err; + + cbqri_resctrl_inited =3D true; + return 0; +} + +static int cbqri_resctrl_online_cpu(unsigned int cpu) +{ + int err; + + mutex_lock(&cbqri_domain_list_lock); + err =3D cbqri_attach_cpu_to_all_ctrls(cpu); + mutex_unlock(&cbqri_domain_list_lock); + if (err) + return err; + + /* + * Seed the per-CPU default RCID/MCID to the reserved (0, 0) pair and + * notify the resctrl core so it tracks this CPU in the default group. + */ + resctrl_arch_set_cpu_default_closid_rmid(cpu, 0, 0); + resctrl_online_cpu(cpu); + return 0; +} + +static int cbqri_resctrl_offline_cpu(unsigned int cpu) +{ + resctrl_offline_cpu(cpu); + + mutex_lock(&cbqri_domain_list_lock); + cbqri_detach_cpu_from_all_ctrls(cpu); + mutex_unlock(&cbqri_domain_list_lock); + return 0; +} + +static int __init cbqri_arch_late_init(void) +{ + int err; + + if (!riscv_isa_extension_available(NULL, SSQOSID)) + return -ENODEV; + + err =3D cbqri_resctrl_setup(); + if (err) + return err; + + err =3D cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "cbqri:online", + cbqri_resctrl_online_cpu, + cbqri_resctrl_offline_cpu); + if (err < 0) { + cbqri_resctrl_teardown(); + return err; + } + + return 0; +} +late_initcall(cbqri_arch_late_init); --=20 2.43.0 From nobody Fri Sep 25 01:22:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9957E53D9FB; Thu, 17 Sep 2026 16:40:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789663250; cv=none; b=EuvTCJqDo5f3YeVrnq6i4H716YTBXoMOAa9BtxDiT3qKDgbwypwZeFDk8Tsm0wIQcr3PdOZFHgYQK1GKf+EN8DUWR//3bXOwMldrMKREiu3e3ulwdlBO/zFU2o6weLNx6aBgidOEfhrBCBY+VKBybFLU75AGHq1YWHKIh4fgEQA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789663250; c=relaxed/simple; bh=8So6ccZldy4L/KcTgwkLAjT7RYXgfaYjoD+/1AtBKIE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=ETo3rVJO5mgIkUxMHOrzWqWpz1au0ysF32+sxSFowbXPk+Atwgv+IrtWr2DFzjlXSaFBUqfhyo7/ASBRgOc/zv9yAmyrZJX6UF9SfZFxuRfn3mnQ2Ha9PN6Efxh5byxa7e/FMJpsuUjDrUeMdZJhxEcCLb5PZpLTjlJP2Z6KLY4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=DX8V+twS; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="DX8V+twS" Received: by smtp.kernel.org (Postfix) with ESMTPSA id ED8C11F008A1; Thu, 17 Sep 2026 16:40:47 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789663248; bh=VwFWqOZ4pOlB2wwYmiolP3Sp+mnMbUJd9qfFhnZEqBQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=DX8V+twSQnA1rX/ZItYbL4XEQLBeM6c+85tg0bD/RIaYy0AC+FxqeH+WG6PLYLu9k UdGdWaCWemAiOEQ4BZZBlb3UW/doSpexHlCuZzL98xHsgiL2OhvAG1tQICE9/vPG5N fV0rIjozU3/PICfUFefdnfD1+WOIuBCZ1NwNBdTDFuGtnsD/jqTr8ES14pzDKsN53I qFNjunPrcGEzd6Xi63X4qmmnE0xBlcBT8l1Bo7hKQ3HWeMxV47ZILZ2h0Qwifm7pjA 5GLwGqRqXJL17nVXovaTJwkE20BE0V9dFScVAkNQIPlLOuMNlls1z5Lhjlq+qYySUp go8oJMh8gn3qg== From: Drew Fustini Date: Thu, 17 Sep 2026 09:39:39 -0700 Subject: [PATCH v8 3/5] riscv_cbqri: Make RESCTRL_FS available on RISC-V Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260917-dfustini-atl-sc-cbqri-dt-v8-3-7964e8d73fe8@kernel.org> References: <20260917-dfustini-atl-sc-cbqri-dt-v8-0-7964e8d73fe8@kernel.org> In-Reply-To: <20260917-dfustini-atl-sc-cbqri-dt-v8-0-7964e8d73fe8@kernel.org> To: Adrien Ricciardi , Alexandre Ghiti , Albert Ou , Atish Kumar Patra , Atish Patra , Babu Moger , Ben Horgan , Borislav Petkov , Chen Pei , Conor Dooley , Conor Dooley , Dave Hansen , Dave Martin , Fenghua Yu , Gong Shuai , Gong Shuai , guo.wenjia23@zte.com.cn, James Morse , =?utf-8?q?Kornel_Dul=C4=99ba?= , Krzysztof Kozlowski , liu.qingtao2@zte.com.cn, Liu Zhiwei , Palmer Dabbelt , Paul Walmsley , Peter Newman , =?utf-8?q?Radim_Kr=C4=8Dm=C3=A1=C5=99?= , Reinette Chatre , Rob Herring , Samuel Holland , Sebastian Andrzej Siewior , Clark Williams , Steven Rostedt , Tony Luck , Vasudevan Srinivasan , Ved Shanbhogue , Weiwei Li , yunhui cui , Zhanpeng Zhang , Drew Fustini Cc: linux-kernel@vger.kernel.org, linux-riscv@lists.infradead.org, x86@kernel.org, devicetree@vger.kernel.org, linux-rt-devel@lists.linux.dev, linux-doc@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=1242; i=fustini@kernel.org; h=from:subject:message-id; bh=8So6ccZldy4L/KcTgwkLAjT7RYXgfaYjoD+/1AtBKIE=; b=owGbwMvMwCV2+43O4ZsaG3kYT6slMWStkeB9F8vOHxF5INHG1mnVgd+Tp346qPmnadOETYt4c pYUzrc701HKwiDGxSArpsiy6UPehSVeoV8XzH+xDWYOKxPIEAYuTgGYyIUtDP+Dfr9139vg8cTp ebhu0Ik/35Wmxx4vZK9LUl3lYmv76EEAI8M/JRau7MsSGW5WgtWfjq/ePXFf4KYLjdsF1l5853z xxxQeAA== X-Developer-Key: i=fustini@kernel.org; a=openpgp; fpr=1B6F948213EA489734F3997035D5CD577C1E6010 Enough CBQRI support is now present to enable ARCH_HAS_CPU_RESCTRL. Have RISCV_CBQRI select it so that RESCTRL_FS can be enabled on RISC-V, and default RISCV_CBQRI to y. RISCV_ISA_SSQOSID is default n, so this only affects kernels where the user has already opted into Ssqosid, and on those the CBQRI support is what makes the extension useful. This is a separate patch so that no commit makes RESCTRL_FS selectable before the resctrl glue it depends on exists. Enabling the resctrl filesystem itself stays a user choice under MISC_FILESYSTEMS. Tested-by: Chen Pei Signed-off-by: Drew Fustini --- drivers/resctrl/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/resctrl/Kconfig b/drivers/resctrl/Kconfig index cac23d20124b..617b813b9808 100644 --- a/drivers/resctrl/Kconfig +++ b/drivers/resctrl/Kconfig @@ -33,6 +33,8 @@ config ARM64_MPAM_RESCTRL_FS menuconfig RISCV_CBQRI bool "RISC-V CBQRI support" depends on RISCV && RISCV_ISA_SSQOSID + select ARCH_HAS_CPU_RESCTRL + default y help Capacity and Bandwidth QoS Register Interface (CBQRI) support for RISC-V cache QoS resources. CBQRI exposes cache capacity --=20 2.43.0 From nobody Fri Sep 25 01:22:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4809D4D179A; Thu, 17 Sep 2026 16:40:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789663252; cv=none; b=j5BSbO20DpyZ9OyVARrs6sSVgaxlu/+PtE+toCHC2xkue6GyV4pXBW5YAMtFPLWemXtGA0z4RUTiFFyMiS1zibIOkE7v4n9PVVs6ZpZMyLp27ei77WGuLTsOzmYmLfHafY9YO+Ud/+6zROuTNy2wUteTWfaHP90GOIeWnrgmIlA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789663252; c=relaxed/simple; bh=8gd2DiQMGEItozKo4O59vb2wFFEJ1o4qZdVPzkL/Tnk=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=fEdsn/ymmoKBX4kbomEBlSm9bbDpdufQVnuXL9J5wT1p4uIi9pnOC8ysZhzxz4hf5SDdM/gbZwC3mViruNpY/bzQTTzMZSLI3DTGyA1GkQlNvQ66dL+0IgWLYSpzKwEhPTHpvcOHvz86q3Dma9ddd1Z9sske5/vUK4q2Gjk9QjE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=S48h55Lf; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="S48h55Lf" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 90D9C1F00A00; Thu, 17 Sep 2026 16:40:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789663249; bh=YyijQXay86LO7WuPF3AZ9mmm2+iWcDQRp4YaV1G9MDQ=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=S48h55LfkH2XX5pNuCu6yu15mtAepY0fDUh6GFcWaNJBztQM3rhPOTA3iRH6jxWNC 0CJDiWamAdM1TS5wwEAy9h5haqHwQEPnlRM/gDJvPWVX46h4lwFP5gz3i9aNXn4njq RWC5NE29htYtCb9aD6zcG2VUr26NJxCo6opg0s8Wmp1KKRBs1ioU+PhvywRxChBPwO H9xQSQEXmLrVX8aDarVcLWA/86tcEvoQnNTDc0tD75mFVlKZC4BE++sEuZyHZC8vjx Rqx1dkLOwoE3Sl2NUnSQmzcnTtolBMDvVhqBJ7NS063URIj5UaeHGS2knaR6S4VMiS WjF7jWfxYigYg== From: Drew Fustini Date: Thu, 17 Sep 2026 09:39:40 -0700 Subject: [PATCH v8 4/5] dt-bindings: riscv: Add binding for CBQRI controllers Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260917-dfustini-atl-sc-cbqri-dt-v8-4-7964e8d73fe8@kernel.org> References: <20260917-dfustini-atl-sc-cbqri-dt-v8-0-7964e8d73fe8@kernel.org> In-Reply-To: <20260917-dfustini-atl-sc-cbqri-dt-v8-0-7964e8d73fe8@kernel.org> To: Adrien Ricciardi , Alexandre Ghiti , Albert Ou , Atish Kumar Patra , Atish Patra , Babu Moger , Ben Horgan , Borislav Petkov , Chen Pei , Conor Dooley , Conor Dooley , Dave Hansen , Dave Martin , Fenghua Yu , Gong Shuai , Gong Shuai , guo.wenjia23@zte.com.cn, James Morse , =?utf-8?q?Kornel_Dul=C4=99ba?= , Krzysztof Kozlowski , liu.qingtao2@zte.com.cn, Liu Zhiwei , Palmer Dabbelt , Paul Walmsley , Peter Newman , =?utf-8?q?Radim_Kr=C4=8Dm=C3=A1=C5=99?= , Reinette Chatre , Rob Herring , Samuel Holland , Sebastian Andrzej Siewior , Clark Williams , Steven Rostedt , Tony Luck , Vasudevan Srinivasan , Ved Shanbhogue , Weiwei Li , yunhui cui , Zhanpeng Zhang , Drew Fustini Cc: linux-kernel@vger.kernel.org, linux-riscv@lists.infradead.org, x86@kernel.org, devicetree@vger.kernel.org, linux-rt-devel@lists.linux.dev, linux-doc@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=4705; i=fustini@kernel.org; h=from:subject:message-id; bh=8gd2DiQMGEItozKo4O59vb2wFFEJ1o4qZdVPzkL/Tnk=; b=owGbwMvMwCV2+43O4ZsaG3kYT6slMWStkeBdzrlh6f65z6LXPa2/qpTM53K/S1P7u8za91JC5 x33yHyW6yhlYRDjYpAVU2TZ9CHvwhKv0K8L5r/YBjOHlQlkCAMXpwBMZFsswz+9jC+vc8UZbHrm r1qvr8tVuMkx58yf0gdqyXt/Hf1xTukFI8MlQduL10q1n9z/lb/8f2Qqc4CZd6vD3zMThEzO3T2 00ZkJAA== X-Developer-Key: i=fustini@kernel.org; a=openpgp; fpr=1B6F948213EA489734F3997035D5CD577C1E6010 Document the device tree binding for RISC-V CBQRI capacity and bandwidth controllers. Each controller is named by a device-specific compatible followed by the generic compatible. The binding also describes the common riscv,cbqri-rcid and riscv,cbqri-mcid properties, and the optional riscv,cbqri-cache phandle that links a capacity controller to the cache whose capacity it allocates. Assisted-by: LLM Reviewed-by: Conor Dooley Co-developed-by: Adrien Ricciardi Signed-off-by: Adrien Ricciardi Signed-off-by: Drew Fustini --- .../devicetree/bindings/riscv/riscv,cbqri.yaml | 97 ++++++++++++++++++= ++++ MAINTAINERS | 1 + 2 files changed, 98 insertions(+) diff --git a/Documentation/devicetree/bindings/riscv/riscv,cbqri.yaml b/Doc= umentation/devicetree/bindings/riscv/riscv,cbqri.yaml new file mode 100644 index 000000000000..62d547a0cb96 --- /dev/null +++ b/Documentation/devicetree/bindings/riscv/riscv,cbqri.yaml @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/riscv/riscv,cbqri.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RISC-V Capacity and Bandwidth QoS Register Interface (CBQRI) contro= ller + +description: | + The RISC-V CBQRI specification defines capacity-controller and + bandwidth-controller register blocks that allocate cache capacity and me= mory + bandwidth to resource-control IDs (RCIDs) and monitor usage per + monitoring-counter ID (MCID): + https://github.com/riscv-non-isa/riscv-cbqri/blob/main/riscv-cbqri.pdf + + Allocation and monitoring share one register block, and a controller may + implement either or both. A driver discovers which at runtime from the + capabilities register, so the compatible names only the controller type.= It + does not distinguish allocation-only, monitoring-only or combined + controllers, and no property declares monitoring support. + +maintainers: + - Drew Fustini + +properties: + compatible: + oneOf: + - items: + - enum: + - tenstorrent,ascalon-shared-cache-controller + - const: riscv,cbqri-capacity-controller + - items: + - {} + - const: riscv,cbqri-bandwidth-controller + + reg: + maxItems: 1 + description: + The CBQRI controller register block. + + riscv,cbqri-rcid: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + The maximum number of RCIDs the controller supports. RCIDs are the + resource-control IDs that allocation operations target. + + riscv,cbqri-mcid: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + The maximum number of MCIDs the controller supports. MCIDs are the + monitoring-counter IDs that usage-monitoring operations target. Pres= ent + on controllers that implement monitoring. + + riscv,cbqri-cache: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the cache node whose capacity this controller allocates. + Applies to capacity controllers that back a CPU cache. The cache lev= el + and the harts sharing it are taken from that node's cache topology. + +required: + - compatible + - reg + +allOf: + - if: + properties: + compatible: + contains: + const: tenstorrent,ascalon-shared-cache-controller + then: + required: + - riscv,cbqri-rcid + - riscv,cbqri-cache + +additionalProperties: false + +examples: + - | + l2_cache: l2-cache { + compatible =3D "cache"; + cache-level =3D <2>; + cache-unified; + cache-size =3D <0xc00000>; + cache-sets =3D <512>; + cache-block-size =3D <64>; + }; + + qos-controller@a21a00c0 { + compatible =3D "tenstorrent,ascalon-shared-cache-controller", + "riscv,cbqri-capacity-controller"; + reg =3D <0xa21a00c0 0xf40>; + riscv,cbqri-rcid =3D <16>; + riscv,cbqri-cache =3D <&l2_cache>; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index db0799d8cbf8..c362d53ed355 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23569,6 +23569,7 @@ M: Drew Fustini R: yunhui cui L: linux-riscv@lists.infradead.org S: Supported +F: Documentation/devicetree/bindings/riscv/riscv,cbqri.yaml F: arch/riscv/include/asm/qos.h F: arch/riscv/include/asm/resctrl.h F: arch/riscv/kernel/qos.c --=20 2.43.0 From nobody Fri Sep 25 01:22:32 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D0ADE53D9EC; Thu, 17 Sep 2026 16:40:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789663253; cv=none; b=q/zuVa05iLSkOMfhYpOKkdjNpg4sA6obRFyJogr2XKstNBVo+jLIjL0zLMsoY+cFt/jx5qL7KX3Yquh8XVO6F8MjzwWj6tn5FPkRaokRn3wwQCdOH9AHiZ9G5HHT94P/uiDAzlc4Sej0v8TGxsjeWd0xdOTQWY8UbxfWseZ79dI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789663253; c=relaxed/simple; bh=FP0m+/daRgFsTGYKDwEWaqsS+U/czkReAu6L4vJb+Rc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=thBp2pav2Q7rFkyAkSSjY4zYk3HeG0vPEocmxTQORXtn2NW5SKuzomnZ7twHzyik4qkgqavpBIDQoll6RTxEJN1eEOOWj5IMvGQ8okLXLBUdn1CsTd7EEfRJqHdk9ubhAZIwTUPpyTUdzFSEuyKiDSNAtY2SUe2bcGlL0nqEnhU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hOylyqeR; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="hOylyqeR" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3574F1F008A4; Thu, 17 Sep 2026 16:40:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1789663249; bh=+jkL1uQZ6ukkDkuWJSROlqGE0IqqKjykC1dpYNpP4j8=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=hOylyqeRrcXJBlifZq7zFvroXYs7fovNt5XHTo361ZaOdLgxLyx/WP3CdGKIWaTe1 dTVEQn2jeEFfL2hY3Ogm4IaiqBDbWiSKNqA0OtvLS2GqIRyRSVnHnbULB2qteoe+r+ QYpZMJwSaZEn+f0Wxgyz33HE/fqWsMGVvnpy8jt+G9Vymk/JEacbgfXHhHn6dUNOc8 GG6ruPq7eUSufjvDISGpLA8ncnAa+EQ+dXIkgvEIvYW0vXvXr/Pr2XkSltbYAVqj0a mJjXtz+bBoWUJsPaSVgGdj0R9ns/KoppmUyVxOpfkyGTxxvvPmgr5IZYcrfMMIr62v 4gejm9RCJsAqA== From: Drew Fustini Date: Thu, 17 Sep 2026 09:39:41 -0700 Subject: [PATCH v8 5/5] riscv_cbqri: Add CBQRI capacity allocation platform driver Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260917-dfustini-atl-sc-cbqri-dt-v8-5-7964e8d73fe8@kernel.org> References: <20260917-dfustini-atl-sc-cbqri-dt-v8-0-7964e8d73fe8@kernel.org> In-Reply-To: <20260917-dfustini-atl-sc-cbqri-dt-v8-0-7964e8d73fe8@kernel.org> To: Adrien Ricciardi , Alexandre Ghiti , Albert Ou , Atish Kumar Patra , Atish Patra , Babu Moger , Ben Horgan , Borislav Petkov , Chen Pei , Conor Dooley , Conor Dooley , Dave Hansen , Dave Martin , Fenghua Yu , Gong Shuai , Gong Shuai , guo.wenjia23@zte.com.cn, James Morse , =?utf-8?q?Kornel_Dul=C4=99ba?= , Krzysztof Kozlowski , liu.qingtao2@zte.com.cn, Liu Zhiwei , Palmer Dabbelt , Paul Walmsley , Peter Newman , =?utf-8?q?Radim_Kr=C4=8Dm=C3=A1=C5=99?= , Reinette Chatre , Rob Herring , Samuel Holland , Sebastian Andrzej Siewior , Clark Williams , Steven Rostedt , Tony Luck , Vasudevan Srinivasan , Ved Shanbhogue , Weiwei Li , yunhui cui , Zhanpeng Zhang , Drew Fustini Cc: linux-kernel@vger.kernel.org, linux-riscv@lists.infradead.org, x86@kernel.org, devicetree@vger.kernel.org, linux-rt-devel@lists.linux.dev, linux-doc@vger.kernel.org X-Mailer: b4 0.16-dev X-Developer-Signature: v=1; a=openpgp-sha256; l=6986; i=fustini@kernel.org; h=from:subject:message-id; bh=FP0m+/daRgFsTGYKDwEWaqsS+U/czkReAu6L4vJb+Rc=; b=owGbwMvMwCV2+43O4ZsaG3kYT6slMWStkeC9nCfJKqTy1eGMReGmMxOuOa9hXbtT8a1TpdFM4 U+5ry25O0pZGMS4GGTFFFk2fci7sMQr9OuC+S+2wcxhZQIZwsDFKQAT+RrKyLB5F/9vzUVXO2ym dE5hdsh9uEmu4XzbSaFEH2ndvbwzbm1gZLiZ+8WQdZnH1ZN+vJPYfbZ0T+5bJLn/9gulfZx98cu ZT7ECAA== X-Developer-Key: i=fustini@kernel.org; a=openpgp; fpr=1B6F948213EA489734F3997035D5CD577C1E6010 Add a platform driver for CBQRI capacity controllers described in device tree with the riscv,cbqri-capacity-controller compatible. The controller node points at the cache it governs with a riscv,cbqri-cache phandle. The driver looks that cache up in cacheinfo and uses its cache id as the resctrl domain id, so the binding does not need to carry a CPU mask. The controller is then handed to the CBQRI core, which probes its capabilities and decides whether to expose it through resctrl. Nothing is vendor-specific and reg is the CBQRI register block itself, so any SoC that describes a CBQRI capacity controller in device tree can use the driver unchanged. Assisted-by: LLM Tested-by: Chen Pei Signed-off-by: Drew Fustini --- MAINTAINERS | 1 + drivers/resctrl/Kconfig | 15 +++++ drivers/resctrl/Makefile | 1 + drivers/resctrl/cbqri_capacity.c | 127 +++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 144 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index c362d53ed355..00b92842ef12 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23573,6 +23573,7 @@ F: Documentation/devicetree/bindings/riscv/riscv,cb= qri.yaml F: arch/riscv/include/asm/qos.h F: arch/riscv/include/asm/resctrl.h F: arch/riscv/kernel/qos.c +F: drivers/resctrl/cbqri_capacity.c F: drivers/resctrl/cbqri_devices.c F: drivers/resctrl/cbqri_internal.h F: drivers/resctrl/cbqri_resctrl.c diff --git a/drivers/resctrl/Kconfig b/drivers/resctrl/Kconfig index 617b813b9808..689a91873d7b 100644 --- a/drivers/resctrl/Kconfig +++ b/drivers/resctrl/Kconfig @@ -41,6 +41,21 @@ menuconfig RISCV_CBQRI allocation through the resctrl filesystem at /sys/fs/resctrl when RESCTRL_FS is also enabled. =20 +if RISCV_CBQRI + +config RISCV_CBQRI_CAPACITY + bool "RISC-V CBQRI cache capacity-allocation controller" + depends on OF + help + Enable driver for a RISC-V CBQRI capacity controller that + governs a CPU cache, matching the "riscv,cbqri-capacity-controller" + compatible. The controller's cache phandle gives the cache level and + id, which the driver registers as a resctrl cache-allocation resource. + + Say N unless your device tree describes a CBQRI capacity controller. + +endif + config RISCV_CBQRI_RESCTRL_FS bool default y if RISCV_CBQRI && RESCTRL_FS diff --git a/drivers/resctrl/Makefile b/drivers/resctrl/Makefile index a7631712dba9..c8339113ef1f 100644 --- a/drivers/resctrl/Makefile +++ b/drivers/resctrl/Makefile @@ -7,3 +7,4 @@ ccflags-$(CONFIG_ARM64_MPAM_DRIVER_DEBUG) +=3D -DDEBUG obj-$(CONFIG_RISCV_CBQRI) +=3D cbqri.o cbqri-y +=3D cbqri_devices.o cbqri-$(CONFIG_RISCV_CBQRI_RESCTRL_FS) +=3D cbqri_resctrl.o +cbqri-$(CONFIG_RISCV_CBQRI_CAPACITY) +=3D cbqri_capacity.o diff --git a/drivers/resctrl/cbqri_capacity.c b/drivers/resctrl/cbqri_capac= ity.c new file mode 100644 index 000000000000..0670a676bdb1 --- /dev/null +++ b/drivers/resctrl/cbqri_capacity.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Platform driver for a RISC-V CBQRI capacity controller that backs a CPU + * cache. The controller is described in device tree by the generic + * "riscv,cbqri-capacity-controller" compatible together with a phandle to= the + * cache node it governs. The driver hands it to the CBQRI core, which pro= bes + * the capabilities register and exposes a controller that supports alloca= tion + * as the resctrl cache allocation resource for that cache. + */ + +#define pr_fmt(fmt) "cbqri-capacity: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int cbqri_capacity_probe(struct platform_device *pdev) +{ + struct device *dev =3D &pdev->dev; + struct cbqri_controller_info info =3D {}; + struct device_node *cache_np; + struct resource *res; + u32 rcid_count, cache_level; + int cache_id, cpu, ret; + + res =3D platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + ret =3D of_property_read_u32(dev->of_node, "riscv,cbqri-rcid", &rcid_coun= t); + if (ret) { + dev_err(dev, "missing riscv,cbqri-rcid\n"); + return ret; + } + + cache_np =3D of_parse_phandle(dev->of_node, "riscv,cbqri-cache", 0); + if (!cache_np) { + dev_err(dev, "missing riscv,cbqri-cache phandle\n"); + return -EINVAL; + } + + ret =3D of_property_read_u32(cache_np, "cache-level", &cache_level); + if (ret) { + dev_err(dev, "%pOF: missing cache-level\n", cache_np); + goto out_put; + } + + /* + * Associate the controller with its cache instance via cacheinfo. + * The matching cache provides the cache id used as the resctrl + * domain id. + * + * Taking the first leaf at the level is sufficient, because fw_token + * is keyed on the DT cache node, so sibling leaves of a split cache + * share it, and riscv,cbqri-cache must reference a unified shared + * cache (cache-unified), not a split private L1. + */ + cache_id =3D -1; + cpus_read_lock(); + for_each_online_cpu(cpu) { + struct cacheinfo *ci =3D get_cpu_cacheinfo_level(cpu, cache_level); + + if (ci && ci->fw_token =3D=3D cache_np) { + cache_id =3D ci->id; + break; + } + } + cpus_read_unlock(); + + if (cache_id < 0) { + dev_err(dev, "%pOF: no online hart reports an L%u cache for this node\n", + cache_np, cache_level); + ret =3D -ENODEV; + goto out_put; + } + + info.type =3D CBQRI_CONTROLLER_TYPE_CAPACITY; + info.addr =3D res->start; + info.size =3D resource_size(res); + info.rcid_count =3D rcid_count; + info.cache_id =3D cache_id; + + ret =3D riscv_cbqri_register_cc_dt(&info, cache_level); + if (ret) { + dev_err(dev, "failed to register capacity controller: %d\n", ret); + goto out_put; + } + + dev_info(dev, "registered L%u capacity controller at %pa (cache_id=3D%d, = rcid=3D%u)\n", + cache_level, &info.addr, cache_id, rcid_count); + +out_put: + of_node_put(cache_np); + return ret; +} + +static const struct of_device_id cbqri_capacity_of_match[] =3D { + { .compatible =3D "riscv,cbqri-capacity-controller" }, + {} +}; +MODULE_DEVICE_TABLE(of, cbqri_capacity_of_match); + +static struct platform_driver cbqri_capacity_driver =3D { + .probe =3D cbqri_capacity_probe, + .driver =3D { + .name =3D "cbqri-capacity", + .of_match_table =3D cbqri_capacity_of_match, + /* + * The controller is registered permanently into the + * CBQRI core for the life of the system. Block unbind + * so userspace cannot leave a dangling controller. + */ + .suppress_bind_attrs =3D true, + }, +}; + +/* + * Register at device_initcall so probe runs before the CBQRI core's + * late_initcall which walks the cbqri_controllers list. + */ +builtin_platform_driver(cbqri_capacity_driver); --=20 2.43.0