summaryrefslogtreecommitdiff
path: root/usr/src/lib/libzfs/common
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libzfs/common')
-rw-r--r--usr/src/lib/libzfs/common/libzfs.h4
-rw-r--r--usr/src/lib/libzfs/common/libzfs_import.c71
-rw-r--r--usr/src/lib/libzfs/common/libzfs_pool.c138
-rw-r--r--usr/src/lib/libzfs/common/libzfs_util.c2
4 files changed, 154 insertions, 61 deletions
diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h
index e2ebae01f4..f4ef8adbe5 100644
--- a/usr/src/lib/libzfs/common/libzfs.h
+++ b/usr/src/lib/libzfs/common/libzfs.h
@@ -113,6 +113,7 @@ enum {
EZFS_UNSHARESMBFAILED, /* failed to unshare over smb */
EZFS_SHARESMBFAILED, /* failed to share over smb */
EZFS_BADCACHE, /* bad cache file */
+ EZFS_ISL2CACHE, /* device is for the level 2 ARC */
EZFS_UNKNOWN
};
@@ -216,7 +217,8 @@ extern int zpool_vdev_fault(zpool_handle_t *, uint64_t);
extern int zpool_vdev_degrade(zpool_handle_t *, uint64_t);
extern int zpool_vdev_clear(zpool_handle_t *, uint64_t);
-extern nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *);
+extern nvlist_t *zpool_find_vdev(zpool_handle_t *, const char *, boolean_t *,
+ boolean_t *);
extern int zpool_label_disk(libzfs_handle_t *, zpool_handle_t *, char *);
/*
diff --git a/usr/src/lib/libzfs/common/libzfs_import.c b/usr/src/lib/libzfs/common/libzfs_import.c
index 50e9ddf97e..a8ae241f88 100644
--- a/usr/src/lib/libzfs/common/libzfs_import.c
+++ b/usr/src/lib/libzfs/common/libzfs_import.c
@@ -213,11 +213,13 @@ add_config(libzfs_handle_t *hdl, pool_list_t *pl, const char *path,
name_entry_t *ne;
/*
- * If this is a hot spare not currently in use, add it to the list of
- * names to translate, but don't do anything else.
+ * If this is a hot spare not currently in use or level 2 cache
+ * device, add it to the list of names to translate, but don't do
+ * anything else.
*/
if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE,
- &state) == 0 && state == POOL_STATE_SPARE &&
+ &state) == 0 &&
+ (state == POOL_STATE_SPARE || state == POOL_STATE_L2CACHE) &&
nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, &vdev_guid) == 0) {
if ((ne = zfs_alloc(hdl, sizeof (name_entry_t))) == NULL)
return (-1);
@@ -415,8 +417,8 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl)
vdev_entry_t *ve;
config_entry_t *ce;
nvlist_t *ret = NULL, *config = NULL, *tmp, *nvtop, *nvroot;
- nvlist_t **spares;
- uint_t i, nspares;
+ nvlist_t **spares, **l2cache;
+ uint_t i, nspares, nl2cache;
boolean_t config_seen;
uint64_t best_txg;
char *name, *hostname;
@@ -647,6 +649,17 @@ get_configs(libzfs_handle_t *hdl, pool_list_t *pl)
}
/*
+ * Update the paths for l2cache devices.
+ */
+ if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
+ &l2cache, &nl2cache) == 0) {
+ for (i = 0; i < nl2cache; i++) {
+ if (fix_paths(l2cache[i], pl->names) != 0)
+ goto nomem;
+ }
+ }
+
+ /*
* Restore the original information read from the actual label.
*/
(void) nvlist_remove(config, ZPOOL_CONFIG_HOSTID,
@@ -728,12 +741,12 @@ zpool_read_label(int fd, nvlist_t **config)
continue;
if (nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_STATE,
- &state) != 0 || state > POOL_STATE_SPARE) {
+ &state) != 0 || state > POOL_STATE_L2CACHE) {
nvlist_free(*config);
continue;
}
- if (state != POOL_STATE_SPARE &&
+ if (state != POOL_STATE_SPARE && state != POOL_STATE_L2CACHE &&
(nvlist_lookup_uint64(*config, ZPOOL_CONFIG_POOL_TXG,
&txg) != 0 || txg == 0)) {
nvlist_free(*config);
@@ -1001,27 +1014,28 @@ find_guid(nvlist_t *nv, uint64_t guid)
return (B_FALSE);
}
-typedef struct spare_cbdata {
+typedef struct aux_cbdata {
+ const char *cb_type;
uint64_t cb_guid;
zpool_handle_t *cb_zhp;
-} spare_cbdata_t;
+} aux_cbdata_t;
static int
-find_spare(zpool_handle_t *zhp, void *data)
+find_aux(zpool_handle_t *zhp, void *data)
{
- spare_cbdata_t *cbp = data;
- nvlist_t **spares;
- uint_t i, nspares;
+ aux_cbdata_t *cbp = data;
+ nvlist_t **list;
+ uint_t i, count;
uint64_t guid;
nvlist_t *nvroot;
verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- &spares, &nspares) == 0) {
- for (i = 0; i < nspares; i++) {
- verify(nvlist_lookup_uint64(spares[i],
+ if (nvlist_lookup_nvlist_array(nvroot, cbp->cb_type,
+ &list, &count) == 0) {
+ for (i = 0; i < count; i++) {
+ verify(nvlist_lookup_uint64(list[i],
ZPOOL_CONFIG_GUID, &guid) == 0);
if (guid == cbp->cb_guid) {
cbp->cb_zhp = zhp;
@@ -1050,7 +1064,7 @@ zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr,
zpool_handle_t *zhp;
nvlist_t *pool_config;
uint64_t stateval, isspare;
- spare_cbdata_t cb = { 0 };
+ aux_cbdata_t cb = { 0 };
boolean_t isactive;
*inuse = B_FALSE;
@@ -1068,7 +1082,7 @@ zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr,
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID,
&vdev_guid) == 0);
- if (stateval != POOL_STATE_SPARE) {
+ if (stateval != POOL_STATE_SPARE && stateval != POOL_STATE_L2CACHE) {
verify(nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME,
&name) == 0);
verify(nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
@@ -1147,7 +1161,24 @@ zpool_in_use(libzfs_handle_t *hdl, int fd, pool_state_t *state, char **namestr,
*/
cb.cb_zhp = NULL;
cb.cb_guid = vdev_guid;
- if (zpool_iter(hdl, find_spare, &cb) == 1) {
+ cb.cb_type = ZPOOL_CONFIG_SPARES;
+ if (zpool_iter(hdl, find_aux, &cb) == 1) {
+ name = (char *)zpool_get_name(cb.cb_zhp);
+ ret = TRUE;
+ } else {
+ ret = FALSE;
+ }
+ break;
+
+ case POOL_STATE_L2CACHE:
+
+ /*
+ * Check if any pool is currently using this l2cache device.
+ */
+ cb.cb_zhp = NULL;
+ cb.cb_guid = vdev_guid;
+ cb.cb_type = ZPOOL_CONFIG_L2CACHE;
+ if (zpool_iter(hdl, find_aux, &cb) == 1) {
name = (char *)zpool_get_name(cb.cb_zhp);
ret = TRUE;
} else {
diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c
index 7320378e6f..75c1ce7492 100644
--- a/usr/src/lib/libzfs/common/libzfs_pool.c
+++ b/usr/src/lib/libzfs/common/libzfs_pool.c
@@ -815,6 +815,11 @@ zpool_create(libzfs_handle_t *hdl, const char *pool, nvlist_t *nvroot,
"one or more devices is out of space"));
return (zfs_error(hdl, EZFS_BADDEV, msg));
+ case ENOTBLK:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "cache device must be a disk or disk slice"));
+ return (zfs_error(hdl, EZFS_BADDEV, msg));
+
default:
return (zpool_standard_error(hdl, errno, msg));
}
@@ -898,14 +903,14 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
int ret;
libzfs_handle_t *hdl = zhp->zpool_hdl;
char msg[1024];
- nvlist_t **spares;
- uint_t nspares;
+ nvlist_t **spares, **l2cache;
+ uint_t nspares, nl2cache;
(void) snprintf(msg, sizeof (msg), dgettext(TEXT_DOMAIN,
"cannot add to '%s'"), zhp->zpool_name);
- if (zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL)
- < SPA_VERSION_SPARES &&
+ if (zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL) <
+ SPA_VERSION_SPARES &&
nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
&spares, &nspares) == 0) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool must be "
@@ -913,6 +918,15 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
return (zfs_error(hdl, EZFS_BADVERSION, msg));
}
+ if (zpool_get_prop_int(zhp, ZPOOL_PROP_VERSION, NULL) <
+ SPA_VERSION_L2CACHE &&
+ nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_L2CACHE,
+ &l2cache, &nl2cache) == 0) {
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool must be "
+ "upgraded to add cache devices"));
+ return (zfs_error(hdl, EZFS_BADVERSION, msg));
+ }
+
if (zcmd_write_conf_nvlist(hdl, &zc, nvroot) != 0)
return (-1);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
@@ -963,6 +977,12 @@ zpool_add(zpool_handle_t *zhp, nvlist_t *nvroot)
(void) zfs_error(hdl, EZFS_POOL_NOTSUP, msg);
break;
+ case ENOTBLK:
+ zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+ "cache device must be a disk or disk slice"));
+ (void) zfs_error(hdl, EZFS_BADDEV, msg);
+ break;
+
default:
(void) zpool_standard_error(hdl, errno, msg);
}
@@ -1172,7 +1192,7 @@ zpool_scrub(zpool_handle_t *zhp, pool_scrub_type_t type)
*/
static nvlist_t *
vdev_to_nvlist_iter(nvlist_t *nv, const char *search, uint64_t guid,
- boolean_t *avail_spare)
+ boolean_t *avail_spare, boolean_t *l2cache)
{
uint_t c, children;
nvlist_t **child;
@@ -1214,25 +1234,37 @@ vdev_to_nvlist_iter(nvlist_t *nv, const char *search, uint64_t guid,
for (c = 0; c < children; c++)
if ((ret = vdev_to_nvlist_iter(child[c], search, guid,
- avail_spare)) != NULL)
+ avail_spare, l2cache)) != NULL)
return (ret);
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
&child, &children) == 0) {
for (c = 0; c < children; c++) {
if ((ret = vdev_to_nvlist_iter(child[c], search, guid,
- avail_spare)) != NULL) {
+ avail_spare, l2cache)) != NULL) {
*avail_spare = B_TRUE;
return (ret);
}
}
}
+ if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_L2CACHE,
+ &child, &children) == 0) {
+ for (c = 0; c < children; c++) {
+ if ((ret = vdev_to_nvlist_iter(child[c], search, guid,
+ avail_spare, l2cache)) != NULL) {
+ *l2cache = B_TRUE;
+ return (ret);
+ }
+ }
+ }
+
return (NULL);
}
nvlist_t *
-zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare)
+zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare,
+ boolean_t *l2cache)
{
char buf[MAXPATHLEN];
const char *search;
@@ -1254,29 +1286,32 @@ zpool_find_vdev(zpool_handle_t *zhp, const char *path, boolean_t *avail_spare)
&nvroot) == 0);
*avail_spare = B_FALSE;
- return (vdev_to_nvlist_iter(nvroot, search, guid, avail_spare));
+ *l2cache = B_FALSE;
+ return (vdev_to_nvlist_iter(nvroot, search, guid, avail_spare,
+ l2cache));
}
/*
- * Returns TRUE if the given guid corresponds to a spare (INUSE or not).
+ * Returns TRUE if the given guid corresponds to the given type.
+ * This is used to check for hot spares (INUSE or not), and level 2 cache
+ * devices.
*/
static boolean_t
-is_spare(zpool_handle_t *zhp, uint64_t guid)
+is_guid_type(zpool_handle_t *zhp, uint64_t guid, const char *type)
{
- uint64_t spare_guid;
+ uint64_t target_guid;
nvlist_t *nvroot;
- nvlist_t **spares;
- uint_t nspares;
+ nvlist_t **list;
+ uint_t count;
int i;
verify(nvlist_lookup_nvlist(zhp->zpool_config, ZPOOL_CONFIG_VDEV_TREE,
&nvroot) == 0);
- if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
- &spares, &nspares) == 0) {
- for (i = 0; i < nspares; i++) {
- verify(nvlist_lookup_uint64(spares[i],
- ZPOOL_CONFIG_GUID, &spare_guid) == 0);
- if (guid == spare_guid)
+ if (nvlist_lookup_nvlist_array(nvroot, type, &list, &count) == 0) {
+ for (i = 0; i < count; i++) {
+ verify(nvlist_lookup_uint64(list[i], ZPOOL_CONFIG_GUID,
+ &target_guid) == 0);
+ if (guid == target_guid)
return (B_TRUE);
}
}
@@ -1295,21 +1330,26 @@ zpool_vdev_online(zpool_handle_t *zhp, const char *path, int flags,
zfs_cmd_t zc = { 0 };
char msg[1024];
nvlist_t *tgt;
- boolean_t avail_spare;
+ boolean_t avail_spare, l2cache;
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) snprintf(msg, sizeof (msg),
dgettext(TEXT_DOMAIN, "cannot online %s"), path);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == NULL)
+ if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache)) == NULL)
return (zfs_error(hdl, EZFS_NODEVICE, msg));
verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
- if (avail_spare || is_spare(zhp, zc.zc_guid) == B_TRUE)
+ if (avail_spare ||
+ is_guid_type(zhp, zc.zc_guid, ZPOOL_CONFIG_SPARES) == B_TRUE)
return (zfs_error(hdl, EZFS_ISSPARE, msg));
+ if (l2cache ||
+ is_guid_type(zhp, zc.zc_guid, ZPOOL_CONFIG_L2CACHE) == B_TRUE)
+ return (zfs_error(hdl, EZFS_ISL2CACHE, msg));
+
zc.zc_cookie = VDEV_STATE_ONLINE;
zc.zc_obj = flags;
@@ -1330,21 +1370,26 @@ zpool_vdev_offline(zpool_handle_t *zhp, const char *path, boolean_t istmp)
zfs_cmd_t zc = { 0 };
char msg[1024];
nvlist_t *tgt;
- boolean_t avail_spare;
+ boolean_t avail_spare, l2cache;
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) snprintf(msg, sizeof (msg),
dgettext(TEXT_DOMAIN, "cannot offline %s"), path);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == NULL)
+ if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache)) == NULL)
return (zfs_error(hdl, EZFS_NODEVICE, msg));
verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
- if (avail_spare || is_spare(zhp, zc.zc_guid) == B_TRUE)
+ if (avail_spare ||
+ is_guid_type(zhp, zc.zc_guid, ZPOOL_CONFIG_SPARES) == B_TRUE)
return (zfs_error(hdl, EZFS_ISSPARE, msg));
+ if (l2cache ||
+ is_guid_type(zhp, zc.zc_guid, ZPOOL_CONFIG_L2CACHE) == B_TRUE)
+ return (zfs_error(hdl, EZFS_ISL2CACHE, msg));
+
zc.zc_cookie = VDEV_STATE_OFFLINE;
zc.zc_obj = istmp ? ZFS_OFFLINE_TEMPORARY : 0;
@@ -1461,7 +1506,7 @@ zpool_vdev_attach(zpool_handle_t *zhp,
char msg[1024];
int ret;
nvlist_t *tgt;
- boolean_t avail_spare;
+ boolean_t avail_spare, l2cache;
uint64_t val, is_log;
char *path;
nvlist_t **child;
@@ -1477,12 +1522,15 @@ zpool_vdev_attach(zpool_handle_t *zhp,
"cannot attach %s to %s"), new_disk, old_disk);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, old_disk, &avail_spare)) == 0)
+ if ((tgt = zpool_find_vdev(zhp, old_disk, &avail_spare, &l2cache)) == 0)
return (zfs_error(hdl, EZFS_NODEVICE, msg));
if (avail_spare)
return (zfs_error(hdl, EZFS_ISSPARE, msg));
+ if (l2cache)
+ return (zfs_error(hdl, EZFS_ISL2CACHE, msg));
+
verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
zc.zc_cookie = replacing;
@@ -1503,7 +1551,7 @@ zpool_vdev_attach(zpool_handle_t *zhp,
if (replacing &&
nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_IS_SPARE, &val) == 0 &&
nvlist_lookup_string(child[0], ZPOOL_CONFIG_PATH, &path) == 0 &&
- (zpool_find_vdev(zhp, path, &avail_spare) == NULL ||
+ (zpool_find_vdev(zhp, path, &avail_spare, &l2cache) == NULL ||
!avail_spare) && is_replacing_spare(config_root, tgt, 1)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"can only be replaced by another hot spare"));
@@ -1516,8 +1564,8 @@ zpool_vdev_attach(zpool_handle_t *zhp,
*/
if (replacing &&
nvlist_lookup_string(child[0], ZPOOL_CONFIG_PATH, &path) == 0 &&
- zpool_find_vdev(zhp, path, &avail_spare) != NULL && avail_spare &&
- is_replacing_spare(config_root, tgt, 0)) {
+ zpool_find_vdev(zhp, path, &avail_spare, &l2cache) != NULL &&
+ avail_spare && is_replacing_spare(config_root, tgt, 0)) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"device has already been replaced with a spare"));
return (zfs_error(hdl, EZFS_BADTARGET, msg));
@@ -1612,19 +1660,22 @@ zpool_vdev_detach(zpool_handle_t *zhp, const char *path)
zfs_cmd_t zc = { 0 };
char msg[1024];
nvlist_t *tgt;
- boolean_t avail_spare;
+ boolean_t avail_spare, l2cache;
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) snprintf(msg, sizeof (msg),
dgettext(TEXT_DOMAIN, "cannot detach %s"), path);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == 0)
+ if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache)) == 0)
return (zfs_error(hdl, EZFS_NODEVICE, msg));
if (avail_spare)
return (zfs_error(hdl, EZFS_ISSPARE, msg));
+ if (l2cache)
+ return (zfs_error(hdl, EZFS_ISL2CACHE, msg));
+
verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID, &zc.zc_guid) == 0);
if (zfs_ioctl(hdl, ZFS_IOC_VDEV_DETACH, &zc) == 0)
@@ -1656,7 +1707,8 @@ zpool_vdev_detach(zpool_handle_t *zhp, const char *path)
}
/*
- * Remove the given device. Currently, this is supported only for hot spares.
+ * Remove the given device. Currently, this is supported only for hot spares
+ * and level 2 cache devices.
*/
int
zpool_vdev_remove(zpool_handle_t *zhp, const char *path)
@@ -1664,19 +1716,20 @@ zpool_vdev_remove(zpool_handle_t *zhp, const char *path)
zfs_cmd_t zc = { 0 };
char msg[1024];
nvlist_t *tgt;
- boolean_t avail_spare;
+ boolean_t avail_spare, l2cache;
libzfs_handle_t *hdl = zhp->zpool_hdl;
(void) snprintf(msg, sizeof (msg),
dgettext(TEXT_DOMAIN, "cannot remove %s"), path);
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == 0)
+ if ((tgt = zpool_find_vdev(zhp, path, &avail_spare, &l2cache)) == 0)
return (zfs_error(hdl, EZFS_NODEVICE, msg));
- if (!avail_spare) {
+ if (!avail_spare && !l2cache) {
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
- "only inactive hot spares can be removed"));
+ "only inactive hot spares or cache devices "
+ "can be removed"));
return (zfs_error(hdl, EZFS_NODEVICE, msg));
}
@@ -1697,7 +1750,7 @@ zpool_clear(zpool_handle_t *zhp, const char *path)
zfs_cmd_t zc = { 0 };
char msg[1024];
nvlist_t *tgt;
- boolean_t avail_spare;
+ boolean_t avail_spare, l2cache;
libzfs_handle_t *hdl = zhp->zpool_hdl;
if (path)
@@ -1711,9 +1764,14 @@ zpool_clear(zpool_handle_t *zhp, const char *path)
(void) strlcpy(zc.zc_name, zhp->zpool_name, sizeof (zc.zc_name));
if (path) {
- if ((tgt = zpool_find_vdev(zhp, path, &avail_spare)) == 0)
+ if ((tgt = zpool_find_vdev(zhp, path, &avail_spare,
+ &l2cache)) == 0)
return (zfs_error(hdl, EZFS_NODEVICE, msg));
+ /*
+ * Don't allow error clearing for hot spares. Do allow
+ * error clearing for l2cache devices.
+ */
if (avail_spare)
return (zfs_error(hdl, EZFS_ISSPARE, msg));
diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c
index d2005867e3..b58da2c0bf 100644
--- a/usr/src/lib/libzfs/common/libzfs_util.c
+++ b/usr/src/lib/libzfs/common/libzfs_util.c
@@ -201,6 +201,8 @@ libzfs_error_description(libzfs_handle_t *hdl)
" modified"));
case EZFS_BADCACHE:
return (dgettext(TEXT_DOMAIN, "invalid or missing cache file"));
+ case EZFS_ISL2CACHE:
+ return (dgettext(TEXT_DOMAIN, "device is in use as a cache"));
case EZFS_UNKNOWN:
return (dgettext(TEXT_DOMAIN, "unknown error"));
default: