Enable userspace to retrieve preserved VFIO device files from VFIO after a Live Update by implementing the retrieve(), can_finish() and finish() file handler callbacks.
During retrieve, find the VFIO device using the BDF and domain information from the serialized state, and open it using the new vfio_device_liveupdate_cdev_open() API. Disallow Live Update to finish if userspace hasn't opened the file successfully or any error occurred during retrieve. Co-developed-by: David Matlack <[email protected]> Signed-off-by: David Matlack <[email protected]> Signed-off-by: Vipin Sharma <[email protected]> --- drivers/vfio/pci/vfio_pci_liveupdate.c | 64 +++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/drivers/vfio/pci/vfio_pci_liveupdate.c b/drivers/vfio/pci/vfio_pci_liveupdate.c index e6eb7f6b53d7..4baac80d03f0 100644 --- a/drivers/vfio/pci/vfio_pci_liveupdate.c +++ b/drivers/vfio/pci/vfio_pci_liveupdate.c @@ -39,7 +39,13 @@ * preserved, so there is no way for the file to be destroyed or the device * to be unbound from the vfio-pci driver while it is preserved. * - * Retrieving the file after kexec is not yet supported. + * After kexec, the preserved VFIO device file can be retrieved from the session + * just like any other preserved file:: + * + * ioctl(session_fd, LIVEUPDATE_SESSION_RETRIEVE_FD, &arg); + * device_fd = arg.fd; + * ... + * ioctl(session_fd, LIVEUPDATE_SESSION_FINISH, ...); * * Restrictions * ============ @@ -93,6 +99,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/errno.h> +#include <linux/file.h> #include <linux/kexec_handover.h> #include <linux/kho/abi/vfio_pci.h> #include <linux/liveupdate.h> @@ -223,13 +230,65 @@ static int vfio_pci_liveupdate_freeze(struct liveupdate_file_op_args *args) return 0; } +static int match_device(struct device *dev, const void *arg) +{ + struct vfio_device *device = container_of(dev, struct vfio_device, device); + const struct vfio_pci_core_device_ser *ser = arg; + struct pci_dev *pdev; + + pdev = dev_is_pci(device->dev) ? to_pci_dev(device->dev) : NULL; + if (!pdev) + return false; + + return ser->bdf == pci_dev_id(pdev) && ser->domain == pci_domain_nr(pdev->bus); +} + static int vfio_pci_liveupdate_retrieve(struct liveupdate_file_op_args *args) { - return -EOPNOTSUPP; + struct vfio_pci_core_device_ser *ser; + struct vfio_device *device; + struct file *file; + int ret = 0; + + ser = phys_to_virt(args->serialized_data); + + device = vfio_find_device(ser, match_device); + if (!device) + return -ENODEV; + + file = vfio_device_liveupdate_cdev_open(device); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto out; + } + + args->file = file; +out: + /* Drop the reference from vfio_find_device() */ + vfio_device_put_registration(device); + return ret; + +} + + +static bool vfio_pci_liveupdate_can_finish(struct liveupdate_file_op_args *args) +{ + struct vfio_device *device; + + if (args->retrieve_status <= 0) + return false; + + device = vfio_device_from_file(args->file); + guard(mutex)(&device->dev_set->lock); + return vfio_device_cdev_opened(device); } static void vfio_pci_liveupdate_finish(struct liveupdate_file_op_args *args) { + struct vfio_device *device = vfio_device_from_file(args->file); + + pci_liveupdate_finish(to_pci_dev(device->dev)); + kho_restore_free(phys_to_virt(args->serialized_data)); } static const struct liveupdate_file_ops vfio_pci_liveupdate_file_ops = { @@ -238,6 +297,7 @@ static const struct liveupdate_file_ops vfio_pci_liveupdate_file_ops = { .unpreserve = vfio_pci_liveupdate_unpreserve, .freeze = vfio_pci_liveupdate_freeze, .retrieve = vfio_pci_liveupdate_retrieve, + .can_finish = vfio_pci_liveupdate_can_finish, .finish = vfio_pci_liveupdate_finish, .owner = THIS_MODULE, }; -- 2.55.0.795.g602f6c329a-goog

