hulk inclusion category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/21064 -------------------------------- resctrl stores the details of a monitoring event file in the kernfs node's private pointer, packed into union mon_data_bits: 10 bits of resource id, 8 bits of event id and 14 bits of domain id. mpam_resctrl_alloc_domain() takes a domain's id from the component id, which for a cache is the cache-id from the ACPI PPTT. That field is 32 bits wide and sparse: the L3 cache of the 8-CPU QEMU topology has id 0x2030000, 33751040, which does not fit in 14 bits. The id is truncated to 0 when the file is created. rdtgroup_mondata_show() later passes the truncated id to resctrl_arch_find_domain(), which returns NULL, and reading the file fails: $ cat /sys/fs/resctrl/mon_data/mon_L3_33751040/llc_occupancy cat: read error: No such file or directory Every monitoring event file on a topology whose cache ids exceed 14 bits is unreadable, which covers every MPAM system that reports PPTT cache ids. The 14-bit field dates from the original x86 implementation. It became reachable for MPAM only once the driver started giving the L3 cache a mon_capable resctrl resource: mkdir_mondata_all() then creates mon_<res>_<id> for each domain and packs d->id into the file's private data, and the domain id at that point is already the component id. Before that, no MPAM resource was mon_capable and no such file existed. Replace the packed union with an allocated struct mon_data whose domain id is an int, the layout mainline uses after 2a6566038544 ("x86/resctrl: Expand the width of domid by replacing mon_data_bits"). mon_get_kn_priv() returns the entry for a resource, event and domain, allocating it on first use, and mon_put_kn_priv() frees the whole list in rdt_kill_sb() when the file system is unmounted, before the kernfs root is destroyed. The same mainline commit was backported for x86 as 2e9648d1d932, which touches arch/x86/kernel/cpu/resctrl only. fs/resctrl, the copy arm64 builds, kept the union and is fixed here. Fixes: 1ca491c7bbcd ("arm_mpam: resctrl: Pick the caches we will use as resctrl resources") Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com> --- fs/resctrl/ctrlmondata.c | 10 +++---- fs/resctrl/internal.h | 23 +++++++-------- fs/resctrl/rdtgroup.c | 60 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 72 insertions(+), 21 deletions(-) diff --git a/fs/resctrl/ctrlmondata.c b/fs/resctrl/ctrlmondata.c index cf4895b41fc4..fc2dea4ebafb 100644 --- a/fs/resctrl/ctrlmondata.c +++ b/fs/resctrl/ctrlmondata.c @@ -503,7 +503,7 @@ int rdtgroup_mondata_show(struct seq_file *m, void *arg) u32 resid, evtid, domid; struct rdtgroup *rdtgrp; struct rdt_resource *r; - union mon_data_bits md; + struct mon_data *md; struct rdt_domain *d; struct rmid_read rr; int ret = 0; @@ -514,10 +514,10 @@ int rdtgroup_mondata_show(struct seq_file *m, void *arg) goto out; } - md.priv = of->kn->priv; - resid = md.u.rid; - domid = md.u.domid; - evtid = md.u.evtid; + md = of->kn->priv; + resid = md->rid; + domid = md->domid; + evtid = md->evtid; r = resctrl_arch_get_resource(resid); d = resctrl_arch_find_domain(r, domid); diff --git a/fs/resctrl/internal.h b/fs/resctrl/internal.h index a5f35e72ebc6..2d0af2469d14 100644 --- a/fs/resctrl/internal.h +++ b/fs/resctrl/internal.h @@ -68,21 +68,22 @@ static inline struct rdt_fs_context *rdt_fc2context(struct fs_context *fc) } /** - * union mon_data_bits - Monitoring details for each event file - * @priv: Used to store monitoring event data in @u - * as kernfs private data + * struct mon_data - Monitoring details for each event file + * @list: Member of the global @mon_data_kn_priv_list list * @rid: Resource id associated with the event file * @evtid: Event id associated with the event file * @domid: The domain to which the event file belongs - * @u: Name of the bit fields struct + * + * Stored as the kernfs kn->priv pointer. Allocated rather than packed into + * the pointer, so that @domid keeps the full width of the cache-id from the + * ACPI PPTT (MPAM platforms use sparse ids wider than the 14 bits a bitfield + * in the pointer has room for). */ -union mon_data_bits { - void *priv; - struct { - unsigned int rid : 10; - enum resctrl_event_id evtid : 8; - unsigned int domid : 14; - } u; +struct mon_data { + struct list_head list; + enum resctrl_res_level rid; + enum resctrl_event_id evtid; + int domid; }; struct rmid_read { diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index c8c78db9da6a..3c55a7b2ead1 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -43,6 +43,9 @@ LIST_HEAD(rdt_all_groups); /* list of entries for the schemata file */ LIST_HEAD(resctrl_schema_all); +/* List of allocated struct mon_data, one per monitoring event file */ +static LIST_HEAD(mon_data_kn_priv_list); + /* The filesystem can only be mounted once. */ bool resctrl_mounted; @@ -66,6 +69,7 @@ static char last_cmd_status_buf[512]; static int rdtgroup_setup_root(struct rdt_fs_context *ctx); static void rdtgroup_destroy_root(void); +static void mon_put_kn_priv(void); struct dentry *debugfs_resctrl; @@ -3043,6 +3047,7 @@ static void rdt_kill_sb(struct super_block *sb) rdtgroup_default.mode = RDT_MODE_SHAREABLE; closid_exit(); schemata_list_destroy(); + mon_put_kn_priv(); rdtgroup_destroy_root(); if (resctrl_arch_alloc_capable()) resctrl_arch_disable_alloc(); @@ -3101,12 +3106,54 @@ static void rmdir_mondata_subdir_allrdtgrp(struct rdt_resource *r, } } +static struct mon_data *mon_get_kn_priv(struct rdt_resource *r, + enum resctrl_event_id evtid, int domid) +{ + struct mon_data *priv; + + lockdep_assert_held(&rdtgroup_mutex); + + list_for_each_entry(priv, &mon_data_kn_priv_list, list) { + if (priv->rid == r->rid && priv->domid == domid && + priv->evtid == evtid) + return priv; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return NULL; + + priv->rid = r->rid; + priv->domid = domid; + priv->evtid = evtid; + list_add_tail(&priv->list, &mon_data_kn_priv_list); + + return priv; +} + +/* + * Free all allocated mon_data structures. Called with rdtgroup_mutex held + * when the filesystem is unmounted: the kernfs files that point at them + * are gone by then. + */ +static void mon_put_kn_priv(void) +{ + struct mon_data *priv, *tmp; + + lockdep_assert_held(&rdtgroup_mutex); + + list_for_each_entry_safe(priv, tmp, &mon_data_kn_priv_list, list) { + list_del(&priv->list); + kfree(priv); + } +} + static int mkdir_mondata_subdir(struct kernfs_node *parent_kn, struct rdt_domain *d, struct rdt_resource *r, struct rdtgroup *prgrp) { - union mon_data_bits priv; struct kernfs_node *kn; + struct mon_data *priv; struct mon_evt *mevt; struct rmid_read rr; char name[32]; @@ -3127,11 +3174,14 @@ static int mkdir_mondata_subdir(struct kernfs_node *parent_kn, goto out_destroy; } - priv.u.rid = r->rid; - priv.u.domid = d->id; list_for_each_entry(mevt, &r->evt_list, list) { - priv.u.evtid = mevt->evtid; - ret = mon_addfile(kn, mevt->name, priv.priv); + priv = mon_get_kn_priv(r, mevt->evtid, d->id); + if (WARN_ON_ONCE(!priv)) { + ret = -ENOMEM; + goto out_destroy; + } + + ret = mon_addfile(kn, mevt->name, priv); if (ret) goto out_destroy; -- 2.34.1