
On 08/24/2017 07:23 AM, Erik Skultety wrote:
We need to perform some sanity checks on the udev monitor before every use so that we know nothing changed in the meantime. The reason for moving the code to a separate function is to be able to perform the same check from a worker thread that will replace the udevEventHandleCallback in terms of poking udev for device events.
Signed-off-by: Erik Skultety <eskultet@redhat.com> --- src/node_device/node_device_udev.c | 57 ++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 18 deletions(-)
diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index f4177455c..465d272ba 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -1615,47 +1615,68 @@ udevHandleOneDevice(struct udev_device *device) }
-static void -udevEventHandleCallback(int watch ATTRIBUTE_UNUSED, - int fd, - int events ATTRIBUTE_UNUSED, - void *data ATTRIBUTE_UNUSED) +/* the caller must be holding the driver lock prior to calling this function */ +static bool +udevCheckMonitorFD(struct udev_monitor *udev_monitor, int fd)
One line for each argument... I think in keeping with other functions - this should be named 'udevEventCheckMonitorFD'
{ - struct udev_device *device = NULL; - struct udev_monitor *udev_monitor = DRV_STATE_UDEV_MONITOR(driver); - int udev_fd = -1; + int real_fd = -1;
- udev_fd = udev_monitor_get_fd(udev_monitor); - if (fd != udev_fd) { + /* sanity check that the monitor socket hasn't changed */ + real_fd = udev_monitor_get_fd(udev_monitor); + + if (real_fd != fd) { udevPrivate *priv = driver->privateData;
virReportError(VIR_ERR_INTERNAL_ERROR, _("File descriptor returned by udev %d does not " "match node device file descriptor %d"), - fd, udev_fd); + real_fd, fd);
- /* this is a non-recoverable error, let's remove the handle, so that we - * don't get in here again because of some spurious behaviour and report - * the same error multiple times + /* this is a non-recoverable error, thus the event handle has to be + * removed, so that we don't get into the handler again because of some + * spurious behaviour */ virEventRemoveHandle(priv->watch); priv->watch = -1;
Hmmm... thinking a couple patches later - this would seem to be a good candidate for something that udevEventThreadDataFree could do (as long as it held the appropriate lock of course). It's almost too bad we couldn't somehow "pretend" to have a restart... different problem though!
- goto cleanup; + return false; }
- device = udev_monitor_receive_device(udev_monitor); - if (device == NULL) { + return true; +} + + +static void +udevEventHandleCallback(int watch ATTRIBUTE_UNUSED, + int fd, + int events ATTRIBUTE_UNUSED, + void *data ATTRIBUTE_UNUSED) +{ + struct udev_device *device = NULL; + struct udev_monitor *udev_monitor = NULL; + + nodeDeviceLock(); + udev_monitor = DRV_STATE_UDEV_MONITOR(driver);
Technically there's a couple of things going on here - including the addition of the nodeDevice{Lock|Unlock}. Probably would have been best to make the split first, then add the Lock/Unlock afterwards (or vice versa). Still once I get to patch 3, I began wondering how the udev interaction works.
+ + if (!udevCheckMonitorFD(udev_monitor, fd)) + goto unlock;> + + if (!(device = udev_monitor_receive_device(udev_monitor))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("udev_monitor_receive_device returned NULL"));
I almost wonder if it would be better to have this be a ReportSystemError w/ errno... Not that udev docs give any guidance there, but maybe it'd help.
- goto cleanup; + goto unlock;
I know this is "existing"; however, if !device, then what's the purpose of calling udev_device_unref(NULL)? In fact there's one path in udevGetDMIData which actually checks != NULL before calling - although it has no reason to since I see no way for it to be NULL at cleanup (again a different issue). Also, wouldn't nodeDeviceLock/Unlock wrapping only be necessary within udevCheckMonitorFD? Why would the udev call need to be wrapped as well? John
}
+ nodeDeviceUnlock(); udevHandleOneDevice(device);
cleanup: udev_device_unref(device); return; + + unlock: + nodeDeviceUnlock(); + goto cleanup; }