提交 e37caef1 编写于 作者: Y Yang Yingliang 提交者: Zheng Zengkai

resctrlfs: mpam: Build basic framework for mpam

hulk inclusion
category: feature
feature: ARM MPAM support
bugzilla: 48265
CVE: NA

--------------------------------

Build basic framework for mpam.
Signed-off-by: NXie XiuQi <xiexiuqi@huawei.com>
Reviewed-by: NHanjun Guo <guohanjun@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
Signed-off-by: NWang ShaoBo <bobo.shaobowang@huawei.com>
Reviewed-by: NCheng Jian <cj.chengjian@huawei.com>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 6065f551
......@@ -990,6 +990,14 @@ config HOTPLUG_CPU
Say Y here to experiment with turning CPUs off and on. CPUs
can be controlled through /sys/devices/system/cpu.
config MPAM
bool "Support Memory Partitioning and Monitoring"
default n
select RESCTRL
help
Memory Partitioning and Monitoring. More exactly Memory system
performance resource Partitioning and Monitoring
# Common NUMA Features
config NUMA
bool "NUMA Memory Allocation and Scheduler Support"
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_ARM64_MPAM_H
#define _ASM_ARM64_MPAM_H
#include <linux/sched.h>
#include <linux/kernfs.h>
#include <linux/jump_label.h>
#include <linux/seq_buf.h>
#include <linux/seq_file.h>
/* MPAM register */
#define SYS_MPAM0_EL1 sys_reg(3, 0, 10, 5, 1)
#define SYS_MPAM1_EL1 sys_reg(3, 0, 10, 5, 0)
#define SYS_MPAM2_EL2 sys_reg(3, 4, 10, 5, 0)
#define SYS_MPAM3_EL3 sys_reg(3, 6, 10, 5, 0)
#define SYS_MPAM1_EL12 sys_reg(3, 5, 10, 5, 0)
#define SYS_MPAMHCR_EL2 sys_reg(3, 4, 10, 4, 0)
#define SYS_MPAMVPMV_EL2 sys_reg(3, 4, 10, 4, 1)
#define SYS_MPAMVPMn_EL2(n) sys_reg(3, 4, 10, 6, n)
#define SYS_MPAMIDR_EL1 sys_reg(3, 0, 10, 4, 4)
#define MPAM_MASK(n) ((1UL << n) - 1)
/* plan to use GENMASK(n, 0) instead */
/*
* MPAMx_ELn:
* 15:0 PARTID_I
* 31:16 PARTID_D
* 39:32 PMG_I
* 47:40 PMG_D
* 48 TRAPMPAM1EL1
* 49 TRAPMPAM0EL1
* 61:49 Reserved
* 62 TRAPLOWER
* 63 MPAMEN
*/
#define PARTID_BITS (16)
#define PMG_BITS (8)
#define PARTID_MASK MPAM_MASK(PARTID_BITS)
#define PMG_MASK MPAM_MASK(PMG_BITS)
#define PARTID_I_SHIFT (0)
#define PARTID_D_SHIFT (PARTID_I_SHIFT + PARTID_BITS)
#define PMG_I_SHIFT (PARTID_D_SHIFT + PARTID_BITS)
#define PMG_D_SHIFT (PMG_I_SHIFT + PMG_BITS)
#define PARTID_I_MASK (PARTID_MASK << PARTID_I_SHIFT)
#define PARTID_D_MASK (PARTID_MASK << PARTID_D_SHIFT)
#define PARTID_I_CLR(r) ((r) & ~PARTID_I_MASK)
#define PARTID_D_CLR(r) ((r) & ~PARTID_D_MASK)
#define PARTID_CLR(r) (PARTID_I_CLR(r) & PARTID_D_CLR(r))
#define PARTID_I_SET(r, id) (PARTID_I_CLR(r) | ((id) << PARTID_I_SHIFT))
#define PARTID_D_SET(r, id) (PARTID_D_CLR(r) | ((id) << PARTID_D_SHIFT))
#define PARTID_SET(r, id) (PARTID_CLR(r) | ((id) << PARTID_I_SHIFT) | ((id) << PARTID_D_SHIFT))
#define PMG_I_MASK (PMG_MASK << PMG_I_SHIFT)
#define PMG_D_MASK (PMG_MASK << PMG_D_SHIFT)
#define PMG_I_CLR(r) ((r) & ~PMG_I_MASK)
#define PMG_D_CLR(r) ((r) & ~PMG_D_MASK)
#define PMG_CLR(r) (PMG_I_CLR(r) & PMG_D_CLR(r))
#define PMG_I_SET(r, id) (PMG_I_CLR(r) | ((id) << PMG_I_SHIFT))
#define PMG_D_SET(r, id) (PMG_D_CLR(r) | ((id) << PMG_D_SHIFT))
#define PMG_SET(r, id) (PMG_CLR(r) | ((id) << PMG_I_SHIFT) | ((id) << PMG_D_SHIFT))
#define TRAPMPAM1EL1_SHIFT (PMG_D_SHIFT + PMG_BITS)
#define TRAPMPAM0EL1_SHIFT (TRAPMPAM1EL1_SHIFT + 1)
#define TRAPLOWER_SHIFT (TRAPMPAM0EL1_SHIFT + 13)
#define MPAMEN_SHIFT (TRAPLOWER_SHIFT + 1)
/*
* MPAMHCR_EL2:
* 0 EL0_VPMEN
* 1 EL1_VPMEN
* 7:2 Reserved
* 8 GSTAPP_PLK
* 30:9 Reserved
* 31 TRAP_MPAMIDR_EL1
* 63:32 Reserved
*/
#define EL0_VPMEN_SHIFT (0)
#define EL1_VPMEN_SHIFT (EL0_VPMEN_SHIFT + 1)
#define GSTAPP_PLK_SHIFT (8)
#define TRAP_MPAMIDR_EL1_SHIFT (31)
/*
* MPAMIDR_EL1:
* 15:0 PARTID_MAX
* 16 Reserved
* 17 HAS_HCR
* 20:18 VPMR_MAX
* 31:21 Reserved
* 39:32 PMG_MAX
* 63:40 Reserved
*/
#define VPMR_MAX_BITS (3)
#define PARTID_MAX_SHIFT (0)
#define HAS_HCR_SHIFT (PARTID_MAX_SHIFT + PARTID_BITS + 1)
#define VPMR_MAX_SHIFT (HAS_HCR_SHIFT + 1)
#define PMG_MAX_SHIFT (VPMR_MAX_SHIFT + VPMR_MAX_BITS + 11)
#define VPMR_MASK MPAM_MASK(VPMR_MAX_BITS)
/*
* MPAMVPMV_EL2:
* 31:0 VPM_V
* 63:32 Reserved
*/
#define VPM_V_BITS 32
DECLARE_STATIC_KEY_FALSE(resctrl_enable_key);
DECLARE_STATIC_KEY_FALSE(resctrl_mon_enable_key);
extern bool rdt_alloc_capable;
extern bool rdt_mon_capable;
enum rdt_group_type {
RDTCTRL_GROUP = 0,
RDTMON_GROUP,
RDT_NUM_GROUP,
};
/**
* struct mongroup - store mon group's data in resctrl fs.
* @mon_data_kn kernlfs node for the mon_data directory
* @parent: parent rdtgrp
* @crdtgrp_list: child rdtgroup node list
* @rmid: rmid for this rdtgroup
*/
struct mongroup {
struct kernfs_node *mon_data_kn;
struct rdtgroup *parent;
struct list_head crdtgrp_list;
u32 rmid;
};
/**
* struct rdtgroup - store rdtgroup's data in resctrl file system.
* @kn: kernfs node
* @resctrl_group_list: linked list for all rdtgroups
* @closid: closid for this rdtgroup
* #endif
* @cpu_mask: CPUs assigned to this rdtgroup
* @flags: status bits
* @waitcount: how many cpus expect to find this
* group when they acquire resctrl_group_mutex
* @type: indicates type of this rdtgroup - either
* monitor only or ctrl_mon group
* @mon: mongroup related data
*/
struct rdtgroup {
struct kernfs_node *kn;
struct list_head resctrl_group_list;
u32 closid;
struct cpumask cpu_mask;
int flags;
atomic_t waitcount;
enum rdt_group_type type;
struct mongroup mon;
};
extern int max_name_width, max_data_width;
/* rdtgroup.flags */
#define RDT_DELETED 1
/**
* struct rdt_domain - group of cpus sharing an RDT resource
* @list: all instances of this resource
* @id: unique id for this instance
* @cpu_mask: which cpus share this resource
* @rmid_busy_llc:
* bitmap of which limbo RMIDs are above threshold
* @mbm_total: saved state for MBM total bandwidth
* @mbm_local: saved state for MBM local bandwidth
* @mbm_over: worker to periodically read MBM h/w counters
* @cqm_limbo: worker to periodically read CQM h/w counters
* @mbm_work_cpu:
* worker cpu for MBM h/w counters
* @cqm_work_cpu:
* worker cpu for CQM h/w counters
* @ctrl_val: array of cache or mem ctrl values (indexed by CLOSID)
* @new_ctrl: new ctrl value to be loaded
* @have_new_ctrl: did user provide new_ctrl for this domain
*/
struct rdt_domain {
struct list_head list;
int id;
struct cpumask cpu_mask;
void __iomem *base;
/* arch specific fields */
u32 *ctrl_val;
u32 new_ctrl;
bool have_new_ctrl;
/* for debug */
char *cpus_list;
};
extern struct mutex resctrl_group_mutex;
extern struct resctrl_resource resctrl_resources_all[];
int __init resctrl_group_init(void);
enum {
MPAM_RESOURCE_SMMU,
MPAM_RESOURCE_CACHE,
MPAM_RESOURCE_MC,
/* Must be the last */
MPAM_NUM_RESOURCES,
};
void rdt_last_cmd_clear(void);
void rdt_last_cmd_puts(const char *s);
void rdt_last_cmd_printf(const char *fmt, ...);
int alloc_rmid(void);
void free_rmid(u32 rmid);
int resctrl_group_mondata_show(struct seq_file *m, void *arg);
void rmdir_mondata_subdir_allrdtgrp(struct resctrl_resource *r,
unsigned int dom_id);
void mkdir_mondata_subdir_allrdtgrp(struct resctrl_resource *r,
struct rdt_domain *d);
void closid_init(void);
int closid_alloc(void);
void closid_free(int closid);
int cdp_enable(int level, int data_type, int code_type);
void resctrl_resource_reset(void);
void release_rdtgroupfs_options(void);
int parse_rdtgroupfs_options(char *data);
static inline int __resctrl_group_show_options(struct seq_file *seq)
{
return 0;
}
void post_resctrl_mount(void);
#define MPAM_SYS_REG_DEBUG
#ifdef MPAM_SYS_REG_DEBUG
static inline u64 mpam_read_sysreg_s(u64 reg, char *name)
{
pr_info("cpu %2d (cur: %s(%d)): read_sysreg_s: %s (addr %016llx)\n",
smp_processor_id(), current->comm, current->pid, name, reg);
return 0;
}
#else
#define mpam_read_sysreg_s(reg, name) read_sysreg_s(reg)
#endif
#ifdef MPAM_SYS_REG_DEBUG
static inline u64 mpam_write_sysreg_s(u64 v, u64 reg, char *name)
{
pr_info("cpu %2d (cur %s(%d)): write_sysreg_s: %s (addr %016llx), value %016llx\n",
smp_processor_id(), current->comm, current->pid, name, reg, v);
return 0;
}
#else
#define mpam_write_sysreg_s(v, r, n) write_sysreg_s(v, r)
#endif
#ifdef MPAM_SYS_REG_DEBUG
static inline u32 mpam_readl(const volatile void __iomem *addr)
{
return pr_info("readl: %016llx\n", (u64)addr);
}
#else
#define mpam_readl(addr) readl(addr)
#endif
#ifdef MPAM_SYS_REG_DEBUG
static inline u32 mpam_writel(u64 v, const volatile void __iomem *addr)
{
return pr_info("writel: %016llx to %016llx\n", v, (u64)addr);
}
#else
#define mpam_writel(v, addr) writel(v, addr)
#endif
/**
* struct msr_param - set a range of MSRs from a domain
* @res: The resource to use
* @value: value
*/
struct msr_param {
struct resctrl_resource *res;
u64 value;
};
/**
* struct resctrl_resource - attributes of an RDT resource
* @rid: The index of the resource
* @alloc_enabled: Is allocation enabled on this machine
* @mon_enabled: Is monitoring enabled for this feature
* @alloc_capable: Is allocation available on this machine
* @mon_capable: Is monitor feature available on this machine
* @name: Name to use in "schemata" file
* @num_closid: Number of CLOSIDs available
* @cache_level: Which cache level defines scope of this resource
* @default_ctrl: Specifies default cache cbm or memory B/W percent.
* @msr_base: Base MSR address for CBMs
* @msr_update: Function pointer to update QOS MSRs
* @data_width: Character width of data when displaying
* @domains: All domains for this resource
* @cache: Cache allocation related data
* @format_str: Per resource format string to show domain value
* @parse_ctrlval: Per resource function pointer to parse control values
* @evt_list: List of monitoring events
* @num_rmid: Number of RMIDs available
* @mon_scale: cqm counter * mon_scale = occupancy in bytes
* @fflags: flags to choose base and info files
*/
struct raw_resctrl_resource {
int num_partid;
u32 default_ctrl;
void (*msr_update) (struct rdt_domain *d, int partid);
u64 (*msr_read) (struct rdt_domain *d, int partid);
int data_width;
const char *format_str;
int (*parse_ctrlval) (char *buf, struct raw_resctrl_resource *r,
struct rdt_domain *d);
int num_pmg;
};
int parse_cbm(char *buf, struct raw_resctrl_resource *r, struct rdt_domain *d);
#endif /* _ASM_ARM64_MPAM_H */
/* mpam resource: like L3, memory */
#ifndef _ASM_ARM64_MPAM_RESOURCE_H
#define _ASM_ARM64_MPAM_RESOURCE_H
#include <linux/bitops.h>
#define MPAMF_IDR 0x0000
#define MPAMF_SIDR 0x0008
#define MPAMF_MSMON_IDR 0x0080
#define MPAMF_IMPL_IDR 0x0028
#define MPAMF_CPOR_IDR 0x0030
#define MPAMF_CCAP_IDR 0x0038
#define MPAMF_MBW_IDR 0x0040
#define MPAMF_PRI_IDR 0x0048
#define MPAMF_CSUMON_IDR 0x0088
#define MPAMF_MBWUMON_IDR 0x0090
#define MPAMF_PARTID_NRW_IDR 0x0050
#define MPAMF_IIDR 0x0018
#define MPAMF_AIDR 0x0020
#define MPAMCFG_PART_SEL 0x0100
#define MPAMCFG_CPBM 0x1000
#define MPAMCFG_CMAX 0x0108
#define MPAMCFG_MBW_MIN 0x0200
#define MPAMCFG_MBW_MAX 0x0208
#define MPAMCFG_MBW_WINWD 0x0220
#define MPAMCFG_MBW_PBM 0x2000
#define MPAMCFG_PRI 0x0400
#define MPAMCFG_MBW_PROP 0x0500
#define MPAMCFG_INTPARTID 0x0600
#define MSMON_CFG_MON_SEL 0x0800
#define MSMON_CFG_CSU_FLT 0x0810
#define MSMON_CFG_CSU_CTL 0x0818
#define MSMON_CFG_MBWU_FLT 0x0820
#define MSMON_CFG_MBWU_CTL 0x0828
#define MSMON_CSU 0x0840
#define MSMON_CSU_CAPTURE 0x0848
#define MSMON_MBWU 0x0860
#define MSMON_MBWU_CAPTURE 0x0868
#define MSMON_CAPT_EVNT 0x0808
#define MPAMF_ESR 0x00F8
#define MPAMF_ECR 0x00F0
#define HAS_CCAP_PART BIT(24)
#define HAS_CPOR_PART BIT(25)
#define HAS_MBW_PART BIT(26)
#define HAS_PRI_PART BIT(27)
#define HAS_IMPL_IDR BIT(29)
#define HAS_MSMON BIT(30)
/* MPAMF_IDR */
/* TODO */
#define CPBM_WD_MASK 0xFFFF
#define CPBM_MASK 0x7FFF
#define BWA_WD 6 /* hard code for P680 */
#define MBW_MAX_MASK 0xFC00
#define MBW_MAX_HARDLIM BIT(31)
/* [FIXME] hard code for hardlim */
#define MBW_MAX_SET(v) (MBW_MAX_HARDLIM|((v) << (15 - BWA_WD)))
#define MBW_MAX_GET(v) (((v) & MBW_MAX_MASK) >> (15 - BWA_WD))
/*
* emulate the mpam nodes
* These should be reported by ACPI MPAM Table.
*/
struct mpam_node {
/* MPAM node header */
u8 type; /* MPAM_SMMU, MPAM_CACHE, MPAM_MC */
u64 addr;
void __iomem *base;
struct cpumask cpu_mask;
u64 default_ctrl;
/* for debug */
char *cpus_list;
char *name;
};
int mpam_nodes_init(void);
#endif /* _ASM_ARM64_MPAM_RESOURCE_H */
#ifndef _ASM_ARM64_MPAM_SCHED_H
#define _ASM_ARM64_MPAM_SCHED_H
#ifdef CONFIG_MPAM
#include <linux/sched.h>
#include <linux/jump_label.h>
/**
* struct intel_pqr_state - State cache for the PQR MSR
* @cur_rmid: The cached Resource Monitoring ID
* @cur_closid: The cached Class Of Service ID
* @default_rmid: The user assigned Resource Monitoring ID
* @default_closid: The user assigned cached Class Of Service ID
*
* The upper 32 bits of IA32_PQR_ASSOC contain closid and the
* lower 10 bits rmid. The update to IA32_PQR_ASSOC always
* contains both parts, so we need to cache them. This also
* stores the user configured per cpu CLOSID and RMID.
*
* The cache also helps to avoid pointless updates if the value does
* not change.
*/
struct intel_pqr_state {
u32 cur_rmid;
u32 cur_closid;
u32 default_rmid;
u32 default_closid;
};
DECLARE_PER_CPU(struct intel_pqr_state, pqr_state);
extern void __mpam_sched_in(void);
DECLARE_STATIC_KEY_FALSE(resctrl_enable_key);
static inline void mpam_sched_in(void)
{
if (static_branch_likely(&resctrl_enable_key))
__mpam_sched_in();
}
#else
static inline void mpam_sched_in(void) {}
#endif /* CONFIG_MPAM */
#endif
#ifndef _ASM_ARM64_RESCTRL_H
#define _ASM_ARM64_RESCTRL_H
#include <asm/mpam_sched.h>
#include <asm/mpam.h>
#define resctrl_group rdtgroup
#define resctrl_alloc_capable rdt_alloc_capable
#define resctrl_mon_capable rdt_mon_capable
static inline int alloc_mon_id(void)
{
return alloc_rmid();
}
static inline void free_mon_id(u32 id)
{
free_rmid(id);
}
void pmg_init(void);
static inline void resctrl_id_init(void)
{
closid_init();
pmg_init();
}
static inline int resctrl_id_alloc(void)
{
return closid_alloc();
}
static inline void resctrl_id_free(int id)
{
closid_free(id);
}
void update_cpu_closid_rmid(void *info);
void update_closid_rmid(const struct cpumask *cpu_mask, struct resctrl_group *r);
int __resctrl_group_move_task(struct task_struct *tsk,
struct resctrl_group *rdtgrp);
ssize_t resctrl_group_schemata_write(struct kernfs_open_file *of,
char *buf, size_t nbytes, loff_t off);
int resctrl_group_schemata_show(struct kernfs_open_file *of,
struct seq_file *s, void *v);
#define release_resctrl_group_fs_options release_rdtgroupfs_options
#define parse_resctrl_group_fs_options parse_rdtgroupfs_options
#define for_each_resctrl_resource(r) \
for (r = resctrl_resources_all; \
r < resctrl_resources_all + MPAM_NUM_RESOURCES; \
r++) \
int mkdir_mondata_all(struct kernfs_node *parent_kn,
struct resctrl_group *prgrp,
struct kernfs_node **dest_kn);
int
mongroup_create_dir(struct kernfs_node *parent_kn, struct resctrl_group *prgrp,
char *name, struct kernfs_node **dest_kn);
#endif /* _ASM_ARM64_RESCTRL_H */
......@@ -64,6 +64,7 @@ obj-$(CONFIG_SDEI_WATCHDOG) += watchdog_sdei.o
obj-$(CONFIG_ARM64_PTR_AUTH) += pointer_auth.o
obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o
obj-$(CONFIG_ARM64_MTE) += mte.o
obj-$(CONFIG_MPAM) += mpam.o mpam_ctrlmon.o mpam_mon.o mpam_resource.o
obj-y += vdso/ probes/
obj-$(CONFIG_COMPAT_VDSO) += vdso32/
......
此差异已折叠。
/*
* Resource Director Technology(RDT)
* - Cache Allocation code.
*
* Copyright (C) 2016 Intel Corporation
*
* Authors:
* Fenghua Yu <fenghua.yu@intel.com>
* Tony Luck <tony.luck@intel.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* More information about RDT be found in the Intel (R) x86 Architecture
* Software Developer Manual June 2016, volume 3, section 17.17.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernfs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/resctrlfs.h>
#include <asm/mpam.h>
#include <asm/mpam_resource.h>
#include <asm/resctrl.h>
/*
* Check whether a cache bit mask is valid. The SDM says:
* Please note that all (and only) contiguous '1' combinations
* are allowed (e.g. FFFFH, 0FF0H, 003CH, etc.).
* Additionally Haswell requires at least two bits set.
*/
static bool cbm_validate(char *buf, unsigned long *data, struct raw_resctrl_resource *r)
{
u64 val;
int ret;
ret = kstrtou64(buf, 16, &val);
if (ret) {
rdt_last_cmd_printf("non-hex character in mask %s\n", buf);
return false;
}
#if 0
if (val == 0 || val > r->default_ctrl) {
rdt_last_cmd_puts("mask out of range\n");
return false;
}
#endif
*data = val;
return true;
}
/*
* Read one cache bit mask (hex). Check that it is valid for the current
* resource type.
*/
int parse_cbm(char *buf, struct raw_resctrl_resource *r, struct rdt_domain *d)
{
unsigned long data;
if (d->have_new_ctrl) {
rdt_last_cmd_printf("duplicate domain %d\n", d->id);
return -EINVAL;
}
if (!cbm_validate(buf, &data, r))
return -EINVAL;
d->new_ctrl = data;
d->have_new_ctrl = true;
return 0;
}
/*
* For each domain in this resource we expect to find a series of:
* id=mask
* separated by ";". The "id" is in decimal, and must match one of
* the "id"s for this resource.
*/
static int parse_line(char *line, struct resctrl_resource *r)
{
struct raw_resctrl_resource *rr = (struct raw_resctrl_resource *)r->res;
char *dom = NULL, *id;
struct rdt_domain *d;
unsigned long dom_id;
next:
if (!line || line[0] == '\0')
return 0;
dom = strsep(&line, ";");
id = strsep(&dom, "=");
if (!dom || kstrtoul(id, 10, &dom_id)) {
rdt_last_cmd_puts("Missing '=' or non-numeric domain\n");
return -EINVAL;
}
dom = strim(dom);
list_for_each_entry(d, &r->domains, list) {
if (d->id == dom_id) {
if (rr->parse_ctrlval(dom, (struct raw_resctrl_resource *)&r->res, d))
return -EINVAL;
goto next;
}
}
return -EINVAL;
}
static int update_domains(struct resctrl_resource *r, int partid)
{
struct raw_resctrl_resource *rr;
struct rdt_domain *d;
rr = (struct raw_resctrl_resource *)r->res;
list_for_each_entry(d, &r->domains, list) {
if (d->have_new_ctrl && d->new_ctrl != d->ctrl_val[partid]) {
d->ctrl_val[partid] = d->new_ctrl;
rr->msr_update(d, partid);
}
}
return 0;
}
static int resctrl_group_parse_resource(char *resname, char *tok, int closid)
{
struct resctrl_resource *r;
struct raw_resctrl_resource *rr;
for_each_resctrl_resource(r) {
if (r->alloc_enabled) {
rr = (struct raw_resctrl_resource *)r->res;
if (!strcmp(resname, r->name) && closid < rr->num_partid)
return parse_line(tok, r);
}
}
rdt_last_cmd_printf("unknown/unsupported resource name '%s'\n", resname);
return -EINVAL;
}
ssize_t resctrl_group_schemata_write(struct kernfs_open_file *of,
char *buf, size_t nbytes, loff_t off)
{
struct rdtgroup *rdtgrp;
struct rdt_domain *dom;
struct resctrl_resource *r;
char *tok, *resname;
int closid, ret = 0;
/* Valid input requires a trailing newline */
if (nbytes == 0 || buf[nbytes - 1] != '\n')
return -EINVAL;
buf[nbytes - 1] = '\0';
rdtgrp = resctrl_group_kn_lock_live(of->kn);
if (!rdtgrp) {
resctrl_group_kn_unlock(of->kn);
return -ENOENT;
}
rdt_last_cmd_clear();
closid = rdtgrp->closid;
for_each_resctrl_resource(r) {
if (r->alloc_enabled) {
list_for_each_entry(dom, &r->domains, list)
dom->have_new_ctrl = false;
}
}
while ((tok = strsep(&buf, "\n")) != NULL) {
resname = strim(strsep(&tok, ":"));
if (!tok) {
rdt_last_cmd_puts("Missing ':'\n");
ret = -EINVAL;
goto out;
}
if (tok[0] == '\0') {
rdt_last_cmd_printf("Missing '%s' value\n", resname);
ret = -EINVAL;
goto out;
}
ret = resctrl_group_parse_resource(resname, tok, closid);
if (ret)
goto out;
}
for_each_resctrl_resource(r) {
if (r->alloc_enabled) {
ret = update_domains(r, closid);
if (ret)
goto out;
}
}
out:
resctrl_group_kn_unlock(of->kn);
return ret ?: nbytes;
}
static void show_doms(struct seq_file *s, struct resctrl_resource *r, int partid)
{
struct raw_resctrl_resource *rr = (struct raw_resctrl_resource *)r->res;
struct rdt_domain *dom;
bool sep = false;
seq_printf(s, "%*s:", max_name_width, r->name);
list_for_each_entry(dom, &r->domains, list) {
if (sep)
seq_puts(s, ";");
seq_printf(s, rr->format_str, dom->id, max_data_width,
rr->msr_read(dom, partid));
sep = true;
}
seq_puts(s, "\n");
}
int resctrl_group_schemata_show(struct kernfs_open_file *of,
struct seq_file *s, void *v)
{
struct rdtgroup *rdtgrp;
struct resctrl_resource *r;
struct raw_resctrl_resource *rr;
int ret = 0;
u32 partid;
rdtgrp = resctrl_group_kn_lock_live(of->kn);
if (rdtgrp) {
partid = rdtgrp->closid;
for_each_resctrl_resource(r) {
if (r->alloc_enabled) {
rr = (struct raw_resctrl_resource *)r->res;
if (partid < rr->num_partid)
show_doms(s, r, partid);
}
}
} else {
ret = -ENOENT;
}
resctrl_group_kn_unlock(of->kn);
return ret;
}
/*
* [FIXME]
* use pmg as monitor id
* just use match_pardid only.
*/
static u64 mbwu_read(struct rdt_domain *d, struct rdtgroup *g)
{
u32 pmg = g->mon.rmid;
mpam_writel(pmg, d->base + MSMON_CFG_MON_SEL);
return mpam_readl(d->base + MSMON_MBWU);
}
static u64 csu_read(struct rdt_domain *d, struct rdtgroup *g)
{
u32 pmg = g->mon.rmid;
mpam_writel(pmg, d->base + MSMON_CFG_MON_SEL);
return mpam_readl(d->base + MSMON_CSU);
}
int resctrl_group_mondata_show(struct seq_file *m, void *arg)
{
struct kernfs_open_file *of = m->private;
struct rdtgroup *rdtgrp;
struct rdt_domain *d;
int ret = 0;
rdtgrp = resctrl_group_kn_lock_live(of->kn);
d = of->kn->priv;
/* for debug */
seq_printf(m, "group: partid: %d, pmg: %d",
rdtgrp->closid, rdtgrp->mon.rmid);
/* show monitor data */
resctrl_group_kn_unlock(of->kn);
return ret;
}
static struct kernfs_ops kf_mondata_ops = {
.atomic_write_len = PAGE_SIZE,
.seq_show = resctrl_group_mondata_show,
};
/* set uid and gid of resctrl_group dirs and files to that of the creator */
static int resctrl_group_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);
}
#if 0 /* used at remove cpu*/
/*
* Remove all subdirectories of mon_data of ctrl_mon groups
* and monitor groups with given domain id.
*/
void rmdir_mondata_subdir_allrdtgrp(struct resctrl_resource *r, unsigned int dom_id)
{
struct resctrl_group *prgrp, *crgrp;
char name[32];
if (!r->mon_enabled)
return;
list_for_each_entry(prgrp, &resctrl_all_groups, resctrl_group_list) {
sprintf(name, "mon_%s_%02d", r->name, dom_id);
kernfs_remove_by_name(prgrp->mon.mon_data_kn, name);
list_for_each_entry(crgrp, &prgrp->mon.crdtgrp_list, mon.crdtgrp_list)
kernfs_remove_by_name(crgrp->mon.mon_data_kn, name);
}
}
#endif
static int mkdir_mondata_subdir(struct kernfs_node *parent_kn,
struct rdt_domain *d,
struct resctrl_resource *r, struct resctrl_group *prgrp)
{
#if 1
struct kernfs_node *kn;
char name[32];
int ret;
sprintf(name, "mon_%s_%02d", r->name, d->id);
kn = __kernfs_create_file(parent_kn, name, 0444,
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, 0,
&kf_mondata_ops, d, NULL, NULL);
if (IS_ERR(kn))
return PTR_ERR(kn);
ret = resctrl_group_kn_set_ugid(kn);
if (ret) {
kernfs_remove(kn);
return ret;
}
return ret;
#if 0
/* create the directory */
kn = kernfs_create_dir(parent_kn, name, parent_kn->mode, prgrp);
if (IS_ERR(kn))
return PTR_ERR(kn);
/*
* This extra ref will be put in kernfs_remove() and guarantees
* that kn is always accessible.
*/
kernfs_get(kn);
ret = resctrl_group_kn_set_ugid(kn);
if (ret)
goto out_destroy;
#endif
#if 0
ret = mon_addfile(kn, mevt->name, d);
if (ret)
goto out_destroy;
kernfs_activate(kn);
return 0;
out_destroy:
kernfs_remove(kn);
return ret;
#endif
#else
return 0;
#endif
}
/*
* Add all subdirectories of mon_data for "ctrl_mon" groups
* and "monitor" groups with given domain id.
*/
void mkdir_mondata_subdir_allrdtgrp(struct resctrl_resource *r,
struct rdt_domain *d)
{
struct kernfs_node *parent_kn;
struct resctrl_group *prgrp, *crgrp;
struct list_head *head;
if (!r->mon_enabled)
return;
list_for_each_entry(prgrp, &resctrl_all_groups, resctrl_group_list) {
parent_kn = prgrp->mon.mon_data_kn;
mkdir_mondata_subdir(parent_kn, d, r, prgrp);
head = &prgrp->mon.crdtgrp_list;
list_for_each_entry(crgrp, head, mon.crdtgrp_list) {
parent_kn = crgrp->mon.mon_data_kn;
mkdir_mondata_subdir(parent_kn, d, r, crgrp);
}
}
}
static int mkdir_mondata_subdir_alldom(struct kernfs_node *parent_kn,
struct resctrl_resource *r,
struct resctrl_group *prgrp)
{
struct rdt_domain *dom;
int ret;
list_for_each_entry(dom, &r->domains, list) {
ret = mkdir_mondata_subdir(parent_kn, dom, r, prgrp);
if (ret)
return ret;
}
return 0;
}
int
mongroup_create_dir(struct kernfs_node *parent_kn, struct resctrl_group *prgrp,
char *name, struct kernfs_node **dest_kn)
{
struct kernfs_node *kn;
int ret;
/* create the directory */
kn = kernfs_create_dir(parent_kn, name, parent_kn->mode, prgrp);
if (IS_ERR(kn))
return PTR_ERR(kn);
if (dest_kn)
*dest_kn = kn;
/*
* This extra ref will be put in kernfs_remove() and guarantees
* that @rdtgrp->kn is always accessible.
*/
kernfs_get(kn);
ret = resctrl_group_kn_set_ugid(kn);
if (ret)
goto out_destroy;
kernfs_activate(kn);
return 0;
out_destroy:
kernfs_remove(kn);
return ret;
}
/*
* This creates a directory mon_data which contains the monitored data.
*
* mon_data has one directory for each domain whic are named
* in the format mon_<domain_name>_<domain_id>. For ex: A mon_data
* with L3 domain looks as below:
* ./mon_data:
* mon_L3_00
* mon_L3_01
* mon_L3_02
* ...
*
* Each domain directory has one file per event:
* ./mon_L3_00/:
* llc_occupancy
*
*/
int mkdir_mondata_all(struct kernfs_node *parent_kn,
struct resctrl_group *prgrp,
struct kernfs_node **dest_kn)
{
struct resctrl_resource *r;
struct kernfs_node *kn;
int ret;
/*
* Create the mon_data directory first.
*/
ret = mongroup_create_dir(parent_kn, NULL, "mon_data", &kn);
if (ret)
return ret;
if (dest_kn)
*dest_kn = kn;
/*
* Create the subdirectories for each domain. Note that all events
* in a domain like L3 are grouped into a resource whose domain is L3
*/
for_each_resctrl_resource(r) {
if (r->mon_enabled) {
ret = mkdir_mondata_subdir_alldom(kn, r, prgrp);
if (ret)
goto out_destroy;
}
}
kernfs_activate(kn);
return 0;
out_destroy:
kernfs_remove(kn);
return ret;
}
/*
* Resource Director Technology(RDT)
* - Monitoring code
*
* Copyright (C) 2017 Intel Corporation
*
* Author:
* Vikas Shivappa <vikas.shivappa@intel.com>
*
* This replaces the cqm.c based on perf but we reuse a lot of
* code and datastructures originally from Peter Zijlstra and Matt Fleming.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* More information about RDT be found in the Intel (R) x86 Architecture
* Software Developer Manual June 2016, volume 3, section 17.17.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/resctrlfs.h>
#include <asm/resctrl.h>
/*
* Global boolean for rdt_monitor which is true if any
* resource monitoring is enabled.
*/
bool rdt_mon_capable;
static int pmg_free_map;
void pmg_init(void)
{
int pmg_max = 16;
pmg_free_map = BIT_MASK(pmg_max) - 1;
/* pmg 0 is always reserved for the default group */
pmg_free_map &= ~1;
}
int alloc_pmg(void)
{
u32 pmg = ffs(pmg_free_map);
if (pmg == 0)
return -ENOSPC;
pmg--;
pmg_free_map &= ~(1 << pmg);
return pmg;
}
void free_pmg(u32 pmg)
{
pmg_free_map |= 1 << pmg;
}
/*
* As of now the RMIDs allocation is global.
* However we keep track of which packages the RMIDs
* are used to optimize the limbo list management.
*/
int alloc_rmid(void)
{
return alloc_pmg();
}
void free_rmid(u32 pmg)
{
free_pmg(pmg);
}
#include <linux/io.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/kthread.h>
#include <linux/resctrlfs.h>
#include <asm/mpam_resource.h>
#include <asm/mpam.h>
......@@ -56,6 +56,7 @@
#include <asm/processor.h>
#include <asm/pointer_auth.h>
#include <asm/stacktrace.h>
#include <asm/mpam_sched.h>
#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_STACKPROTECTOR_PER_TASK)
#include <linux/stackprotector.h>
......@@ -575,6 +576,8 @@ __notrace_funcgraph struct task_struct *__switch_to(struct task_struct *prev,
/* the actual thread switch */
last = cpu_switch_to(prev, next);
mpam_sched_in();
return last;
}
......
......@@ -350,3 +350,12 @@ config IO_WQ
bool
endmenu
config RESCTRL
bool "Support Memory Partitioning and Monitoring"
default n
depends on INTEL_RDT || MPAM
select KERNFS
help
Memory Partitioning and Monitoring. More exactly Memory system
performance resource Partitioning and Monitoring
......@@ -73,6 +73,7 @@ static int resctrl_group_add_file(struct kernfs_node *parent_kn, struct rftype *
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);
......@@ -111,7 +112,7 @@ static int __resctrl_group_add_files(struct kernfs_node *kn, unsigned long fflag
struct rftype *rfts, int len)
{
struct rftype *rft;
int ret;
int ret = 0;
lockdep_assert_held(&resctrl_group_mutex);
......@@ -138,7 +139,7 @@ static int __resctrl_group_add_files(struct kernfs_node *kn, unsigned long fflag
static int resctrl_group_add_files(struct kernfs_node *kn, unsigned long fflags)
{
int ret;
int ret = 0;
if (res_common_files)
ret = __resctrl_group_add_files(kn, fflags, res_common_files,
......@@ -187,19 +188,23 @@ static int resctrl_group_create_info_dir(struct kernfs_node *parent_kn)
if (ret)
goto out_destroy;
for_each_alloc_enabled_resctrl_resource(r) {
fflags = r->fflags | RF_CTRL_INFO;
ret = resctrl_group_mkdir_info_resdir(r, r->name, fflags);
if (ret)
goto out_destroy;
for_each_resctrl_resource(r) {
if (r->alloc_enabled) {
fflags = r->fflags | RF_CTRL_INFO;
ret = resctrl_group_mkdir_info_resdir(r, r->name, fflags);
if (ret)
goto out_destroy;
}
}
for_each_mon_enabled_resctrl_resource(r) {
fflags = r->fflags | RF_MON_INFO;
sprintf(name, "%s_MON", r->name);
ret = resctrl_group_mkdir_info_resdir(r, name, fflags);
if (ret)
goto out_destroy;
for_each_resctrl_resource(r) {
if (r->mon_enabled) {
fflags = r->fflags | RF_MON_INFO;
sprintf(name, "%s_MON", r->name);
ret = resctrl_group_mkdir_info_resdir(r, name, fflags);
if (ret)
goto out_destroy;
}
}
/*
......
......@@ -1092,7 +1092,7 @@ struct task_struct {
/* cg_list protected by css_set_lock and tsk->alloc_lock: */
struct list_head cg_list;
#endif
#ifdef CONFIG_X86_CPU_RESCTRL
#if defined(CONFIG_RESCTRL) || defined(CONFIG_X86_CPU_RESCTRL)
u32 closid;
u32 rmid;
#endif
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册