Re: [PATCH] pci: iov: use device lock to protect IOV sysfs accesses

From: Bjorn Helgaas
Date: Fri May 26 2017 - 21:23:04 EST


Hi Jakub,

On Mon, May 22, 2017 at 03:50:23PM -0700, Jakub Kicinski wrote:
> PCI core sets the driver pointer before calling ->probe() and only
> clears it after ->remove(). This means driver's ->sriov_configure()
> callback will happily race with probe() and remove(), most likely
> leading to BUGs, since drivers don't expect this.

I guess you're referring to the pci_dev->driver pointer set by
local_pci_probe(), and this is important because sriov_numvfs_store()
checks that pointer, right?

I was trying to make sure there weren't other similar problems elsewhere.
But I don't see any other sysfs functions that use pci_dev->driver in that
way, so I think this is the only one.

I think this looks good.

pci_bus_add_devices
pci_bus_add_device
pci_create_sysfs_dev_files
device_attach
__device_attach
device_lock(dev)
__device_attach_driver
...
local_pci_probe
pci_dev->driver = pci_drv <--- set
pci_drv->probe()
device_unlock(dev)

sriov_numvfs_store
- mutex_lock(&iov->dev->sriov->lock)
+ device_lock(&pdev->dev)
if (pdev->driver && pdev->driver->sriov_configure) <--- test
pdev->driver->sriov_configure
- mutex_unlock(&iov->dev->sriov->lock)
+ device_unlock(&pdev->dev)

> We could reorder pointer assignments, or try detecting races in all
> drivers, but it seems simpler and cleaner to just hold the device lock
> instead of special SR-IOV lock, since that lock is already supposed
> to synchronize the driver callbacks.
>
> Remove the iov lock completely, since we remove the last user.
>
> Signed-off-by: Jakub Kicinski <jakub.kicinski@xxxxxxxxxxxxx>
> ---
> drivers/pci/iov.c | 4 ----
> drivers/pci/pci-sysfs.c | 5 ++---
> drivers/pci/pci.h | 1 -
> 3 files changed, 2 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
> index d9dc7363ac77..120485d6f352 100644
> --- a/drivers/pci/iov.c
> +++ b/drivers/pci/iov.c
> @@ -461,8 +461,6 @@ static int sriov_init(struct pci_dev *dev, int pos)
> else
> iov->dev = dev;
>
> - mutex_init(&iov->lock);
> -
> dev->sriov = iov;
> dev->is_physfn = 1;
> rc = compute_max_vf_buses(dev);
> @@ -491,8 +489,6 @@ static void sriov_release(struct pci_dev *dev)
> if (dev != dev->sriov->dev)
> pci_dev_put(dev->sriov->dev);
>
> - mutex_destroy(&dev->sriov->lock);
> -
> kfree(dev->sriov);
> dev->sriov = NULL;
> }
> diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
> index 31e99613a12e..7755559558df 100644
> --- a/drivers/pci/pci-sysfs.c
> +++ b/drivers/pci/pci-sysfs.c
> @@ -472,7 +472,6 @@ static ssize_t sriov_numvfs_store(struct device *dev,
> const char *buf, size_t count)
> {
> struct pci_dev *pdev = to_pci_dev(dev);
> - struct pci_sriov *iov = pdev->sriov;
> int ret;
> u16 num_vfs;
>
> @@ -483,7 +482,7 @@ static ssize_t sriov_numvfs_store(struct device *dev,
> if (num_vfs > pci_sriov_get_totalvfs(pdev))
> return -ERANGE;
>
> - mutex_lock(&iov->dev->sriov->lock);
> + device_lock(&pdev->dev);
>
> if (num_vfs == pdev->sriov->num_VFs)
> goto exit;
> @@ -518,7 +517,7 @@ static ssize_t sriov_numvfs_store(struct device *dev,
> num_vfs, ret);
>
> exit:
> - mutex_unlock(&iov->dev->sriov->lock);
> + device_unlock(&pdev->dev);
>
> if (ret < 0)
> return ret;
> diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
> index f8113e5b9812..93f4044b8f4b 100644
> --- a/drivers/pci/pci.h
> +++ b/drivers/pci/pci.h
> @@ -272,7 +272,6 @@ struct pci_sriov {
> u16 driver_max_VFs; /* max num VFs driver supports */
> struct pci_dev *dev; /* lowest numbered PF */
> struct pci_dev *self; /* this PF */
> - struct mutex lock; /* lock for setting sriov_numvfs in sysfs */
> resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */
> bool drivers_autoprobe; /* auto probing of VFs by driver */
> };
> --
> 2.11.0
>