Retry the device ioctl for EAGAIN

This commit is contained in:
Richard Hughes 2022-04-28 11:42:08 +01:00
parent e803ed30f5
commit 2fcdf352df
3 changed files with 53 additions and 4 deletions

View File

@ -1375,25 +1375,32 @@ fu_udev_device_close(FuDevice *device, GError **error)
} }
/** /**
* fu_udev_device_ioctl: * fu_udev_device_ioctl_full:
* @self: a #FuUdevDevice * @self: a #FuUdevDevice
* @request: request number * @request: request number
* @buf: a buffer to use, which *must* be large enough for the request * @buf: a buffer to use, which *must* be large enough for the request
* @rc: (out) (nullable): the raw return value from the ioctl * @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 * @error: (nullable): optional return location for an error
* *
* Control a device using a low-level request. * Control a device using a low-level request.
* *
* Returns: %TRUE for success * Returns: %TRUE for success
* *
* Since: 1.3.3 * Since: 1.8.1
**/ **/
gboolean 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 #ifdef HAVE_IOCTL_H
FuUdevDevicePrivate *priv = GET_PRIVATE(self); FuUdevDevicePrivate *priv = GET_PRIVATE(self);
gint rc_tmp; 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(FU_IS_UDEV_DEVICE(self), FALSE);
g_return_val_if_fail(request != 0x0, 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; 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) if (rc != NULL)
*rc = rc_tmp; *rc = rc_tmp;
if (rc_tmp < 0) { if (rc_tmp < 0) {
@ -1443,6 +1457,26 @@ fu_udev_device_ioctl(FuUdevDevice *self, gulong request, guint8 *buf, gint *rc,
#endif #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: * fu_udev_device_pread_full:
* @self: a #FuUdevDevice * @self: a #FuUdevDevice

View File

@ -34,6 +34,7 @@ struct _FuUdevDeviceClass {
* @FU_UDEV_DEVICE_FLAG_USE_CONFIG: Read and write from the device config * @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_NONBLOCK: Open nonblocking, e.g. O_NONBLOCK
* @FU_UDEV_DEVICE_FLAG_OPEN_SYNC: Open sync, e.g. O_SYNC * @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(). * 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_USE_CONFIG = 1 << 3,
FU_UDEV_DEVICE_FLAG_OPEN_NONBLOCK = 1 << 4, FU_UDEV_DEVICE_FLAG_OPEN_NONBLOCK = 1 << 4,
FU_UDEV_DEVICE_FLAG_OPEN_SYNC = 1 << 5, FU_UDEV_DEVICE_FLAG_OPEN_SYNC = 1 << 5,
FU_UDEV_DEVICE_FLAG_IOCTL_RETRY = 1 << 6,
/*< private >*/ /*< private >*/
FU_UDEV_DEVICE_FLAG_LAST FU_UDEV_DEVICE_FLAG_LAST
} FuUdevDeviceFlags; } FuUdevDeviceFlags;
@ -102,6 +104,13 @@ gboolean
fu_udev_device_ioctl(FuUdevDevice *self, gulong request, guint8 *buf, gint *rc, GError **error) fu_udev_device_ioctl(FuUdevDevice *self, gulong request, guint8 *buf, gint *rc, GError **error)
G_GNUC_WARN_UNUSED_RESULT; G_GNUC_WARN_UNUSED_RESULT;
gboolean 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) fu_udev_device_pwrite(FuUdevDevice *self, goffset port, guint8 data, GError **error)
G_GNUC_WARN_UNUSED_RESULT; G_GNUC_WARN_UNUSED_RESULT;
gboolean gboolean

View File

@ -1034,3 +1034,9 @@ LIBFWUPDPLUGIN_1.8.0 {
fu_uswid_firmware_new; fu_uswid_firmware_new;
local: *; local: *;
} LIBFWUPDPLUGIN_1.7.7; } LIBFWUPDPLUGIN_1.7.7;
LIBFWUPDPLUGIN_1.8.1 {
global:
fu_udev_device_ioctl_full;
local: *;
} LIBFWUPDPLUGIN_1.8.0;