aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmd/zpool/zpool_main.c3
-rw-r--r--configure.ac1
-rw-r--r--include/libzfs.h2
-rw-r--r--include/spl/sys/kstat.h7
-rw-r--r--include/sys/spa.h3
-rw-r--r--lib/libspl/include/sys/kstat.h2
-rw-r--r--lib/libzfs/libzfs_pool.c46
-rw-r--r--lib/libzfs/libzfs_status.c12
-rw-r--r--module/spl/spl-kstat.c4
-rw-r--r--module/zfs/spa_misc.c40
-rw-r--r--module/zfs/spa_stats.c62
-rw-r--r--tests/runfiles/linux.run4
-rw-r--r--tests/zfs-tests/include/blkdev.shlib11
-rw-r--r--tests/zfs-tests/tests/functional/Makefile.am1
-rw-r--r--tests/zfs-tests/tests/functional/kstat/Makefile.am5
-rwxr-xr-xtests/zfs-tests/tests/functional/kstat/cleanup.ksh28
-rwxr-xr-xtests/zfs-tests/tests/functional/kstat/setup.ksh34
-rwxr-xr-xtests/zfs-tests/tests/functional/kstat/state.ksh144
18 files changed, 383 insertions, 26 deletions
diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c
index 39f379cb0..ad25a2f6e 100644
--- a/cmd/zpool/zpool_main.c
+++ b/cmd/zpool/zpool_main.c
@@ -6484,7 +6484,8 @@ status_callback(zpool_handle_t *zhp, void *data)
nvroot = fnvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE);
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);
diff --git a/configure.ac b/configure.ac
index 8e360f46e..b6fca99b4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -278,6 +278,7 @@ AC_CONFIG_FILES([
tests/zfs-tests/tests/functional/hkdf/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
diff --git a/include/libzfs.h b/include/libzfs.h
index 7ea6d7fa0..b98963158 100644
--- a/include/libzfs.h
+++ b/include/libzfs.h
@@ -301,6 +301,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
*/
diff --git a/include/spl/sys/kstat.h b/include/spl/sys/kstat.h
index 9170fe24e..f197ce455 100644
--- a/include/spl/sys/kstat.h
+++ b/include/spl/sys/kstat.h
@@ -69,10 +69,9 @@
#define KSTAT_FLAG_WRITABLE 0x04
#define KSTAT_FLAG_PERSISTENT 0x08
#define KSTAT_FLAG_DORMANT 0x10
-#define KSTAT_FLAG_UNSUPPORTED \
- (KSTAT_FLAG_VAR_SIZE | KSTAT_FLAG_WRITABLE | \
- KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_DORMANT)
-
+#define KSTAT_FLAG_INVALID 0x20
+#define KSTAT_FLAG_LONGSTRINGS 0x40
+#define KSTAT_FLAG_NO_HEADERS 0x80
#define KS_MAGIC 0x9d9d9d9d
diff --git a/include/sys/spa.h b/include/sys/spa.h
index 8a3938e86..e8578be9a 100644
--- a/include/sys/spa.h
+++ b/include/sys/spa.h
@@ -873,6 +873,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 {
@@ -1048,6 +1049,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, const zbookmark_phys_t *zb);
diff --git a/lib/libspl/include/sys/kstat.h b/lib/libspl/include/sys/kstat.h
index 24c71e27c..9bd0d949d 100644
--- a/lib/libspl/include/sys/kstat.h
+++ b/lib/libspl/include/sys/kstat.h
@@ -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
diff --git a/lib/libzfs/libzfs_pool.c b/lib/libzfs/libzfs_pool.c
index 3b16c012f..16b30d177 100644
--- a/lib/libzfs/libzfs_pool.c
+++ b/lib/libzfs/libzfs_pool.c
@@ -243,6 +243,38 @@ zpool_pool_state_to_name(pool_state_t state)
}
/*
+ * 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.
*/
@@ -253,9 +285,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) {
@@ -264,7 +293,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:
@@ -365,14 +394,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) {
diff --git a/lib/libzfs/libzfs_status.c b/lib/libzfs/libzfs_status.c
index 57d2deabf..4089f0cc6 100644
--- a/lib/libzfs/libzfs_status.c
+++ b/lib/libzfs/libzfs_status.c
@@ -404,12 +404,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);
}
diff --git a/module/spl/spl-kstat.c b/module/spl/spl-kstat.c
index bcbff94a6..c3fc2e4b2 100644
--- a/module/spl/spl-kstat.c
+++ b/module/spl/spl-kstat.c
@@ -389,7 +389,8 @@ kstat_seq_start(struct seq_file *f, loff_t *pos)
ksp->ks_snaptime = gethrtime();
- if (!n && kstat_seq_show_headers(f))
+ if (!(ksp->ks_flags & KSTAT_FLAG_NO_HEADERS) && !n &&
+ kstat_seq_show_headers(f))
return (NULL);
if (n >= ksp->ks_ndata)
@@ -539,7 +540,6 @@ __kstat_create(const char *ks_module, int ks_instance, const char *ks_name,
ASSERT(ks_module);
ASSERT(ks_instance == 0);
ASSERT(ks_name);
- ASSERT(!(ks_flags & KSTAT_FLAG_UNSUPPORTED));
if ((ks_type == KSTAT_TYPE_INTR) || (ks_type == KSTAT_TYPE_IO))
ASSERT(ks_ndata == 1);
diff --git a/module/zfs/spa_misc.c b/module/zfs/spa_misc.c
index 288e5378b..6c7e2f55c 100644
--- a/module/zfs/spa_misc.c
+++ b/module/zfs/spa_misc.c
@@ -2254,6 +2254,45 @@ spa_set_missing_tvds(spa_t *spa, uint64_t missing)
spa->spa_missing_tvds = missing;
}
+/*
+ * 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)
#include <linux/mod_compat.h>
@@ -2406,6 +2445,7 @@ EXPORT_SYMBOL(spa_namespace_lock);
EXPORT_SYMBOL(spa_trust_config);
EXPORT_SYMBOL(spa_missing_tvds_allowed);
EXPORT_SYMBOL(spa_set_missing_tvds);
+EXPORT_SYMBOL(spa_state_to_name);
/* BEGIN CSTYLED */
module_param(zfs_flags, uint, 0644);
diff --git a/module/zfs/spa_stats.c b/module/zfs/spa_stats.c
index 3f137d9c7..fa1cf9e98 100644
--- a/module/zfs/spa_stats.c
+++ b/module/zfs/spa_stats.c
@@ -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.
@@ -997,6 +999,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)
{
@@ -1005,11 +1065,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);
diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run
index 0260eb884..b56bc2ebd 100644
--- a/tests/runfiles/linux.run
+++ b/tests/runfiles/linux.run
@@ -575,6 +575,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']
diff --git a/tests/zfs-tests/include/blkdev.shlib b/tests/zfs-tests/include/blkdev.shlib
index 87ffa8560..5163ea2ae 100644
--- a/tests/zfs-tests/include/blkdev.shlib
+++ b/tests/zfs-tests/include/blkdev.shlib
@@ -421,7 +421,16 @@ function unload_scsi_debug
#
function get_debug_device
{
- lsscsi | nawk '/scsi_debug/ {print $6; exit}' | cut -d / -f3
+ 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"
}
#
diff --git a/tests/zfs-tests/tests/functional/Makefile.am b/tests/zfs-tests/tests/functional/Makefile.am
index 396124986..2368b829b 100644
--- a/tests/zfs-tests/tests/functional/Makefile.am
+++ b/tests/zfs-tests/tests/functional/Makefile.am
@@ -28,6 +28,7 @@ SUBDIRS = \
hkdf \
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
index 000000000..8ad83ec3e
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/kstat/Makefile.am
@@ -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
index 000000000..8a212ce37
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/kstat/cleanup.ksh
@@ -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
index 000000000..57717a096
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/kstat/setup.ksh
@@ -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
index 000000000..bf0b6e313
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/kstat/state.ksh
@@ -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"