summaryrefslogtreecommitdiff
path: root/usr/src/lib/libzfs
diff options
context:
space:
mode:
authorBrian Behlendorf <behlendorf1@llnl.gov>2019-09-09 08:10:46 -0600
committerJerry Jelinek <jerry.jelinek@joyent.com>2019-09-10 10:28:15 -0600
commit084fd14f7c3336eb67ee283cabad2da8998b00d6 (patch)
treedd1bedf49b682d5802edc30b15127c012e15647e /usr/src/lib/libzfs
parentada70d038ca186abd71e023e9dec2c5148e9b36f (diff)
downloadillumos-joyent-084fd14f7c3336eb67ee283cabad2da8998b00d6.tar.gz
1701 ZFS to support UNMAP/TRIM for SSD
Portions contributed by: Jerry Jelinek <jerry.jelinek@joyent.com> Portions contributed by: George Wilson <george.wilson@delphix.com> Portions contributed by: Saso Kiselkov <saso.kiselkov@nexenta.com> Portions contributed by: Tim Chase <tim@chase2k.com> Portions contributed by: Chunwei Chen <tuxoko@gmail.com> Reviewed by: Matt Ahrens <mahrens@delphix.com> Reviewed by: loli10K <ezomori.nozomu@gmail.com> Reviewed by: Tim Chase <tim@chase2k.com> Reviewed by: George Wilson <george.wilson@delphix.com> Reviewed by: Richard Elling <Richard.Elling@RichardElling.com> Reviewed by: Tom Caputi <tcaputi@datto.com> Reviewed by: Jorgen Lundman <lundman@lundman.net> Reviewed by: Serapheim Dimitropoulos <serapheim@delphix.com> Reviewed by: C Fraire <cfraire@me.com> Reviewed by: Kody Kantor <kody.kantor@joyent.com> Approved by: Dan McDonald <danmcd@joyent.com>
Diffstat (limited to 'usr/src/lib/libzfs')
-rw-r--r--usr/src/lib/libzfs/common/libzfs.h19
-rw-r--r--usr/src/lib/libzfs/common/libzfs_pool.c210
-rw-r--r--usr/src/lib/libzfs/common/libzfs_util.c11
-rw-r--r--usr/src/lib/libzfs/common/mapfile-vers2
4 files changed, 199 insertions, 43 deletions
diff --git a/usr/src/lib/libzfs/common/libzfs.h b/usr/src/lib/libzfs/common/libzfs.h
index b23995b1f9..36b70c11fb 100644
--- a/usr/src/lib/libzfs/common/libzfs.h
+++ b/usr/src/lib/libzfs/common/libzfs.h
@@ -131,6 +131,7 @@ typedef enum zfs_error {
EZFS_POOLREADONLY, /* pool is in read-only mode */
EZFS_SCRUB_PAUSED, /* scrub currently paused */
EZFS_ACTIVE_POOL, /* pool is imported on a different system */
+ EZFS_CRYPTOFAILED, /* failed to setup encryption */
EZFS_NO_PENDING, /* cannot cancel, no operation is pending */
EZFS_CHECKPOINT_EXISTS, /* checkpoint exists */
EZFS_DISCARDING_CHECKPOINT, /* currently discarding a checkpoint */
@@ -141,8 +142,10 @@ typedef enum zfs_error {
EZFS_INITIALIZING, /* currently initializing */
EZFS_NO_INITIALIZE, /* no active initialize */
EZFS_WRONG_PARENT, /* invalid parent dataset (e.g ZVOL) */
+ EZFS_TRIMMING, /* currently trimming */
+ EZFS_NO_TRIM, /* no active trim */
+ EZFS_TRIM_NOTSUP, /* device does not support trim */
EZFS_NO_RESILVER_DEFER, /* pool doesn't support resilver_defer */
- EZFS_CRYPTOFAILED, /* failed to setup encryption */
EZFS_UNKNOWN
} zfs_error_t;
@@ -264,12 +267,26 @@ typedef struct splitflags {
int name_flags;
} splitflags_t;
+typedef struct trimflags {
+ /* requested vdevs are for the entire pool */
+ boolean_t fullpool;
+
+ /* request a secure trim, requires support from device */
+ boolean_t secure;
+
+ /* trim at the requested rate in bytes/second */
+ uint64_t rate;
+} trimflags_t;
+
/*
* Functions to manipulate pool and vdev state
*/
extern int zpool_scan(zpool_handle_t *, pool_scan_func_t, pool_scrub_cmd_t);
extern int zpool_initialize(zpool_handle_t *, pool_initialize_func_t,
nvlist_t *);
+extern int zpool_trim(zpool_handle_t *, pool_trim_func_t, nvlist_t *,
+ trimflags_t *);
+
extern int zpool_clear(zpool_handle_t *, const char *, nvlist_t *);
extern int zpool_reguid(zpool_handle_t *);
extern int zpool_reopen(zpool_handle_t *);
diff --git a/usr/src/lib/libzfs/common/libzfs_pool.c b/usr/src/lib/libzfs/common/libzfs_pool.c
index e4b3e6b72f..2632eab321 100644
--- a/usr/src/lib/libzfs/common/libzfs_pool.c
+++ b/usr/src/lib/libzfs/common/libzfs_pool.c
@@ -2029,6 +2029,57 @@ zpool_import_props(libzfs_handle_t *hdl, nvlist_t *config, const char *newname,
}
/*
+ * Translate vdev names to guids. If a vdev_path is determined to be
+ * unsuitable then a vd_errlist is allocated and the vdev path and errno
+ * are added to it.
+ */
+static int
+zpool_translate_vdev_guids(zpool_handle_t *zhp, nvlist_t *vds,
+ nvlist_t *vdev_guids, nvlist_t *guids_to_paths, nvlist_t **vd_errlist)
+{
+ nvlist_t *errlist = NULL;
+ int error = 0;
+
+ for (nvpair_t *elem = nvlist_next_nvpair(vds, NULL); elem != NULL;
+ elem = nvlist_next_nvpair(vds, elem)) {
+ boolean_t spare, cache;
+
+ char *vd_path = nvpair_name(elem);
+ nvlist_t *tgt = zpool_find_vdev(zhp, vd_path, &spare, &cache,
+ NULL);
+
+ if ((tgt == NULL) || cache || spare) {
+ if (errlist == NULL) {
+ errlist = fnvlist_alloc();
+ error = EINVAL;
+ }
+
+ uint64_t err = (tgt == NULL) ? EZFS_NODEVICE :
+ (spare ? EZFS_ISSPARE : EZFS_ISL2CACHE);
+ fnvlist_add_int64(errlist, vd_path, err);
+ continue;
+ }
+
+ uint64_t guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID);
+ fnvlist_add_uint64(vdev_guids, vd_path, guid);
+
+ char msg[MAXNAMELEN];
+ (void) snprintf(msg, sizeof (msg), "%llu", (u_longlong_t)guid);
+ fnvlist_add_string(guids_to_paths, msg, vd_path);
+ }
+
+ if (error != 0) {
+ verify(errlist != NULL);
+ if (vd_errlist != NULL)
+ *vd_errlist = errlist;
+ else
+ fnvlist_free(errlist);
+ }
+
+ return (error);
+}
+
+/*
* Scan the pool.
*/
int
@@ -2129,72 +2180,151 @@ zpool_initialize(zpool_handle_t *zhp, pool_initialize_func_t cmd_type,
nvlist_t *vds)
{
char msg[1024];
- libzfs_handle_t *hdl = zhp->zpool_hdl;
-
- nvlist_t *errlist;
+ int err;
- /* translate vdev names to guids */
nvlist_t *vdev_guids = fnvlist_alloc();
nvlist_t *guids_to_paths = fnvlist_alloc();
- boolean_t spare, cache;
- nvlist_t *tgt;
+ nvlist_t *vd_errlist = NULL;
+ nvlist_t *errlist;
nvpair_t *elem;
- for (elem = nvlist_next_nvpair(vds, NULL); elem != NULL;
- elem = nvlist_next_nvpair(vds, elem)) {
- char *vd_path = nvpair_name(elem);
- tgt = zpool_find_vdev(zhp, vd_path, &spare, &cache, NULL);
+ err = zpool_translate_vdev_guids(zhp, vds, vdev_guids,
+ guids_to_paths, &vd_errlist);
- if ((tgt == NULL) || cache || spare) {
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "cannot initialize '%s'"),
- vd_path);
- int err = (tgt == NULL) ? EZFS_NODEVICE :
- (spare ? EZFS_ISSPARE : EZFS_ISL2CACHE);
+ if (err == 0) {
+ err = lzc_initialize(zhp->zpool_name, cmd_type,
+ vdev_guids, &errlist);
+ if (err == 0) {
fnvlist_free(vdev_guids);
fnvlist_free(guids_to_paths);
- return (zfs_error(hdl, err, msg));
+ return (0);
}
- uint64_t guid = fnvlist_lookup_uint64(tgt, ZPOOL_CONFIG_GUID);
- fnvlist_add_uint64(vdev_guids, vd_path, guid);
+ if (errlist != NULL) {
+ vd_errlist = fnvlist_lookup_nvlist(errlist,
+ ZPOOL_INITIALIZE_VDEVS);
+ }
- (void) snprintf(msg, sizeof (msg), "%llu", guid);
- fnvlist_add_string(guids_to_paths, msg, vd_path);
+ (void) snprintf(msg, sizeof (msg),
+ dgettext(TEXT_DOMAIN, "operation failed"));
+ } else {
+ verify(vd_errlist != NULL);
+ }
+
+ for (elem = nvlist_next_nvpair(vd_errlist, NULL); elem != NULL;
+ elem = nvlist_next_nvpair(vd_errlist, elem)) {
+ int64_t vd_error = xlate_init_err(fnvpair_value_int64(elem));
+ char *path;
+
+ if (nvlist_lookup_string(guids_to_paths, nvpair_name(elem),
+ &path) != 0)
+ path = nvpair_name(elem);
+
+ (void) zfs_error_fmt(zhp->zpool_hdl, vd_error,
+ "cannot initialize '%s'", path);
}
- int err = lzc_initialize(zhp->zpool_name, cmd_type, vdev_guids,
- &errlist);
fnvlist_free(vdev_guids);
+ fnvlist_free(guids_to_paths);
- if (err == 0) {
- fnvlist_free(guids_to_paths);
- return (0);
+ if (vd_errlist != NULL) {
+ fnvlist_free(vd_errlist);
+ return (-1);
}
+ return (zpool_standard_error(zhp->zpool_hdl, err, msg));
+}
+
+static int
+xlate_trim_err(int err)
+{
+ switch (err) {
+ case ENODEV:
+ return (EZFS_NODEVICE);
+ case EINVAL:
+ case EROFS:
+ return (EZFS_BADDEV);
+ case EBUSY:
+ return (EZFS_TRIMMING);
+ case ESRCH:
+ return (EZFS_NO_TRIM);
+ case EOPNOTSUPP:
+ return (EZFS_TRIM_NOTSUP);
+ }
+ return (err);
+}
+
+/*
+ * Begin, suspend, or cancel the TRIM (discarding of all free blocks) for
+ * the given vdevs in the given pool.
+ */
+int
+zpool_trim(zpool_handle_t *zhp, pool_trim_func_t cmd_type, nvlist_t *vds,
+ trimflags_t *trim_flags)
+{
+ char msg[1024];
+ int err;
+
+ nvlist_t *vdev_guids = fnvlist_alloc();
+ nvlist_t *guids_to_paths = fnvlist_alloc();
nvlist_t *vd_errlist = NULL;
- if (errlist != NULL) {
- vd_errlist = fnvlist_lookup_nvlist(errlist,
- ZPOOL_INITIALIZE_VDEVS);
+ nvlist_t *errlist;
+ nvpair_t *elem;
+
+ err = zpool_translate_vdev_guids(zhp, vds, vdev_guids,
+ guids_to_paths, &vd_errlist);
+ if (err == 0) {
+ err = lzc_trim(zhp->zpool_name, cmd_type, trim_flags->rate,
+ trim_flags->secure, vdev_guids, &errlist);
+ if (err == 0) {
+ fnvlist_free(vdev_guids);
+ fnvlist_free(guids_to_paths);
+ return (0);
+ }
+
+ if (errlist != NULL) {
+ vd_errlist = fnvlist_lookup_nvlist(errlist,
+ ZPOOL_TRIM_VDEVS);
+ }
+
+ (void) snprintf(msg, sizeof (msg),
+ dgettext(TEXT_DOMAIN, "operation failed"));
+ } else {
+ verify(vd_errlist != NULL);
}
- (void) snprintf(msg, sizeof (msg),
- dgettext(TEXT_DOMAIN, "operation failed"));
+ for (elem = nvlist_next_nvpair(vd_errlist, NULL);
+ elem != NULL; elem = nvlist_next_nvpair(vd_errlist, elem)) {
+ int64_t vd_error = xlate_trim_err(fnvpair_value_int64(elem));
+ char *path;
+ /*
+ * If only the pool was specified, and it was not a secure
+ * trim then suppress warnings for individual vdevs which
+ * do not support trimming.
+ */
+ if (vd_error == EZFS_TRIM_NOTSUP &&
+ trim_flags->fullpool &&
+ !trim_flags->secure) {
+ continue;
+ }
- for (elem = nvlist_next_nvpair(vd_errlist, NULL); elem != NULL;
- elem = nvlist_next_nvpair(vd_errlist, elem)) {
- int64_t vd_error = xlate_init_err(fnvpair_value_int64(elem));
- char *path = fnvlist_lookup_string(guids_to_paths,
- nvpair_name(elem));
- (void) zfs_error_fmt(hdl, vd_error, "cannot initialize '%s'",
- path);
+ if (nvlist_lookup_string(guids_to_paths, nvpair_name(elem),
+ &path) != 0)
+ path = nvpair_name(elem);
+
+ (void) zfs_error_fmt(zhp->zpool_hdl, vd_error,
+ "cannot trim '%s'", path);
}
+ fnvlist_free(vdev_guids);
fnvlist_free(guids_to_paths);
- if (vd_errlist != NULL)
+
+ if (vd_errlist != NULL) {
+ fnvlist_free(vd_errlist);
return (-1);
+ }
- return (zpool_standard_error(hdl, err, msg));
+ return (zpool_standard_error(zhp->zpool_hdl, err, msg));
}
/*
diff --git a/usr/src/lib/libzfs/common/libzfs_util.c b/usr/src/lib/libzfs/common/libzfs_util.c
index 696a9e10d0..75eb9722b3 100644
--- a/usr/src/lib/libzfs/common/libzfs_util.c
+++ b/usr/src/lib/libzfs/common/libzfs_util.c
@@ -253,6 +253,8 @@ libzfs_error_description(libzfs_handle_t *hdl)
case EZFS_ACTIVE_POOL:
return (dgettext(TEXT_DOMAIN, "pool is imported on a "
"different host"));
+ case EZFS_CRYPTOFAILED:
+ return (dgettext(TEXT_DOMAIN, "encryption failure"));
case EZFS_TOOMANY:
return (dgettext(TEXT_DOMAIN, "argument list too long"));
case EZFS_INITIALIZING:
@@ -262,11 +264,16 @@ libzfs_error_description(libzfs_handle_t *hdl)
"initialization"));
case EZFS_WRONG_PARENT:
return (dgettext(TEXT_DOMAIN, "invalid parent dataset"));
+ case EZFS_TRIMMING:
+ return (dgettext(TEXT_DOMAIN, "currently trimming"));
+ case EZFS_NO_TRIM:
+ return (dgettext(TEXT_DOMAIN, "there is no active trim"));
+ case EZFS_TRIM_NOTSUP:
+ return (dgettext(TEXT_DOMAIN, "trim operations are not "
+ "supported by this device"));
case EZFS_NO_RESILVER_DEFER:
return (dgettext(TEXT_DOMAIN, "this action requires the "
"resilver_defer feature"));
- case EZFS_CRYPTOFAILED:
- return (dgettext(TEXT_DOMAIN, "encryption failure"));
case EZFS_UNKNOWN:
return (dgettext(TEXT_DOMAIN, "unknown error"));
default:
diff --git a/usr/src/lib/libzfs/common/mapfile-vers b/usr/src/lib/libzfs/common/mapfile-vers
index f4412bbd9a..95fb983e8c 100644
--- a/usr/src/lib/libzfs/common/mapfile-vers
+++ b/usr/src/lib/libzfs/common/mapfile-vers
@@ -23,6 +23,7 @@
# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2011, 2017 by Delphix. All rights reserved.
# Copyright 2016 Nexenta Systems, Inc.
+# Copyright 2019 Joyent, Inc.
#
#
@@ -251,6 +252,7 @@ SYMBOL_VERSION SUNWprivate_1.1 {
zpool_skip_pool;
zpool_state_to_name;
zpool_sync_one;
+ zpool_trim;
zpool_tryimport;
zpool_unmount_datasets;
zpool_upgrade;