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
* @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

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_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

View File

@ -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;