APPLIED: [bionic/master-next 1/1] UBUNTU: SAUCE: vfio -- release device lock before userspace requests
Kleber Souza
kleber.souza at canonical.com
Wed Sep 12 09:22:25 UTC 2018
On 09/12/18 10:50, Andy Whitcroft wrote:
> During a hotplug event vfio_pci_remove() will call
> vfio_del_group_dev() to release the device group. This may trigger
> a userspace request. Currently this userspace request is performed
> while holding the device lock. This leads userspace to deadlock
> against it while trying to perform the requested cleanup.
>
> Drop the device lock while the userspace request is in flight.
> After it completes reaquire the lock and revalidate the device as
> it may have been successfully removed by a concurrent operation.
> As the remove callback may now drop the lock also check and
> revalidation at the end of that operation.
>
> BugLink: http://bugs.launchpad.net/bugs/1792099
> Signed-off-by: Andy Whitcroft <apw at canonical.com>
> ---
> drivers/base/dd.c | 7 +++++++
> drivers/vfio/vfio.c | 23 ++++++++++++++++++++++-
> 2 files changed, 29 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/base/dd.c b/drivers/base/dd.c
> index 2c964f56dafe..37c01054521b 100644
> --- a/drivers/base/dd.c
> +++ b/drivers/base/dd.c
> @@ -868,6 +868,13 @@ static void __device_release_driver(struct device *dev, struct device *parent)
> dev->bus->remove(dev);
> else if (drv->remove)
> drv->remove(dev);
> + /*
> + * A concurrent invocation of the same function might
> + * have released the driver successfully while this one
> + * was waiting, so check for that.
> + */
> + if (dev->driver != drv)
> + return;
>
> device_links_driver_cleanup(dev);
> dma_deconfigure(dev);
> diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
> index 2bc3705a99bd..6ae4d3f432fe 100644
> --- a/drivers/vfio/vfio.c
> +++ b/drivers/vfio/vfio.c
> @@ -934,6 +934,10 @@ void *vfio_del_group_dev(struct device *dev)
> unsigned int i = 0;
> long ret;
> bool interrupted = false;
> + bool locked = true;
> + struct device_driver *drv;
> +
> + drv = dev->driver;
>
> /*
> * The group exists so long as we have a device reference. Get
> @@ -974,8 +978,11 @@ void *vfio_del_group_dev(struct device *dev)
> if (!device)
> break;
>
> - if (device->ops->request)
> + if (device->ops->request) {
> + device_unlock(dev);
> + locked = false;
> device->ops->request(device_data, i++);
> + }
>
> vfio_device_put(device);
>
> @@ -994,6 +1001,20 @@ void *vfio_del_group_dev(struct device *dev)
> current->comm, task_pid_nr(current));
> }
> }
> +
> + if (!locked) {
> + device_lock(dev);
> + locked = true;
> + /*
> + * A concurrent operation may have released the driver
> + * successfully while we had dropped the lock,
> + * check for that.
> + */
> + if (dev->driver != drv) {
> + vfio_group_put(group);
> + return NULL;
> + }
> + }
> } while (ret <= 0);
>
> /*
>
Applied to bionic/master-next branch.
Thanks,
Kleber
More information about the kernel-team
mailing list