4921 lines · 121.4 KB · C
RawDownload
// 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.