// SPDX-License-Identifier: GPL-2.0-only/* * User interface for Resource Allocation in Resource Director Technology(RDT) * * Copyright (C) 2016 Intel Corporation * * Author: Fenghua Yu <fenghua.yu@intel.com> * * More information about RDT be found in the Intel (R) x86 Architecture * Software Developer Manual. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/cpu.h>#include <linux/debugfs.h>#include <linux/fs.h>#include <linux/fs_parser.h>#include <linux/sysfs.h>#include <linux/kernfs.h>#include <linux/once.h>#include <linux/resctrl.h>#include <linux/seq_buf.h>#include <linux/seq_file.h>#include <linux/sched/task.h>#include <linux/slab.h>#include <linux/user_namespace.h> #include <uapi/linux/magic.h> #include "internal.h" /* Mutex to protect rdtgroup access. */DEFINE_MUTEX(rdtgroup_mutex); static struct kernfs_root *rdt_root; struct rdtgroup rdtgroup_default; LIST_HEAD(rdt_all_groups); /* list of entries for the schemata file */LIST_HEAD(resctrl_schema_all); /* * List of struct mon_data containing private data of event files for use by * rdtgroup_mondata_show(). Protected by rdtgroup_mutex. */static LIST_HEAD(mon_data_kn_priv_list); /* The filesystem can only be mounted once. */bool resctrl_mounted; /* Kernel fs node for "info" directory under root */static struct kernfs_node *kn_info; /* Kernel fs node for "mon_groups" directory under root */static struct kernfs_node *kn_mongrp; /* Kernel fs node for "mon_data" directory under root */static struct kernfs_node *kn_mondata; /* * Used to store the max resource name width to display the schemata names in * a tabular format. */int max_name_width; static struct seq_buf last_cmd_status; static char last_cmd_status_buf[512]; static int rdtgroup_setup_root(struct rdt_fs_context *ctx); static void rdtgroup_destroy_root(void); struct dentry *debugfs_resctrl; /* * Memory bandwidth monitoring event to use for the default CTRL_MON group * and each new CTRL_MON group created by the user. Only relevant when * the filesystem is mounted with the "mba_MBps" option so it does not * matter that it remains uninitialized on systems that do not support * the "mba_MBps" option. */enum resctrl_event_id mba_mbps_default_event; static bool resctrl_debug; void rdt_last_cmd_clear(void){ lockdep_assert_held(&rdtgroup_mutex); seq_buf_clear(&last_cmd_status);} void rdt_last_cmd_puts(const char *s){ lockdep_assert_held(&rdtgroup_mutex); seq_buf_puts(&last_cmd_status, s);} 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); va_end(ap);} void rdt_staged_configs_clear(void){ struct rdt_ctrl_domain *dom; struct rdt_resource *r; lockdep_assert_held(&rdtgroup_mutex); for_each_alloc_capable_rdt_resource(r) { list_for_each_entry_rcu(dom, &r->ctrl_domains, hdr.list, lockdep_is_cpus_held()) memset(dom->staged_config, 0, sizeof(dom->staged_config)); }} static bool resctrl_is_mbm_enabled(void){ return (resctrl_is_mon_event_enabled(QOS_L3_MBM_TOTAL_EVENT_ID) || resctrl_is_mon_event_enabled(QOS_L3_MBM_LOCAL_EVENT_ID));} /* * Trivial allocator for CLOSIDs. Use BITMAP APIs to manipulate a bitmap * of free CLOSIDs. * * Using a global CLOSID across all resources has some advantages and * some drawbacks: * + We can simply set current's closid to assign a task to a resource * group. * + Context switch code can avoid extra memory references deciding which * CLOSID to load into the PQR_ASSOC MSR * - We give up some options in configuring resource groups across multi-socket * systems. * - Our choices on how to configure each resource become progressively more * limited as the number of resources grows. */static unsigned long *closid_free_map; static int closid_free_map_len; int closids_supported(void){ return closid_free_map_len;} static int closid_init(void){ struct resctrl_schema *s; u32 rdt_min_closid = ~0; /* Monitor only platforms still call closid_init() */ if (list_empty(&resctrl_schema_all)) return 0; /* Compute rdt_min_closid across all resources */ list_for_each_entry(s, &resctrl_schema_all, list) rdt_min_closid = min(rdt_min_closid, s->num_closid); closid_free_map = bitmap_alloc(rdt_min_closid, GFP_KERNEL); if (!closid_free_map) return -ENOMEM; bitmap_fill(closid_free_map, rdt_min_closid); /* RESCTRL_RESERVED_CLOSID is always reserved for the default group */ __clear_bit(RESCTRL_RESERVED_CLOSID, closid_free_map); closid_free_map_len = rdt_min_closid; return 0;} static void closid_exit(void){ bitmap_free(closid_free_map); closid_free_map = NULL;} static int closid_alloc(void){ int cleanest_closid; u32 closid; lockdep_assert_held(&rdtgroup_mutex); if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID) && resctrl_is_mon_event_enabled(QOS_L3_OCCUP_EVENT_ID)) { cleanest_closid = resctrl_find_cleanest_closid(); if (cleanest_closid < 0) return cleanest_closid; closid = cleanest_closid; } else { closid = find_first_bit(closid_free_map, closid_free_map_len); if (closid == closid_free_map_len) return -ENOSPC; } __clear_bit(closid, closid_free_map); return closid;} void closid_free(int closid){ lockdep_assert_held(&rdtgroup_mutex); __set_bit(closid, closid_free_map);} /** * closid_allocated - test if provided closid is in use * @closid: closid to be tested * * Return: true if @closid is currently associated with a resource group, * false if @closid is free */bool closid_allocated(unsigned int closid){ lockdep_assert_held(&rdtgroup_mutex); return !test_bit(closid, closid_free_map);} bool closid_alloc_fixed(u32 closid){ return __test_and_clear_bit(closid, closid_free_map);} /** * rdtgroup_mode_by_closid - Return mode of resource group with closid * @closid: closid if the resource group * * Each resource group is associated with a @closid. Here the mode * of a resource group can be queried by searching for it using its closid. * * Return: mode as &enum rdtgrp_mode of resource group with closid @closid */enum rdtgrp_mode rdtgroup_mode_by_closid(int closid){ struct rdtgroup *rdtgrp; list_for_each_entry(rdtgrp, &rdt_all_groups, rdtgroup_list) { if (rdtgrp->closid == closid) return rdtgrp->mode; } return RDT_NUM_MODES;} static const char * const rdt_mode_str[] = { [RDT_MODE_SHAREABLE] = "shareable", [RDT_MODE_EXCLUSIVE] = "exclusive", [RDT_MODE_PSEUDO_LOCKSETUP] = "pseudo-locksetup", [RDT_MODE_PSEUDO_LOCKED] = "pseudo-locked",}; /** * rdtgroup_mode_str - Return the string representation of mode * @mode: the resource group mode as &enum rdtgroup_mode * * Return: string representation of valid mode, "unknown" otherwise */static const char *rdtgroup_mode_str(enum rdtgrp_mode mode){ if (mode < RDT_MODE_SHAREABLE || mode >= RDT_NUM_MODES) return "unknown"; return rdt_mode_str[mode];} /* set uid and gid of rdtgroup dirs and files to that of the creator */static int rdtgroup_kn_set_ugid(struct kernfs_node *kn){ struct iattr iattr = { .ia_valid = ATTR_UID | ATTR_GID, .ia_uid = current_fsuid(), .ia_gid = current_fsgid(), }; if (uid_eq(iattr.ia_uid, GLOBAL_ROOT_UID) && gid_eq(iattr.ia_gid, GLOBAL_ROOT_GID)) return 0; return kernfs_setattr(kn, &iattr);} static int rdtgroup_add_file(struct kernfs_node *parent_kn, struct rftype *rft){ struct kernfs_node *kn; int ret; kn = __kernfs_create_file(parent_kn, rft->name, rft->mode, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, 0, rft->kf_ops, rft, NULL, NULL); if (IS_ERR(kn)) return PTR_ERR(kn); ret = rdtgroup_kn_set_ugid(kn); if (ret) { kernfs_remove(kn); return ret; } return 0;} static int rdtgroup_seqfile_show(struct seq_file *m, void *arg){ struct kernfs_open_file *of = m->private; struct rftype *rft = of->kn->priv; if (rft->seq_show) return rft->seq_show(of, m, arg); return 0;} static ssize_t rdtgroup_file_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off){ struct rftype *rft = of->kn->priv; if (rft->write) return rft->write(of, buf, nbytes, off); return -EINVAL;} static const struct kernfs_ops rdtgroup_kf_single_ops = { .atomic_write_len = PAGE_SIZE, .write = rdtgroup_file_write, .seq_show = rdtgroup_seqfile_show,}; static const struct kernfs_ops kf_mondata_ops = { .atomic_write_len = PAGE_SIZE, .seq_show = rdtgroup_mondata_show,}; static bool is_cpu_list(struct kernfs_open_file *of){ struct rftype *rft = of->kn->priv; return rft->flags & RFTYPE_FLAGS_CPUS_LIST;} static int rdtgroup_cpus_show(struct kernfs_open_file *of, struct seq_file *s, void *v){ struct rdtgroup *rdtgrp; struct cpumask *mask; int ret = 0; rdtgrp = rdtgroup_kn_lock_live(of->kn); if (rdtgrp) { if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) { if (!rdtgrp->plr->d) { rdt_last_cmd_puts("Cache domain offline\n"); ret = -ENODEV; } else { mask = &rdtgrp->plr->d->hdr.cpu_mask; seq_printf(s, is_cpu_list(of) ? "%*pbl\n" : "%*pb\n", cpumask_pr_args(mask)); } } else { seq_printf(s, is_cpu_list(of) ? "%*pbl\n" : "%*pb\n", cpumask_pr_args(&rdtgrp->cpu_mask)); } } else { ret = -ENOENT; } rdtgroup_kn_unlock(of->kn); return ret;} /* * Update the PGR_ASSOC MSR on all cpus in @cpu_mask, * * Per task closids/rmids must have been set up before calling this function. * @r may be NULL. */static voidupdate_closid_rmid(const struct cpumask *cpu_mask, struct rdtgroup *r){ struct resctrl_cpu_defaults defaults, *p = NULL; if (r) { defaults.closid = r->closid; defaults.rmid = r->mon.rmid; p = &defaults; } on_each_cpu_mask(cpu_mask, resctrl_arch_sync_cpu_closid_rmid, p, 1);} static int cpus_mon_write(struct rdtgroup *rdtgrp, cpumask_var_t newmask, cpumask_var_t tmpmask){ struct rdtgroup *prgrp = rdtgrp->mon.parent, *crgrp; struct list_head *head; /* Check whether cpus belong to parent ctrl group */ cpumask_andnot(tmpmask, newmask, &prgrp->cpu_mask); if (!cpumask_empty(tmpmask)) { rdt_last_cmd_puts("Can only add CPUs to mongroup that belong to parent\n"); return -EINVAL; } /* Check whether cpus are dropped from this group */ cpumask_andnot(tmpmask, &rdtgrp->cpu_mask, newmask); if (!cpumask_empty(tmpmask)) { /* Give any dropped cpus to parent rdtgroup */ cpumask_or(&prgrp->cpu_mask, &prgrp->cpu_mask, tmpmask); update_closid_rmid(tmpmask, prgrp); } /* * If we added cpus, remove them from previous group that owned them * and update per-cpu rmid */ cpumask_andnot(tmpmask, newmask, &rdtgrp->cpu_mask); if (!cpumask_empty(tmpmask)) { head = &prgrp->mon.crdtgrp_list; list_for_each_entry(crgrp, head, mon.crdtgrp_list) { if (crgrp == rdtgrp) continue; cpumask_andnot(&crgrp->cpu_mask, &crgrp->cpu_mask, tmpmask); } update_closid_rmid(tmpmask, rdtgrp); } /* Done pushing/pulling - update this group with new mask */ cpumask_copy(&rdtgrp->cpu_mask, newmask); return 0;} static void cpumask_rdtgrp_clear(struct rdtgroup *r, struct cpumask *m){ struct rdtgroup *crgrp; cpumask_andnot(&r->cpu_mask, &r->cpu_mask, m); /* update the child mon group masks as well*/ list_for_each_entry(crgrp, &r->mon.crdtgrp_list, mon.crdtgrp_list) cpumask_and(&crgrp->cpu_mask, &r->cpu_mask, &crgrp->cpu_mask);} static int cpus_ctrl_write(struct rdtgroup *rdtgrp, cpumask_var_t newmask, cpumask_var_t tmpmask, cpumask_var_t tmpmask1){ struct rdtgroup *r, *crgrp; struct list_head *head; /* Check whether cpus are dropped from this group */ cpumask_andnot(tmpmask, &rdtgrp->cpu_mask, newmask); if (!cpumask_empty(tmpmask)) { /* Can't drop from default group */ if (rdtgrp == &rdtgroup_default) { rdt_last_cmd_puts("Can't drop CPUs from default group\n"); return -EINVAL; } /* Give any dropped cpus to rdtgroup_default */ cpumask_or(&rdtgroup_default.cpu_mask, &rdtgroup_default.cpu_mask, tmpmask); update_closid_rmid(tmpmask, &rdtgroup_default); } /* * If we added cpus, remove them from previous group and * the prev group's child groups that owned them * and update per-cpu closid/rmid. */ cpumask_andnot(tmpmask, newmask, &rdtgrp->cpu_mask); if (!cpumask_empty(tmpmask)) { list_for_each_entry(r, &rdt_all_groups, rdtgroup_list) { if (r == rdtgrp) continue; cpumask_and(tmpmask1, &r->cpu_mask, tmpmask); if (!cpumask_empty(tmpmask1)) cpumask_rdtgrp_clear(r, tmpmask1); } update_closid_rmid(tmpmask, rdtgrp); } /* Done pushing/pulling - update this group with new mask */ cpumask_copy(&rdtgrp->cpu_mask, newmask); /* * Clear child mon group masks since there is a new parent mask * now and update the rmid for the cpus the child lost. */ head = &rdtgrp->mon.crdtgrp_list;Lines 1–500 of 4921. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * User interface for Resource Allocation in Resource Director Technology(RDT) * * Copyright (C) 2016 Intel Corporation * * Author: Fenghua Yu <fenghua.yu@intel.com> * * More information about RDT be found in the Intel (R) x86 Architecture * Software Developer Manual. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/cpu.h> #include <linux/debugfs.h> #include <linux/fs.h> #include <linux/fs_parser.h> #include <linux/sysfs.h> #include <linux/kernfs.h> #include <linux/once.h> #include <linux/resctrl.h> #include <linux/seq_buf.h> #include <linux/seq_file.h> #include <linux/sched/task.h> #include <linux/slab.h> #include <linux/user_namespace.h> #include <uapi/linux/magic.h> #include "internal.h" /* Mutex to protect rdtgroup access. */ DEFINE_MUTEX(rdtgroup_mutex); static struct kernfs_root *rdt_root; struct rdtgroup rdtgroup_default; LIST_HEAD(rdt_all_groups); /* list of entries for the schemata file */ LIST_HEAD(resctrl_schema_all); /* * List of struct mon_data containing private data of event files for use by * rdtgroup_mondata_show(). Protected by rdtgroup_mutex. */ static LIST_HEAD(mon_data_kn_priv_list); /* The filesystem can only be mounted once. */ bool resctrl_mounted; /* Kernel fs node for "info" directory under root */ static struct kernfs_node *kn_info; /* Kernel fs node for "mon_groups" directory under root */ static struct kernfs_node *kn_mongrp; /* Kernel fs node for "mon_data" directory under root */ static struct kernfs_node *kn_mondata; /* * Used to store the max resource name width to display the schemata names in * a tabular format. */ int max_name_width; static struct seq_buf last_cmd_status; static char last_cmd_status_buf[512]; static int rdtgroup_setup_root(struct rdt_fs_context *ctx); static void rdtgroup_destroy_root(void); struct dentry *debugfs_resctrl; /* * Memory bandwidth monitoring event to use for the default CTRL_MON group * and each new CTRL_MON group created by the user. Only relevant when * the filesystem is mounted with the "mba_MBps" option so it does not * matter that it remains uninitialized on systems that do not support * the "mba_MBps" option. */ enum resctrl_event_id mba_mbps_default_event; static bool resctrl_debug; void rdt_last_cmd_clear(void) { lockdep_assert_held(&rdtgroup_mutex); seq_buf_clear(&last_cmd_status); } void rdt_last_cmd_puts(const char *s) { lockdep_assert_held(&rdtgroup_mutex); seq_buf_puts(&last_cmd_status, s); } 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); va_end(ap); } void rdt_staged_configs_clear(void) { struct rdt_ctrl_domain *dom; struct rdt_resource *r; lockdep_assert_held(&rdtgroup_mutex); for_each_alloc_capable_rdt_resource(r) { list_for_each_entry_rcu(dom, &r->ctrl_domains, hdr.list, lockdep_is_cpus_held()) memset(dom->staged_config, 0, sizeof(dom->staged_config)); } } static bool resctrl_is_mbm_enabled(void) { return (resctrl_is_mon_event_enabled(QOS_L3_MBM_TOTAL_EVENT_ID) || resctrl_is_mon_event_enabled(QOS_L3_MBM_LOCAL_EVENT_ID)); } /* * Trivial allocator for CLOSIDs. Use BITMAP APIs to manipulate a bitmap * of free CLOSIDs. * * Using a global CLOSID across all resources has some advantages and * some drawbacks: * + We can simply set current's closid to assign a task to a resource * group. * + Context switch code can avoid extra memory references deciding which * CLOSID to load into the PQR_ASSOC MSR * - We give up some options in configuring resource groups across multi-socket * systems. * - Our choices on how to configure each resource become progressively more * limited as the number of resources grows. */ static unsigned long *closid_free_map; static int closid_free_map_len; int closids_supported(void) { return closid_free_map_len; } static int closid_init(void) { struct resctrl_schema *s; u32 rdt_min_closid = ~0; /* Monitor only platforms still call closid_init() */ if (list_empty(&resctrl_schema_all)) return 0; /* Compute rdt_min_closid across all resources */ list_for_each_entry(s, &resctrl_schema_all, list) rdt_min_closid = min(rdt_min_closid, s->num_closid); closid_free_map = bitmap_alloc(rdt_min_closid, GFP_KERNEL); if (!closid_free_map) return -ENOMEM; bitmap_fill(closid_free_map, rdt_min_closid); /* RESCTRL_RESERVED_CLOSID is always reserved for the default group */ __clear_bit(RESCTRL_RESERVED_CLOSID, closid_free_map); closid_free_map_len = rdt_min_closid; return 0; } static void closid_exit(void) { bitmap_free(closid_free_map); closid_free_map = NULL; } static int closid_alloc(void) { int cleanest_closid; u32 closid; lockdep_assert_held(&rdtgroup_mutex); if (IS_ENABLED(CONFIG_RESCTRL_RMID_DEPENDS_ON_CLOSID) && resctrl_is_mon_event_enabled(QOS_L3_OCCUP_EVENT_ID)) { cleanest_closid = resctrl_find_cleanest_closid(); if (cleanest_closid < 0) return cleanest_closid; closid = cleanest_closid; } else { closid = find_first_bit(closid_free_map, closid_free_map_len); if (closid == closid_free_map_len) return -ENOSPC; } __clear_bit(closid, closid_free_map); return closid; } void closid_free(int closid) { lockdep_assert_held(&rdtgroup_mutex); __set_bit(closid, closid_free_map); } /** * closid_allocated - test if provided closid is in use * @closid: closid to be tested * * Return: true if @closid is currently associated with a resource group, * false if @closid is free */ bool closid_allocated(unsigned int closid) { lockdep_assert_held(&rdtgroup_mutex); return !test_bit(closid, closid_free_map); } bool closid_alloc_fixed(u32 closid) { return __test_and_clear_bit(closid, closid_free_map); } /** * rdtgroup_mode_by_closid - Return mode of resource group with closid * @closid: closid if the resource group * * Each resource group is associated with a @closid. Here the mode * of a resource group can be queried by searching for it using its closid. * * Return: mode as &enum rdtgrp_mode of resource group with closid @closid */ enum rdtgrp_mode rdtgroup_mode_by_closid(int closid) { struct rdtgroup *rdtgrp; list_for_each_entry(rdtgrp, &rdt_all_groups, rdtgroup_list) { if (rdtgrp->closid == closid) return rdtgrp->mode; } return RDT_NUM_MODES; } static const char * const rdt_mode_str[] = { [RDT_MODE_SHAREABLE] = "shareable", [RDT_MODE_EXCLUSIVE] = "exclusive", [RDT_MODE_PSEUDO_LOCKSETUP] = "pseudo-locksetup", [RDT_MODE_PSEUDO_LOCKED] = "pseudo-locked", }; /** * rdtgroup_mode_str - Return the string representation of mode * @mode: the resource group mode as &enum rdtgroup_mode * * Return: string representation of valid mode, "unknown" otherwise */ static const char *rdtgroup_mode_str(enum rdtgrp_mode mode) { if (mode < RDT_MODE_SHAREABLE || mode >= RDT_NUM_MODES) return "unknown"; return rdt_mode_str[mode]; } /* set uid and gid of rdtgroup dirs and files to that of the creator */ static int rdtgroup_kn_set_ugid(struct kernfs_node *kn) { struct iattr iattr = { .ia_valid = ATTR_UID | ATTR_GID, .ia_uid = current_fsuid(), .ia_gid = current_fsgid(), }; if (uid_eq(iattr.ia_uid, GLOBAL_ROOT_UID) && gid_eq(iattr.ia_gid, GLOBAL_ROOT_GID)) return 0; return kernfs_setattr(kn, &iattr); } static int rdtgroup_add_file(struct kernfs_node *parent_kn, struct rftype *rft) { struct kernfs_node *kn; int ret; kn = __kernfs_create_file(parent_kn, rft->name, rft->mode, GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, 0, rft->kf_ops, rft, NULL, NULL); if (IS_ERR(kn)) return PTR_ERR(kn); ret = rdtgroup_kn_set_ugid(kn); if (ret) { kernfs_remove(kn); return ret; } return 0; } static int rdtgroup_seqfile_show(struct seq_file *m, void *arg) { struct kernfs_open_file *of = m->private; struct rftype *rft = of->kn->priv; if (rft->seq_show) return rft->seq_show(of, m, arg); return 0; } static ssize_t rdtgroup_file_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { struct rftype *rft = of->kn->priv; if (rft->write) return rft->write(of, buf, nbytes, off); return -EINVAL; } static const struct kernfs_ops rdtgroup_kf_single_ops = { .atomic_write_len = PAGE_SIZE, .write = rdtgroup_file_write, .seq_show = rdtgroup_seqfile_show, }; static const struct kernfs_ops kf_mondata_ops = { .atomic_write_len = PAGE_SIZE, .seq_show = rdtgroup_mondata_show, }; static bool is_cpu_list(struct kernfs_open_file *of) { struct rftype *rft = of->kn->priv; return rft->flags & RFTYPE_FLAGS_CPUS_LIST; } static int rdtgroup_cpus_show(struct kernfs_open_file *of, struct seq_file *s, void *v) { struct rdtgroup *rdtgrp; struct cpumask *mask; int ret = 0; rdtgrp = rdtgroup_kn_lock_live(of->kn); if (rdtgrp) { if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKED) { if (!rdtgrp->plr->d) { rdt_last_cmd_puts("Cache domain offline\n"); ret = -ENODEV; } else { mask = &rdtgrp->plr->d->hdr.cpu_mask; seq_printf(s, is_cpu_list(of) ? "%*pbl\n" : "%*pb\n", cpumask_pr_args(mask)); } } else { seq_printf(s, is_cpu_list(of) ? "%*pbl\n" : "%*pb\n", cpumask_pr_args(&rdtgrp->cpu_mask)); } } else { ret = -ENOENT; } rdtgroup_kn_unlock(of->kn); return ret; } /* * Update the PGR_ASSOC MSR on all cpus in @cpu_mask, * * Per task closids/rmids must have been set up before calling this function. * @r may be NULL. */ static void update_closid_rmid(const struct cpumask *cpu_mask, struct rdtgroup *r) { struct resctrl_cpu_defaults defaults, *p = NULL; if (r) { defaults.closid = r->closid; defaults.rmid = r->mon.rmid; p = &defaults; } on_each_cpu_mask(cpu_mask, resctrl_arch_sync_cpu_closid_rmid, p, 1); } static int cpus_mon_write(struct rdtgroup *rdtgrp, cpumask_var_t newmask, cpumask_var_t tmpmask) { struct rdtgroup *prgrp = rdtgrp->mon.parent, *crgrp; struct list_head *head; /* Check whether cpus belong to parent ctrl group */ cpumask_andnot(tmpmask, newmask, &prgrp->cpu_mask); if (!cpumask_empty(tmpmask)) { rdt_last_cmd_puts("Can only add CPUs to mongroup that belong to parent\n"); return -EINVAL; } /* Check whether cpus are dropped from this group */ cpumask_andnot(tmpmask, &rdtgrp->cpu_mask, newmask); if (!cpumask_empty(tmpmask)) { /* Give any dropped cpus to parent rdtgroup */ cpumask_or(&prgrp->cpu_mask, &prgrp->cpu_mask, tmpmask); update_closid_rmid(tmpmask, prgrp); } /* * If we added cpus, remove them from previous group that owned them * and update per-cpu rmid */ cpumask_andnot(tmpmask, newmask, &rdtgrp->cpu_mask); if (!cpumask_empty(tmpmask)) { head = &prgrp->mon.crdtgrp_list; list_for_each_entry(crgrp, head, mon.crdtgrp_list) { if (crgrp == rdtgrp) continue; cpumask_andnot(&crgrp->cpu_mask, &crgrp->cpu_mask, tmpmask); } update_closid_rmid(tmpmask, rdtgrp); } /* Done pushing/pulling - update this group with new mask */ cpumask_copy(&rdtgrp->cpu_mask, newmask); return 0; } static void cpumask_rdtgrp_clear(struct rdtgroup *r, struct cpumask *m) { struct rdtgroup *crgrp; cpumask_andnot(&r->cpu_mask, &r->cpu_mask, m); /* update the child mon group masks as well*/ list_for_each_entry(crgrp, &r->mon.crdtgrp_list, mon.crdtgrp_list) cpumask_and(&crgrp->cpu_mask, &r->cpu_mask, &crgrp->cpu_mask); } static int cpus_ctrl_write(struct rdtgroup *rdtgrp, cpumask_var_t newmask, cpumask_var_t tmpmask, cpumask_var_t tmpmask1) { struct rdtgroup *r, *crgrp; struct list_head *head; /* Check whether cpus are dropped from this group */ cpumask_andnot(tmpmask, &rdtgrp->cpu_mask, newmask); if (!cpumask_empty(tmpmask)) { /* Can't drop from default group */ if (rdtgrp == &rdtgroup_default) { rdt_last_cmd_puts("Can't drop CPUs from default group\n"); return -EINVAL; } /* Give any dropped cpus to rdtgroup_default */ cpumask_or(&rdtgroup_default.cpu_mask, &rdtgroup_default.cpu_mask, tmpmask); update_closid_rmid(tmpmask, &rdtgroup_default); } /* * If we added cpus, remove them from previous group and * the prev group's child groups that owned them * and update per-cpu closid/rmid. */ cpumask_andnot(tmpmask, newmask, &rdtgrp->cpu_mask); if (!cpumask_empty(tmpmask)) { list_for_each_entry(r, &rdt_all_groups, rdtgroup_list) { if (r == rdtgrp) continue; cpumask_and(tmpmask1, &r->cpu_mask, tmpmask); if (!cpumask_empty(tmpmask1)) cpumask_rdtgrp_clear(r, tmpmask1); } update_closid_rmid(tmpmask, rdtgrp); } /* Done pushing/pulling - update this group with new mask */ cpumask_copy(&rdtgrp->cpu_mask, newmask); /* * Clear child mon group masks since there is a new parent mask * now and update the rmid for the cpus the child lost. */ head = &rdtgrp->mon.crdtgrp_list;