aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--include/sys/Makefile.am1
-rw-r--r--include/sys/zfs_sysfs.h47
-rw-r--r--include/zfeature_common.h2
-rw-r--r--include/zfs_prop.h1
-rw-r--r--lib/libzfs/libzfs_util.c16
-rw-r--r--lib/libzpool/Makefile.am2
-rw-r--r--module/zcommon/zfeature_common.c51
-rw-r--r--module/zcommon/zfs_prop.c3
-rw-r--r--module/zcommon/zprop_common.c32
-rw-r--r--module/zfs/Makefile.in1
-rw-r--r--module/zfs/zfs_ioctl.c4
-rw-r--r--module/zfs/zfs_sysfs.c617
-rw-r--r--tests/runfiles/linux.run6
-rw-r--r--tests/zfs-tests/tests/functional/cli_root/Makefile.am1
-rw-r--r--tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/Makefile.am10
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_sysfs/cleanup.ksh30
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_sysfs/setup.ksh32
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfeature_set_unsupported.ksh54
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfs_get_unsupported.ksh53
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfs_set_unsupported.ksh54
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfs_sysfs_live.ksh57
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zpool_get_unsupported.ksh55
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zpool_set_unsupported.ksh54
24 files changed, 1183 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac
index dbc063fd0..3e6dcf1c3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -227,6 +227,7 @@ AC_CONFIG_FILES([
tests/zfs-tests/tests/functional/cli_root/zfs_set/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_share/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_snapshot/Makefile
+ tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_unload-key/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_unmount/Makefile
tests/zfs-tests/tests/functional/cli_root/zfs_unshare/Makefile
diff --git a/include/sys/Makefile.am b/include/sys/Makefile.am
index f246c111d..644ddaab3 100644
--- a/include/sys/Makefile.am
+++ b/include/sys/Makefile.am
@@ -119,6 +119,7 @@ COMMON_H = \
$(top_srcdir)/include/sys/zfs_rlock.h \
$(top_srcdir)/include/sys/zfs_sa.h \
$(top_srcdir)/include/sys/zfs_stat.h \
+ $(top_srcdir)/include/sys/zfs_sysfs.h \
$(top_srcdir)/include/sys/zfs_vfsops.h \
$(top_srcdir)/include/sys/zfs_vnops.h \
$(top_srcdir)/include/sys/zfs_znode.h \
diff --git a/include/sys/zfs_sysfs.h b/include/sys/zfs_sysfs.h
new file mode 100644
index 000000000..7c4a0304f
--- /dev/null
+++ b/include/sys/zfs_sysfs.h
@@ -0,0 +1,47 @@
+/*
+ * 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 Delphix. All rights reserved.
+ */
+
+#ifndef _SYS_ZFS_SYSFS_H
+#define _SYS_ZFS_SYSFS_H
+
+#ifdef _KERNEL
+
+void zfs_sysfs_init(void);
+void zfs_sysfs_fini(void);
+
+#else
+
+#define zfs_sysfs_init()
+#define zfs_sysfs_fini()
+
+#endif
+
+#define ZFS_SYSFS_POOL_PROPERTIES "properties.pool"
+#define ZFS_SYSFS_DATASET_PROPERTIES "properties.dataset"
+#define ZFS_SYSFS_KERNEL_FEATURES "features.kernel"
+#define ZFS_SYSFS_POOL_FEATURES "features.pool"
+
+#define ZFS_SYSFS_DIR "/sys/module/zfs"
+
+#endif /* _SYS_ZFS_SYSFS_H */
diff --git a/include/zfeature_common.h b/include/zfeature_common.h
index c5aabce0e..8d31309e8 100644
--- a/include/zfeature_common.h
+++ b/include/zfeature_common.h
@@ -85,6 +85,7 @@ typedef struct zfeature_info {
const char *fi_guid; /* On-disk feature identifier */
const char *fi_desc; /* Feature description */
zfeature_flags_t fi_flags;
+ boolean_t fi_zfs_mod_supported; /* supported by running zfs module */
/* array of dependencies, terminated by SPA_FEATURE_NONE */
const spa_feature_t *fi_depends;
} zfeature_info_t;
@@ -98,6 +99,7 @@ extern zfeature_info_t spa_feature_table[SPA_FEATURES];
extern boolean_t zfeature_is_valid_guid(const char *);
extern boolean_t zfeature_is_supported(const char *);
+extern int zfeature_lookup_guid(const char *, spa_feature_t *);
extern int zfeature_lookup_name(const char *, spa_feature_t *);
extern boolean_t zfeature_depends_on(spa_feature_t, spa_feature_t);
diff --git a/include/zfs_prop.h b/include/zfs_prop.h
index 60e08552a..89b6a2024 100644
--- a/include/zfs_prop.h
+++ b/include/zfs_prop.h
@@ -78,6 +78,7 @@ typedef struct {
boolean_t pd_rightalign; /* column alignment for "zfs list" */
boolean_t pd_visible; /* do we list this property with the */
/* "zfs get" help message */
+ boolean_t pd_zfs_mod_supported; /* supported by running zfs module */
const zprop_index_t *pd_table; /* for index properties, a table */
/* defining the possible values */
size_t pd_table_size; /* number of entries in pd_table[] */
diff --git a/lib/libzfs/libzfs_util.c b/lib/libzfs/libzfs_util.c
index a19b34415..d18b582c6 100644
--- a/lib/libzfs/libzfs_util.c
+++ b/lib/libzfs/libzfs_util.c
@@ -1091,6 +1091,22 @@ libzfs_init(void)
hdl->libzfs_prop_debug = B_TRUE;
}
+ /*
+ * For testing, remove some settable properties and features
+ */
+ if (libzfs_envvar_is_set("ZFS_SYSFS_PROP_SUPPORT_TEST")) {
+ zprop_desc_t *proptbl;
+
+ proptbl = zpool_prop_get_table();
+ proptbl[ZPOOL_PROP_COMMENT].pd_zfs_mod_supported = B_FALSE;
+
+ proptbl = zfs_prop_get_table();
+ proptbl[ZFS_PROP_DNODESIZE].pd_zfs_mod_supported = B_FALSE;
+
+ zfeature_info_t *ftbl = spa_feature_table;
+ ftbl[SPA_FEATURE_LARGE_BLOCKS].fi_zfs_mod_supported = B_FALSE;
+ }
+
return (hdl);
}
diff --git a/lib/libzpool/Makefile.am b/lib/libzpool/Makefile.am
index 58e3b6eec..149706a27 100644
--- a/lib/libzpool/Makefile.am
+++ b/lib/libzpool/Makefile.am
@@ -12,6 +12,8 @@ AM_CFLAGS += $(NO_UNUSED_BUT_SET_VARIABLE)
# Includes kernel code generate warnings for large stack frames
AM_CFLAGS += $(FRAME_LARGER_THAN)
+AM_CFLAGS += -DLIB_ZPOOL_BUILD
+
DEFAULT_INCLUDES += \
-I$(top_srcdir)/include \
-I$(top_srcdir)/lib/libspl/include
diff --git a/module/zcommon/zfeature_common.c b/module/zcommon/zfeature_common.c
index f5c98933c..e2b0fd511 100644
--- a/module/zcommon/zfeature_common.c
+++ b/module/zcommon/zfeature_common.c
@@ -29,11 +29,13 @@
#ifndef _KERNEL
#include <errno.h>
#include <string.h>
+#include <sys/stat.h>
#endif
#include <sys/debug.h>
#include <sys/fs/zfs.h>
#include <sys/inttypes.h>
#include <sys/types.h>
+#include <sys/zfs_sysfs.h>
#include "zfeature_common.h"
/*
@@ -101,10 +103,29 @@ zfeature_is_supported(const char *guid)
}
int
+zfeature_lookup_guid(const char *guid, spa_feature_t *res)
+{
+ for (spa_feature_t i = 0; i < SPA_FEATURES; i++) {
+ zfeature_info_t *feature = &spa_feature_table[i];
+ if (!feature->fi_zfs_mod_supported)
+ continue;
+ if (strcmp(guid, feature->fi_guid) == 0) {
+ if (res != NULL)
+ *res = i;
+ return (0);
+ }
+ }
+
+ return (ENOENT);
+}
+
+int
zfeature_lookup_name(const char *name, spa_feature_t *res)
{
for (spa_feature_t i = 0; i < SPA_FEATURES; i++) {
zfeature_info_t *feature = &spa_feature_table[i];
+ if (!feature->fi_zfs_mod_supported)
+ continue;
if (strcmp(name, feature->fi_uname) == 0) {
if (res != NULL)
*res = i;
@@ -137,6 +158,34 @@ deps_contains_feature(const spa_feature_t *deps, const spa_feature_t feature)
return (B_FALSE);
}
+static boolean_t
+zfs_mod_supported_feature(const char *name)
+{
+ /*
+ * The zfs module spa_feature_table[], whether in-kernel or in
+ * libzpool, always supports all the features. libzfs needs to
+ * query the running module, via sysfs, to determine which
+ * features are supported.
+ */
+#if defined(_KERNEL) || defined(LIB_ZPOOL_BUILD)
+ return (B_TRUE);
+#else
+ struct stat64 statbuf;
+ char *path;
+ boolean_t supported = B_FALSE;
+ int len;
+
+ len = asprintf(&path, "%s/%s/%s", ZFS_SYSFS_DIR,
+ ZFS_SYSFS_POOL_FEATURES, name);
+
+ if (len > 0) {
+ supported = !!(stat64(path, &statbuf) == 0);
+ free(path);
+ }
+ return (supported);
+#endif
+}
+
static void
zfeature_register(spa_feature_t fid, const char *guid, const char *name,
const char *desc, zfeature_flags_t flags, const spa_feature_t *deps)
@@ -163,6 +212,7 @@ zfeature_register(spa_feature_t fid, const char *guid, const char *name,
feature->fi_desc = desc;
feature->fi_flags = flags;
feature->fi_depends = deps;
+ feature->fi_zfs_mod_supported = zfs_mod_supported_feature(guid);
}
void
@@ -361,6 +411,7 @@ zpool_feature_init(void)
}
#if defined(_KERNEL)
+EXPORT_SYMBOL(zfeature_lookup_guid);
EXPORT_SYMBOL(zfeature_lookup_name);
EXPORT_SYMBOL(zfeature_is_supported);
EXPORT_SYMBOL(zfeature_is_valid_guid);
diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c
index 28eb1255b..04bb33e5e 100644
--- a/module/zcommon/zfs_prop.c
+++ b/module/zcommon/zfs_prop.c
@@ -720,7 +720,8 @@ zfs_prop_readonly(zfs_prop_t prop)
boolean_t
zfs_prop_visible(zfs_prop_t prop)
{
- return (zfs_prop_table[prop].pd_visible);
+ return (zfs_prop_table[prop].pd_visible &&
+ zfs_prop_table[prop].pd_zfs_mod_supported);
}
/*
diff --git a/module/zcommon/zprop_common.c b/module/zcommon/zprop_common.c
index 838988a4c..0ce2ee762 100644
--- a/module/zcommon/zprop_common.c
+++ b/module/zcommon/zprop_common.c
@@ -34,6 +34,7 @@
#include <sys/spa.h>
#include <sys/zfs_acl.h>
#include <sys/zfs_ioctl.h>
+#include <sys/zfs_sysfs.h>
#include <sys/zfs_znode.h>
#include <sys/fs/zfs.h>
@@ -48,6 +49,7 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
+#include <sys/stat.h>
#endif
static zprop_desc_t *
@@ -68,6 +70,34 @@ zprop_get_numprops(zfs_type_t type)
return (ZFS_NUM_PROPS);
}
+static boolean_t
+zfs_mod_supported_prop(const char *name, zfs_type_t type)
+{
+/*
+ * The zfs module spa_feature_table[], whether in-kernel or in libzpool,
+ * always supports all the properties. libzfs needs to query the running
+ * module, via sysfs, to determine which properties are supported.
+ */
+#if defined(_KERNEL) || defined(LIB_ZPOOL_BUILD)
+ return (B_TRUE);
+#else
+ struct stat64 statbuf;
+ char *path;
+ boolean_t supported = B_FALSE;
+ int len;
+
+ len = asprintf(&path, "%s/%s/%s", ZFS_SYSFS_DIR,
+ (type == ZFS_TYPE_POOL) ? ZFS_SYSFS_POOL_PROPERTIES :
+ ZFS_SYSFS_DATASET_PROPERTIES, name);
+
+ if (len > 0) {
+ supported = !!(stat64(path, &statbuf) == 0);
+ free(path);
+ }
+ return (supported);
+#endif
+}
+
void
zprop_register_impl(int prop, const char *name, zprop_type_t type,
uint64_t numdefault, const char *strdefault, zprop_attr_t attr,
@@ -94,6 +124,7 @@ zprop_register_impl(int prop, const char *name, zprop_type_t type,
pd->pd_colname = colname;
pd->pd_rightalign = rightalign;
pd->pd_visible = visible;
+ pd->pd_zfs_mod_supported = zfs_mod_supported_prop(name, objset_types);
pd->pd_table = idx_tbl;
pd->pd_table_size = 0;
while (idx_tbl && (idx_tbl++)->pi_name != NULL)
@@ -193,6 +224,7 @@ zprop_iter_common(zprop_func func, void *cb, boolean_t show_all,
prop = ZPROP_CONT;
for (i = 0; i < num_props; i++) {
if ((order[i]->pd_visible || show_all) &&
+ order[i]->pd_zfs_mod_supported &&
(func(order[i]->pd_propnum, cb) != ZPROP_CONT)) {
prop = order[i]->pd_propnum;
break;
diff --git a/module/zfs/Makefile.in b/module/zfs/Makefile.in
index 2c27cf65c..6fd24757c 100644
--- a/module/zfs/Makefile.in
+++ b/module/zfs/Makefile.in
@@ -120,6 +120,7 @@ $(MODULE)-objs += zfs_ratelimit.o
$(MODULE)-objs += zfs_replay.o
$(MODULE)-objs += zfs_rlock.o
$(MODULE)-objs += zfs_sa.o
+$(MODULE)-objs += zfs_sysfs.o
$(MODULE)-objs += zfs_vfsops.o
$(MODULE)-objs += zfs_vnops.o
$(MODULE)-objs += zfs_znode.o
diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c
index 6a5846bfe..4ea9fa41a 100644
--- a/module/zfs/zfs_ioctl.c
+++ b/module/zfs/zfs_ioctl.c
@@ -187,6 +187,7 @@
#include <sys/zcp.h>
#include <sys/zio_checksum.h>
#include <sys/vdev_removal.h>
+#include <sys/zfs_sysfs.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
@@ -7083,6 +7084,7 @@ _init(void)
zfs_init();
zfs_ioctl_init();
+ zfs_sysfs_init();
if ((error = zfs_attach()) != 0)
goto out;
@@ -7102,6 +7104,7 @@ _init(void)
return (0);
out:
+ zfs_sysfs_fini();
zfs_fini();
spa_fini();
(void) zvol_fini();
@@ -7116,6 +7119,7 @@ static void __exit
_fini(void)
{
zfs_detach();
+ zfs_sysfs_fini();
zfs_fini();
spa_fini();
zvol_fini();
diff --git a/module/zfs/zfs_sysfs.c b/module/zfs/zfs_sysfs.c
new file mode 100644
index 000000000..6bf2e23aa
--- /dev/null
+++ b/module/zfs/zfs_sysfs.c
@@ -0,0 +1,617 @@
+/*
+ * 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 Delphix. All rights reserved.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/zfeature.h>
+#include <sys/zfs_ioctl.h>
+#include <sys/zfs_sysfs.h>
+#include <sys/kmem.h>
+#include <sys/fs/zfs.h>
+#include <linux/kobject.h>
+
+#include "zfs_prop.h"
+
+#if !defined(_KERNEL)
+#error kernel builds only
+#endif
+
+/*
+ * ZFS Module sysfs support
+ *
+ * This extends our sysfs '/sys/module/zfs' entry to include feature
+ * and property attributes. The primary consumer of this information
+ * is user processes, like the zfs CLI, that need to know what the
+ * current loaded ZFS module supports. The libzfs binary will consult
+ * this information when instantiating the zfs|zpool property tables
+ * and the pool features table.
+ *
+ * The added top-level directories are:
+ * /sys/module/zfs
+ * ├── features.kernel
+ * ├── features.pool
+ * ├── properties.dataset
+ * └── properties.pool
+ *
+ * The local interface for the zfs kobjects includes:
+ * zfs_kobj_init()
+ * zfs_kobj_add()
+ * zfs_kobj_release()
+ * zfs_kobj_add_attr()
+ * zfs_kobj_fini()
+ */
+
+/*
+ * A zfs_mod_kobj_t represents a zfs kobject under '/sys/module/zfs'
+ */
+struct zfs_mod_kobj;
+typedef struct zfs_mod_kobj zfs_mod_kobj_t;
+
+struct zfs_mod_kobj {
+ struct kobject zko_kobj;
+ struct kobj_type zko_kobj_type;
+ struct sysfs_ops zko_sysfs_ops;
+ size_t zko_attr_count;
+ struct attribute *zko_attr_list; /* allocated */
+ struct attribute **zko_default_attrs; /* allocated */
+ size_t zko_child_count;
+ zfs_mod_kobj_t *zko_children; /* allocated */
+};
+
+#define ATTR_TABLE_SIZE(cnt) (sizeof (struct attribute) * (cnt))
+/* Note +1 for NULL terminator slot */
+#define DEFAULT_ATTR_SIZE(cnt) (sizeof (struct attribute *) * (cnt + 1))
+#define CHILD_TABLE_SIZE(cnt) (sizeof (zfs_mod_kobj_t) * (cnt))
+
+/*
+ * These are the top-level kobjects under '/sys/module/zfs/'
+ */
+static zfs_mod_kobj_t kernel_features_kobj;
+static zfs_mod_kobj_t pool_features_kobj;
+static zfs_mod_kobj_t dataset_props_kobj;
+static zfs_mod_kobj_t pool_props_kobj;
+
+/*
+ * The show function is used to provide the content
+ * of an attribute into a PAGE_SIZE buffer.
+ */
+typedef ssize_t (*sysfs_show_func)(struct kobject *, struct attribute *,
+ char *);
+
+static void
+zfs_kobj_fini(zfs_mod_kobj_t *zkobj)
+{
+ /* finialize any child kobjects */
+ if (zkobj->zko_child_count != 0) {
+ ASSERT(zkobj->zko_children);
+ for (int i = 0; i < zkobj->zko_child_count; i++)
+ zfs_kobj_fini(&zkobj->zko_children[i]);
+ }
+
+ /* kobject_put() will call zfs_kobj_release() to release memory */
+ kobject_del(&zkobj->zko_kobj);
+ kobject_put(&zkobj->zko_kobj);
+}
+
+static void
+zfs_kobj_release(struct kobject *kobj)
+{
+ zfs_mod_kobj_t *zkobj = container_of(kobj, zfs_mod_kobj_t, zko_kobj);
+
+ if (zkobj->zko_attr_count != 0) {
+ ASSERT(zkobj->zko_attr_list);
+ ASSERT(zkobj->zko_default_attrs);
+
+ kmem_free(zkobj->zko_attr_list,
+ ATTR_TABLE_SIZE(zkobj->zko_attr_count));
+ kmem_free(zkobj->zko_default_attrs,
+ DEFAULT_ATTR_SIZE(zkobj->zko_attr_count));
+ zkobj->zko_attr_count = 0;
+ zkobj->zko_attr_list = NULL;
+ zkobj->zko_default_attrs = NULL;
+ }
+ if (zkobj->zko_child_count != 0) {
+ ASSERT(zkobj->zko_children);
+
+ kmem_free(zkobj->zko_children,
+ CHILD_TABLE_SIZE(zkobj->zko_child_count));
+ zkobj->zko_child_count = 0;
+ zkobj->zko_children = NULL;
+ }
+}
+
+static void
+zfs_kobj_add_attr(zfs_mod_kobj_t *zkobj, int attr_num, const char *attr_name)
+{
+ VERIFY3U(attr_num, <, zkobj->zko_attr_count);
+ ASSERT(zkobj->zko_attr_list);
+ ASSERT(zkobj->zko_default_attrs);
+
+ zkobj->zko_attr_list[attr_num].name = attr_name;
+ zkobj->zko_attr_list[attr_num].mode = 0444;
+ zkobj->zko_default_attrs[attr_num] = &zkobj->zko_attr_list[attr_num];
+}
+
+static int
+zfs_kobj_init(zfs_mod_kobj_t *zkobj, int attr_cnt, int child_cnt,
+ sysfs_show_func show_func)
+{
+ /*
+ * Initialize object's attributes. Count can be zero.
+ */
+ if (attr_cnt > 0) {
+ zkobj->zko_attr_list = kmem_zalloc(ATTR_TABLE_SIZE(attr_cnt),
+ KM_SLEEP);
+ if (zkobj->zko_attr_list == NULL)
+ return (ENOMEM);
+ }
+ /* this will always have at least one slot for NULL termination */
+ zkobj->zko_default_attrs = kmem_zalloc(DEFAULT_ATTR_SIZE(attr_cnt),
+ KM_SLEEP);
+ if (zkobj->zko_default_attrs == NULL) {
+ if (zkobj->zko_attr_list) {
+ kmem_free(zkobj->zko_attr_list,
+ ATTR_TABLE_SIZE(attr_cnt));
+ }
+ return (ENOMEM);
+ }
+ zkobj->zko_attr_count = attr_cnt;
+ zkobj->zko_kobj_type.default_attrs = zkobj->zko_default_attrs;
+
+ if (child_cnt > 0) {
+ zkobj->zko_children = kmem_zalloc(CHILD_TABLE_SIZE(child_cnt),
+ KM_SLEEP);
+ if (zkobj->zko_children == NULL) {
+ if (attr_cnt > 0) {
+ kmem_free(zkobj->zko_attr_list,
+ DEFAULT_ATTR_SIZE(attr_cnt));
+ kmem_free(zkobj->zko_attr_list,
+ ATTR_TABLE_SIZE(attr_cnt));
+ }
+ return (ENOMEM);
+ }
+ zkobj->zko_child_count = child_cnt;
+ }
+
+ zkobj->zko_sysfs_ops.show = show_func;
+ zkobj->zko_kobj_type.sysfs_ops = &zkobj->zko_sysfs_ops;
+ zkobj->zko_kobj_type.release = zfs_kobj_release;
+
+ return (0);
+}
+
+static int
+zfs_kobj_add(zfs_mod_kobj_t *zkobj, struct kobject *parent, const char *name)
+{
+ /* zko_default_attrs must be NULL terminated */
+ ASSERT(zkobj->zko_default_attrs != NULL);
+ ASSERT(zkobj->zko_default_attrs[zkobj->zko_attr_count] == NULL);
+
+ kobject_init(&zkobj->zko_kobj, &zkobj->zko_kobj_type);
+ return (kobject_add(&zkobj->zko_kobj, parent, name));
+}
+
+/*
+ * Each zfs property has these common attributes
+ */
+static const char *zprop_attrs[] = {
+ "type",
+ "readonly",
+ "setonce",
+ "visible",
+ "values",
+ "default",
+ "datasets" /* zfs properties only */
+};
+
+#define ZFS_PROP_ATTR_COUNT ARRAY_SIZE(zprop_attrs)
+#define ZPOOL_PROP_ATTR_COUNT (ZFS_PROP_ATTR_COUNT - 1)
+
+static const char *zprop_types[] = {
+ "number",
+ "string",
+ "index",
+};
+
+typedef struct zfs_type_map {
+ zfs_type_t ztm_type;
+ const char *ztm_name;
+} zfs_type_map_t;
+
+static zfs_type_map_t type_map[] = {
+ {ZFS_TYPE_FILESYSTEM, "filesystem"},
+ {ZFS_TYPE_SNAPSHOT, "snapshot"},
+ {ZFS_TYPE_VOLUME, "volume"},
+ {ZFS_TYPE_BOOKMARK, "bookmark"}
+};
+
+/*
+ * Show the content for a zfs property attribute
+ */
+static ssize_t
+zprop_sysfs_show(const char *attr_name, const zprop_desc_t *property,
+ char *buf, size_t buflen)
+{
+ const char *show_str;
+
+ /* For dataset properties list the dataset types that apply */
+ if (strcmp(attr_name, "datasets") == 0 &&
+ property->pd_types != ZFS_TYPE_POOL) {
+ int len = 0;
+
+ for (int i = 0; i < ARRAY_SIZE(type_map); i++) {
+ if (type_map[i].ztm_type & property->pd_types) {
+ len += snprintf(buf + len, buflen - len, "%s ",
+ type_map[i].ztm_name);
+ }
+ }
+ len += snprintf(buf + len, buflen - len, "\n");
+ return (len);
+ }
+
+ if (strcmp(attr_name, "type") == 0) {
+ show_str = zprop_types[property->pd_proptype];
+ } else if (strcmp(attr_name, "readonly") == 0) {
+ show_str = property->pd_attr == PROP_READONLY ? "1" : "0";
+ } else if (strcmp(attr_name, "setonce") == 0) {
+ show_str = property->pd_attr == PROP_ONETIME ? "1" : "0";
+ } else if (strcmp(attr_name, "visible") == 0) {
+ show_str = property->pd_visible ? "1" : "0";
+ } else if (strcmp(attr_name, "values") == 0) {
+ show_str = property->pd_values ? property->pd_values : "";
+ } else if (strcmp(attr_name, "default") == 0) {
+ char number[32];
+
+ switch (property->pd_proptype) {
+ case PROP_TYPE_NUMBER:
+ (void) snprintf(number, sizeof (number), "%llu",
+ (u_longlong_t)property->pd_numdefault);
+ show_str = number;
+ break;
+ case PROP_TYPE_STRING:
+ show_str = property->pd_strdefault ?
+ property->pd_strdefault : "";
+ break;
+ case PROP_TYPE_INDEX:
+ (void) zprop_index_to_string(property->pd_propnum,
+ property->pd_numdefault, &show_str,
+ property->pd_types);
+ break;
+ default:
+ return (0);
+ }
+ } else {
+ return (0);
+ }
+
+ return (snprintf(buf, buflen, "%s\n", show_str));
+}
+
+static ssize_t
+dataset_property_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ zfs_prop_t prop = zfs_name_to_prop(kobject_name(kobj));
+ zprop_desc_t *prop_tbl = zfs_prop_get_table();
+ ssize_t len;
+
+ ASSERT3U(prop, <, ZFS_NUM_PROPS);
+
+ len = zprop_sysfs_show(attr->name, &prop_tbl[prop], buf, PAGE_SIZE);
+
+ return (len);
+}
+
+static ssize_t
+pool_property_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ zpool_prop_t prop = zpool_name_to_prop(kobject_name(kobj));
+ zprop_desc_t *prop_tbl = zpool_prop_get_table();
+ ssize_t len;
+
+ ASSERT3U(prop, <, ZPOOL_NUM_PROPS);
+
+ len = zprop_sysfs_show(attr->name, &prop_tbl[prop], buf, PAGE_SIZE);
+
+ return (len);
+}
+
+/*
+ * ZFS kernel feature attributes for '/sys/module/zfs/features.kernel'
+ *
+ * This list is intended for kernel features that don't have a pool feature
+ * association or that extend existing user kernel interfaces.
+ *
+ * A user processes can easily check if the running zfs kernel module
+ * supports the new feature.
+ *
+ * For example, the initial channel_program feature was extended to support
+ * async calls (i.e. a sync flag). If this mechanism were in place at that
+ * time, we could have added a 'channel_program_async' to this list.
+ */
+static const char *zfs_features[] = {
+ /* --> Add new kernel features here (post ZoL 0.8.0) */
+};
+
+#define ZFS_FEATURE_COUNT ARRAY_SIZE(zfs_features)
+
+static ssize_t
+kernel_feature_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ return (snprintf(buf, PAGE_SIZE, "supported\n"));
+}
+
+static int
+zfs_kernel_features_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent)
+{
+ int err;
+
+ err = zfs_kobj_init(zfs_kobj, ZFS_FEATURE_COUNT, 0,
+ kernel_feature_show);
+ if (err)
+ return (err);
+
+ for (int f = 0; f < ZFS_FEATURE_COUNT; f++)
+ zfs_kobj_add_attr(zfs_kobj, f, zfs_features[f]);
+
+ err = zfs_kobj_add(zfs_kobj, parent, ZFS_SYSFS_KERNEL_FEATURES);
+ if (err)
+ zfs_kobj_release(&zfs_kobj->zko_kobj);
+
+ return (err);
+}
+
+/*
+ * Each pool feature has these common attributes
+ */
+static const char *pool_feature_attrs[] = {
+ "description",
+ "guid",
+ "uname",
+ "readonly_compatible",
+ "required_for_mos",
+ "activate_on_enable",
+ "per_dataset"
+};
+
+#define ZPOOL_FEATURE_ATTR_COUNT ARRAY_SIZE(pool_feature_attrs)
+
+/*
+ * Show the content for the given zfs pool feature attribute
+ */
+static ssize_t
+pool_feature_show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+ spa_feature_t fid;
+
+ if (zfeature_lookup_guid(kobject_name(kobj), &fid) != 0)
+ return (0);
+
+ ASSERT3U(fid, <, SPA_FEATURES);
+
+ zfeature_flags_t flags = spa_feature_table[fid].fi_flags;
+ const char *show_str = NULL;
+
+ if (strcmp(attr->name, "description") == 0) {
+ show_str = spa_feature_table[fid].fi_desc;
+ } else if (strcmp(attr->name, "guid") == 0) {
+ show_str = spa_feature_table[fid].fi_guid;
+ } else if (strcmp(attr->name, "uname") == 0) {
+ show_str = spa_feature_table[fid].fi_uname;
+ } else if (strcmp(attr->name, "readonly_compatible") == 0) {
+ show_str = flags & ZFEATURE_FLAG_READONLY_COMPAT ? "1" : "0";
+ } else if (strcmp(attr->name, "required_for_mos") == 0) {
+ show_str = flags & ZFEATURE_FLAG_MOS ? "1" : "0";
+ } else if (strcmp(attr->name, "activate_on_enable") == 0) {
+ show_str = flags & ZFEATURE_FLAG_ACTIVATE_ON_ENABLE ? "1" : "0";
+ } else if (strcmp(attr->name, "per_dataset") == 0) {
+ show_str = flags & ZFEATURE_FLAG_PER_DATASET ? "1" : "0";
+ }
+ if (show_str == NULL)
+ return (0);
+
+ return (snprintf(buf, PAGE_SIZE, "%s\n", show_str));
+}
+
+static void
+pool_feature_to_kobj(zfs_mod_kobj_t *parent, spa_feature_t fid,
+ const char *name)
+{
+ zfs_mod_kobj_t *zfs_kobj = &parent->zko_children[fid];
+
+ ASSERT3U(fid, <, SPA_FEATURES);
+ ASSERT(name);
+
+ int err = zfs_kobj_init(zfs_kobj, ZPOOL_FEATURE_ATTR_COUNT, 0,
+ pool_feature_show);
+ if (err)
+ return;
+
+ for (int i = 0; i < ZPOOL_FEATURE_ATTR_COUNT; i++)
+ zfs_kobj_add_attr(zfs_kobj, i, pool_feature_attrs[i]);
+
+ err = zfs_kobj_add(zfs_kobj, &parent->zko_kobj, name);
+ if (err)
+ zfs_kobj_release(&zfs_kobj->zko_kobj);
+}
+
+static int
+zfs_pool_features_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent)
+{
+ /*
+ * Create a parent kobject to host pool features.
+ *
+ * '/sys/module/zfs/features.pool'
+ */
+ int err = zfs_kobj_init(zfs_kobj, 0, SPA_FEATURES, pool_feature_show);
+ if (err)
+ return (err);
+ err = zfs_kobj_add(zfs_kobj, parent, ZFS_SYSFS_POOL_FEATURES);
+ if (err) {
+ zfs_kobj_release(&zfs_kobj->zko_kobj);
+ return (err);
+ }
+
+ /*
+ * Now create a kobject for each feature.
+ *
+ * '/sys/module/zfs/features.pool/<feature>'
+ */
+ for (spa_feature_t i = 0; i < SPA_FEATURES; i++)
+ pool_feature_to_kobj(zfs_kobj, i, spa_feature_table[i].fi_guid);
+
+ return (0);
+}
+
+typedef struct prop_to_kobj_arg {
+ zprop_desc_t *p2k_table;
+ zfs_mod_kobj_t *p2k_parent;
+ sysfs_show_func p2k_show_func;
+ int p2k_attr_count;
+} prop_to_kobj_arg_t;
+
+static int
+zprop_to_kobj(int prop, void *args)
+{
+ prop_to_kobj_arg_t *data = args;
+ zfs_mod_kobj_t *parent = data->p2k_parent;
+ zfs_mod_kobj_t *zfs_kobj = &parent->zko_children[prop];
+ const char *name = data->p2k_table[prop].pd_name;
+ int err;
+
+ ASSERT(name);
+
+ err = zfs_kobj_init(zfs_kobj, data->p2k_attr_count, 0,
+ data->p2k_show_func);
+ if (err)
+ return (ZPROP_CONT);
+
+ for (int i = 0; i < data->p2k_attr_count; i++)
+ zfs_kobj_add_attr(zfs_kobj, i, zprop_attrs[i]);
+
+ err = zfs_kobj_add(zfs_kobj, &parent->zko_kobj, name);
+ if (err)
+ zfs_kobj_release(&zfs_kobj->zko_kobj);
+
+ return (ZPROP_CONT);
+}
+
+static int
+zfs_sysfs_properties_init(zfs_mod_kobj_t *zfs_kobj, struct kobject *parent,
+ zfs_type_t type)
+{
+ prop_to_kobj_arg_t context;
+ const char *name;
+ int err;
+
+ /*
+ * Create a parent kobject to host properties.
+ *
+ * '/sys/module/zfs/properties.<type>'
+ */
+ if (type == ZFS_TYPE_POOL) {
+ name = ZFS_SYSFS_POOL_PROPERTIES;
+ context.p2k_table = zpool_prop_get_table();
+ context.p2k_attr_count = ZPOOL_PROP_ATTR_COUNT;
+ context.p2k_parent = zfs_kobj;
+ context.p2k_show_func = pool_property_show;
+ err = zfs_kobj_init(zfs_kobj, 0, ZPOOL_NUM_PROPS,
+ pool_property_show);
+ } else {
+ name = ZFS_SYSFS_DATASET_PROPERTIES;
+ context.p2k_table = zfs_prop_get_table();
+ context.p2k_attr_count = ZFS_PROP_ATTR_COUNT;
+ context.p2k_parent = zfs_kobj;
+ context.p2k_show_func = dataset_property_show;
+ err = zfs_kobj_init(zfs_kobj, 0, ZFS_NUM_PROPS,
+ dataset_property_show);
+ }
+
+ if (err)
+ return (err);
+
+ err = zfs_kobj_add(zfs_kobj, parent, name);
+ if (err) {
+ zfs_kobj_release(&zfs_kobj->zko_kobj);
+ return (err);
+ }
+
+ /*
+ * Create a kobject for each property.
+ *
+ * '/sys/module/zfs/properties.<type>/<property>'
+ */
+ (void) zprop_iter_common(zprop_to_kobj, &context, B_TRUE,
+ B_FALSE, type);
+
+ return (err);
+}
+
+void
+zfs_sysfs_init(void)
+{
+ struct kobject *parent =
+ &(((struct module *)(THIS_MODULE))->mkobj).kobj;
+ int err;
+
+ ASSERT(parent != NULL);
+
+ err = zfs_kernel_features_init(&kernel_features_kobj, parent);
+ if (err)
+ return;
+
+ err = zfs_pool_features_init(&pool_features_kobj, parent);
+ if (err) {
+ zfs_kobj_fini(&kernel_features_kobj);
+ return;
+ }
+
+ err = zfs_sysfs_properties_init(&pool_props_kobj, parent,
+ ZFS_TYPE_POOL);
+ if (err) {
+ zfs_kobj_fini(&kernel_features_kobj);
+ zfs_kobj_fini(&pool_features_kobj);
+ return;
+ }
+
+ err = zfs_sysfs_properties_init(&dataset_props_kobj, parent,
+ ZFS_TYPE_FILESYSTEM);
+ if (err) {
+ zfs_kobj_fini(&kernel_features_kobj);
+ zfs_kobj_fini(&pool_features_kobj);
+ zfs_kobj_fini(&pool_props_kobj);
+ return;
+ }
+}
+
+void
+zfs_sysfs_fini(void)
+{
+ /*
+ * Remove top-level kobjects; each will remove any children kobjects
+ */
+ zfs_kobj_fini(&kernel_features_kobj);
+ zfs_kobj_fini(&pool_features_kobj);
+ zfs_kobj_fini(&dataset_props_kobj);
+ zfs_kobj_fini(&pool_props_kobj);
+}
diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run
index 0a64e8cd5..cde8105f9 100644
--- a/tests/runfiles/linux.run
+++ b/tests/runfiles/linux.run
@@ -255,6 +255,12 @@ tests = ['zfs_snapshot_001_neg', 'zfs_snapshot_002_neg',
'zfs_snapshot_009_pos']
tags = ['functional', 'cli_root', 'zfs_snapshot']
+[tests/functional/cli_root/zfs_sysfs]
+tests = ['zfeature_set_unsupported.ksh', 'zfs_get_unsupported',
+ 'zfs_set_unsupported', 'zfs_sysfs_live.ksh', 'zpool_get_unsupported',
+ 'zpool_set_unsupported']
+tags = ['functional', 'cli_root', 'zfs_sysfs']
+
[tests/functional/cli_root/zfs_unload-key]
tests = ['zfs_unload-key', 'zfs_unload-key_all', 'zfs_unload-key_recursive']
tags = ['functional', 'cli_root', 'zfs_unload-key']
diff --git a/tests/zfs-tests/tests/functional/cli_root/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/Makefile.am
index aab49b168..7a765a160 100644
--- a/tests/zfs-tests/tests/functional/cli_root/Makefile.am
+++ b/tests/zfs-tests/tests/functional/cli_root/Makefile.am
@@ -28,6 +28,7 @@ SUBDIRS = \
zfs_set \
zfs_share \
zfs_snapshot \
+ zfs_sysfs \
zfs_unload-key \
zfs_unmount \
zfs_unshare \
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/Makefile.am
new file mode 100644
index 000000000..6a83edf2a
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/Makefile.am
@@ -0,0 +1,10 @@
+pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/cli_root/zfs_sysfs
+dist_pkgdata_SCRIPTS = \
+ setup.ksh \
+ cleanup.ksh \
+ zfeature_set_unsupported.ksh \
+ zfs_get_unsupported.ksh \
+ zfs_set_unsupported.ksh \
+ zfs_sysfs_live.ksh \
+ zpool_get_unsupported.ksh \
+ zpool_set_unsupported.ksh
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/cleanup.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/cleanup.ksh
new file mode 100755
index 000000000..79cd6e9f9
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/cleanup.ksh
@@ -0,0 +1,30 @@
+#!/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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+default_cleanup
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/setup.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/setup.ksh
new file mode 100755
index 000000000..969238599
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/setup.ksh
@@ -0,0 +1,32 @@
+#!/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 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+DISK=${DISKS%% *}
+
+default_container_volume_setup $DISK
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfeature_set_unsupported.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfeature_set_unsupported.ksh
new file mode 100755
index 000000000..c9d242243
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfeature_set_unsupported.ksh
@@ -0,0 +1,54 @@
+#!/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 Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# A feature not supported by the zfs module should fail in 'zpool set <feature>'
+#
+# STRATEGY:
+# 1. Run zpool set <featureprop> with env var 'ZFS_SYSFS_PROP_SUPPORT_TEST'
+# 2. Verify that zpool set returns error
+#
+
+verify_runnable "global"
+
+if ! is_linux ; then
+ log_unsupported "sysfs is linux-only"
+fi
+
+claim="Features not supported by zfs module should fail in 'zpool set <feature>'"
+
+unsupported_feature="feature@large_blocks"
+value="enabled"
+
+log_assert $claim
+
+log_mustnot eval "ZFS_SYSFS_PROP_SUPPORT_TEST=yes zpool set \
+ ${unsupported_feature}=${value} $TESTPOOL/$TESTFS >/dev/null 2>&1"
+
+log_pass $claim
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfs_get_unsupported.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfs_get_unsupported.ksh
new file mode 100755
index 000000000..59ed7e950
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfs_get_unsupported.ksh
@@ -0,0 +1,53 @@
+#!/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 Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# A property not supported by the zfs module should fail in 'zfs get <prop>'
+#
+# STRATEGY:
+# 1. Run zfs get <prop> with the env variable 'ZFS_SYSFS_PROP_SUPPORT_TEST'
+# 2. Verify that zfs get returns error
+#
+
+verify_runnable "global"
+
+if ! is_linux ; then
+ log_unsupported "sysfs is linux-only"
+fi
+
+claim="Properties not supported by zfs module should fail in 'zfs get <prop>'"
+
+unsupported_prop="dnodesize"
+
+log_assert $claim
+
+log_mustnot eval "ZFS_SYSFS_PROP_SUPPORT_TEST=yes zfs get ${unsupported_prop} \
+ $TESTPOOL/$TESTFS >/dev/null 2>&1"
+
+log_pass $claim
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfs_set_unsupported.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfs_set_unsupported.ksh
new file mode 100755
index 000000000..5a0b88a0b
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfs_set_unsupported.ksh
@@ -0,0 +1,54 @@
+#!/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 Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# A property not supported by the zfs module should fail in 'zfs set <prop>'
+#
+# STRATEGY:
+# 1. Run zfs set <prop> with the env variable 'ZFS_SYSFS_PROP_SUPPORT_TEST'
+# 2. Verify that zfs get returns error
+#
+
+verify_runnable "global"
+
+if ! is_linux ; then
+ log_unsupported "sysfs is linux-only"
+fi
+
+claim="Properties not supported by zfs module should fail in 'zfs set <prop>'"
+
+unsupported_prop="dnodesize"
+value="any"
+
+log_assert $claim
+
+log_mustnot eval "ZFS_SYSFS_PROP_SUPPORT_TEST=yes zfs set \
+ ${unsupported_prop}=${value} $TESTPOOL/$TESTFS >/dev/null 2>&1"
+
+log_pass $claim
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfs_sysfs_live.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfs_sysfs_live.ksh
new file mode 100755
index 000000000..17ac8f8a1
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zfs_sysfs_live.ksh
@@ -0,0 +1,57 @@
+#!/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 Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# Test if the expected '/sys/module/zfs/<dir>/<attr>' are present
+#
+
+verify_runnable "global"
+
+if ! is_linux ; then
+ log_unsupported "sysfs is linux-only"
+fi
+
+claim="Expected '/sys/module/zfs/<dir>/<attr>' attributes are present"
+
+feature_attr="/sys/module/zfs/features.pool/org.open-zfs:large_blocks/guid"
+pool_prop__attr="/sys/module/zfs/properties.pool/comment/values"
+ds_prop__attr="/sys/module/zfs/properties.dataset/recordsize/values"
+
+log_assert $claim
+
+log_must cat $feature_attr
+log_must cat $pool_prop__attr
+log_must cat $ds_prop__attr
+
+# force a read of all the attributes for show func code coverage
+log_must grep -R "[a-z]" /sys/module/zfs/features.*
+log_must grep -R "[a-z]" /sys/module/zfs/properties.*
+log_mustnot grep -RE "[^[:print:]]" /sys/module/zfs/properties.*
+
+log_pass $claim
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zpool_get_unsupported.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zpool_get_unsupported.ksh
new file mode 100755
index 000000000..3ab1d941e
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zpool_get_unsupported.ksh
@@ -0,0 +1,55 @@
+#!/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 Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# A property not supported by the zfs module should fail in 'zpool get <prop>'
+#
+# STRATEGY:
+# 1. Run zpool get <prop> with the env variable 'ZFS_SYSFS_PROP_SUPPORT_TEST'
+# 2. Verify that zfs get returns error
+#
+
+verify_runnable "global"
+
+if ! is_linux ; then
+ log_unsupported "sysfs is linux-only"
+fi
+
+export ZFS_SYSFS_PROP_SUPPORT_TEST
+
+claim="Properties not supported by zfs module should fail in 'zpool get <prop>'"
+
+unsupported_prop="comment"
+
+log_assert $claim
+
+log_mustnot eval "ZFS_SYSFS_PROP_SUPPORT_TEST=yes zpool get \
+ ${unsupported_prop} $TESTPOOL/$TESTFS >/dev/null 2>&1"
+
+log_pass $claim
diff --git a/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zpool_set_unsupported.ksh b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zpool_set_unsupported.ksh
new file mode 100755
index 000000000..03eb2aea3
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zfs_sysfs/zpool_set_unsupported.ksh
@@ -0,0 +1,54 @@
+#!/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 Delphix. All rights reserved.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+#
+# DESCRIPTION:
+# A property not supported by the zfs module should fail in 'zpool set <prop>'
+#
+# STRATEGY:
+# 1. Run zpool set <prop> with the env variable 'ZFS_SYSFS_PROP_SUPPORT_TEST'
+# 2. Verify that zpool set returns error
+#
+
+verify_runnable "global"
+
+if ! is_linux ; then
+ log_unsupported "sysfs is linux-only"
+fi
+
+claim="Properties not supported by zfs module should fail in 'zpool set <prop>'"
+
+unsupported_prop="comment"
+value="You Shall Not Pass"
+
+log_assert $claim
+
+log_mustnot eval "ZFS_SYSFS_PROP_SUPPORT_TEST=yes zpool set \
+ ${unsupported_prop}=${value} $TESTPOOL/$TESTFS >/dev/null 2>&1"
+
+log_pass $claim