diff --git a/include/libzfs.h b/include/libzfs.h index 55dd34c99de1..cb78f1d62931 100644 --- a/include/libzfs.h +++ b/include/libzfs.h @@ -410,6 +410,7 @@ extern int zpool_history_unpack(char *, uint64_t, uint64_t *, nvlist_t ***, uint_t *); extern int zpool_events_next(libzfs_handle_t *, nvlist_t **, int *, int, int); extern int zpool_events_clear(libzfs_handle_t *, int *); +extern int zpool_events_seek(libzfs_handle_t *, uint64_t, int); extern void zpool_obj_to_path(zpool_handle_t *, uint64_t, uint64_t, char *, size_t len); extern int zfs_ioctl(libzfs_handle_t *, int, struct zfs_cmd *); diff --git a/include/sys/fm/util.h b/include/sys/fm/util.h index 9dfd436c1b43..18fe49073239 100644 --- a/include/sys/fm/util.h +++ b/include/sys/fm/util.h @@ -71,7 +71,7 @@ typedef struct erpt_dump { #ifdef _KERNEL -#define ZEVENT_SHUTDOWN 0x1 +#define ZEVENT_SHUTDOWN 0x1 typedef void zevent_cb_t(nvlist_t *, nvlist_t *); @@ -99,6 +99,7 @@ extern int zfs_zevent_fd_hold(int, minor_t *, zfs_zevent_t **); extern void zfs_zevent_fd_rele(int); extern int zfs_zevent_next(zfs_zevent_t *, nvlist_t **, uint64_t *, uint64_t *); extern int zfs_zevent_wait(zfs_zevent_t *); +extern int zfs_zevent_seek(zfs_zevent_t *, uint64_t); extern void zfs_zevent_init(zfs_zevent_t **); extern void zfs_zevent_destroy(zfs_zevent_t *); diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index 882e9e84ab85..ae72f834de52 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -864,6 +864,7 @@ typedef enum zfs_ioc { ZFS_IOC_LINUX = ('Z' << 8) + 0x80, ZFS_IOC_EVENTS_NEXT, ZFS_IOC_EVENTS_CLEAR, + ZFS_IOC_EVENTS_SEEK, /* * FreeBSD - 1/64 numbers reserved. diff --git a/include/sys/zfs_ioctl.h b/include/sys/zfs_ioctl.h index dad611328cbc..c63b16c78d32 100644 --- a/include/sys/zfs_ioctl.h +++ b/include/sys/zfs_ioctl.h @@ -262,6 +262,9 @@ typedef struct zinject_record { #define ZEVENT_NONBLOCK 0x1 #define ZEVENT_SIZE 1024 +#define ZEVENT_SEEK_START 0 +#define ZEVENT_SEEK_END UINT64_MAX + typedef enum zinject_type { ZINJECT_UNINITIALIZED, ZINJECT_DATA_FAULT, diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c index b822ace688c9..1b8f3b63a82a 100644 --- a/lib/libzfs/libzfs_pool.c +++ b/lib/libzfs/libzfs_pool.c @@ -3870,6 +3870,42 @@ zpool_events_clear(libzfs_handle_t *hdl, int *count) return (0); } +/* + * Seek to a specific EID, ZEVENT_SEEK_START, or ZEVENT_SEEK_END for + * the passed zevent_fd file handle. On success zero is returned, + * otherwise -1 is returned and hdl->libzfs_error is set to the errno. + */ +int +zpool_events_seek(libzfs_handle_t *hdl, uint64_t eid, int zevent_fd) +{ + zfs_cmd_t zc = {"\0"}; + int error = 0; + + zc.zc_guid = eid; + zc.zc_cleanup_fd = zevent_fd; + + if (zfs_ioctl(hdl, ZFS_IOC_EVENTS_SEEK, &zc) != 0) { + switch (errno) { + case ENOENT: + error = zfs_error_fmt(hdl, EZFS_NOENT, + dgettext(TEXT_DOMAIN, "cannot get event")); + break; + + case ENOMEM: + error = zfs_error_fmt(hdl, EZFS_NOMEM, + dgettext(TEXT_DOMAIN, "cannot get event")); + break; + + default: + error = zpool_standard_error_fmt(hdl, errno, + dgettext(TEXT_DOMAIN, "cannot get event")); + break; + } + } + + return (error); +} + void zpool_obj_to_path(zpool_handle_t *zhp, uint64_t dsobj, uint64_t obj, char *pathname, size_t len) diff --git a/module/zfs/fm.c b/module/zfs/fm.c index fe9223ff8186..246b3d2cf606 100644 --- a/module/zfs/fm.c +++ b/module/zfs/fm.c @@ -664,6 +664,67 @@ zfs_zevent_wait(zfs_zevent_t *ze) return (error); } +/* + * The caller may seek to a specific EID by passing that EID. If the EID + * is still available in the posted list of events the cursor is positioned + * there. Otherwise ENOENT is returned and the cursor is not moved. + * + * There are two reserved EIDs which may be passed and will never fail. + * ZEVENT_SEEK_START positions the cursor at the start of the list, and + * ZEVENT_SEEK_END positions the cursor at the end of the list. + */ +int +zfs_zevent_seek(zfs_zevent_t *ze, uint64_t eid) +{ + zevent_t *ev; + int error = 0; + + mutex_enter(&zevent_lock); + + if (eid == ZEVENT_SEEK_START) { + if (ze->ze_zevent) + list_remove(&ze->ze_zevent->ev_ze_list, ze); + + ze->ze_zevent = NULL; + goto out; + } + + if (eid == ZEVENT_SEEK_END) { + if (ze->ze_zevent) + list_remove(&ze->ze_zevent->ev_ze_list, ze); + + ev = list_head(&zevent_list); + if (ev) { + ze->ze_zevent = ev; + list_insert_head(&ev->ev_ze_list, ze); + } else { + ze->ze_zevent = NULL; + } + + goto out; + } + + for (ev = list_tail(&zevent_list); ev != NULL; + ev = list_prev(&zevent_list, ev)) { + if (ev->ev_eid == eid) { + if (ze->ze_zevent) + list_remove(&ze->ze_zevent->ev_ze_list, ze); + + ze->ze_zevent = ev; + list_insert_head(&ev->ev_ze_list, ze); + break; + } + } + + if (ev == NULL) + error = ENOENT; + +out: + mutex_exit(&zevent_lock); + + return (error); +} + void zfs_zevent_init(zfs_zevent_t **zep) { diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index 5951bc673cb3..cd47790166f5 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -4919,6 +4919,28 @@ zfs_ioc_events_clear(zfs_cmd_t *zc) return (0); } +/* + * inputs: + * zc_guid eid | ZEVENT_SEEK_START | ZEVENT_SEEK_END + * zc_cleanup zevent file descriptor + */ +static int +zfs_ioc_events_seek(zfs_cmd_t *zc) +{ + zfs_zevent_t *ze; + minor_t minor; + int error; + + error = zfs_zevent_fd_hold(zc->zc_cleanup_fd, &minor, &ze); + if (error != 0) + return (error); + + error = zfs_zevent_seek(ze, zc->zc_guid); + zfs_zevent_fd_rele(zc->zc_cleanup_fd); + + return (error); +} + /* * inputs: * zc_name name of new filesystem or snapshot @@ -5393,6 +5415,8 @@ zfs_ioctl_init(void) zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_NONE); zfs_ioctl_register_legacy(ZFS_IOC_EVENTS_CLEAR, zfs_ioc_events_clear, zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_NONE); + zfs_ioctl_register_legacy(ZFS_IOC_EVENTS_SEEK, zfs_ioc_events_seek, + zfs_secpolicy_config, NO_NAME, B_FALSE, POOL_CHECK_NONE); } int