From 2fcdf352df4212ff679d90a1fd63b6469e5f346d Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Thu, 28 Apr 2022 11:42:08 +0100 Subject: [PATCH] Retry the device ioctl for EAGAIN --- libfwupdplugin/fu-udev-device.c | 42 +++++++++++++++++++++++++++++---- libfwupdplugin/fu-udev-device.h | 9 +++++++ libfwupdplugin/fwupdplugin.map | 6 +++++ 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/libfwupdplugin/fu-udev-device.c b/libfwupdplugin/fu-udev-device.c index 5469d66e1..78ad8372a 100644 --- a/libfwupdplugin/fu-udev-device.c +++ b/libfwupdplugin/fu-udev-device.c @@ -1375,25 +1375,32 @@ fu_udev_device_close(FuDevice *device, GError **error) } /** - * fu_udev_device_ioctl: + * fu_udev_device_ioctl_full: * @self: a #FuUdevDevice * @request: request number * @buf: a buffer to use, which *must* be large enough for the request * @rc: (out) (nullable): the raw return value from the ioctl + * @timeout: (nullable): timeout in ms for the retry action, see %FU_UDEV_DEVICE_FLAG_IOCTL_RETRY * @error: (nullable): optional return location for an error * * Control a device using a low-level request. * * Returns: %TRUE for success * - * Since: 1.3.3 + * Since: 1.8.1 **/ gboolean -fu_udev_device_ioctl(FuUdevDevice *self, gulong request, guint8 *buf, gint *rc, GError **error) +fu_udev_device_ioctl_full(FuUdevDevice *self, + gulong request, + guint8 *buf, + gint *rc, + guint timeout, + GError **error) { #ifdef HAVE_IOCTL_H FuUdevDevicePrivate *priv = GET_PRIVATE(self); gint rc_tmp; + g_autoptr(GTimer) timer = g_timer_new(); g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE); g_return_val_if_fail(request != 0x0, FALSE); @@ -1411,7 +1418,14 @@ fu_udev_device_ioctl(FuUdevDevice *self, gulong request, guint8 *buf, gint *rc, return FALSE; } - rc_tmp = ioctl(priv->fd, request, buf); + /* poll if required up to the timeout */ + do { + rc_tmp = ioctl(priv->fd, request, buf); + if (rc_tmp >= 0) + break; + } while ((priv->flags & FU_UDEV_DEVICE_FLAG_IOCTL_RETRY) && + (errno == EINTR || errno == EAGAIN) && + g_timer_elapsed(timer, NULL) < timeout * 1000.f); if (rc != NULL) *rc = rc_tmp; if (rc_tmp < 0) { @@ -1443,6 +1457,26 @@ fu_udev_device_ioctl(FuUdevDevice *self, gulong request, guint8 *buf, gint *rc, #endif } +/** + * fu_udev_device_ioctl: + * @self: a #FuUdevDevice + * @request: request number + * @buf: a buffer to use, which *must* be large enough for the request + * @rc: (out) (nullable): the raw return value from the ioctl + * @error: (nullable): optional return location for an error + * + * Control a device using a low-level request. + * + * Returns: %TRUE for success + * + * Since: 1.3.3 + **/ +gboolean +fu_udev_device_ioctl(FuUdevDevice *self, gulong request, guint8 *buf, gint *rc, GError **error) +{ + return fu_udev_device_ioctl_full(self, request, buf, rc, 0, error); +} + /** * fu_udev_device_pread_full: * @self: a #FuUdevDevice diff --git a/libfwupdplugin/fu-udev-device.h b/libfwupdplugin/fu-udev-device.h index 38869a94f..6bcb299bf 100644 --- a/libfwupdplugin/fu-udev-device.h +++ b/libfwupdplugin/fu-udev-device.h @@ -34,6 +34,7 @@ struct _FuUdevDeviceClass { * @FU_UDEV_DEVICE_FLAG_USE_CONFIG: Read and write from the device config * @FU_UDEV_DEVICE_FLAG_OPEN_NONBLOCK: Open nonblocking, e.g. O_NONBLOCK * @FU_UDEV_DEVICE_FLAG_OPEN_SYNC: Open sync, e.g. O_SYNC + * @FU_UDEV_DEVICE_FLAG_IOCTL_RETRY: Retry the ioctl() call when required * * Flags used when opening the device using fu_device_open(). **/ @@ -45,6 +46,7 @@ typedef enum { FU_UDEV_DEVICE_FLAG_USE_CONFIG = 1 << 3, FU_UDEV_DEVICE_FLAG_OPEN_NONBLOCK = 1 << 4, FU_UDEV_DEVICE_FLAG_OPEN_SYNC = 1 << 5, + FU_UDEV_DEVICE_FLAG_IOCTL_RETRY = 1 << 6, /*< private >*/ FU_UDEV_DEVICE_FLAG_LAST } FuUdevDeviceFlags; @@ -102,6 +104,13 @@ gboolean fu_udev_device_ioctl(FuUdevDevice *self, gulong request, guint8 *buf, gint *rc, GError **error) G_GNUC_WARN_UNUSED_RESULT; gboolean +fu_udev_device_ioctl_full(FuUdevDevice *self, + gulong request, + guint8 *buf, + gint *rc, + guint timeout, + GError **error) G_GNUC_WARN_UNUSED_RESULT; +gboolean fu_udev_device_pwrite(FuUdevDevice *self, goffset port, guint8 data, GError **error) G_GNUC_WARN_UNUSED_RESULT; gboolean diff --git a/libfwupdplugin/fwupdplugin.map b/libfwupdplugin/fwupdplugin.map index 47250c498..5e425dba1 100644 --- a/libfwupdplugin/fwupdplugin.map +++ b/libfwupdplugin/fwupdplugin.map @@ -1034,3 +1034,9 @@ LIBFWUPDPLUGIN_1.8.0 { fu_uswid_firmware_new; local: *; } LIBFWUPDPLUGIN_1.7.7; + +LIBFWUPDPLUGIN_1.8.1 { + global: + fu_udev_device_ioctl_full; + local: *; +} LIBFWUPDPLUGIN_1.8.0;