]> granicus.if.org Git - zfs/commitdiff
Add pool state /proc entry, "SUSPENDED" pools
authorTony Hutter <hutter2@llnl.gov>
Wed, 6 Jun 2018 16:33:54 +0000 (09:33 -0700)
committerTony Hutter <hutter2@llnl.gov>
Fri, 6 Jul 2018 09:46:51 +0000 (02:46 -0700)
1. Add a proc entry to display the pool's state:

$ cat /proc/spl/kstat/zfs/tank/state
ONLINE

This is done without using the spa config locks, so it will
never hang.

2. Fix 'zpool status' and 'zpool list -o health' output to print
"SUSPENDED" instead of "ONLINE" for suspended pools.

Reviewed-by: Olaf Faaland <faaland1@llnl.gov>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed by: Richard Elling <Richard.Elling@RichardElling.com>
Signed-off-by: Tony Hutter <hutter2@llnl.gov>
Closes #7331
Closes #7563

16 files changed:
cmd/zpool/zpool_main.c
configure.ac
include/libzfs.h
include/sys/spa.h
lib/libspl/include/sys/kstat.h
lib/libzfs/libzfs_pool.c
lib/libzfs/libzfs_status.c
module/zfs/spa_misc.c
module/zfs/spa_stats.c
tests/runfiles/linux.run
tests/zfs-tests/include/libtest.shlib
tests/zfs-tests/tests/functional/Makefile.am
tests/zfs-tests/tests/functional/kstat/Makefile.am [new file with mode: 0644]
tests/zfs-tests/tests/functional/kstat/cleanup.ksh [new file with mode: 0755]
tests/zfs-tests/tests/functional/kstat/setup.ksh [new file with mode: 0755]
tests/zfs-tests/tests/functional/kstat/state.ksh [new file with mode: 0755]

index b07569389f825196b15739ce81e83889b48dbcbc..97697011230f9f4e31c73630b323b20b7f1fe1cf 100644 (file)
@@ -6226,7 +6226,8 @@ status_callback(zpool_handle_t *zhp, void *data)
            &nvroot) == 0);
        verify(nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_VDEV_STATS,
            (uint64_t **)&vs, &c) == 0);
-       health = zpool_state_to_name(vs->vs_state, vs->vs_aux);
+
+       health = zpool_get_state_str(zhp);
 
        (void) printf(gettext("  pool: %s\n"), zpool_get_name(zhp));
        (void) printf(gettext(" state: %s\n"), health);
index 3f4925c3a521732e3d1009ca763d7061b9b19f5b..42cfc1a3d7a167051364e2973a9a36a3970e8c7e 100644 (file)
@@ -253,6 +253,7 @@ AC_CONFIG_FILES([
        tests/zfs-tests/tests/functional/history/Makefile
        tests/zfs-tests/tests/functional/inheritance/Makefile
        tests/zfs-tests/tests/functional/inuse/Makefile
+       tests/zfs-tests/tests/functional/kstat/Makefile
        tests/zfs-tests/tests/functional/large_files/Makefile
        tests/zfs-tests/tests/functional/largest_pool/Makefile
        tests/zfs-tests/tests/functional/link_count/Makefile
index 945bd5b86064cb33ca9d207c99ac229b03d8b32c..fea2fee4527716dbed67cb8d14e82c1ee7da0416 100644 (file)
@@ -296,6 +296,8 @@ int zfs_dev_is_whole_disk(char *dev_name);
 char *zfs_get_underlying_path(char *dev_name);
 char *zfs_get_enclosure_sysfs_path(char *dev_name);
 
+const char *zpool_get_state_str(zpool_handle_t *);
+
 /*
  * Functions to manage pool properties
  */
index 3b268419ab9cee55400cfa7e8aa17123cfd14caa..810999c95055e9932bc06b866d0a196ff1aed6aa 100644 (file)
@@ -730,6 +730,7 @@ typedef struct spa_stats {
        spa_stats_history_t     tx_assign_histogram;
        spa_stats_history_t     io_history;
        spa_stats_history_t     mmp_history;
+       spa_stats_history_t     state;          /* pool state */
 } spa_stats_t;
 
 typedef enum txg_state {
@@ -889,6 +890,8 @@ extern void spa_history_log_internal_ds(struct dsl_dataset *ds, const char *op,
 extern void spa_history_log_internal_dd(dsl_dir_t *dd, const char *operation,
     dmu_tx_t *tx, const char *fmt, ...);
 
+extern const char *spa_state_to_name(spa_t *spa);
+
 /* error handling */
 struct zbookmark_phys;
 extern void spa_log_error(spa_t *spa, zio_t *zio);
index fcd3ed98b3826abeebe0d3e884c2a59c8b517377..84c3d7ca2e7176b92dcc6bbdee8a7e8fc71ba2e9 100644 (file)
@@ -304,6 +304,8 @@ typedef struct kstat32 {
 #define        KSTAT_FLAG_PERSISTENT           0x08
 #define        KSTAT_FLAG_DORMANT              0x10
 #define        KSTAT_FLAG_INVALID              0x20
+#define        KSTAT_FLAG_LONGSTRINGS          0x40
+#define        KSTAT_FLAG_NO_HEADERS           0x80
 
 /*
  * Dynamic update support
index 53bc503407760e4b90c7093d8612f896681f8067..315ba954caa29a78cdf2145b3226eb0ef4a99e51 100644 (file)
@@ -239,6 +239,38 @@ zpool_pool_state_to_name(pool_state_t state)
        return (gettext("UNKNOWN"));
 }
 
+/*
+ * Given a pool handle, return the pool health string ("ONLINE", "DEGRADED",
+ * "SUSPENDED", etc).
+ */
+const char *
+zpool_get_state_str(zpool_handle_t *zhp)
+{
+       zpool_errata_t errata;
+       zpool_status_t status;
+       nvlist_t *nvroot;
+       vdev_stat_t *vs;
+       uint_t vsc;
+       const char *str;
+
+       status = zpool_get_status(zhp, NULL, &errata);
+
+       if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
+               str = gettext("FAULTED");
+       } else if (status == ZPOOL_STATUS_IO_FAILURE_WAIT ||
+           status == ZPOOL_STATUS_IO_FAILURE_MMP) {
+               str = gettext("SUSPENDED");
+       } else {
+               verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
+                   ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
+               verify(nvlist_lookup_uint64_array(nvroot,
+                   ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &vsc)
+                   == 0);
+               str = zpool_state_to_name(vs->vs_state, vs->vs_aux);
+       }
+       return (str);
+}
+
 /*
  * Get a zpool property value for 'prop' and return the value in
  * a pre-allocated buffer.
@@ -250,9 +282,6 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
        uint64_t intval;
        const char *strval;
        zprop_source_t src = ZPROP_SRC_NONE;
-       nvlist_t *nvroot;
-       vdev_stat_t *vs;
-       uint_t vsc;
 
        if (zpool_get_state(zhp) == POOL_STATE_UNAVAIL) {
                switch (prop) {
@@ -261,7 +290,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
                        break;
 
                case ZPOOL_PROP_HEALTH:
-                       (void) strlcpy(buf, "FAULTED", len);
+                       (void) strlcpy(buf, zpool_get_state_str(zhp), len);
                        break;
 
                case ZPOOL_PROP_GUID:
@@ -362,14 +391,7 @@ zpool_get_prop(zpool_handle_t *zhp, zpool_prop_t prop, char *buf,
                        break;
 
                case ZPOOL_PROP_HEALTH:
-                       verify(nvlist_lookup_nvlist(zpool_get_config(zhp, NULL),
-                           ZPOOL_CONFIG_VDEV_TREE, &nvroot) == 0);
-                       verify(nvlist_lookup_uint64_array(nvroot,
-                           ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &vsc)
-                           == 0);
-
-                       (void) strlcpy(buf, zpool_state_to_name(intval,
-                           vs->vs_aux), len);
+                       (void) strlcpy(buf, zpool_get_state_str(zhp), len);
                        break;
                case ZPOOL_PROP_VERSION:
                        if (intval >= SPA_VERSION_FEATURES) {
index 6cdcd38272fd6032a4fc977088defd721ce4a735..5e423f3afeb20a0e27515ac2274ebc7523e23dbd 100644 (file)
@@ -403,12 +403,12 @@ zpool_status_t
 zpool_get_status(zpool_handle_t *zhp, char **msgid, zpool_errata_t *errata)
 {
        zpool_status_t ret = check_status(zhp->zpool_config, B_FALSE, errata);
-
-       if (ret >= NMSGID)
-               *msgid = NULL;
-       else
-               *msgid = zfs_msgid_table[ret];
-
+       if (msgid != NULL) {
+               if (ret >= NMSGID)
+                       *msgid = NULL;
+               else
+                       *msgid = zfs_msgid_table[ret];
+       }
        return (ret);
 }
 
index e92c39482455f211e3829cce7798e78dff6391d6..cc1c641d771989ebd18d0f7ed04c0fa4cc21ee27 100644 (file)
@@ -2100,6 +2100,45 @@ spa_get_hostid(void)
        return (myhostid);
 }
 
+/*
+ * Return the pool state string ("ONLINE", "DEGRADED", "SUSPENDED", etc).
+ */
+const char *
+spa_state_to_name(spa_t *spa)
+{
+       vdev_state_t state = spa->spa_root_vdev->vdev_state;
+       vdev_aux_t aux = spa->spa_root_vdev->vdev_stat.vs_aux;
+
+       if (spa_suspended(spa) &&
+           (spa_get_failmode(spa) != ZIO_FAILURE_MODE_CONTINUE))
+               return ("SUSPENDED");
+
+       switch (state) {
+       case VDEV_STATE_CLOSED:
+       case VDEV_STATE_OFFLINE:
+               return ("OFFLINE");
+       case VDEV_STATE_REMOVED:
+               return ("REMOVED");
+       case VDEV_STATE_CANT_OPEN:
+               if (aux == VDEV_AUX_CORRUPT_DATA || aux == VDEV_AUX_BAD_LOG)
+                       return ("FAULTED");
+               else if (aux == VDEV_AUX_SPLIT_POOL)
+                       return ("SPLIT");
+               else
+                       return ("UNAVAIL");
+       case VDEV_STATE_FAULTED:
+               return ("FAULTED");
+       case VDEV_STATE_DEGRADED:
+               return ("DEGRADED");
+       case VDEV_STATE_HEALTHY:
+               return ("ONLINE");
+       default:
+               break;
+       }
+
+       return ("UNKNOWN");
+}
+
 #if defined(_KERNEL) && defined(HAVE_SPL)
 /* Namespace manipulation */
 EXPORT_SYMBOL(spa_lookup);
@@ -2178,6 +2217,7 @@ EXPORT_SYMBOL(spa_is_root);
 EXPORT_SYMBOL(spa_writeable);
 EXPORT_SYMBOL(spa_mode);
 EXPORT_SYMBOL(spa_namespace_lock);
+EXPORT_SYMBOL(spa_state_to_name);
 
 /* BEGIN CSTYLED */
 module_param(zfs_flags, uint, 0644);
index 8950d9c5494be2e256db16e44b98c5e7a9797bd5..ca3d0be7b40953c4b5a0162ff94624d59befa456 100644 (file)
@@ -22,6 +22,8 @@
 #include <sys/zfs_context.h>
 #include <sys/spa_impl.h>
 #include <sys/vdev_impl.h>
+#include <sys/spa.h>
+#include <zfs_comutil.h>
 
 /*
  * Keeps stats on last N reads per spa_t, disabled by default.
@@ -992,6 +994,64 @@ spa_mmp_history_add(spa_t *spa, uint64_t txg, uint64_t timestamp,
        return ((void *)smh);
 }
 
+static void *
+spa_state_addr(kstat_t *ksp, loff_t n)
+{
+       return (ksp->ks_private);       /* return the spa_t */
+}
+
+static int
+spa_state_data(char *buf, size_t size, void *data)
+{
+       spa_t *spa = (spa_t *)data;
+       (void) snprintf(buf, size, "%s\n", spa_state_to_name(spa));
+       return (0);
+}
+
+/*
+ * Return the state of the pool in /proc/spl/kstat/zfs/<pool>/state.
+ *
+ * This is a lock-less read of the pool's state (unlike using 'zpool', which
+ * can potentially block for seconds).  Because it doesn't block, it can useful
+ * as a pool heartbeat value.
+ */
+static void
+spa_state_init(spa_t *spa)
+{
+       spa_stats_history_t *ssh = &spa->spa_stats.state;
+       char *name;
+       kstat_t *ksp;
+
+       mutex_init(&ssh->lock, NULL, MUTEX_DEFAULT, NULL);
+
+       name = kmem_asprintf("zfs/%s", spa_name(spa));
+       ksp = kstat_create(name, 0, "state", "misc",
+           KSTAT_TYPE_RAW, 0, KSTAT_FLAG_VIRTUAL);
+
+       ssh->kstat = ksp;
+       if (ksp) {
+               ksp->ks_lock = &ssh->lock;
+               ksp->ks_data = NULL;
+               ksp->ks_private = spa;
+               ksp->ks_flags |= KSTAT_FLAG_NO_HEADERS;
+               kstat_set_raw_ops(ksp, NULL, spa_state_data, spa_state_addr);
+               kstat_install(ksp);
+       }
+
+       strfree(name);
+}
+
+static void
+spa_health_destroy(spa_t *spa)
+{
+       spa_stats_history_t *ssh = &spa->spa_stats.state;
+       kstat_t *ksp = ssh->kstat;
+       if (ksp)
+               kstat_delete(ksp);
+
+       mutex_destroy(&ssh->lock);
+}
+
 void
 spa_stats_init(spa_t *spa)
 {
@@ -1000,11 +1060,13 @@ spa_stats_init(spa_t *spa)
        spa_tx_assign_init(spa);
        spa_io_history_init(spa);
        spa_mmp_history_init(spa);
+       spa_state_init(spa);
 }
 
 void
 spa_stats_destroy(spa_t *spa)
 {
+       spa_health_destroy(spa);
        spa_tx_assign_destroy(spa);
        spa_txg_history_destroy(spa);
        spa_read_history_destroy(spa);
index 379c9f7358757c7d28beb3373a062a90d3a9fc54..69e9eb267dfc5bef5826b733c4c1b4441911ae2e 100644 (file)
@@ -467,6 +467,10 @@ tests = ['inuse_001_pos', 'inuse_003_pos', 'inuse_004_pos',
 post =
 tags = ['functional', 'inuse']
 
+[tests/functional/kstat]
+tests = ['state']
+tags = ['functional', 'kstat']
+
 [tests/functional/large_files]
 tests = ['large_files_001_pos', 'large_files_002_pos']
 tags = ['functional', 'large_files']
index 13c859127491c9e3a3bd637da3f1d3d63dd126ec..86dae6ead3220e41dbe6717ceb73afe5e62fd89a 100644 (file)
@@ -26,6 +26,7 @@
 # Copyright 2016 Nexenta Systems, Inc.
 # Copyright (c) 2017 Lawrence Livermore National Security, LLC.
 # Copyright (c) 2017 Datto Inc.
+# Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
 #
 
 . ${STF_TOOLS}/include/logapi.shlib
@@ -3718,3 +3719,40 @@ function get_pool_devices #testpool #devdir
        fi
        echo $out
 }
+
+#
+# Get scsi_debug device name.
+# Returns basename of scsi_debug device (for example "sdb").
+#
+function get_debug_device
+{
+       for i in {1..10} ; do
+               val=$(lsscsi | nawk '/scsi_debug/ {print $6; exit}' | cut -d / -f3)
+
+               # lsscsi can take time to settle
+               if [ "$val" != "-" ] ; then
+                       break
+               fi
+               sleep 1
+       done
+       echo "$val"
+}
+
+#
+# Returns SCSI host number for the given disk
+#
+function get_scsi_host #disk
+{
+       typeset disk=$1
+       ls /sys/block/${disk}/device/scsi_device | cut -d : -f 1
+}
+
+#
+# Simulate disk removal
+#
+function remove_disk #disk
+{
+       typeset disk=$1
+       on_off_disk $disk "offline"
+       block_device_wait
+}
index ea52205a210b984bac2cb45a365fa6ec8aa9b564..bbbf3ba08058081f8b28a509d04a458a391a6f1e 100644 (file)
@@ -24,6 +24,7 @@ SUBDIRS = \
        history \
        inheritance \
        inuse \
+       kstat \
        large_files \
        largest_pool \
        libzfs \
diff --git a/tests/zfs-tests/tests/functional/kstat/Makefile.am b/tests/zfs-tests/tests/functional/kstat/Makefile.am
new file mode 100644 (file)
index 0000000..8ad83ec
--- /dev/null
@@ -0,0 +1,5 @@
+pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/kstat
+dist_pkgdata_SCRIPTS = \
+       setup.ksh \
+       cleanup.ksh \
+       state.ksh
diff --git a/tests/zfs-tests/tests/functional/kstat/cleanup.ksh b/tests/zfs-tests/tests/functional/kstat/cleanup.ksh
new file mode 100755 (executable)
index 0000000..8a212ce
--- /dev/null
@@ -0,0 +1,28 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2018 by Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+default_cleanup
diff --git a/tests/zfs-tests/tests/functional/kstat/setup.ksh b/tests/zfs-tests/tests/functional/kstat/setup.ksh
new file mode 100755 (executable)
index 0000000..57717a0
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2018 by Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+if ! is_linux ; then
+       log_unsupported "/proc/spl/kstat/<pool>/health only supported on Linux"
+fi
+
+default_mirror_setup $DISKS
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/kstat/state.ksh b/tests/zfs-tests/tests/functional/kstat/state.ksh
new file mode 100755 (executable)
index 0000000..bf0b6e3
--- /dev/null
@@ -0,0 +1,144 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+
+#
+# Copyright (c) 2018 by Lawrence Livermore National Security, LLC.
+#
+
+#
+# DESCRIPTION:
+# Test /proc/spl/kstat/zfs/<pool>/state kstat
+#
+# STRATEGY:
+# 1. Create a mirrored pool
+# 2. Check that pool is ONLINE
+# 3. Fault one disk
+# 4. Check that pool is DEGRADED
+# 5. Create a new pool with a single scsi_debug disk
+# 6. Remove the disk
+# 7. Check that pool is SUSPENDED
+# 8. Add the disk back in
+# 9. Clear errors and destroy the pools
+
+. $STF_SUITE/include/libtest.shlib
+
+verify_runnable "both"
+
+function cleanup
+{
+       # Destroy the scsi_debug pool
+       if [ -n "$TESTPOOL2" ] ; then
+               if  [ -n "$host" ] ; then
+                       # Re-enable the disk
+                       scan_scsi_hosts $host
+
+                       # Device may have changed names after being inserted
+                       SDISK=$(get_debug_device)
+                       log_must ln $DEV_RDSKDIR/$SDISK $REALDISK
+               fi
+
+               # Restore our working pool image
+               if [ -n "$BACKUP" ] ; then
+                       gunzip -c $BACKUP > $REALDISK
+                       log_must rm -f $BACKUP
+               fi
+
+               # Our disk is back.  Now we can clear errors and destroy the
+               # pool cleanly.
+               log_must zpool clear $TESTPOOL2
+
+               # Now that the disk is back and errors cleared, wait for our
+               # hung 'zpool scrub' to finish.
+               wait
+
+               destroy_pool $TESTPOOL2
+               log_must rm $REALDISK
+               unload_scsi_debug
+       fi
+}
+
+# Check that our pool state values match what's expected
+#
+# $1: pool name
+# $2: expected state ("ONLINE", "DEGRADED", "SUSPENDED", etc)
+function check_all
+{
+       pool=$1
+       expected=$2
+
+       state1=$(zpool status $pool | awk '/state: /{print $2}');
+       state2=$(zpool list -H -o health $pool)
+       state3=$(cat /proc/spl/kstat/zfs/$pool/state)
+       log_note "Checking $expected = $state1 = $state2 = $state3"
+       if [[ "$expected" == "$state1" &&  "$expected" == "$state2" && \
+           "$expected" == "$state3" ]] ; then
+               true
+       else
+               false
+       fi
+}
+
+log_onexit cleanup
+
+log_assert "Testing /proc/spl/kstat/zfs/<pool>/state kstat"
+
+# Test that the initial pool is healthy
+check_all $TESTPOOL "ONLINE"
+
+# Fault one of the disks, and check that pool is degraded
+DISK1=$(echo "$DISKS" | awk '{print $2}')
+zpool offline -tf $TESTPOOL $DISK1
+check_all $TESTPOOL "DEGRADED"
+
+# Create a new pool out of a scsi_debug disk
+TESTPOOL2=testpool2
+MINVDEVSIZE_MB=$((MINVDEVSIZE / 1048576))
+load_scsi_debug $MINVDEVSIZE_MB 1 1 1 '512b'
+
+SDISK=$(get_debug_device)
+host=$(get_scsi_host $SDISK)
+
+# Use $REALDISK instead of $SDISK in our pool because $SDISK can change names
+# as we remove/add the disk (i.e. /dev/sdf -> /dev/sdg).
+REALDISK=/dev/kstat-state-realdisk
+log_must [ ! -e $REALDISK ]
+ln $DEV_RDSKDIR/$SDISK $REALDISK
+
+log_must zpool create $TESTPOOL2 $REALDISK
+
+# Backup the contents of the disk image
+BACKUP=/tmp/kstat-state-realdisk.gz
+log_must [ ! -e $BACKUP ]
+gzip -c $REALDISK > $BACKUP
+
+# Yank out the disk from under the pool
+log_must rm $REALDISK
+remove_disk $SDISK
+
+# Run a 'zpool scrub' in the background to suspend the pool.  We run it in the
+# background since the command will hang when the pool gets suspended.  The
+# command will resume and exit after we restore the missing disk later on.
+zpool scrub $TESTPOOL2 &
+sleep 1                # Give the scrub some time to run before we check if it fails
+
+log_must check_all $TESTPOOL2 "SUSPENDED"
+
+log_pass "/proc/spl/kstat/zfs/<pool>/state test successful"