[libvirt PATCH v6 30/30] nodedev: avoid delay when defining a new mdev

Erik Skultety eskultet at redhat.com
Tue Mar 30 15:42:29 UTC 2021


On Fri, Mar 26, 2021 at 11:48:26AM -0500, Jonathon Jongsma wrote:
> When calling virNodeDeviceDefineXML() to define a new mediated device,
> we call virMdevctlDefine() and then wait for the new device to appear in
> the driver's device list before returning. This caused long delays due
> to the behavior of nodeDeviceFindNewMediatedDevice(). This function
> checks to see if the device is in the list and then waits for 5s before
> checking again.
> 
> Because mdevctl is relatively slow to query the list of defined
> devices[0], the newly-defined device was generally not in the device
> list when we first checked. This results in libvirt almost always taking
> at least 5s to complete this API call for mediated devices, which is
> unacceptable.
> 
> In order to avoid this long delay, we resort to a workaround. If the
> call to virMdevctlDefine() was successful, we can assume that this new
> device will exist the next time we query mdevctl for new devices. So we
> simply add this provisional device definition directly to the nodedev
> driver's device list and return from the function. At some point in the
> future, the mdevctl handler will run and the "official" device will be
> processed, which will update the provisional device if any new details
> need to be added.
> 
> The reason that this is not necessary for virNodeDeviceCreateXML() is
> because detecting newly-created (not defined) mdevs happens through
> udev instead of mdevctl. And nodeDeviceFindNewMediatedDevice() always
> calls 'udevadm settle' before checking to see whether the device is in
> the list. This allows us to wait just long enough for all udev events to
> be processed, so the device is almost always in the list the first time
> we check and so we almost never end up hitting the 5s sleep.
> 
> [0] on my machine, 'mdevctl list --defined' took around 0.8s to
> complete for only 3 defined mdevs.
> ---
>  src/node_device/node_device_driver.c | 126 +++++++++++++++------------
>  1 file changed, 68 insertions(+), 58 deletions(-)
> 
> diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c
> index 35db24817a..a1b79d93f7 100644
> --- a/src/node_device/node_device_driver.c
> +++ b/src/node_device/node_device_driver.c
> @@ -1215,16 +1215,70 @@ nodeDeviceDestroy(virNodeDevicePtr device)
>      return ret;
>  }
>  
> +
> +/* takes ownership of @def and potentially frees it. @def should not be used
> + * after returning from this function */
> +static int
> +nodeDeviceUpdateMediatedDevice(virNodeDeviceDef *def)
> +{
> +    virNodeDeviceObj *obj;
> +    virObjectEvent *event;
> +    bool defined = false;
> +    g_autoptr(virNodeDeviceDef) owned = def;
> +    g_autofree char *name = g_strdup(owned->name);
> +
> +    owned->driver = g_strdup("vfio_mdev");
> +
> +    if (!(obj = virNodeDeviceObjListFindByName(driver->devs, owned->name))) {
> +        virNodeDeviceDef *d = g_steal_pointer(&owned);
> +        if (!(obj = virNodeDeviceObjListAssignDef(driver->devs, d))) {
> +            virNodeDeviceDefFree(d);
> +            return -1;
> +        }
> +    } else {
> +        bool changed;
> +        virNodeDeviceDef *olddef = virNodeDeviceObjGetDef(obj);
> +
> +        defined = virNodeDeviceObjIsPersistent(obj);
> +        /* Active devices contain some additional information (e.g. sysfs
> +         * path) that is not provided by mdevctl, so re-use the existing
> +         * definition and copy over new mdev data */
> +        changed = nodeDeviceDefCopyFromMdevctl(olddef, owned);
> +
> +        if (defined && !changed) {
> +            /* if this device was already defined and the definition
> +             * hasn't changed, there's nothing to do for this device */
> +            virNodeDeviceObjEndAPI(&obj);
> +            return 0;
> +        }
> +    }
> +
> +    /* all devices returned by virMdevctlListDefined() are persistent */
> +    virNodeDeviceObjSetPersistent(obj, true);
> +
> +    if (!defined)
> +        event = virNodeDeviceEventLifecycleNew(name,
> +                                               VIR_NODE_DEVICE_EVENT_DEFINED,
> +                                               0);
> +    else
> +        event = virNodeDeviceEventUpdateNew(name);
> +
> +    virNodeDeviceObjEndAPI(&obj);
> +    virObjectEventStateQueue(driver->nodeDeviceEventState, event);
> +
> +    return 0;

In 29/30, you extracted ^this code into a separate function. If you'd moved it
to the right place as well, the diff in this patch would be smaller.

> +}
> +
>  virNodeDevice*
> -nodeDeviceDefineXML(virConnect *conn,
> +nodeDeviceDefineXML(virConnectPtr conn,

I don't think you wanted ^this hunk

>                      const char *xmlDesc,
>                      unsigned int flags)
>  {
>      g_autoptr(virNodeDeviceDef) def = NULL;
> -    virNodeDevice *device = NULL;
>      const char *virt_type = NULL;
>      g_autofree char *uuid = NULL;
>      g_autofree char *errmsg = NULL;
> +    g_autofree char *name = NULL;
>  
>      virCheckFlags(0, NULL);
>  
> @@ -1264,9 +1318,19 @@ nodeDeviceDefineXML(virConnect *conn,
>      }
>  
>      mdevGenerateDeviceName(def);
> -    device = nodeDeviceFindNewMediatedDevice(conn, def->caps->data.mdev.uuid);
> +    name = g_strdup(def->name);
> +
> +    /* Normally we would call nodeDeviceFindNewMediatedDevice() here to wait
> +     * for the new device to appear. But mdevctl can take a while to query
> +     * devices, and if nodeDeviceFindNewMediatedDevice() doesn't find the new
> +     * device immediately it will wait at 5s before checking again. Since we
> +     * have already received the uuid from virMdevctlDefine(), we can simply
> +     * add the provisional device to the list and return it immediately and
> +     * avoid this long delay. */
> +    if (nodeDeviceUpdateMediatedDevice(g_steal_pointer(&def)) < 0)
> +        return NULL;

Let's just hope this workaround will not bite us in the back in the future.
That said, I really didn't like the 5s delay :).

After decreasing the diff of this patch:
Reviewed-by: Erik Skultety <eskultet at redhat.com>




More information about the libvir-list mailing list