// 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.
*/
/* Mutex to protect rdtgroup access. */
DEFINE_MUTEX(rdtgroup_mutex);
staticstruct 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 */ staticstruct kernfs_node *kn_info;
/* Kernel fs node for "mon_groups" directory under root */ staticstruct kernfs_node *kn_mongrp;
/* Kernel fs node for "mon_data" directory under root */ staticstruct 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;
/* * 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;
staticbool resctrl_is_mbm_event(int e)
{ return (e >= QOS_L3_MBM_TOTAL_EVENT_ID &&
e <= 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.
*/ staticunsignedlong *closid_free_map;
staticint closid_free_map_len;
int closids_supported(void)
{ return closid_free_map_len;
}
/* 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;
/** * 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(unsignedint closid)
{
lockdep_assert_held(&rdtgroup_mutex);
return !test_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;
/** * 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
*/ staticconstchar *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 */ staticint 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;
/* * 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.
*/ staticvoid
update_closid_rmid(conststruct 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;
}
/* 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);
/* 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;
list_for_each_entry(crgrp, head, mon.crdtgrp_list) {
cpumask_and(tmpmask, &rdtgrp->cpu_mask, &crgrp->cpu_mask);
update_closid_rmid(tmpmask, rdtgrp);
cpumask_clear(&crgrp->cpu_mask);
}
if (!zalloc_cpumask_var(&tmpmask, GFP_KERNEL)) return -ENOMEM; if (!zalloc_cpumask_var(&newmask, GFP_KERNEL)) {
free_cpumask_var(tmpmask); return -ENOMEM;
} if (!zalloc_cpumask_var(&tmpmask1, GFP_KERNEL)) {
free_cpumask_var(tmpmask);
free_cpumask_var(newmask); return -ENOMEM;
}
rdtgrp = rdtgroup_kn_lock_live(of->kn); if (!rdtgrp) {
ret = -ENOENT; goto unlock;
}
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 (is_cpu_list(of))
ret = cpulist_parse(buf, newmask); else
ret = cpumask_parse(buf, newmask);
if (ret) {
rdt_last_cmd_puts("Bad CPU list/mask\n"); goto unlock;
}
/* check that user didn't specify any offline cpus */
cpumask_andnot(tmpmask, newmask, cpu_online_mask); if (!cpumask_empty(tmpmask)) {
ret = -EINVAL;
rdt_last_cmd_puts("Can only assign online CPUs\n"); goto unlock;
}
if (rdtgrp->type == RDTCTRL_GROUP)
ret = cpus_ctrl_write(rdtgrp, newmask, tmpmask, tmpmask1); elseif (rdtgrp->type == RDTMON_GROUP)
ret = cpus_mon_write(rdtgrp, newmask, tmpmask); else
ret = -EINVAL;
/** * rdtgroup_remove - the helper to remove resource group safely * @rdtgrp: resource group to remove * * On resource group creation via a mkdir, an extra kernfs_node reference is * taken to ensure that the rdtgroup structure remains accessible for the * rdtgroup_kn_unlock() calls where it is removed. * * Drop the extra reference here, then free the rdtgroup structure. * * Return: void
*/ staticvoid rdtgroup_remove(struct rdtgroup *rdtgrp)
{
kernfs_put(rdtgrp->kn);
kfree(rdtgrp);
}
staticvoid _update_task_closid_rmid(void *task)
{ /* * If the task is still current on this CPU, update PQR_ASSOC MSR. * Otherwise, the MSR is updated when the task is scheduled in.
*/ if (task == current)
resctrl_arch_sched_in(task);
}
staticint __rdtgroup_move_task(struct task_struct *tsk, struct rdtgroup *rdtgrp)
{ /* If the task is already in rdtgrp, no need to move the task. */ if (task_in_rdtgroup(tsk, rdtgrp)) return 0;
/* * Set the task's closid/rmid before the PQR_ASSOC MSR can be * updated by them. * * For ctrl_mon groups, move both closid and rmid. * For monitor groups, can move the tasks only from * their parent CTRL group.
*/ if (rdtgrp->type == RDTMON_GROUP &&
!resctrl_arch_match_closid(tsk, rdtgrp->mon.parent->closid)) {
rdt_last_cmd_puts("Can't move task to different control group\n"); return -EINVAL;
}
/* * Ensure the task's closid and rmid are written before determining if * the task is current that will decide if it will be interrupted. * This pairs with the full barrier between the rq->curr update and * resctrl_arch_sched_in() during context switch.
*/
smp_mb();
/* * By now, the task's closid and rmid are set. If the task is current * on a CPU, the PQR_ASSOC MSR needs to be updated to make the resource * group go into effect. If the task is not current, the MSR will be * updated when the task is scheduled in.
*/
update_task_closid_rmid(tsk);
/** * rdtgroup_tasks_assigned - Test if tasks have been assigned to resource group * @r: Resource group * * Return: 1 if tasks have been assigned to @r, 0 otherwise
*/ int rdtgroup_tasks_assigned(struct rdtgroup *r)
{ struct task_struct *p, *t; int ret = 0;
lockdep_assert_held(&rdtgroup_mutex);
rcu_read_lock();
for_each_process_thread(p, t) { if (is_closid_match(t, r) || is_rmid_match(t, r)) {
ret = 1; break;
}
}
rcu_read_unlock();
/* * Even if we're attaching all tasks in the thread group, we only * need to check permissions on one of them.
*/ if (!uid_eq(cred->euid, GLOBAL_ROOT_UID) &&
!uid_eq(cred->euid, tcred->uid) &&
!uid_eq(cred->euid, tcred->suid)) {
rdt_last_cmd_printf("No permission to move task %d\n", task->pid);
ret = -EPERM;
}
staticint rdtgroup_tasks_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_tasks(rdtgrp, s); else
ret = -ENOENT;
rdtgroup_kn_unlock(of->kn);
return ret;
}
staticint rdtgroup_closid_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)
seq_printf(s, "%u\n", rdtgrp->closid); else
ret = -ENOENT;
rdtgroup_kn_unlock(of->kn);
return ret;
}
staticint rdtgroup_rmid_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)
seq_printf(s, "%u\n", rdtgrp->mon.rmid); else
ret = -ENOENT;
rdtgroup_kn_unlock(of->kn);
return ret;
}
#ifdef CONFIG_PROC_CPU_RESCTRL /* * A task can only be part of one resctrl control group and of one monitor * group which is associated to that control group. * * 1) res: * mon: * * resctrl is not available. * * 2) res:/ * mon: * * Task is part of the root resctrl control group, and it is not associated * to any monitor group. * * 3) res:/ * mon:mon0 * * Task is part of the root resctrl control group and monitor group mon0. * * 4) res:group0 * mon: * * Task is part of resctrl control group group0, and it is not associated * to any monitor group. * * 5) res:group0 * mon:mon1 * * Task is part of resctrl control group group0 and monitor group mon1.
*/ int proc_resctrl_show(struct seq_file *s, struct pid_namespace *ns, struct pid *pid, struct task_struct *tsk)
{ struct rdtgroup *rdtg; int ret = 0;
mutex_lock(&rdtgroup_mutex);
/* Return empty if resctrl has not been mounted. */ if (!resctrl_mounted) {
seq_puts(s, "res:\nmon:\n"); goto unlock;
}
/* * Task information is only relevant for shareable * and exclusive groups.
*/ if (rdtg->mode != RDT_MODE_SHAREABLE &&
rdtg->mode != RDT_MODE_EXCLUSIVE) continue;
if (!resctrl_arch_match_closid(tsk, rdtg->closid)) continue;
seq_printf(s, "res:%s%s\n", (rdtg == &rdtgroup_default) ? "/" : "",
rdt_kn_name(rdtg->kn));
seq_puts(s, "mon:");
list_for_each_entry(crg, &rdtg->mon.crdtgrp_list,
mon.crdtgrp_list) { if (!resctrl_arch_match_rmid(tsk, crg->mon.parent->closid,
crg->mon.rmid)) continue;
seq_printf(s, "%s", rdt_kn_name(crg->kn)); break;
}
seq_putc(s, '\n'); goto unlock;
} /* * The above search should succeed. Otherwise return * with an error.
*/
ret = -ENOENT;
unlock:
mutex_unlock(&rdtgroup_mutex);
mutex_lock(&rdtgroup_mutex);
len = seq_buf_used(&last_cmd_status); if (len)
seq_printf(seq, "%.*s", len, last_cmd_status_buf); else
seq_puts(seq, "ok\n");
mutex_unlock(&rdtgroup_mutex); return 0;
}
staticvoid *rdt_kn_parent_priv(struct kernfs_node *kn)
{ /* * The parent pointer is only valid within RCU section since it can be * replaced.
*/
guard(rcu)(); return rcu_dereference(kn->__parent)->priv;
}
/* * rdt_bit_usage_show - Display current usage of resources * * A domain is a shared resource that can now be allocated differently. Here * we display the current regions of the domain as an annotated bitmask. * For each domain of this resource its allocation bitmask * is annotated as below to indicate the current usage of the corresponding bit: * 0 - currently unused * X - currently available for sharing and used by software and hardware * H - currently used by hardware only but available for software use * S - currently used and shareable by software only * E - currently used exclusively by one resource group * P - currently pseudo-locked by one resource group
*/ staticint rdt_bit_usage_show(struct kernfs_open_file *of, struct seq_file *seq, void *v)
{ struct resctrl_schema *s = rdt_kn_parent_priv(of->kn); /* * Use unsigned long even though only 32 bits are used to ensure * test_bit() is used safely.
*/ unsignedlong sw_shareable = 0, hw_shareable = 0; unsignedlong exclusive = 0, pseudo_locked = 0; struct rdt_resource *r = s->res; struct rdt_ctrl_domain *dom; int i, hwb, swb, excl, psl; enum rdtgrp_mode mode; bool sep = false;
u32 ctrl_val;
cpus_read_lock();
mutex_lock(&rdtgroup_mutex);
hw_shareable = r->cache.shareable_bits;
list_for_each_entry(dom, &r->ctrl_domains, hdr.list) { if (sep)
seq_putc(seq, ';');
sw_shareable = 0;
exclusive = 0;
seq_printf(seq, "%d=", dom->hdr.id); for (i = 0; i < closids_supported(); i++) { if (!closid_allocated(i)) continue;
ctrl_val = resctrl_arch_get_config(r, dom, i,
s->conf_type);
mode = rdtgroup_mode_by_closid(i); switch (mode) { case RDT_MODE_SHAREABLE:
sw_shareable |= ctrl_val; break; case RDT_MODE_EXCLUSIVE:
exclusive |= ctrl_val; break; case RDT_MODE_PSEUDO_LOCKSETUP: /* * RDT_MODE_PSEUDO_LOCKSETUP is possible * here but not included since the CBM * associated with this CLOSID in this mode * is not initialized and no task or cpu can be * assigned this CLOSID.
*/ break; case RDT_MODE_PSEUDO_LOCKED: case RDT_NUM_MODES:
WARN(1, "invalid mode for closid %d\n", i); break;
}
} for (i = r->cache.cbm_len - 1; i >= 0; i--) {
pseudo_locked = dom->plr ? dom->plr->cbm : 0;
hwb = test_bit(i, &hw_shareable);
swb = test_bit(i, &sw_shareable);
excl = test_bit(i, &exclusive);
psl = test_bit(i, &pseudo_locked); if (hwb && swb)
seq_putc(seq, 'X'); elseif (hwb && !swb)
seq_putc(seq, 'H'); elseif (!hwb && swb)
seq_putc(seq, 'S'); elseif (excl)
seq_putc(seq, 'E'); elseif (psl)
seq_putc(seq, 'P'); else/* Unused bits remain */
seq_putc(seq, '0');
}
sep = true;
}
seq_putc(seq, '\n');
mutex_unlock(&rdtgroup_mutex);
cpus_read_unlock(); return 0;
}
/** * __rdtgroup_cbm_overlaps - Does CBM for intended closid overlap with other * @r: Resource to which domain instance @d belongs. * @d: The domain instance for which @closid is being tested. * @cbm: Capacity bitmask being tested. * @closid: Intended closid for @cbm. * @type: CDP type of @r. * @exclusive: Only check if overlaps with exclusive resource groups * * Checks if provided @cbm intended to be used for @closid on domain * @d overlaps with any other closids or other hardware usage associated * with this domain. If @exclusive is true then only overlaps with * resource groups in exclusive mode will be considered. If @exclusive * is false then overlaps with any resource group or hardware entities * will be considered. * * @cbm is unsigned long, even if only 32 bits are used, to make the * bitmap functions work correctly. * * Return: false if CBM does not overlap, true if it does.
*/ staticbool __rdtgroup_cbm_overlaps(struct rdt_resource *r, struct rdt_ctrl_domain *d, unsignedlong cbm, int closid, enum resctrl_conf_type type, bool exclusive)
{ enum rdtgrp_mode mode; unsignedlong ctrl_b; int i;
/* Check for any overlap with regions used by hardware directly */ if (!exclusive) {
ctrl_b = r->cache.shareable_bits; if (bitmap_intersects(&cbm, &ctrl_b, r->cache.cbm_len)) returntrue;
}
/* Check for overlap with other resource groups */ for (i = 0; i < closids_supported(); i++) {
ctrl_b = resctrl_arch_get_config(r, d, i, type);
mode = rdtgroup_mode_by_closid(i); if (closid_allocated(i) && i != closid &&
mode != RDT_MODE_PSEUDO_LOCKSETUP) { if (bitmap_intersects(&cbm, &ctrl_b, r->cache.cbm_len)) { if (exclusive) { if (mode == RDT_MODE_EXCLUSIVE) returntrue; continue;
} returntrue;
}
}
}
returnfalse;
}
/** * rdtgroup_cbm_overlaps - Does CBM overlap with other use of hardware * @s: Schema for the resource to which domain instance @d belongs. * @d: The domain instance for which @closid is being tested. * @cbm: Capacity bitmask being tested. * @closid: Intended closid for @cbm. * @exclusive: Only check if overlaps with exclusive resource groups * * Resources that can be allocated using a CBM can use the CBM to control * the overlap of these allocations. rdtgroup_cmb_overlaps() is the test * for overlap. Overlap test is not limited to the specific resource for * which the CBM is intended though - when dealing with CDP resources that * share the underlying hardware the overlap check should be performed on * the CDP resource sharing the hardware also. * * Refer to description of __rdtgroup_cbm_overlaps() for the details of the * overlap test. * * Return: true if CBM overlap detected, false if there is no overlap
*/ bool rdtgroup_cbm_overlaps(struct resctrl_schema *s, struct rdt_ctrl_domain *d, unsignedlong cbm, int closid, bool exclusive)
{ enum resctrl_conf_type peer_type = resctrl_peer_type(s->conf_type); struct rdt_resource *r = s->res;
if (__rdtgroup_cbm_overlaps(r, d, cbm, closid, s->conf_type,
exclusive)) returntrue;
/** * rdtgroup_mode_test_exclusive - Test if this resource group can be exclusive * @rdtgrp: Resource group identified through its closid. * * An exclusive resource group implies that there should be no sharing of * its allocated resources. At the time this group is considered to be * exclusive this test can determine if its current schemata supports this * setting by testing for overlap with all other resource groups. * * Return: true if resource group can be exclusive, false if there is overlap * with allocations of other resource groups and thus this resource group * cannot be exclusive.
*/ staticbool rdtgroup_mode_test_exclusive(struct rdtgroup *rdtgrp)
{ int closid = rdtgrp->closid; struct rdt_ctrl_domain *d; struct resctrl_schema *s; struct rdt_resource *r; bool has_cache = false;
u32 ctrl;
/* Walking r->domains, ensure it can't race with cpuhp */
lockdep_assert_cpus_held();
if (mode == RDT_MODE_PSEUDO_LOCKED) {
rdt_last_cmd_puts("Cannot change pseudo-locked group\n");
ret = -EINVAL; goto out;
}
if (!strcmp(buf, "shareable")) { if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
ret = rdtgroup_locksetup_exit(rdtgrp); if (ret) goto out;
}
rdtgrp->mode = RDT_MODE_SHAREABLE;
} elseif (!strcmp(buf, "exclusive")) { if (!rdtgroup_mode_test_exclusive(rdtgrp)) {
ret = -EINVAL; goto out;
} if (rdtgrp->mode == RDT_MODE_PSEUDO_LOCKSETUP) {
ret = rdtgroup_locksetup_exit(rdtgrp); if (ret) goto out;
}
rdtgrp->mode = RDT_MODE_EXCLUSIVE;
} elseif (IS_ENABLED(CONFIG_RESCTRL_FS_PSEUDO_LOCK) &&
!strcmp(buf, "pseudo-locksetup")) {
ret = rdtgroup_locksetup_enter(rdtgrp); if (ret) goto out;
rdtgrp->mode = RDT_MODE_PSEUDO_LOCKSETUP;
} else {
rdt_last_cmd_puts("Unknown or unsupported mode\n");
ret = -EINVAL;
}
out:
rdtgroup_kn_unlock(of->kn); return ret ?: nbytes;
}
/** * rdtgroup_cbm_to_size - Translate CBM to size in bytes * @r: RDT resource to which @d belongs. * @d: RDT domain instance. * @cbm: bitmask for which the size should be computed. * * The bitmask provided associated with the RDT domain instance @d will be * translated into how many bytes it represents. The size in bytes is * computed by first dividing the total cache size by the CBM length to * determine how many bytes each bit in the bitmask represents. The result * is multiplied with the number of bits set in the bitmask. * * @cbm is unsigned long, even if only 32 bits are used to make the * bitmap functions work correctly.
*/ unsignedint rdtgroup_cbm_to_size(struct rdt_resource *r, struct rdt_ctrl_domain *d, unsignedlong cbm)
{ unsignedint size = 0; struct cacheinfo *ci; int num_b;
if (WARN_ON_ONCE(r->ctrl_scope != RESCTRL_L2_CACHE && r->ctrl_scope != RESCTRL_L3_CACHE)) return size;
num_b = bitmap_weight(&cbm, r->cache.cbm_len);
ci = get_cpu_cacheinfo_level(cpumask_any(&d->hdr.cpu_mask), r->ctrl_scope); if (ci)
size = ci->size / r->cache.cbm_len * num_b;
return size;
}
bool is_mba_sc(struct rdt_resource *r)
{ if (!r)
r = resctrl_arch_get_resource(RDT_RESOURCE_MBA);
/* * The software controller support is only applicable to MBA resource. * Make sure to check for resource type.
*/ if (r->rid != RDT_RESOURCE_MBA) returnfalse;
return r->membw.mba_sc;
}
/* * rdtgroup_size_show - Display size in bytes of allocated regions * * The "size" file mirrors the layout of the "schemata" file, printing the * size in bytes of each region instead of the capacity bitmask.
*/ staticint rdtgroup_size_show(struct kernfs_open_file *of, struct seq_file *s, void *v)
{ struct resctrl_schema *schema; enum resctrl_conf_type type; struct rdt_ctrl_domain *d; struct rdtgroup *rdtgrp; struct rdt_resource *r; unsignedint size; int ret = 0;
u32 closid; bool sep;
u32 ctrl;
rdtgrp = rdtgroup_kn_lock_live(of->kn); if (!rdtgrp) {
rdtgroup_kn_unlock(of->kn); return -ENOENT;
}
/* * Read the current config value first. If both are the same then * no need to write it again.
*/
mon_info.r = r;
mon_info.d = d;
mon_info.evtid = evtid;
mondata_config_read(&mon_info); if (mon_info.mon_config == val) return;
mon_info.mon_config = val;
/* * Update MSR_IA32_EVT_CFG_BASE MSR on one of the CPUs in the * domain. The MSRs offset from MSR MSR_IA32_EVT_CFG_BASE * are scoped at the domain level. Writing any of these MSRs * on one CPU is observed by all the CPUs in the domain.
*/
smp_call_function_any(&d->hdr.cpu_mask, resctrl_arch_mon_event_config_write,
&mon_info, 1);
/* * When an Event Configuration is changed, the bandwidth counters * for all RMIDs and Events will be cleared by the hardware. The * hardware also sets MSR_IA32_QM_CTR.Unavailable (bit 62) for * every RMID on the next read to any event for every RMID. * Subsequent reads will have MSR_IA32_QM_CTR.Unavailable (bit 62) * cleared while it is tracked by the hardware. Clear the * mbm_local and mbm_total counts for all the RMIDs.
*/
resctrl_arch_reset_rmid_all(r, d);
}
/* Value from user cannot be more than the supported set of events */ if ((val & r->mbm_cfg_mask) != val) {
rdt_last_cmd_printf("Invalid event configuration: max valid mask is 0x%02x\n",
r->mbm_cfg_mask); return -EINVAL;
}
rft = rdtgroup_get_rftype_by_name(config); if (rft)
rft->fflags = fflags;
}
/** * rdtgroup_kn_mode_restrict - Restrict user access to named resctrl file * @r: The resource group with which the file is associated. * @name: Name of the file * * The permissions of named resctrl file, directory, or link are modified * to not allow read, write, or execute by any user. * * WARNING: This function is intended to communicate to the user that the * resctrl file has been locked down - that it is not relevant to the * particular state the system finds itself in. It should not be relied * on to protect from user access because after the file's permissions * are restricted the user can still change the permissions using chmod * from the command line. * * Return: 0 on success, <0 on failure.
*/ int rdtgroup_kn_mode_restrict(struct rdtgroup *r, constchar *name)
{ struct iattr iattr = {.ia_valid = ATTR_MODE,}; struct kernfs_node *kn; int ret = 0;
kn = kernfs_find_and_get_ns(r->kn, name, NULL); if (!kn) return -ENOENT;
switch (kernfs_type(kn)) { case KERNFS_DIR:
iattr.ia_mode = S_IFDIR; break; case KERNFS_FILE:
iattr.ia_mode = S_IFREG; break; case KERNFS_LINK:
iattr.ia_mode = S_IFLNK; break;
}
ret = kernfs_setattr(kn, &iattr);
kernfs_put(kn); return ret;
}
/** * rdtgroup_kn_mode_restore - Restore user access to named resctrl file * @r: The resource group with which the file is associated. * @name: Name of the file * @mask: Mask of permissions that should be restored * * Restore the permissions of the named file. If @name is a directory the * permissions of its parent will be used. * * Return: 0 on success, <0 on failure.
*/ int rdtgroup_kn_mode_restore(struct rdtgroup *r, constchar *name,
umode_t mask)
{ struct iattr iattr = {.ia_valid = ATTR_MODE,}; struct kernfs_node *kn, *parent; struct rftype *rfts, *rft; int ret, len;
rfts = res_common_files;
len = ARRAY_SIZE(res_common_files);
for (rft = rfts; rft < rfts + len; rft++) { if (!strcmp(rft->name, name))
iattr.ia_mode = rft->mode & mask;
}
kn = kernfs_find_and_get_ns(r->kn, name, NULL); if (!kn) return -ENOENT;
switch (kernfs_type(kn)) { case KERNFS_DIR:
parent = kernfs_get_parent(kn); if (parent) {
iattr.ia_mode |= parent->mode;
kernfs_put(parent);
}
iattr.ia_mode |= S_IFDIR; break; case KERNFS_FILE:
iattr.ia_mode |= S_IFREG; break; case KERNFS_LINK:
iattr.ia_mode |= S_IFLNK; break;
}
ret = kernfs_setattr(kn, &iattr);
kernfs_put(kn); return ret;
}
/* create the directory */
kn_info = kernfs_create_dir(parent_kn, "info", parent_kn->mode, NULL); if (IS_ERR(kn_info)) return PTR_ERR(kn_info);
ret = rdtgroup_add_files(kn_info, RFTYPE_TOP_INFO); if (ret) goto out_destroy;
/* loop over enabled controls, these are all alloc_capable */
list_for_each_entry(s, &resctrl_schema_all, list) {
r = s->res;
fflags = fflags_from_resource(r) | RFTYPE_CTRL_INFO;
ret = rdtgroup_mkdir_info_resdir(s, s->name, fflags); if (ret) goto out_destroy;
}
for_each_mon_capable_rdt_resource(r) {
fflags = fflags_from_resource(r) | RFTYPE_MON_INFO;
sprintf(name, "%s_MON", r->name);
ret = rdtgroup_mkdir_info_resdir(r, name, fflags); if (ret) goto out_destroy;
}
ret = rdtgroup_kn_set_ugid(kn_info); if (ret) goto out_destroy;
/* * MBA software controller is supported only if * MBM is supported and MBA is in linear scale, * and the MBM monitor scope is the same as MBA * control scope.
*/ staticbool supports_mba_mbps(void)
{ struct rdt_resource *rmbm = resctrl_arch_get_resource(RDT_RESOURCE_L3); struct rdt_resource *r = resctrl_arch_get_resource(RDT_RESOURCE_MBA);
/* * We don't allow rdtgroup directories to be created anywhere * except the root directory. Thus when looking for the rdtgroup * structure for a kernfs node we are either looking at a directory, * in which case the rdtgroup structure is pointed at by the "priv" * field, otherwise we have a file, and need only look to the parent * to find the rdtgroup.
*/ staticstruct rdtgroup *kernfs_to_rdtgroup(struct kernfs_node *kn)
{ if (kernfs_type(kn) == KERNFS_DIR) { /* * All the resource directories use "kn->priv" * to point to the "struct rdtgroup" for the * resource. "info" and its subdirectories don't * have rdtgroup structures, so return NULL here.
*/ if (kn == kn_info ||
rcu_access_pointer(kn->__parent) == kn_info) return NULL; else return kn->priv;
} else { return rdt_kn_parent_priv(kn);
}
}
s->conf_type = type; switch (type) { case CDP_CODE:
suffix = "CODE"; break; case CDP_DATA:
suffix = "DATA"; break; case CDP_NONE:
suffix = ""; break;
}
ret = snprintf(s->name, sizeof(s->name), "%s%s", r->name, suffix); if (ret >= sizeof(s->name)) {
kfree(s); return -EINVAL;
}
cl = strlen(s->name);
/* * If CDP is supported by this resource, but not enabled, * include the suffix. This ensures the tabular format of the * schemata file does not change between mounts of the filesystem.
*/ if (r->cdp_capable && !resctrl_arch_get_cdp_enabled(r->rid))
cl += 4;
if (cl > max_name_width)
max_name_width = cl;
switch (r->schema_fmt) { case RESCTRL_SCHEMA_BITMAP:
s->fmt_str = "%d=%x"; break; case RESCTRL_SCHEMA_RANGE:
s->fmt_str = "%d=%u"; break;
}
if (WARN_ON_ONCE(!s->fmt_str)) {
kfree(s); return -EINVAL;
}
cpus_read_lock();
mutex_lock(&rdtgroup_mutex); /* * resctrl file system can only be mounted once.
*/ if (resctrl_mounted) {
ret = -EBUSY; goto out;
}
ret = rdtgroup_setup_root(ctx); if (ret) goto out;
ret = rdt_enable_ctx(ctx); if (ret) goto out_root;
ret = schemata_list_create(); if (ret) {
schemata_list_destroy(); goto out_ctx;
}
ret = closid_init(); if (ret) goto out_schemata_free;
if (resctrl_arch_mon_capable())
flags |= RFTYPE_MON;
ret = rdtgroup_add_files(rdtgroup_default.kn, flags); if (ret) goto out_closid_exit;
kernfs_activate(rdtgroup_default.kn);
ret = rdtgroup_create_info_dir(rdtgroup_default.kn); if (ret < 0) goto out_closid_exit;
if (resctrl_arch_mon_capable()) {
ret = mongroup_create_dir(rdtgroup_default.kn,
&rdtgroup_default, "mon_groups",
&kn_mongrp); if (ret < 0) goto out_info;
ret = mkdir_mondata_all(rdtgroup_default.kn,
&rdtgroup_default, &kn_mondata); if (ret < 0) goto out_mongrp;
rdtgroup_default.mon.mon_data_kn = kn_mondata;
}
ret = rdt_pseudo_lock_init(); if (ret) goto out_mondata;
ret = kernfs_get_tree(fc); if (ret < 0) goto out_psl;
if (resctrl_arch_alloc_capable())
resctrl_arch_enable_alloc(); if (resctrl_arch_mon_capable())
resctrl_arch_enable_mon();
if (resctrl_arch_alloc_capable() || resctrl_arch_mon_capable())
resctrl_mounted = true;
if (resctrl_is_mbm_enabled()) {
r = resctrl_arch_get_resource(RDT_RESOURCE_L3);
list_for_each_entry(dom, &r->mon_domains, hdr.list)
mbm_setup_overflow_handler(dom, MBM_OVERFLOW_INTERVAL,
RESCTRL_PICK_ANY_CPU);
}
goto out;
out_psl:
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.16 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.