From 4c56f8ef604b87b22207c39fe844294a56f6874d Mon Sep 17 00:00:00 2001 From: James Morse Date: Sat, 19 Apr 2025 16:33:47 +0800 Subject: [PATCH 01/15] iommu/arm-smmu-v3: Register SMMU capabilities with MPAM maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/morse/linux.git/commit/?h=mpam/snapshot%2bextras/v6.14-rc1&id=7b15f363f27872351545009f28e830f6dcd5671d -------------------------------- Traffic in the system can be tagged with a PARTID and PMG. Different requestors can support a different number of bits for these fields. Before MPAM can be used, the MPAM driver has to discover the minimum number of bits supported by any requestor, which affects the range of PARTID and PMG that can be used. Detect whether the SMMU supports MPAM, if it does provide the MPAM driver with the maximum PARTID and PMG values. Tested-by: Amit Singh Tomar Signed-off-by: James Morse Signed-off-by: Zeng Heng --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 28 +++++++++++++++++++++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 6 +++++ 2 files changed, 34 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index 7fe05fea676a9..fe99fd44b5af8 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -4978,6 +4979,29 @@ static void arm_smmu_get_httu(struct arm_smmu_device *smmu, u32 reg) hw_features, fw_features); } +static void arm_smmu_mpam_register_smmu(struct arm_smmu_device *smmu) +{ + u16 partid_max; + u8 pmg_max; + u32 reg; + + if (!IS_ENABLED(CONFIG_ARM64_MPAM)) + return; + + if (!(smmu->features & ARM_SMMU_FEAT_MPAM)) + return; + + reg = readl_relaxed(smmu->base + ARM_SMMU_MPAMIDR); + if (!reg) + return; + + partid_max = FIELD_GET(SMMU_MPAMIDR_PARTID_MAX, reg); + pmg_max = FIELD_GET(SMMU_MPAMIDR_PMG_MAX, reg); + + if (mpam_register_requestor(partid_max, pmg_max)) + smmu->features &= ~ARM_SMMU_FEAT_MPAM; +} + static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu) { u32 reg; @@ -5147,6 +5171,8 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu) if (FIELD_GET(IDR3_RIL, reg)) smmu->features |= ARM_SMMU_FEAT_RANGE_INV; + if (FIELD_GET(IDR3_MPAM, reg)) + smmu->features |= ARM_SMMU_FEAT_MPAM; /* IDR5 */ reg = readl_relaxed(smmu->base + ARM_SMMU_IDR5); @@ -5218,6 +5244,8 @@ static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu) if (arm_smmu_sva_supported(smmu)) smmu->features |= ARM_SMMU_FEAT_SVA; + arm_smmu_mpam_register_smmu(smmu); + dev_info(smmu->dev, "ias %lu-bit, oas %lu-bit (features 0x%08x)\n", smmu->ias, smmu->oas, smmu->features); diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h index 38d46098c668b..3c1b12d076d25 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h @@ -67,6 +67,7 @@ #define IDR3_BBML2 2 #define IDR3_FWB (1 << 8) #define IDR3_RIL (1 << 10) +#define IDR3_MPAM (1 << 7) #define ARM_SMMU_IDR5 0x14 #define IDR5_STALL_MAX GENMASK(31, 16) @@ -198,6 +199,10 @@ #define ARM_SMMU_PRIQ_IRQ_CFG1 0xd8 #define ARM_SMMU_PRIQ_IRQ_CFG2 0xdc +#define ARM_SMMU_MPAMIDR 0x130 +#define SMMU_MPAMIDR_PARTID_MAX GENMASK(15, 0) +#define SMMU_MPAMIDR_PMG_MAX GENMASK(23, 16) + #define ARM_SMMU_REG_SZ 0xe00 /* Common MSI config fields */ @@ -771,6 +776,7 @@ struct arm_smmu_device { #define ARM_SMMU_FEAT_BBML2 (1 << 24) #define ARM_SMMU_FEAT_ECMDQ (1 << 25) #define ARM_SMMU_FEAT_S2FWB (1 << 26) +#define ARM_SMMU_FEAT_MPAM (1 << 27) u32 features; #define ARM_SMMU_OPT_SKIP_PREFETCH (1 << 0) -- Gitee From 2d88ccae02e06030eb407a9aa901c40df72888bd Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sat, 19 Apr 2025 16:33:48 +0800 Subject: [PATCH 02/15] iommu/arm-smmu-v3: Issue a batch of commands to the same cmdq mainline inclusion from mainline-v6.12-rc1 commit 56ae8866f3b408836c5f6cafbe6102f6e97911ba category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=56ae8866f3b408836c5f6cafbe6102f6e97911ba -------------------------------- The driver calls in different places the arm_smmu_get_cmdq() helper, and it's fine to do so since the helper always returns the single SMMU CMDQ. However, with NVIDIA CMDQV extension or SMMU ECMDQ, there can be multiple cmdqs in the system to select one from. And either case requires a batch of commands to be issued to the same cmdq. Thus, a cmdq has to be decided in the higher-level callers. Add a cmdq pointer in arm_smmu_cmdq_batch structure, and decide the cmdq when initializing the batch. Pass its pointer down to the bottom function. Update __arm_smmu_cmdq_issue_cmd() accordingly for single command issuers. Suggested-by: Jason Gunthorpe Reviewed-by: Jason Gunthorpe Signed-off-by: Nicolin Chen Link: https://lore.kernel.org/r/2cbf5ddefb6ea611e48d67c642271bd24421eb21.1724970714.git.nicolinc@nvidia.com Signed-off-by: Will Deacon Conflicts: drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c [Fix context conflict.] Signed-off-by: Zeng Heng --- .../arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 2 +- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 52 ++++++++++++------- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 2 + 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c index 6b11af76eb975..06995ee563d56 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c @@ -336,7 +336,7 @@ static int arm_vsmmu_cache_invalidate(struct iommufd_viommu *viommu, continue; /* FIXME always uses the main cmdq rather than trying to group by type */ - ret = arm_smmu_cmdq_issue_cmdlist(smmu, last->cmd, cur - last, true); + ret = arm_smmu_cmdq_issue_cmdlist(smmu, &smmu->cmdq, last->cmd, cur - last, true); if (ret) { cur--; goto out; diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index fe99fd44b5af8..887295ecbb96d 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -720,11 +720,11 @@ static void arm_smmu_cmdq_poll_valid_map(struct arm_smmu_cmdq *cmdq, /* Wait for the command queue to become non-full */ static int arm_smmu_cmdq_poll_until_not_full(struct arm_smmu_device *smmu, + struct arm_smmu_cmdq *cmdq, struct arm_smmu_ll_queue *llq) { unsigned long flags; struct arm_smmu_queue_poll qp; - struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu); int ret = 0; /* @@ -755,11 +755,11 @@ static int arm_smmu_cmdq_poll_until_not_full(struct arm_smmu_device *smmu, * Must be called with the cmdq lock held in some capacity. */ static int __arm_smmu_cmdq_poll_until_msi(struct arm_smmu_device *smmu, + struct arm_smmu_cmdq *cmdq, struct arm_smmu_ll_queue *llq) { int ret = 0; struct arm_smmu_queue_poll qp; - struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu); u32 *cmd = (u32 *)(Q_ENT(&cmdq->q, llq->prod)); queue_poll_init(smmu, &qp); @@ -779,10 +779,10 @@ static int __arm_smmu_cmdq_poll_until_msi(struct arm_smmu_device *smmu, * Must be called with the cmdq lock held in some capacity. */ static int __arm_smmu_cmdq_poll_until_consumed(struct arm_smmu_device *smmu, + struct arm_smmu_cmdq *cmdq, struct arm_smmu_ll_queue *llq) { struct arm_smmu_queue_poll qp; - struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu); u32 prod = llq->prod; int ret = 0; @@ -829,12 +829,13 @@ static int __arm_smmu_cmdq_poll_until_consumed(struct arm_smmu_device *smmu, } static int arm_smmu_cmdq_poll_until_sync(struct arm_smmu_device *smmu, + struct arm_smmu_cmdq *cmdq, struct arm_smmu_ll_queue *llq) { if (smmu->options & ARM_SMMU_OPT_MSIPOLL) - return __arm_smmu_cmdq_poll_until_msi(smmu, llq); + return __arm_smmu_cmdq_poll_until_msi(smmu, cmdq, llq); - return __arm_smmu_cmdq_poll_until_consumed(smmu, llq); + return __arm_smmu_cmdq_poll_until_consumed(smmu, cmdq, llq); } static void arm_smmu_cmdq_write_entries(struct arm_smmu_cmdq *cmdq, u64 *cmds, @@ -879,7 +880,7 @@ static int arm_smmu_ecmdq_issue_cmdlist(struct arm_smmu_device *smmu, while (!queue_has_space(&llq, n + sync)) { local_irq_restore(flags); - if (arm_smmu_cmdq_poll_until_not_full(smmu, &llq)) + if (arm_smmu_cmdq_poll_until_not_full(smmu, cmdq, &llq)) dev_err_ratelimited(smmu->dev, "ECMDQ timeout\n"); local_irq_save(flags); } @@ -915,7 +916,7 @@ static int arm_smmu_ecmdq_issue_cmdlist(struct arm_smmu_device *smmu, /* 5. If we are inserting a CMD_SYNC, we must wait for it to complete */ if (sync) { llq.prod = queue_inc_prod_n(&llq, n); - ret = arm_smmu_cmdq_poll_until_sync(smmu, &llq); + ret = arm_smmu_cmdq_poll_until_sync(smmu, cmdq, &llq); if (ret) { dev_err_ratelimited(smmu->dev, "CMD_SYNC timeout at 0x%08x [hwprod 0x%08x, hwcons 0x%08x]\n", @@ -954,13 +955,13 @@ static int arm_smmu_ecmdq_issue_cmdlist(struct arm_smmu_device *smmu, * CPU will appear before any of the commands from the other CPU. */ int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu, + struct arm_smmu_cmdq *cmdq, u64 *cmds, int n, bool sync) { u64 cmd_sync[CMDQ_ENT_DWORDS]; u32 prod; unsigned long flags; bool owner; - struct arm_smmu_cmdq *cmdq = arm_smmu_get_cmdq(smmu); struct arm_smmu_ll_queue llq, head; int ret = 0; @@ -979,7 +980,7 @@ int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu, while (!queue_has_space(&llq, n + sync)) { local_irq_restore(flags); - if (arm_smmu_cmdq_poll_until_not_full(smmu, &llq)) + if (arm_smmu_cmdq_poll_until_not_full(smmu, cmdq, &llq)) dev_err_ratelimited(smmu->dev, "CMDQ timeout\n"); local_irq_save(flags); } @@ -1065,7 +1066,7 @@ int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu, /* 5. If we are inserting a CMD_SYNC, we must wait for it to complete */ if (sync) { llq.prod = queue_inc_prod_n(&llq, n); - ret = arm_smmu_cmdq_poll_until_sync(smmu, &llq); + ret = arm_smmu_cmdq_poll_until_sync(smmu, cmdq, &llq); if (ret) { dev_err_ratelimited(smmu->dev, "CMD_SYNC timeout at 0x%08x [hwprod 0x%08x, hwcons 0x%08x]\n", @@ -1100,7 +1101,8 @@ static int __arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu, return -EINVAL; } - return arm_smmu_cmdq_issue_cmdlist(smmu, cmd, 1, sync); + return arm_smmu_cmdq_issue_cmdlist( + smmu, arm_smmu_get_cmdq(smmu), cmd, 1, sync); } static int arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu, @@ -1115,6 +1117,13 @@ static int arm_smmu_cmdq_issue_cmd_with_sync(struct arm_smmu_device *smmu, return __arm_smmu_cmdq_issue_cmd(smmu, ent, true); } +static void arm_smmu_cmdq_batch_init(struct arm_smmu_device *smmu, + struct arm_smmu_cmdq_batch *cmds) +{ + cmds->num = 0; + cmds->cmdq = arm_smmu_get_cmdq(smmu); +} + static void arm_smmu_cmdq_batch_add(struct arm_smmu_device *smmu, struct arm_smmu_cmdq_batch *cmds, struct arm_smmu_cmdq_ent *cmd) @@ -1123,13 +1132,15 @@ static void arm_smmu_cmdq_batch_add(struct arm_smmu_device *smmu, if (cmds->num == CMDQ_BATCH_ENTRIES - 1 && (smmu->options & ARM_SMMU_OPT_CMDQ_FORCE_SYNC)) { - arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, true); - cmds->num = 0; + arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmdq, cmds->cmds, + cmds->num, true); + arm_smmu_cmdq_batch_init(smmu, cmds); } if (cmds->num == CMDQ_BATCH_ENTRIES) { - arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, false); - cmds->num = 0; + arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmdq, cmds->cmds, + cmds->num, false); + arm_smmu_cmdq_batch_init(smmu, cmds); } index = cmds->num * CMDQ_ENT_DWORDS; @@ -1145,7 +1156,8 @@ static void arm_smmu_cmdq_batch_add(struct arm_smmu_device *smmu, static int arm_smmu_cmdq_batch_submit(struct arm_smmu_device *smmu, struct arm_smmu_cmdq_batch *cmds) { - return arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, true); + return arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmdq, cmds->cmds, + cmds->num, true); } static void arm_smmu_page_response(struct device *dev, struct iopf_fault *unused, @@ -1397,7 +1409,7 @@ static void arm_smmu_sync_cd(struct arm_smmu_master *master, }, }; - cmds.num = 0; + arm_smmu_cmdq_batch_init(smmu, &cmds); arm_smmu_preempt_disable(smmu); for (i = 0; i < master->num_streams; i++) { @@ -2255,7 +2267,7 @@ static int arm_smmu_atc_inv_master(struct arm_smmu_master *master, arm_smmu_atc_inv_to_cmd(ssid, 0, 0, &cmd); - cmds.num = 0; + arm_smmu_cmdq_batch_init(master->smmu, &cmds); arm_smmu_preempt_disable(master->smmu); for (i = 0; i < master->num_streams; i++) { cmd.atc.sid = master->streams[i].id; @@ -2296,7 +2308,7 @@ int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain, if (!atomic_read(&smmu_domain->nr_ats_masters)) return 0; - cmds.num = 0; + arm_smmu_cmdq_batch_init(smmu_domain->smmu, &cmds); arm_smmu_preempt_disable(smmu_domain->smmu); spin_lock_irqsave(&smmu_domain->devices_lock, flags); @@ -2391,7 +2403,7 @@ static void __arm_smmu_tlb_inv_range(struct arm_smmu_cmdq_ent *cmd, num_pages++; } - cmds.num = 0; + arm_smmu_cmdq_batch_init(smmu, &cmds); arm_smmu_preempt_disable(smmu); while (iova < end) { diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h index 3c1b12d076d25..4891422e9c71e 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h @@ -673,6 +673,7 @@ struct arm_smmu_ecmdq { struct arm_smmu_cmdq_batch { u64 cmds[CMDQ_BATCH_ENTRIES * CMDQ_ENT_DWORDS]; + struct arm_smmu_cmdq *cmdq; int num; }; @@ -1000,6 +1001,7 @@ void arm_smmu_install_ste_for_dev(struct arm_smmu_master *master, const struct arm_smmu_ste *target); int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu, + struct arm_smmu_cmdq *cmdq, u64 *cmds, int n, bool sync); #ifdef CONFIG_ARM_SMMU_V3_SVA -- Gitee From 587296e12d7d64c971a182ca1d28de8cdbe29ca9 Mon Sep 17 00:00:00 2001 From: James Morse Date: Sat, 19 Apr 2025 16:33:49 +0800 Subject: [PATCH 03/15] iommu/arm-smmu-v3: Add mpam helpers to query and set state maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/morse/linux.git/commit/?h=mpam/snapshot%2bextras/v6.14-rc1&id=3b263c0f5b05d8c60fe8921c055c99ea280374c9 -------------------------------- To allow an iommu_group to be moved between resctrl groups as if it were a CPU thread, the mpam driver needs to be able to set the partid and pmg for the iommu_group. Use the properties in the STE, as these only apply to one stream. The MPAM driver also needs to know the maximum partid and pmg values that the SMMU can generate. This allows it to determine the system-wide common supported range of values. Add a helper to return this id register. Tested-by: Amit Singh Tomar Signed-off-by: James Morse Signed-off-by: Zeng Heng --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 92 +++++++++++++++++++++ drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 7 ++ drivers/iommu/iommu.c | 6 ++ include/linux/iommu.h | 7 ++ 4 files changed, 112 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index 887295ecbb96d..dbe13f6e5f1c6 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -3998,6 +3998,96 @@ static int arm_smmu_def_domain_type(struct device *dev) return ret; } +static int arm_smmu_group_set_mpam(struct iommu_group *group, u16 partid, + u8 pmg) +{ + int i; + u32 sid; + unsigned long flags; + struct arm_smmu_ste *step; + struct iommu_domain *domain; + struct arm_smmu_device *smmu; + struct arm_smmu_master *master; + struct arm_smmu_cmdq_batch cmds; + struct arm_smmu_domain *smmu_domain; + struct arm_smmu_cmdq_ent cmd = { + .opcode = CMDQ_OP_CFGI_STE, + .cfgi = { + .leaf = true, + }, + }; + struct arm_smmu_master_domain *master_domain; + + domain = iommu_get_domain_for_group(group); + smmu_domain = to_smmu_domain(domain); + if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_MPAM)) + return -EIO; + smmu = smmu_domain->smmu; + + arm_smmu_cmdq_batch_init(smmu, &cmds); + + spin_lock_irqsave(&smmu_domain->devices_lock, flags); + list_for_each_entry(master_domain, &smmu_domain->devices, + devices_elm) { + master = master_domain->master; + + for (i = 0; i < master->num_streams; i++) { + sid = master->streams[i].id; + step = arm_smmu_get_step_for_sid(smmu, sid); + + /* These need locking if the VMSPtr is ever used */ + step->data[4] = FIELD_PREP(STRTAB_STE_4_PARTID, partid); + step->data[5] = FIELD_PREP(STRTAB_STE_5_PMG, pmg); + + cmd.cfgi.sid = sid; + arm_smmu_cmdq_batch_add(smmu, &cmds, &cmd); + } + + master->partid = partid; + master->pmg = pmg; + } + spin_unlock_irqrestore(&smmu_domain->devices_lock, flags); + + arm_smmu_cmdq_batch_submit(smmu, &cmds); + + return 0; +} + +static int arm_smmu_group_get_mpam(struct iommu_group *group, u16 *partid, + u8 *pmg) +{ + int err = -EINVAL; + unsigned long flags; + struct iommu_domain *domain; + struct arm_smmu_master *master; + struct arm_smmu_domain *smmu_domain; + struct arm_smmu_master_domain *master_domain; + + domain = iommu_get_domain_for_group(group); + smmu_domain = to_smmu_domain(domain); + if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_MPAM)) + return -EIO; + + if (!partid && !pmg) + return 0; + + spin_lock_irqsave(&smmu_domain->devices_lock, flags); + list_for_each_entry(master_domain, &smmu_domain->devices, + devices_elm) { + master = master_domain->master; + if (master) { + if (partid) + *partid = master->partid; + if (pmg) + *pmg = master->pmg; + err = 0; + } + } + spin_unlock_irqrestore(&smmu_domain->devices_lock, flags); + + return err; +} + static struct iommu_ops arm_smmu_ops = { .identity_domain = &arm_smmu_identity_domain, .blocked_domain = &arm_smmu_blocked_domain, @@ -4014,6 +4104,8 @@ static struct iommu_ops arm_smmu_ops = { .remove_dev_pasid = arm_smmu_remove_dev_pasid, .dev_enable_feat = arm_smmu_dev_enable_feature, .dev_disable_feat = arm_smmu_dev_disable_feature, + .get_group_qos_params = arm_smmu_group_get_mpam, + .set_group_qos_params = arm_smmu_group_set_mpam, .page_response = arm_smmu_page_response, .def_domain_type = arm_smmu_def_domain_type, .viommu_alloc = arm_vsmmu_alloc, diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h index 4891422e9c71e..a26011401b118 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h @@ -303,6 +303,7 @@ static inline u32 arm_smmu_strtab_l2_idx(u32 sid) #define STRTAB_STE_1_S2FWB (1UL << 25) #define STRTAB_STE_1_S1STALLD (1UL << 27) +#define STRTAB_STE_1_S1MPAM (1UL << 26) #define STRTAB_STE_1_EATS GENMASK_ULL(29, 28) #define STRTAB_STE_1_EATS_ABT 0UL @@ -332,6 +333,10 @@ static inline u32 arm_smmu_strtab_l2_idx(u32 sid) #define STRTAB_STE_3_S2TTB_MASK GENMASK_ULL(51, 4) +#define STRTAB_STE_4_PARTID GENMASK_ULL(31, 16) + +#define STRTAB_STE_5_PMG GENMASK_ULL(7, 0) + /* These bits can be controlled by userspace for STRTAB_STE_0_CFG_NESTED */ #define STRTAB_STE_0_NESTING_ALLOWED \ cpu_to_le64(STRTAB_STE_0_V | STRTAB_STE_0_CFG | STRTAB_STE_0_S1FMT | \ @@ -855,6 +860,8 @@ struct arm_smmu_master { bool sva_enabled; bool iopf_enabled; unsigned int ssid_bits; + u16 partid; + u8 pmg; }; /* SMMU private data for an IOMMU domain */ diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 8e02783eabcba..60d67402bd3ca 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -2155,6 +2155,12 @@ struct iommu_domain *iommu_get_domain_for_dev(struct device *dev) } EXPORT_SYMBOL_GPL(iommu_get_domain_for_dev); +struct iommu_domain *iommu_get_domain_for_group(struct iommu_group *group) +{ + return group->domain; +} +EXPORT_SYMBOL_GPL(iommu_get_domain_for_group); + /* * For IOMMU_DOMAIN_DMA implementations which already provide their own * guarantees that the group and its default domain are valid and correct. diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 83ec4bf9809ec..959bb067b69ba 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -703,6 +703,12 @@ struct iommu_ops { int (*dev_enable_feat)(struct device *dev, enum iommu_dev_features f); int (*dev_disable_feat)(struct device *dev, enum iommu_dev_features f); + /* Per group IOMMU features */ + int (*get_group_qos_params)(struct iommu_group *group, u16 *partition, + u8 *perf_mon_grp); + int (*set_group_qos_params)(struct iommu_group *group, u16 partition, + u8 perf_mon_grp); + void (*page_response)(struct device *dev, struct iopf_fault *evt, struct iommu_page_response *msg); @@ -971,6 +977,7 @@ extern void iommu_detach_device(struct iommu_domain *domain, extern int iommu_sva_unbind_gpasid(struct iommu_domain *domain, struct device *dev, ioasid_t pasid); extern struct iommu_domain *iommu_get_domain_for_dev(struct device *dev); +extern struct iommu_domain *iommu_get_domain_for_group(struct iommu_group *group); extern struct iommu_domain *iommu_get_dma_domain(struct device *dev); extern size_t iommu_pgsize(struct iommu_domain *domain, unsigned long iova, phys_addr_t paddr, size_t size, size_t *count); -- Gitee From f6a2e789d2c866b9810c1a17952d890cfb56344a Mon Sep 17 00:00:00 2001 From: James Morse Date: Sat, 19 Apr 2025 16:33:50 +0800 Subject: [PATCH 04/15] iommu: Add helpers to get and set the QoS state maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/morse/linux.git/commit/?h=mpam/snapshot%2bextras/v6.14-rc1&id=78810f99b2e80497cc813ce02006350178574ae4 -------------------------------- To allow an iommu_group to be moved between resctrl groups as if it were a CPU thread, the mpam driver needs to be able to set the partid and pmg for the iommu_group. Add helpers that call the iommu driver's get/set methods for these parameters. Signed-off-by: James Morse Signed-off-by: Zeng Heng --- drivers/iommu/iommu.c | 76 +++++++++++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 15 +++++++++ 2 files changed, 91 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 60d67402bd3ca..8562abdeced16 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -3790,3 +3790,79 @@ int iommu_replace_group_handle(struct iommu_group *group, return ret; } EXPORT_SYMBOL_NS_GPL(iommu_replace_group_handle, IOMMUFD_INTERNAL); + +/* + * iommu_group_set_qos_params() - Set the QoS parameters for a group + * @group: the iommu group. + * @partition: the partition label all traffic from the group should use. + * @perf_mon_grp: the performance label all traffic from the group should use. + * + * Return: 0 on success, or an error. + */ +int iommu_group_set_qos_params(struct iommu_group *group, + u16 partition, u8 perf_mon_grp) +{ + const struct iommu_ops *ops; + struct group_device *device; + int ret; + + mutex_lock(&group->mutex); + device = list_first_entry_or_null(&group->devices, typeof(*device), + list); + if (!device) { + ret = -ENODEV; + goto out_unlock; + } + + ops = dev_iommu_ops(device->dev); + if (!ops->set_group_qos_params) { + ret = -EOPNOTSUPP; + goto out_unlock; + } + + ret = ops->set_group_qos_params(group, partition, perf_mon_grp); + +out_unlock: + mutex_unlock(&group->mutex); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(iommu_group_set_qos_params, IOMMUFD_INTERNAL); + +/* + * iommu_group_get_qos_params() - Get the QoS parameters for a group + * @group: the iommu group. + * @partition: the partition label all traffic from the group uses. + * @perf_mon_grp: the performance label all traffic from the group uses. + * + * Return: 0 on success, or an error. + */ +int iommu_group_get_qos_params(struct iommu_group *group, + u16 *partition, u8 *perf_mon_grp) +{ + const struct iommu_ops *ops; + struct group_device *device; + int ret; + + mutex_lock(&group->mutex); + device = list_first_entry_or_null(&group->devices, typeof(*device), + list); + if (!device) { + ret = -ENODEV; + goto out_unlock; + } + + ops = dev_iommu_ops(device->dev); + if (!ops->get_group_qos_params) { + ret = -EOPNOTSUPP; + goto out_unlock; + } + + ret = ops->get_group_qos_params(group, partition, perf_mon_grp); + +out_unlock: + mutex_unlock(&group->mutex); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(iommu_group_get_qos_params, IOMMUFD_INTERNAL); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 959bb067b69ba..058a5d5ed92b7 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -1290,6 +1290,10 @@ void iommu_detach_device_pasid(struct iommu_domain *domain, struct device *dev, ioasid_t pasid); ioasid_t iommu_alloc_global_pasid(struct device *dev); void iommu_free_global_pasid(ioasid_t pasid); +int iommu_group_set_qos_params(struct iommu_group *group, + u16 partition, u8 perf_mon_grp); +int iommu_group_get_qos_params(struct iommu_group *group, + u16 *partition, u8 *perf_mon_grp); #else /* CONFIG_IOMMU_API */ struct iommu_ops {}; @@ -1665,6 +1669,17 @@ static inline ioasid_t iommu_alloc_global_pasid(struct device *dev) } static inline void iommu_free_global_pasid(ioasid_t pasid) {} +static inline int iommu_group_set_qos_params(struct iommu_group *group, + u16 partition, u8 perf_mon_grp) +{ + return -ENODEV; +} + +static inline int iommu_group_get_qos_params(struct iommu_group *group, + u16 *partition, u8 *perf_mon_grp) +{ + return -ENODEV; +} #endif /* CONFIG_IOMMU_API */ #if IS_ENABLED(CONFIG_LOCKDEP) && IS_ENABLED(CONFIG_IOMMU_API) -- Gitee From 6d86dd10f1655218b5a1eccfc5ab217f14911d7b Mon Sep 17 00:00:00 2001 From: James Morse Date: Sat, 19 Apr 2025 16:33:51 +0800 Subject: [PATCH 05/15] iommu: Add helpers to retrieve iommu_groups by id or kobject maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/morse/linux.git/commit/?h=mpam/snapshot%2bextras/v6.14-rc1&id=797f61d3749cdf0f232714c615ce060cc62b44a3 -------------------------------- ARM SMMU with MPAM support are able to mark streams of traffic with the QoS labels MPAM uses. The user-space interface for MPAM is the resctrl filesystem, which allows threads to be moved between groups, its natural to do the same for iommu_groups. The resctrl interface lists threads, so will also need to list iommu_groups, it will be necessary to walk the list of iommu_groups. To ensure this matches what user-space sees via sysfs, it is best to walk the kobjects. When making a change, resctrl will only have the id of a group. To avoid walking the list of kobjects in this case, add iommu_group_get_by_id(). Signed-off-by: James Morse Signed-off-by: Zeng Heng --- drivers/iommu/iommu.c | 34 ++++++++++++++++++++++++++++++++++ include/linux/iommu.h | 12 ++++++++++++ 2 files changed, 46 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 8562abdeced16..faffa07c5e889 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1016,6 +1016,40 @@ struct iommu_group *iommu_group_alloc(void) } EXPORT_SYMBOL_GPL(iommu_group_alloc); +struct iommu_group *iommu_group_get_from_kobj(struct kobject *group_kobj) +{ + struct iommu_group *group; + + if (!iommu_group_kset || !group_kobj) + return NULL; + + group = container_of(group_kobj, struct iommu_group, kobj); + + kobject_get(group->devices_kobj); + kobject_put(&group->kobj); + + return group; +} + +struct iommu_group *iommu_group_get_by_id(int id) +{ + struct kobject *group_kobj; + const char *name; + + if (!iommu_group_kset) + return NULL; + + name = kasprintf(GFP_KERNEL, "%d", id); + if (!name) + return NULL; + + group_kobj = kset_find_obj(iommu_group_kset, name); + kfree(name); + + return iommu_group_get_from_kobj(group_kobj); +} +EXPORT_SYMBOL_GPL(iommu_group_get_by_id); + /** * iommu_group_get_iommudata - retrieve iommu_data registered for a group * @group: the group diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 058a5d5ed92b7..f0585e004e9ef 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -969,6 +969,8 @@ extern bool iommu_present(const struct bus_type *bus); extern bool device_iommu_capable(struct device *dev, enum iommu_cap cap); extern bool iommu_group_has_isolated_msi(struct iommu_group *group); extern struct iommu_domain *iommu_domain_alloc(const struct bus_type *bus); +struct iommu_group *iommu_group_get_from_kobj(struct kobject *group_kobj); +extern struct iommu_group *iommu_group_get_by_id(int id); extern void iommu_domain_free(struct iommu_domain *domain); extern int iommu_attach_device(struct iommu_domain *domain, struct device *dev); @@ -1320,6 +1322,16 @@ static inline struct iommu_domain *iommu_domain_alloc(const struct bus_type *bus return NULL; } +static inline struct iommu_group *iommu_group_get_from_kobj(struct kobject *group_kobj) +{ + return NULL; +} + +static inline struct iommu_group *iommu_group_get_by_id(int id) +{ + return NULL; +} + static inline void iommu_domain_free(struct iommu_domain *domain) { } -- Gitee From dea98e03b2a30dfabd10ea81f2338e2b59fccaa6 Mon Sep 17 00:00:00 2001 From: James Morse Date: Sat, 19 Apr 2025 16:33:52 +0800 Subject: [PATCH 06/15] iommu: Add helper to retrieve iommu kset maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/morse/linux.git/commit/?h=mpam/snapshot%2bextras/v6.14-rc1&id=3872ac93f58df6678b3571ea227681667639b7f9 -------------------------------- To walk the list of iommu groups visible in sysfs, resctrl needs access to iommu_group_kset. Expose it. Signed-off-by: James Morse Signed-off-by: Zeng Heng --- drivers/iommu/iommu.c | 5 +++++ include/linux/iommu.h | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index faffa07c5e889..98397cbb8b9bf 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1050,6 +1050,11 @@ struct iommu_group *iommu_group_get_by_id(int id) } EXPORT_SYMBOL_GPL(iommu_group_get_by_id); +struct kset *iommu_get_group_kset(void) +{ + return kset_get(iommu_group_kset); +} + /** * iommu_group_get_iommudata - retrieve iommu_data registered for a group * @group: the group diff --git a/include/linux/iommu.h b/include/linux/iommu.h index f0585e004e9ef..919514bb1231f 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -1028,6 +1028,7 @@ extern struct iommu_group *iommu_group_ref_get(struct iommu_group *group); extern void iommu_group_put(struct iommu_group *group); extern int iommu_group_id(struct iommu_group *group); +struct kset *iommu_get_group_kset(void); extern struct iommu_domain *iommu_group_default_domain(struct iommu_group *); int iommu_set_pgtable_quirks(struct iommu_domain *domain, @@ -1490,6 +1491,11 @@ static inline int iommu_group_id(struct iommu_group *group) return -ENODEV; } +static inline struct kset *iommu_get_group_kset(void) +{ + return NULL; +} + static inline int iommu_set_pgtable_quirks(struct iommu_domain *domain, unsigned long quirks) { -- Gitee From c40fd1bf67db26788f6c736587cb6ae9554c4b69 Mon Sep 17 00:00:00 2001 From: James Morse Date: Sat, 19 Apr 2025 16:33:53 +0800 Subject: [PATCH 07/15] kobject: Add kset_get_next_obj() to allow a kset to be walked maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/morse/linux.git/commit/?h=mpam/snapshot%2bextras/v6.14-rc1&id=6dcb4d4f7801f5d853c4f7eef9779a6c2285fe21 -------------------------------- To expose iommu_groups via the resctrl filesystem, the resctrl driver needs to be able to walk the list of iommu_groups. These are exposed via sysfs as a kset. Add kset_get_next_obj() to allow resctrl to walk the kobjects in the kset. Signed-off-by: James Morse Signed-off-by: Zeng Heng --- include/linux/kobject.h | 2 ++ lib/kobject.c | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 505dee42aaae2..70a04d484158f 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -210,6 +210,8 @@ static inline const struct kobj_type *get_ktype(const struct kobject *kobj) struct kobject *kset_find_obj(struct kset *, const char *); +struct kobject *kset_get_next_obj(struct kset *kset, struct kobject *prev); + /* The global /sys/kernel/ kobject for people to chain off of */ extern struct kobject *kernel_kobj; /* The global /sys/kernel/mm/ kobject for people to chain off of */ diff --git a/lib/kobject.c b/lib/kobject.c index 72fa20f405f15..8dc19eba3835f 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -920,6 +920,27 @@ struct kobject *kset_find_obj(struct kset *kset, const char *name) } EXPORT_SYMBOL_GPL(kset_find_obj); +struct kobject *kset_get_next_obj(struct kset *kset, struct kobject *prev) +{ + struct kobject *k; + + spin_lock(&kset->list_lock); + + if (!prev) + k = list_first_entry_or_null(&kset->list, typeof(*k), entry); + else + k = list_next_entry(prev, entry); + + if (list_entry_is_head(k, &kset->list, entry)) + k = NULL; + + kobject_get(k); + spin_unlock(&kset->list_lock); + kobject_put(prev); + + return k; +} + static void kset_release(struct kobject *kobj) { struct kset *kset = container_of(kobj, struct kset, kobj); -- Gitee From 944c6f1409841cfd8c55d20c3daa92411201d60a Mon Sep 17 00:00:00 2001 From: James Morse Date: Sat, 19 Apr 2025 16:33:54 +0800 Subject: [PATCH 08/15] arm_mpam: resctrl: Add iommu helpers to get/set the partid and pmg maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/morse/linux.git/commit/?h=mpam/snapshot%2bextras/v6.14-rc1&id=5b0b490d221d23279aed567910a1e66f7498f190 -------------------------------- SMMU that support MPAM can be configured to use a particular partid and pmg for a stream. The assignment of an iommu_group and its corresponding streams should be done via resctrl. Add helpers similar to setting a closid/rmid on a task. We need the same shifting if the CPUs are using CDP. The SMMU only takes one partid, conceptually its always making data accesses. Signed-off-by: James Morse Signed-off-by: Zeng Heng --- drivers/platform/mpam/mpam_resctrl.c | 44 ++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/drivers/platform/mpam/mpam_resctrl.c b/drivers/platform/mpam/mpam_resctrl.c index dd1ccca8edcb6..609dcb21c7ac0 100644 --- a/drivers/platform/mpam/mpam_resctrl.c +++ b/drivers/platform/mpam/mpam_resctrl.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -244,6 +245,49 @@ bool resctrl_arch_match_rmid(struct task_struct *tsk, u32 closid, u32 rmid) return (tsk_closid == closid) && (tsk_rmid == rmid); } +int resctrl_arch_set_iommu_closid_rmid(struct iommu_group *group, u32 closid, + u32 rmid) +{ + u16 partid; + + if (cdp_enabled) + partid = closid << 1; + else + partid = closid; + + return iommu_group_set_qos_params(group, partid, rmid); +} + +bool resctrl_arch_match_iommu_closid(struct iommu_group *group, u32 closid) +{ + u16 partid; + int err = iommu_group_get_qos_params(group, &partid, NULL); + + if (err) + return false; + + if (cdp_enabled) + partid >>= 1; + + return (partid == closid); +} + +bool resctrl_arch_match_iommu_closid_rmid(struct iommu_group *group, + u32 closid, u32 rmid) +{ + u8 pmg; + u16 partid; + int err = iommu_group_get_qos_params(group, &partid, &pmg); + + if (err) + return false; + + if (cdp_enabled) + partid >>= 1; + + return (partid == closid) && (rmid == pmg); +} + struct rdt_resource *resctrl_arch_get_resource(enum resctrl_res_level l) { if (l >= RDT_NUM_RESOURCES) -- Gitee From 7e3f95a1a8616ccda06e3263c7a6cacb162e07a5 Mon Sep 17 00:00:00 2001 From: James Morse Date: Sat, 19 Apr 2025 16:33:55 +0800 Subject: [PATCH 09/15] fs/resctrl: Add support for assigning iommu_groups to resctrl groups maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/morse/linux.git/commit/?h=mpam/snapshot%2bextras/v6.14-rc1&id=8cca5e0670f231a507fc6976f69070d070179e16 -------------------------------- Arm's MPAM has support for assigning devices behind an IOMMU to a control or monitor group. This can be used for device-passthrough for a VM, or user-space drivers using VFIO to ensure the device is either in the same control group as the CPU threads. Alternatively, the iommu_group may be assigned to a different control group with preferential schema values. Extend the resctrl tasks file to include iommu_groups. These appear as 'iommu_group:0', where 0 is the group number that can be found from /sys/kernel/iommu_groups/. iommu_groups can be moved between resctrl groups by writing this string in the same way as tasks are moved. No state is preserved by resctrl, an iommu_group that disappears will no longer be listed as being part of a resctrl group. A new iommu_group will appear in the default group. Add helpers to list and move iommu_groups. Architecture specific helpers are used to apply the closid/rmid to the iommu_group due to the way MPAM emulates CDP. Tested-by: Amit Singh Tomar Signed-off-by: James Morse Signed-off-by: Zeng Heng --- fs/resctrl/Kconfig | 6 +++ fs/resctrl/rdtgroup.c | 97 ++++++++++++++++++++++++++++++++++++++++- include/linux/resctrl.h | 28 ++++++++++++ 3 files changed, 130 insertions(+), 1 deletion(-) diff --git a/fs/resctrl/Kconfig b/fs/resctrl/Kconfig index 5bbf5a1798cd9..468f02df8dd5d 100644 --- a/fs/resctrl/Kconfig +++ b/fs/resctrl/Kconfig @@ -21,3 +21,9 @@ config RESCTRL_RMID_DEPENDS_ON_CLOSID Enable by the architecture when the RMID values depend on the CLOSID. This causes the closid allocator to search for CLOSID with clean RMID. + +config RESCTRL_IOMMU + bool + help + Enabled by the architecture when some IOMMU are able to be configured + with CLOSID/RMID. diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index 1ade368e26315..d8ba3a0d5dbf3 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -740,10 +741,64 @@ static int rdtgroup_move_task(pid_t pid, struct rdtgroup *rdtgrp, return ret; } +static int rdtgroup_move_iommu(int iommu_group_id, struct rdtgroup *rdtgrp, + struct kernfs_open_file *of) +{ + const struct cred *cred = current_cred(); + struct iommu_group *iommu_group; + int err; + + if (!uid_eq(cred->euid, GLOBAL_ROOT_UID)) { + rdt_last_cmd_printf("No permission to move iommu_group %d\n", + iommu_group_id); + return -EPERM; + } + + iommu_group = iommu_group_get_by_id(iommu_group_id); + if (!iommu_group) { + rdt_last_cmd_printf("No matching iommu_group %d\n", + iommu_group_id); + return -ESRCH; + } + + if (rdtgrp->type == RDTMON_GROUP && + !resctrl_arch_match_iommu_closid(iommu_group, + rdtgrp->mon.parent->closid)) { + rdt_last_cmd_puts("Can't move iommu_group to different control group\n"); + err = -EINVAL; + } else { + err = resctrl_arch_set_iommu_closid_rmid(iommu_group, + rdtgrp->closid, + rdtgrp->mon.rmid); + } + + iommu_group_put(iommu_group); + + return err; +} + +static bool string_is_iommu_group(char *buf, int *val) +{ + if (!IS_ENABLED(CONFIG_RESCTRL_IOMMU)) + return false; + + if (strlen(buf) <= strlen("iommu_group:")) + return false; + + if (strncmp(buf, "iommu_group:", strlen("iommu_group:"))) + return false; + + buf += strlen("iommu_group:"); + + return !kstrtoint(buf, 0, val); +} + static ssize_t rdtgroup_tasks_write(struct kernfs_open_file *of, char *buf, size_t nbytes, loff_t off) { struct rdtgroup *rdtgrp; + int iommu_group_id; + bool is_iommu; char *pid_str; int ret = 0; pid_t pid; @@ -765,7 +820,10 @@ static ssize_t rdtgroup_tasks_write(struct kernfs_open_file *of, while (buf && buf[0] != '\0' && buf[0] != '\n') { pid_str = strim(strsep(&buf, ",")); - if (kstrtoint(pid_str, 0, &pid)) { + is_iommu = string_is_iommu_group(pid_str, &iommu_group_id); + if (is_iommu) + ret = rdtgroup_move_iommu(iommu_group_id, rdtgrp, of); + else if (kstrtoint(pid_str, 0, &pid)) { rdt_last_cmd_printf("Task list parsing error pid %s\n", pid_str); ret = -EINVAL; break; @@ -790,6 +848,41 @@ static ssize_t rdtgroup_tasks_write(struct kernfs_open_file *of, return ret ?: nbytes; } +static bool iommu_matches_rdtgroup(struct iommu_group *group, struct rdtgroup *r) +{ + if (r->type == RDTCTRL_GROUP) + return resctrl_arch_match_iommu_closid(group, r->closid); + + return resctrl_arch_match_iommu_closid_rmid(group, r->closid, + r->mon.rmid); +} + +static void show_rdt_iommu(struct rdtgroup *r, struct seq_file *s) +{ + struct kset *iommu_groups; + struct iommu_group *group; + struct kobject *group_kobj = NULL; + + if (!IS_ENABLED(CONFIG_RESCTRL_IOMMU)) + return; + + iommu_groups = iommu_get_group_kset(); + + while ((group_kobj = kset_get_next_obj(iommu_groups, group_kobj))) { + /* iommu_group_get_from_kobj() wants to drop a reference */ + kobject_get(group_kobj); + + group = iommu_group_get_from_kobj(group_kobj); + if (!group) + continue; + + if (iommu_matches_rdtgroup(group, r)) + seq_printf(s, "iommu_group:%s\n", group_kobj->name); + } + + kset_put(iommu_groups); +} + static void show_rdt_tasks(struct rdtgroup *r, struct seq_file *s) { struct task_struct *p, *t; @@ -804,6 +897,8 @@ static void show_rdt_tasks(struct rdtgroup *r, struct seq_file *s) } } rcu_read_unlock(); + + show_rdt_iommu(r, s); } static int rdtgroup_tasks_show(struct kernfs_open_file *of, diff --git a/include/linux/resctrl.h b/include/linux/resctrl.h index edc7264a8369b..61ff7dd5d74c9 100644 --- a/include/linux/resctrl.h +++ b/include/linux/resctrl.h @@ -422,4 +422,32 @@ void resctrl_exit(void); int resctrl_arch_mon_resource_init(void); void mbm_config_rftype_init(const char *config); +/* When supported, the architecture must implement these */ +#ifdef CONFIG_RESCTRL_IOMMU +int resctrl_arch_set_iommu_closid_rmid(struct iommu_group *group, u32 closid, + u32 rmid); +bool resctrl_arch_match_iommu_closid(struct iommu_group *group, u32 closid); +bool resctrl_arch_match_iommu_closid_rmid(struct iommu_group *group, u32 closid, + u32 rmid); +#else +static inline int +resctrl_arch_set_iommu_closid_rmid(struct iommu_group *group, + u32 closid, u32 rmid) +{ + return -EOPNOTSUPP; +} + +static inline bool +resctrl_arch_match_iommu_closid(struct iommu_group *group, u32 closid) +{ + return false; +} + +static inline bool +resctrl_arch_match_iommu_closid_rmid(struct iommu_group *group, + u32 closid, u32 rmid) +{ + return false; +} +#endif /* CONFIG_RESCTRL_IOMMU */ #endif /* _RESCTRL_H */ -- Gitee From 8c32b6c0318071b390bf5b33f8f5d7e46de3ccf1 Mon Sep 17 00:00:00 2001 From: James Morse Date: Sat, 19 Apr 2025 16:33:56 +0800 Subject: [PATCH 10/15] ACPI/MPAM: Parse the rest of the ACPI table maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/morse/linux.git/commit/?h=mpam/snapshot%2bextras/v6.14-rc1&id=57e2ba3addf1cc6f86acd56e16eb8f20ddbe3d71 -------------------------------- The MPAM ACPI table lists the MPAM MSCs and indicates which resources in the system the control. Not everything this table can describe is supported by resctrl, e.g. memory-side-caches. Add the additional table parsing to avoid reporting these as 'unknown' to the MPAM driver. This allows class+component hierarchys to be built. Until resctrl has support for any of these resources, users would be in-kernel managers of a resource/PARTID or perf to query bandwidth counters on a resource resctrl is unaware of. Signed-off-by: James Morse Signed-off-by: Zeng Heng --- drivers/acpi/arm64/mpam.c | 93 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 89 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/arm64/mpam.c b/drivers/acpi/arm64/mpam.c index 8a63449f27b52..de866e711be2e 100644 --- a/drivers/acpi/arm64/mpam.c +++ b/drivers/acpi/arm64/mpam.c @@ -95,25 +95,110 @@ static void acpi_mpam_parse_irqs(struct platform_device *pdev, } } -static int acpi_mpam_parse_resource(struct mpam_msc *msc, +#define UUID_MPAM_INTERCONNECT_TABLE "fe2bd645-033b-49e6-9479-2e0b8b21d1cd" + +struct acpi_mpam_interconnect_descriptor_table { + u8 type_uuid[16]; + u32 num_descriptors; +}; + +struct acpi_mpam_interconnect_descriptor { + u32 source_id; + u32 destination_id; + u8 link_type; + u8 reserved[3]; +}; + +static int acpi_mpam_parse_resource(struct acpi_mpam_msc_node *tbl_msc, + struct mpam_msc *msc, struct acpi_mpam_resource_node *res) { + struct acpi_mpam_interconnect_descriptor_table *tbl_int_tbl; + struct acpi_mpam_interconnect_descriptor *tbl_int; + guid_t int_tbl_uuid, spec_uuid; u32 cache_id; + off_t offset; int level; + /* + * Class IDs are somewhat arbitrary, but need to be co-ordinated. + * 0-N are caches, + * 64, 65: Interconnect, but ideally these would appear between the + * classes the controls are adjacent to. + * 128: SMMU, + * 192-192+level: Memory Side Caches, nothing checks that N is a + * small number. + * 255: Memory Controllers + * + * ACPI devices would need a class id allocated based on the _HID. + * + * Classes that the mpam driver can't currently plumb into resctrl + * are registered as UNKNOWN. + */ switch (res->locator_type) { case ACPI_MPAM_LOCATION_TYPE_PROCESSOR_CACHE: cache_id = res->locator.cache_locator.cache_reference; level = find_acpi_cache_level_from_id(cache_id); - if (level < 0) { + if (level < 0 || level >= 64) { pr_err_once("Bad level for cache with id %u\n", cache_id); - return level; + return -EINVAL; } return mpam_ris_create(msc, res->ris_index, MPAM_CLASS_CACHE, level, cache_id); case ACPI_MPAM_LOCATION_TYPE_MEMORY: return mpam_ris_create(msc, res->ris_index, MPAM_CLASS_MEMORY, 255, res->locator.memory_locator.proximity_domain); + case ACPI_MPAM_LOCATION_TYPE_SMMU: + return mpam_ris_create(msc, res->ris_index, MPAM_CLASS_UNKNOWN, + 128, res->locator.smmu_locator.smmu_interface); + case ACPI_MPAM_LOCATION_TYPE_MEMORY_CACHE: + cache_id = res->locator.mem_cache_locator.reference; + level = res->locator.mem_cache_locator.level; + if (192 + level >= 255) { + pr_err_once("Bad level for memory side cache with reference %u\n", + cache_id); + return -EINVAL; + } + + return mpam_ris_create(msc, res->ris_index, MPAM_CLASS_CACHE, + 192 + level, cache_id); + + case ACPI_MPAM_LOCATION_TYPE_INTERCONNECT: + /* Find the descriptor table, and check it lands in the parent msc */ + offset = res->locator.interconnect_ifc_locator.inter_connect_desc_tbl_off; + if (offset >= tbl_msc->length) { + pr_err_once("Bad offset for interconnect descriptor on msc %u\n", + tbl_msc->identifier); + return -EINVAL; + } + tbl_int_tbl = ACPI_ADD_PTR(struct acpi_mpam_interconnect_descriptor_table, + tbl_msc, offset); + guid_parse(UUID_MPAM_INTERCONNECT_TABLE, &spec_uuid); + import_guid(&int_tbl_uuid, tbl_int_tbl->type_uuid); + if (guid_equal(&spec_uuid, &int_tbl_uuid)) { + pr_err_once("Bad UUID for interconnect descriptor on msc %u\n", + tbl_msc->identifier); + return -EINVAL; + } + + offset += sizeof(*tbl_int_tbl); + offset += tbl_int_tbl->num_descriptors * sizeof(*tbl_int); + if (offset >= tbl_msc->length) { + pr_err_once("Bad num_descriptors for interconnect descriptor on msc %u\n", + tbl_msc->identifier); + return -EINVAL; + } + + tbl_int = ACPI_ADD_PTR(struct acpi_mpam_interconnect_descriptor, + tbl_int_tbl, sizeof(*tbl_int_tbl)); + cache_id = tbl_int->source_id; + + /* Unknown link type? */ + if (tbl_int->link_type != 0 && tbl_int->link_type == 1) + return 0; + + return mpam_ris_create(msc, res->ris_index, MPAM_CLASS_UNKNOWN, + 64 + tbl_int->link_type, cache_id); default: /* These get discovered later and treated as unknown */ return 0; @@ -128,7 +213,7 @@ int acpi_mpam_parse_resources(struct mpam_msc *msc, resources = (struct acpi_mpam_resource_node *)(tbl_msc + 1); for (i = 0; i < tbl_msc->num_resouce_nodes; i++) { - err = acpi_mpam_parse_resource(msc, &resources[i]); + err = acpi_mpam_parse_resource(tbl_msc, msc, &resources[i]); if (err) return err; } -- Gitee From 9b2d7a0f30970e59a0ca4940fd4cc7ca624cba3a Mon Sep 17 00:00:00 2001 From: Zeng Heng Date: Sat, 19 Apr 2025 16:33:57 +0800 Subject: [PATCH 11/15] arm_mpam: Select CONFIG_RESCTRL_IOMMU on the ARM64 by default hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 -------------------------------- On ARM64 arch, the CONFIG_RESCTRL_IOMMU is selected by default. Signed-off-by: Zeng Heng --- drivers/platform/mpam/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/mpam/Kconfig b/drivers/platform/mpam/Kconfig index 75f5b2454fbe4..a6e937b8f91d6 100644 --- a/drivers/platform/mpam/Kconfig +++ b/drivers/platform/mpam/Kconfig @@ -6,3 +6,4 @@ config ARM_CPU_RESCTRL depends on ARM64 && ARCH_HAS_CPU_RESCTRL depends on MISC_FILESYSTEMS select RESCTRL_RMID_DEPENDS_ON_CLOSID + select RESCTRL_IOMMU -- Gitee From 9d57b76cb92d4045070f2bed696547da8ea75667 Mon Sep 17 00:00:00 2001 From: Zeng Heng Date: Sat, 19 Apr 2025 16:33:58 +0800 Subject: [PATCH 12/15] iommu/arm-smmu-v3: Check pointer valid before dereferencing it hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 -------------------------------- Before checking smmu->flag, it is necessary to first verify whether the smmu pointer is valid. Otherwise, when an iommu_group is not attached with any SMMU device yet, the smmu pointer will be NULL, which can lead to a memory access exception: Unable to handle kernel NULL pointer dereference at virtual address 0000000000000018 Call trace: arm_smmu_group_get_mpam+0x30/0x150 iommu_group_get_qos_params+0x70/0xb0 resctrl_arch_match_iommu_closid+0x38/0x98 show_rdt_iommu+0xc8/0xe0 show_rdt_tasks+0x110/0x150 rdtgroup_tasks_show+0x38/0x68 rdtgroup_seqfile_show+0x38/0x58 kernfs_seq_show+0x34/0x48 seq_read_iter+0x1d4/0x4e8 kernfs_fop_read_iter+0x40/0x58 new_sync_read+0xa4/0x110 vfs_read+0x19c/0x200 Fixes: b5b8955197cf ("iommu/arm-smmu-v3: Add mpam helpers to query and set state") Signed-off-by: Zeng Heng --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index dbe13f6e5f1c6..f778936d75794 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -4020,6 +4020,10 @@ static int arm_smmu_group_set_mpam(struct iommu_group *group, u16 partid, domain = iommu_get_domain_for_group(group); smmu_domain = to_smmu_domain(domain); + + if (!smmu_domain->smmu) + return -EINVAL; + if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_MPAM)) return -EIO; smmu = smmu_domain->smmu; @@ -4065,6 +4069,10 @@ static int arm_smmu_group_get_mpam(struct iommu_group *group, u16 *partid, domain = iommu_get_domain_for_group(group); smmu_domain = to_smmu_domain(domain); + + if (!smmu_domain->smmu) + return 0; + if (!(smmu_domain->smmu->features & ARM_SMMU_FEAT_MPAM)) return -EIO; -- Gitee From 77ffe8001238df63c0beadb2fda06d121fc87b2e Mon Sep 17 00:00:00 2001 From: Zeng Heng Date: Sat, 19 Apr 2025 16:33:59 +0800 Subject: [PATCH 13/15] fs/resctrl: Fix the iommu_group parsing process hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 -------------------------------- When the tasks interface receives the iommu_group configuration and completes parsing the configuration, it should return and continue to parse next token, without entering the PID parsing process. Fixes: 150e3bb389bf ("fs/resctrl: Add support for assigning iommu_groups to resctrl groups") Signed-off-by: Zeng Heng --- fs/resctrl/rdtgroup.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index d8ba3a0d5dbf3..994cd831786c1 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -821,9 +821,15 @@ static ssize_t rdtgroup_tasks_write(struct kernfs_open_file *of, pid_str = strim(strsep(&buf, ",")); is_iommu = string_is_iommu_group(pid_str, &iommu_group_id); - if (is_iommu) + if (is_iommu) { ret = rdtgroup_move_iommu(iommu_group_id, rdtgrp, of); - else if (kstrtoint(pid_str, 0, &pid)) { + if (ret) + break; + + continue; + } + + if (kstrtoint(pid_str, 0, &pid)) { rdt_last_cmd_printf("Task list parsing error pid %s\n", pid_str); ret = -EINVAL; break; -- Gitee From 1b866beed2b47e39af4b027f0961e17b0cd51a7b Mon Sep 17 00:00:00 2001 From: Zeng Heng Date: Sat, 19 Apr 2025 16:34:00 +0800 Subject: [PATCH 14/15] fs/resctrl: Move iommu_groups back when their associated RDT group is deleted hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 -------------------------------- When unmounting the resctrl system or removing a control group, move all its iommu_groups to the default resource group. When removing a monitor group, move its iommu_groups back to the parent control group. Signed-off-by: Zeng Heng --- fs/resctrl/rdtgroup.c | 70 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/fs/resctrl/rdtgroup.c b/fs/resctrl/rdtgroup.c index 994cd831786c1..13c64231c82d0 100644 --- a/fs/resctrl/rdtgroup.c +++ b/fs/resctrl/rdtgroup.c @@ -741,8 +741,7 @@ static int rdtgroup_move_task(pid_t pid, struct rdtgroup *rdtgrp, return ret; } -static int rdtgroup_move_iommu(int iommu_group_id, struct rdtgroup *rdtgrp, - struct kernfs_open_file *of) +static int rdtgroup_move_iommu(int iommu_group_id, struct rdtgroup *rdtgrp) { const struct cred *cred = current_cred(); struct iommu_group *iommu_group; @@ -777,6 +776,53 @@ static int rdtgroup_move_iommu(int iommu_group_id, struct rdtgroup *rdtgrp, return err; } +static bool iommu_matches_rdtgroup(struct iommu_group *group, struct rdtgroup *r) +{ + if (r->type == RDTCTRL_GROUP) + return resctrl_arch_match_iommu_closid(group, r->closid); + + return resctrl_arch_match_iommu_closid_rmid(group, r->closid, + r->mon.rmid); +} + +static int rdt_move_group_iommus(struct rdtgroup *from, struct rdtgroup *to) +{ + int err, iommu_group_id; + struct kset *iommu_groups; + struct iommu_group *group; + struct kobject *group_kobj = NULL; + + if (!IS_ENABLED(CONFIG_RESCTRL_IOMMU)) + return 0; + + if (from == to) + return 0; + + iommu_groups = iommu_get_group_kset(); + + while ((group_kobj = kset_get_next_obj(iommu_groups, group_kobj))) { + /* iommu_group_get_from_kobj() wants to drop a reference */ + kobject_get(group_kobj); + + group = iommu_group_get_from_kobj(group_kobj); + if (!group) + continue; + + if (!from || iommu_matches_rdtgroup(group, from)) { + err = kstrtoint(group_kobj->name, 0, &iommu_group_id); + if (err) + break; + + err = rdtgroup_move_iommu(iommu_group_id, to); + if (err) + break; + } + } + + kset_put(iommu_groups); + return err; +} + static bool string_is_iommu_group(char *buf, int *val) { if (!IS_ENABLED(CONFIG_RESCTRL_IOMMU)) @@ -822,7 +868,7 @@ static ssize_t rdtgroup_tasks_write(struct kernfs_open_file *of, is_iommu = string_is_iommu_group(pid_str, &iommu_group_id); if (is_iommu) { - ret = rdtgroup_move_iommu(iommu_group_id, rdtgrp, of); + ret = rdtgroup_move_iommu(iommu_group_id, rdtgrp); if (ret) break; @@ -854,15 +900,6 @@ static ssize_t rdtgroup_tasks_write(struct kernfs_open_file *of, return ret ?: nbytes; } -static bool iommu_matches_rdtgroup(struct iommu_group *group, struct rdtgroup *r) -{ - if (r->type == RDTCTRL_GROUP) - return resctrl_arch_match_iommu_closid(group, r->closid); - - return resctrl_arch_match_iommu_closid_rmid(group, r->closid, - r->mon.rmid); -} - static void show_rdt_iommu(struct rdtgroup *r, struct seq_file *s) { struct kset *iommu_groups; @@ -2855,6 +2892,9 @@ static void rmdir_all_sub(void) /* Move all tasks to the default resource group */ rdt_move_group_tasks(NULL, &rdtgroup_default, NULL); + /* Move all iommu_groups to the default resource group */ + rdt_move_group_iommus(NULL, &rdtgroup_default); + list_for_each_entry_safe(rdtgrp, tmp, &rdt_all_groups, rdtgroup_list) { /* Free any child rmids */ free_all_child_rdtgrp(rdtgrp); @@ -3570,6 +3610,9 @@ static int rdtgroup_rmdir_mon(struct rdtgroup *rdtgrp, cpumask_var_t tmpmask) /* Give any tasks back to the parent group */ rdt_move_group_tasks(rdtgrp, prdtgrp, tmpmask); + /* Give any iommu_groups back to the parent group */ + rdt_move_group_iommus(rdtgrp, prdtgrp); + /* Update per cpu rmid of the moved CPUs first */ for_each_cpu(cpu, &rdtgrp->cpu_mask) resctrl_arch_set_cpu_default_closid_rmid(cpu, rdtgrp->closid, @@ -3613,6 +3656,9 @@ static int rdtgroup_rmdir_ctrl(struct rdtgroup *rdtgrp, cpumask_var_t tmpmask) /* Give any tasks back to the default group */ rdt_move_group_tasks(rdtgrp, &rdtgroup_default, tmpmask); + /* Give any iommu_groups back to the default group */ + rdt_move_group_iommus(rdtgrp, &rdtgroup_default); + /* Give any CPUs back to the default group */ cpumask_or(&rdtgroup_default.cpu_mask, &rdtgroup_default.cpu_mask, &rdtgrp->cpu_mask); -- Gitee From ab25ef53dfe06855ec9d3133d9355818f22d7dba Mon Sep 17 00:00:00 2001 From: Zeng Heng Date: Sat, 19 Apr 2025 16:34:01 +0800 Subject: [PATCH 15/15] iommu: Fix kabi broken of struct iommu_ops hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IC03L1 -------------------------------- Fix kabi broken of the struct iommu_ops caused by the following commit. Fixes: b5b8955197cf ("iommu/arm-smmu-v3: Add mpam helpers to query and set state") Signed-off-by: Zeng Heng --- include/linux/iommu.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 919514bb1231f..f95e11c4052df 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -703,12 +703,6 @@ struct iommu_ops { int (*dev_enable_feat)(struct device *dev, enum iommu_dev_features f); int (*dev_disable_feat)(struct device *dev, enum iommu_dev_features f); - /* Per group IOMMU features */ - int (*get_group_qos_params)(struct iommu_group *group, u16 *partition, - u8 *perf_mon_grp); - int (*set_group_qos_params)(struct iommu_group *group, u16 partition, - u8 perf_mon_grp); - void (*page_response)(struct device *dev, struct iopf_fault *evt, struct iommu_page_response *msg); @@ -730,8 +724,13 @@ struct iommu_ops { KABI_USE(3, struct iommufd_viommu *(*viommu_alloc)( struct device *dev, struct iommu_domain *parent_domain, struct iommufd_ctx *ictx, unsigned int viommu_type)) - KABI_RESERVE(4) - KABI_RESERVE(5) + + /* Per group IOMMU features */ + KABI_USE(4, int (*get_group_qos_params)(struct iommu_group *group, u16 *partition, + u8 *perf_mon_grp)) + KABI_USE(5, int (*set_group_qos_params)(struct iommu_group *group, u16 partition, + u8 perf_mon_grp)) + KABI_RESERVE(6) KABI_RESERVE(7) KABI_RESERVE(8) -- Gitee