]> granicus.if.org Git - zfs/commitdiff
Fix missing 'zpool events'
authorBrian Behlendorf <behlendorf1@llnl.gov>
Mon, 4 Oct 2010 23:21:04 +0000 (16:21 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Tue, 12 Oct 2010 21:55:03 +0000 (14:55 -0700)
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.

include/sys/fm/util.h
module/zfs/fm.c
module/zfs/zfs_ioctl.c

index 94947d67c6dd574ef87d6be204334bb993cb08a2..a3a8c3f86935c587c79943ba77dd433f14ef491a 100644 (file)
@@ -96,7 +96,7 @@ extern void zfs_zevent_post(nvlist_t *, nvlist_t *, zevent_cb_t *);
 extern void zfs_zevent_drain_all(int *);
 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 *);
+extern int zfs_zevent_next(zfs_zevent_t *, nvlist_t **, uint64_t *, uint64_t *);
 extern int zfs_zevent_wait(zfs_zevent_t *);
 extern void zfs_zevent_init(zfs_zevent_t **);
 extern void zfs_zevent_destroy(zfs_zevent_t *);
index 67d0c1a6e4cdfcab2afcf2108042b58cea419db1..d409b2974ace52bd734bb04492b7b43a31b9b665 100644 (file)
@@ -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);
index cc5e66f9a05ab0ab34a4433e591b1db2cde4588a..45e118e537396fc05ea0433ca148bfd4ea50adc0 100644 (file)
@@ -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)