[libvirt] [PATCH v5 2/6] nodedev: udev: Convert udev private data to a lockable object

John Ferlan jferlan at redhat.com
Sun Oct 15 14:23:45 UTC 2017



On 10/11/2017 10:52 AM, Erik Skultety wrote:
> Since there's going to be a worker thread which needs to have some data
> protected by a lock, the whole code would just simply get unnecessary
> complex, since two sets of locks would be necessary, driver lock (for
> udev monitor and event handle) and a mutex protecting thread-local data.
> Given the future thread will need to access the udev monitor socket as
> well, why not protect everything with a single lock, even better, by
> converting the driver's private data to a lockable object, we get the
> automatic object disposal feature for free.
> 
> Signed-off-by: Erik Skultety <eskultet at redhat.com>
> ---
>  src/node_device/node_device_udev.c | 140 ++++++++++++++++++++++++-------------
>  src/node_device/node_device_udev.h |   3 -
>  2 files changed, 93 insertions(+), 50 deletions(-)
> 
> diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
> index a9a4c9b6b..bb9787fdb 100644
> --- a/src/node_device/node_device_udev.c
> +++ b/src/node_device/node_device_udev.c
> @@ -53,12 +53,78 @@ VIR_LOG_INIT("node_device.node_device_udev");
>  # define TYPE_RAID 12
>  #endif
>  
> -struct _udevPrivate {
> +typedef struct _udevEventData udevEventData;
> +typedef udevEventData *udevEventDataPtr;
> +
> +struct _udevEventData {
> +    virObjectLockable parent;
> +
>      struct udev_monitor *udev_monitor;
>      int watch;
>      bool privileged;
>  };

Mental note - maybe the driver->privateData should change to
driver->udevEventData in _virNodeDeviceDriverState

You interchange using @priv and @data throughout which right now could
make sense, but in the future may make less sense. I have a preference
for consistency, so calling @data in some places and @priv in others
leads to inconsistency. I believe pick one and be consistent.

>  
> +static virClassPtr udevEventDataClass;
> +
> +static void
> +udevEventDataDispose(void *obj)
> +{
> +    struct udev *udev = NULL;
> +    udevEventDataPtr data = obj;
> +
> +    if (data->watch != -1)
> +        virEventRemoveHandle(data->watch);
> +
> +    if (data->udev_monitor)
> +        udev = udev_monitor_get_udev(data->udev_monitor);
> +
> +    udev_unref(udev);
> +    udev_monitor_unref(data->udev_monitor);

This is a different order than previous cleanup - perhaps should be:

    if (data->udev_monitor) {
        udev = udev_monitor_get_udev(data->udev_monitor);
        udev_monitor_unref(data->udev_monitor);
        udev_unref(udev);
    }


or

    if (!data->udev_monitor)
        return;

    udev = udev_monitor_get_udev(data->udev_monitor);
    udev_monitor_unref(data->udev_monitor);
    udev_unref(udev);

Just not clear what happens to udev if you unref udev before unref
udev_monitor.  Better safe than sorry and go in the opposite order of
Initialization.

> +}
> +
> +
> +static int
> +udevEventDataOnceInit(void)
> +{
> +    if (!(udevEventDataClass = virClassNew(virClassForObjectLockable(),
> +                                           "udevEventData",
> +                                           sizeof(udevEventData),
> +                                           udevEventDataDispose)))
> +        return -1;
> +
> +    return 0;
> +}
> +
> +VIR_ONCE_GLOBAL_INIT(udevEventData)
> +
> +static udevEventDataPtr
> +udevEventDataNew(void)
> +{
> +    udevEventDataPtr ret = NULL;
> +
> +    if (udevEventDataInitialize() < 0)
> +        return NULL;
> +
> +    if (!(ret = virObjectLockableNew(udevEventDataClass)))
> +        return NULL;
> +
> +    ret->watch = -1;
> +    return ret;
> +}
> +
> +
> +static bool
> +udevEventDataIsPrivileged(udevEventDataPtr data)
> +{
> +    bool privileged;

Hmmm... is @data privileged or is the driver that needs the privilege
check... IOW: should privileged by a DriverState value (even though it'd
be unused in _hal).  It comes in as a virDrvStateInitialize value. That
way it's just a driver->privileged check regardless of whether there is
an event thread running.

I know - should have thought of this sooner, but sometimes things are
more obvious at odd points in review time. Moving privileged would be a
patch 1.5 and thus make this helper unnecessary. It'd be a simple move
from private to NodeDeviceDriverState and then store/read from there
instead of @priv.  Then udevGetDMIData wouldn't need to get @priv.

> +
> +    virObjectLock(data);
> +    privileged = data->privileged;
> +    virObjectUnlock(data);
> +
> +    return privileged;
> +}
> +
>  
>  static bool
>  udevHasDeviceProperty(struct udev_device *dev,
> @@ -447,7 +513,7 @@ udevProcessPCI(struct udev_device *device,
>      virNodeDevCapPCIDevPtr pci_dev = &def->caps->data.pci_dev;
>      virPCIEDeviceInfoPtr pci_express = NULL;
>      virPCIDevicePtr pciDev = NULL;
> -    udevPrivate *priv = driver->privateData;
> +    udevEventDataPtr priv = driver->privateData;
>      int ret = -1;
>      char *p;
>  
> @@ -498,7 +564,7 @@ udevProcessPCI(struct udev_device *device,
>          goto cleanup;
>  
>      /* We need to be root to read PCI device configs */
> -    if (priv->privileged) {
> +    if (udevEventDataIsPrivileged(priv)) {
>          if (virPCIGetHeaderType(pciDev, &pci_dev->hdrType) < 0)
>              goto cleanup;
>  
> @@ -1559,39 +1625,18 @@ udevPCITranslateDeinit(void)
>  static int
>  nodeStateCleanup(void)
>  {
> -    udevPrivate *priv = NULL;
> -    struct udev_monitor *udev_monitor = NULL;
> -    struct udev *udev = NULL;
> -
>      if (!driver)
>          return -1;
>  
>      nodeDeviceLock();
>  
> +    virObjectUnref(driver->privateData);
>      virObjectUnref(driver->nodeDeviceEventState);
>  
> -    priv = driver->privateData;
> -
> -    if (priv) {

FWIW: It's this that got me to thinking privileged should never have
been part of @priv...

> -        if (priv->watch != -1)
> -            virEventRemoveHandle(priv->watch);
> -
> -        udev_monitor = DRV_STATE_UDEV_MONITOR(driver);
> -
> -        if (udev_monitor != NULL) {
> -            udev = udev_monitor_get_udev(udev_monitor);
> -            udev_monitor_unref(udev_monitor);
> -        }
> -    }
> -
> -    if (udev != NULL)
> -        udev_unref(udev);
> -
>      virNodeDeviceObjListFree(driver->devs);
>      nodeDeviceUnlock();
>      virMutexDestroy(&driver->lock);
>      VIR_FREE(driver);
> -    VIR_FREE(priv);
>  
>      udevPCITranslateDeinit();
>      return 0;
> @@ -1615,16 +1660,17 @@ udevHandleOneDevice(struct udev_device *device)
>  }
>  
>  
> +/* the caller must be holding the udevEventData object lock prior to calling
> + * this function
> + */
>  static bool
> -udevEventMonitorSanityCheck(struct udev_monitor *udev_monitor,
> +udevEventMonitorSanityCheck(udevEventDataPtr priv,
>                              int fd)
>  {
>      int rc = -1;
>  
> -    rc = udev_monitor_get_fd(udev_monitor);
> +    rc = udev_monitor_get_fd(priv->udev_monitor);
>      if (fd != rc) {
> -        udevPrivate *priv = driver->privateData;
> -
>          virReportError(VIR_ERR_INTERNAL_ERROR,
>                         _("File descriptor returned by udev %d does not "
>                           "match node device file descriptor %d"),
> @@ -1650,15 +1696,19 @@ udevEventHandleCallback(int watch ATTRIBUTE_UNUSED,
>                          int events ATTRIBUTE_UNUSED,
>                          void *data ATTRIBUTE_UNUSED)
>  {
> +    udevEventDataPtr priv = driver->privateData;
>      struct udev_device *device = NULL;
> -    struct udev_monitor *udev_monitor = NULL;
>  
> -    udev_monitor = DRV_STATE_UDEV_MONITOR(driver);

Should we add a "if (!priv) return" before this?

> +    virObjectLock(priv);
>  
> -    if (!udevEventMonitorSanityCheck(udev_monitor, fd))
> +    if (!udevEventMonitorSanityCheck(priv, fd)) {
> +        virObjectUnlock(priv);
>          return;
> +    }
> +
> +    device = udev_monitor_receive_device(priv->udev_monitor);
> +    virObjectUnlock(priv);
>  
> -    device = udev_monitor_receive_device(udev_monitor);
>      if (device == NULL) {
>          virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>                         _("udev_monitor_receive_device returned NULL"));
> @@ -1675,12 +1725,13 @@ udevEventHandleCallback(int watch ATTRIBUTE_UNUSED,
>  static void
>  udevGetDMIData(virNodeDevCapSystemPtr syscap)
>  {
> +    udevEventDataPtr priv = driver->privateData;
>      struct udev *udev = NULL;
>      struct udev_device *device = NULL;
>      virNodeDevCapSystemHardwarePtr hardware = &syscap->hardware;
>      virNodeDevCapSystemFirmwarePtr firmware = &syscap->firmware;
>  
> -    udev = udev_monitor_get_udev(DRV_STATE_UDEV_MONITOR(driver));
> +    udev = udev_monitor_get_udev(priv->udev_monitor);

There's no lock/unlock around priv-> access here (although there is
earlier).

>  
>      device = udev_device_new_from_syspath(udev, DMI_DEVPATH);
>      if (device == NULL) {
> @@ -1791,15 +1842,9 @@ nodeStateInitialize(bool privileged,
>                      virStateInhibitCallback callback ATTRIBUTE_UNUSED,
>                      void *opaque ATTRIBUTE_UNUSED)
>  {
> -    udevPrivate *priv = NULL;
> +    udevEventDataPtr priv = NULL;
>      struct udev *udev = NULL;
>  
> -    if (VIR_ALLOC(priv) < 0)
> -        return -1;
> -
> -    priv->watch = -1;
> -    priv->privileged = privileged;

FWIW: You lost priv->privileged setting in this patch... So it'd always
be false

> -
>      if (VIR_ALLOC(driver) < 0) {
>          VIR_FREE(priv);

^^ won't be necessary and then of course the { } won't be either...

Same a few lines lower where there's VIR_FREE(priv), but the { } would
stay after a virMutexInit failure

With a few tweaks and edits...

Reviewed-by: John Ferlan <jferlan at redhat.com>

John
>          return -1;
> @@ -1813,12 +1858,13 @@ nodeStateInitialize(bool privileged,
>          return -1;
>      }
>  
> +    nodeDeviceLock();
> +
> +    if (!(driver->devs = virNodeDeviceObjListNew()) ||
> +        !(priv = udevEventDataNew()))
> +        goto unlock;
> +
>      driver->privateData = priv;
> -    nodeDeviceLock();
> -
> -    if (!(driver->devs = virNodeDeviceObjListNew()))
> -        goto unlock;
> -
>      driver->nodeDeviceEventState = virObjectEventStateNew();
>  
>      if (udevPCITranslateInit(privileged) < 0)
> @@ -1836,7 +1882,7 @@ nodeStateInitialize(bool privileged,
>  #endif
>  
>      priv->udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
> -    if (priv->udev_monitor == NULL) {
> +    if (!priv->udev_monitor) {
>          virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
>                         _("udev_monitor_new_from_netlink returned NULL"));
>          goto unlock;
> diff --git a/src/node_device/node_device_udev.h b/src/node_device/node_device_udev.h
> index 9a07ab77e..f15e5204c 100644
> --- a/src/node_device/node_device_udev.h
> +++ b/src/node_device/node_device_udev.h
> @@ -23,9 +23,6 @@
>  #include <libudev.h>
>  #include <stdint.h>
>  
> -typedef struct _udevPrivate udevPrivate;
> -
>  #define SYSFS_DATA_SIZE 4096
> -#define DRV_STATE_UDEV_MONITOR(ds) (((udevPrivate *)((ds)->privateData))->udev_monitor)
>  #define DMI_DEVPATH "/sys/devices/virtual/dmi/id"
>  #define DMI_DEVPATH_FALLBACK "/sys/class/dmi/id"
> 




More information about the libvir-list mailing list