aboutsummaryrefslogtreecommitdiffstats
path: root/module
diff options
context:
space:
mode:
authorBrian Behlendorf <[email protected]>2010-10-04 16:21:04 -0700
committerBrian Behlendorf <[email protected]>2010-10-12 14:55:03 -0700
commitbaa40d45cbb336765b2f89d934cd9ea690e4f7c9 (patch)
tree3beb455efbc97b3c014772c121b1dc9fe3a76e11 /module
parenta69052be7f9a4008e2b09578e9db5fdebc186111 (diff)
Fix missing 'zpool events'
It turns out that 'zpool events' over 1024 bytes in size where being silently dropped. This was discovered while writing the zfault.sh tests to validate common failure modes. This could occur because the zfs interface for passing an arbitrary size nvlist_t over an ioctl() is to provide a buffer for the packed nvlist which is usually big enough. In this case 1024 byte is the default. If the kernel determines the buffer is to small it returns ENOMEM and the minimum required size of the nvlist_t. This was working properly but in the case of 'zpool events' the event stream was advanced dispite the error. Thus the retry with the bigger buffer would succeed but it would skip over the previous event. The fix is to pass this size to zfs_zevent_next() and determine before removing the event from the list if it will fit. This was preferable to checking after the event was returned because this avoids the need to rewind the stream.
Diffstat (limited to 'module')
-rw-r--r--module/zfs/fm.c21
-rw-r--r--module/zfs/zfs_ioctl.c5
2 files changed, 20 insertions, 6 deletions
diff --git a/module/zfs/fm.c b/module/zfs/fm.c
index 67d0c1a6e..d409b2974 100644
--- a/module/zfs/fm.c
+++ b/module/zfs/fm.c
@@ -568,13 +568,18 @@ zfs_zevent_fd_rele(int fd)
}
/*
- * Get the next zevent in the stream and place a copy in 'event'.
+ * Get the next zevent in the stream and place a copy in 'event'. This
+ * may fail with ENOMEM if the encoded nvlist size exceeds the passed
+ * 'event_size'. In this case the stream pointer is not advanced and
+ * and 'event_size' is set to the minimum required buffer size.
*/
int
-zfs_zevent_next(zfs_zevent_t *ze, nvlist_t **event, uint64_t *dropped)
+zfs_zevent_next(zfs_zevent_t *ze, nvlist_t **event, uint64_t *event_size,
+ uint64_t *dropped)
{
zevent_t *ev;
- int error;
+ size_t size;
+ int error = 0;
mutex_enter(&zevent_lock);
if (ze->ze_zevent == NULL) {
@@ -592,10 +597,18 @@ zfs_zevent_next(zfs_zevent_t *ze, nvlist_t **event, uint64_t *dropped)
error = ENOENT;
goto out;
}
+ }
- list_remove(&ze->ze_zevent->ev_ze_list, ze);
+ VERIFY(nvlist_size(ev->ev_nvl, &size, NV_ENCODE_NATIVE) == 0);
+ if (size > *event_size) {
+ *event_size = size;
+ error = ENOMEM;
+ goto out;
}
+ 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);
nvlist_dup(ev->ev_nvl, event, KM_SLEEP);
diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c
index cc5e66f9a..45e118e53 100644
--- a/module/zfs/zfs_ioctl.c
+++ b/module/zfs/zfs_ioctl.c
@@ -4741,11 +4741,12 @@ zfs_ioc_events_next(zfs_cmd_t *zc)
return (error);
do {
- error = zfs_zevent_next(ze, &event, &dropped);
+ error = zfs_zevent_next(ze, &event,
+ &zc->zc_nvlist_dst_size, &dropped);
if (event != NULL) {
zc->zc_cookie = dropped;
error = put_nvlist(zc, event);
- nvlist_free(event);
+ nvlist_free(event);
}
if (zc->zc_guid & ZEVENT_NONBLOCK)