Re: [PATCH v4 11/13] iommu/arm-smmu-v3: Improve add_device() error handling

2020-01-15 Thread Robin Murphy

On 14/01/2020 3:25 pm, Will Deacon wrote:

On Thu, Dec 19, 2019 at 05:30:31PM +0100, Jean-Philippe Brucker wrote:

Let add_device() clean up after itself. The iommu_bus_init() function
does call remove_device() on error, but other sites (e.g. of_iommu) do
not.

Don't free level-2 stream tables because we'd have to track if we
allocated each of them or if they are used by other endpoints. It's not
worth the hassle since they are managed resources.

Reviewed-by: Eric Auger 
Reviewed-by: Jonathan Cameron 
Signed-off-by: Jean-Philippe Brucker 
---
  drivers/iommu/arm-smmu-v3.c | 28 +---
  1 file changed, 21 insertions(+), 7 deletions(-)


I think this is alright, with one caveat relating to:


/*
 * We _can_ actually withstand dodgy bus code re-calling add_device()
 * without an intervening remove_device()/of_xlate() sequence, but
 * we're not going to do so quietly...
 */
if (WARN_ON_ONCE(fwspec->iommu_priv)) {
master = fwspec->iommu_priv;
smmu = master->smmu;
} ...


which may be on shakey ground if the subsequent add_device() call can fail
and free stuff that the first one allocated. At least, I don't know what
we're trying to support with this, so it's hard to tell whether or not it
still works as intended after your change.


Hmm, if add_device() ever did fail it should really be expected to 
return the device back to an un-added state, so I don't believe that 
particular concern should be significant regardless...

How is this supposed to work? I don't recall ever seeing that WARN fire,
so can we just remove this and bail instead? Robin?


However, I am inclined to agree that it's probably better to make it all 
moot. Although it indeed should never happen, ISTR at the time there 
appeared to be some possible path somewhere by which the notifier may 
have been triggered a second time - possibly if some other device failed 
or deferred after the first call, triggering the bus code to start all 
over again. Since then, though, we've made a lot of changes to how 
->add_device usually gets called, plus stuff like the 
iommu_device_link() call has snuck in that might not stand up to a 
replay anyway, so I don't see any problem with making this condition a 
hard failure. It's certainly much easier to reason about.


In fact, there will already be a WARN from iommu_probe_device() now 
(because the first call will have set the group), so I don't think we 
need any additional diagnostic in the driver any more.


Robin.


Something like below before your changes...

Will

--->8

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index effe72eb89e7..6ae3df2f3495 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -2534,28 +2534,23 @@ static int arm_smmu_add_device(struct device *dev)
  
  	if (!fwspec || fwspec->ops != _smmu_ops)

return -ENODEV;
-   /*
-* We _can_ actually withstand dodgy bus code re-calling add_device()
-* without an intervening remove_device()/of_xlate() sequence, but
-* we're not going to do so quietly...
-*/
-   if (WARN_ON_ONCE(fwspec->iommu_priv)) {
-   master = fwspec->iommu_priv;
-   smmu = master->smmu;
-   } else {
-   smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
-   if (!smmu)
-   return -ENODEV;
-   master = kzalloc(sizeof(*master), GFP_KERNEL);
-   if (!master)
-   return -ENOMEM;
  
-		master->dev = dev;

-   master->smmu = smmu;
-   master->sids = fwspec->ids;
-   master->num_sids = fwspec->num_ids;
-   fwspec->iommu_priv = master;
-   }
+   if (WARN_ON_ONCE(fwspec->iommu_priv))
+   return -EBUSY;
+
+   smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
+   if (!smmu)
+   return -ENODEV;
+
+   master = kzalloc(sizeof(*master), GFP_KERNEL);
+   if (!master)
+   return -ENOMEM;
+
+   master->dev = dev;
+   master->smmu = smmu;
+   master->sids = fwspec->ids;
+   master->num_sids = fwspec->num_ids;
+   fwspec->iommu_priv = master;
  
  	/* Check the SIDs are in range of the SMMU and our stream table */

for (i = 0; i < master->num_sids; i++) {


___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v4 11/13] iommu/arm-smmu-v3: Improve add_device() error handling

2020-01-15 Thread Will Deacon
On Tue, Jan 14, 2020 at 03:25:39PM +, Will Deacon wrote:
> On Thu, Dec 19, 2019 at 05:30:31PM +0100, Jean-Philippe Brucker wrote:
> > Let add_device() clean up after itself. The iommu_bus_init() function
> > does call remove_device() on error, but other sites (e.g. of_iommu) do
> > not.
> > 
> > Don't free level-2 stream tables because we'd have to track if we
> > allocated each of them or if they are used by other endpoints. It's not
> > worth the hassle since they are managed resources.
> > 
> > Reviewed-by: Eric Auger 
> > Reviewed-by: Jonathan Cameron 
> > Signed-off-by: Jean-Philippe Brucker 
> > ---
> >  drivers/iommu/arm-smmu-v3.c | 28 +---
> >  1 file changed, 21 insertions(+), 7 deletions(-)
> 
> I think this is alright, with one caveat relating to:
> 
> 
>   /*
>* We _can_ actually withstand dodgy bus code re-calling add_device()
>* without an intervening remove_device()/of_xlate() sequence, but
>* we're not going to do so quietly...
>*/
>   if (WARN_ON_ONCE(fwspec->iommu_priv)) {
>   master = fwspec->iommu_priv;
>   smmu = master->smmu;
>   } ...
> 
> 
> which may be on shakey ground if the subsequent add_device() call can fail
> and free stuff that the first one allocated. At least, I don't know what
> we're trying to support with this, so it's hard to tell whether or not it
> still works as intended after your change.
> 
> How is this supposed to work? I don't recall ever seeing that WARN fire,
> so can we just remove this and bail instead? Robin?
> 
> Something like below before your changes...

FWIW, I've written this as a patch locally, since I'd like to apply it
on top of v5 of your series.

Will

--->8

>From 6029102f406d4db5e7a465da5fd2e08a5b12c532 Mon Sep 17 00:00:00 2001
From: Will Deacon 
Date: Wed, 15 Jan 2020 15:35:16 +
Subject: [PATCH] iommu/arm-smmu-v3: Return -EBUSY when trying to re-add a
 device

Although we WARN in arm_smmu_add_device() if the device being added has
been added already without a subsequent call to arm_smmu_remove_device(),
we still continue half-heartedly, initialising the stream-table for any
new StreamIDs that may have magically appeared and re-establishing device
links that should still be there from last time.

Given that calling ->add_device() twice without removing the device in the
meantime is indicative of an error in the caller, just return -EBUSY after
warning.

Cc: Robin Murphy 
Cc: Jean Philippe-Brucker 
Signed-off-by: Will Deacon 
---
 drivers/iommu/arm-smmu-v3.c | 37 -
 1 file changed, 16 insertions(+), 21 deletions(-)

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index efa326601308..cc26e1323da3 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -2841,28 +2841,23 @@ static int arm_smmu_add_device(struct device *dev)
 
if (!fwspec || fwspec->ops != _smmu_ops)
return -ENODEV;
-   /*
-* We _can_ actually withstand dodgy bus code re-calling add_device()
-* without an intervening remove_device()/of_xlate() sequence, but
-* we're not going to do so quietly...
-*/
-   if (WARN_ON_ONCE(fwspec->iommu_priv)) {
-   master = fwspec->iommu_priv;
-   smmu = master->smmu;
-   } else {
-   smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
-   if (!smmu)
-   return -ENODEV;
-   master = kzalloc(sizeof(*master), GFP_KERNEL);
-   if (!master)
-   return -ENOMEM;
 
-   master->dev = dev;
-   master->smmu = smmu;
-   master->sids = fwspec->ids;
-   master->num_sids = fwspec->num_ids;
-   fwspec->iommu_priv = master;
-   }
+   if (WARN_ON_ONCE(fwspec->iommu_priv))
+   return -EBUSY;
+
+   smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
+   if (!smmu)
+   return -ENODEV;
+
+   master = kzalloc(sizeof(*master), GFP_KERNEL);
+   if (!master)
+   return -ENOMEM;
+
+   master->dev = dev;
+   master->smmu = smmu;
+   master->sids = fwspec->ids;
+   master->num_sids = fwspec->num_ids;
+   fwspec->iommu_priv = master;
 
/* Check the SIDs are in range of the SMMU and our stream table */
for (i = 0; i < master->num_sids; i++) {
-- 
2.25.0.rc1.283.g88dfdc4193-goog

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


Re: [PATCH v4 11/13] iommu/arm-smmu-v3: Improve add_device() error handling

2020-01-14 Thread Will Deacon
On Thu, Dec 19, 2019 at 05:30:31PM +0100, Jean-Philippe Brucker wrote:
> Let add_device() clean up after itself. The iommu_bus_init() function
> does call remove_device() on error, but other sites (e.g. of_iommu) do
> not.
> 
> Don't free level-2 stream tables because we'd have to track if we
> allocated each of them or if they are used by other endpoints. It's not
> worth the hassle since they are managed resources.
> 
> Reviewed-by: Eric Auger 
> Reviewed-by: Jonathan Cameron 
> Signed-off-by: Jean-Philippe Brucker 
> ---
>  drivers/iommu/arm-smmu-v3.c | 28 +---
>  1 file changed, 21 insertions(+), 7 deletions(-)

I think this is alright, with one caveat relating to:


/*
 * We _can_ actually withstand dodgy bus code re-calling add_device()
 * without an intervening remove_device()/of_xlate() sequence, but
 * we're not going to do so quietly...
 */
if (WARN_ON_ONCE(fwspec->iommu_priv)) {
master = fwspec->iommu_priv;
smmu = master->smmu;
} ...


which may be on shakey ground if the subsequent add_device() call can fail
and free stuff that the first one allocated. At least, I don't know what
we're trying to support with this, so it's hard to tell whether or not it
still works as intended after your change.

How is this supposed to work? I don't recall ever seeing that WARN fire,
so can we just remove this and bail instead? Robin?

Something like below before your changes...

Will

--->8

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index effe72eb89e7..6ae3df2f3495 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -2534,28 +2534,23 @@ static int arm_smmu_add_device(struct device *dev)
 
if (!fwspec || fwspec->ops != _smmu_ops)
return -ENODEV;
-   /*
-* We _can_ actually withstand dodgy bus code re-calling add_device()
-* without an intervening remove_device()/of_xlate() sequence, but
-* we're not going to do so quietly...
-*/
-   if (WARN_ON_ONCE(fwspec->iommu_priv)) {
-   master = fwspec->iommu_priv;
-   smmu = master->smmu;
-   } else {
-   smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
-   if (!smmu)
-   return -ENODEV;
-   master = kzalloc(sizeof(*master), GFP_KERNEL);
-   if (!master)
-   return -ENOMEM;
 
-   master->dev = dev;
-   master->smmu = smmu;
-   master->sids = fwspec->ids;
-   master->num_sids = fwspec->num_ids;
-   fwspec->iommu_priv = master;
-   }
+   if (WARN_ON_ONCE(fwspec->iommu_priv))
+   return -EBUSY;
+
+   smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
+   if (!smmu)
+   return -ENODEV;
+
+   master = kzalloc(sizeof(*master), GFP_KERNEL);
+   if (!master)
+   return -ENOMEM;
+
+   master->dev = dev;
+   master->smmu = smmu;
+   master->sids = fwspec->ids;
+   master->num_sids = fwspec->num_ids;
+   fwspec->iommu_priv = master;
 
/* Check the SIDs are in range of the SMMU and our stream table */
for (i = 0; i < master->num_sids; i++) {
___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu