Add Tegra241 (Grace) CMDQV Support (part 1/2)
Subsystems (4)
v6 cover Add Tegra241 (Grace) CMDQV Support (part 1/2)
NVIDIA's Tegra241 (Grace) SoC has a CMDQ-Virtualization (CMDQV) hardware that extends standard ARM SMMUv3 to support multiple command queues with virtualization capabilities. Though this is similar to the ECMDQ in SMMU v3.3, CMDQV provides additional Virtual Interfaces (VINTFs) allowing VMs to have their own VINTFs and Virtual Command Queues (VCMDQs). The VCMDQs can only execute a limited set of commands, mainly invalidation commands when exclusively used by the VMs, compared to the standard SMMUv3 CMDQ.
Thus, there are two parts of patch series to add its support: the basic in-kernel support as part 1, and the user-space support as part 2.
The in-kernel support is to detect/configure the CMDQV hardware and then allocate a VINTF with some VCMDQs for the kernel/hypervisor to use. Like ECMDQ, CMDQV also allows the kernel to use multiple VCMDQs, giving some limited performance improvement: up to 20% reduction of TLB invalidation time was measured by a multi-threaded DMA unmap benchmark, compared to a single queue.
The user-space support is to provide uAPIs (via IOMMUFD) for hypervisors in user space to passthrough VCMDQs to VMs, allowing these VMs to access the VCMDQs directly without trappings, i.e. no VM Exits. This gives huge performance improvements: 70% to 90% reductions of TLB invalidation time were measured by various DMA unmap tests running in a guest OS, compared to a nested SMMU CMDQ (with trappings).
This is the part-1 series:
- Preparatory changes to share the existing SMMU functions
- A new CMDQV driver and extending the SMMUv3 driver to interact with the new driver
- Limit the commands for a guest kernel.
It's available on Github:
https://github.com/nicolinc/iommufd/commits/vcmdq_in_kernel-v6
And the part-2 RFC series is also sent for discussion:
https://lore.kernel.org/all/[email protected]/
Note that this in-kernel support isn't confined to host kernels running on Grace-powered servers, but is also used by guest kernels running on VMs virtualized on those servers. So, those VMs must install the driver, ideally before the part 2 is merged. So, later those servers would only need to upgrade their host kernels without bothering the VMs.
Thank you!
Changelog
v6:
* Reordered the patch sequence to fix git-bisect break
* Added a status cache to cmdqv/vintf/vcmdq structure
* Added gerror/gerrorn value match in hw_deinit()
* Minimized changes in __arm_smmu_cmdq_skip_err()
* Preallocated VCMDQs to VINTFs for stablility
v5:
https://lore.kernel.org/all/[email protected]/
* Improved print/mmio helpers
* Added proper register reset routines
* Reorganized init/deinit functions to share with VIOMMU callbacks in the upcoming part-2 user-space series (RFC)
v4:
https://lore.kernel.org/all/[email protected]/
* Rebased on v6.9-rc1
* Renamed to "tegra241-cmdqv", following other Grace kernel patches
* Added a set of print and MMIO helpers
* Reworked the guest limitation patch
v3:
https://lore.kernel.org/all/[email protected]/
* Dropped VMID and mdev patches to redesign later based on IOMMUFD
* Separated HYP_OWN part for guest support into a new patch
* Added new preparatory changes
v2:
https://lore.kernel.org/all/[email protected]/
* Added mdev interface support for hypervisor and VMs
* Added preparatory changes for mdev interface implementation
* PATCH-12 Changed ->issue_cmdlist() to ->get_cmdq() for a better integration with recently merged ECMDQ-related changes
v1:
https://lore.kernel.org/all/[email protected]/
Nate Watterson (1):
iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace)
CMDQV
Nicolin Chen (5):
iommu/arm-smmu-v3: Pass in cmdq pointer to
arm_smmu_cmdq_issue_cmdlist()
iommu/arm-smmu-v3: Add CS_NONE quirk iommu/arm-smmu-v3: Make arm_smmu_cmdq_init reusable iommu/arm-smmu-v3: Make __arm_smmu_cmdq_skip_err reusable iommu/tegra241-cmdqv: Limit CMDs for guest owned VINTF
MAINTAINERS | 1 + drivers/iommu/Kconfig | 12 + drivers/iommu/arm/arm-smmu-v3/Makefile | 1 + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 65 +- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 47 + .../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 857 ++++++++++++++++++ 6 files changed, 961 insertions(+), 22 deletions(-) create mode 100644 drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
Signature · 1 line
2.43.0
PATCH v6 1/6 iommu/arm-smmu-v3: Pass in cmdq pointer to arm_smmu_cmdq_issue_cmdlist()
The driver currently calls arm_smmu_get_cmdq() helper in different places, although they are all called from the arm_smmu_cmdq_issue_cmdlist().
Allow to pass in the cmdq pointer, instead of calling arm_smmu_get_cmdq() every time.
This will also help CMDQV extension in NVIDIA Tegra241 SoC, as its driver will maintain its own cmdq pointers, then need to redirect arm_smmu->cmdq to one of its vcmdqs upon seeing a supported command.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +8 -7
@@ -596,11 +596,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 ;/*
@@ -631,11 +631,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);
@@ -655,10 +655,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 ;
@@ -705,12 +705,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,
@@ -767,7 +768,7 @@ static 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); }
@@ -843,7 +844,7 @@ static 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",
Signature · 1 line
2.43.0
Re: [PATCH v6 1/6] iommu/arm-smmu-v3: Pass in cmdq pointer to arm_smmu_cmdq_issue_cmdlist()
Quoted 6 lines · Nicolin Chen
The driver currently calls
arm_smmu_get_cmdq()helper in different places, although they are all called from thearm_smmu_cmdq_issue_cmdlist().Allow to pass in the cmdq pointer, instead of calling
arm_smmu_get_cmdq()every time.
Quoted 8 lines
This will also help CMDQV extension in NVIDIA Tegra241 SoC, as its driver will maintain its own cmdq pointers, then need to redirect
arm_smmu->cmdqto one of its vcmdqs upon seeing a supported command.Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-)
Jason
PATCH v6 2/6 iommu/arm-smmu-v3: Add CS_NONE quirk
In reply to Nicolin ChenThe CMDQV extension in NVIDIA Tegra241 SoC only supports CS_NONE in the CS field of CMD_SYNC. Add a quirk flag to accommodate that.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 10 ++++++++-- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +8 -2
@@ -334,7 +334,9 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent) cmd[1 ] |=FIELD_PREP (CMDQ_RESUME_1_STAG , ent->resume.stag);break ;case CMDQ_OP_CMD_SYNC : -if (ent->sync.msiaddr) { +if (ent->sync.cs_none) { + cmd[0 ] |=FIELD_PREP (CMDQ_SYNC_0_CS ,CMDQ_SYNC_0_CS_NONE ); + }else if (ent->sync.msiaddr) { cmd[0 ] |=FIELD_PREP (CMDQ_SYNC_0_CS ,CMDQ_SYNC_0_CS_IRQ ); cmd[1 ] |= ent->sync.msiaddr &CMDQ_SYNC_1_MSIADDR_MASK ; }else {
@@ -371,6 +373,9 @@ static void arm_smmu_cmdq_build_sync_cmd(u64 *cmd, struct arm_smmu_device *smmu, q->ent_dwords *8 ; } +if (q->quirks &CMDQ_QUIRK_SYNC_CS_NONE_ONLY ) + ent.sync.cs_none = true; +arm_smmu_cmdq_build_cmd (cmd, &ent); }
@@ -708,7 +713,8 @@ 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 ) +if (smmu->options &ARM_SMMU_OPT_MSIPOLL && + !(cmdq->q.quirks &CMDQ_QUIRK_SYNC_CS_NONE_ONLY ))return __arm_smmu_cmdq_poll_until_msi (smmu, cmdq, llq);return __arm_smmu_cmdq_poll_until_consumed (smmu, cmdq, llq);
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +4 -0
@@ -510,6 +510,7 @@ struct arm_smmu_cmdq_ent {#define CMDQ_OP_CMD_SYNC 0x46struct {u64 msiaddr; +bool cs_none; } sync; }; };
@@ -542,6 +543,9 @@ struct arm_smmu_queue {u32 __iomem *prod_reg;u32 __iomem *cons_reg; + +#define CMDQ_QUIRK_SYNC_CS_NONE_ONLY BIT(0)/* CMD_SYNC CS field supports CS_NONE only */ +u32 quirks; };struct arm_smmu_queue_poll {
Signature · 1 line
2.43.0
Re: [PATCH v6 2/6] iommu/arm-smmu-v3: Add CS_NONE quirk
Quoted 8 lines · Nicolin Chen
The CMDQV extension in NVIDIA Tegra241 SoC only supports
CS_NONEin the CS field ofCMD_SYNC. Add a quirk flag to accommodate that.Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 10 ++++++++-- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-)
This seems fine, other than the misplaced hunk
But it might be tidier like the below. There is already a function that is called to build the sync that has the q, just build it directly there and avoid going through the ent?
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +21 -33
@@ -338,18 +338,6 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent) cmd[0 ] |=FIELD_PREP (CMDQ_RESUME_0_RESP , ent->resume.resp); cmd[1 ] |=FIELD_PREP (CMDQ_RESUME_1_STAG , ent->resume.stag);break ; -case CMDQ_OP_CMD_SYNC : -if (ent->sync.cs_none) { - cmd[0 ] |=FIELD_PREP (CMDQ_SYNC_0_CS ,CMDQ_SYNC_0_CS_NONE ); - }else if (ent->sync.msiaddr) { - cmd[0 ] |=FIELD_PREP (CMDQ_SYNC_0_CS ,CMDQ_SYNC_0_CS_IRQ ); - cmd[1 ] |= ent->sync.msiaddr &CMDQ_SYNC_1_MSIADDR_MASK ; - }else { - cmd[0 ] |=FIELD_PREP (CMDQ_SYNC_0_CS ,CMDQ_SYNC_0_CS_SEV ); - } - cmd[0 ] |=FIELD_PREP (CMDQ_SYNC_0_MSH ,ARM_SMMU_SH_ISH ); - cmd[0 ] |=FIELD_PREP (CMDQ_SYNC_0_MSIATTR ,ARM_SMMU_MEMATTR_OIWB ); -break ;default :return -ENOENT ; }
@@ -367,25 +355,30 @@ static struct arm_smmu_cmdq *arm_smmu_get_cmdq(struct arm_smmu_device *smmu, }static void arm_smmu_cmdq_build_sync_cmd (u64 *cmd,struct arm_smmu_device *smmu, -struct arm_smmu_queue *q,u32 prod) +struct arm_smmu_queue *q,u32 prod, +bool msi) { -struct arm_smmu_cmdq_ent ent = { - .opcode =CMDQ_OP_CMD_SYNC , - }; +memset (cmd,0 ,1 <<CMDQ_ENT_SZ_SHIFT ); + cmd[0 ] =FIELD_PREP (CMDQ_0_OP ,CMDQ_OP_CMD_SYNC ) | +FIELD_PREP (CMDQ_SYNC_0_MSH ,ARM_SMMU_SH_ISH ) | +FIELD_PREP (CMDQ_SYNC_0_MSIATTR ,ARM_SMMU_MEMATTR_OIWB ); +if (q->quirks &CMDQ_QUIRK_SYNC_CS_NONE_ONLY ) { + cmd[0 ] |=FIELD_PREP (CMDQ_SYNC_0_CS ,CMDQ_SYNC_0_CS_NONE ); +return ; + } + +if (!msi || !(smmu->options &ARM_SMMU_OPT_MSIPOLL )) { + cmd[0 ] |=FIELD_PREP (CMDQ_SYNC_0_CS ,CMDQ_SYNC_0_CS_SEV ); +return ; + }/* * Beware that Hi16xx adds an extra 32 bits of goodness to its MSI * payload, so the write will zero the entire command on that platform. */ -if (smmu->options &ARM_SMMU_OPT_MSIPOLL ) { - ent.sync.msiaddr = q->base_dma +Q_IDX (&q->llq, prod) * - q->ent_dwords *8 ; - } - -if (q->quirks &CMDQ_QUIRK_SYNC_CS_NONE_ONLY ) - ent.sync.cs_none = true; - -arm_smmu_cmdq_build_cmd (cmd, &ent); + cmd[0 ] |=FIELD_PREP (CMDQ_SYNC_0_CS ,CMDQ_SYNC_0_CS_IRQ ); + cmd[1 ] = (q->base_dma +Q_IDX (&q->llq, prod) * q->ent_dwords *8 ) & +CMDQ_SYNC_1_MSIADDR_MASK ; } void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,
@@ -402,9 +395,6 @@ void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,u64 cmd[CMDQ_ENT_DWORDS ];u32 cons =readl_relaxed (q->cons_reg);u32 idx =FIELD_GET (CMDQ_CONS_ERR , cons); -struct arm_smmu_cmdq_ent cmd_sync = { - .opcode =CMDQ_OP_CMD_SYNC , - };dev_err (smmu->dev,"CMDQ error (cons 0x%08x): %s\n" , cons, idx <ARRAY_SIZE (cerror_str) ? cerror_str[idx] :"Unknown" );
@@ -437,11 +427,8 @@ void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,for (i =0 ; i <ARRAY_SIZE (cmd); ++i)dev_err (smmu->dev,"\t0x%016llx\n" , (unsigned long long )cmd[i]); -if (q->quirks &CMDQ_QUIRK_SYNC_CS_NONE_ONLY ) - cmd_sync.sync.cs_none = true; -/* Convert the erroneous command into a CMD_SYNC */ -arm_smmu_cmdq_build_cmd (cmd, &cmd_sync); +arm_smmu_cmdq_build_sync_cmd (cmd, smmu, q,0 , false);queue_write (Q_ENT (q, cons), cmd, q->ent_dwords); }
@@ -812,7 +799,8 @@ static int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu,arm_smmu_cmdq_write_entries (cmdq, cmds, llq.prod, n);if (sync) { prod =queue_inc_prod_n (&llq, n); -arm_smmu_cmdq_build_sync_cmd (cmd_sync, smmu, &cmdq->q, prod); +arm_smmu_cmdq_build_sync_cmd (cmd_sync, smmu, &cmdq->q, prod, + true);queue_write (Q_ENT (&cmdq->q, prod), cmd_sync,CMDQ_ENT_DWORDS );/*
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +0 -4
@@ -520,10 +520,6 @@ struct arm_smmu_cmdq_ent { } resume;#define CMDQ_OP_CMD_SYNC 0x46 -struct { -u64 msiaddr; -bool cs_none; - } sync; }; };
Re: [PATCH v6 2/6] iommu/arm-smmu-v3: Add CS_NONE quirk
Quoted 10 lines · Jason Gunthorpe
Quoted 8 lines · Nicolin Chen
The CMDQV extension in NVIDIA Tegra241 SoC only supports
CS_NONEin the CS field ofCMD_SYNC. Add a quirk flag to accommodate that.Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 10 ++++++++-- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-)
Quoted 7 lines
This seems fine, other than the misplaced hunk
Reviewed-by Jason Gunthorpe <[email protected]>But it might be tidier like the below. There is already a function that is called to build the sync that has the q, just build it directly there and avoid going through the ent?
Yea, and looks like we can have a patch tidying the existing sync building function, and then another one adding CS_NONE.
Quoted 5 lines
static void arm_smmu_cmdq_build_sync_cmd(u64 *cmd, struct
arm_smmu_device*smmu,drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2
-1- struct arm_smmu_queue *q, u32 prod) + struct arm_smmu_queue *q, u32 prod, + bool msi) {
...
Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +4
-0+if (!msi || !(smmu->options &ARM_SMMU_OPT_MSIPOLL )) { + cmd[0 ] |=FIELD_PREP (CMDQ_SYNC_0_CS ,CMDQ_SYNC_0_CS_SEV ); +return ; + }
...
Quoted 12 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +0
-3@@ -402,9 +395,6 @@ void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,u64 cmd[CMDQ_ENT_DWORDS ];u32 cons =readl_relaxed (q->cons_reg);u32 idx =FIELD_GET (CMDQ_CONS_ERR , cons); -struct arm_smmu_cmdq_ent cmd_sync = { - .opcode =CMDQ_OP_CMD_SYNC , - };dev_err (smmu->dev,"CMDQ error (cons 0x%08x): %s\n" , cons, idx <ARRAY_SIZE (cerror_str) ? cerror_str[idx] :"Unknown" );@@ -437,11 +427,8 @@ void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,for (i =0 ; i <ARRAY_SIZE (cmd); ++i)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu, · line 433
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +1
-4dev_err (smmu->dev,"\t0x%016llx\n" , (unsigned long long )cmd[i]); -if (q->quirks &CMDQ_QUIRK_SYNC_CS_NONE_ONLY ) - cmd_sync.sync.cs_none = true; -/* Convert the erroneous command into a CMD_SYNC */ -arm_smmu_cmdq_build_cmd (cmd, &cmd_sync); +arm_smmu_cmdq_build_sync_cmd (cmd, smmu, q,0 , false);queue_write (Q_ENT (q, cons), cmd, q->ent_dwords);
Here is the only caller for "msi=false". Maybe we could just do:
+ arm_smmu_cmdq_build_sync_cmd(cmd, smmu, q, cons);
So, no need of "bool msi"? It would slightly change the behavior though, a SYNC for ARM_SMMU_OPT_MSIPOLL should be still a SYNC.
Thanks
Nicolin
Re: [PATCH v6 2/6] iommu/arm-smmu-v3: Add CS_NONE quirk
Quoted 7 lines · Nicolin Chen
Quoted 1 line
queue_write(Q_ENT(q, cons), cmd, q->ent_dwords);Here is the only caller for "msi=false". Maybe we could just do:
(quoted diff) +1
-0+ arm_smmu_cmdq_build_sync_cmd(cmd, smmu, q, cons);So, no need of "bool msi"? It would slightly change the behavior though, a SYNC for
ARM_SMMU_OPT_MSIPOLLshould be still a SYNC.
I don't know, I didn't try to figure out what to stick for prod in that case. It is probably OK to convert an error entry into a MSI sync if it works out?
Jason
Re: [PATCH v6 2/6] iommu/arm-smmu-v3: Add CS_NONE quirk
Quoted 6 lines · Jason Gunthorpe
Quoted 5 lines · Nicolin Chen
Quoted 1 line
queue_write(Q_ENT(q, cons), cmd, q->ent_dwords);Here is the only caller for "msi=false". Maybe we could just do:
(quoted diff) +1
-0+ arm_smmu_cmdq_build_sync_cmd(cmd, smmu, q, cons);
Quoted 6 lines
Quoted 2 lines
So, no need of "bool msi"? It would slightly change the behavior though, a SYNC for
ARM_SMMU_OPT_MSIPOLLshould be still a SYNC.I don't know, I didn't try to figure out what to stick for prod in that case. It is probably OK to convert an error entry into a MSI sync if it works out?
Yea. I think it should be okay. I will try a hack with an illegal command to test it out.
Nicolin
PATCH v6 3/6 iommu/arm-smmu-v3: Make arm_smmu_cmdq_init reusable
In reply to Nicolin ChenThe CMDQV extension in NVIDIA Tegra241 SoC resues the arm_smmu_cmdq structure while the queue location isn't same as smmu->cmdq.
Add a cmdq argument to arm_smmu_cmdq_init() function and shares its define in the header for CMDQV driver to use.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 6 +++--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +3 -3
@@ -3145,9 +3145,9 @@ static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu,return 0 ; } -static int arm_smmu_cmdq_init (struct arm_smmu_device *smmu) +int arm_smmu_cmdq_init (struct arm_smmu_device *smmu, +struct arm_smmu_cmdq *cmdq) { -struct arm_smmu_cmdq *cmdq = &smmu->cmdq;unsigned int nents =1 << cmdq->q.llq.max_n_shift;atomic_set (&cmdq->owner_prod,0 );
@@ -3172,7 +3172,7 @@ static int arm_smmu_init_queues(struct arm_smmu_device *smmu)if (ret)return ret; - ret =arm_smmu_cmdq_init (smmu); + ret =arm_smmu_cmdq_init (smmu, &smmu->cmdq);if (ret)return ret;
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +3 -0
@@ -760,6 +760,9 @@ bool arm_smmu_free_asid(struct arm_smmu_ctx_desc *cd);int arm_smmu_atc_inv_domain (struct arm_smmu_domain *smmu_domain,int ssid,unsigned long iova,size_t size); +int arm_smmu_cmdq_init (struct arm_smmu_device *smmu, +struct arm_smmu_cmdq *cmdq); +#ifdef CONFIG_ARM_SMMU_V3_SVA bool arm_smmu_sva_supported (struct arm_smmu_device *smmu);bool arm_smmu_master_sva_supported (struct arm_smmu_master *master);
Signature · 1 line
2.43.0
Re: [PATCH v6 3/6] iommu/arm-smmu-v3: Make arm_smmu_cmdq_init reusable
Quoted 11 lines · Nicolin Chen
The CMDQV extension in NVIDIA Tegra241 SoC resues the
arm_smmu_cmdqstructure while the queue location isn't same assmmu->cmdq.Add a cmdq argument to
arm_smmu_cmdq_init()function and shares its define in the header for CMDQV driver to use.Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 6 +++--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-)
I would squash this patch and the next together:
iommu/arm-smm-v3: Make symbols public
The symbols arm_smmu_cmdq_init() and __arm_smmu_cmdq_skip_err() need to be used by the tegra241-cmdqv.c compilation unit in the next patch. Remove the static and put prototypes in the header.
But the code is fine
Jason
Re: [PATCH v6 3/6] iommu/arm-smmu-v3: Make arm_smmu_cmdq_init reusable
Quoted 17 lines · Jason Gunthorpe
Quoted 11 lines · Nicolin Chen
The CMDQV extension in NVIDIA Tegra241 SoC resues the
arm_smmu_cmdqstructure while the queue location isn't same assmmu->cmdq.Add a cmdq argument to
arm_smmu_cmdq_init()function and shares its define in the header for CMDQV driver to use.Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 6 +++--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-)I would squash this patch and the next together:
iommu/arm-smm-v3: Make symbols public
Quoted 7 lines
The symbols
arm_smmu_cmdq_init()and__arm_smmu_cmdq_skip_err()need to be used by thetegra241-cmdqv.ccompilation unit in the next patch. Remove the static and put prototypes in the header.But the code is fine
Reviewed-by Jason Gunthorpe <[email protected]>
Then, arm_smmu_init_one_queue could be moved to this patch too.
Thanks
Nicolin
PATCH v6 4/6 iommu/arm-smmu-v3: Make __arm_smmu_cmdq_skip_err reusable
In reply to Nicolin ChenAllow __arm_smmu_cmdq_skip_err function to be reused by NVIDIA Tegra241 CMDQV unit since it will use the same data structure for q. And include the CMDQ_QUIRK_SYNC_CS_NONE_ONLY quirk when inserting a CMD_SYNC.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 7 +++++-- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +5 -2
@@ -379,8 +379,8 @@ static void arm_smmu_cmdq_build_sync_cmd(u64 *cmd, struct arm_smmu_device *smmu,arm_smmu_cmdq_build_cmd (cmd, &ent); } -static void __arm_smmu_cmdq_skip_err (struct arm_smmu_device *smmu, -struct arm_smmu_queue *q) +void __arm_smmu_cmdq_skip_err (struct arm_smmu_device *smmu, +struct arm_smmu_queue *q) {static const char *const cerror_str[] = { [CMDQ_ERR_CERROR_NONE_IDX ] ="No error" ,
@@ -428,6 +428,9 @@ static void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,for (i =0 ; i <ARRAY_SIZE (cmd); ++i)dev_err (smmu->dev,"\t0x%016llx\n" , (unsigned long long )cmd[i]); +if (q->quirks &CMDQ_QUIRK_SYNC_CS_NONE_ONLY ) + cmd_sync.sync.cs_none = true; +/* Convert the erroneous command into a CMD_SYNC */ arm_smmu_cmdq_build_cmd (cmd, &cmd_sync);
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +2 -0
@@ -762,6 +762,8 @@ int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain, int ssid,int arm_smmu_cmdq_init (struct arm_smmu_device *smmu,struct arm_smmu_cmdq *cmdq); +void __arm_smmu_cmdq_skip_err (struct arm_smmu_device *smmu, +struct arm_smmu_queue *q);#ifdef CONFIG_ARM_SMMU_V3_SVA bool arm_smmu_sva_supported (struct arm_smmu_device *smmu);
Signature · 1 line
2.43.0
Re: [PATCH v6 4/6] iommu/arm-smmu-v3: Make __arm_smmu_cmdq_skip_err reusable
Quoted 26 lines · Nicolin Chen
Allow
__arm_smmu_cmdq_skip_errfunction to be reused by NVIDIA Tegra241 CMDQV unit since it will use the same data structure for q. And include theCMDQ_QUIRK_SYNC_CS_NONE_ONLYquirk when inserting aCMD_SYNC.Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 7 +++++-- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-)drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2
-2@@ -379,8 +379,8 @@ static void arm_smmu_cmdq_build_sync_cmd(u64 *cmd, struct arm_smmu_device *smmu,arm_smmu_cmdq_build_cmd (cmd, &ent); } -static void __arm_smmu_cmdq_skip_err (struct arm_smmu_device *smmu, -struct arm_smmu_queue *q) +void __arm_smmu_cmdq_skip_err (struct arm_smmu_device *smmu, +struct arm_smmu_queue *q) {static const char *const cerror_str[] = { [CMDQ_ERR_CERROR_NONE_IDX ] ="No error" ,
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu, · line 432
Quoted 5 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2
-0@@ -428,6 +428,9 @@ static void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,for (i =0 ; i <ARRAY_SIZE (cmd); ++i)dev_err (smmu->dev,"\t0x%016llx\n" , (unsigned long long )cmd[i]); +if (q->quirks &CMDQ_QUIRK_SYNC_CS_NONE_ONLY ) + cmd_sync.sync.cs_none = true;
This hunk should be in "iommu/arm-smmu-v3: Add CS_NONE quirk" ?
Jason
Re: [PATCH v6 4/6] iommu/arm-smmu-v3: Make __arm_smmu_cmdq_skip_err reusable
Quoted 30 lines · Jason Gunthorpe
Quoted 29 lines · Nicolin Chen
Allow
__arm_smmu_cmdq_skip_errfunction to be reused by NVIDIA Tegra241 CMDQV unit since it will use the same data structure for q. And include theCMDQ_QUIRK_SYNC_CS_NONE_ONLYquirk when inserting aCMD_SYNC.Signed-off-by Nicolin Chen <[email protected]>--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 7 +++++-- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-)drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2
-2@@ -379,8 +379,8 @@ static void arm_smmu_cmdq_build_sync_cmd(u64 *cmd, struct arm_smmu_device *smmu,arm_smmu_cmdq_build_cmd (cmd, &ent); } -static void __arm_smmu_cmdq_skip_err (struct arm_smmu_device *smmu, -struct arm_smmu_queue *q) +void __arm_smmu_cmdq_skip_err (struct arm_smmu_device *smmu, +struct arm_smmu_queue *q) {static const char *const cerror_str[] = { [CMDQ_ERR_CERROR_NONE_IDX ] ="No error" ,@@ -428,6 +428,9 @@ static void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu,for (i =0 ; i <ARRAY_SIZE (cmd); ++i)dev_err (smmu->dev,"\t0x%016llx\n" , (unsigned long long )cmd[i]);
Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static void __arm_smmu_cmdq_skip_err(struct arm_smmu_device *smmu, · line 432
Quoted 2 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2
-0+if (q->quirks &CMDQ_QUIRK_SYNC_CS_NONE_ONLY ) + cmd_sync.sync.cs_none = true;This hunk should be in "iommu/arm-smmu-v3: Add
CS_NONEquirk" ?
Oh, yea. Will move it.
Thanks!
Nicolin
PATCH v6 5/6 iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV
In reply to Nicolin ChenFrom: Nate Watterson <[email protected]>
NVIDIA's Tegra241 Soc has a CMDQ-Virtualization (CMDQV) hardware, extending the standard ARM SMMU v3 IP to support multiple VCMDQs with virtualization capabilities. In terms of command queue, they are very like a standard SMMU CMDQ (or ECMDQs), but only support CS_NONE in the CS field of CMD_SYNC.
Add a new tegra241-cmdqv driver, and insert its structure pointer into the existing arm_smmu_device, and then add related function calls in the SMMUv3 driver to interact with the CMDQV driver.
In the CMDQV driver, add a minimal part for the in-kernel support: reserve VINTF0 for in-kernel use, and assign some of the VCMDQs to the VINTF0, and select one VCMDQ based on the current CPU ID to execute supported commands. This multi-queue design for in-kernel use gives some limited improvements:
up to 20% reduction of invalidation time was measured by a multi-threaded DMA unmap benchmark, compared to a single queue.
The other part of the CMDQV driver will be user-space support that gives a hypervisor running on the host OS to talk to the driver for virtualization use cases, allowing VMs to use VCMDQs without trappings, i.e. no VM Exits. This is currently WIP based on IOMMUFD, and will be sent for review after SMMU nesting patches are getting merged. This part will provide a guest OS a bigger improvement: 70% to 90% reductions of TLB invalidation time were measured by DMA unmap tests running in a guest OS, compared to nested SMMU CMDQ (with trappings).
However, it is very important for this in-kernel support to get merged and installed to VMs running on Grace-powered servers as soon as possible. So, later those servers would only need to upgrade their host kernels for the user-space support.
As the initial version, the CMDQV driver only supports ACPI configurations.
--- MAINTAINERS | 1 + drivers/iommu/Kconfig | 12 + drivers/iommu/arm/arm-smmu-v3/Makefile | 1 + drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 22 +- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 37 + .../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 815 ++++++++++++++++++ 6 files changed, 882 insertions(+), 6 deletions(-) create mode 100644 drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c
MAINTAINERS +1 -0
@@ -21742,6 +21742,7 @@ M: Thierry Reding <[email protected]> R: Krishna Reddy <[email protected]> L: [email protected] S: Supported +F: drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c F: drivers/iommu/arm/arm-smmu/arm-smmu-nvidia.c F: drivers/iommu/tegra*
drivers/iommu/Kconfig +12 -0
@@ -410,6 +410,18 @@ config ARM_SMMU_V3_SVA Say Y here if your system supports SVA extensions such as PCIe PASID and PRI. +config TEGRA241_CMDQV + bool "NVIDIA Tegra241 CMDQ-V extension support for ARM SMMUv3" + depends on ARM_SMMU_V3 + depends on ACPI + help + Support for NVIDIA CMDQ-Virtualization extension for ARM SMMUv3. The + CMDQ-V extension is similar to v3.3 ECMDQ for multi command queues + support, except with virtualization capabilities. + + Say Y here if your system is NVIDIA Tegra241 (Grace) or it has the same + CMDQ-V extension. + config S390_IOMMU def_bool y if S390 && PCI depends on S390 && PCI
drivers/iommu/arm/arm-smmu-v3/Makefile +1 -0
@@ -2,4 +2,5 @@ obj-$(CONFIG_ARM_SMMU_V3) += arm_smmu_v3.o arm_smmu_v3-objs-y += arm-smmu-v3.o arm_smmu_v3-objs-$(CONFIG_ARM_SMMU_V3_SVA) += arm-smmu-v3-sva.o +arm_smmu_v3-objs-$(CONFIG_TEGRA241_CMDQV) += tegra241-cmdqv.o arm_smmu_v3-objs := $(arm_smmu_v3-objs-y)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +16 -6
@@ -354,6 +354,9 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)static struct arm_smmu_cmdq *arm_smmu_get_cmdq (struct arm_smmu_device *smmu) { +if (smmu->tegra241_cmdqv) +return tegra241_cmdqv_get_cmdq (smmu); +return &smmu->cmdq; }
@@ -3105,12 +3108,10 @@ static struct iommu_ops arm_smmu_ops = { };/* Probing and initialisation functions */ -static int arm_smmu_init_one_queue (struct arm_smmu_device *smmu, -struct arm_smmu_queue *q, -void __iomem *page, -unsigned long prod_off, -unsigned long cons_off, -size_t dwords,const char *name) +int arm_smmu_init_one_queue (struct arm_smmu_device *smmu, +struct arm_smmu_queue *q,void __iomem *page, +unsigned long prod_off,unsigned long cons_off, +size_t dwords,const char *name) {size_t qsz;
@@ -3567,6 +3568,12 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu, bool bypass)return ret; } +if (smmu->tegra241_cmdqv) { + ret =tegra241_cmdqv_device_reset (smmu); +if (ret) +return ret; + } +/* Invalidate any cached configuration */ cmd.opcode =CMDQ_OP_CFGI_ALL ;arm_smmu_cmdq_issue_cmd_with_sync (smmu, &cmd);
@@ -3941,6 +3948,9 @@ static int arm_smmu_device_acpi_probe(struct platform_device *pdev,if (iort_smmu->flags &ACPI_IORT_SMMU_V3_COHACC_OVERRIDE ) smmu->features |=ARM_SMMU_FEAT_COHERENCY ; + smmu->tegra241_cmdqv = +tegra241_cmdqv_acpi_probe (smmu, node->identifier); + return 0; }#else
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +37 -0
@@ -626,6 +626,8 @@ struct arm_smmu_strtab_cfg {u32 strtab_base_cfg; }; +struct tegra241_cmdqv ; +/* An SMMUv3 instance */ struct arm_smmu_device {struct device *dev;
@@ -689,6 +691,12 @@ struct arm_smmu_device {struct rb_root streams;struct mutex streams_mutex; + +/* +* Pointer to NVIDIA Tegra241 CMDQ-Virtualization Extension support, +* similar to v3.3 ECMDQ except with virtualization capabilities. +*/ +struct tegra241_cmdqv *tegra241_cmdqv; };struct arm_smmu_stream {
@@ -764,6 +772,10 @@ int arm_smmu_cmdq_init(struct arm_smmu_device *smmu, struct arm_smmu_cmdq *cmdq);void __arm_smmu_cmdq_skip_err (struct arm_smmu_device *smmu,struct arm_smmu_queue *q); +int arm_smmu_init_one_queue (struct arm_smmu_device *smmu, +struct arm_smmu_queue *q,void __iomem *page, +unsigned long prod_off,unsigned long cons_off, +size_t dwords,const char *name);#ifdef CONFIG_ARM_SMMU_V3_SVA bool arm_smmu_sva_supported (struct arm_smmu_device *smmu);
@@ -820,4 +832,29 @@ static inline void arm_smmu_sva_remove_dev_pasid(struct iommu_domain *domain, { }#endif /* CONFIG_ARM_SMMU_V3_SVA */ + +#ifdef CONFIG_TEGRA241_CMDQV +struct tegra241_cmdqv * +tegra241_cmdqv_acpi_probe (struct arm_smmu_device *smmu,int id); +int tegra241_cmdqv_device_reset (struct arm_smmu_device *smmu); +struct arm_smmu_cmdq *tegra241_cmdqv_get_cmdq (struct arm_smmu_device *smmu); +#else /* CONFIG_TEGRA241_CMDQV */ +static inline struct tegra241_cmdqv * +tegra241_cmdqv_acpi_probe (struct arm_smmu_device *smmu,int id) +{ +return NULL ; +} + +static inline int tegra241_cmdqv_device_reset (struct arm_smmu_device *smmu) +{ +return -ENODEV ; +} + +static inline struct arm_smmu_cmdq * +tegra241_cmdqv_get_cmdq (struct arm_smmu_device *smmu) +{ +return NULL ; +} +#endif /* CONFIG_TEGRA241_CMDQV */ +#endif /* _ARM_SMMU_V3_H */
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +815 -0
815 lines not on this page · raw message
Signature · 1 line
2.43.0
Re: [PATCH v6 5/6] iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent) · line 358
Quoted 4 lines · Nicolin Chen
static struct
arm_smmu_cmdq*arm_smmu_get_cmdq(structarm_smmu_device*smmu) {drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2
-0+if (smmu->tegra241_cmdqv) +return tegra241_cmdqv_get_cmdq (smmu);
Since it is compile time optional it would make some sense to optimize this (in all the places) too:
if (arm_smmu_has_smmu_tegra241_cmdqv(smmu))
[..]
static inline bool arm_smmu_has_smmu_tegra241_cmdqv(struct arm_smmu_device *smmu)
{
return IS_ENABLED(CONFIG_TEGRA241_CMDQV) && smmu->tegra241_cmdqv;
}Quoted 6 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +0
-2@@ -3105,12 +3108,10 @@ static struct iommu_ops arm_smmu_ops = { };/* Probing and initialisation functions */ -static int arm_smmu_init_one_queue(struct arm_smmu_device *smmu, - struct arm_smmu_queue *q,
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static struct iommu_ops arm_smmu_ops = { · line 3116
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +4
-4-void __iomem *page, - unsigned long prod_off, - unsigned long cons_off, - size_t dwords, const char *name) +int arm_smmu_init_one_queue (struct arm_smmu_device *smmu, +struct arm_smmu_queue *q,void __iomem *page, +unsigned long prod_off,unsigned long cons_off, +size_t dwords,const char *name) {size_t qsz;
This hunk and the .h file part should be moved to the prior patch that is de-exporting things.
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 103
Quoted 9 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +9
-0+/* MMIO helpers */ +#define cmdqv_readl (reg) \ + readl(cmdqv->base + TEGRA241_CMDQV_##reg) +#define cmdqv_readl_relaxed (reg) \ + readl_relaxed(cmdqv->base + TEGRA241_CMDQV_##reg) +#define cmdqv_writel (val, reg) \ + writel((val), cmdqv->base + TEGRA241_CMDQV_##reg) +#define cmdqv_writel_relaxed (val, reg) \ + writel_relaxed((val), cmdqv->base + TEGRA241_CMDQV_##reg)
Please don't hide access to a stack variable in a macro, and I'm not keen on the ##reg scheme either - it makes it much harder to search for things.
Really this all seems like alot of overkill to make a little bit of shorthand. It is not so wordy just to type it out:
readl(vintf->base + TEGRA241_VINTF_CONFIG)
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 146
Quoted 9 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +9
-0+/* Logging helpers */ +#define cmdqv_warn (fmt, ...) \ + dev_warn(cmdqv->dev, "CMDQV: " fmt, ##__VA_ARGS__) +#define cmdqv_err (fmt, ...) \ + dev_err(cmdqv->dev, "CMDQV: " fmt, ##__VA_ARGS__) +#define cmdqv_info (fmt, ...) \ + dev_info(cmdqv->dev, "CMDQV: " fmt, ##__VA_ARGS__) +#define cmdqv_dbg (fmt, ...) \ + dev_dbg(cmdqv->dev, "CMDQV: " fmt, ##__VA_ARGS__)
Really not sure these are necessary, same remark about the stack variable.
Also cmdqv->dev is the wrong thing to print, this is part of the smmu driver, it should print cmdqv->smmu->dev for consistency
Quoted 43 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +43
-0+#define vintf_warn (fmt, ...) \ + dev_warn(vintf->cmdqv->dev, "VINTF%u: " fmt, vintf->idx, ##__VA_ARGS__) +#define vintf_err (fmt, ...) \ + dev_err(vintf->cmdqv->dev, "VINTF%u: " fmt, vintf->idx, ##__VA_ARGS__) +#define vintf_info (fmt, ...) \ + dev_info(vintf->cmdqv->dev, "VINTF%u: " fmt, vintf->idx, ##__VA_ARGS__) +#define vintf_dbg (fmt, ...) \ + dev_dbg(vintf->cmdqv->dev, "VINTF%u: " fmt, vintf->idx, ##__VA_ARGS__) + +#define vcmdq_warn (fmt, ...) \ + ({ \ + struct tegra241_vintf *vintf = vcmdq->vintf; \ + if (vintf) \ + vintf_warn("VCMDQ%u/LVCMDQ%u: " fmt, \ + vcmdq->idx, vcmdq->lidx, \ + ##__VA_ARGS__); \ + else \ + dev_warn(vcmdq->cmdqv->dev, "VCMDQ%u: " fmt, \ + vcmdq->idx, ##__VA_ARGS__); \ + }) +#define vcmdq_err (fmt, ...) \ + ({ \ + struct tegra241_vintf *vintf = vcmdq->vintf; \ + if (vintf) \ + vintf_err("VCMDQ%u/LVCMDQ%u: " fmt, \ + vcmdq->idx, vcmdq->lidx, \ + ##__VA_ARGS__); \ + else \ + dev_err(vcmdq->cmdqv->dev, "VCMDQ%u: " fmt, \ + vcmdq->idx, ##__VA_ARGS__); \ + }) +#define vcmdq_info (fmt, ...) \ + ({ \ + struct tegra241_vintf *vintf = vcmdq->vintf; \ + if (vintf) \ + vintf_info("VCMDQ%u/LVCMDQ%u: " fmt, \ + vcmdq->idx, vcmdq->lidx, \ + ##__VA_ARGS__); \ + else \ + dev_info(vcmdq->cmdqv->dev, "VCMDQ%u: " fmt, \ + vcmdq->idx, ##__VA_ARGS__); \ + }) +#define vcmdq_dbg (fmt, ...) \
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 200
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +10
-0+ ({ \ +struct tegra241_vintf *vintf = vcmdq->vintf; \ +if (vintf) \ +vintf_dbg ("VCMDQ%u/LVCMDQ%u: " fmt, \ + vcmdq->idx, vcmdq->lidx, \ + ##__VA_ARGS__); \ +else \ +dev_dbg (vcmdq->cmdqv->dev,"VCMDQ%u: " fmt, \ + vcmdq->idx, ##__VA_ARGS__); \ + })
Some of these are barely used, is it worth all these macros??
Quoted 9 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +9
-0+ +/* Configuring and polling helpers */ +#define tegra241_cmdqv_write_config (_owner, _OWNER, _regval) \ + ({ \ + bool _en = (_regval) & _OWNER##_EN; \ + u32 _status; \ + int _ret; \ + writel((_regval), _owner->base + TEGRA241_##_OWNER##_CONFIG); \ + _ret = readl_poll_timeout( \
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 219
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +10
-0+ _owner->base + TEGRA241_##_OWNER##_STATUS, _status, \ + _en ? (_regval) & _OWNER##_ENABLED : \ + !((_regval) & _OWNER##_ENABLED), \ + 1, ARM_SMMU_POLL_TIMEOUT_US); \ +if (_ret) \ + _owner##_err("failed to %sable, STATUS = 0x%08X\n", \ + _en ?"en" :"dis" , _status); \ +atomic_set (&_owner->status, _status); \ + _ret; \ + })
I feel like this could be an actual inline function without the macro wrapper with a little fiddling.
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 226
Quoted 7 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +7
-0+ +#define cmdqv_write_config (_regval) \ + tegra241_cmdqv_write_config(cmdqv, CMDQV, _regval) +#define vintf_write_config (_regval) \ + tegra241_cmdqv_write_config(vintf, VINTF, _regval) +#define vcmdq_write_config (_regval) \ + tegra241_cmdqv_write_config(vcmdq, VCMDQ, _regval)
More hidden access to stack values
Quoted 4 lines
+/**
+ * structtegra241_cmdqv- CMDQ-V for SMMUv3
+ * @smmu: SMMUv3 pointer
+ * @dev: Device pointer
This should probably be clarified as the device pointer to the ACPI companion device
Quoted 39 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +39
-0+static void tegra241_cmdqv_handle_vintf0_error (struct tegra241_cmdqv *cmdqv) +{ +struct tegra241_vintf *vintf = cmdqv->vintfs[0 ]; +int i; + +/* Cache status to bypass VCMDQs until error is recovered */ +atomic_set (&vintf->status,vintf_readl (STATUS )); + +for (i =0 ; i <4 ; i++) { +u32 lvcmdq_err_map =vintf_readl_relaxed (CMDQ_ERR_MAP (i)); + +while (lvcmdq_err_map) { +int lidx =ffs (lvcmdq_err_map) -1 ; +struct tegra241_vcmdq *vcmdq = vintf->vcmdqs[lidx]; +u32 gerrorn, gerror; + + lvcmdq_err_map &= ~BIT (lidx); + +__arm_smmu_cmdq_skip_err (cmdqv->smmu, &vcmdq->cmdq.q); + + gerrorn =vcmdq_page0_readl_relaxed (GERRORN ); + gerror =vcmdq_page0_readl_relaxed (GERROR ); + +vcmdq_page0_writel (gerror,GERRORN ); + } + } + +/* Now error status should be clean, cache it again */ +atomic_set (&vintf->status,vintf_readl (STATUS )); +} + +static irqreturn_t tegra241_cmdqv_isr (int irq,void *devid) +{ +struct tegra241_cmdqv *cmdqv = (struct tegra241_cmdqv *)devid; +u32 vintf_errs[2 ]; +u32 vcmdq_errs[4 ]; + + vintf_errs[0 ] =cmdqv_readl_relaxed (VINTF_ERR_MAP ); + vintf_errs[1 ] =cmdqv_readl_relaxed (VINTF_ERR_MAP +0x4 );
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 366
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +10
-0+ + vcmdq_errs[0 ] =cmdqv_readl_relaxed (VCMDQ_ERR_MAP (0 )); + vcmdq_errs[1 ] =cmdqv_readl_relaxed (VCMDQ_ERR_MAP (1 )); + vcmdq_errs[2 ] =cmdqv_readl_relaxed (VCMDQ_ERR_MAP (2 )); + vcmdq_errs[3 ] =cmdqv_readl_relaxed (VCMDQ_ERR_MAP (3 )); + +cmdqv_warn ("unexpected cmdqv error reported\n" ); +cmdqv_warn (" vintf_map: 0x%08X%08X\n" , vintf_errs[1 ], vintf_errs[0 ]); +cmdqv_warn (" vcmdq_map: 0x%08X%08X%08X%08X\n" , + vcmdq_errs[3 ], vcmdq_errs[2 ], vcmdq_errs[1 ], vcmdq_errs[0 ]);
Put warnings in one print only, spreading them like this just increases the risk of tearing.. It doesn't need to be all pretty.
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 382
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +8
-0+struct arm_smmu_cmdq *tegra241_cmdqv_get_cmdq (struct arm_smmu_device *smmu) +{ +struct tegra241_cmdqv *cmdqv = smmu->tegra241_cmdqv; +struct tegra241_vintf *vintf = cmdqv->vintfs[0 ]; +struct tegra241_vcmdq *vcmdq; +u16 lidx; + +if (bypass_vcmdq)
READ_ONCE
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 391
Quoted 9 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +9
-0+return &smmu->cmdq; + +/* Use SMMU CMDQ if vintfs[0] is uninitialized */ +if (!FIELD_GET (VINTF_ENABLED ,atomic_read (&vintf->status))) +return &smmu->cmdq; + +/* Use SMMU CMDQ if vintfs[0] has error status */ +if (FIELD_GET (VINTF_STATUS ,atomic_read (&vintf->status))) +return &smmu->cmdq;
Why atomic_read? The unlocked interaction with tegra241_cmdqv_handle_vintf0_error() doesn't seem especially sane IMHO
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 414
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +8
-0+static void tegra241_vcmdq_hw_deinit (struct tegra241_vcmdq *vcmdq) +{ +u32 gerrorn, gerror; + +if (vcmdq_write_config (0 )) { +vcmdq_err ("GERRORN=0x%X\n" ,vcmdq_page0_readl_relaxed (GERRORN )); +vcmdq_err ("GERROR=0x%X\n" ,vcmdq_page0_readl_relaxed (GERROR )); +vcmdq_err ("CONS=0x%X\n" ,vcmdq_page0_readl_relaxed (CONS ));
Less prints, include a unique message about why this is being printed..
Quoted 21 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +21
-0+ } +vcmdq_page0_writel_relaxed (0 ,PROD ); +vcmdq_page0_writel_relaxed (0 ,CONS ); +vcmdq_page1_writeq_relaxed (0 ,BASE ); +vcmdq_page1_writeq_relaxed (0 ,CONS_INDX_BASE ); + + gerrorn =vcmdq_page0_readl_relaxed (GERRORN ); + gerror =vcmdq_page0_readl_relaxed (GERROR ); +if (gerror != gerrorn) { +vcmdq_info ("Uncleared error detected, resetting\n" ); +vcmdq_page0_writel (gerror,GERRORN ); + } + +vcmdq_dbg ("deinited\n" ); +} + +static int tegra241_vcmdq_hw_init (struct tegra241_vcmdq *vcmdq) +{ +int ret; + +/* Configure and enable the vcmdq */
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 445
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +10
-0+tegra241_vcmdq_hw_deinit (vcmdq); + +vcmdq_page1_writeq_relaxed (vcmdq->cmdq.q.q_base,BASE ); + + ret =vcmdq_write_config (VCMDQ_EN ); +if (ret) { +vcmdq_err ("GERRORN=0x%X\n" ,vcmdq_page0_readl_relaxed (GERRORN )); +vcmdq_err ("GERROR=0x%X\n" ,vcmdq_page0_readl_relaxed (GERROR )); +vcmdq_err ("CONS=0x%X\n" ,vcmdq_page0_readl_relaxed (CONS )); +return ret;
Same print?
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 489
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +8
-0+static void tegra241_vcmdq_free_smmu_cmdq (struct tegra241_vcmdq *vcmdq) +{ +struct tegra241_cmdqv *cmdqv = vcmdq->cmdqv; +struct arm_smmu_queue *q = &vcmdq->cmdq.q; +size_t nents =1 << q->llq.max_n_shift; + +dmam_free_coherent (cmdqv->smmu->dev, (nents *CMDQ_ENT_DWORDS ) <<3 , + q->base, q->base_dma);
If we are calling dmam_free, do we really need devm at all?
Quoted 31 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +31
-0+static struct tegra241_vcmdq * +tegra241_vintf_lvcmdq_alloc (struct tegra241_vintf *vintf,u16 lidx) +{ +struct tegra241_cmdqv *cmdqv = vintf->cmdqv; +struct tegra241_vcmdq *vcmdq; +int ret; + + vcmdq =devm_kzalloc (cmdqv->dev,sizeof (*vcmdq),GFP_KERNEL ); +if (!vcmdq) +return ERR_PTR (-ENOMEM ); + + ret =tegra241_vintf_lvcmdq_init (vintf, lidx, vcmdq); +if (ret) + goto free_vcmdq; + +/* Setup struct arm_smmu_cmdq data members */ + ret =tegra241_vcmdq_alloc_smmu_cmdq (vcmdq); +if (ret) + goto deinit_lvcmdq; + + ret =tegra241_vcmdq_hw_init (vcmdq); +if (ret) + goto free_queue; + +vcmdq_dbg ("allocated\n" ); +return vcmdq; +free_queue: +tegra241_vcmdq_free_smmu_cmdq (vcmdq); +deinit_lvcmdq: +tegra241_vintf_lvcmdq_deinit (vcmdq); +free_vcmdq:
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 559
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +10
-0+devm_kfree (cmdqv->dev, vcmdq); +return ERR_PTR (ret); +} + +static void tegra241_vintf_lvcmdq_free (struct tegra241_vcmdq *vcmdq) +{ +tegra241_vcmdq_hw_deinit (vcmdq); +tegra241_vcmdq_free_smmu_cmdq (vcmdq); +tegra241_vintf_lvcmdq_deinit (vcmdq); +devm_kfree (vcmdq->cmdqv->dev, vcmdq);
Ditto for devm_kfree.
Review
Quoted 2 lines
(quoted diff) +2
-0+struct tegra241_cmdqv * +tegra241_cmdqv_acpi_probe(struct arm_smmu_device *smmu, int id)
id is a u32.
It might be clearer to just pass in the structacpi_iort_node *?
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 811
Quoted 13 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +13
-0+{ +struct tegra241_cmdqv *cmdqv; + + cmdqv =tegra241_cmdqv_find_resource (smmu, id); +if (!cmdqv) +return NULL ; + +if (tegra241_cmdqv_probe (cmdqv)) { +if (cmdqv->irq >0 ) +devm_free_irq (smmu->dev, cmdqv->irq, cmdqv); +devm_iounmap (smmu->dev, cmdqv->base); +devm_kfree (smmu->dev, cmdqv); +return NULL ;
Oh. Please don't use devm at all in this code then, it is not attached to a probed driver with the proper scope, devm isn't going to work in sensible way.
Jason
Re: [PATCH v6 5/6] iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV
(Removing chunks that I simply ack)
Quoted 15 lines · Jason Gunthorpe
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 103
Quoted 9 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +9
-0+/* MMIO helpers */ +#define cmdqv_readl (reg) \ + readl(cmdqv->base + TEGRA241_CMDQV_##reg) +#define cmdqv_readl_relaxed (reg) \ + readl_relaxed(cmdqv->base + TEGRA241_CMDQV_##reg) +#define cmdqv_writel (val, reg) \ + writel((val), cmdqv->base + TEGRA241_CMDQV_##reg) +#define cmdqv_writel_relaxed (val, reg) \ + writel_relaxed((val), cmdqv->base + TEGRA241_CMDQV_##reg)Please don't hide access to a stack variable in a macro, and I'm not keen on the ##reg scheme either - it makes it much harder to search for things.
I can pass in cmdqv/vintf/vcmdq pointers, if it would be better.
Quoted 4 lines
Really this all seems like alot of overkill to make a little bit of shorthand. It is not so wordy just to type it out:
readl(vintf->base + TEGRA241_VINTF_CONFIG)
vintf_readl(vintf, CONFIG) is much shorter. Doing so reduced the line breaks at quite a lot places, so overall the driver looks a lot cleaner to me. It also helps a bit, when I want to debug the HW configuration flow by adding prints to these helpers. It might be a personal preference, yet I would still like to have these.
Quoted 4 lines
Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +4
-0+/* Logging helpers */ +#define cmdqv_warn (fmt, ...) \ + dev_warn(cmdqv->dev, "CMDQV: " fmt, ##__VA_ARGS__) +#define cmdqv_err (fmt, ...) \
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 146
Quoted 5 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +5
-0+ dev_err(cmdqv->dev, "CMDQV: " fmt, ##__VA_ARGS__) +#define cmdqv_info (fmt, ...) \ + dev_info(cmdqv->dev, "CMDQV: " fmt, ##__VA_ARGS__) +#define cmdqv_dbg (fmt, ...) \ + dev_dbg(cmdqv->dev, "CMDQV: " fmt, ##__VA_ARGS__)Really not sure these are necessary, same remark about the stack variable.
Same justification. And it simply keeps the same style of prints. Also, it eases the following vcmdq prints.
I can probably change these logging helpers to inline functions.
Quoted 2 lines
Also
cmdqv->devis the wrong thing to print, this is part of the smmu driver, it should printcmdqv->smmu->devfor consistency
Yea. I can drop the dev from the cmdqv structure.
Quoted 14 lines
Quoted 14 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +14
-0+#define vintf_warn (fmt, ...) \ + dev_warn(vintf->cmdqv->dev, "VINTF%u: " fmt, vintf->idx, ##__VA_ARGS__) +#define vintf_err (fmt, ...) \ + dev_err(vintf->cmdqv->dev, "VINTF%u: " fmt, vintf->idx, ##__VA_ARGS__) +#define vintf_info (fmt, ...) \ + dev_info(vintf->cmdqv->dev, "VINTF%u: " fmt, vintf->idx, ##__VA_ARGS__) +#define vintf_dbg (fmt, ...) \ + dev_dbg(vintf->cmdqv->dev, "VINTF%u: " fmt, vintf->idx, ##__VA_ARGS__) + +#define vcmdq_warn (fmt, ...) \ + ({ \ + struct tegra241_vintf *vintf = vcmdq->vintf; \ + if (vintf) \ + vintf_warn("VCMDQ%u/LVCMDQ%u: " fmt, \
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 167
Quoted 6 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +6
-0+ vcmdq->idx, vcmdq->lidx, \ + ##__VA_ARGS__); \ + else \ + dev_warn(vcmdq->cmdqv->dev, "VCMDQ%u: " fmt, \ + vcmdq->idx, ##__VA_ARGS__); \ + })Some of these are barely used, is it worth all these macros??
Only vcmdq_warn isn't called. But I think it would be useful. I could also find a place to call it, if that's a must.
Quoted 14 lines
Quoted 14 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +14
-0+ +/* Configuring and polling helpers */ +#define tegra241_cmdqv_write_config (_owner, _OWNER, _regval) \ + ({ \ + bool _en = (_regval) & _OWNER##_EN; \ + u32 _status; \ + int _ret; \ + writel((_regval), _owner->base + TEGRA241_##_OWNER##_CONFIG); \ + _ret = readl_poll_timeout( \ + _owner->base + TEGRA241_##_OWNER##_STATUS, _status, \ + _en ? (_regval) & _OWNER##_ENABLED : \ + !((_regval) & _OWNER##_ENABLED), \ + 1, ARM_SMMU_POLL_TIMEOUT_US); \ + if (_ret) \
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 219
Quoted 5 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +5
-0+ _owner##_err("failed to %sable, STATUS = 0x%08X\n", \ + _en ? "en" : "dis", _status); \ +atomic_set (&_owner->status, _status); \ + _ret; \ + })I feel like this could be an actual inline function without the macro wrapper with a little fiddling.
It would be unrolled to three mostly identical inline functions:
tegra241_cmdqv_write_config(cmdqv, regval) tegra241_vintf_write_config(vintf, regval) tegra241_vcmdq_write_config(vcmdq, regval)
Quoted 9 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 226
Quoted 7 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +7
-0+ +#define cmdqv_write_config (_regval) \ + tegra241_cmdqv_write_config(cmdqv, CMDQV, _regval) +#define vintf_write_config (_regval) \ + tegra241_cmdqv_write_config(vintf, VINTF, _regval) +#define vcmdq_write_config (_regval) \ + tegra241_cmdqv_write_config(vcmdq, VCMDQ, _regval)More hidden access to stack values
Btw, any reason for forbidding this practice? It will break the build if something goes wrong, which seems to be pretty easy to catch.
Quoted 7 lines
Quoted 4 lines
+/**
+ * structtegra241_cmdqv- CMDQ-V for SMMUv3
+ * @smmu: SMMUv3 pointer
+ * @dev: Device pointerThis should probably be clarified as the device pointer to the ACPI companion device
I could drop it and use cmdqv->smmu->dev as your previous remark suggested.
Quoted 2 lines
Quoted 2 lines
(quoted diff) +2
-0+struct arm_smmu_cmdq *tegra241_cmdqv_get_cmdq(struct arm_smmu_device *smmu) +{
...
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 391
Quoted 7 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +7
-0+/* Use SMMU CMDQ if vintfs[0] is uninitialized */ +if (!FIELD_GET (VINTF_ENABLED ,atomic_read (&vintf->status))) +return &smmu->cmdq; + +/* Use SMMU CMDQ if vintfs[0] has error status */ +if (FIELD_GET (VINTF_STATUS ,atomic_read (&vintf->status))) +return &smmu->cmdq;Why
atomic_read? The unlocked interaction withtegra241_cmdqv_handle_vintf0_error()doesn't seem especially sane IMHO
Race between this get_cmdq() and the isr. Any alternative practice?
Quoted 11 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 414
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +8
-0+static void tegra241_vcmdq_hw_deinit (struct tegra241_vcmdq *vcmdq) +{ +u32 gerrorn, gerror; + +if (vcmdq_write_config (0 )) { +vcmdq_err ("GERRORN=0x%X\n" ,vcmdq_page0_readl_relaxed (GERRORN )); +vcmdq_err ("GERROR=0x%X\n" ,vcmdq_page0_readl_relaxed (GERROR )); +vcmdq_err ("CONS=0x%X\n" ,vcmdq_page0_readl_relaxed (CONS ));Less prints, include a unique message about why this is being printed..
Something must be wrong if disabling VCMDQ fails, so the prints of error register values would be helpful. And "failed to disable" is already printed by the vcmdq_write_config() call. I can merge them into one vcmdq_err call though.
Quoted 25 lines
Quoted 25 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +25
-0+ } +vcmdq_page0_writel_relaxed (0 ,PROD ); +vcmdq_page0_writel_relaxed (0 ,CONS ); +vcmdq_page1_writeq_relaxed (0 ,BASE ); +vcmdq_page1_writeq_relaxed (0 ,CONS_INDX_BASE ); + + gerrorn =vcmdq_page0_readl_relaxed (GERRORN ); + gerror =vcmdq_page0_readl_relaxed (GERROR ); +if (gerror != gerrorn) { +vcmdq_info ("Uncleared error detected, resetting\n" ); +vcmdq_page0_writel (gerror,GERRORN ); + } + +vcmdq_dbg ("deinited\n" ); +} + +static int tegra241_vcmdq_hw_init (struct tegra241_vcmdq *vcmdq) +{ +int ret; + +/* Configure and enable the vcmdq */ +tegra241_vcmdq_hw_deinit (vcmdq); + +vcmdq_page1_writeq_relaxed (vcmdq->cmdq.q.q_base,BASE ); +
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 445
Quoted 6 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +6
-0+ ret =vcmdq_write_config (VCMDQ_EN ); +if (ret) { +vcmdq_err ("GERRORN=0x%X\n" ,vcmdq_page0_readl_relaxed (GERRORN )); +vcmdq_err ("GERROR=0x%X\n" ,vcmdq_page0_readl_relaxed (GERROR )); +vcmdq_err ("CONS=0x%X\n" ,vcmdq_page0_readl_relaxed (CONS )); +return ret;Same print?
Yea. Here the prints are for a failure when enabling a VCMDQ. Again, "failed to enable" is already printed by vcmdq_write_config(). I'll merge three to one call here too.
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 489
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +8
-0+static void tegra241_vcmdq_free_smmu_cmdq (struct tegra241_vcmdq *vcmdq) +{ +struct tegra241_cmdqv *cmdqv = vcmdq->cmdqv; +struct arm_smmu_queue *q = &vcmdq->cmdq.q; +size_t nents =1 << q->llq.max_n_shift; + +dmam_free_coherent (cmdqv->smmu->dev, (nents *CMDQ_ENT_DWORDS ) <<3 , + q->base, q->base_dma);If we are calling
dmam_free, do we really need devm at all?
Hmm. This is a part of SMMU's probe/device_reset(). So, all the devm calls in cmdqv driver are following the style there, since the arm-smmu-v3 driver could be rmmod-ed? Though the arm-smmu-v3 driver seems to miss a dmam_free_coherent for its own queues..
Quoted 3 lines
Quoted 1 line
+tegra241_cmdqv_acpi_probe(struct
arm_smmu_device*smmu, int id)id is a u32.
Ack.
Quoted 2 lines
It might be clearer to just pass in the struct
acpi_iort_node*?
Well, it felt quite similar to me, yet acpi_iort_node probably fits the name better.
Quoted 9 lines
Quoted 9 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +9
-0+{ +struct tegra241_cmdqv *cmdqv; + + cmdqv =tegra241_cmdqv_find_resource (smmu, id); +if (!cmdqv) +return NULL ; + +if (tegra241_cmdqv_probe (cmdqv)) { +if (cmdqv->irq >0 )
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 811
Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +4
-0+devm_free_irq (smmu->dev, cmdqv->irq, cmdqv); +devm_iounmap (smmu->dev, cmdqv->base); +devm_kfree (smmu->dev, cmdqv); +return NULL ;Oh. Please don't use devm at all in this code then, it is not attached to a probed driver with the proper scope, devm isn't going to work in sensible way.
Mind elaborating "it is not"? This function is called by arm_smmu_device_acpi_probe and arm_smmu_device_probe.
Thanks for the help!
Nicolin
Re: [PATCH v6 5/6] iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV
Quoted 21 lines · Nicolin Chen
(Removing chunks that I simply ack)
Quoted 15 lines · Jason Gunthorpe
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 103
Quoted 9 lines · Nicolin Chen
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +9
-0+/* MMIO helpers */ +#define cmdqv_readl (reg) \ + readl(cmdqv->base + TEGRA241_CMDQV_##reg) +#define cmdqv_readl_relaxed (reg) \ + readl_relaxed(cmdqv->base + TEGRA241_CMDQV_##reg) +#define cmdqv_writel (val, reg) \ + writel((val), cmdqv->base + TEGRA241_CMDQV_##reg) +#define cmdqv_writel_relaxed (val, reg) \ + writel_relaxed((val), cmdqv->base + TEGRA241_CMDQV_##reg)Please don't hide access to a stack variable in a macro, and I'm not keen on the ##reg scheme either - it makes it much harder to search for things.
I can pass in cmdqv/vintf/vcmdq pointers, if it would be better.
Quoted 8 lines
Quoted 4 lines
Really this all seems like alot of overkill to make a little bit of shorthand. It is not so wordy just to type it out:
readl(vintf->base + TEGRA241_VINTF_CONFIG)
vintf_readl(vintf, CONFIG)is much shorter. Doing so reduced the line breaks at quite a lot places, so overall the driver looks a lot cleaner to me.
We don't have the strict 80 column limit now, it would be fine to go a few extra to avoid the breaks.
Certainly preferred to these readability damaging macros.
Quoted 1 line
I can probably change these logging helpers to inline functions.
Just call the normal logging functions directly.
Quoted 23 lines
Quoted 14 lines
Quoted 14 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +14
-0+#define vintf_warn (fmt, ...) \ + dev_warn(vintf->cmdqv->dev, "VINTF%u: " fmt, vintf->idx, ##__VA_ARGS__) +#define vintf_err (fmt, ...) \ + dev_err(vintf->cmdqv->dev, "VINTF%u: " fmt, vintf->idx, ##__VA_ARGS__) +#define vintf_info (fmt, ...) \ + dev_info(vintf->cmdqv->dev, "VINTF%u: " fmt, vintf->idx, ##__VA_ARGS__) +#define vintf_dbg (fmt, ...) \ + dev_dbg(vintf->cmdqv->dev, "VINTF%u: " fmt, vintf->idx, ##__VA_ARGS__) + +#define vcmdq_warn (fmt, ...) \ + ({ \ + struct tegra241_vintf *vintf = vcmdq->vintf; \ + if (vintf) \ + vintf_warn("VCMDQ%u/LVCMDQ%u: " fmt, \Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 167
Quoted 6 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +6
-0+ vcmdq->idx, vcmdq->lidx, \ + ##__VA_ARGS__); \ + else \ + dev_warn(vcmdq->cmdqv->dev, "VCMDQ%u: " fmt, \ + vcmdq->idx, ##__VA_ARGS__); \ + })Some of these are barely used, is it worth all these macros??
Quoted 2 lines
Only
vcmdq_warnisn't called. But I think it would be useful. I could also find a place to call it, if that's a must.
Just call the normal logging functions, there are so few callers typing out the VCMDQ%u is not going to be so bad
Quoted 23 lines
Quoted 14 lines
Quoted 14 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +14
-0+ +/* Configuring and polling helpers */ +#define tegra241_cmdqv_write_config (_owner, _OWNER, _regval) \ + ({ \ + bool _en = (_regval) & _OWNER##_EN; \ + u32 _status; \ + int _ret; \ + writel((_regval), _owner->base + TEGRA241_##_OWNER##_CONFIG); \ + _ret = readl_poll_timeout( \ + _owner->base + TEGRA241_##_OWNER##_STATUS, _status, \ + _en ? (_regval) & _OWNER##_ENABLED : \ + !((_regval) & _OWNER##_ENABLED), \ + 1, ARM_SMMU_POLL_TIMEOUT_US); \ + if (_ret) \Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 219
Quoted 5 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +5
-0+ _owner##_err("failed to %sable, STATUS = 0x%08X\n", \ + _en ? "en" : "dis", _status); \ +atomic_set (&_owner->status, _status); \ + _ret; \ + })I feel like this could be an actual inline function without the macro wrapper with a little fiddling.
Quoted 4 lines
It would be unrolled to three mostly identical inline functions:
tegra241_cmdqv_write_config(cmdqv, regval)tegra241_vintf_write_config(vintf, regval)tegra241_vcmdq_write_config(vcmdq, regval)
Expand the parameters in the caller:
__do_write_config(owner->base, &owner->status, _CMDQV_EN, TEGRA241_CMDQ_CONFIG,
TEGRA241_CMDQ_STATUS, _CMDQ_ENABLED)
Quoted 9 lines
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 226
Quoted 6 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +6
-0+#define cmdqv_write_config (_regval) \ + tegra241_cmdqv_write_config(cmdqv, CMDQV, _regval) +#define vintf_write_config (_regval) \ + tegra241_cmdqv_write_config(vintf, VINTF, _regval) +#define vcmdq_write_config (_regval) \ + tegra241_cmdqv_write_config(vcmdq, VCMDQ, _regval)More hidden access to stack values
Quoted 3 lines
Btw, any reason for forbidding this practice? It will break the build if something goes wrong, which seems to be pretty easy to catch.
It is the kernel consensus not to do that. function-like-macros should act like functions and not reach into some other stack frame. It makes it very hard to follow the calling function if you can't follow where the references are.
Quoted 11 lines
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 391
Quoted 7 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +7
-0+/* Use SMMU CMDQ if vintfs[0] is uninitialized */ +if (!FIELD_GET (VINTF_ENABLED ,atomic_read (&vintf->status))) +return &smmu->cmdq; + +/* Use SMMU CMDQ if vintfs[0] has error status */ +if (FIELD_GET (VINTF_STATUS ,atomic_read (&vintf->status))) +return &smmu->cmdq;Why
atomic_read? The unlocked interaction withtegra241_cmdqv_handle_vintf0_error()doesn't seem especially sane IMHO
Quoted 1 line
Race between this
get_cmdq()and the isr. Any alternative practice?
It doesn't fix any real race, I'm not sure what this is supposed to be doing. The cmdq becomes broken and you get an ISR, so before the ISR it will still post but get stuck, during the ISR it will avoid posting, and after it will go back to posting?
Why? Just always post to the Q and let the ISR fix it?
Quoted 12 lines
Quoted 11 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 414
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +8
-0+static void tegra241_vcmdq_hw_deinit (struct tegra241_vcmdq *vcmdq) +{ +u32 gerrorn, gerror; + +if (vcmdq_write_config (0 )) { +vcmdq_err ("GERRORN=0x%X\n" ,vcmdq_page0_readl_relaxed (GERRORN )); +vcmdq_err ("GERROR=0x%X\n" ,vcmdq_page0_readl_relaxed (GERROR )); +vcmdq_err ("CONS=0x%X\n" ,vcmdq_page0_readl_relaxed (CONS ));Less prints, include a unique message about why this is being printed..
Quoted 4 lines
Something must be wrong if disabling VCMDQ fails, so the prints of error register values would be helpful. And "failed to disable" is already printed by the
vcmdq_write_config()call. I can merge them into onevcmdq_errcall though.
Print on one line
Quoted 11 lines
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 489
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +8
-0+static void tegra241_vcmdq_free_smmu_cmdq (struct tegra241_vcmdq *vcmdq) +{ +struct tegra241_cmdqv *cmdqv = vcmdq->cmdqv; +struct arm_smmu_queue *q = &vcmdq->cmdq.q; +size_t nents =1 << q->llq.max_n_shift; + +dmam_free_coherent (cmdqv->smmu->dev, (nents *CMDQ_ENT_DWORDS ) <<3 , + q->base, q->base_dma);If we are calling
dmam_free, do we really need devm at all?
Quoted 1 line
Hmm. This is a part of SMMU's probe/device_reset().
But that is a proper device driver, this isn't.
Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +8
-0+struct tegra241_cmdqv *cmdqv; + + cmdqv =tegra241_cmdqv_find_resource (smmu, id); +if (!cmdqv) +return NULL ; + +if (tegra241_cmdqv_probe (cmdqv)) { +if (cmdqv->irq >0 )Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 811
Quoted 4 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +4
-0+devm_free_irq (smmu->dev, cmdqv->irq, cmdqv); +devm_iounmap (smmu->dev, cmdqv->base); +devm_kfree (smmu->dev, cmdqv); +return NULL ;Oh. Please don't use devm at all in this code then, it is not attached to a probed driver with the proper scope, devm isn't going to work in sensible way.
Quoted 2 lines
Mind elaborating "it is not"? This function is called by
arm_smmu_device_acpi_probeandarm_smmu_device_probe.
Normal devm usage will unwind the devm allocations when probe fails.
That doesn't happen here, you open coded the unwind above, and then you have open coded freeing in another place anyhow.
So just don't use it. There is no value if the places where it should work automatically are not functioning.
Jason
Re: [PATCH v6 5/6] iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV
Quoted 8 lines · Jason Gunthorpe
Quoted 7 lines · Nicolin Chen
Quoted 5 lines · Jason Gunthorpe
On Mon, Apr 29, 2024 at 09:43:48PM -0700, Nicolin Chen wrote:
Really this all seems like alot of overkill to make a little bit of shorthand. It is not so wordy just to type it out:
readl(vintf->base + TEGRA241_VINTF_CONFIG)
Quoted 8 lines
Quoted 3 lines
vintf_readl(vintf, CONFIG)is much shorter. Doing so reduced the line breaks at quite a lot places, so overall the driver looks a lot cleaner to me.We don't have the strict 80 column limit now, it would be fine to go a few extra to avoid the breaks.
Certainly preferred to these readability damaging macros.
I think only very few folks will look up for the register macros that are actually damaged, and these MMIO macros still look very straightforward that we will always know the correct ->base and register offset pair will be used.
There are four sets of ->base in the driver. Sometimes they can be so dazzling that a wrong pair might be accidentally used. Yet such a stupid mistake can be really difficult to catch it out -- I had a hard time earlier to sort it out..
Quoted 3 lines
Quoted 1 line
I can probably change these logging helpers to inline functions.
Just call the normal logging functions directly.
..
Quoted 2 lines
Just call the normal logging functions, there are so few callers typing out the VCMDQ%u is not going to be so bad
There're a few more in the part 2. And it's actually a bit wordy doing "VINTF%u: LVCDMQ%u/VCMDQ%u" at every place..
Quoted 9 lines
Quoted 4 lines
It would be unrolled to three mostly identical inline functions:
tegra241_cmdqv_write_config(cmdqv, regval)tegra241_vintf_write_config(vintf, regval)tegra241_vcmdq_write_config(vcmdq, regval)Expand the parameters in the caller:
__do_write_config(owner->base,
&owner->status,_CMDQV_EN,TEGRA241_CMDQ_CONFIG,
TEGRA241_CMDQ_STATUS,_CMDQ_ENABLED)
Yea, I just did something like that. _CMDQV_EN and _CMDQ_ENABLED always equal to BIT(0), so I also move them to the common place.
Quoted 9 lines
Quoted 9 lines
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 226
Quoted 6 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +6
-0+#define cmdqv_write_config (_regval) \ + tegra241_cmdqv_write_config(cmdqv, CMDQV, _regval) +#define vintf_write_config (_regval) \ + tegra241_cmdqv_write_config(vintf, VINTF, _regval) +#define vcmdq_write_config (_regval) \ + tegra241_cmdqv_write_config(vcmdq, VCMDQ, _regval)More hidden access to stack values
Quoted 8 lines
Quoted 3 lines
Btw, any reason for forbidding this practice? It will break the build if something goes wrong, which seems to be pretty easy to catch.
It is the kernel consensus not to do that. function-like-macros should act like functions and not reach into some other stack frame. It makes it very hard to follow the calling function if you can't follow where the references are.
I see.
Quoted 11 lines
Quoted 11 lines
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 391
Quoted 7 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +7
-0+/* Use SMMU CMDQ if vintfs[0] is uninitialized */ +if (!FIELD_GET (VINTF_ENABLED ,atomic_read (&vintf->status))) +return &smmu->cmdq; + +/* Use SMMU CMDQ if vintfs[0] has error status */ +if (FIELD_GET (VINTF_STATUS ,atomic_read (&vintf->status))) +return &smmu->cmdq;Why
atomic_read? The unlocked interaction withtegra241_cmdqv_handle_vintf0_error()doesn't seem especially sane IMHO
Quoted 8 lines
Quoted 1 line
Race between this
get_cmdq()and the isr. Any alternative practice?It doesn't fix any real race, I'm not sure what this is supposed to be doing. The cmdq becomes broken and you get an ISR, so before the ISR it will still post but get stuck, during the ISR it will avoid posting, and after it will go back to posting?
Why? Just always post to the Q and let the ISR fix it?
Yes, we could do so. I was thinking of the worst case by giving the guest OS a chance to continue (though in a slower mode), if something unrecoverable happens to the VINTF/VCMDQ part.
Quoted 14 lines
Quoted 11 lines
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c line 811
Quoted 6 lines
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c new file +6
-0+if (tegra241_cmdqv_probe (cmdqv)) { +if (cmdqv->irq >0 ) +devm_free_irq (smmu->dev, cmdqv->irq, cmdqv); +devm_iounmap (smmu->dev, cmdqv->base); +devm_kfree (smmu->dev, cmdqv); +return NULL ;Oh. Please don't use devm at all in this code then, it is not attached to a probed driver with the proper scope, devm isn't going to work in sensible way.
Quoted 2 lines
Mind elaborating "it is not"? This function is called by
arm_smmu_device_acpi_probeandarm_smmu_device_probe.
Quoted 7 lines
Normal devm usage will unwind the devm allocations when probe fails.
That doesn't happen here, you open coded the unwind above, and then you have open coded freeing in another place anyhow.
So just don't use it. There is no value if the places where it should work automatically are not functioning.
I thought devm could work when rmmod too, not only when the probe fails..
In that case, should we be concerned about the "rmmod arm-smmu-v3"? There is no callback function for that yet, so memory on the cmdqv side wouldn't be free-ed.
Thanks
Nicolin
Re: [PATCH v6 5/6] iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV
Quoted 10 lines · Nicolin Chen
Quoted 6 lines
It doesn't fix any real race, I'm not sure what this is supposed to be doing. The cmdq becomes broken and you get an ISR, so before the ISR it will still post but get stuck, during the ISR it will avoid posting, and after it will go back to posting?
Why? Just always post to the Q and let the ISR fix it?
Yes, we could do so. I was thinking of the worst case by giving the guest OS a chance to continue (though in a slower mode), if something unrecoverable happens to the VINTF/VCMDQ part.
Does that happn? The stuck vcmdq will have stuck entries on it no matter what, can we actually fully recover from that? Ie re-issue the commands on another queue?
Quoted 5 lines
Quoted 2 lines
So just don't use it. There is no value if the places where it should work automatically are not functioning.
I thought devm could work when rmmod too, not only when the probe fails..
It is limited to cases when the probing driver of the passed struct device unbinds, including probe failure.
Jason
Re: [PATCH v6 5/6] iommu/arm-smmu-v3: Add in-kernel support for NVIDIA Tegra241 (Grace) CMDQV
Quoted 8 lines · Jason Gunthorpe
Quoted 7 lines · Nicolin Chen
Quoted 6 lines
It doesn't fix any real race, I'm not sure what this is supposed to be doing. The cmdq becomes broken and you get an ISR, so before the ISR it will still post but get stuck, during the ISR it will avoid posting, and after it will go back to posting?
Why? Just always post to the Q and let the ISR fix it?
Quoted 7 lines
Quoted 3 lines
Yes, we could do so. I was thinking of the worst case by giving the guest OS a chance to continue (though in a slower mode), if something unrecoverable happens to the VINTF/VCMDQ part.
Does that happn? The stuck vcmdq will have stuck entries on it no matter what, can we actually fully recover from that? Ie re-issue the commands on another queue?
Well, the handle_vintf0_error() should fix that and recover. And rethinking about this, if this happens it's likely a SW bug that we shouldn't ignore.
With that being said, the viommu infrastructure still needs an irq forwarding that is currently missing. I'd need to draft something likely on top of Baolu's work.
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
So just don't use it. There is no value if the places where it should work automatically are not functioning.
I thought devm could work when rmmod too, not only when the probe fails..
It is limited to cases when the probing driver of the passed struct device unbinds, including probe failure.
OK. I'll drop all devm_ and add tegra241_cmdqv_device_remove() instead.
Thanks
Nicolin
PATCH v6 6/6 iommu/tegra241-cmdqv: Limit CMDs for guest owned VINTF
In reply to Nicolin ChenWhen VCMDQs are assigned to a VINTF owned by a guest (HYP_OWN bit unset), only TLB and ATC invalidation commands are supported by the VCMDQ HW. So, add a new helper to scan the input cmds to make sure every single command is supported when selecting a queue.
Note that the guest VM shouldn't have HYP_OWN bit being set regardless of guest kernel driver writing it or not, i.e. the hypervisor running in the host OS should wire this bit to zero when trapping a write access to this VINTF_CONFIG register from a guest kernel.
--- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 7 +-- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 5 ++- .../iommu/arm/arm-smmu-v3/tegra241-cmdqv.c | 44 ++++++++++++++++++- 3 files changed, 50 insertions(+), 6 deletions(-)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +4 -3
@@ -352,10 +352,11 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)return 0 ; } -static struct arm_smmu_cmdq *arm_smmu_get_cmdq (struct arm_smmu_device *smmu) +static struct arm_smmu_cmdq *arm_smmu_get_cmdq (struct arm_smmu_device *smmu, +u64 *cmds,int n) {if (smmu->tegra241_cmdqv) -return tegra241_cmdqv_get_cmdq (smmu); +return tegra241_cmdqv_get_cmdq (smmu, cmds, n);return &smmu->cmdq; }
@@ -766,7 +767,7 @@ static int arm_smmu_cmdq_issue_cmdlist(struct arm_smmu_device *smmu,u32 prod;unsigned long flags;bool owner; -struct arm_smmu_cmdq *cmdq =arm_smmu_get_cmdq (smmu); +struct arm_smmu_cmdq *cmdq =arm_smmu_get_cmdq (smmu, cmds, n);struct arm_smmu_ll_queue llq, head;int ret =0 ;
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +3 -2
@@ -837,7 +837,8 @@ static inline void arm_smmu_sva_remove_dev_pasid(struct iommu_domain *domain,struct tegra241_cmdqv *tegra241_cmdqv_acpi_probe (struct arm_smmu_device *smmu,int id);int tegra241_cmdqv_device_reset (struct arm_smmu_device *smmu); -struct arm_smmu_cmdq *tegra241_cmdqv_get_cmdq (struct arm_smmu_device *smmu); +struct arm_smmu_cmdq *tegra241_cmdqv_get_cmdq (struct arm_smmu_device *smmu, +u64 *cmds,int n);#else /* CONFIG_TEGRA241_CMDQV */ static inline struct tegra241_cmdqv *tegra241_cmdqv_acpi_probe (struct arm_smmu_device *smmu,int id)
@@ -851,7 +852,7 @@ static inline int tegra241_cmdqv_device_reset(struct arm_smmu_device *smmu) }static inline struct arm_smmu_cmdq * -tegra241_cmdqv_get_cmdq (struct arm_smmu_device *smmu) +tegra241_cmdqv_get_cmdq (struct arm_smmu_device *smmu,u64 *cmds,int n) {return NULL ; }
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +43 -1
80 lines not on this page · raw message
Signature · 1 line
2.43.0
Re: [PATCH v6 6/6] iommu/tegra241-cmdqv: Limit CMDs for guest owned VINTF
Quoted 6 lines · Nicolin Chen
(quoted diff) +5
-1-struct arm_smmu_cmdq *tegra241_cmdqv_get_cmdq(struct arm_smmu_device *smmu) +static bool tegra241_vintf_support_cmds(struct tegra241_vintf *vintf, + u64 *cmds, int n) +{ + int i; +
Review
Quoted 10 lines
(quoted diff) +10
-0+ /* VINTF owned by hypervisor can execute any command */ + if (vintf->hyp_own) + return true; + + /* Guest-owned VINTF must Check against the list of supported CMDs */ + for (i = 0; i < n; i++) { + switch (FIELD_GET(CMDQ_0_OP, cmds[i * CMDQ_ENT_DWORDS])) { + case CMDQ_OP_TLBI_NH_ASID: + case CMDQ_OP_TLBI_NH_VA: + case CMDQ_OP_ATC_INV:
So CMDQ only works if not ARM_SMMU_FEAT_E2H? Probably worth mentioning that too along with the discussion about HYP
Review
Quoted 8 lines
(quoted diff) +8
-0+ continue; + default: + return false; + } + } + + return true; +}
For a performance path this looping seems disappointing.. The callers don't actually mix different command type. Is there something preventing adding a parameter at the callers?
Actually looking at this more closely, isn't the command q selection in the wrong place?
Ie this batch stuff:
static void arm_smmu_cmdq_batch_add(struct arm_smmu_device *smmu,
struct arm_smmu_cmdq_batch *cmds, struct arm_smmu_cmdq_ent *cmd)
{
int index;
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;
}
if (cmds->num == CMDQ_BATCH_ENTRIES) {
arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, false);
cmds->num = 0;
}
index = cmds->num * CMDQ_ENT_DWORDS;
if (unlikely(arm_smmu_cmdq_build_cmd(&cmds->cmds[index], cmd))) {
dev_warn(smmu->dev, "ignoring unknown CMDQ opcode 0x%x\n",
cmd->opcode);
return;
}Has to push everything, across all the iterations of add/submut, onto the same CMDQ otherwise the SYNC won't be properly flushing?
But each arm_smmu_cmdq_issue_cmdlist() calls its own get q function. Yes, they probably return the same Q since we are probably on the same CPU, but it seems logically wrong (and slower!) to organize it like this.
I would expect the Q to be selected when the struct arm_smmu_cmdq_batch is allocated on the stack, and be the same for the entire batch operation. Not only do we spend less time trying to compute the Q to use we have a built in guarentee that every command will be on the same Q as the fenching SYNC.
Something sort of like this as another patch?
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +36 -13
@@ -357,11 +357,22 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)return 0 ; } -static struct arm_smmu_cmdq *arm_smmu_get_cmdq (struct arm_smmu_device *smmu, -u64 *cmds,int n) +enum required_cmds { +CMDS_ALL , +/* +* Commands will be one of: +* CMDQ_OP_ATC_INV, CMDQ_OP_TLBI_EL2_VA, CMDQ_OP_TLBI_NH_VA, +* CMDQ_OP_TLBI_EL2_ASID, CMDQ_OP_TLBI_NH_ASID, CMDQ_OP_TLBI_S2_IPA, +* CMDQ_OP_TLBI_S12_VMALL, CMDQ_OP_SYNC +*/ +CMDS_INVALIDATION , +}; + +static struct arm_smmu_cmdq * +arm_smmu_get_cmdq (struct arm_smmu_device *smmu,enum required_cmds required) {if (smmu->tegra241_cmdqv) -return tegra241_cmdqv_get_cmdq (smmu, cmds, n); +return tegra241_cmdqv_get_cmdq (smmu, required);return &smmu->cmdq; }
@@ -766,13 +777,13 @@ static void arm_smmu_cmdq_write_entries(struct arm_smmu_cmdq *cmdq, u64 *cmds,* CPU will appear before any of the commands from the other CPU. */ static int arm_smmu_cmdq_issue_cmdlist (struct arm_smmu_device *smmu, -u64 *cmds,int n,bool sync) +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, cmds, n);struct arm_smmu_ll_queue llq, head;int ret =0 ;
@@ -897,7 +908,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,CMDS_ALL ), cmd,1 , sync); } static int arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
@@ -912,6 +924,14 @@ 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, +enum required_cmds required) +{ + cmds->num =0 ; + cmds->q =arm_smmu_get_cmdq (smmu, required); +} +static void arm_smmu_cmdq_batch_add (struct arm_smmu_device *smmu,struct arm_smmu_cmdq_batch *cmds,struct arm_smmu_cmdq_ent *cmd)
@@ -920,12 +940,14 @@ 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); +arm_smmu_cmdq_issue_cmdlist (smmu, cmds->q, cmds->cmds, + cmds->num, true); cmds->num =0 ; }if (cmds->num ==CMDQ_BATCH_ENTRIES ) { -arm_smmu_cmdq_issue_cmdlist (smmu, cmds->cmds, cmds->num, false); +arm_smmu_cmdq_issue_cmdlist (smmu, cmds->q, cmds->cmds, + cmds->num, false); cmds->num =0 ; }
@@ -942,7 +964,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->q, cmds->cmds, cmds->num, + true); } static void arm_smmu_page_response(struct device *dev, struct iopf_fault *unused,
@@ -1181,7 +1204,7 @@ static void arm_smmu_sync_cd(struct arm_smmu_master *master, }, }; - cmds.num =0 ; +arm_smmu_cmdq_batch_init (smmu, &cmds,CMDS_ALL );for (i =0 ; i < master->num_streams; i++) { cmd.cfgi.sid = master->streams[i].id;arm_smmu_cmdq_batch_add (smmu, &cmds, &cmd);
@@ -2045,7 +2068,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,CMDS_INVALIDATION );for (i =0 ; i < master->num_streams; i++) { cmd.atc.sid = master->streams[i].id;arm_smmu_cmdq_batch_add (master->smmu, &cmds, &cmd);
@@ -2083,7 +2106,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,CMDS_INVALIDATION );spin_lock_irqsave (&smmu_domain->devices_lock, flags); list_for_each_entry(master_domain, &smmu_domain->devices,
@@ -2161,7 +2184,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_domain->smmu, &cmds,CMDS_INVALIDATION );while (iova < end) {if (smmu->features &ARM_SMMU_FEAT_RANGE_INV ) {
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +1 -0
@@ -576,6 +576,7 @@ struct arm_smmu_cmdq {struct arm_smmu_cmdq_batch {u64 cmds[CMDQ_BATCH_ENTRIES *CMDQ_ENT_DWORDS ]; +struct arm_smmu_cmdq *q;int num; };
Re: [PATCH v6 6/6] iommu/tegra241-cmdqv: Limit CMDs for guest owned VINTF
Quoted 12 lines · Jason Gunthorpe
Quoted 11 lines · Nicolin Chen
(quoted diff) +10
-1-struct arm_smmu_cmdq *tegra241_cmdqv_get_cmdq(struct arm_smmu_device *smmu) +static bool tegra241_vintf_support_cmds(struct tegra241_vintf *vintf, + u64 *cmds, int n) +{ + int i; + + /* VINTF owned by hypervisor can execute any command */ + if (vintf->hyp_own) + return true; + + /* Guest-owned VINTF must Check against the list of supported CMDs */
Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ for (i = 0; i < n; i++) { + switch (FIELD_GET(CMDQ_0_OP, cmds[i * CMDQ_ENT_DWORDS])) { + case CMDQ_OP_TLBI_NH_ASID: + case CMDQ_OP_TLBI_NH_VA: + case CMDQ_OP_ATC_INV:So CMDQ only works if not
ARM_SMMU_FEAT_E2H? Probably worth mentioning that too along with the discussion about HYP
Nod. EL2/EL3 commands aren't supported. And they aren't supposed to be issued by a guess either, since ARM64_HAS_VIRT_HOST_EXTN is the feature of "Virtualization Host Extensions"?
Quoted 13 lines
Review
Quoted 9 lines
(quoted diff) +8
-0+ continue; + default: + return false; + } + } + + return true; +}For a performance path this looping seems disappointing.. The callers don't actually mix different command type. Is there something preventing adding a parameter at the callers?
The callers don't seem to mix at this moment. Yet we would have to be extra careful against any future SMMU patch that may mix commands?
Quoted 29 lines
Actually looking at this more closely, isn't the command q selection in the wrong place?
Ie this batch stuff:
static void arm_smmu_cmdq_batch_add(struct
arm_smmu_device*smmu,
structarm_smmu_cmdq_batch*cmds, structarm_smmu_cmdq_ent*cmd){ int index; 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; } if (cmds->num == CMDQ_BATCH_ENTRIES) { arm_smmu_cmdq_issue_cmdlist(smmu, cmds->cmds, cmds->num, false); cmds->num = 0; } index = cmds->num * CMDQ_ENT_DWORDS; if (unlikely(arm_smmu_cmdq_build_cmd(&cmds->cmds[index], cmd))) { dev_warn(smmu->dev, "ignoring unknown CMDQ opcode 0x%x\n", cmd->opcode); return; }
Quoted 2 lines
Has to push everything, across all the iterations of add/submut, onto the same CMDQ otherwise the SYNC won't be properly flushing?
ECMDQ seems to have such a limitation, but VCMDQs can get away as HW can insert a SYNC to a queue that doesn't end with a SYNC.
Quoted 10 lines
But each
arm_smmu_cmdq_issue_cmdlist()calls its own get q function. Yes, they probably return the same Q since we are probably on the same CPU, but it seems logically wrong (and slower!) to organize it like this.I would expect the Q to be selected when the struct
arm_smmu_cmdq_batchis allocated on the stack, and be the same for the entire batch operation. Not only do we spend less time trying to compute the Q to use we have a built in guarentee that every command will be on the same Q as the fenching SYNC.
This seems to be helpful to ECMDQ. The current version disables the preempts, which feels costly to me.
Quoted 12 lines
Something sort of like this as another patch?
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +0
-2@@ -357,11 +357,22 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)return 0 ; } -static struct arm_smmu_cmdq *arm_smmu_get_cmdq (struct arm_smmu_device *smmu, -u64 *cmds,int n)
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent) · line 369
Quoted 10 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +10
-0+enum required_cmds { +CMDS_ALL , +/* +* Commands will be one of: +* CMDQ_OP_ATC_INV, CMDQ_OP_TLBI_EL2_VA, CMDQ_OP_TLBI_NH_VA, +* CMDQ_OP_TLBI_EL2_ASID, CMDQ_OP_TLBI_NH_ASID, CMDQ_OP_TLBI_S2_IPA, +* CMDQ_OP_TLBI_S12_VMALL, CMDQ_OP_SYNC +*/ +CMDS_INVALIDATION , +};
Hmm, guest-owned VCMDQs don't support EL2 commands. So, it feels to be somehow complicated to decouple them further in the callers of arm_smmu_cmdq_batch_add(). And I am not sure if there is a use case of guest issuing CMDQ_OP_TLBI_S2_IPA/CMDQ_OP_TLBI_S12_VMALL either, HW surprisingly supports these two though.
Perhaps we could just scan the first command in the batch, giving a faith that no one will covertly sneak different commands in it?
Otherwise, there has to be a get_suported_cmdq callback so batch or its callers can avoid adding unsupported commands at the first place.
Thanks
Nicolin
Re: [PATCH v6 6/6] iommu/tegra241-cmdqv: Limit CMDs for guest owned VINTF
Quoted 5 lines · Nicolin Chen
Quoted 2 lines
Has to push everything, across all the iterations of add/submut, onto the same CMDQ otherwise the SYNC won't be properly flushing?
ECMDQ seems to have such a limitation, but VCMDQs can get away as HW can insert a SYNC to a queue that doesn't end with a SYNC.
That seems like a strange thing to do in HW, but I recall you mentioned it once before. Still, I'm not sure there is any merit in relying on it?
Quoted 5 lines
Quoted 5 lines
But each
arm_smmu_cmdq_issue_cmdlist()calls its own get q function. Yes, they probably return the same Q since we are probably on the same CPU, but it seems logically wrong (and slower!) to organize it like this.
Quoted 8 lines
Quoted 5 lines
I would expect the Q to be selected when the struct
arm_smmu_cmdq_batchis allocated on the stack, and be the same for the entire batch operation. Not only do we spend less time trying to compute the Q to use we have a built in guarentee that every command will be on the same Q as the fenching SYNC.This seems to be helpful to ECMDQ. The current version disables the preempts, which feels costly to me.
Oh, yes, definately should work like this then!
Quoted 20 lines
Quoted 20 lines
Something sort of like this as another patch?
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +8
-2@@ -357,11 +357,22 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)return 0 ; } -static struct arm_smmu_cmdq *arm_smmu_get_cmdq (struct arm_smmu_device *smmu, -u64 *cmds,int n) +enum required_cmds { +CMDS_ALL , +/* +* Commands will be one of: +* CMDQ_OP_ATC_INV, CMDQ_OP_TLBI_EL2_VA, CMDQ_OP_TLBI_NH_VA, +* CMDQ_OP_TLBI_EL2_ASID, CMDQ_OP_TLBI_NH_ASID, CMDQ_OP_TLBI_S2_IPA, +* CMDQ_OP_TLBI_S12_VMALL, CMDQ_OP_SYNC +*/
Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent) · line 369
Quoted 2 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2
-0+ CMDS_INVALIDATION, +};Hmm, guest-owned VCMDQs don't support EL2 commands. So, it feels to be somehow complicated to decouple them further in the callers of
arm_smmu_cmdq_batch_add(). And I am not sure if there is a use case of guest issuing CMDQ_OP_TLBI_S2_IPA/CMDQ_OP_TLBI_S12_VMALL either, HW surprisingly supports these two though.
These are the max commands that could be issued, but they are all gated based on the feature bits. The ones VCMDQ don't support are not going to be issued because of the feature bits. You could test and enforce this when probing the ECMDQ parts.
Quoted 2 lines
Perhaps we could just scan the first command in the batch, giving a faith that no one will covertly sneak different commands in it?
Yes with the current design that does seem to work, but it also feels a bit obtuse.
Quoted 3 lines
Otherwise, there has to be a
get_suported_cmdqcallback so batch or its callers can avoid adding unsupported commands at the first place.
If you really feel strongly the invalidation could be split into S1/S2/S1_VM groupings that align with the feature bits and that could be passed down from one step above. But I don't think the complexity is really needed. It is better to deal with it through the feature mechanism.
If high speed invalidation is supported then the invalidation queue must support all the invalidation commands used by the SMMU's active feature set, otherwise do not enable the invalidation queue. It does make logical sense.
Jason
Re: [PATCH v6 6/6] iommu/tegra241-cmdqv: Limit CMDs for guest owned VINTF
Quoted 11 lines · Jason Gunthorpe
Quoted 5 lines · Nicolin Chen
Quoted 2 lines
Has to push everything, across all the iterations of add/submut, onto the same CMDQ otherwise the SYNC won't be properly flushing?
ECMDQ seems to have such a limitation, but VCMDQs can get away as HW can insert a SYNC to a queue that doesn't end with a SYNC.
That seems like a strange thing to do in HW, but I recall you mentioned it once before. Still, I'm not sure there is any merit in relying on it?
I was hoping to get some idea from the designer. Yet, at this moment, let's say there's likely no merit besides SW can care less and stay simpler, AFAIK.
Robin previously remarked that there could be some performance impact from such a feature, so I think adding your patch would be nicer.
Quoted 29 lines
Quoted 20 lines
Quoted 20 lines
Something sort of like this as another patch?
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +8
-2@@ -357,11 +357,22 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)return 0 ; } -static struct arm_smmu_cmdq *arm_smmu_get_cmdq (struct arm_smmu_device *smmu, -u64 *cmds,int n) +enum required_cmds { +CMDS_ALL , +/* +* Commands will be one of: +* CMDQ_OP_ATC_INV, CMDQ_OP_TLBI_EL2_VA, CMDQ_OP_TLBI_NH_VA, +* CMDQ_OP_TLBI_EL2_ASID, CMDQ_OP_TLBI_NH_ASID, CMDQ_OP_TLBI_S2_IPA, +* CMDQ_OP_TLBI_S12_VMALL, CMDQ_OP_SYNC +*/ Quoted 8 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent) · line 369
Quoted 2 lines
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +2
-0+ CMDS_INVALIDATION, +};Hmm, guest-owned VCMDQs don't support EL2 commands. So, it feels to be somehow complicated to decouple them further in the callers of
arm_smmu_cmdq_batch_add(). And I am not sure if there is a use case of guest issuing CMDQ_OP_TLBI_S2_IPA/CMDQ_OP_TLBI_S12_VMALL either, HW surprisingly supports these two though.
Quoted 4 lines
These are the max commands that could be issued, but they are all gated based on the feature bits. The ones VCMDQ don't support are not going to be issued because of the feature bits. You could test and enforce this when probing the ECMDQ parts.
Ah, I see. So cmdqv's probe() could check the smmu->features against ARM_SMMU_FEAT_E2H, and disable VINTF0 right away, in case of guest-owned while E2H is present.
And we could do the same for ARM_SMMU_FEAT_TRANS_S2, stating that the driver does not expect use cases of issuing S2 TLBI commands from the guest OS.
Thanks
Nicolin
Re: [PATCH v6 6/6] iommu/tegra241-cmdqv: Limit CMDs for guest owned VINTF
In reply to Jason GunthorpeQuoted 10 lines · Jason Gunthorpe
Quoted 3 lines · Nicolin Chen
Otherwise, there has to be a
get_suported_cmdqcallback so batch or its callers can avoid adding unsupported commands at the first place.If you really feel strongly the invalidation could be split into S1/S2/S1_VM groupings that align with the feature bits and that could be passed down from one step above. But I don't think the complexity is really needed. It is better to deal with it through the feature mechanism.
Hmm, I tried following your design by passing in a CMD_TYPE_xxx to the tegra241_cmdqv_get_cmdq(), but I found a little painful to accommodate these two cases:
1. TLBI_NH_ASID is issued via arm_smmu_cmdq_issue_cmdlist(), so we should not mark it as CMD_TYPE_ALL. Yet, this function is used by other commands too. So, either we pass in a type from higher callers, or simply check the opcode in that function.
2. It is a bit tricky to define, from SMMU's P.O.V, a good TYPE subset for VCMDQ, since guest-owned VCMDQ does not support TLBI_NSNH_ALL.
So, it feels to me that checking against the opcode is still a straightforward solution. And what I ended up with is somewhat similar to this v6, yet this time it only checks at batch init call as your design does.
How do you think of this?
Thanks
Nicolin
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +11 -9
@@ -332,10 +332,11 @@ static int arm_smmu_cmdq_build_cmd(u64 *cmd, struct arm_smmu_cmdq_ent *ent)return 0 ; } -static struct arm_smmu_cmdq *arm_smmu_get_cmdq (struct arm_smmu_device *smmu) +static struct arm_smmu_cmdq * +arm_smmu_get_cmdq (struct arm_smmu_device *smmu,u8 opcode) {if (arm_smmu_has_tegra241_cmdqv (smmu)) -return tegra241_cmdqv_get_cmdq (smmu); +return tegra241_cmdqv_get_cmdq (smmu, opcode);return &smmu->cmdq; }
@@ -871,7 +872,7 @@ static int __arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu, }return arm_smmu_cmdq_issue_cmdlist ( - smmu,arm_smmu_get_cmdq (smmu), cmd,1 , sync); + smmu,arm_smmu_get_cmdq (smmu, ent->opcode), cmd,1 , sync); } static int arm_smmu_cmdq_issue_cmd(struct arm_smmu_device *smmu,
@@ -887,10 +888,11 @@ static int arm_smmu_cmdq_issue_cmd_with_sync(struct arm_smmu_device *smmu, }static void arm_smmu_cmdq_batch_init (struct arm_smmu_device *smmu, -struct arm_smmu_cmdq_batch *cmds) +struct arm_smmu_cmdq_batch *cmds, +u8 opcode) { cmds->num =0 ; - cmds->cmdq =arm_smmu_get_cmdq (smmu); + cmds->cmdq =arm_smmu_get_cmdq (smmu, opcode); } static void arm_smmu_cmdq_batch_add(struct arm_smmu_device *smmu,
@@ -1167,7 +1169,7 @@ static void arm_smmu_sync_cd(struct arm_smmu_master *master, }, }; -arm_smmu_cmdq_batch_init (smmu, &cmds); +arm_smmu_cmdq_batch_init (smmu, &cmds, cmd.opcode);for (i =0 ; i < master->num_streams; i++) { cmd.cfgi.sid = master->streams[i].id;arm_smmu_cmdq_batch_add (smmu, &cmds, &cmd);
@@ -2006,7 +2008,7 @@ static int arm_smmu_atc_inv_master(struct arm_smmu_master *master)arm_smmu_atc_inv_to_cmd (IOMMU_NO_PASID ,0 ,0 , &cmd); -arm_smmu_cmdq_batch_init (master->smmu, &cmds); +arm_smmu_cmdq_batch_init (master->smmu, &cmds, cmd.opcode);for (i =0 ; i < master->num_streams; i++) { cmd.atc.sid = master->streams[i].id;arm_smmu_cmdq_batch_add (master->smmu, &cmds, &cmd);
@@ -2046,7 +2048,7 @@ int arm_smmu_atc_inv_domain(struct arm_smmu_domain *smmu_domain, int ssid,arm_smmu_atc_inv_to_cmd (ssid, iova, size, &cmd); -arm_smmu_cmdq_batch_init (smmu_domain->smmu, &cmds); +arm_smmu_cmdq_batch_init (smmu_domain->smmu, &cmds, cmd.opcode);spin_lock_irqsave (&smmu_domain->devices_lock, flags);list_for_each_entry (master, &smmu_domain->devices, domain_head) {
@@ -2123,7 +2125,7 @@ static void __arm_smmu_tlb_inv_range(struct arm_smmu_cmdq_ent *cmd, num_pages++; } -arm_smmu_cmdq_batch_init (smmu_domain->smmu, &cmds); +arm_smmu_cmdq_batch_init (smmu_domain->smmu, &cmds, cmd->opcode);while (iova < end) {if (smmu->features &ARM_SMMU_FEAT_RANGE_INV ) {
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h +3 -2
@@ -879,7 +879,8 @@ struct tegra241_cmdqv *tegra241_cmdqv_acpi_probe(struct arm_smmu_device *smmu, struct acpi_iort_node *node);void tegra241_cmdqv_device_remove (struct arm_smmu_device *smmu);int tegra241_cmdqv_device_reset (struct arm_smmu_device *smmu); -struct arm_smmu_cmdq *tegra241_cmdqv_get_cmdq (struct arm_smmu_device *smmu); +struct arm_smmu_cmdq *tegra241_cmdqv_get_cmdq (struct arm_smmu_device *smmu, +u8 opcode);#else /* CONFIG_TEGRA241_CMDQV */ static inline bool arm_smmu_has_tegra241_cmdqv (struct arm_smmu_device *smmu) {
@@ -903,7 +904,7 @@ static inline int tegra241_cmdqv_device_reset(struct arm_smmu_device *smmu) }static inline struct arm_smmu_cmdq * -tegra241_cmdqv_get_cmdq (struct arm_smmu_device *smmu) +tegra241_cmdqv_get_cmdq (struct arm_smmu_device *smmu,u8 opcode) {return NULL ; }
drivers/iommu/arm/arm-smmu-v3/tegra241-cmdqv.c +36 -1
@@ -181,6 +181,7 @@ struct tegra241_vcmdq {* struct tegra241_vintf - Virtual Interface * @idx: Global index in the CMDQV HW * @enabled: Enable status +* @hyp_own: Owned by hypervisor (in-kernel) * @cmdqv: CMDQV HW pointer * @vcmdqs: List of VCMDQ pointers * @base: MMIO base address
@@ -189,6 +190,7 @@ struct tegra241_vintf {u16 idx;bool enabled; +bool hyp_own;struct tegra241_cmdqv *cmdqv;struct tegra241_vcmdq **vcmdqs;
@@ -321,7 +323,25 @@ static irqreturn_t tegra241_cmdqv_isr(int irq, void *devid)return IRQ_HANDLED ; } -struct arm_smmu_cmdq *tegra241_cmdqv_get_cmdq (struct arm_smmu_device *smmu) +static bool tegra241_vintf_support_cmd (struct tegra241_vintf *vintf,u8 opcode) +{ +/* Hypervisor-owned VINTF can execute any command in its VCMDQs */ +if (READ_ONCE (vintf->hyp_own)) +return true; + +/* Guest-owned VINTF must Check against the list of supported CMDs */ +switch (opcode) { +case CMDQ_OP_TLBI_NH_ASID : +case CMDQ_OP_TLBI_NH_VA : +case CMDQ_OP_ATC_INV : +return true; +default : +return false; + } +} + +struct arm_smmu_cmdq *tegra241_cmdqv_get_cmdq (struct arm_smmu_device *smmu, +u8 opcode) {struct tegra241_cmdqv *cmdqv = smmu->tegra241_cmdqv;struct tegra241_vintf *vintf = cmdqv->vintfs[0 ];
@@ -335,6 +355,10 @@ struct arm_smmu_cmdq *tegra241_cmdqv_get_cmdq(struct arm_smmu_device *smmu)if (!READ_ONCE (vintf->enabled))return &smmu->cmdq; +/* Unsupported CMD go for smmu->cmdq pathway */ +if (!tegra241_vintf_support_cmd (vintf, opcode)) +return &smmu->cmdq; +/* * Select a vcmdq to use. Here we use a temporal solution to * balance out traffic on cmdq issuing: each cmdq has its own
@@ -523,6 +547,11 @@ int tegra241_cmdqv_device_reset(struct arm_smmu_device *smmu) } } +/* +* Note that HYP_OWN bit is wired to zero when running in guest kernel +* regardless of enabling it here, as !HYP_OWN cmdqs have a restricted +* set of supported commands, by following the HW design. +*/ regval =FIELD_PREP (VINTF_HYP_OWN ,1 );vintf_writel (vintf, regval,CONFIG );
@@ -530,6 +559,12 @@ int tegra241_cmdqv_device_reset(struct arm_smmu_device *smmu)if (ret)return ret; +/* +* As being mentioned above, HYP_OWN bit is wired to zero for a guest +* kernel, so read it back from HW to ensure that reflects in hyp_own +*/ + vintf->hyp_own = !!(VINTF_HYP_OWN &vintf_readl (vintf,CONFIG )); +for (lidx =0 ; lidx < cmdqv->num_vcmdqs_per_vintf; lidx++) { ret =tegra241_vcmdq_hw_init (vintf->vcmdqs[lidx]);if (ret)
Re: [PATCH v6 6/6] iommu/tegra241-cmdqv: Limit CMDs for guest owned VINTF
Quoted 12 lines · Nicolin Chen
Quoted 10 lines · Jason Gunthorpe
Quoted 3 lines · Nicolin Chen
Otherwise, there has to be a
get_suported_cmdqcallback so batch or its callers can avoid adding unsupported commands at the first place.If you really feel strongly the invalidation could be split into S1/S2/S1_VM groupings that align with the feature bits and that could be passed down from one step above. But I don't think the complexity is really needed. It is better to deal with it through the feature mechanism.
Quoted 7 lines
Hmm, I tried following your design by passing in a
CMD_TYPE_xxxto thetegra241_cmdqv_get_cmdq(), but I found a little painful to accommodate these two cases:
1.TLBI_NH_ASIDis issued viaarm_smmu_cmdq_issue_cmdlist(), so we should not mark it asCMD_TYPE_ALL. Yet, this function is used by other commands too. So, either we pass in a type from higher callers, or simply check the opcode in that function.
Yes, you'd have to pass in the type there too, which makes it more ugly.
Quoted 4 lines
So, it feels to me that checking against the opcode is still a straightforward solution. And what I ended up with is somewhat similar to this v6, yet this time it only checks at batch init call as your design does.
Well, the only downside is that the commands have to be same in a batch, but maybe that is OK anyhow.
Don't forget to take the hunks that fix the queue as well.
Jason