Do not overload the resctrl tasks file with architecture-specific non-PID tokens. The tasks ABI remains a list of task IDs, while the new devices file carries external requesters assigned to a resctrl group.
Add architecture hooks for assigning and showing those external objects, and reject group removal, reparenting, or pseudo-lock setup while devices are still assigned. The teardown path performs a best-effort reset to the default group. The devices file is an assignment interface only. It does not describe a new resource schema or domain; resource allocation and monitoring policy remain described by the existing schemata and info files. Signed-off-by: Zhanpeng Zhang <[email protected]> --- Documentation/filesystems/resctrl.rst | 26 ++++ arch/Kconfig | 6 + fs/resctrl/rdtgroup.c | 206 +++++++++++++++++++++++++- include/linux/resctrl.h | 45 ++++++ 4 files changed, 280 insertions(+), 3 deletions(-) diff --git a/Documentation/filesystems/resctrl.rst b/Documentation/filesystems/resctrl.rst index e4b66af55ffb..fce61d019114 100644 --- a/Documentation/filesystems/resctrl.rst +++ b/Documentation/filesystems/resctrl.rst @@ -581,6 +581,32 @@ All groups contain the following files: idle tasks. Instead, a CPU's idle task is always considered as a member of the group owning the CPU. +"devices": + On architectures that support external requester assignment through + resctrl, reading this file shows the devices or device groups assigned + to this resource group. Writing an architecture-specific device token + moves that external requester to the group. Multiple tokens can be + separated by commas and are processed sequentially. A failure aborts the + write, but requesters moved before the failure remain in their new groups. + + On RISC-V, an IOMMU group is identified by the following token:: + + iommu_group:<group-id> + + Each assigned IOMMU group is reported on a separate line when the file is + read. Writing the token to the root control group's devices file restores + the IOMMU group to the reserved default QoS IDs. Default assignments are + not listed in the root devices file. + + This file only controls external requester membership. Resource + allocation and monitoring policy remains described by schemata and + info files. + + Resource groups with assigned devices cannot be removed or reparented. + Move devices to another group first. + + Failures will be logged to /sys/fs/resctrl/info/last_cmd_status. + "cpus": Reading this file shows a bitmask of the logical CPUs owned by this group. Writing a mask to this file will add and remove diff --git a/arch/Kconfig b/arch/Kconfig index fa7507ac8e13..36a4f4fbb164 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -1615,6 +1615,12 @@ config ARCH_HAS_CPU_RESCTRL monitoring and control interfaces provided by the 'resctrl' filesystem (see RESCTRL_FS). +config ARCH_HAS_RESCTRL_DEVICES + bool + help + An architecture selects this option to indicate that external + device objects can be attached to resctrl resource groups. + config HAVE_ARCH_COMPILER_H bool help diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index af2cbab14497..2e424c911049 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -99,13 +99,32 @@ void rdt_last_cmd_puts(const char *s) seq_buf_puts(&last_cmd_status, s); } +void resctrl_last_cmd_puts(const char *s) +{ + rdt_last_cmd_puts(s); +} + +static void rdt_last_cmd_vprintf(const char *fmt, va_list ap) +{ + lockdep_assert_held(&rdtgroup_mutex); + seq_buf_vprintf(&last_cmd_status, fmt, ap); +} + void rdt_last_cmd_printf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - lockdep_assert_held(&rdtgroup_mutex); - seq_buf_vprintf(&last_cmd_status, fmt, ap); + rdt_last_cmd_vprintf(fmt, ap); + va_end(ap); +} + +void resctrl_last_cmd_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + rdt_last_cmd_vprintf(fmt, ap); va_end(ap); } @@ -766,6 +785,151 @@ static int rdtgroup_move_task(pid_t pid, struct rdtgroup *rdtgrp, return ret; } +static bool rdtgroup_effective_ids(struct rdtgroup *r, + struct resctrl_group_ids *ids) +{ + if (!r || !ids) + return false; + + if (r->type == RDTMON_GROUP) + ids->closid = r->mon.parent->closid; + else if (r->type == RDTCTRL_GROUP) + ids->closid = r->closid; + else + return false; + + ids->rmid = r->mon.rmid; + return true; +} + +#ifdef CONFIG_ARCH_HAS_RESCTRL_DEVICES +static void show_rdt_devices(struct rdtgroup *r, struct seq_file *s) +{ + struct resctrl_group_ids ids; + + if (rdtgroup_effective_ids(r, &ids)) + resctrl_arch_devices_show(s, ids); +} + +static int rdtgroup_reset_all_devices(struct rdtgroup *to) +{ + struct resctrl_group_ids default_ids; + + if (!to || !rdtgroup_effective_ids(to, &default_ids)) + return 0; + + return resctrl_arch_devices_reset_all(default_ids); +} + +static bool rdtgroup_arch_devices_assigned(struct rdtgroup *rdtgrp) +{ + struct resctrl_group_ids ids; + + if (!rdtgroup_effective_ids(rdtgrp, &ids)) + return false; + + return resctrl_arch_devices_assigned(ids); +} + +static bool rdtgroup_child_arch_devices_assigned(struct rdtgroup *rdtgrp) +{ + struct rdtgroup *crgrp; + + list_for_each_entry(crgrp, &rdtgrp->mon.crdtgrp_list, mon.crdtgrp_list) { + if (rdtgroup_arch_devices_assigned(crgrp)) + return true; + } + + return false; +} + +static int rdtgroup_reject_assigned_devices(struct rdtgroup *rdtgrp, + bool include_children, + const char *operation) +{ + if (!rdtgroup_arch_devices_assigned(rdtgrp) && + (!include_children || !rdtgroup_child_arch_devices_assigned(rdtgrp))) + return 0; + + rdt_last_cmd_printf("Move devices out before %s group\n", operation); + return -EBUSY; +} + +static ssize_t rdtgroup_devices_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) +{ + struct resctrl_group_ids ids; + struct rdtgroup *rdtgrp; + char *tok; + int ret = 0; + + rdtgrp = rdtgroup_kn_lock_live(of->kn); + if (!rdtgrp) { + rdtgroup_kn_unlock(of->kn); + return -ENOENT; + } + rdt_last_cmd_clear(); + + if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED || + rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) { + ret = -EINVAL; + rdt_last_cmd_puts("Pseudo-locking in progress\n"); + goto unlock; + } + + if (!rdtgroup_effective_ids(rdtgrp, &ids)) { + ret = -EINVAL; + goto unlock; + } + + while ((tok = strsep(&buf, ","))) { + tok = strim(tok); + if (!*tok) { + rdt_last_cmd_puts("Device list parsing error\n"); + ret = -EINVAL; + break; + } + + ret = resctrl_arch_devices_write(tok, ids); + if (ret) + break; + } + +unlock: + rdtgroup_kn_unlock(of->kn); + + return ret ?: nbytes; +} + +static int rdtgroup_devices_show(struct kernfs_open_file *of, + struct seq_file *s, void *v) +{ + struct rdtgroup *rdtgrp; + int ret = 0; + + rdtgrp = rdtgroup_kn_lock_live(of->kn); + if (rdtgrp) + show_rdt_devices(rdtgrp, s); + else + ret = -ENOENT; + rdtgroup_kn_unlock(of->kn); + + return ret; +} +#else +static inline int rdtgroup_reset_all_devices(struct rdtgroup *to) +{ + return 0; +} + +static inline int rdtgroup_reject_assigned_devices(struct rdtgroup *rdtgrp, + bool include_children, + const char *operation) +{ + return 0; +} +#endif + static ssize_t rdtgroup_tasks_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { @@ -1491,6 +1655,11 @@ static ssize_t rdtgroup_mode_write(struct kernfs_open_file *of, rdtgrp->mode = RDT_MODE_EXCLUSIVE; } else if (IS_ENABLED(CONFIG_RESCTRL_FS_PSEUDO_LOCK) && !strcmp(buf, "pseudo-locksetup")) { + ret = rdtgroup_reject_assigned_devices(rdtgrp, true, + "entering pseudo-locksetup"); + if (ret) + goto out; + ret = rdtgroup_locksetup_enter(rdtgrp); if (ret) goto out; @@ -2067,6 +2236,16 @@ static struct rftype res_common_files[] = { .seq_show = rdtgroup_tasks_show, .fflags = RFTYPE_BASE, }, +#ifdef CONFIG_ARCH_HAS_RESCTRL_DEVICES + { + .name = "devices", + .mode = 0644, + .kf_ops = &rdtgroup_kf_single_ops, + .write = rdtgroup_devices_write, + .seq_show = rdtgroup_devices_show, + .fflags = RFTYPE_BASE, + }, +#endif { .name = "mon_hw_id", .mode = 0444, @@ -3061,6 +3240,8 @@ static void rmdir_all_sub(void) /* Move all tasks to the default resource group */ rdt_move_group_tasks(NULL, &rdtgroup_default, NULL); + WARN_ON_ONCE(rdtgroup_reset_all_devices(&rdtgroup_default)); + list_for_each_entry_safe(rdtgrp, tmp, &rdt_all_groups, rdtgroup_list) { /* Free any child rmids */ free_all_child_rdtgrp(rdtgrp); @@ -3970,6 +4151,11 @@ static int rdtgroup_rmdir_mon(struct rdtgroup *rdtgrp, cpumask_var_t tmpmask) struct rdtgroup *prdtgrp = rdtgrp->mon.parent; u32 closid, rmid; int cpu; + int ret; + + ret = rdtgroup_reject_assigned_devices(rdtgrp, false, "removing"); + if (ret) + return ret; /* Give any tasks back to the parent group */ rdt_move_group_tasks(rdtgrp, prdtgrp, tmpmask); @@ -4020,6 +4206,11 @@ static int rdtgroup_rmdir_ctrl(struct rdtgroup *rdtgrp, cpumask_var_t tmpmask) { u32 closid, rmid; int cpu; + int ret; + + ret = rdtgroup_reject_assigned_devices(rdtgrp, true, "removing"); + if (ret) + return ret; /* Give any tasks back to the default group */ rdt_move_group_tasks(rdtgrp, &rdtgroup_default, tmpmask); @@ -4093,7 +4284,10 @@ static int rdtgroup_rmdir(struct kernfs_node *kn) rdtgrp != &rdtgroup_default) { if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP || rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) { - ret = rdtgroup_ctrl_remove(rdtgrp); + ret = rdtgroup_reject_assigned_devices(rdtgrp, true, + "removing"); + if (!ret) + ret = rdtgroup_ctrl_remove(rdtgrp); } else { ret = rdtgroup_rmdir_ctrl(rdtgrp, tmpmask); } @@ -4210,6 +4404,12 @@ static int rdtgroup_rename(struct kernfs_node *kn, goto out; } + if (rdtgrp->mon.parent != new_prdtgrp) { + ret = rdtgroup_reject_assigned_devices(rdtgrp, false, "reparenting"); + if (ret) + goto out; + } + /* * Allocate the cpumask for use in mongrp_reparent() to avoid the * possibility of failing to allocate it after kernfs_rename() has diff --git a/include/linux/resctrl.h b/include/linux/resctrl.h index 73ff522448a0..a4b5c2d5e814 100644 --- a/include/linux/resctrl.h +++ b/include/linux/resctrl.h @@ -8,6 +8,8 @@ #include <linux/pid.h> #include <linux/resctrl_types.h> +struct seq_file; + #ifdef CONFIG_ARCH_HAS_CPU_RESCTRL #include <asm/resctrl.h> #endif @@ -18,6 +20,49 @@ #define RESCTRL_PICK_ANY_CPU -1 +/** + * struct resctrl_group_ids - resctrl control and monitoring IDs + * @closid: resource control class ID + * @rmid: resource monitoring ID + */ +struct resctrl_group_ids { + u32 closid; + u32 rmid; +}; + +void resctrl_last_cmd_puts(const char *s); +void resctrl_last_cmd_printf(const char *fmt, ...) __printf(1, 2); + +#ifdef CONFIG_ARCH_HAS_RESCTRL_DEVICES +int resctrl_arch_devices_write(char *tok, struct resctrl_group_ids ids); +void resctrl_arch_devices_show(struct seq_file *s, + struct resctrl_group_ids ids); +bool resctrl_arch_devices_assigned(struct resctrl_group_ids ids); +int resctrl_arch_devices_reset_all(struct resctrl_group_ids default_ids); +#else +static inline int resctrl_arch_devices_write(char *tok, + struct resctrl_group_ids ids) +{ + return -EOPNOTSUPP; +} + +static inline void resctrl_arch_devices_show(struct seq_file *s, + struct resctrl_group_ids ids) +{ +} + +static inline bool resctrl_arch_devices_assigned(struct resctrl_group_ids ids) +{ + return false; +} + +static inline int +resctrl_arch_devices_reset_all(struct resctrl_group_ids default_ids) +{ + return 0; +} +#endif + #ifdef CONFIG_PROC_CPU_RESCTRL int proc_resctrl_show(struct seq_file *m, -- 2.50.1 (Apple Git-155)

