System software can read resctrl event data for a particular resource by writing the RMID and Event Identifier (EvtID) to the QM_EVTSEL register and then reading the event data from the QM_CTR register. In ABMC mode, the event data of a specific counter ID can be read by setting the following fields in QM_EVTSEL.ExtendedEvtID = 1, QM_EVTSEL.EvtID = L3CacheABMC (=1) and setting [RMID] to the desired counter ID. Reading QM_CTR will then return the contents of the specified counter ID. The E bit will be set if the counter configuration was invalid, or if an invalid counter ID was set in the QM_EVTSEL[RMID] field. Introduce __cntr_id_read_phys() to read event data for a specific counter ID. In ABMC mode, ensure QM_EVTSEL is properly configured by setting the counter ID, Extended Event Identifier, and Event Identifier. QM_EVTSEL Register definition: ======================================================= Bits Mnemonic Description ======================================================= 63:44 -- Reserved 43:32 RMID Resource Monitoring Identifier 31 ExtEvtID Extended Event Identifier 30:8 -- Reserved 7:0 EvtID Event Identifier ======================================================= Link: https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/programmer-references/40332.pdf Signed-off-by: Babu Moger <babu.moger@xxxxxxx> --- v14: Updated the context in changelog. Added text in imperative tone. Added WARN_ON_ONCE() when cntr_id < 0. Improved code documentation in include/linux/resctrl.h. Added the check in mbm_update() to skip overflow handler when counter is unassigned. v13: Split the patch into 2. First one to handle the passing of rdtgroup structure to few functions( __mon_event_count and mbm_update(). Second one to handle ABMC counter reading. Added new function __cntr_id_read_phys() to handle ABMC event reading. Updated kernel doc for resctrl_arch_reset_rmid() and resctrl_arch_rmid_read(). Resolved conflicts caused by the recent FS/ARCH code restructure. The monitor.c file has now been split between the FS and ARCH directories. v12: New patch to support extended event mode when ABMC is enabled. --- arch/x86/kernel/cpu/resctrl/internal.h | 6 +++ arch/x86/kernel/cpu/resctrl/monitor.c | 66 ++++++++++++++++++++++---- fs/resctrl/monitor.c | 26 +++++++--- include/linux/resctrl.h | 13 +++-- 4 files changed, 94 insertions(+), 17 deletions(-) diff --git a/arch/x86/kernel/cpu/resctrl/internal.h b/arch/x86/kernel/cpu/resctrl/internal.h index 23c17ce172d3..77a9ce4a8403 100644 --- a/arch/x86/kernel/cpu/resctrl/internal.h +++ b/arch/x86/kernel/cpu/resctrl/internal.h @@ -40,6 +40,12 @@ struct arch_mbm_state { /* Setting bit 0 in L3_QOS_EXT_CFG enables the ABMC feature. */ #define ABMC_ENABLE_BIT 0 +/* + * Qos Event Identifiers. + */ +#define ABMC_EXTENDED_EVT_ID BIT(31) +#define ABMC_EVT_ID BIT(0) + /** * struct rdt_hw_ctrl_domain - Arch private attributes of a set of CPUs that share * a resource for a control function diff --git a/arch/x86/kernel/cpu/resctrl/monitor.c b/arch/x86/kernel/cpu/resctrl/monitor.c index 6b0ea4b17c7a..ee0aa741cf6c 100644 --- a/arch/x86/kernel/cpu/resctrl/monitor.c +++ b/arch/x86/kernel/cpu/resctrl/monitor.c @@ -157,6 +157,41 @@ static int __rmid_read_phys(u32 prmid, enum resctrl_event_id eventid, u64 *val) return 0; } +static int __cntr_id_read_phys(u32 cntr_id, u64 *val) +{ + u64 msr_val; + + /* + * QM_EVTSEL Register definition: + * ======================================================= + * Bits Mnemonic Description + * ======================================================= + * 63:44 -- Reserved + * 43:32 RMID Resource Monitoring Identifier + * 31 ExtEvtID Extended Event Identifier + * 30:8 -- Reserved + * 7:0 EvtID Event Identifier + * ======================================================= + * The contents of a specific counter can be read by setting the + * following fields in QM_EVTSEL.ExtendedEvtID(=1) and + * QM_EVTSEL.EvtID = L3CacheABMC (=1) and setting [RMID] to the + * desired counter ID. Reading QM_CTR will then return the + * contents of the specified counter. The E bit will be set if the + * counter configuration was invalid, or if an invalid counter ID + * was set in the QM_EVTSEL[RMID] field. + */ + wrmsr(MSR_IA32_QM_EVTSEL, ABMC_EXTENDED_EVT_ID | ABMC_EVT_ID, cntr_id); + rdmsrl(MSR_IA32_QM_CTR, msr_val); + + if (msr_val & RMID_VAL_ERROR) + return -EIO; + if (msr_val & RMID_VAL_UNAVAIL) + return -EINVAL; + + *val = msr_val; + return 0; +} + static struct arch_mbm_state *get_arch_mbm_state(struct rdt_hw_mon_domain *hw_dom, u32 rmid, enum resctrl_event_id eventid) @@ -172,7 +207,7 @@ static struct arch_mbm_state *get_arch_mbm_state(struct rdt_hw_mon_domain *hw_do } void resctrl_arch_reset_rmid(struct rdt_resource *r, struct rdt_mon_domain *d, - u32 unused, u32 rmid, + u32 unused, u32 rmid, int cntr_id, enum resctrl_event_id eventid) { struct rdt_hw_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); @@ -184,9 +219,16 @@ void resctrl_arch_reset_rmid(struct rdt_resource *r, struct rdt_mon_domain *d, if (am) { memset(am, 0, sizeof(*am)); - prmid = logical_rmid_to_physical_rmid(cpu, rmid); - /* Record any initial, non-zero count value. */ - __rmid_read_phys(prmid, eventid, &am->prev_msr); + if (resctrl_arch_mbm_cntr_assign_enabled(r) && + resctrl_is_mbm_event(eventid)) { + if (WARN_ON_ONCE(cntr_id < 0)) + return; + __cntr_id_read_phys(cntr_id, &am->prev_msr); + } else { + prmid = logical_rmid_to_physical_rmid(cpu, rmid); + /* Record any initial, non-zero count value. */ + __rmid_read_phys(prmid, eventid, &am->prev_msr); + } } } @@ -218,8 +260,8 @@ static u64 mbm_overflow_count(u64 prev_msr, u64 cur_msr, unsigned int width) } int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_mon_domain *d, - u32 unused, u32 rmid, enum resctrl_event_id eventid, - u64 *val, void *ignored) + u32 unused, u32 rmid, int cntr_id, + enum resctrl_event_id eventid, u64 *val, void *ignored) { struct rdt_hw_mon_domain *hw_dom = resctrl_to_arch_mon_dom(d); struct rdt_hw_resource *hw_res = resctrl_to_arch_res(r); @@ -231,8 +273,16 @@ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_mon_domain *d, resctrl_arch_rmid_read_context_check(); - prmid = logical_rmid_to_physical_rmid(cpu, rmid); - ret = __rmid_read_phys(prmid, eventid, &msr_val); + if (resctrl_arch_mbm_cntr_assign_enabled(r) && + resctrl_is_mbm_event(eventid)) { + if (WARN_ON_ONCE(cntr_id < 0)) + return cntr_id; + ret = __cntr_id_read_phys(cntr_id, &msr_val); + } else { + prmid = logical_rmid_to_physical_rmid(cpu, rmid); + ret = __rmid_read_phys(prmid, eventid, &msr_val); + } + if (ret) return ret; diff --git a/fs/resctrl/monitor.c b/fs/resctrl/monitor.c index 31e08d891db2..ef6ef58f180b 100644 --- a/fs/resctrl/monitor.c +++ b/fs/resctrl/monitor.c @@ -159,7 +159,11 @@ void __check_limbo(struct rdt_mon_domain *d, bool force_free) break; entry = __rmid_entry(idx); - if (resctrl_arch_rmid_read(r, d, entry->closid, entry->rmid, + /* + * cntr_id is not relevant for QOS_L3_OCCUP_EVENT_ID. + * Pass dummy value -1. + */ + if (resctrl_arch_rmid_read(r, d, entry->closid, entry->rmid, -1, QOS_L3_OCCUP_EVENT_ID, &val, arch_mon_ctx)) { rmid_dirty = true; @@ -358,6 +362,7 @@ static struct mbm_state *get_mbm_state(struct rdt_mon_domain *d, u32 closid, static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) { + int cntr_id = mbm_cntr_get(rr->r, rr->d, rdtgrp, rr->evtid); int cpu = smp_processor_id(); u32 closid = rdtgrp->closid; u32 rmid = rdtgrp->mon.rmid; @@ -368,7 +373,7 @@ static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) u64 tval = 0; if (rr->first) { - resctrl_arch_reset_rmid(rr->r, rr->d, closid, rmid, rr->evtid); + resctrl_arch_reset_rmid(rr->r, rr->d, closid, rmid, cntr_id, rr->evtid); m = get_mbm_state(rr->d, closid, rmid, rr->evtid); if (m) memset(m, 0, sizeof(struct mbm_state)); @@ -379,7 +384,7 @@ static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) /* Reading a single domain, must be on a CPU in that domain. */ if (!cpumask_test_cpu(cpu, &rr->d->hdr.cpu_mask)) return -EINVAL; - rr->err = resctrl_arch_rmid_read(rr->r, rr->d, closid, rmid, + rr->err = resctrl_arch_rmid_read(rr->r, rr->d, closid, rmid, cntr_id, rr->evtid, &tval, rr->arch_mon_ctx); if (rr->err) return rr->err; @@ -405,7 +410,8 @@ static int __mon_event_count(struct rdtgroup *rdtgrp, struct rmid_read *rr) list_for_each_entry(d, &rr->r->mon_domains, hdr.list) { if (d->ci_id != rr->ci_id) continue; - err = resctrl_arch_rmid_read(rr->r, d, closid, rmid, + cntr_id = mbm_cntr_get(rr->r, d, rdtgrp, rr->evtid); + err = resctrl_arch_rmid_read(rr->r, d, closid, rmid, cntr_id, rr->evtid, &tval, rr->arch_mon_ctx); if (!err) { rr->val += tval; @@ -638,12 +644,20 @@ static void mbm_update(struct rdt_resource *r, struct rdt_mon_domain *d, /* * This is protected from concurrent reads from user as both * the user and overflow handler hold the global mutex. + * Skip the update if the counter is unassigned while mbm_event + * mode is enabled. */ - if (resctrl_is_mon_event_enabled(QOS_L3_MBM_TOTAL_EVENT_ID)) + if (resctrl_is_mon_event_enabled(QOS_L3_MBM_TOTAL_EVENT_ID) && + (!resctrl_arch_mbm_cntr_assign_enabled(r) || + mbm_cntr_get(r, d, rdtgrp, QOS_L3_MBM_TOTAL_EVENT_ID) >= 0)) { mbm_update_one_event(r, d, rdtgrp, QOS_L3_MBM_TOTAL_EVENT_ID); + } - if (resctrl_is_mon_event_enabled(QOS_L3_MBM_LOCAL_EVENT_ID)) + if (resctrl_is_mon_event_enabled(QOS_L3_MBM_LOCAL_EVENT_ID) && + (!resctrl_arch_mbm_cntr_assign_enabled(r) || + mbm_cntr_get(r, d, rdtgrp, QOS_L3_MBM_LOCAL_EVENT_ID) >= 0)) { mbm_update_one_event(r, d, rdtgrp, QOS_L3_MBM_LOCAL_EVENT_ID); + } } /* diff --git a/include/linux/resctrl.h b/include/linux/resctrl.h index 1539d1faa1a1..4b52bac5dbbc 100644 --- a/include/linux/resctrl.h +++ b/include/linux/resctrl.h @@ -507,6 +507,9 @@ void resctrl_offline_cpu(unsigned int cpu); * counter may match traffic of both @closid and @rmid, or @rmid * only. * @rmid: rmid of the counter to read. + * @cntr_id: Counter ID used to read MBM events in mbm_event mode. Only valid + * when mbm_event mode is enabled and @eventid is an MBM event. + * Can be negative when invalid. * @eventid: eventid to read, e.g. L3 occupancy. * @val: result of the counter read in bytes. * @arch_mon_ctx: An architecture specific value from @@ -524,8 +527,9 @@ void resctrl_offline_cpu(unsigned int cpu); * 0 on success, or -EIO, -EINVAL etc on error. */ int resctrl_arch_rmid_read(struct rdt_resource *r, struct rdt_mon_domain *d, - u32 closid, u32 rmid, enum resctrl_event_id eventid, - u64 *val, void *arch_mon_ctx); + u32 closid, u32 rmid, int cntr_id, + enum resctrl_event_id eventid, u64 *val, + void *arch_mon_ctx); /** * resctrl_arch_rmid_read_context_check() - warn about invalid contexts @@ -566,12 +570,15 @@ struct rdt_domain_hdr *resctrl_find_domain(struct list_head *h, int id, * @closid: closid that matches the rmid. Depending on the architecture, the * counter may match traffic of both @closid and @rmid, or @rmid only. * @rmid: The rmid whose counter values should be reset. + * @cntr_id: Counter ID used to read MBM events in mbm_event mode. Only valid + * when mbm_event mode is enabled and @eventid is an MBM event. Can + * be negative when invalid. * @eventid: The eventid whose counter values should be reset. * * This can be called from any CPU. */ void resctrl_arch_reset_rmid(struct rdt_resource *r, struct rdt_mon_domain *d, - u32 closid, u32 rmid, + u32 closid, u32 rmid, int cntr_id, enum resctrl_event_id eventid); /** -- 2.34.1