Linux Documentation
 help / color / mirror / Atom feed
* Re: [External] [PATCH v2 4/8] riscv_cbqri: Add capacity controller probe and allocation device ops
From: yunhui cui @ 2026-06-27  9:19 UTC (permalink / raw)
  To: Drew Fustini
  Cc: Adrien Ricciardi, Alexandre Ghiti, 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, James Morse, Kornel Dulęba,
	Krzysztof Kozlowski, liu.qingtao2, Liu Zhiwei, Palmer Dabbelt,
	Paul Walmsley, Peter Newman, Radim Krčmář,
	Reinette Chatre, Rob Herring, Samuel Holland,
	Sebastian Andrzej Siewior, Tony Luck, Vasudevan Srinivasan,
	Ved Shanbhogue, Weiwei Li, linux-kernel, linux-riscv, x86,
	devicetree, linux-rt-devel, linux-doc
In-Reply-To: <20260624-dfustini-atl-sc-cbqri-dt-v2-4-2f8049fd902b@kernel.org>

Hi Drew,

On Thu, Jun 25, 2026 at 9:41 AM Drew Fustini <fustini@kernel.org> wrote:
>
> 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: Claude:claude-opus-4-7
> Co-developed-by: Adrien Ricciardi <aricciardi@baylibre.com>
> Signed-off-by: Adrien Ricciardi <aricciardi@baylibre.com>
> Signed-off-by: Drew Fustini <fustini@kernel.org>
> ---
>  MAINTAINERS                      |   3 +
>  drivers/resctrl/Kconfig          |  13 +
>  drivers/resctrl/Makefile         |   3 +
>  drivers/resctrl/cbqri_devices.c  | 520 +++++++++++++++++++++++++++++++++++++++
>  drivers/resctrl/cbqri_internal.h | 107 ++++++++
>  include/linux/riscv_cbqri.h      |  47 ++++
>  6 files changed, 693 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 07109e1a8f8470377916c98074ab68fec51dfdc6..811c0c9b1fac806945cad0229c5330654420a835 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -23300,6 +23300,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
>
>  RISC-V RPMI AND MPXY DRIVERS
>  M:     Rahul Pathak <rahul@summations.net>
> diff --git a/drivers/resctrl/Kconfig b/drivers/resctrl/Kconfig
> index 672abea3b03ccbeb4532832dd18e5cb80e90ff5b..92b9c82cf9f397437d28006e79d40c2d9b384eb0 100644
> --- a/drivers/resctrl/Kconfig
> +++ b/drivers/resctrl/Kconfig
> @@ -29,3 +29,16 @@ 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.
> +
> +if RISCV_CBQRI
> +
> +endif
> diff --git a/drivers/resctrl/Makefile b/drivers/resctrl/Makefile
> index 4f6d0e81f9b8f34bd8842fdb13be69da72515b79..4d8a2c4b5627144a651da007174d4d9df6171330 100644
> --- a/drivers/resctrl/Makefile
> +++ b/drivers/resctrl/Makefile
> @@ -3,3 +3,6 @@ mpam-y                                          += mpam_devices.o
>  mpam-$(CONFIG_ARM64_MPAM_RESCTRL_FS)           += mpam_resctrl.o
>
>  ccflags-$(CONFIG_ARM64_MPAM_DRIVER_DEBUG)      += -DDEBUG
> +
> +obj-$(CONFIG_RISCV_CBQRI)                      += cbqri.o
> +cbqri-y                                                += cbqri_devices.o
> diff --git a/drivers/resctrl/cbqri_devices.c b/drivers/resctrl/cbqri_devices.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..8ad9df404f65d5d82722cf8b78f02936c489ca6d
> --- /dev/null
> +++ b/drivers/resctrl/cbqri_devices.c
> @@ -0,0 +1,520 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
> +
> +#include <linux/bitfield.h>
> +#include <linux/riscv_cbqri.h>
> +#include <linux/cpumask.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/ioport.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/printk.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +#include <asm/csr.h>
> +
> +#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 resctrl
> + * glue. Runtime cpuhp walks happen after registration has settled.
> + */
> +DEFINE_MUTEX(cbqri_controllers_lock);
> +
> +/* Set capacity block mask (cc_block_mask) */
> +static void cbqri_set_cbm(struct cbqri_controller *ctrl, u64 cbm)
> +{
> +       iowrite64(cbm, ctrl->base + CBQRI_CC_BLOCK_MASK_OFF);
> +}
> +
> +static int cbqri_wait_busy_flag(struct cbqri_controller *ctrl, int reg_offset,
> +                               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 = readq_poll_timeout(ctrl->base + reg_offset, reg,
> +                                !FIELD_GET(CBQRI_CONTROL_REGISTERS_BUSY_MASK, reg),
> +                                10, 1000);
> +       if (ret)
> +               return ret;
> +       if (regp)
> +               *regp = reg;
> +       return 0;
> +}
> +
> +/*
> + * Perform capacity allocation control operation on capacity controller.
> + * Caller must hold ctrl->lock.
> + */
> +static int cbqri_cc_alloc_op(struct cbqri_controller *ctrl, int operation,
> +                            int rcid, u32 at)
> +{
> +       int reg_offset = CBQRI_CC_ALLOC_CTL_OFF;
> +       int status;
> +       u64 reg;
> +
> +       lockdep_assert_held(&ctrl->lock);
> +
> +       if (cbqri_wait_busy_flag(ctrl, reg_offset, &reg) < 0) {
> +               pr_err_ratelimited("BUSY timeout before starting operation\n");
> +               return -EIO;
> +       }
> +       FIELD_MODIFY(CBQRI_CONTROL_REGISTERS_OP_MASK, &reg, operation);
> +       FIELD_MODIFY(CBQRI_CONTROL_REGISTERS_RCID_MASK, &reg, rcid);
> +
> +       /*
> +        * CBQRI Table 1: AT 0=Data, 1=Code. 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 &= ~CBQRI_CONTROL_REGISTERS_AT_MASK;
> +       if (ctrl->cc.supports_alloc_at_code)
> +               reg |= FIELD_PREP(CBQRI_CONTROL_REGISTERS_AT_MASK, at);
> +
> +       iowrite64(reg, ctrl->base + reg_offset);
> +
> +       if (cbqri_wait_busy_flag(ctrl, reg_offset, &reg) < 0) {
> +               pr_err_ratelimited("BUSY timeout during operation\n");
> +               return -EIO;
> +       }
> +
> +       status = FIELD_GET(CBQRI_CONTROL_REGISTERS_STATUS_MASK, reg);
> +       if (status != CBQRI_CC_ALLOC_CTL_STATUS_SUCCESS) {
> +               pr_err_ratelimited("operation %d failed: status=%d\n", operation, status);
> +               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=Data / 1=Code, 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 = 0;
> +       int err = 0;
> +       u64 reg;
> +
> +       mutex_lock(&ctrl->lock);
> +
> +       need_at_mirror = 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.
> +        * Pre-clear cc_block_mask so a silent firmware no-op (status
> +        * SUCCESS but staging not updated) shows as a zero readback
> +        * rather than carrying stale data from a prior op.
> +        */
> +       if (need_at_mirror) {
> +               cbqri_set_cbm(ctrl, 0);
> +               err = cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT,
> +                                       closid, cfg->at);
> +               if (err < 0)
> +                       goto out;
> +               saved_cbm = ioread64(ctrl->base + CBQRI_CC_BLOCK_MASK_OFF);
> +       }
> +
> +       /* Set capacity block mask (cc_block_mask) */
> +       cbqri_set_cbm(ctrl, cfg->cbm);
> +
> +       /* Capacity config limit operation for the AT half implied by cfg->at */
> +       err = cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT,
> +                               closid, cfg->at);
> +       if (err < 0)
> +               goto out;

When CUNITS=1, CONFIG_LIMIT also consumes cc_cunits. If resctrl does not
expose unit limits, the driver should still write cc_cunits=0 before
CONFIG_LIMIT to avoid a hidden stale/implementation-defined unit limit.

Should we handle cc_cunits here?

> +
> +       /*
> +        * 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 = (cfg->at == CBQRI_CONTROL_REGISTERS_AT_CODE) ?
> +                           CBQRI_CONTROL_REGISTERS_AT_DATA :
> +                           CBQRI_CONTROL_REGISTERS_AT_CODE;
> +
> +               cbqri_set_cbm(ctrl, cfg->cbm);
> +               err = cbqri_cc_alloc_op(ctrl,
> +                                       CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT,
> +                                       closid, other);
> +               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.
> +                        */
> +                       cbqri_set_cbm(ctrl, saved_cbm);
> +                       rerr = cbqri_cc_alloc_op(ctrl,
> +                                                CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT,
> +                                                closid, cfg->at);
> +                       if (rerr < 0)
> +                               pr_err_ratelimited("AT-mirror revert failed (err=%d), AT halves diverged\n",
> +                                                  rerr);
> +                       goto out;
> +               }
> +       }
> +
> +       /* Clear cc_block_mask before read limit to verify op works */
> +       cbqri_set_cbm(ctrl, 0);
> +
> +       /* Perform a capacity read limit operation to verify blockmask */
> +       err = cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT,
> +                               closid, cfg->at);
> +       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 = ioread64(ctrl->base + CBQRI_CC_BLOCK_MASK_OFF);
> +       if (lower_32_bits(reg) != cfg->cbm) {
> +               pr_err_ratelimited("CBM verify mismatch (reg=%llx != cbm=%llx)\n",
> +                                  reg, cfg->cbm);
> +               err = -EIO;
> +       }
> +
> +out:
> +       mutex_unlock(&ctrl->lock);
> +       return err;
> +}
> +
> +/*
> + * Read the configured CBM for closid on the at half via READ_LIMIT.
> + * Pre-clears cc_block_mask before the op so a silent firmware no-op
> + * (status SUCCESS but staging not updated) is detectable in cbm_out.
> + */
> +int cbqri_read_cache_config(struct cbqri_controller *ctrl, u32 closid,
> +                           u32 at, u32 *cbm_out)
> +{
> +       int err;
> +
> +       mutex_lock(&ctrl->lock);
> +       cbqri_set_cbm(ctrl, 0);
> +       err = cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT, closid, at);
> +       if (err == 0) {
> +               /*
> +                * cc_block_mask is a 64-bit MMIO register. resctrl exposes the
> +                * CBM as a u32. cbqri_probe_cc() rejects ncblks > 32 so the
> +                * upper 32 bits are reserved zero by the spec. Narrow
> +                * explicitly via lower_32_bits() so the assumption is visible
> +                * at the read site.
> +                */
> +               *cbm_out = lower_32_bits(ioread64(ctrl->base + CBQRI_CC_BLOCK_MASK_OFF));
> +       }
> +       mutex_unlock(&ctrl->lock);
> +       return err;
> +}
> +
> +static int cbqri_probe_feature(struct cbqri_controller *ctrl, int reg_offset,
> +                              int operation, int *status, bool *access_type_supported)
> +{
> +       const u64 active_mask = 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==0 (feature not
> +        * implemented) path returns a deterministic value to the caller
> +        * rather than leaving an uninitialized bool.
> +        */
> +       *access_type_supported = false;
> +
> +       /* Keep the initial register value to preserve the WPRI fields */
> +       reg = ioread64(ctrl->base + reg_offset);
> +       saved_reg = 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 = (saved_reg & ~active_mask) |
> +             FIELD_PREP(CBQRI_CONTROL_REGISTERS_OP_MASK, operation);
> +       iowrite64(reg, ctrl->base + reg_offset);
> +       if (cbqri_wait_busy_flag(ctrl, reg_offset, &reg) < 0) {
> +               pr_err_ratelimited("BUSY timeout during operation\n");
> +               return -EIO;
> +       }
> +
> +       /* Get the operation status */
> +       *status = 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 != 0) {
> +               /*
> +                * Re-issue operation with AT=CODE so the controller
> +                * latches AT=CODE on supported hardware (or resets it to 0
> +                * on hardware that doesn't). OP must be a defined CBQRI op
> +                * here. OP=0 is a no-op and would silently disable CDP.
> +                */
> +               reg = (saved_reg & ~active_mask) |
> +                     FIELD_PREP(CBQRI_CONTROL_REGISTERS_OP_MASK, operation) |
> +                     FIELD_PREP(CBQRI_CONTROL_REGISTERS_AT_MASK,
> +                                CBQRI_CONTROL_REGISTERS_AT_CODE);
> +               iowrite64(reg, ctrl->base + reg_offset);
> +               if (cbqri_wait_busy_flag(ctrl, reg_offset, &reg) < 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 = FIELD_GET(CBQRI_CONTROL_REGISTERS_AT_MASK, reg);
> +               if (at == CBQRI_CONTROL_REGISTERS_AT_CODE)
> +                       *access_type_supported = true;
> +       }
> +
> +       /*
> +        * Restore the original register value.
> +        * Clear OP to avoid re-triggering the probe op.
> +        */
> +       saved_reg &= ~CBQRI_CONTROL_REGISTERS_OP_MASK;
> +       iowrite64(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 = ioread64(ctrl->base + CBQRI_CC_CAPABILITIES_OFF);
> +       if (reg == 0)
> +               return -ENODEV;
> +
> +       ver_minor = FIELD_GET(CBQRI_CC_CAPABILITIES_VER_MINOR_MASK, reg);
> +       ver_major = FIELD_GET(CBQRI_CC_CAPABILITIES_VER_MAJOR_MASK, reg);
> +       ctrl->cc.ncblks = FIELD_GET(CBQRI_CC_CAPABILITIES_NCBLKS_MASK, reg);
> +
> +       pr_debug("version=%d.%d ncblks=%d cache_level=%d\n",
> +                ver_major, ver_minor,
> +                ctrl->cc.ncblks, ctrl->cache.cache_level);
> +
> +       /*
> +        * NCBLKS == 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;
> +       }
> +
> +       if (ctrl->cc.ncblks > 32) {
> +               pr_warn("CC at %pa has ncblks=%u > 32 (resctrl CBM is u32), skipping\n",
> +                       &ctrl->addr, ctrl->cc.ncblks);
> +               return -ENODEV;
> +       }
> +
> +       /* Probe allocation features */
> +       err = 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 == CBQRI_CC_ALLOC_CTL_STATUS_SUCCESS)
> +               ctrl->alloc_capable = true;
> +
> +       return 0;
> +}
> +
> +static int cbqri_probe_controller(struct cbqri_controller *ctrl)
> +{
> +       int err;
> +
> +       pr_debug("controller info: type=%d addr=%pa size=%pa max-rcid=%u\n",
> +                ctrl->type, &ctrl->addr, &ctrl->size, ctrl->rcid_count);
> +
> +       if (!ctrl->addr) {
> +               pr_warn("controller has invalid addr=0x0, 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 = ioremap(ctrl->addr, ctrl->size);
> +       if (!ctrl->base) {
> +               pr_err("ioremap failed for %pa\n", &ctrl->addr);
> +               err = -ENOMEM;
> +               goto err_release;
> +       }
> +
> +       switch (ctrl->type) {
> +       case CBQRI_CONTROLLER_TYPE_CAPACITY:
> +               err = cbqri_probe_cc(ctrl);
> +               break;
> +       default:
> +               pr_err("unknown controller type %d\n", ctrl->type);
> +               err = -ENODEV;
> +               break;
> +       }
> +
> +       if (err)
> +               goto err_iounmap;
> +
> +       return 0;
> +
> +err_iounmap:
> +       iounmap(ctrl->base);
> +       ctrl->base = 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 controller
> + * @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.
> + * @cpu_mask:    CPUs that share this cache.
> + *
> + * The cache topology is supplied directly by the caller. A device-tree
> + * platform driver that already knows which CPUs share the cache and at what
> + * level passes that in. There is no firmware table to resolve it 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, const struct cpumask *cpu_mask)
> +{
> +       struct cbqri_controller *ctrl;
> +       int err;
> +
> +       if (!info->addr) {
> +               pr_warn("skipping controller with invalid addr=0x0\n");
> +               return -EINVAL;
> +       }
> +
> +       if (info->type != CBQRI_CONTROLLER_TYPE_CAPACITY) {
> +               pr_warn("register_cc_dt called with non-capacity type %u\n",
> +                       info->type);
> +               return -EINVAL;
> +       }
> +
> +       if (!cpu_mask || cpumask_empty(cpu_mask)) {
> +               pr_warn("register_cc_dt called with empty cpu_mask\n");
> +               return -EINVAL;
> +       }
> +
> +       ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
> +       if (!ctrl)
> +               return -ENOMEM;
> +
> +       mutex_init(&ctrl->lock);
> +
> +       ctrl->addr = info->addr;
> +       ctrl->size = info->size;
> +       ctrl->type = info->type;
> +       ctrl->rcid_count = 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, skipping\n",
> +                       &ctrl->addr, ctrl->rcid_count);
> +               cbqri_controller_destroy(ctrl);
> +               return -EINVAL;
> +       }
> +
> +       ctrl->cache.cache_id = info->cache_id;
> +       ctrl->cache.cache_level = cache_level;
> +       cpumask_copy(&ctrl->cache.cpu_mask, cpu_mask);
> +
> +       err = 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;
> +       }
> +
> +       mutex_lock(&cbqri_controllers_lock);
> +       list_add_tail(&ctrl->list, &cbqri_controllers);
> +       mutex_unlock(&cbqri_controllers_lock);
> +       return 0;
> +}
> diff --git a/drivers/resctrl/cbqri_internal.h b/drivers/resctrl/cbqri_internal.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..2192a3831bcde7cd4fddf03f2ce1103902e8815c
> --- /dev/null
> +++ b/drivers/resctrl/cbqri_internal.h
> @@ -0,0 +1,107 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#ifndef _DRIVERS_RESCTRL_CBQRI_INTERNAL_H
> +#define _DRIVERS_RESCTRL_CBQRI_INTERNAL_H
> +
> +#include <linux/bitfield.h>
> +#include <linux/riscv_cbqri.h>
> +#include <linux/cpumask.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/types.h>
> +
> +/* 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
> +
> +/*
> + * Highest defined register offset (0x20) plus the 8-byte register width.
> + * cbqri_probe_controller() rejects smaller mappings so a u64 access at
> + * BLOCK_MASK stays in bounds.
> + */
> +#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)
> +
> +/*
> + * CC control registers are 64-bit. Keep every field mask GENMASK_ULL so
> + * FIELD_MODIFY() or ~mask on a u64 register never zero-extends a 32-bit
> + * mask and clobbers STATUS/BUSY/WPRI in bits 63:32 if RV32 support is
> + * added in the future.
> + */
> +#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;
> +};
> +
> +/**
> + * 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_DATA
> + *               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;
> +               struct cpumask cpu_mask;
> +               /* 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 0000000000000000000000000000000000000000..58737224d2f2c67a443f78f9fea7f368c7d16044
> --- /dev/null
> +++ b/include/linux/riscv_cbqri.h
> @@ -0,0 +1,47 @@
> +/* 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 <linux/types.h>
> +
> +struct cpumask;
> +
> +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, const struct cpumask *cpu_mask);
> +#else
> +static inline int
> +riscv_cbqri_register_cc_dt(const struct cbqri_controller_info *info,
> +                          u32 cache_level, const struct cpumask *cpu_mask)
> +{
> +       return -ENODEV;
> +}
> +#endif
> +
> +#endif /* _LINUX_RISCV_CBQRI_H */
>
> --
> 2.34.1
>

Thanks,
Yunhui

^ permalink raw reply

* [PATCH] Documentation: parport-lowlevel: Fix curly bracket
From: Manuel Ebner @ 2026-06-27  9:24 UTC (permalink / raw)
  To: Sudip Mukherjee, Jonathan Corbet, Shuah Khan, Andreas Faerber,
	Manivannan Sadhasivam, open list:DOCUMENTATION,
	moderated list:ARM/ACTIONS SEMI ARCHITECTURE,
	moderated list:ARM/ACTIONS SEMI ARCHITECTURE, open list,
	Randy Dunlap
  Cc: Manuel Ebner

Remove needless '}'

Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
---
 Documentation/driver-api/parport-lowlevel.rst | 1 -
 1 file changed, 1 deletion(-)

diff --git a/Documentation/driver-api/parport-lowlevel.rst b/Documentation/driver-api/parport-lowlevel.rst
index a907e279f509..af73741289c9 100644
--- a/Documentation/driver-api/parport-lowlevel.rst
+++ b/Documentation/driver-api/parport-lowlevel.rst
@@ -341,7 +341,6 @@ EXAMPLE
 			/* Try to detect a device on the port... */
 			...
 		}
-		}
 
 		...
 	}
-- 
2.54.0


^ permalink raw reply related

* [PATCH] Documentation: RCU: fix brackets
From: Manuel Ebner @ 2026-06-27  9:26 UTC (permalink / raw)
  To: Paul E . McKenney, Frederic Weisbecker, Neeraj Upadhyay,
	Joel Fernandes, Josh Triplett, Boqun Feng, Uladzislau Rezki,
	Steven Rostedt, Mathieu Desnoyers, Lai Jiangshan, Zqiang,
	Jonathan Corbet, Shuah Khan, open list:DOCUMENTATION, open list,
	Randy Dunlap
  Cc: Manuel Ebner

Remove needless brackets and add missing bracket.

Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
---
 .../Design/Expedited-Grace-Periods/Expedited-Grace-Periods.rst  | 2 +-
 Documentation/RCU/Design/Memory-Ordering/TreeRCU-gp.svg         | 2 +-
 Documentation/RCU/Design/Memory-Ordering/TreeRCU-qs.svg         | 2 +-
 Documentation/RCU/Design/Requirements/Requirements.rst          | 2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/Documentation/RCU/Design/Expedited-Grace-Periods/Expedited-Grace-Periods.rst b/Documentation/RCU/Design/Expedited-Grace-Periods/Expedited-Grace-Periods.rst
index 414f8a2012d6..cf0f9cdca7e8 100644
--- a/Documentation/RCU/Design/Expedited-Grace-Periods/Expedited-Grace-Periods.rst
+++ b/Documentation/RCU/Design/Expedited-Grace-Periods/Expedited-Grace-Periods.rst
@@ -410,7 +410,7 @@ workqueues (see Documentation/core-api/workqueue.rst).
 
 The requesting task still does counter snapshotting and funnel-lock
 processing, but the task reaching the top of the funnel lock does a
-``schedule_work()`` (from ``_synchronize_rcu_expedited()`` so that a
+``schedule_work()`` (from ``_synchronize_rcu_expedited()``) so that a
 workqueue kthread does the actual grace-period processing. Because
 workqueue kthreads do not accept POSIX signals, grace-period-wait
 processing need not allow for POSIX signals. In addition, this approach
diff --git a/Documentation/RCU/Design/Memory-Ordering/TreeRCU-gp.svg b/Documentation/RCU/Design/Memory-Ordering/TreeRCU-gp.svg
index d05bc7b27edb..95a66de40ca5 100644
--- a/Documentation/RCU/Design/Memory-Ordering/TreeRCU-gp.svg
+++ b/Documentation/RCU/Design/Memory-Ordering/TreeRCU-gp.svg
@@ -3933,7 +3933,7 @@
          font-style="normal"
          y="-3914.085"
          x="3745.7725"
-         xml:space="preserve">rcu__report_qs_rdp())</text>
+         xml:space="preserve">rcu__report_qs_rdp()</text>
     </g>
     <g
        id="g4504-3"
diff --git a/Documentation/RCU/Design/Memory-Ordering/TreeRCU-qs.svg b/Documentation/RCU/Design/Memory-Ordering/TreeRCU-qs.svg
index 7d6c5f7e505c..882132680308 100644
--- a/Documentation/RCU/Design/Memory-Ordering/TreeRCU-qs.svg
+++ b/Documentation/RCU/Design/Memory-Ordering/TreeRCU-qs.svg
@@ -815,7 +815,7 @@
          font-style="normal"
          y="-3914.085"
          x="3745.7725"
-         xml:space="preserve">rcu__report_qs_rdp())</text>
+         xml:space="preserve">rcu__report_qs_rdp()</text>
     </g>
     <g
        id="g4504-3"
diff --git a/Documentation/RCU/Design/Requirements/Requirements.rst b/Documentation/RCU/Design/Requirements/Requirements.rst
index 8a216e4a46a7..8101fe6229d5 100644
--- a/Documentation/RCU/Design/Requirements/Requirements.rst
+++ b/Documentation/RCU/Design/Requirements/Requirements.rst
@@ -2785,7 +2785,7 @@ both srcu_read_lock() and srcu_read_unlock().  This need is handled by
 a Tasks Trace RCU API implemented as thin wrappers around SRCU-fast,
 which avoids the read-side memory barriers, at least for architectures
 that apply noinstr to kernel entry/exit code (or that build with
-``CONFIG_TASKS_TRACE_RCU_NO_MB=y``.
+``CONFIG_TASKS_TRACE_RCU_NO_MB=y``).
 
 Now that the implementation is based on SRCU-fast, a call
 to synchronize_rcu_tasks_trace() implies at least one call to
-- 
2.54.0


^ permalink raw reply related

* Re: [RFC PATCH 00/40] mm: reliable 1GB page allocation
From: Lorenzo Stoakes @ 2026-06-27  9:28 UTC (permalink / raw)
  To: Rik van Riel
  Cc: linux-kernel, kernel-team, linux-mm, david, willy, surenb, hannes,
	ziy, usama.arif, fvdl, Andrew Morton, Jonathan Corbet,
	Chris Mason, David Sterba, Vlastimil Babka, Steven Rostedt,
	Masami Hiramatsu, Rafael J. Wysocki, Oscar Salvador,
	Mike Rapoport, linux-doc, linux-btrfs, linux-trace-kernel,
	linux-pm, linux-cxl, Linus Torvalds
In-Reply-To: <20260520150018.2491267-1-riel@surriel.com>

+cc missing maintainers.
+cc Linus for general issues raised.

(Apologies, this email is VERY long, even for me)

Hi Rik,

I appreciate this is an RFC, but obviously part of that is early
feedback. So if this is meant to be an (EXTREMELY) rough pre-RFC
proof-of-concept then fine (however your 'todo' suggests otherwise).

And of course, before I get critical :) thank you for looking into this,
it's very interesting work. I want this feature to land. BUT :)

So if this in any way resembles what you plan to send upstream un-RFC'd I
need to pour a fairly large bucket of very cold water over this.

TL;DR: The series is completely unmergeable as it stands. Not even close.

Before we get into the code, please please please make sure you cc- the
right people. You're doing very invasive, very major work here.

You failed to even cc- the page allocator maintainer (Vlastimil) for a
series that radically alters page allocation.

MAKE SURE you cc- Vlastimil and all other maintainers and reviewers + lists
on future (RFC!!) revisions of this please.

If you want to do a quiet off-list pre-RFC that's fine, but it's
unforgivable to leave Vlastimil out of this even for that.

It's 30 seconds running a script (filtering for maintainers alone to save
on noise):

$ scripts/get_maintainer.pl --nogit --nogit-fallback --nor 20260520150018.2491267-1-riel@surriel.com.mbx
Andrew Morton <akpm@linux-foundation.org> (maintainer:MEMORY MANAGEMENT - CORE)
David Hildenbrand <david@kernel.org> (maintainer:MEMORY MANAGEMENT - CORE)
Jonathan Corbet <corbet@lwn.net> (maintainer:DOCUMENTATION)
Chris Mason <clm@fb.com> (maintainer:BTRFS FILE SYSTEM)
David Sterba <dsterba@suse.com> (maintainer:BTRFS FILE SYSTEM)
Vlastimil Babka <vbabka@kernel.org> (maintainer:MEMORY MANAGEMENT - PAGE ALLOCATOR)
Steven Rostedt <rostedt@goodmis.org> (maintainer:TRACING)
Masami Hiramatsu <mhiramat@kernel.org> (maintainer:TRACING)
"Rafael J. Wysocki" <rafael@kernel.org> (maintainer:HIBERNATION (aka Software Suspend, aka swsusp))
Oscar Salvador <osalvador@suse.de> (maintainer:MEMORY HOT(UN)PLUG)
Mike Rapoport <rppt@kernel.org> (maintainer:MEMBLOCK AND MEMORY MANAGEMENT INITIALIZATION)
Johannes Weiner <hannes@cmpxchg.org> (maintainer:MEMORY MANAGEMENT - RECLAIM)
linux-mm@kvack.org (open list:MEMORY MANAGEMENT - CORE)
linux-doc@vger.kernel.org (open list:DOCUMENTATION)
linux-kernel@vger.kernel.org (open list)
linux-btrfs@vger.kernel.org (open list:BTRFS FILE SYSTEM)
linux-trace-kernel@vger.kernel.org (open list:TRACING)
linux-pm@vger.kernel.org (open list:HIBERNATION (aka Software Suspend, aka swsusp))
linux-cxl@vger.kernel.org (open list:MEMORY HOT(UN)PLUG)
HIBERNATION (aka Software Suspend, aka swsusp) status: Supported
SUSPEND TO RAM status: Supported

OK, so getting into the code. You didn't include an overall diffstat. So let's
see:

$ git checkout e1914add2799
...
$ b4 shazam 20260520150018.2491267-1-riel@surriel.com
...
$ git format-patch --cover-letter HEAD~40
...
$ tail -31 0000-cover-letter.patch
 Documentation/admin-guide/sysctl/vm.rst |   21 -
 Documentation/mm/physical_memory.rst    |   13 +-
 fs/btrfs/extent_io.c                    |   69 +-
 fs/btrfs/extent_io.h                    |    4 +-
 fs/btrfs/inode.c                        |    2 +-
 fs/btrfs/ioctl.c                        |    2 +-
 fs/btrfs/raid56.c                       |    6 +-
 fs/btrfs/relocation.c                   |    2 +-
 fs/btrfs/scrub.c                        |    3 +-
 include/linux/mmzone.h                  |  236 +-
 include/linux/page-flags.h              |    9 +
 include/linux/pageblock-flags.h         |   10 +
 include/linux/vm_event_item.h           |   10 +
 include/trace/events/kmem.h             |  373 ++
 kernel/power/snapshot.c                 |   35 +-
 mm/compaction.c                         |  360 +-
 mm/debug.c                              |   19 +-
 mm/internal.h                           |   39 +
 mm/memory_hotplug.c                     |    4 +
 mm/mm_init.c                            |  400 +-
 mm/page_alloc.c                         | 5064 ++++++++++++++++++++---
 mm/page_reporting.c                     |  149 +-
 mm/show_mem.c                           |   27 +-
 mm/sparse.c                             |    3 +-
 mm/vmscan.c                             |  115 +-
 mm/vmstat.c                             |   71 +-
 26 files changed, 6131 insertions(+), 915 deletions(-)

This is completely insane :) I do hope the omission of the diffstat was not
to hide this, but at any rate, adding ~5,000 lines of code to page_alloc.c
is a complete non-starter.

In any case I think clearly more files are required, mm/super_pageblock.c
or whatever it'll be, but at any rate this much code being added to me
clearly indicates something is terribly wrong here anyway.

And you added no tests whatsoever. I get that some things are hard to test
in the kernel and mm, but not even an attempt? Surely you have some
localised stuff that you've been using to exercise this, couldn't some of
that be made into selftests?

Certainly if tests are not present, this needs to be justified in the cover
letter.

(Again I appreciate this is an RFC, so perhaps those are coming.)

Anyway let's look at some of this code. Johannes's patches all look
reasonable, but as soon as we start seeing:

Assisted-by: Claude:claude-opus-4.7

Things start to go awry.

The series is full of extremely dense comments that contain far too much
specific information to be parseable. E.g.:

+	 *  - owner_cpu == this CPU (or no owner): take the local PCP
+	 *    lock with spin_trylock and enqueue normally. The trylock
+	 *    fails only on rare local self re-entry (IRQ/NMI fires
+	 *    while the interrupted task already holds the lock) or
+	 *    while a remote drain is active; either way, fall back to
+	 *    free_one_page (or the zone-llist for FPI_TRYLOCK). No
+	 *    irqsave: the trylock cannot block on self, and remote
+	 *    CPUs never take this pcp->lock (they go via free_llist),
+	 *    so an interruption cannot deadlock against another freer.

This isn't readable. This isn't acceptable. It matches exactly what I've
found LLMs to do, adding far too much detail in way that isn't
human-readable.

You must redo all of these.

The patches are all quite dense. 40 patches is already far too many IMO for
a series this massive, but you're also piling in tonnes of complexity in
every patch.

The series should really be broken out into separate series that each
slowly build up the prerequisites, so they are reviewable, parseable, can
be seen in action before we move to the superblock concept and each having
tests or means of asserting that they in fact function.

It feels that there are patches that indeed can be broken out sensibly like
this,
e.g. https://lore.kernel.org/all/20260520150018.2491267-5-riel@surriel.com/
(which also seems to be a fix? So maybe should be sent as one with
appropriate tags?), so I think that's really the only viable way to land
something this huge.

I see Usama commented on you breaking userspace, and you said you are not
going to do that after all (thanks)... but it worries me that you
essentially render the watermark boost broken? I don't think that's OK?

The code in general dumps large blocks of code into already existing huge
functions but, even more unforgivably, turns small, maintainable, easily
understood functions into completely disgusting horror shows.

For instance, __rmqueue_smallest() goes from ~20 lines to 604 lines. This
is completely insane and totally unacceptable.

The code is also full of software engineering 101 fails. For instance in
__rmqueue_sb_find_fallback():

	if (search_cats & SB_SEARCH_PREFERRED) {
		...
		for (full = SB_FULL; full < __NR_SB_FULLNESS; full++) {
			list_for_each_entry(sb,
					    &zone->spb_lists[cat][full], list) {
				...
				if (movable && cat == SB_TAINTED &&
				    sb->nr_free <= spb_tainted_reserve(sb))
					continue;
				...
				for (i = 0; i < MIGRATE_PCPTYPES - 1; i++) {
					...
					if (page) {
						...
					}
				}
			}
		}
	}

Note the mix of insane nesting AND guard clauses to make it even more
indecipherable.

There's no world in which I, or any other mm maintainer (I would venture to
say) want to maintain stuff like this.

A particularly concerning patch is
https://lore.kernel.org/all/20260520150018.2491267-15-riel@surriel.com/ -

The commit messages suffer from the same issue as the comments - incredibly
dense material that is far too detailed to the point of absolutely
clarifying nothing. E.g.:

"The SPB list heads (zone->spb_empty and the spb_lists[cat][full] matrix)
 are initialized only by setup_superpageblocks(), which is __init and runs
 only at boot.  Hot-add into a previously-empty zone invokes
 init_one_superpageblock() with zero-initialized list_heads, and the
 inlined list_add_tail() NULL-derefs walking ->next->prev."

Is just complete word salad. It may as well be written in Egyptian
hieroglyphs.

You're explaining spaghetti code using English language which makes it even
more indecipherable, and not getting to the root of what you are actually
doing.

Again this feels very LLM-generated. I've noticed that they tend to do this
'write out the code again but in English' stuff (and is why I do not
allow LLMs to generate code or comments for me!)

You need to make commit messages and comments as succinct as possible and
as clear as possible for _human beings_ :)

I also notice you have this huge blocks of word salad comments all over the
place but I haven't seen a single kdoc comment (EDIT: noticed at least 1 later!)

This is a far more helpful, structured, form of describing functions and
for anything put in a header you really do need to provide them.

Back to the patch. The diffstat is:

 include/linux/mmzone.h |  10 +
 mm/compaction.c        |  36 +-
 mm/internal.h          |  10 +
 mm/mm_init.c           | 146 +++++--
 mm/page_alloc.c        | 853 ++++++++++++++++++++++++++++++++---------
 mm/vmstat.c            |  66 ++--
 6 files changed, 883 insertions(+), 238 deletions(-)

This is (generally speaking, and certainly in this case) far, far too much
for a single patch.

But as per above this is the patch where you commit what are essentially
code war crimes against __rmqueue_smallest(), adding code so densely nested
that you have to lop off the end of code to fit:

+	if (!movable && !is_migrate_cma(migratetype)) {
+		for (full = SB_FULL; full < __NR_SB_FULLNESS; full++) {
+			list_for_each_entry(sb,
+				&zone->spb_lists[SB_TAINTED][full], list) {
+				if (!sb->nr_free)
+					continue;
+				for (current_order = max_t(unsigned int,
+						order, pageblock_order);
+				     current_order < NR_PAGE_ORDERS;
+				     ++current_order) {
+					area = &sb->free_area[current_order];
+					page = get_page_from_free_area(
+						area, MIGRATE_MOVABLE);
+					if (!page)
+						continue;
+					if (get_pageblock_isolate(page))
+						continue;
+					if (is_migrate_cma(
+					    get_pageblock_migratetype(page)))
+						continue;
+					page = claim_whole_block(zone, page,
+						current_order, order,
+						migratetype, MIGRATE_MOVABLE);
+					trace_mm_page_alloc_zone_locked(
+						page, order, migratetype,
+						pcp_allowed_order(order) &&
+						migratetype < MIGRATE_PCPTYPES);
+					return page;
+				}
+			}
+		}
+	}

I mean in what world are we taking code like this?

https://lore.kernel.org/all/20260520150018.2491267-22-riel@surriel.com/ has
what seem to be more human comments (good!) but then so densely nested that
they have to be cropped (bad).

using smaller, separate, functions and a structured programming approach
here is the way to go.

There's random smaller issues too like
https://lore.kernel.org/all/20260520150018.2491267-23-riel@surriel.com/
seems to assume the compiler can't figure out to remove dead code (note the
wild inconsistency too in comments)

You seem in some commits to undo or correct stuff you did in previous ones
like
https://lore.kernel.org/all/20260520150018.2491267-24-riel@surriel.com/

I mean perhaps I'm mistaken, but otherwise, can you instead rebase your
series please?

And again we see the world salad:

+ * Maximum tainted superpageblock candidates per spb_evacuate_for_order call.
+ * Collected under zone->lock, then evacuated without it. Larger than the
+ * contig-allocation candidate cap because evacuation runs from the slowpath
+ * after reclaim/compaction failed: we need a meaningful chance of freeing a
+ * non-MOV-claimable pageblock before the slowpath escalates to dropping
+ * ALLOC_NOFRAGMENT (which lets __rmqueue_claim taint clean SPBs). Sized to
+ * scan a meaningful fraction of a typical tainted-pool population.

No kdocs, some functions/complicated logic missing any comments at all,
then piles of words thrown at you like a rock.

This isn't pleasant to read, it adds no clarity, it just makes the whole
thing a mess.

The code in that one is at least vaguely ok (though still too nested).

I feel like where you've manually written code the quality substantially
improves, where the LLM has, the quality nosedives into oblivion.

For instance
https://lore.kernel.org/all/20260520150018.2491267-26-riel@surriel.com/
seems to be radically better, albeit adding far too much code. So I assume
that was more manual.

Stuff like
https://lore.kernel.org/all/20260520150018.2491267-27-riel@surriel.com/
shows more of a structural issue.

In:

+	 * Mark callers that have a cheap fallback if the page allocator returns
+	 * NULL, so __rmqueue can refuse to taint a clean SPB when an existing
+	 * tainted SPB still has free pageblocks waiting to be evacuated.
+	 *
+	 * Two shapes qualify:
+	 *
+	 *  1. Explicit fallback declaration: __GFP_NORETRY without
+	 *     __GFP_RETRY_MAYFAIL. Used by THP, slab high-order refill,
+	 *     skb_page_frag_refill on full sockets, etc.
+	 *
+	 *  2. Atomic-context shape: no __GFP_DIRECT_RECLAIM, no __GFP_NOMEMALLOC,
+	 *     no __GFP_NOFAIL. These callers (GFP_ATOMIC, GFP_NOWAIT, including
+	 *     ALLOC_HIGHATOMIC consumers) have implicit fallbacks: drop the
+	 *     packet, demote the slab order, return ENOMEM up the slowpath,
+	 *     retry from process context with GFP_KERNEL, etc. ALLOC_HIGHATOMIC
+	 *     callers also get a second crack at the dedicated MIGRATE_HIGHATOMIC
+	 *     reserve in rmqueue_buddy after __rmqueue returns NULL.
+	 *     Tainting a 1 GiB SPB to satisfy any of them is a long-lived
+	 *     fragmentation event for short-lived data.
+	 *

The comment is vastly better than most, but you seem to be tying far too
much up in assumptions about what particular workloads do.

This is indicative perhaps of a need to refactor to more reasonably
determine these.

Adding a function and state, say, that expresses these properties would
allow you to break out these concepts and have the code be self-documenting
(as well as adding suitable actual documentation in comments that would be
more succinct).

https://lore.kernel.org/all/20260520150018.2491267-29-riel@surriel.com/
also shows some real issues with how you've implemented this, e.g.:

+ * Called from each PASS_1/2/2B/2C/2D success path after a successful
+ * allocation against a tainted SPB. If the SPB is below its shrink
+ * high-water mark, queue the SPB-driven slab shrink and try to start
+ * the per-SPB defrag worker. Both have their own cooldown gates inside,
+ * so this is cheap to call on every such allocation.

Now you're putting information from one gargantuan function with labels
about 'passes' (rather reminiscent of VMA merge 'cases') into another.

This is a clear sign of a broken abstraction and the code not being
structured correctly.

You should _express_ state encoded in these 'passes' in the _actual code_,
break that code up sensibly and in a way that can be assessed for
correctness, not put a bit-rotting comment on top of a function whose
correctness is much harder to confirm.

Again this patch has similar issues with ridiculously dense indecipherable
comments, e.g.:

+	 * Last-chance defrag trigger before tainting a fresh clean SPB.
+	 * Walk the tainted-SPB list and try to wake the per-SPB defrag
+	 * worker on each. Catches SPBs that are stuck in expired-cooldown
+	 * state because no allocator activity has touched them recently
+	 * (the routine event-driven trigger from spb_update_list only
+	 * fires on bucket transitions, not on every alloc). Once the
+	 * cooldown has expired, spb_maybe_start_defrag() will requeue
+	 * work; otherwise the gate inside spb_needs_defrag() no-ops
+	 * cheaply. Bounded by nr_tainted_spbs and only runs when we are
+	 * already on the slow path of fragmenting the clean pool.

The spoondecker is montiplexed in the fradupple complex dedadderated in the
splunkyfied concratanator underfined by the transpontaculatoration
matrixifier... :)

I think this is symptomatic of the abstraction being fundamentally broken.

If you have to establish vast swathes of cognitive context like this mid
way through a huge function (again poor __rmqueue_smallest() who will never
forgive you), then you've basically failed to abstract it.

Please I beg you ADD FUNCTIONS :) structured programming is a thing,
struct's are a thing, abstraction is a thing :)

Pouring spaghetti into a single function is something you expect to see on
a 1990's PHP website, not in core mm code ;)

https://lore.kernel.org/all/20260520150018.2491267-30-riel@surriel.com/
seems to be another example of you just rethinking parts of what you
already submitted midway through the series.

Again I might be missing something here, but if you are doing this, please
just rebase your series to use $NEWIDEA from the start.

https://lore.kernel.org/all/20260520150018.2491267-35-riel@surriel.com/ is
another wall of text (newlines! Please :) but it seems like something you
can break out separately no?

It seems at least reviewable :)

https://lore.kernel.org/all/20260520150018.2491267-36-riel@surriel.com/
contains more of the same broken abstraction stuff, terrible nesting stuff,
word salad stuff but is at least mercifully smaller.

https://lore.kernel.org/all/20260520150018.2491267-37-riel@surriel.com/ is
almost emblematic of the terrible (LLM I hope?) comment issue in the
series. Overly dense hieroglyphs.

https://lore.kernel.org/all/20260520150018.2491267-38-riel@surriel.com/ is
another 'why didn't you rebase?' patch (again maybe I'm missing something
here!)

https://lore.kernel.org/all/20260520150018.2491267-39-riel@surriel.com/
seems more reasonable although newlines please :)

And finally
https://lore.kernel.org/all/20260520150018.2491267-40-riel@surriel.com/ -
while it's a do-not-merge patch in an RFC that changes tracing so maybe
unfair, but the comments are suffering the same symptoms as the rest of the
series.

~

So really, you need to start again, from scratch, and without the use of an
LLM for generating code, or at least with it kept on a (very very short)
leash.

And to be clear, I _want_ this concept of GB superpageblocks to land. It's
a really exciting concept.

Pulling compaction kicking and screaming into 2026 stands to significantly
benefit linux users and developers.

But the execution has to be _completely_ rethought.

I also worry about correctness - I simply cannot see how you can have sense
of it, given the state of the code. For something so invasive and so
critical to kernel functionality, code quality is simply not optional here.

Practical thoughts on how to rework the series:

- Properly abstract the concepts
- Properly separate out functions and data structures
- Add _human-readable_ comments that are succinct + clear as possible
- If a function becomes too nested, separate into smaller functions
- Add tests, if at all or in any way possible (and justify if you can't)
- Clarify commit messages, don't just rewrite code in English
- Use newlines for new paragraphs everywhere :)
- Refactor existing code in preparation for your changes
- Add a new file to contain your changes (+ add to page alloc MAINTAINERS
  section)
- Add kdocs for anything not static, and clearly describe static functions
- Split into separate series as much as possible, gradually building
  foundations for your changes
- Make everything less dense and more abstracted

In general, write the series with reviewers and other kernel developers in
mind - write clear explanations in comments and commit messages, have the
series slowly build to what you're implementing.

That way, we can see that correctness is maintained throughout, can review
what's there sensibly, and the series becomes upstreamable.

IOW I say we take off and nuke the entire site from orbit. It's the only
way to be sure :)

~

Another issue here is maintainer time - even this _extremely_ light-touch
review has taken me a few hours (of my weekend :). To review it in detail
would take probably DAYS of dedicated work.

In general, I'm concerned that we're going to get caught in a cycle of LLM
code sent - reviewers spending hours reviewing - LLM generates new revision
- etc.

This simply isn't scalable, maintainers cannot do this in the face of the
sheer quantity and complexity of code that LLMs can generate.

We are simply going to have to NAK. And that helps nobody.

Luckily for me, this isn't in a sub-(sub-)system I maintain, so I am not
obliged, but I do have empathy for my fellow maintainers, and am VERY
concerned about this trend.

There has recently been an absolute wave of LLM code, some acked as such
(and I think you for doing so here!) but others unacknowledged entirely,
and the workload, which was already too much, has risen significantly (Jon
has noted the rise in commit count for instance in LWN).

Treating maintainer time as without value was already an issue, but I fear
that we'll see a significant increase in maintainer stress and sadly,
burnout.

As such, I feel that we will have to implement measures at some point to
deprioritise/quickly dismiss such series, unfortunately.

But series from smart and capable engineers such as yourselfk who are well
respected in the communityk will likely not bek and thus will suffer from
this issue indefinitely.

So on that basis, I ask respectfully that you account for this when using
this tooling.

(Also, I appreciate that this is an RFC, but your recent non-RFC GUP series
https://lore.kernel.org/all/20260616190300.1509639-1-riel@surriel.com/
(revision v2 at
https://lore.kernel.org/all/20260625015053.2445008-1-riel@surriel.com/) has
all the same hallmarks as this one, so I feel this point needs to be
underlined).

~

Speaking more generally across the industry, I've been reading about
companies generating code blindly and running into terrible problems with
software that ends up becoming totally unmanageable.

I won't tolerate this happening happening in mm, and strongly object to the
concept (held by some AI proponents) that code is simply an unimportant
byproduct of AI.

The kernel's code (and especially mm's) is _critically_ important, quite
literally, as it forms the basis of the world's critical technical
infrastructure.

I am not opposed to the use of LLMs, but they _must_ serve as tools to
_assist_ experts at their job, not a means by which code bases are
degraded.

~

OK with all that said - to be absolutely clear - I respect you a great
deal, and I KNOW you're (much, much) better than this.

And, to repeat, this idea is very exciting and I _want_ to see this land.

But I feel you've rather let the LLM run amok and it's selling you (very,
very) short, given just how smart and capable you are.

Let's try again :)

Thanks, Lorenzo

^ permalink raw reply

* Re: [External] [PATCH v2 4/8] riscv_cbqri: Add capacity controller probe and allocation device ops
From: yunhui cui @ 2026-06-27  9:31 UTC (permalink / raw)
  To: Drew Fustini
  Cc: Adrien Ricciardi, Alexandre Ghiti, 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, James Morse, Kornel Dulęba,
	Krzysztof Kozlowski, liu.qingtao2, Liu Zhiwei, Palmer Dabbelt,
	Paul Walmsley, Peter Newman, Radim Krčmář,
	Reinette Chatre, Rob Herring, Samuel Holland,
	Sebastian Andrzej Siewior, Tony Luck, Vasudevan Srinivasan,
	Ved Shanbhogue, Weiwei Li, linux-kernel, linux-riscv, x86,
	devicetree, linux-rt-devel, linux-doc
In-Reply-To: <20260624-dfustini-atl-sc-cbqri-dt-v2-4-2f8049fd902b@kernel.org>

Hi Drew,

On Thu, Jun 25, 2026 at 9:41 AM Drew Fustini <fustini@kernel.org> wrote:
>
> 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: Claude:claude-opus-4-7
> Co-developed-by: Adrien Ricciardi <aricciardi@baylibre.com>
> Signed-off-by: Adrien Ricciardi <aricciardi@baylibre.com>
> Signed-off-by: Drew Fustini <fustini@kernel.org>
> ---
>  MAINTAINERS                      |   3 +
>  drivers/resctrl/Kconfig          |  13 +
>  drivers/resctrl/Makefile         |   3 +
>  drivers/resctrl/cbqri_devices.c  | 520 +++++++++++++++++++++++++++++++++++++++
>  drivers/resctrl/cbqri_internal.h | 107 ++++++++
>  include/linux/riscv_cbqri.h      |  47 ++++
>  6 files changed, 693 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 07109e1a8f8470377916c98074ab68fec51dfdc6..811c0c9b1fac806945cad0229c5330654420a835 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -23300,6 +23300,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
>
>  RISC-V RPMI AND MPXY DRIVERS
>  M:     Rahul Pathak <rahul@summations.net>
> diff --git a/drivers/resctrl/Kconfig b/drivers/resctrl/Kconfig
> index 672abea3b03ccbeb4532832dd18e5cb80e90ff5b..92b9c82cf9f397437d28006e79d40c2d9b384eb0 100644
> --- a/drivers/resctrl/Kconfig
> +++ b/drivers/resctrl/Kconfig
> @@ -29,3 +29,16 @@ 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.
> +
> +if RISCV_CBQRI
> +
> +endif
> diff --git a/drivers/resctrl/Makefile b/drivers/resctrl/Makefile
> index 4f6d0e81f9b8f34bd8842fdb13be69da72515b79..4d8a2c4b5627144a651da007174d4d9df6171330 100644
> --- a/drivers/resctrl/Makefile
> +++ b/drivers/resctrl/Makefile
> @@ -3,3 +3,6 @@ mpam-y                                          += mpam_devices.o
>  mpam-$(CONFIG_ARM64_MPAM_RESCTRL_FS)           += mpam_resctrl.o
>
>  ccflags-$(CONFIG_ARM64_MPAM_DRIVER_DEBUG)      += -DDEBUG
> +
> +obj-$(CONFIG_RISCV_CBQRI)                      += cbqri.o
> +cbqri-y                                                += cbqri_devices.o
> diff --git a/drivers/resctrl/cbqri_devices.c b/drivers/resctrl/cbqri_devices.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..8ad9df404f65d5d82722cf8b78f02936c489ca6d
> --- /dev/null
> +++ b/drivers/resctrl/cbqri_devices.c
> @@ -0,0 +1,520 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#define pr_fmt(fmt) "%s:%s: " fmt, KBUILD_MODNAME, __func__
> +
> +#include <linux/bitfield.h>
> +#include <linux/riscv_cbqri.h>
> +#include <linux/cpumask.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/iopoll.h>
> +#include <linux/ioport.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/printk.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +
> +#include <asm/csr.h>
> +
> +#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 resctrl
> + * glue. Runtime cpuhp walks happen after registration has settled.
> + */
> +DEFINE_MUTEX(cbqri_controllers_lock);
> +
> +/* Set capacity block mask (cc_block_mask) */
> +static void cbqri_set_cbm(struct cbqri_controller *ctrl, u64 cbm)
> +{
> +       iowrite64(cbm, ctrl->base + CBQRI_CC_BLOCK_MASK_OFF);

The CBQRI spec allows naturally aligned 4-byte accesses and only guarantees
atomicity for 4-byte accesses; 8-byte atomicity is unspecified.

Would 32-bit split accesses be preferable here instead of relying on
ioread64/iowrite64? This may also make the driver less dependent on native
64-bit MMIO support.

> +}
> +
> +static int cbqri_wait_busy_flag(struct cbqri_controller *ctrl, int reg_offset,
> +                               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 = readq_poll_timeout(ctrl->base + reg_offset, reg,
> +                                !FIELD_GET(CBQRI_CONTROL_REGISTERS_BUSY_MASK, reg),
> +                                10, 1000);
> +       if (ret)
> +               return ret;
> +       if (regp)
> +               *regp = reg;
> +       return 0;
> +}
> +
> +/*
> + * Perform capacity allocation control operation on capacity controller.
> + * Caller must hold ctrl->lock.
> + */
> +static int cbqri_cc_alloc_op(struct cbqri_controller *ctrl, int operation,
> +                            int rcid, u32 at)
> +{
> +       int reg_offset = CBQRI_CC_ALLOC_CTL_OFF;
> +       int status;
> +       u64 reg;
> +
> +       lockdep_assert_held(&ctrl->lock);
> +
> +       if (cbqri_wait_busy_flag(ctrl, reg_offset, &reg) < 0) {
> +               pr_err_ratelimited("BUSY timeout before starting operation\n");
> +               return -EIO;
> +       }
> +       FIELD_MODIFY(CBQRI_CONTROL_REGISTERS_OP_MASK, &reg, operation);
> +       FIELD_MODIFY(CBQRI_CONTROL_REGISTERS_RCID_MASK, &reg, rcid);
> +
> +       /*
> +        * CBQRI Table 1: AT 0=Data, 1=Code. 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 &= ~CBQRI_CONTROL_REGISTERS_AT_MASK;
> +       if (ctrl->cc.supports_alloc_at_code)
> +               reg |= FIELD_PREP(CBQRI_CONTROL_REGISTERS_AT_MASK, at);
> +
> +       iowrite64(reg, ctrl->base + reg_offset);
> +
> +       if (cbqri_wait_busy_flag(ctrl, reg_offset, &reg) < 0) {
> +               pr_err_ratelimited("BUSY timeout during operation\n");
> +               return -EIO;
> +       }
> +
> +       status = FIELD_GET(CBQRI_CONTROL_REGISTERS_STATUS_MASK, reg);
> +       if (status != CBQRI_CC_ALLOC_CTL_STATUS_SUCCESS) {
> +               pr_err_ratelimited("operation %d failed: status=%d\n", operation, status);
> +               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=Data / 1=Code, 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 = 0;
> +       int err = 0;
> +       u64 reg;
> +
> +       mutex_lock(&ctrl->lock);
> +
> +       need_at_mirror = 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.
> +        * Pre-clear cc_block_mask so a silent firmware no-op (status
> +        * SUCCESS but staging not updated) shows as a zero readback
> +        * rather than carrying stale data from a prior op.
> +        */
> +       if (need_at_mirror) {
> +               cbqri_set_cbm(ctrl, 0);
> +               err = cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT,
> +                                       closid, cfg->at);
> +               if (err < 0)
> +                       goto out;
> +               saved_cbm = ioread64(ctrl->base + CBQRI_CC_BLOCK_MASK_OFF);
> +       }
> +
> +       /* Set capacity block mask (cc_block_mask) */
> +       cbqri_set_cbm(ctrl, cfg->cbm);
> +
> +       /* Capacity config limit operation for the AT half implied by cfg->at */
> +       err = cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT,
> +                               closid, cfg->at);
> +       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 = (cfg->at == CBQRI_CONTROL_REGISTERS_AT_CODE) ?
> +                           CBQRI_CONTROL_REGISTERS_AT_DATA :
> +                           CBQRI_CONTROL_REGISTERS_AT_CODE;
> +
> +               cbqri_set_cbm(ctrl, cfg->cbm);
> +               err = cbqri_cc_alloc_op(ctrl,
> +                                       CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT,
> +                                       closid, other);
> +               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.
> +                        */
> +                       cbqri_set_cbm(ctrl, saved_cbm);
> +                       rerr = cbqri_cc_alloc_op(ctrl,
> +                                                CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT,
> +                                                closid, cfg->at);
> +                       if (rerr < 0)
> +                               pr_err_ratelimited("AT-mirror revert failed (err=%d), AT halves diverged\n",
> +                                                  rerr);
> +                       goto out;
> +               }
> +       }
> +
> +       /* Clear cc_block_mask before read limit to verify op works */
> +       cbqri_set_cbm(ctrl, 0);
> +
> +       /* Perform a capacity read limit operation to verify blockmask */
> +       err = cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT,
> +                               closid, cfg->at);
> +       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 = ioread64(ctrl->base + CBQRI_CC_BLOCK_MASK_OFF);
> +       if (lower_32_bits(reg) != cfg->cbm) {
> +               pr_err_ratelimited("CBM verify mismatch (reg=%llx != cbm=%llx)\n",
> +                                  reg, cfg->cbm);
> +               err = -EIO;
> +       }
> +
> +out:
> +       mutex_unlock(&ctrl->lock);
> +       return err;
> +}
> +
> +/*
> + * Read the configured CBM for closid on the at half via READ_LIMIT.
> + * Pre-clears cc_block_mask before the op so a silent firmware no-op
> + * (status SUCCESS but staging not updated) is detectable in cbm_out.
> + */
> +int cbqri_read_cache_config(struct cbqri_controller *ctrl, u32 closid,
> +                           u32 at, u32 *cbm_out)
> +{
> +       int err;
> +
> +       mutex_lock(&ctrl->lock);
> +       cbqri_set_cbm(ctrl, 0);
> +       err = cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_READ_LIMIT, closid, at);
> +       if (err == 0) {
> +               /*
> +                * cc_block_mask is a 64-bit MMIO register. resctrl exposes the
> +                * CBM as a u32. cbqri_probe_cc() rejects ncblks > 32 so the
> +                * upper 32 bits are reserved zero by the spec. Narrow
> +                * explicitly via lower_32_bits() so the assumption is visible
> +                * at the read site.
> +                */
> +               *cbm_out = lower_32_bits(ioread64(ctrl->base + CBQRI_CC_BLOCK_MASK_OFF));
> +       }
> +       mutex_unlock(&ctrl->lock);
> +       return err;
> +}
> +
> +static int cbqri_probe_feature(struct cbqri_controller *ctrl, int reg_offset,
> +                              int operation, int *status, bool *access_type_supported)
> +{
> +       const u64 active_mask = 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==0 (feature not
> +        * implemented) path returns a deterministic value to the caller
> +        * rather than leaving an uninitialized bool.
> +        */
> +       *access_type_supported = false;
> +
> +       /* Keep the initial register value to preserve the WPRI fields */
> +       reg = ioread64(ctrl->base + reg_offset);
> +       saved_reg = 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 = (saved_reg & ~active_mask) |
> +             FIELD_PREP(CBQRI_CONTROL_REGISTERS_OP_MASK, operation);
> +       iowrite64(reg, ctrl->base + reg_offset);
> +       if (cbqri_wait_busy_flag(ctrl, reg_offset, &reg) < 0) {
> +               pr_err_ratelimited("BUSY timeout during operation\n");
> +               return -EIO;
> +       }
> +
> +       /* Get the operation status */
> +       *status = 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 != 0) {
> +               /*
> +                * Re-issue operation with AT=CODE so the controller
> +                * latches AT=CODE on supported hardware (or resets it to 0
> +                * on hardware that doesn't). OP must be a defined CBQRI op
> +                * here. OP=0 is a no-op and would silently disable CDP.
> +                */
> +               reg = (saved_reg & ~active_mask) |
> +                     FIELD_PREP(CBQRI_CONTROL_REGISTERS_OP_MASK, operation) |
> +                     FIELD_PREP(CBQRI_CONTROL_REGISTERS_AT_MASK,
> +                                CBQRI_CONTROL_REGISTERS_AT_CODE);
> +               iowrite64(reg, ctrl->base + reg_offset);
> +               if (cbqri_wait_busy_flag(ctrl, reg_offset, &reg) < 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 = FIELD_GET(CBQRI_CONTROL_REGISTERS_AT_MASK, reg);
> +               if (at == CBQRI_CONTROL_REGISTERS_AT_CODE)
> +                       *access_type_supported = true;
> +       }
> +
> +       /*
> +        * Restore the original register value.
> +        * Clear OP to avoid re-triggering the probe op.
> +        */
> +       saved_reg &= ~CBQRI_CONTROL_REGISTERS_OP_MASK;
> +       iowrite64(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 = ioread64(ctrl->base + CBQRI_CC_CAPABILITIES_OFF);
> +       if (reg == 0)
> +               return -ENODEV;
> +
> +       ver_minor = FIELD_GET(CBQRI_CC_CAPABILITIES_VER_MINOR_MASK, reg);
> +       ver_major = FIELD_GET(CBQRI_CC_CAPABILITIES_VER_MAJOR_MASK, reg);
> +       ctrl->cc.ncblks = FIELD_GET(CBQRI_CC_CAPABILITIES_NCBLKS_MASK, reg);
> +
> +       pr_debug("version=%d.%d ncblks=%d cache_level=%d\n",
> +                ver_major, ver_minor,
> +                ctrl->cc.ncblks, ctrl->cache.cache_level);
> +
> +       /*
> +        * NCBLKS == 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;
> +       }
> +
> +       if (ctrl->cc.ncblks > 32) {
> +               pr_warn("CC at %pa has ncblks=%u > 32 (resctrl CBM is u32), skipping\n",
> +                       &ctrl->addr, ctrl->cc.ncblks);
> +               return -ENODEV;
> +       }
> +
> +       /* Probe allocation features */
> +       err = 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 == CBQRI_CC_ALLOC_CTL_STATUS_SUCCESS)
> +               ctrl->alloc_capable = true;
> +
> +       return 0;
> +}
> +
> +static int cbqri_probe_controller(struct cbqri_controller *ctrl)
> +{
> +       int err;
> +
> +       pr_debug("controller info: type=%d addr=%pa size=%pa max-rcid=%u\n",
> +                ctrl->type, &ctrl->addr, &ctrl->size, ctrl->rcid_count);
> +
> +       if (!ctrl->addr) {
> +               pr_warn("controller has invalid addr=0x0, 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 = ioremap(ctrl->addr, ctrl->size);
> +       if (!ctrl->base) {
> +               pr_err("ioremap failed for %pa\n", &ctrl->addr);
> +               err = -ENOMEM;
> +               goto err_release;
> +       }
> +
> +       switch (ctrl->type) {
> +       case CBQRI_CONTROLLER_TYPE_CAPACITY:
> +               err = cbqri_probe_cc(ctrl);
> +               break;
> +       default:
> +               pr_err("unknown controller type %d\n", ctrl->type);
> +               err = -ENODEV;
> +               break;
> +       }
> +
> +       if (err)
> +               goto err_iounmap;
> +
> +       return 0;
> +
> +err_iounmap:
> +       iounmap(ctrl->base);
> +       ctrl->base = 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 controller
> + * @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.
> + * @cpu_mask:    CPUs that share this cache.
> + *
> + * The cache topology is supplied directly by the caller. A device-tree
> + * platform driver that already knows which CPUs share the cache and at what
> + * level passes that in. There is no firmware table to resolve it 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, const struct cpumask *cpu_mask)
> +{
> +       struct cbqri_controller *ctrl;
> +       int err;
> +
> +       if (!info->addr) {
> +               pr_warn("skipping controller with invalid addr=0x0\n");
> +               return -EINVAL;
> +       }
> +
> +       if (info->type != CBQRI_CONTROLLER_TYPE_CAPACITY) {
> +               pr_warn("register_cc_dt called with non-capacity type %u\n",
> +                       info->type);
> +               return -EINVAL;
> +       }
> +
> +       if (!cpu_mask || cpumask_empty(cpu_mask)) {
> +               pr_warn("register_cc_dt called with empty cpu_mask\n");
> +               return -EINVAL;
> +       }
> +
> +       ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
> +       if (!ctrl)
> +               return -ENOMEM;
> +
> +       mutex_init(&ctrl->lock);
> +
> +       ctrl->addr = info->addr;
> +       ctrl->size = info->size;
> +       ctrl->type = info->type;
> +       ctrl->rcid_count = 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, skipping\n",
> +                       &ctrl->addr, ctrl->rcid_count);
> +               cbqri_controller_destroy(ctrl);
> +               return -EINVAL;
> +       }
> +
> +       ctrl->cache.cache_id = info->cache_id;
> +       ctrl->cache.cache_level = cache_level;
> +       cpumask_copy(&ctrl->cache.cpu_mask, cpu_mask);
> +
> +       err = 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;
> +       }
> +
> +       mutex_lock(&cbqri_controllers_lock);
> +       list_add_tail(&ctrl->list, &cbqri_controllers);
> +       mutex_unlock(&cbqri_controllers_lock);
> +       return 0;
> +}
> diff --git a/drivers/resctrl/cbqri_internal.h b/drivers/resctrl/cbqri_internal.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..2192a3831bcde7cd4fddf03f2ce1103902e8815c
> --- /dev/null
> +++ b/drivers/resctrl/cbqri_internal.h
> @@ -0,0 +1,107 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +#ifndef _DRIVERS_RESCTRL_CBQRI_INTERNAL_H
> +#define _DRIVERS_RESCTRL_CBQRI_INTERNAL_H
> +
> +#include <linux/bitfield.h>
> +#include <linux/riscv_cbqri.h>
> +#include <linux/cpumask.h>
> +#include <linux/list.h>
> +#include <linux/mutex.h>
> +#include <linux/types.h>
> +
> +/* 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
> +
> +/*
> + * Highest defined register offset (0x20) plus the 8-byte register width.
> + * cbqri_probe_controller() rejects smaller mappings so a u64 access at
> + * BLOCK_MASK stays in bounds.
> + */
> +#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)
> +
> +/*
> + * CC control registers are 64-bit. Keep every field mask GENMASK_ULL so
> + * FIELD_MODIFY() or ~mask on a u64 register never zero-extends a 32-bit
> + * mask and clobbers STATUS/BUSY/WPRI in bits 63:32 if RV32 support is
> + * added in the future.
> + */
> +#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;
> +};
> +
> +/**
> + * 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_DATA
> + *               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;
> +               struct cpumask cpu_mask;
> +               /* 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 0000000000000000000000000000000000000000..58737224d2f2c67a443f78f9fea7f368c7d16044
> --- /dev/null
> +++ b/include/linux/riscv_cbqri.h
> @@ -0,0 +1,47 @@
> +/* 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 <linux/types.h>
> +
> +struct cpumask;
> +
> +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, const struct cpumask *cpu_mask);
> +#else
> +static inline int
> +riscv_cbqri_register_cc_dt(const struct cbqri_controller_info *info,
> +                          u32 cache_level, const struct cpumask *cpu_mask)
> +{
> +       return -ENODEV;
> +}
> +#endif
> +
> +#endif /* _LINUX_RISCV_CBQRI_H */
>
> --
> 2.34.1
>

Thanks,
Yunhui

^ permalink raw reply

* [PATCH] docs: infiniband: fix bracket
From: Manuel Ebner @ 2026-06-27  9:31 UTC (permalink / raw)
  To: Jason Gunthorpe, Leon Romanovsky, Jonathan Corbet, Shuah Khan,
	open list:INFINIBAND SUBSYSTEM, open list:DOCUMENTATION,
	open list
  Cc: Manuel Ebner

Remove needless ')'.

Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
---
 Documentation/infiniband/user_mad.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/infiniband/user_mad.rst b/Documentation/infiniband/user_mad.rst
index d88abfc0e370..cd66e7623d66 100644
--- a/Documentation/infiniband/user_mad.rst
+++ b/Documentation/infiniband/user_mad.rst
@@ -62,7 +62,7 @@ Receiving MADs
 	struct ib_user_mad *mad;
 	mad = malloc(sizeof *mad + 256);
 	ret = read(fd, mad, sizeof *mad + 256);
-	if (ret == -ENOSPC)) {
+	if (ret == -ENOSPC) {
 		length = mad.length;
 		free(mad);
 		mad = malloc(sizeof *mad + length);
-- 
2.54.0


^ permalink raw reply related

* [PATCH] docs/mm: Fix brackets
From: Manuel Ebner @ 2026-06-27  9:32 UTC (permalink / raw)
  To: Jason Gunthorpe, Leon Romanovsky, Andrew Morton,
	David Hildenbrand, Lorenzo Stoakes, Liam R . Howlett,
	Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
	Jonathan Corbet, Shuah Khan, Shakeel Butt,
	open list:HMM - Heterogeneous Memory Management,
	open list:DOCUMENTATION, open list
  Cc: Manuel Ebner

Remove unnecessary ')' and missing '('.
Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
---
 Documentation/mm/hmm.rst           | 4 ++--
 Documentation/mm/process_addrs.rst | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/Documentation/mm/hmm.rst b/Documentation/mm/hmm.rst
index 7d61b7a8b65b..54c461e7a143 100644
--- a/Documentation/mm/hmm.rst
+++ b/Documentation/mm/hmm.rst
@@ -191,7 +191,7 @@ like a CPU page fault. The usage pattern is::
       mmap_read_unlock(mm);
 
       take_lock(driver->update);
-      if (mmu_interval_read_retry(&ni, range.notifier_seq) {
+      if (mmu_interval_read_retry(&ni, range.notifier_seq)) {
           release_lock(driver->update);
           goto again;
       }
@@ -316,7 +316,7 @@ between device driver specific code and shared common code:
    system memory and device private memory.
 
    One of the first steps migrate_vma_setup() does is to invalidate other
-   device's MMUs with the ``mmu_notifier_invalidate_range_start(()`` and
+   device's MMUs with the ``mmu_notifier_invalidate_range_start()`` and
    ``mmu_notifier_invalidate_range_end()`` calls around the page table
    walks to fill in the ``args->src`` array with PFNs to be migrated.
    The ``invalidate_range_start()`` callback is passed a
diff --git a/Documentation/mm/process_addrs.rst b/Documentation/mm/process_addrs.rst
index 851680ead45f..b391502fbfd6 100644
--- a/Documentation/mm/process_addrs.rst
+++ b/Documentation/mm/process_addrs.rst
@@ -724,7 +724,7 @@ the zap and the invocation of :c:func:`!free_pgtables`.
 
 Since it is assumed that all such steps have been taken, page table entries are
 cleared without page table locks (in the :c:func:`!pgd_clear`, :c:func:`!p4d_clear`,
-:c:func:`!pud_clear`, and :c:func:`!pmd_clear` functions.
+:c:func:`!pud_clear`, and :c:func:`!pmd_clear` functions).
 
 .. note:: It is possible for leaf page tables to be torn down independent of
           the page tables above it as is done by
-- 
2.54.0


^ permalink raw reply related

* Re: [External] [PATCH v2 3/8] riscv: Add support for srmcfg CSR from Ssqosid extension
From: yunhui cui @ 2026-06-27  9:38 UTC (permalink / raw)
  To: Drew Fustini
  Cc: Adrien Ricciardi, Alexandre Ghiti, 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, James Morse, Kornel Dulęba,
	Krzysztof Kozlowski, liu.qingtao2, Liu Zhiwei, Palmer Dabbelt,
	Paul Walmsley, Peter Newman, Radim Krčmář,
	Reinette Chatre, Rob Herring, Samuel Holland,
	Sebastian Andrzej Siewior, Tony Luck, Vasudevan Srinivasan,
	Ved Shanbhogue, Weiwei Li, linux-kernel, linux-riscv, x86,
	devicetree, linux-rt-devel, linux-doc
In-Reply-To: <20260624-dfustini-atl-sc-cbqri-dt-v2-3-2f8049fd902b@kernel.org>

Hi Drew,

On Thu, Jun 25, 2026 at 9:40 AM Drew Fustini <fustini@kernel.org> wrote:
>
> Add support for the srmcfg CSR defined in the Ssqosid ISA extension.
> The CSR contains two fields:
>
>   - Resource Control ID (RCID) for resource allocation
>   - Monitoring Counter ID (MCID) for tracking resource usage
>
> Requests from a hart to shared resources are tagged with these IDs,
> allowing resource usage to be associated with the running task.
>
> Add a srmcfg field to thread_struct with the same format as the CSR so
> the scheduler can set the RCID and MCID for each task on context
> switch. A per-cpu cpu_srmcfg variable mirrors the CSR state to avoid
> redundant writes. L1D-hot memory access is faster than a CSR read and
> avoids traps under virtualization.
>
> A per-cpu cpu_srmcfg_default holds the default srmcfg for each CPU as
> set by resctrl CPU group assignment. On context switch, RCID and MCID
> inherit from the CPU default independently: a task whose thread RCID
> field is zero takes the CPU default's RCID, and likewise for MCID.
>
> Link: https://github.com/riscv/riscv-ssqosid/releases/tag/v1.0
> Assisted-by: Claude:claude-opus-4-7
> Co-developed-by: Kornel Dulęba <mindal@semihalf.com>
> Signed-off-by: Kornel Dulęba <mindal@semihalf.com>
> Signed-off-by: Drew Fustini <fustini@kernel.org>
> ---
>  MAINTAINERS                        |  8 ++++
>  arch/riscv/Kconfig                 | 18 +++++++
>  arch/riscv/include/asm/csr.h       |  5 ++
>  arch/riscv/include/asm/processor.h |  3 ++
>  arch/riscv/include/asm/qos.h       | 83 ++++++++++++++++++++++++++++++++
>  arch/riscv/include/asm/switch_to.h |  3 ++
>  arch/riscv/kernel/Makefile         |  2 +
>  arch/riscv/kernel/qos.c            | 98 ++++++++++++++++++++++++++++++++++++++
>  8 files changed, 220 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0b9d7c8276acbafdb28a0ea5e81aa853ebee50b9..07109e1a8f8470377916c98074ab68fec51dfdc6 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -23293,6 +23293,14 @@ F:     drivers/perf/riscv_pmu.c
>  F:     drivers/perf/riscv_pmu_legacy.c
>  F:     drivers/perf/riscv_pmu_sbi.c
>
> +RISC-V QOS RESCTRL SUPPORT
> +M:     Drew Fustini <fustini@kernel.org>
> +R:     yunhui cui <cuiyunhui@bytedance.com>
> +L:     linux-riscv@lists.infradead.org
> +S:     Supported
> +F:     arch/riscv/include/asm/qos.h
> +F:     arch/riscv/kernel/qos.c
> +
>  RISC-V RPMI AND MPXY DRIVERS
>  M:     Rahul Pathak <rahul@summations.net>
>  M:     Anup Patel <anup@brainfault.org>
> diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
> index 3f0a647218e407f72890e83722ba8472858c1a59..ee586925f97227668c228b5481c05a2f914d928c 100644
> --- a/arch/riscv/Kconfig
> +++ b/arch/riscv/Kconfig
> @@ -590,6 +590,24 @@ config RISCV_ISA_SVNAPOT
>
>           If you don't know what to do here, say Y.
>
> +config RISCV_ISA_SSQOSID
> +       bool "Ssqosid extension support for supervisor mode Quality of Service ID"
> +       depends on 64BIT

Why is 64BIT required here?

> +       default n
> +       help
> +         Adds support for the Ssqosid ISA extension (Supervisor-mode
> +         Quality of Service ID).
> +
> +         Ssqosid defines the srmcfg CSR which allows the system to tag the
> +         running process with an RCID (Resource Control ID) and MCID
> +         (Monitoring Counter ID). The RCID is used to determine resource
> +         allocation. The MCID is used to track resource usage in event
> +         counters.
> +
> +         For example, a cache controller may use the RCID to apply a
> +         cache partitioning scheme and use the MCID to track how much
> +         cache a process, or a group of processes, is using.
> +
>  config RISCV_ISA_SVPBMT
>         bool "Svpbmt extension support for supervisor mode page-based memory types"
>         depends on 64BIT && MMU
> diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
> index 31b8988f4488daa89b854ccc97c4efe1c82bcc3e..7bce928e5daa09bd62f0917279b04cfad30f46f5 100644
> --- a/arch/riscv/include/asm/csr.h
> +++ b/arch/riscv/include/asm/csr.h
> @@ -84,6 +84,10 @@
>  #define SATP_ASID_MASK _AC(0xFFFF, UL)
>  #endif
>
> +/* SRMCFG fields */
> +#define SRMCFG_RCID_MASK       GENMASK(11, 0)
> +#define SRMCFG_MCID_MASK       GENMASK(27, 16)
> +
>  /* Exception cause high bit - is an interrupt if set */
>  #define CAUSE_IRQ_FLAG         (_AC(1, UL) << (__riscv_xlen - 1))
>
> @@ -328,6 +332,7 @@
>  #define CSR_STVAL              0x143
>  #define CSR_SIP                        0x144
>  #define CSR_SATP               0x180
> +#define CSR_SRMCFG             0x181
>
>  #define CSR_STIMECMP           0x14D
>  #define CSR_STIMECMPH          0x15D
> diff --git a/arch/riscv/include/asm/processor.h b/arch/riscv/include/asm/processor.h
> index 812517b2cec1350f741849c1c56a35027321ef50..49a386d74cd3f0603a3ff919059d077a7e4d513c 100644
> --- a/arch/riscv/include/asm/processor.h
> +++ b/arch/riscv/include/asm/processor.h
> @@ -123,6 +123,9 @@ struct thread_struct {
>         /* A forced icache flush is not needed if migrating to the previous cpu. */
>         unsigned int prev_cpu;
>  #endif
> +#ifdef CONFIG_RISCV_ISA_SSQOSID
> +       u32 srmcfg;
> +#endif
>  };
>
>  /* Whitelist the fstate from the task_struct for hardened usercopy */
> diff --git a/arch/riscv/include/asm/qos.h b/arch/riscv/include/asm/qos.h
> new file mode 100644
> index 0000000000000000000000000000000000000000..e9e1d69f3797be5f89785a9b3aa7d9d51c476a8a
> --- /dev/null
> +++ b/arch/riscv/include/asm/qos.h
> @@ -0,0 +1,83 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _ASM_RISCV_QOS_H
> +#define _ASM_RISCV_QOS_H
> +
> +#include <linux/percpu-defs.h>
> +
> +#ifdef CONFIG_RISCV_ISA_SSQOSID
> +
> +#include <linux/bitfield.h>
> +#include <linux/cpufeature.h>
> +#include <linux/sched.h>
> +
> +#include <asm/csr.h>
> +#include <asm/fence.h>
> +#include <asm/hwcap.h>
> +
> +/* cached value of srmcfg csr for each cpu */
> +DECLARE_PER_CPU(u32, cpu_srmcfg);
> +
> +/* default srmcfg value for each cpu, set via resctrl cpu assignment */
> +DECLARE_PER_CPU(u32, cpu_srmcfg_default);
> +
> +static inline void __switch_to_srmcfg(struct task_struct *next)
> +{
> +       u32 thread_srmcfg, default_srmcfg;
> +
> +       thread_srmcfg = READ_ONCE(next->thread.srmcfg);
> +       default_srmcfg = __this_cpu_read(cpu_srmcfg_default);
> +
> +       /*
> +        * RCID and MCID inherit from cpu_srmcfg_default independently.
> +        * RESCTRL_RESERVED_CLOSID and RESCTRL_RESERVED_RMID are both 0, so a
> +        * zero field means "unassigned" and takes the CPU default.
> +        */
> +       if (thread_srmcfg == 0) {
> +               thread_srmcfg = default_srmcfg;
> +       } else {
> +               u32 rcid = FIELD_GET(SRMCFG_RCID_MASK, thread_srmcfg);
> +               u32 mcid = FIELD_GET(SRMCFG_MCID_MASK, thread_srmcfg);
> +
> +               if (rcid == 0 || mcid == 0) {
> +                       if (rcid == 0)
> +                               rcid = FIELD_GET(SRMCFG_RCID_MASK, default_srmcfg);
> +                       if (mcid == 0)
> +                               mcid = FIELD_GET(SRMCFG_MCID_MASK, default_srmcfg);
> +                       thread_srmcfg = FIELD_PREP(SRMCFG_RCID_MASK, rcid) |
> +                                       FIELD_PREP(SRMCFG_MCID_MASK, mcid);
> +               }
> +       }
> +
> +       if (thread_srmcfg != __this_cpu_read(cpu_srmcfg)) {
> +               /*
> +                * Drain stores from the outgoing task before the CSR write
> +                * so they retain the previous RCID/MCID tag at the cache
> +                * interconnect.
> +                */
> +               RISCV_FENCE(rw, o);
> +
> +               __this_cpu_write(cpu_srmcfg, thread_srmcfg);
> +               csr_write(CSR_SRMCFG, thread_srmcfg);
> +               /*
> +                * Order the csrw before the new task's loads/stores so they
> +                * pick up the new tag. Zicsr 6.1.1 makes CSR writes weakly
> +                * ordered (device-output) vs memory ops. Ssqosid v1.0 is
> +                * silent so honor the general CSR rule.
> +                */
> +               RISCV_FENCE(o, rw);
> +       }
> +}
> +
> +static __always_inline bool has_srmcfg(void)
> +{
> +       return riscv_has_extension_unlikely(RISCV_ISA_EXT_SSQOSID);
> +}
> +
> +#else /* ! CONFIG_RISCV_ISA_SSQOSID  */
> +
> +struct task_struct;
> +static __always_inline bool has_srmcfg(void) { return false; }
> +static inline void __switch_to_srmcfg(struct task_struct *next) { }
> +
> +#endif /* CONFIG_RISCV_ISA_SSQOSID */
> +#endif /* _ASM_RISCV_QOS_H */
> diff --git a/arch/riscv/include/asm/switch_to.h b/arch/riscv/include/asm/switch_to.h
> index 0e71eb82f920cac2f14bb626879bb219a2f247cc..1c7ea53ec012adeaf03bf7c5d549ab21849768b5 100644
> --- a/arch/riscv/include/asm/switch_to.h
> +++ b/arch/riscv/include/asm/switch_to.h
> @@ -14,6 +14,7 @@
>  #include <asm/processor.h>
>  #include <asm/ptrace.h>
>  #include <asm/csr.h>
> +#include <asm/qos.h>
>
>  #ifdef CONFIG_FPU
>  extern void __fstate_save(struct task_struct *save_to);
> @@ -119,6 +120,8 @@ do {                                                        \
>                 __switch_to_fpu(__prev, __next);        \
>         if (has_vector() || has_xtheadvector())         \
>                 __switch_to_vector(__prev, __next);     \
> +       if (has_srmcfg())                               \
> +               __switch_to_srmcfg(__next);             \
>         if (switch_to_should_flush_icache(__next))      \
>                 local_flush_icache_all();               \
>         __switch_to_envcfg(__next);                     \
> diff --git a/arch/riscv/kernel/Makefile b/arch/riscv/kernel/Makefile
> index cabb99cadfb6d1e1284d6b4e9ae76044d36949f5..ebe1c3588177b4b825a52af9ca17e17b5561427c 100644
> --- a/arch/riscv/kernel/Makefile
> +++ b/arch/riscv/kernel/Makefile
> @@ -128,3 +128,5 @@ obj-$(CONFIG_ACPI_NUMA)     += acpi_numa.o
>
>  obj-$(CONFIG_GENERIC_CPU_VULNERABILITIES) += bugs.o
>  obj-$(CONFIG_RISCV_USER_CFI) += usercfi.o
> +
> +obj-$(CONFIG_RISCV_ISA_SSQOSID) += qos.o
> diff --git a/arch/riscv/kernel/qos.c b/arch/riscv/kernel/qos.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..ea33201a43f61534bf28b9c02b62801f30f62154
> --- /dev/null
> +++ b/arch/riscv/kernel/qos.c
> @@ -0,0 +1,98 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +#include <linux/cpu.h>
> +#include <linux/cpu_pm.h>
> +#include <linux/cpuhotplug.h>
> +#include <linux/notifier.h>
> +#include <linux/percpu-defs.h>
> +#include <linux/types.h>
> +
> +#include <asm/cpufeature-macros.h>
> +#include <asm/hwcap.h>
> +#include <asm/qos.h>
> +
> +/*
> + * Cached value of srmcfg csr for each cpu. Seeded to U32_MAX so the next
> + * __switch_to_srmcfg() unconditionally writes the CSR. The encoding
> + * MCID << 16 | RCID with both fields well under 16 bits can never
> + * produce this sentinel. This covers early-boot context switches that
> + * happen before riscv_srmcfg_init() runs as an arch_initcall.
> + */
> +DEFINE_PER_CPU(u32, cpu_srmcfg) = U32_MAX;
> +
> +/* default srmcfg value for each cpu, set via resctrl cpu assignment */
> +DEFINE_PER_CPU(u32, cpu_srmcfg_default);
> +
> +/*
> + * Invalidate the per-CPU srmcfg cache, used as both the cpuhp startup and
> + * teardown callback. The sentinel is a value no real srmcfg encoding can
> + * produce (MCID << 16 | RCID, both fields well under 16 bits) so the next
> + * __switch_to_srmcfg() unconditionally writes the CSR.
> + *
> + * Ssqosid v1.0 leaves CSR state across hart stop/start implementation-
> + * defined, so the cached value cannot be trusted after online. Invalidating
> + * on offline as well means the sentinel persists across the offline period:
> + * a CPU brought back online finds the cache already invalidated before it is
> + * schedulable, closing the window where a task scheduled before the startup
> + * callback runs could match a stale cache and skip the CSR write while the
> + * hardware CSR was reset across hart stop/start.
> + */
> +static int riscv_srmcfg_reset_cache(unsigned int cpu)
> +{
> +       per_cpu(cpu_srmcfg, cpu) = U32_MAX;
> +       return 0;
> +}
> +
> +/*
> + * CPU PM notifier: invalidate the cached srmcfg on resume from a deep
> + * idle / suspend. Ssqosid v1.0 leaves CSR_SRMCFG state across low-power
> + * transitions implementation-defined, and the boot CPU never goes
> + * through the cpuhp online callback during system suspend, so without
> + * this hook __switch_to_srmcfg() would skip the CSR write when the
> + * outgoing task happens to share its srmcfg with the pre-suspend cache.
> + */
> +static int riscv_srmcfg_pm_notify(struct notifier_block *nb,
> +                                 unsigned long action, void *unused)
> +{
> +       switch (action) {
> +       case CPU_PM_EXIT:
> +       case CPU_PM_ENTER_FAILED:
> +               /*
> +                * The CSR is implementation-defined across the low-power
> +                * transition. Invalidate the cache and eagerly rewrite the
> +                * CSR for the current task so it does not run mis-tagged
> +                * until the next context switch.
> +                */
> +               __this_cpu_write(cpu_srmcfg, U32_MAX);
> +               __switch_to_srmcfg(current);
> +               break;
> +       }
> +       return NOTIFY_OK;
> +}
> +
> +static struct notifier_block riscv_srmcfg_pm_nb = {
> +       .notifier_call = riscv_srmcfg_pm_notify,
> +};
> +
> +static int __init riscv_srmcfg_init(void)
> +{
> +       int err;
> +
> +       if (!riscv_has_extension_unlikely(RISCV_ISA_EXT_SSQOSID))
> +               return 0;
> +
> +       /*
> +        * cpuhp_setup_state() invokes the startup callback locally on every
> +        * already-online CPU, so no separate seed loop is needed here.
> +        */
> +       err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "riscv/srmcfg:online",
> +                               riscv_srmcfg_reset_cache, riscv_srmcfg_reset_cache);
> +       if (err < 0) {
> +               pr_warn("srmcfg cpuhp registration failed (%d), cpus brought online after boot will not invalidate the CSR_SRMCFG cache\n",
> +                       err);
> +               return err;
> +       }
> +
> +       cpu_pm_register_notifier(&riscv_srmcfg_pm_nb);
> +       return 0;
> +}
> +arch_initcall(riscv_srmcfg_init);
>
> --
> 2.34.1
>

Thanks,
Yunhui

^ permalink raw reply

* Re: [RFC PATCH net-next v8 03/12] net: phylink: add phylink_release_pcs() to externally release a PCS
From: Christian Marangi @ 2026-06-27 12:33 UTC (permalink / raw)
  To: Maxime Chevallier
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Simon Horman, Jonathan Corbet, Shuah Khan, Lorenzo Bianconi,
	Heiner Kallweit, Russell King, Saravana Kannan, Philipp Zabel,
	Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt,
	netdev, devicetree, linux-kernel, linux-doc, linux-arm-kernel,
	linux-mediatek, llvm
In-Reply-To: <a271385e-302d-45c7-a1df-aebd380b427b@bootlin.com>

On Thu, Jun 25, 2026 at 04:13:14PM +0200, Maxime Chevallier wrote:
> Hello Christian,
> 
> On 6/18/26 14:57, Christian Marangi wrote:
> > Add phylink_release_pcs() to externally release a PCS from a phylink
> > instance. This can be used to handle case when a single PCS needs to be
> > removed and the phylink instance needs to be refreshed.
> > 
> > On calling phylink_release_pcs(), the PCS will be removed from the
> > phylink internal PCS list and the phylink supported_interfaces value is
> > reparsed with the remaining PCS interfaces.
> > 
> > Also a phylink resolve is triggered to handle the PCS removal.
> > 
> > The flag force_major_config is set to make phylink resolve reconfigure
> > the interface (even if it didn't change).
> > This is needed to handle the special case when the current PCS used
> > by phylink is removed and a major_config is needed to propagae the
> > configuration change. With this option enabled we also force mac_config
> > even if the PHY link is not up for the in-band case.
> > 
> > Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
> > ---
> >  drivers/net/phy/phylink.c | 56 +++++++++++++++++++++++++++++++++++++++
> >  include/linux/phylink.h   |  2 ++
> >  2 files changed, 58 insertions(+)
> > 
> > diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
> > index c38bcd43b8c8..064d6f5a06da 100644
> > --- a/drivers/net/phy/phylink.c
> > +++ b/drivers/net/phy/phylink.c
> > @@ -158,6 +158,8 @@ static const phy_interface_t phylink_sfp_interface_preference[] = {
> >  static DECLARE_PHY_INTERFACE_MASK(phylink_sfp_interfaces);
> >  
> >  static void phylink_run_resolve(struct phylink *pl);
> > +static void phylink_link_down(struct phylink *pl);
> > +static void phylink_pcs_disable(struct phylink_pcs *pcs);
> >  
> >  /**
> >   * phylink_set_port_modes() - set the port type modes in the ethtool mask
> > @@ -918,6 +920,60 @@ static void phylink_resolve_an_pause(struct phylink_link_state *state)
> >  	}
> >  }
> >  
> > +/**
> > + * phylink_release_pcs - Removes a PCS from the phylink PCS available list
> > + * @pcs: a pointer to the phylink_pcs struct to be released
> > + *
> > + * This function release a PCS from the phylink PCS available list if
> > + * actually in use. It also refreshes the supported interfaces of the
> > + * phylink instance by copying the supported interfaces from the phylink
> > + * conf and merging the supported interfaces of the remaining available PCS
> > + * in the list and trigger a resolve.
> > + */
> > +void phylink_release_pcs(struct phylink_pcs *pcs)
> > +{
> > +	struct phylink *pl;
> > +
> > +	ASSERT_RTNL();
> > +
> > +	pl = pcs->phylink;
> > +	if (!pl)
> > +		return;
> > +
> > +	mutex_lock(&pl->state_mutex);
> > +
> > +	list_del(&pcs->list);
> > +	pcs->phylink = NULL;
> > +
> > +	/*
> > +	 * Check if we are removing the PCS currently
> > +	 * in use by phylink. If this is the case, tear down
> > +	 * the link, force phylink resolve to reconfigure the
> > +	 * interface mode, disable the current PCS and set the
> > +	 * phylink PCS to NULL.
> > +	 */
> > +	if (pl->pcs == pcs) {
> > +		phylink_link_down(pl);
> > +		phylink_pcs_disable(pl->pcs);
> > +
> > +		pl->force_major_config = true;
> > +		pl->pcs = NULL;
> > +	}
> > +
> > +	mutex_unlock(&pl->state_mutex);
> > +
> > +	/* Refresh supported interfaces */
> > +	phy_interface_copy(pl->supported_interfaces,
> > +			   pl->config->supported_interfaces);
> > +	list_for_each_entry(pcs, &pl->pcs_list, list)
> > +		phy_interface_or(pl->supported_interfaces,
> > +				 pl->supported_interfaces,
> > +				 pcs->supported_interfaces);
> 
> I've given more thought to that 'supported_interfaces' thing. This
> patchset redefines the meaning of
> 
>   pl->config->supported_interfaces
> 
> Currently, it's filled by the MAC driver and means "Every interface
> we can support, including the ones provided by PCSs that we can use
> with this MAC".
> 
> It now becomes "Every interface we support without needing a PCS", at
> least the way I understand that.
>

Wait but with the current code using the OR logic, it still follows
"Every interface we can support...". The modes that needs a PCS are
specificed with the pcs_interfaces mask in phylink_config.

The late add and release operates on the phylink supported_interfaces ONLY
when the MAC didn't specify support for it (by removing it as only the PCS
will declare support for it)

The confusion is present because everything is validated later on
major_config so those supported_interfaces are just an HINT that are later
verified with get_caps and with the pcs_validate OPs.

Adding the supported_interfaces to phylink is really to keep an original
reference of the value. This is to address a pattern I have notice where
the MAC driver always OR the interfaces with the one supported by the PCS.
(I remember it was pointed out by Russell)

But I'm more than open to discussion as this is something marginal to the
whole implementation, I'm also questioning if this OR is actually useful to
anything on the nth tought on this.

One thing that I notice is that parsing this early with AND might be
problematic at phylink_create, but I still have to evaluate that.

My take is that would be good to have some review also on the other logic
as I think I reached a point where Sashiko starts to comments on more or
less unreal problem.

> It's not an error in your code, but I think this is worth documenting
> somewhere as this changes one the things that's already fairly
> error-prone in new drivers.
> 
> I don't know to what extent people use that, be we have a porting guide
> that explains how to use phylink in a MAC driver, maybe an update in there
> would be nice as well :
> 
> https://docs.kernel.org/networking/sfp-phylink.html#rough-guide-to-converting-a-network-driver-to-sfp-phylink
> 
> Maxime
> 
> 

-- 
	Ansuel

^ permalink raw reply

* Re: [RFC PATCH 00/40] mm: reliable 1GB page allocation
From: Rik van Riel @ 2026-06-27 13:36 UTC (permalink / raw)
  To: Lorenzo Stoakes
  Cc: linux-kernel, kernel-team, linux-mm, david, willy, surenb, hannes,
	ziy, usama.arif, fvdl, Andrew Morton, Jonathan Corbet,
	Chris Mason, David Sterba, Vlastimil Babka, Steven Rostedt,
	Masami Hiramatsu, Rafael J. Wysocki, Oscar Salvador,
	Mike Rapoport, linux-doc, linux-btrfs, linux-trace-kernel,
	linux-pm, linux-cxl, Linus Torvalds
In-Reply-To: <aj9yrlB0TrlYCLlf@lucifer>

On Sat, 2026-06-27 at 10:28 +0100, Lorenzo Stoakes wrote:
> 
> So really, you need to start again, from scratch, and without the use
> of an
> LLM for generating code, or at least with it kept on a (very very
> short)
> leash.
> 
> And to be clear, I _want_ this concept of GB superpageblocks to land.
> It's
> a really exciting concept.

That is the one reason I sent out RFC code before it
is ready. I am looking for feedback on the concepts
in this series.

How do people feel about splitting up the free lists,
so each gigabyte (well, PUD sized) chunk of memory
has its own free lists?

How can we balance the desire for higher-order kernel
allocations, against the desire to preserve gigabyte
sized chunks of memory that can be used for user space?

> 
> Pulling compaction kicking and screaming into 2026 stands to
> significantly
> benefit linux users and developers.

That's another big question. How do we balance the
desire to keep compaction overhead low with the desire
to do higher order allocations almost everywhere?

> 
> But the execution has to be _completely_ rethought.

There's no argument there.

I am just hoping to figure out what I should be
doing on a conceptual level, before figuring out
how to do it cleanly.

The mess in the RFC is the result of trying something
that seemed right, watching it fail in some subtle
way, and trying to fix it up.

Once I know what I need to do, coming up with a
cleaner implementation is very doable.

> 
> IOW I say we take off and nuke the entire site from orbit. It's the
> only
> way to be sure :)
> 
BOOM?

> Another issue here is maintainer time - even this _extremely_ light-
> touch
> review has taken me a few hours (of my weekend :). To review it in
> detail
> would take probably DAYS of dedicated work.

I suspect there is a mismatch in expectations here.

I already knew this code has to be totally redone.

I was looking for feedback on the basic concepts
and design in the patch series, but failed to
clearly communicate that.

You provided some detailed feedback on the code,
but as of yet nobody has really provided any
opinions on things like whether it is desirable
at all to have the free lists per gigablock,
or whether we need to come up with some totally
different approach.

How do we better communicate that kind of thing
in the future?

Is that something to spell out more clearly in
the cover letter?

Is that kind of feedback something developers
could even reasonably ask for? (if not, how do
we figure out what maintainers want?)


-- 
All Rights Reversed.

^ permalink raw reply

* Re: [PATCH] KVM: x86/xen: Add KVM_XEN_VCPU_ATTR_TYPE_WRITE_HYPERCALL_PAGE
From: David Woodhouse @ 2026-06-27 13:58 UTC (permalink / raw)
  To: Sean Christopherson
  Cc: Gerd Hoffmann, Paolo Bonzini, Jonathan Corbet, Shuah Khan,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Paul Durrant, kvm, linux-doc, linux-kernel,
	linux-kselftest
In-Reply-To: <aj6BSMT2LOd4JRpu@google.com>

[-- Attachment #1: Type: text/plain, Size: 3985 bytes --]

On Fri, 2026-06-26 at 06:40 -0700, Sean Christopherson wrote:
> On Fri, Jun 26, 2026, David Woodhouse wrote:
> > On Thu, 2026-06-25 at 16:09 -0700, Sean Christopherson wrote:
> > > > diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c
> > > > index 91fd3673c09a..c16b4560c9e7 100644
> > > > --- a/arch/x86/kvm/xen.c
> > > > +++ b/arch/x86/kvm/xen.c
> > > > @@ -907,6 +907,13 @@ int kvm_xen_vcpu_set_attr(struct kvm_vcpu *vcpu, struct kvm_xen_vcpu_attr *data)
> > > >  {
> > > >  	int idx, r = -ENOENT;
> > > >  
> > > > +	/*
> > > > +	 * kvm_xen_write_hypercall_page() manages its own locking.
> > > > +	 * Handle it before taking xen_lock to avoid a deadlock.
> > > 
> > > Do we actually want the side effects that necessitate taking xen.xen_lock?  From
> > > a uAPI perspective, it's odd to effectively bundle KVM_XEN_ATTR_TYPE_LONG_MODE
> > > into KVM_XEN_VCPU_ATTR_TYPE_WRITE_HYPERCALL_PAGE.
> > 
> > That's *guest* ABI, and it's derived from Xen behaviour. Xen will
> > 'latch' its idea of whether a guest VM is 32-bit or 64-bit, for the
> > purpose of shared data structures (shared_info page, vcpu_info,
> > runstate).
> > 
> > Xen latches this from the current mode of the running vCPU in *two*
> > places:
> >  • When the hypercall MSR is invoked
> >  • When the guest sets the event channel GSI (HVM_PARAM_CALLBACK_IRQ).
> > 
> > Thus far, the former has been handled in the kernel (in the code you're
> > looking at), while the latter is why we have the ioctl to explicitly
> > latch the guest's long_mode from userspace too, as userspace handles
> > the HVMOP_set_param calls.
> 
> Right, and I'm pointing out that from a KVM uAPI perspective, bundling the first
> one in a "write hypercall page" call is rather odd, especially since there's
> already uAPI to handle the latching.

Mostly just because they came in the opposite order. The kernel was
writing the hypercall page for itself for about a decade before there
was anything to latch. And userspace wasn't *involved* when it happened
(initially because there was no facility for userspace intercepting MSR
writes).

> > > The other question is, why does kvm_xen_write_hypercall_page() drop xen_lock
> > > when writing guest memory?  That seems odd and unnecessary.
> > 
> > Huh? It takes the lock to do the thing that needs the lock, then drops
> > it. That is not "odd and unnecessary" at all.
> > 
> > You've been spending too long with these scope-guarded locks.
> 
> No, I'm asking why KVM doesn't serialize the writes to guest memory.  Usually
> when KVM writes to guest memory, KVM is emulating something that is very much
> vCPU-specific, and so if there are races it's the guest's problem to deal with.
> 
> The Xen MSR here is clearly VM-scoped though, which is why it feels odd to take
> a per-VM lock, and then deliberately drop the lock before completing the operation,
> In practice it shouldn't matter, since it sounds like the same repeating 16 byte
> pattern will be written every time, but it was a bit head-scratching when reading
> the code.

The lock protects the long_mode latching. It wouldn't make sense to
hold it around the actual writing of the hypercall page. It's not
intended to be an *atomic* write, and holding some random lock around
it wouldn't make it atomic with respect to all the other things that
can write guest memory anyway.

> We can/should return whatever kvm_vcpu_write_guest() returns, i.e. literally
> return its result directly.  Which of course is only ever going to be -EFAULT,
> but in the extremely unlikely case that ever changes, we won't have to worry
> about creating misleading behavior in the Xen code.

Yes, and if we do it with a wrapper as you suggested, we can return a
proper error code and the MSR handler can convert that to 0/1.

As it is, we were doing the conversion the other way round, which is
why we were making up error numbers for the failure cause.

[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]

^ permalink raw reply

* Re: [PATCH] Documentation: device-mapper: adopt new coding style of type-aware kmalloc-family
From: Jonathan Corbet @ 2026-06-27 16:24 UTC (permalink / raw)
  To: Manuel Ebner, Alasdair Kergon, Mike Snitzer, Mikulas Patocka,
	Benjamin Marzinski, Shuah Khan, Kees Cook,
	open list:DEVICE-MAPPER  (LVM), open list:DOCUMENTATION,
	open list
  Cc: Manuel Ebner
In-Reply-To: <20260627091506.29534-2-manuelebner@mailbox.org>

Manuel Ebner <manuelebner@mailbox.org> writes:

> Change the Documentation to reflect this commit 69050f8d6d07 ("treewide: Replace
> kmalloc with kmalloc_obj for non-scalar types")
> kmalloc -> kmalloc_objs
>
> Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
> ---
>  Documentation/admin-guide/device-mapper/statistics.rst | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/Documentation/admin-guide/device-mapper/statistics.rst b/Documentation/admin-guide/device-mapper/statistics.rst
> index 41ded0bc5933..2190b90118f8 100644
> --- a/Documentation/admin-guide/device-mapper/statistics.rst
> +++ b/Documentation/admin-guide/device-mapper/statistics.rst
> @@ -30,7 +30,7 @@ region, etc.  Unique region_ids enable multiple userspace programs to
>  request and process statistics for the same DM device without stepping
>  on each other's data.
>  
> -The creation of DM statistics will allocate memory via kmalloc or
> +The creation of DM statistics will allocate memory via kmalloc_objs or

A nit, but if you're going to fix the function name, why not make it
kmalloc_objs() so that the automarkup code can do its magic?

Thanks,

jon

^ permalink raw reply

* Re: [PATCH] docs: infiniband: fix bracket
From: Randy Dunlap @ 2026-06-27 18:04 UTC (permalink / raw)
  To: Manuel Ebner, Jason Gunthorpe, Leon Romanovsky, Jonathan Corbet,
	Shuah Khan, open list:INFINIBAND SUBSYSTEM,
	open list:DOCUMENTATION, open list
In-Reply-To: <20260627093107.31068-2-manuelebner@mailbox.org>



On 6/27/26 2:31 AM, Manuel Ebner wrote:
> Remove needless ')'.
> 
> Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>

Acked-by: Randy Dunlap <rdunlap@infradead.org>
Thanks.

> ---
>  Documentation/infiniband/user_mad.rst | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/Documentation/infiniband/user_mad.rst b/Documentation/infiniband/user_mad.rst
> index d88abfc0e370..cd66e7623d66 100644
> --- a/Documentation/infiniband/user_mad.rst
> +++ b/Documentation/infiniband/user_mad.rst
> @@ -62,7 +62,7 @@ Receiving MADs
>  	struct ib_user_mad *mad;
>  	mad = malloc(sizeof *mad + 256);
>  	ret = read(fd, mad, sizeof *mad + 256);
> -	if (ret == -ENOSPC)) {
> +	if (ret == -ENOSPC) {
>  		length = mad.length;
>  		free(mad);
>  		mad = malloc(sizeof *mad + length);

-- 
~Randy

^ permalink raw reply

* Re: [PATCH] docs/mm: Fix brackets
From: Randy Dunlap @ 2026-06-27 18:05 UTC (permalink / raw)
  To: Manuel Ebner, Jason Gunthorpe, Leon Romanovsky, Andrew Morton,
	David Hildenbrand, Lorenzo Stoakes, Liam R . Howlett,
	Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
	Jonathan Corbet, Shuah Khan, Shakeel Butt,
	open list:HMM - Heterogeneous Memory Management,
	open list:DOCUMENTATION, open list
In-Reply-To: <20260627093258.31265-2-manuelebner@mailbox.org>



On 6/27/26 2:32 AM, Manuel Ebner wrote:
> Remove unnecessary ')' and missing '('.
> Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>

Acked-by: Randy Dunlap <rdunlap@infradead.org>
Thanks.

> ---
>  Documentation/mm/hmm.rst           | 4 ++--
>  Documentation/mm/process_addrs.rst | 2 +-
>  2 files changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/Documentation/mm/hmm.rst b/Documentation/mm/hmm.rst
> index 7d61b7a8b65b..54c461e7a143 100644
> --- a/Documentation/mm/hmm.rst
> +++ b/Documentation/mm/hmm.rst
> @@ -191,7 +191,7 @@ like a CPU page fault. The usage pattern is::
>        mmap_read_unlock(mm);
>  
>        take_lock(driver->update);
> -      if (mmu_interval_read_retry(&ni, range.notifier_seq) {
> +      if (mmu_interval_read_retry(&ni, range.notifier_seq)) {
>            release_lock(driver->update);
>            goto again;
>        }
> @@ -316,7 +316,7 @@ between device driver specific code and shared common code:
>     system memory and device private memory.
>  
>     One of the first steps migrate_vma_setup() does is to invalidate other
> -   device's MMUs with the ``mmu_notifier_invalidate_range_start(()`` and
> +   device's MMUs with the ``mmu_notifier_invalidate_range_start()`` and
>     ``mmu_notifier_invalidate_range_end()`` calls around the page table
>     walks to fill in the ``args->src`` array with PFNs to be migrated.
>     The ``invalidate_range_start()`` callback is passed a
> diff --git a/Documentation/mm/process_addrs.rst b/Documentation/mm/process_addrs.rst
> index 851680ead45f..b391502fbfd6 100644
> --- a/Documentation/mm/process_addrs.rst
> +++ b/Documentation/mm/process_addrs.rst
> @@ -724,7 +724,7 @@ the zap and the invocation of :c:func:`!free_pgtables`.
>  
>  Since it is assumed that all such steps have been taken, page table entries are
>  cleared without page table locks (in the :c:func:`!pgd_clear`, :c:func:`!p4d_clear`,
> -:c:func:`!pud_clear`, and :c:func:`!pmd_clear` functions.
> +:c:func:`!pud_clear`, and :c:func:`!pmd_clear` functions).
>  
>  .. note:: It is possible for leaf page tables to be torn down independent of
>            the page tables above it as is done by

-- 
~Randy

^ permalink raw reply

* Re: [PATCH] docs: kernel-hacking: fix typo
From: Randy Dunlap @ 2026-06-27 18:09 UTC (permalink / raw)
  To: Manuel Ebner, Jonathan Corbet, Shuah Khan,
	open list:DOCUMENTATION, open list
In-Reply-To: <20260627091603.29612-2-manuelebner@mailbox.org>

Hi,

On 6/27/26 2:16 AM, Manuel Ebner wrote:
> GP_KERNEL -> GFP_KERNEL
> 
> Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
> ---
>  Documentation/kernel-hacking/locking.rst | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/Documentation/kernel-hacking/locking.rst b/Documentation/kernel-hacking/locking.rst
> index c969c76ef7cb..9c6c76d3a723 100644
> --- a/Documentation/kernel-hacking/locking.rst
> +++ b/Documentation/kernel-hacking/locking.rst
> @@ -1317,7 +1317,7 @@ from user context, and can sleep.
>  
>     -  put_user()
>  
> --  kmalloc(GP_KERNEL) <kmalloc>`
> +-  kmalloc(GFP_KERNEL) <kmalloc>`

Please drop the trailing ` mark.
I have no idea what it was meant to be, but it adds nothing
to the rendered HTML output.
(I also have no idea why the trailing <kmalloc> is there.)

But the GFP_KERNEL change is good.

>  
>  -  mutex_lock_interruptible() and
>     mutex_lock()

-- 
~Randy


^ permalink raw reply

* Re: [PATCH] docs: watchdog: Fix brackets
From: Randy Dunlap @ 2026-06-27 18:11 UTC (permalink / raw)
  To: Manuel Ebner, Wim Van Sebroeck, Guenter Roeck, Jonathan Corbet,
	Shuah Khan, open list:WATCHDOG DEVICE DRIVERS,
	open list:DOCUMENTATION, open list
In-Reply-To: <20260627091707.29688-2-manuelebner@mailbox.org>



On 6/27/26 2:17 AM, Manuel Ebner wrote:
> Add missing brackets ')'.
> 
> Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>

Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Thanks.

> ---
>  Documentation/watchdog/watchdog-parameters.rst | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/Documentation/watchdog/watchdog-parameters.rst b/Documentation/watchdog/watchdog-parameters.rst
> index 2359aa32e25d..8e2df5b7d241 100644
> --- a/Documentation/watchdog/watchdog-parameters.rst
> +++ b/Documentation/watchdog/watchdog-parameters.rst
> @@ -59,7 +59,7 @@ advantechwdt:
>  
>  alim1535_wdt:
>      timeout:
> -	Watchdog timeout in seconds. (0 < timeout < 18000, default=60
> +	Watchdog timeout in seconds. (0 < timeout < 18000, default=60)
>      nowayout:
>  	Watchdog cannot be stopped once started
>  	(default=kernel config parameter)
> @@ -68,7 +68,7 @@ alim1535_wdt:
>  
>  alim7101_wdt:
>      timeout:
> -	Watchdog timeout in seconds. (1<=timeout<=3600, default=30
> +	Watchdog timeout in seconds. (1<=timeout<=3600, default=30)
>      use_gpio:
>  	Use the gpio watchdog (required by old cobalt boards).
>  	default=0/off/no

-- 
~Randy

^ permalink raw reply

* Re: [PATCH] Documentation: parport-lowlevel: Fix curly bracket
From: Randy Dunlap @ 2026-06-27 18:13 UTC (permalink / raw)
  To: Manuel Ebner, Sudip Mukherjee, Jonathan Corbet, Shuah Khan,
	Andreas Faerber, Manivannan Sadhasivam, open list:DOCUMENTATION,
	moderated list:ARM/ACTIONS SEMI ARCHITECTURE,
	moderated list:ARM/ACTIONS SEMI ARCHITECTURE, open list
In-Reply-To: <20260627092359.30044-3-manuelebner@mailbox.org>



On 6/27/26 2:24 AM, Manuel Ebner wrote:
> Remove needless '}'
> 
> Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>

Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
Thanks.

> ---
>  Documentation/driver-api/parport-lowlevel.rst | 1 -
>  1 file changed, 1 deletion(-)
> 
> diff --git a/Documentation/driver-api/parport-lowlevel.rst b/Documentation/driver-api/parport-lowlevel.rst
> index a907e279f509..af73741289c9 100644
> --- a/Documentation/driver-api/parport-lowlevel.rst
> +++ b/Documentation/driver-api/parport-lowlevel.rst
> @@ -341,7 +341,6 @@ EXAMPLE
>  			/* Try to detect a device on the port... */
>  			...
>  		}
> -		}
>  
>  		...
>  	}

-- 
~Randy

^ permalink raw reply

* Re: [PATCH net-deletions] net: remove ax25 and amateur radio (hamradio) subsystem
From: Jiri Slaby @ 2026-06-27 19:04 UTC (permalink / raw)
  To: Jakub Kicinski, davem
  Cc: netdev, edumazet, pabeni, andrew+netdev, horms, corbet, skhan,
	federico.vaga, carlos.bilbao, avadhut.naik, alexs, si.yanteng,
	dzm91, 2023002089, tsbogend, dsahern, jani.nikula, mchehab+huawei,
	gregkh, tytso, herbert, ebiggers, johannes.berg, geert, pablo,
	tglx, mashiro.chen, mingo, dqfext, jreuter, sdf, pkshih,
	enelsonmoore, mkl, toke, kees, crossd, jlayton, wangliang74,
	aha310510, takamitz, kuniyu, linux-doc, linux-mips
In-Reply-To: <20260421021824.1293976-1-kuba@kernel.org>

On 21. 04. 26, 4:18, Jakub Kicinski wrote:
> Remove the amateur radio (AX.25, NET/ROM, ROSE) protocol implementation
> and all associated hamradio device drivers from the kernel tree.
> This set of protocols has long been a huge bug/syzbot magnet,
> and since nobody stepped up to help us deal with the influx
> of the AI-generated bug reports we need to move it out of tree
> to protect our sanity.
> 
> The code is moved to an out-of-tree repo:
> https://github.com/linux-netdev/mod-orphan
> if it's cleaned up and reworked there we can accept it back.
> 
> Minimal stub headers are kept for include/net/ax25.h (AX25_P_IP,
> AX25_ADDR_LEN, ax25_address) and include/net/rose.h (ROSE_ADDR_LEN)
> so that the conditional integration code in arp.c and tun.c continues
> to compile and work when the out-of-tree modules are loaded.
...
>   delete mode 100644 include/uapi/linux/scc.h
Unfortunately, this broke builds of LLVM -- compiler-rt in particular 
(and GCC builds allegedly too). They dropped the include and its use 
[1], but IMO we should keep the uapi header with those two structs 
(scc_modem + scc_stat) for some time.

[1] 
https://github.com/llvm/llvm-project/commit/3dc4fd6dd41100f051a63642f449b16324389c96

thanks,
-- 
js
suse labs

^ permalink raw reply

* [PATCH 1/2] kernel/entry: add CONFIG_SYSCALL_USER_DISPATCH to compile SUD out
From: Gregory Price @ 2026-06-27 20:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-doc, kernel-team, corbet, skhan, tglx, peterz, luto, akpm,
	feng.tang, pmladek, mhiramat, marc.herbert, joel.granados, gourry,
	lirongqing, kees, nathan, linusw, arnd, deller, jpoimboe,
	ruanjinjie, lukas.bulwahn, ryan.roberts, ojeda

Syscall User Dispatch is built under CONFIG_GENERIC_SYSCALL and cannot
be disabled independent of the core syscall-entry machinery.

Native foreign-binary emulators (Wine/Proton) need it, but it should
be an optional for minimal/high security systems.

Add CONFIG_SYSCALL_USER_DISPATCH to make it optional.

Signed-off-by: Gregory Price <gourry@gourry.net>
---
 arch/Kconfig                                | 11 ++++++++
 include/linux/entry-common.h                |  6 ++---
 include/linux/syscall_user_dispatch.h       | 28 +++++++++++++++++++--
 include/linux/syscall_user_dispatch_types.h |  2 +-
 kernel/entry/Makefile                       |  3 ++-
 5 files changed, 42 insertions(+), 8 deletions(-)

diff --git a/arch/Kconfig b/arch/Kconfig
index e86880045158..a40686e2ad5b 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -114,6 +114,17 @@ config GENERIC_ENTRY
 	select GENERIC_IRQ_ENTRY
 	select GENERIC_SYSCALL
 
+config SYSCALL_USER_DISPATCH
+	bool "Syscall User Dispatch (SUD)"
+	depends on GENERIC_ENTRY
+	default y
+	help
+	  Syscall User Dispatch (SUD) lets a thread have its own system calls
+	  redirected to a userspace handler.  It is used by emulators that run
+	  foreign binaries which issue system calls directly.
+
+	  If unsure, say Y.
+
 config KPROBES
 	bool "Kprobes"
 	depends on HAVE_KPROBES
diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h
index 416a3352261f..9336516430a1 100644
--- a/include/linux/entry-common.h
+++ b/include/linux/entry-common.h
@@ -9,6 +9,7 @@
 #include <linux/resume_user_mode.h>
 #include <linux/seccomp.h>
 #include <linux/sched.h>
+#include <linux/syscall_user_dispatch.h>
 
 #include <asm/entry-common.h>
 #include <asm/syscall.h>
@@ -55,7 +56,6 @@ static __always_inline int arch_ptrace_report_syscall_entry(struct pt_regs *regs
 }
 #endif
 
-bool syscall_user_dispatch(struct pt_regs *regs);
 long trace_syscall_enter(struct pt_regs *regs, long syscall);
 void trace_syscall_exit(struct pt_regs *regs, long ret);
 
@@ -232,10 +232,8 @@ static __always_inline void syscall_exit_work(struct pt_regs *regs, unsigned lon
 	 * of these syscalls is unknown.
 	 */
 	if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) {
-		if (unlikely(current->syscall_dispatch.on_dispatch)) {
-			current->syscall_dispatch.on_dispatch = false;
+		if (syscall_user_dispatch_clear_on_dispatch())
 			return;
-		}
 	}
 
 	audit_syscall_exit(regs);
diff --git a/include/linux/syscall_user_dispatch.h b/include/linux/syscall_user_dispatch.h
index 3858a6ffdd5c..3dcb4c2dc544 100644
--- a/include/linux/syscall_user_dispatch.h
+++ b/include/linux/syscall_user_dispatch.h
@@ -7,8 +7,22 @@
 
 #include <linux/thread_info.h>
 #include <linux/syscall_user_dispatch_types.h>
+#include <linux/sched.h>
 
-#ifdef CONFIG_GENERIC_ENTRY
+struct pt_regs;
+
+#ifdef CONFIG_SYSCALL_USER_DISPATCH
+
+bool syscall_user_dispatch(struct pt_regs *regs);
+
+static inline bool syscall_user_dispatch_clear_on_dispatch(void)
+{
+	if (likely(!current->syscall_dispatch.on_dispatch))
+		return false;
+
+	current->syscall_dispatch.on_dispatch = false;
+	return true;
+}
 
 int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
 			      unsigned long len, char __user *selector);
@@ -24,6 +38,16 @@ int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long siz
 
 #else
 
+static inline bool syscall_user_dispatch(struct pt_regs *regs)
+{
+	return false;
+}
+
+static inline bool syscall_user_dispatch_clear_on_dispatch(void)
+{
+	return false;
+}
+
 static inline int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
 					    unsigned long len, char __user *selector)
 {
@@ -46,6 +70,6 @@ static inline int syscall_user_dispatch_set_config(struct task_struct *task,
 	return -EINVAL;
 }
 
-#endif /* CONFIG_GENERIC_ENTRY */
+#endif /* CONFIG_SYSCALL_USER_DISPATCH */
 
 #endif /* _SYSCALL_USER_DISPATCH_H */
diff --git a/include/linux/syscall_user_dispatch_types.h b/include/linux/syscall_user_dispatch_types.h
index 3be36b06c7d7..c0bdd4f760d3 100644
--- a/include/linux/syscall_user_dispatch_types.h
+++ b/include/linux/syscall_user_dispatch_types.h
@@ -4,7 +4,7 @@
 
 #include <linux/types.h>
 
-#ifdef CONFIG_GENERIC_ENTRY
+#ifdef CONFIG_SYSCALL_USER_DISPATCH
 
 struct syscall_user_dispatch {
 	char __user	*selector;
diff --git a/kernel/entry/Makefile b/kernel/entry/Makefile
index 2333d70802e4..f220bae86b12 100644
--- a/kernel/entry/Makefile
+++ b/kernel/entry/Makefile
@@ -13,5 +13,6 @@ CFLAGS_REMOVE_common.o	 = -fstack-protector -fstack-protector-strong
 CFLAGS_common.o		+= -fno-stack-protector
 
 obj-$(CONFIG_GENERIC_IRQ_ENTRY) 	+= common.o
-obj-$(CONFIG_GENERIC_SYSCALL) 		+= syscall-common.o syscall_user_dispatch.o
+obj-$(CONFIG_GENERIC_SYSCALL) 		+= syscall-common.o
+obj-$(CONFIG_SYSCALL_USER_DISPATCH)	+= syscall_user_dispatch.o
 obj-$(CONFIG_VIRT_XFER_TO_GUEST_WORK)	+= virt.o
-- 
2.54.0


^ permalink raw reply related

* [PATCH 2/2] kernel/entry: add kernel.syscall_user_dispatch sysctl
From: Gregory Price @ 2026-06-27 20:55 UTC (permalink / raw)
  To: linux-kernel
  Cc: linux-doc, kernel-team, corbet, skhan, tglx, peterz, luto, akpm,
	feng.tang, pmladek, mhiramat, marc.herbert, joel.granados, gourry,
	lirongqing, kees, nathan, linusw, arnd, deller, jpoimboe,
	ruanjinjie, lukas.bulwahn, ryan.roberts, ojeda
In-Reply-To: <20260627205551.769684-1-gourry@gourry.net>

Add a matching sysctl to go with CONFIG_SYSCALL_USER_DISPATCH.

kernel.syscall_user_dispatch (default 1) controls whether userspace
may arm SUD (both via prctl and ptrace).

Disarming is always permitted - same semantics as comparable knobs

Signed-off-by: Gregory Price <gourry@gourry.net>
---
 Documentation/admin-guide/sysctl/kernel.rst | 17 +++++++++++++
 kernel/entry/syscall_user_dispatch.c        | 28 +++++++++++++++++++++
 2 files changed, 45 insertions(+)

diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst
index c6994e55d141..4c90caaf1e21 100644
--- a/Documentation/admin-guide/sysctl/kernel.rst
+++ b/Documentation/admin-guide/sysctl/kernel.rst
@@ -1402,6 +1402,23 @@ Note that if you change this from 0 to 1, already created segments
 without users and with a dead originative process will be destroyed.
 
 
+syscall_user_dispatch
+=====================
+
+Controls whether userspace may arm Syscall User Dispatch (SUD) via
+``prctl(PR_SET_SYSCALL_USER_DISPATCH, ...)`` or the
+``PTRACE_SET_SYSCALL_USER_DISPATCH_CONFIG`` ptrace request:
+
+  ==  ===================================================================
+  0   Arming SUD is denied with ``-EPERM``.  Tasks that already armed it
+      keep it, and disabling SUD (``PR_SYS_DISPATCH_OFF``) is always
+      permitted.
+  1   (default) Arming SUD is permitted.
+  ==  ===================================================================
+
+Only present when the kernel is built with ``CONFIG_SYSCALL_USER_DISPATCH``.
+
+
 sysctl_writes_strict
 ====================
 
diff --git a/kernel/entry/syscall_user_dispatch.c b/kernel/entry/syscall_user_dispatch.c
index d89dffcc2d64..1c39ccd733f5 100644
--- a/kernel/entry/syscall_user_dispatch.c
+++ b/kernel/entry/syscall_user_dispatch.c
@@ -11,12 +11,15 @@
 #include <linux/uaccess.h>
 #include <linux/signal.h>
 #include <linux/elf.h>
+#include <linux/sysctl.h>
 
 #include <linux/sched/signal.h>
 #include <linux/sched/task_stack.h>
 
 #include <asm/syscall.h>
 
+static int syscall_user_dispatch_allowed __read_mostly = 1;
+
 static void trigger_sigsys(struct pt_regs *regs)
 {
 	struct kernel_siginfo info;
@@ -102,6 +105,10 @@ static int task_set_syscall_user_dispatch(struct task_struct *task, unsigned lon
 		return -EINVAL;
 	}
 
+	/* Arming can be denied at runtime via sysctl, disarming is allowed */
+	if (mode != PR_SYS_DISPATCH_OFF && !syscall_user_dispatch_allowed)
+		return -EPERM;
+
 	/*
 	 * access_ok() will clear memory tags for tagged addresses
 	 * if current has memory tagging enabled.
@@ -172,3 +179,24 @@ int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long siz
 	return task_set_syscall_user_dispatch(task, cfg.mode, cfg.offset, cfg.len,
 					      (char __user *)(uintptr_t)cfg.selector);
 }
+
+#ifdef CONFIG_SYSCTL
+static const struct ctl_table syscall_user_dispatch_sysctls[] = {
+	{
+		.procname	= "syscall_user_dispatch",
+		.data		= &syscall_user_dispatch_allowed,
+		.maxlen		= sizeof(syscall_user_dispatch_allowed),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec_minmax,
+		.extra1		= SYSCTL_ZERO,
+		.extra2		= SYSCTL_ONE,
+	},
+};
+
+static int __init syscall_user_dispatch_sysctl_init(void)
+{
+	register_sysctl_init("kernel", syscall_user_dispatch_sysctls);
+	return 0;
+}
+late_initcall(syscall_user_dispatch_sysctl_init);
+#endif /* CONFIG_SYSCTL */
-- 
2.54.0


^ permalink raw reply related

* Re: [External] [PATCH v2 3/8] riscv: Add support for srmcfg CSR from Ssqosid extension
From: Drew Fustini @ 2026-06-27 21:21 UTC (permalink / raw)
  To: yunhui cui
  Cc: Adrien Ricciardi, Alexandre Ghiti, 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, James Morse, Kornel Dulęba,
	Krzysztof Kozlowski, liu.qingtao2, Liu Zhiwei, Palmer Dabbelt,
	Paul Walmsley, Peter Newman, Radim Krčmář,
	Reinette Chatre, Rob Herring, Samuel Holland,
	Sebastian Andrzej Siewior, Tony Luck, Vasudevan Srinivasan,
	Ved Shanbhogue, Weiwei Li, linux-kernel, linux-riscv, x86,
	devicetree, linux-rt-devel, linux-doc
In-Reply-To: <CAEEQ3w=ZLnW-Wz_=PcUQTN8q0wxUq1_epCs0B+v=sSug+OHD=Q@mail.gmail.com>

On Sat, Jun 27, 2026 at 05:11:11PM +0800, yunhui cui wrote:
> Hi Drew,

Hi, thanks for the reviews.

> 
> On Thu, Jun 25, 2026 at 9:40 AM Drew Fustini <fustini@kernel.org> wrote:
> >
> > Add support for the srmcfg CSR defined in the Ssqosid ISA extension.
> > The CSR contains two fields:
> >
> >   - Resource Control ID (RCID) for resource allocation
> >   - Monitoring Counter ID (MCID) for tracking resource usage
> >
> > Requests from a hart to shared resources are tagged with these IDs,
> > allowing resource usage to be associated with the running task.
> >
> > Add a srmcfg field to thread_struct with the same format as the CSR so
> > the scheduler can set the RCID and MCID for each task on context
> > switch. A per-cpu cpu_srmcfg variable mirrors the CSR state to avoid
> > redundant writes. L1D-hot memory access is faster than a CSR read and
> > avoids traps under virtualization.
> >
> > A per-cpu cpu_srmcfg_default holds the default srmcfg for each CPU as
> > set by resctrl CPU group assignment. On context switch, RCID and MCID
> > inherit from the CPU default independently: a task whose thread RCID
> > field is zero takes the CPU default's RCID, and likewise for MCID.
> >
> > Link: https://github.com/riscv/riscv-ssqosid/releases/tag/v1.0
> > Assisted-by: Claude:claude-opus-4-7
> > Co-developed-by: Kornel Dulęba <mindal@semihalf.com>
> > Signed-off-by: Kornel Dulęba <mindal@semihalf.com>
> > Signed-off-by: Drew Fustini <fustini@kernel.org>
[..]
> > diff --git a/arch/riscv/include/asm/qos.h b/arch/riscv/include/asm/qos.h
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..e9e1d69f3797be5f89785a9b3aa7d9d51c476a8a
> > --- /dev/null
> > +++ b/arch/riscv/include/asm/qos.h
[..]
> > +static inline void __switch_to_srmcfg(struct task_struct *next)
> > +{
[..]
> > +       if (thread_srmcfg != __this_cpu_read(cpu_srmcfg)) {
> > +               /*
> > +                * Drain stores from the outgoing task before the CSR write
> > +                * so they retain the previous RCID/MCID tag at the cache
> > +                * interconnect.
> > +                */
> > +               RISCV_FENCE(rw, o);
> > +
> > +               __this_cpu_write(cpu_srmcfg, thread_srmcfg);
> > +               csr_write(CSR_SRMCFG, thread_srmcfg);
> > +               /*
> > +                * Order the csrw before the new task's loads/stores so they
> > +                * pick up the new tag. Zicsr 6.1.1 makes CSR writes weakly
> > +                * ordered (device-output) vs memory ops. Ssqosid v1.0 is
> > +                * silent so honor the general CSR rule.
> > +                */
> > +               RISCV_FENCE(o, rw);
> 
> This is in the context-switch path and may be expensive in practice. Even if
> the target workload is pinned and grouped, unpinned/default-group tasks or
> kworkers may still run on those CPUs, causing frequent SRMCFG transitions and
> paying two fences each time.
> 
> Is this strict ordering required by the Ssqosid spec or known hardware? If
> not, can we make this a trade-off and avoid the fences by default, accepting a
> small QoS-tagging inaccuracy around the context-switch boundary?

These fences were introduced based on Sashiko feedback on the RFC
series. You make a good point that this may be too conservative and some
inaccuracy probably would be acceptable. I would be okay with dropping
them, and we can reevaluate once more hardware implementations with
Ssqosid become public.

Thanks,
Drew

^ permalink raw reply

* Re: [PATCH net-next] Documentation: networking: Add a test plan for ethtool pause validation
From: Jakub Kicinski @ 2026-06-27 21:30 UTC (permalink / raw)
  To: Maxime Chevallier
  Cc: Andrew Lunn, davem, Eric Dumazet, Paolo Abeni, Simon Horman,
	Russell King, Heiner Kallweit, Jonathan Corbet, Shuah Khan,
	Oleksij Rempel, Vladimir Oltean, Florian Fainelli,
	thomas.petazzoni, netdev, linux-kernel, linux-doc
In-Reply-To: <12b66ea3-42df-4ecb-8eb7-44471407b83f@bootlin.com>

On Sat, 27 Jun 2026 07:34:31 +0200 Maxime Chevallier wrote:
> > This is very far from what existing python tests do in netdev.  
> 
> We can probably drop the class, as it is with this discussion, it's merely a way
> to regroup doc common to similar tests. The rest really is the usual set of
> ksft funcs you can feed to the run function, with a set of ksft_ethtool_*
> annotators for generic checks.

The common way of checking prereqs in the tests is to call a function
called require_xyz() which then raises a skip. At a quick glance - the
rss_api and xdp_metadata are good tests to get a sense of the usual format.

^ permalink raw reply

* Re: [External] [PATCH v2 4/8] riscv_cbqri: Add capacity controller probe and allocation device ops
From: Drew Fustini @ 2026-06-27 21:45 UTC (permalink / raw)
  To: yunhui cui
  Cc: Adrien Ricciardi, Alexandre Ghiti, 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, James Morse, Kornel Dulęba,
	Krzysztof Kozlowski, liu.qingtao2, Liu Zhiwei, Palmer Dabbelt,
	Paul Walmsley, Peter Newman, Radim Krčmář,
	Reinette Chatre, Rob Herring, Samuel Holland,
	Sebastian Andrzej Siewior, Tony Luck, Vasudevan Srinivasan,
	Ved Shanbhogue, Weiwei Li, linux-kernel, linux-riscv, x86,
	devicetree, linux-rt-devel, linux-doc
In-Reply-To: <CAEEQ3w=n6L3ugSL6_LTCQcw0CyUSSxpfEVV0Dp-CpQ_XkLUXPQ@mail.gmail.com>

On Sat, Jun 27, 2026 at 05:19:44PM +0800, yunhui cui wrote:
> Hi Drew,
> 
> On Thu, Jun 25, 2026 at 9:41 AM Drew Fustini <fustini@kernel.org> wrote:
> >
> > 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: Claude:claude-opus-4-7
> > Co-developed-by: Adrien Ricciardi <aricciardi@baylibre.com>
> > Signed-off-by: Adrien Ricciardi <aricciardi@baylibre.com>
> > Signed-off-by: Drew Fustini <fustini@kernel.org>
> > ---
> >  MAINTAINERS                      |   3 +
> >  drivers/resctrl/Kconfig          |  13 +
> >  drivers/resctrl/Makefile         |   3 +
> >  drivers/resctrl/cbqri_devices.c  | 520 +++++++++++++++++++++++++++++++++++++++
> >  drivers/resctrl/cbqri_internal.h | 107 ++++++++
> >  include/linux/riscv_cbqri.h      |  47 ++++
> >  6 files changed, 693 insertions(+)
[..]
> > +int cbqri_apply_cache_config(struct cbqri_controller *ctrl, u32 closid,
> > +                            const struct cbqri_cc_config *cfg)
> > +{
[..]
> > +
> > +       /* Set capacity block mask (cc_block_mask) */
> > +       cbqri_set_cbm(ctrl, cfg->cbm);
> > +
> > +       /* Capacity config limit operation for the AT half implied by cfg->at */
> > +       err = cbqri_cc_alloc_op(ctrl, CBQRI_CC_ALLOC_CTL_OP_CONFIG_LIMIT,
> > +                               closid, cfg->at);
> > +       if (err < 0)
> > +               goto out;
> 
> When CUNITS=1, CONFIG_LIMIT also consumes cc_cunits. If resctrl does not
> expose unit limits, the driver should still write cc_cunits=0 before
> CONFIG_LIMIT to avoid a hidden stale/implementation-defined unit limit.
> 
> Should we handle cc_cunits here?

That is a good that cc_units should be handled even though we can't yet
expose it to resctrl. I will change the code to set cc_cunits to 0
before a config limit operation on controllers that support capacity
units, so a stale unit limit does not constrain block-mask allocation.

Thanks,
Drew

^ permalink raw reply

* Re: [External] [PATCH v2 3/8] riscv: Add support for srmcfg CSR from Ssqosid extension
From: Drew Fustini @ 2026-06-27 21:58 UTC (permalink / raw)
  To: yunhui cui
  Cc: Adrien Ricciardi, Alexandre Ghiti, 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, James Morse, Kornel Dulęba,
	Krzysztof Kozlowski, liu.qingtao2, Liu Zhiwei, Palmer Dabbelt,
	Paul Walmsley, Peter Newman, Radim Krčmář,
	Reinette Chatre, Rob Herring, Samuel Holland,
	Sebastian Andrzej Siewior, Tony Luck, Vasudevan Srinivasan,
	Ved Shanbhogue, Weiwei Li, linux-kernel, linux-riscv, x86,
	devicetree, linux-rt-devel, linux-doc
In-Reply-To: <CAEEQ3wkhiE=TdFTzEn39GVWSgGnaN0raBpmd=6sYaoh304LNBg@mail.gmail.com>

On Sat, Jun 27, 2026 at 05:38:28PM +0800, yunhui cui wrote:
> Hi Drew,
> 
> On Thu, Jun 25, 2026 at 9:40 AM Drew Fustini <fustini@kernel.org> wrote:
[..]
> > +config RISCV_ISA_SSQOSID
> > +       bool "Ssqosid extension support for supervisor mode Quality of Service ID"
> > +       depends on 64BIT
> 
> Why is 64BIT required here?

Sashiko raised many potential issues related to RV32 in reviews of my
RFC series: 

v3: https://sashiko.dev/#/patchset/20260414-ssqosid-cbqri-rqsc-v7-0-v3-0-b3b2e7e9847a%40kernel.org
v4: https://sashiko.dev/#/patchset/20260510-ssqosid-cbqri-rqsc-v7-0-v4-0-eb53831ef683%40kernel.org
v5: https://sashiko.dev/#/patchset/20260524-ssqosid-cbqri-rqsc-v7-0-v5-0-78d3a7ba9dbe%40kernel.org
v6: https://sashiko.dev/#/patchset/20260601-ssqosid-cbqri-rqsc-v7-0-v6-0-baf00f50028a%40kernel.org

RV32 systems with CBQRI didn't seem likely to me so I decided requiring
64BIT would eliminate the potential issues that Sashiko raised. If there
is actually a 32-bit implementation that someone knows of, then I would
be happy to re-evaluate RV32 support.

Thanks,
Drew

^ permalink raw reply

* Re: [External] [PATCH v2 4/8] riscv_cbqri: Add capacity controller probe and allocation device ops
From: Drew Fustini @ 2026-06-27 22:10 UTC (permalink / raw)
  To: yunhui cui
  Cc: Adrien Ricciardi, Alexandre Ghiti, 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, James Morse, Kornel Dulęba,
	Krzysztof Kozlowski, liu.qingtao2, Liu Zhiwei, Palmer Dabbelt,
	Paul Walmsley, Peter Newman, Radim Krčmář,
	Reinette Chatre, Rob Herring, Samuel Holland,
	Sebastian Andrzej Siewior, Tony Luck, Vasudevan Srinivasan,
	Ved Shanbhogue, Weiwei Li, linux-kernel, linux-riscv, x86,
	devicetree, linux-rt-devel, linux-doc
In-Reply-To: <CAEEQ3w=ZVtHPdLfN0tTzG8AACu+rjhEFWY5pCg+uxvmr7ofqfA@mail.gmail.com>

On Sat, Jun 27, 2026 at 05:31:03PM +0800, yunhui cui wrote:
> Hi Drew,
> 
> On Thu, Jun 25, 2026 at 9:41 AM Drew Fustini <fustini@kernel.org> wrote:
> >
> > 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: Claude:claude-opus-4-7
> > Co-developed-by: Adrien Ricciardi <aricciardi@baylibre.com>
> > Signed-off-by: Adrien Ricciardi <aricciardi@baylibre.com>
> > Signed-off-by: Drew Fustini <fustini@kernel.org>
[..]
> > diff --git a/drivers/resctrl/cbqri_devices.c b/drivers/resctrl/cbqri_devices.c
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..8ad9df404f65d5d82722cf8b78f02936c489ca6d
> > --- /dev/null
> > +++ b/drivers/resctrl/cbqri_devices.c
[..]
> > +
> > +/* Set capacity block mask (cc_block_mask) */
> > +static void cbqri_set_cbm(struct cbqri_controller *ctrl, u64 cbm)
> > +{
> > +       iowrite64(cbm, ctrl->base + CBQRI_CC_BLOCK_MASK_OFF);
> 
> The CBQRI spec allows naturally aligned 4-byte accesses and only guarantees
> atomicity for 4-byte accesses; 8-byte atomicity is unspecified.
> 
> Would 32-bit split accesses be preferable here instead of relying on
> ioread64/iowrite64? This may also make the driver less dependent on native
> 64-bit MMIO support.

I suppose there could be systems that are RV64 but do 4-byte access for
the CBQRI registers. You are right the spec only guarantees atomicity
for naturally aligned 4-byte accesses and leaves 8-byte atomicity
unspecified.

I will switch the controller register accesses to 32-bit reads and
writes. The driver rejects ncblks > 32, so cc_block_mask only uses its
low 32 bits. For cc_alloc_ctl, the writable fields all sit in the low
word while the status and busy bits are read-only in the high word. A
read can reconstruct the value from two 32-bit reads and a write only
needs the low word. 

Thanks,
Drew

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox