aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorBrian Behlendorf <[email protected]>2019-03-29 09:13:20 -0700
committerGitHub <[email protected]>2019-03-29 09:13:20 -0700
commit1b939560be5c51deecf875af9dada9d094633bf7 (patch)
tree2a780b838134636ddbc65f89d227e37c74abe17b /tests
parentf94b3cbf43d62f4962e71cfe7ba8c6f0602e2a45 (diff)
Add TRIM support
UNMAP/TRIM support is a frequently-requested feature to help prevent performance from degrading on SSDs and on various other SAN-like storage back-ends. By issuing UNMAP/TRIM commands for sectors which are no longer allocated the underlying device can often more efficiently manage itself. This TRIM implementation is modeled on the `zpool initialize` feature which writes a pattern to all unallocated space in the pool. The new `zpool trim` command uses the same vdev_xlate() code to calculate what sectors are unallocated, the same per- vdev TRIM thread model and locking, and the same basic CLI for a consistent user experience. The core difference is that instead of writing a pattern it will issue UNMAP/TRIM commands for those extents. The zio pipeline was updated to accommodate this by adding a new ZIO_TYPE_TRIM type and associated spa taskq. This new type makes is straight forward to add the platform specific TRIM/UNMAP calls to vdev_disk.c and vdev_file.c. These new ZIO_TYPE_TRIM zios are handled largely the same way as ZIO_TYPE_READs or ZIO_TYPE_WRITEs. This makes it possible to largely avoid changing the pipieline, one exception is that TRIM zio's may exceed the 16M block size limit since they contain no data. In addition to the manual `zpool trim` command, a background automatic TRIM was added and is controlled by the 'autotrim' property. It relies on the exact same infrastructure as the manual TRIM. However, instead of relying on the extents in a metaslab's ms_allocatable range tree, a ms_trim tree is kept per metaslab. When 'autotrim=on', ranges added back to the ms_allocatable tree are also added to the ms_free tree. The ms_free tree is then periodically consumed by an autotrim thread which systematically walks a top level vdev's metaslabs. Since the automatic TRIM will skip ranges it considers too small there is value in occasionally running a full `zpool trim`. This may occur when the freed blocks are small and not enough time was allowed to aggregate them. An automatic TRIM and a manual `zpool trim` may be run concurrently, in which case the automatic TRIM will yield to the manual TRIM. Reviewed-by: Jorgen Lundman <[email protected]> Reviewed-by: Tim Chase <[email protected]> Reviewed-by: Matt Ahrens <[email protected]> Reviewed-by: George Wilson <[email protected]> Reviewed-by: Serapheim Dimitropoulos <[email protected]> Contributions-by: Saso Kiselkov <[email protected]> Contributions-by: Tim Chase <[email protected]> Contributions-by: Chunwei Chen <[email protected]> Signed-off-by: Brian Behlendorf <[email protected]> Closes #8419 Closes #598
Diffstat (limited to 'tests')
-rw-r--r--tests/runfiles/linux.run16
-rwxr-xr-xtests/test-runner/bin/zts-report.py9
-rw-r--r--tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c25
-rw-r--r--tests/zfs-tests/include/libtest.shlib10
-rw-r--r--tests/zfs-tests/tests/functional/Makefile.am1
-rw-r--r--tests/zfs-tests/tests/functional/cli_root/Makefile.am1
-rw-r--r--tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg1
-rw-r--r--tests/zfs-tests/tests/functional/cli_root/zpool_trim/Makefile.am22
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/cleanup.ksh34
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/setup.ksh37
-rw-r--r--tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib43
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_attach_detach_add_remove.ksh64
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_import_export.ksh88
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_multiple.ksh65
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_neg.ksh53
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_offline_export_import_online.ksh62
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_online_offline.ksh70
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_partial.ksh114
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_rate.ksh90
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_rate_neg.ksh53
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_secure.ksh59
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_split.ksh60
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_start_and_cancel_neg.ksh56
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_start_and_cancel_pos.ksh48
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_suspend_resume.ksh74
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_unsupported_vdevs.ksh70
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_verify_checksums.ksh69
-rwxr-xr-xtests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_verify_trimmed.ksh81
-rw-r--r--tests/zfs-tests/tests/functional/trim/Makefile.am11
-rwxr-xr-xtests/zfs-tests/tests/functional/trim/autotrim_config.ksh103
-rwxr-xr-xtests/zfs-tests/tests/functional/trim/autotrim_integrity.ksh87
-rwxr-xr-xtests/zfs-tests/tests/functional/trim/autotrim_trim_integrity.ksh93
-rwxr-xr-xtests/zfs-tests/tests/functional/trim/cleanup.ksh48
-rwxr-xr-xtests/zfs-tests/tests/functional/trim/setup.ksh37
-rw-r--r--tests/zfs-tests/tests/functional/trim/trim.cfg33
-rw-r--r--tests/zfs-tests/tests/functional/trim/trim.kshlib154
-rwxr-xr-xtests/zfs-tests/tests/functional/trim/trim_config.ksh103
-rwxr-xr-xtests/zfs-tests/tests/functional/trim/trim_integrity.ksh89
38 files changed, 2127 insertions, 6 deletions
diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run
index 211daaf27..aee1ad9ea 100644
--- a/tests/runfiles/linux.run
+++ b/tests/runfiles/linux.run
@@ -470,6 +470,17 @@ tags = ['functional', 'cli_root', 'zpool_status']
tests = ['zpool_sync_001_pos', 'zpool_sync_002_neg']
tags = ['functional', 'cli_root', 'zpool_sync']
+[tests/functional/cli_root/zpool_trim]
+tests = ['zpool_trim_attach_detach_add_remove',
+ 'zpool_trim_import_export', 'zpool_trim_multiple', 'zpool_trim_neg',
+ 'zpool_trim_offline_export_import_online', 'zpool_trim_online_offline',
+ 'zpool_trim_partial', 'zpool_trim_rate', 'zpool_trim_rate_neg',
+ 'zpool_trim_secure', 'zpool_trim_split', 'zpool_trim_start_and_cancel_neg',
+ 'zpool_trim_start_and_cancel_pos', 'zpool_trim_suspend_resume',
+ 'zpool_trim_unsupported_vdevs', 'zpool_trim_verify_checksums',
+ 'zpool_trim_verify_trimmed']
+tags = ['functional', 'zpool_trim']
+
[tests/functional/cli_root/zpool_upgrade]
tests = ['zpool_upgrade_001_pos', 'zpool_upgrade_002_pos',
'zpool_upgrade_003_pos', 'zpool_upgrade_004_pos',
@@ -839,6 +850,11 @@ tags = ['functional', 'threadsappend']
tests = ['tmpfile_001_pos', 'tmpfile_002_pos', 'tmpfile_003_pos']
tags = ['functional', 'tmpfile']
+[tests/functional/trim]
+tests = ['autotrim_integrity', 'autotrim_config', 'autotrim_trim_integrity',
+ 'trim_integrity', 'trim_config']
+tags = ['functional', 'trim']
+
[tests/functional/truncate]
tests = ['truncate_001_pos', 'truncate_002_pos', 'truncate_timestamps']
tags = ['functional', 'truncate']
diff --git a/tests/test-runner/bin/zts-report.py b/tests/test-runner/bin/zts-report.py
index 953427bba..d046c13a5 100755
--- a/tests/test-runner/bin/zts-report.py
+++ b/tests/test-runner/bin/zts-report.py
@@ -129,6 +129,13 @@ enospc_reason = 'Exact free space reporting is not guaranteed'
fio_reason = 'Fio v2.3 or newer required'
#
+# Some tests require that the DISKS provided support the discard operation.
+# Normally this is not an issue because loop back devices are used for DISKS
+# and they support discard (TRIM/UNMAP).
+#
+trim_reason = 'DISKS must support discard (TRIM/UNMAP)'
+
+#
# Some tests are not applicable to Linux or need to be updated to operate
# in the manor required by Linux. Any tests which are skipped for this
# reason will be suppressed in the final analysis output.
@@ -235,6 +242,7 @@ maybe = {
['FAIL', rewind_reason],
'cli_root/zpool_import/zpool_import_missing_003_pos': ['SKIP', '6839'],
'cli_root/zpool_remove/setup': ['SKIP', disk_reason],
+ 'cli_root/zpool_trim/setup': ['SKIP', trim_reason],
'cli_root/zpool_upgrade/zpool_upgrade_004_pos': ['FAIL', '6141'],
'cli_user/misc/arc_summary3_001_pos': ['SKIP', python_reason],
'delegate/setup': ['SKIP', exec_reason],
@@ -267,6 +275,7 @@ maybe = {
'snapused/snapused_004_pos': ['FAIL', '5513'],
'tmpfile/setup': ['SKIP', tmpfile_reason],
'threadsappend/threadsappend_001_pos': ['FAIL', '6136'],
+ 'trim/setup': ['SKIP', trim_reason],
'upgrade/upgrade_projectquota_001_pos': ['SKIP', project_id_reason],
'user_namespace/setup': ['SKIP', user_ns_reason],
'userquota/setup': ['SKIP', exec_reason],
diff --git a/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c b/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c
index b927cd4e6..8855a5358 100644
--- a/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c
+++ b/tests/zfs-tests/cmd/libzfs_input_check/libzfs_input_check.c
@@ -169,7 +169,7 @@ lzc_ioctl_run(zfs_ioc_t ioc, const char *name, nvlist_t *innvl, int expected)
}
/*
- * Test each ioc for the folowing ioctl input errors:
+ * Test each ioc for the following ioctl input errors:
* ZFS_ERR_IOC_ARG_UNAVAIL an input argument is not supported by kernel
* ZFS_ERR_IOC_ARG_REQUIRED a required input argument is missing
* ZFS_ERR_IOC_ARG_BADTYPE an input argument has an invalid type
@@ -650,7 +650,7 @@ test_vdev_initialize(const char *pool)
fnvlist_add_uint64(vdev_guids, "path", 0xdeadbeefdeadbeef);
fnvlist_add_uint64(required, ZPOOL_INITIALIZE_COMMAND,
- POOL_INITIALIZE_DO);
+ POOL_INITIALIZE_START);
fnvlist_add_nvlist(required, ZPOOL_INITIALIZE_VDEVS, vdev_guids);
IOC_INPUT_TEST(ZFS_IOC_POOL_INITIALIZE, pool, required, NULL, EINVAL);
@@ -658,6 +658,25 @@ test_vdev_initialize(const char *pool)
nvlist_free(required);
}
+static void
+test_vdev_trim(const char *pool)
+{
+ nvlist_t *required = fnvlist_alloc();
+ nvlist_t *optional = fnvlist_alloc();
+ nvlist_t *vdev_guids = fnvlist_alloc();
+
+ fnvlist_add_uint64(vdev_guids, "path", 0xdeadbeefdeadbeef);
+ fnvlist_add_uint64(required, ZPOOL_TRIM_COMMAND, POOL_TRIM_START);
+ fnvlist_add_nvlist(required, ZPOOL_TRIM_VDEVS, vdev_guids);
+ fnvlist_add_uint64(optional, ZPOOL_TRIM_RATE, 1ULL << 30);
+ fnvlist_add_boolean_value(optional, ZPOOL_TRIM_SECURE, B_TRUE);
+
+ IOC_INPUT_TEST(ZFS_IOC_POOL_TRIM, pool, required, optional, EINVAL);
+ nvlist_free(vdev_guids);
+ nvlist_free(optional);
+ nvlist_free(required);
+}
+
static int
zfs_destroy(const char *dataset)
{
@@ -749,6 +768,7 @@ zfs_ioc_input_tests(const char *pool)
test_unload_key(dataset);
test_vdev_initialize(pool);
+ test_vdev_trim(pool);
/*
* cleanup
@@ -888,6 +908,7 @@ validate_ioc_values(void)
ZFS_IOC_BASE + 77 == ZFS_IOC_POOL_CHECKPOINT &&
ZFS_IOC_BASE + 78 == ZFS_IOC_POOL_DISCARD_CHECKPOINT &&
ZFS_IOC_BASE + 79 == ZFS_IOC_POOL_INITIALIZE &&
+ ZFS_IOC_BASE + 80 == ZFS_IOC_POOL_TRIM &&
LINUX_IOC_BASE + 1 == ZFS_IOC_EVENTS_NEXT &&
LINUX_IOC_BASE + 2 == ZFS_IOC_EVENTS_CLEAR &&
LINUX_IOC_BASE + 3 == ZFS_IOC_EVENTS_SEEK);
diff --git a/tests/zfs-tests/include/libtest.shlib b/tests/zfs-tests/include/libtest.shlib
index 045c12207..748a4f96d 100644
--- a/tests/zfs-tests/include/libtest.shlib
+++ b/tests/zfs-tests/include/libtest.shlib
@@ -23,10 +23,12 @@
# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
# Copyright (c) 2012, 2017 by Delphix. All rights reserved.
-# Copyright 2016 Nexenta Systems, Inc.
+# Copyright (c) 2017 by Tim Chase. All rights reserved.
+# Copyright (c) 2017 by Nexenta Systems, Inc. All rights reserved.
# Copyright (c) 2017 Lawrence Livermore National Security, LLC.
# Copyright (c) 2017 Datto Inc.
# Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
+# Use is subject to license terms.
#
. ${STF_TOOLS}/include/logapi.shlib
@@ -1092,14 +1094,14 @@ function fill_fs # destdir dirnum filenum bytes num_writes data
typeset -i filenum=${3:-50}
typeset -i bytes=${4:-8192}
typeset -i num_writes=${5:-10240}
- typeset -i data=${6:-0}
+ typeset data=${6:-0}
typeset -i odirnum=1
typeset -i idirnum=0
typeset -i fn=0
typeset -i retval=0
- log_must mkdir -p $destdir/$idirnum
+ mkdir -p $destdir/$idirnum
while (($odirnum > 0)); do
if ((dirnum >= 0 && idirnum >= dirnum)); then
odirnum=0
@@ -1115,7 +1117,7 @@ function fill_fs # destdir dirnum filenum bytes num_writes data
if (($fn >= $filenum)); then
fn=0
((idirnum = idirnum + 1))
- log_must mkdir -p $destdir/$idirnum
+ mkdir -p $destdir/$idirnum
else
((fn = fn + 1))
fi
diff --git a/tests/zfs-tests/tests/functional/Makefile.am b/tests/zfs-tests/tests/functional/Makefile.am
index 90f5e1821..da27673ec 100644
--- a/tests/zfs-tests/tests/functional/Makefile.am
+++ b/tests/zfs-tests/tests/functional/Makefile.am
@@ -68,6 +68,7 @@ SUBDIRS = \
sparse \
threadsappend \
tmpfile \
+ trim \
truncate \
upgrade \
user_namespace \
diff --git a/tests/zfs-tests/tests/functional/cli_root/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/Makefile.am
index 625cf8579..99f125783 100644
--- a/tests/zfs-tests/tests/functional/cli_root/Makefile.am
+++ b/tests/zfs-tests/tests/functional/cli_root/Makefile.am
@@ -59,4 +59,5 @@ SUBDIRS = \
zpool_split \
zpool_status \
zpool_sync \
+ zpool_trim \
zpool_upgrade
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg b/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg
index b9ede86ed..fdcce8b56 100644
--- a/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg
@@ -57,6 +57,7 @@ typeset -a properties=(
"fragmentation"
"leaked"
"multihost"
+ "autotrim"
"feature@async_destroy"
"feature@empty_bpobj"
"feature@lz4_compress"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/Makefile.am
new file mode 100644
index 000000000..c357eeffb
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/Makefile.am
@@ -0,0 +1,22 @@
+pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/cli_root/zpool_trim
+dist_pkgdata_SCRIPTS = \
+ setup.ksh \
+ cleanup.ksh \
+ zpool_trim.kshlib \
+ zpool_trim_attach_detach_add_remove.ksh \
+ zpool_trim_import_export.ksh \
+ zpool_trim_multiple.ksh \
+ zpool_trim_neg.ksh \
+ zpool_trim_offline_export_import_online.ksh \
+ zpool_trim_online_offline.ksh \
+ zpool_trim_partial.ksh \
+ zpool_trim_rate.ksh \
+ zpool_trim_rate_neg.ksh \
+ zpool_trim_secure.ksh \
+ zpool_trim_split.ksh \
+ zpool_trim_start_and_cancel_neg.ksh \
+ zpool_trim_start_and_cancel_pos.ksh \
+ zpool_trim_suspend_resume.ksh \
+ zpool_trim_unsupported_vdevs.ksh \
+ zpool_trim_verify_checksums.ksh \
+ zpool_trim_verify_trimmed.ksh
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/cleanup.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/cleanup.ksh
new file mode 100755
index 000000000..8ba816633
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/cleanup.ksh
@@ -0,0 +1,34 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+verify_runnable "global"
+
+if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+fi
+
+if poolexists $TESTPOOL1; then
+ destroy_pool $TESTPOOL1
+fi
+
+default_cleanup
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/setup.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/setup.ksh
new file mode 100755
index 000000000..cdcf038ad
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/setup.ksh
@@ -0,0 +1,37 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+verify_runnable "global"
+
+DISK1=${DISKS%% *}
+
+typeset -i max_discard=0
+if [[ -b $DEV_RDSKDIR/$DISK1 ]]; then
+ max_discard=$(lsblk -Dbn $DEV_RDSKDIR/$DISK1 | awk '{ print $4; exit }')
+fi
+
+if test $max_discard -eq 0; then
+ log_unsupported "DISKS do not support discard (TRIM/UNMAP)"
+fi
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
new file mode 100644
index 000000000..1c54c66c1
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
@@ -0,0 +1,43 @@
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2016 by Delphix. All rights reserved.
+# Copyright (c) 2019 by Lawrence Livermore National Security, LLC.
+#
+
+function trim_prog_line # pool disk
+{
+ typeset pool="$1"
+ typeset disk="$2"
+ zpool status -t "$pool" | grep "$disk" | grep "[[:digit:]]* trimmed"
+}
+
+function trim_progress # pool disk
+{
+ trim_prog_line "$1" "$2" | sed 's/.*(\([0-9]\{1,\}\)% trimmed.*/\1/g'
+}
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+
+ if poolexists $TESTPOOL1; then
+ destroy_pool $TESTPOOL1
+ fi
+}
+log_onexit cleanup
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_attach_detach_add_remove.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_attach_detach_add_remove.ksh
new file mode 100755
index 000000000..e715de964
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_attach_detach_add_remove.ksh
@@ -0,0 +1,64 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# Detaching/attaching, adding/removing data devices works with trimming.
+#
+# STRATEGY:
+# 1. Create a single-disk pool.
+# 2. Start trimming.
+# 3. Attach a second disk, ensure trimming continues.
+# 4. Detach the second disk, ensure trimming continues.
+# 5. Add a second disk, ensure trimming continues.
+# 6. Remove the first disk, ensure trimming stops.
+#
+
+DISK1="$(echo $DISKS | cut -d' ' -f1)"
+DISK2="$(echo $DISKS | cut -d' ' -f2)"
+
+log_must zpool create -f $TESTPOOL $DISK1
+
+log_must zpool trim -r 128M $TESTPOOL $DISK1
+progress="$(trim_progress $TESTPOOL $DISK1)"
+[[ -z "$progress" ]] && log_fail "Trim did not start"
+
+log_must zpool attach $TESTPOOL $DISK1 $DISK2
+new_progress="$(trim_progress $TESTPOOL $DISK1)"
+[[ "$progress" -le "$new_progress" ]] || \
+ log_fail "Lost trimming progress on demotion to child vdev"
+progress="$new_progress"
+
+log_must zpool detach $TESTPOOL $DISK2
+new_progress="$(trim_progress $TESTPOOL $DISK1)"
+[[ "$progress" -le "$new_progress" ]] || \
+ log_fail "Lost trimming progress on promotion to top vdev"
+progress="$new_progress"
+
+log_must zpool add $TESTPOOL $DISK2
+log_must zpool remove $TESTPOOL $DISK1
+[[ -z "$(trim_prog_line $TESTPOOL $DISK1)" ]] || \
+ log_fail "Trimming continued after initiating removal"
+
+log_pass "Trimming worked as expected across attach/detach and add/remove"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_import_export.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_import_export.ksh
new file mode 100755
index 000000000..a624d368c
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_import_export.ksh
@@ -0,0 +1,88 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# Trimming automatically resumes across import/export.
+#
+# STRATEGY:
+# 1. Create a one-disk pool.
+# 2. Start trimming and verify that trimming is active.
+# 3. Export the pool.
+# 4. Import the pool.
+# 5. Verify that trimming resumes and progress does not regress.
+# 6. Suspend trimming.
+# 7. Repeat steps 3-4.
+# 8. Verify that progress does not regress but trimming is still suspended.
+#
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+
+ if [[ -d "$TESTDIR" ]]; then
+ rm -rf "$TESTDIR"
+ fi
+}
+
+LARGEFILE="$TESTDIR/largefile"
+
+log_must mkdir "$TESTDIR"
+log_must truncate -s 10G "$LARGEFILE"
+log_must zpool create -f $TESTPOOL $LARGEFILE
+
+log_must zpool trim -r 256M $TESTPOOL
+sleep 2
+
+progress="$(trim_progress $TESTPOOL $LARGEFILE)"
+[[ -z "$progress" ]] && log_fail "Trimming did not start"
+
+log_must zpool export $TESTPOOL
+log_must zpool import -d $TESTDIR $TESTPOOL
+
+new_progress="$(trim_progress $TESTPOOL $LARGEFILE)"
+[[ -z "$new_progress" ]] && log_fail "Trimming did not restart after import"
+
+[[ "$progress" -le "$new_progress" ]] || \
+ log_fail "Trimming lost progress after import"
+log_mustnot eval "trim_prog_line $TESTPOOL $LARGEFILE | grep suspended"
+
+log_must zpool trim -s $TESTPOOL $LARGEFILE
+action_date="$(trim_prog_line $TESTPOOL $LARGEFILE | \
+ sed 's/.*ed at \(.*\)).*/\1/g')"
+log_must zpool export $TESTPOOL
+log_must zpool import -d $TESTDIR $TESTPOOL
+new_action_date=$(trim_prog_line $TESTPOOL $LARGEFILE | \
+ sed 's/.*ed at \(.*\)).*/\1/g')
+[[ "$action_date" != "$new_action_date" ]] && \
+ log_fail "Trimming action date did not persist across export/import"
+
+[[ "$new_progress" -le "$(trim_progress $TESTPOOL $LARGEFILE)" ]] || \
+ log_fail "Trimming lost progress after import"
+
+log_must eval "trim_prog_line $TESTPOOL $LARGEFILE | grep suspended"
+
+log_pass "Trimming retains state as expected across export/import"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_multiple.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_multiple.ksh
new file mode 100755
index 000000000..e8236ff83
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_multiple.ksh
@@ -0,0 +1,65 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# Trimming can be performed multiple times
+#
+# STRATEGY:
+# 1. Create a pool with a single disk.
+# 2. Trim the entire pool.
+# 3. Verify trimming is reset (status, offset, and action date).
+# 4. Repeat steps 2 and 3 with the existing pool.
+#
+
+DISK1=${DISKS%% *}
+
+log_must zpool create -f $TESTPOOL $DISK1
+
+typeset action_date="none"
+for n in {1..3}; do
+ log_must zpool trim -r 2G $TESTPOOL
+ log_mustnot eval "trim_prog_line $TESTPOOL $DISK1 | grep complete"
+
+ [[ "$(trim_progress $TESTPOOL $DISK1)" -lt "100" ]] ||
+ log_fail "Trimming progress wasn't reset"
+
+ new_action_date="$(trim_prog_line $TESTPOOL $DISK1 | \
+ sed 's/.*ed at \(.*\)).*/\1/g')"
+ [[ "$action_date" != "$new_action_date" ]] ||
+ log_fail "Trimming action date wasn't reset"
+ action_date=$new_action_date
+
+ while [[ "$(trim_progress $TESTPOOL $DISK1)" -lt "100" ]]; do
+ progress="$(trim_progress $TESTPOOL $DISK1)"
+ sleep 0.5
+ [[ "$progress" -le "$(trim_progress $TESTPOOL $DISK1)" ]] ||
+ log_fail "Trimming progress regressed"
+ done
+
+ log_must eval "trim_prog_line $TESTPOOL $DISK1 | grep complete"
+ sleep 1
+done
+
+log_pass "Trimming multiple times performs as expected"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_neg.ksh
new file mode 100755
index 000000000..4ab2eb1a7
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_neg.ksh
@@ -0,0 +1,53 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# A badly formed parameter passed to 'zpool trim' should
+# return an error.
+#
+# STRATEGY:
+# 1. Create an array containing bad 'zpool trim' parameters.
+# 2. For each element, execute the sub-command.
+# 3. Verify it returns an error.
+#
+
+DISK1=${DISKS%% *}
+DISK2="$(echo $DISKS | cut -d' ' -f2)"
+
+verify_runnable "global"
+
+set -A args "1" "-a" "-?" "--%" "-123456" "0.5" "-o" "-b" "-b no" "-z 2"
+
+log_assert "Execute 'zpool trim' using invalid parameters."
+
+log_must zpool create -f $TESTPOOL mirror $DISK1 $DISK2
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot zpool trim ${args[i]} $TESTPOOL
+ ((i = i + 1))
+done
+
+log_pass "Invalid parameters to 'zpool trim' fail as expected."
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_offline_export_import_online.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_offline_export_import_online.ksh
new file mode 100755
index 000000000..4f904d11d
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_offline_export_import_online.ksh
@@ -0,0 +1,62 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# Miscellaneous complex sequences of operations function as expected.
+#
+# STRATEGY:
+# 1. Create a pool with a two-way mirror.
+# 2. Start trimming, offline, export, import, online and verify that
+# trimming state is preserved / trimming behaves as expected
+# at each step.
+#
+
+DISK1="$(echo $DISKS | cut -d' ' -f1)"
+DISK2="$(echo $DISKS | cut -d' ' -f2)"
+
+log_must zpool create -f $TESTPOOL mirror $DISK1 $DISK2
+
+log_must zpool trim -r 128M $TESTPOOL $DISK1
+log_must zpool offline $TESTPOOL $DISK1
+progress="$(trim_progress $TESTPOOL $DISK1)"
+[[ -z "$progress" ]] && log_fail "Trimming did not start"
+log_mustnot eval "trim_prog_line $TESTPOOL $DISK1 | grep suspended"
+
+log_must zpool export $TESTPOOL
+log_must zpool import $TESTPOOL
+
+new_progress="$(trim_progress $TESTPOOL $DISK1)"
+[[ -z "$new_progress" ]] && log_fail "Trimming did not start after import"
+[[ "$new_progress" -ge "$progress" ]] || \
+ log_fail "Trimming lost progress after import"
+log_mustnot eval "trim_prog_line $TESTPOOL $DISK1 | grep suspended"
+
+log_must zpool online $TESTPOOL $DISK1
+new_progress="$(trim_progress $TESTPOOL $DISK1)"
+[[ "$new_progress" -ge "$progress" ]] || \
+ log_fail "Trimming lost progress after online"
+
+log_pass "Trimming behaves as expected at each step of:" \
+ "trim + offline + export + import + online"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_online_offline.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_online_offline.ksh
new file mode 100755
index 000000000..681cd12f7
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_online_offline.ksh
@@ -0,0 +1,70 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# Trimming automatically resumes across offline/online.
+#
+# STRATEGY:
+# 1. Create a pool with a two-way mirror.
+# 2. Start trimming one of the disks and verify that trimming is active.
+# 3. Offline the disk.
+# 4. Online the disk.
+# 5. Verify that trimming resumes and progress does not regress.
+# 6. Suspend trimming.
+# 7. Repeat steps 3-4 and verify that trimming does not resume.
+#
+
+DISK1=${DISKS%% *}
+DISK2="$(echo $DISKS | cut -d' ' -f2)"
+
+log_must zpool create -f $TESTPOOL mirror $DISK1 $DISK2
+log_must zpool trim -r 128M $TESTPOOL $DISK1
+
+log_must zpool offline $TESTPOOL $DISK1
+
+progress="$(trim_progress $TESTPOOL $DISK1)"
+[[ -z "$progress" ]] && log_fail "Trimming did not start"
+
+log_must zpool online $TESTPOOL $DISK1
+
+new_progress="$(trim_progress $TESTPOOL $DISK1)"
+[[ -z "$new_progress" ]] && \
+ log_fail "Trimming did not restart after onlining"
+[[ "$progress" -le "$new_progress" ]] || \
+ log_fail "Trimming lost progress after onlining"
+log_mustnot eval "trim_prog_line $TESTPOOL $DISK1 | grep suspended"
+
+log_must zpool trim -s $TESTPOOL $DISK1
+action_date="$(trim_prog_line $TESTPOOL $DISK1 | \
+ sed 's/.*ed at \(.*\)).*/\1/g')"
+log_must zpool offline $TESTPOOL $DISK1
+log_must zpool online $TESTPOOL $DISK1
+new_action_date=$(trim_prog_line $TESTPOOL $DISK1 | \
+ sed 's/.*ed at \(.*\)).*/\1/g')
+[[ "$action_date" != "$new_action_date" ]] && \
+ log_fail "Trimming action date did not persist across offline/online"
+log_must eval "trim_prog_line $TESTPOOL $DISK1 | grep suspended"
+
+log_pass "Trimming performs as expected across offline/online"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_partial.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_partial.ksh
new file mode 100755
index 000000000..58e0ef77c
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_partial.ksh
@@ -0,0 +1,114 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# Verify 'zpool trim' partial trim.
+#
+# STRATEGY:
+# 1. Create a pool on a single disk and mostly fill it.
+# 2. Expand the pool to create new unallocated metaslabs.
+# 3. Run 'zpool trim' to only TRIM allocated space maps.
+# 4. Verify the disk is least 90% of its original size.
+# 5. Run 'zpool trim' to perform a full TRIM.
+# 6. Verify the disk is less than 10% of its original size.
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+
+ if [[ -d "$TESTDIR" ]]; then
+ rm -rf "$TESTDIR"
+ fi
+
+ log_must set_tunable64 zfs_trim_metaslab_skip 0
+ log_must set_tunable64 zfs_trim_extent_bytes_min $trim_extent_bytes_min
+ log_must set_tunable64 zfs_vdev_min_ms_count $vdev_min_ms_count
+}
+log_onexit cleanup
+
+LARGESIZE=$((MINVDEVSIZE * 4))
+LARGEFILE="$TESTDIR/largefile"
+
+# The minimum number of metaslabs is increased in order to simulate the
+# behavior of partial trimming on a more typically sized 1TB disk.
+typeset vdev_min_ms_count=$(get_tunable zfs_vdev_min_ms_count)
+log_must set_tunable64 zfs_vdev_min_ms_count 64
+
+# Minimum trim size is decreased to verify all trim sizes.
+typeset trim_extent_bytes_min=$(get_tunable zfs_trim_extent_bytes_min)
+log_must set_tunable64 zfs_trim_extent_bytes_min 4096
+
+log_must mkdir "$TESTDIR"
+log_must truncate -s $LARGESIZE "$LARGEFILE"
+log_must zpool create $TESTPOOL "$LARGEFILE"
+log_must mkfile $(( floor(LARGESIZE * 0.80) )) /$TESTPOOL/file
+log_must zpool sync
+
+new_size=$(du -B1 "$LARGEFILE" | cut -f1)
+log_must test $new_size -le $LARGESIZE
+log_must test $new_size -gt $(( floor(LARGESIZE * 0.70) ))
+
+# Expand the pool to create new unallocated metaslabs.
+log_must zpool export $TESTPOOL
+log_must dd if=/dev/urandom of=$LARGEFILE conv=notrunc,nocreat \
+ seek=$((LARGESIZE / (1024 * 1024))) bs=$((1024 * 1024)) \
+ count=$((3 * LARGESIZE / (1024 * 1024)))
+log_must zpool import -d $TESTDIR $TESTPOOL
+log_must zpool online -e $TESTPOOL "$LARGEFILE"
+
+new_size=$(du -B1 "$LARGEFILE" | cut -f1)
+log_must test $new_size -gt $((4 * floor(LARGESIZE * 0.70) ))
+
+# Perform a partial trim, we expect it to skip most of the new metaslabs
+# which have never been used and therefore do not need be trimmed.
+log_must set_tunable64 zfs_trim_metaslab_skip 1
+log_must zpool trim $TESTPOOL
+log_must set_tunable64 zfs_trim_metaslab_skip 0
+
+log_must zpool sync
+while [[ "$(trim_progress $TESTPOOL $LARGEFILE)" -lt "100" ]]; do
+ sleep 0.5
+done
+
+new_size=$(du -B1 "$LARGEFILE" | cut -f1)
+log_must test $new_size -gt $LARGESIZE
+
+# Perform a full trim, all metaslabs will be trimmed the pool vdev
+# size will be reduced but not down to its original size due to the
+# space usage of the new metaslabs.
+log_must zpool trim $TESTPOOL
+
+log_must zpool sync
+while [[ "$(trim_progress $TESTPOOL $LARGEFILE)" -lt "100" ]]; do
+ sleep 0.5
+done
+
+new_size=$(du -B1 "$LARGEFILE" | cut -f1)
+log_must test $new_size -le $(( 2 * LARGESIZE))
+log_must test $new_size -gt $(( floor(LARGESIZE * 0.70) ))
+
+log_pass "Manual 'zpool trim' successfully partially trimmed pool"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_rate.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_rate.ksh
new file mode 100755
index 000000000..6b83a1eed
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_rate.ksh
@@ -0,0 +1,90 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# Verify 'zpool trim -r <rate>' rate limiting.
+#
+# STRATEGY:
+# 1. Create a pool on a single disk.
+# 2. Manually TRIM the pool with rate limiting.
+# 3. Verify the TRIM can be suspended.
+# 4. Restart the TRIM and verify the rate is preserved.
+#
+# NOTE: The tolerances and delays used in the test below are intentionally
+# set be to fairly large since we are capping the maximum trim rate. The
+# actual trim rate can be lower. The critical thing is that the trim rate
+# is limited, the rate is preserved when resuming, and it can be changed.
+#
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+
+ if [[ -d "$TESTDIR" ]]; then
+ rm -rf "$TESTDIR"
+ fi
+}
+log_onexit cleanup
+
+LARGEFILE="$TESTDIR/largefile"
+
+log_must mkdir "$TESTDIR"
+log_must truncate -s 10G "$LARGEFILE"
+log_must zpool create -f $TESTPOOL "$LARGEFILE"
+
+# Start trimming at 200M/s for 5 seconds (approximately 10% of the pool)
+log_must zpool trim -r 200M $TESTPOOL
+log_must sleep 4
+progress=$(trim_progress $TESTPOOL $LARGEFILE)
+log_must zpool trim -s $TESTPOOL
+log_must eval "trim_prog_line $TESTPOOL $LARGEFILE | grep suspended"
+log_must within_tolerance 10 $progress 5
+
+# Resuming trimming at 200M/s for 5 seconds (approximately 20% of the pool)
+log_must zpool trim $TESTPOOL
+log_must sleep 4
+progress=$(trim_progress $TESTPOOL $LARGEFILE)
+log_must zpool trim -s $TESTPOOL
+log_must eval "trim_prog_line $TESTPOOL $LARGEFILE | grep suspended"
+log_must within_tolerance 20 $progress 10
+
+# Increase trimming to 600M/s for 5 seconds (approximately 50% of the pool)
+log_must zpool trim -r 600M $TESTPOOL
+log_must sleep 4
+progress=$(trim_progress $TESTPOOL $LARGEFILE)
+log_must zpool trim -s $TESTPOOL
+log_must eval "trim_prog_line $TESTPOOL $LARGEFILE | grep suspended"
+log_must within_tolerance 50 $progress 15
+
+# Set maximum trim rate for 5 seconds (100% of the pool)
+log_must zpool trim -r 1T $TESTPOOL
+log_must sleep 4
+progress=$(trim_progress $TESTPOOL $LARGEFILE)
+log_must eval "trim_prog_line $TESTPOOL $LARGEFILE | grep complete"
+log_must within_tolerance 100 $progress 0
+
+log_pass "Manual TRIM rate throttles as expected"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_rate_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_rate_neg.ksh
new file mode 100755
index 000000000..11bd43076
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_rate_neg.ksh
@@ -0,0 +1,53 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# A badly formed parameter passed to 'zpool trim -r' should
+# return an error.
+#
+# STRATEGY:
+# 1. Create an array containing bad 'zpool trim -r' parameters.
+# 2. For each element, execute the sub-command.
+# 3. Verify it returns an error.
+#
+
+DISK1=${DISKS%% *}
+DISK2="$(echo $DISKS | cut -d' ' -f2)"
+
+verify_runnable "global"
+
+set -A args "a" "--%" "10X" "yes" "-?" "z 99"
+
+log_assert "Execute 'zpool trim -r' using invalid parameters."
+
+log_must zpool create -f $TESTPOOL mirror $DISK1 $DISK2
+
+typeset -i i=0
+while [[ $i -lt ${#args[*]} ]]; do
+ log_mustnot zpool trim -r ${args[i]} $TESTPOOL
+ ((i = i + 1))
+done
+
+log_pass "Invalid parameters to 'zpool trim -r' fail as expected."
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_secure.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_secure.ksh
new file mode 100755
index 000000000..e97d09f51
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_secure.ksh
@@ -0,0 +1,59 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# Verify 'zpool trim -d' secure trim.
+#
+# STRATEGY:
+# 1. Create a pool on a single file vdev.
+# 2. Run 'zpool trim -d' to securely TRIM allocated space maps.
+# 3. Verify it fails when using a file vdev.
+#
+# NOTE: Currently secure discard cannot be verified using file vdevs,
+# loopback, or scsi_debug devices. None of which support the feature.
+# It can only be tested using real SSDs which provide support.
+#
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+
+ if [[ -d "$TESTDIR" ]]; then
+ rm -rf "$TESTDIR"
+ fi
+}
+log_onexit cleanup
+
+LARGESIZE=$((MINVDEVSIZE * 4))
+LARGEFILE="$TESTDIR/largefile"
+
+log_must mkdir "$TESTDIR"
+log_must truncate -s $LARGESIZE "$LARGEFILE"
+log_must zpool create $TESTPOOL "$LARGEFILE"
+log_mustnot zpool trim -d $TESTPOOL
+
+log_pass "Manual 'zpool trim -d' failed as expected for file vdevs"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_split.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_split.ksh
new file mode 100755
index 000000000..450dc6fd8
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_split.ksh
@@ -0,0 +1,60 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# Trimming state is preserved across zpool split.
+#
+# STRATEGY:
+# 1. Create a pool with a two-way mirror.
+# 2. Start trimming both devices.
+# 3. Split the pool. Ensure trimming continues on the original.
+# 4. Import the new pool. Ensure trimming resumes on it.
+#
+
+DISK1="$(echo $DISKS | cut -d' ' -f1)"
+DISK2="$(echo $DISKS | cut -d' ' -f2)"
+POOL2="${TESTPOOL}_split"
+
+log_must zpool create -f $TESTPOOL mirror $DISK1 $DISK2
+
+log_must zpool trim $TESTPOOL $DISK1 $DISK2
+orig_prog1="$(trim_progress $TESTPOOL $DISK1)"
+orig_prog2="$(trim_progress $TESTPOOL $DISK2)"
+[[ -z "$orig_prog1" ]] && log_fail "Trimming did not start"
+
+log_must zpool split $TESTPOOL $TESTPOOL1 $DISK2
+
+# Ensure trimming continued as expected on the original pool.
+[[ "$(trim_progress $TESTPOOL $DISK1)" -ge "$orig_prog1" ]] || \
+ log_fail "Trimming lost progress on original pool"
+log_mustnot eval "trim_prog_line $TESTPOOL $DISK1 | grep suspended"
+
+log_must zpool import $TESTPOOL1
+
+[[ "$(trim_progress $TESTPOOL1 $DISK2)" -ge "$orig_prog2" ]] || \
+ log_fail "Trimming lost progress on split pool"
+log_mustnot eval "trim_prog_line $TESTPOOL1 $DISK1 | grep suspended"
+
+log_pass "Trimming behaves as expected on zpool split"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_start_and_cancel_neg.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_start_and_cancel_neg.ksh
new file mode 100755
index 000000000..faf134fbb
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_start_and_cancel_neg.ksh
@@ -0,0 +1,56 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# Cancelling and suspending trim doesn't work if not all specified vdevs
+# are being trimmed.
+#
+# STRATEGY:
+# 1. Create a three-disk pool.
+# 2. Start trimming and verify that trimming is active.
+# 3. Try to cancel and suspend trimming on the non-trimming disks.
+# 4. Try to re-trim the currently trimming disk.
+#
+
+DISK1=${DISKS%% *}
+DISK2="$(echo $DISKS | cut -d' ' -f2)"
+DISK3="$(echo $DISKS | cut -d' ' -f3)"
+
+log_must zpool list -v
+log_must zpool create -f $TESTPOOL $DISK1 $DISK2 $DISK3
+log_must zpool trim -r 128M $TESTPOOL $DISK1
+
+[[ -z "$(trim_progress $TESTPOOL $DISK1)" ]] && \
+ log_fail "Trim did not start"
+
+log_mustnot zpool trim -c $TESTPOOL $DISK2
+log_mustnot zpool trim -c $TESTPOOL $DISK2 $DISK3
+
+log_mustnot zpool trim -s $TESTPOOL $DISK2
+log_mustnot zpool trim -s $TESTPOOL $DISK2 $DISK3
+
+log_mustnot zpool trim $TESTPOOL $DISK1
+
+log_pass "Nonsensical trim operations fail"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_start_and_cancel_pos.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_start_and_cancel_pos.ksh
new file mode 100755
index 000000000..eaa4d9044
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_start_and_cancel_pos.ksh
@@ -0,0 +1,48 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_initialize/zpool_initialize.kshlib
+
+#
+# DESCRIPTION:
+# Starting and stopping an initialize works.
+#
+# STRATEGY:
+# 1. Create a one-disk pool.
+# 2. Start initializing and verify that initializing is active.
+# 3. Cancel initializing and verify that initializing is not active.
+#
+
+DISK1=${DISKS%% *}
+
+log_must zpool create -f $TESTPOOL $DISK1
+log_must zpool initialize $TESTPOOL
+
+[[ -z "$(initialize_progress $TESTPOOL $DISK1)" ]] && \
+ log_fail "Initialize did not start"
+
+log_must zpool initialize -c $TESTPOOL
+
+[[ -z "$(initialize_progress $TESTPOOL $DISK1)" ]] || \
+ log_fail "Initialize did not stop"
+
+log_pass "Initialize start + cancel works"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_suspend_resume.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_suspend_resume.ksh
new file mode 100755
index 000000000..553d911e1
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_suspend_resume.ksh
@@ -0,0 +1,74 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# Suspending and resuming trimming works.
+#
+# STRATEGY:
+# 1. Create a one-disk pool.
+# 2. Start trimming and verify that trimming is active.
+# 3. Wait 3 seconds, then suspend trimming and verify that the progress
+# reporting says so.
+# 4. Wait 3 seconds and ensure trimming progress doesn't advance.
+# 5. Restart trimming and verify that the progress doesn't regress.
+#
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+
+ if [[ -d "$TESTDIR" ]]; then
+ rm -rf "$TESTDIR"
+ fi
+}
+
+LARGEFILE="$TESTDIR/largefile"
+
+log_must mkdir "$TESTDIR"
+log_must truncate -s 10G "$LARGEFILE"
+log_must zpool create -f $TESTPOOL $LARGEFILE
+
+log_must zpool trim -r 256M $TESTPOOL
+sleep 2
+
+[[ -z "$(trim_progress $TESTPOOL $LARGEFILE)" ]] && \
+ log_fail "Trimming did not start"
+
+sleep 3
+log_must zpool trim -s $TESTPOOL
+log_must eval "trim_prog_line $TESTPOOL $LARGEFILE | grep suspended"
+progress="$(trim_progress $TESTPOOL $LARGEFILE)"
+
+sleep 3
+[[ "$progress" -eq "$(trim_progress $TESTPOOL $LARGEFILE)" ]] || \
+ log_fail "Trimming progress advanced while suspended"
+
+log_must zpool trim $TESTPOOL $LARGEFILE
+[[ "$progress" -le "$(trim_progress $TESTPOOL $LARGEFILE)" ]] ||
+ log_fail "Trimming progress regressed after resuming"
+
+log_pass "Suspend + resume trimming works as expected"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_unsupported_vdevs.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_unsupported_vdevs.ksh
new file mode 100755
index 000000000..988745e30
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_unsupported_vdevs.ksh
@@ -0,0 +1,70 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# Attempting to trim unsupported vdevs should fail.
+#
+# STRATEGY:
+# 1. Create a pool with the following configuration:
+# root
+# mirror
+# vdev0
+# vdev1 (offline)
+# cache
+# vdev2
+# spare
+# vdev3
+# 2. Try to trim vdev1, vdev2, and vdev3. Ensure that all 3 fail.
+#
+function cleanup
+{
+ if datasetexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+ if [[ -d $TESTDIR ]]; then
+ log_must rm -rf $TESTDIR
+ fi
+}
+log_onexit cleanup
+
+log_must mkdir $TESTDIR
+set -A FDISKS
+for n in {0..2}; do
+ log_must mkfile $MINVDEVSIZE $TESTDIR/vdev$n
+ FDISKS+=("$TESTDIR/vdev$n")
+done
+FDISKS+=("${DISKS%% *}")
+
+log_must zpool create $TESTPOOL mirror ${FDISKS[0]} ${FDISKS[1]} \
+ spare ${FDISKS[2]} cache ${FDISKS[3]}
+
+log_must zpool offline $TESTPOOL ${FDISKS[1]}
+
+log_mustnot zpool trim $TESTPOOL mirror-0
+for n in {1..3}; do
+ log_mustnot zpool trim $TESTPOOL ${FDISKS[$n]}
+done
+
+log_pass "Attempting to trim failed on unsupported devices"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_verify_checksums.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_verify_checksums.ksh
new file mode 100755
index 000000000..093dc3fb9
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_verify_checksums.ksh
@@ -0,0 +1,69 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# Trimming does not cause file corruption.
+#
+# STRATEGY:
+# 1. Create a one-disk pool.
+# 2. Write data to the pool.
+# 3. Start trimming and verify that trimming is active.
+# 4. Write more data to the pool.
+# 5. Export the pool and use zdb to validate checksums.
+#
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+
+ if [[ -d "$TESTDIR" ]]; then
+ rm -rf "$TESTDIR"
+ fi
+}
+log_onexit cleanup
+
+LARGESIZE=$((MINVDEVSIZE * 4))
+LARGEFILE="$TESTDIR/largefile"
+
+log_must mkdir "$TESTDIR"
+log_must truncate -s $LARGESIZE "$LARGEFILE"
+log_must zpool create $TESTPOOL "$LARGEFILE"
+
+log_must dd if=/dev/urandom of=/$TESTPOOL/file1 bs=1048576 count=64
+log_must zpool sync
+log_must zpool trim $TESTPOOL
+
+[[ -z "$(trim_progress $TESTPOOL $DISK1)" ]] && \
+ log_fail "Trimming did not start"
+
+log_must dd if=/dev/urandom of=/$TESTPOOL/file2 bs=1048576 count=64
+log_must zpool sync
+
+log_must zpool export $TESTPOOL
+log_must zdb -e -p "$TESTDIR" -cc $TESTPOOL
+
+log_pass "Trimming does not corrupt existing or new data"
diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_verify_trimmed.ksh b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_verify_trimmed.ksh
new file mode 100755
index 000000000..a216d132f
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/cli_root/zpool_trim/zpool_trim_verify_trimmed.ksh
@@ -0,0 +1,81 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/cli_root/zpool_initialize/zpool_initialize.kshlib
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# DESCRIPTION:
+# After trimming, the disk is actually trimmed.
+#
+# STRATEGY:
+# 1. Create a one-disk pool using a sparse file.
+# 2. Initialize the pool and verify the file vdev is no longer sparse.
+# 3. Trim the pool and verify the file vdev is again sparse.
+#
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+
+ if [[ -d "$TESTDIR" ]]; then
+ rm -rf "$TESTDIR"
+ fi
+
+ log_must set_tunable64 zfs_trim_extent_bytes_min $trim_extent_bytes_min
+}
+log_onexit cleanup
+
+LARGESIZE=$((MINVDEVSIZE * 4))
+LARGEFILE="$TESTDIR/largefile"
+
+# Reduce trim size to allow for tighter tolerance below when checking.
+typeset trim_extent_bytes_min=$(get_tunable zfs_trim_extent_bytes_min)
+log_must set_tunable64 zfs_trim_extent_bytes_min 4096
+
+log_must mkdir "$TESTDIR"
+log_must truncate -s $LARGESIZE "$LARGEFILE"
+log_must zpool create $TESTPOOL "$LARGEFILE"
+
+original_size=$(du -B1 "$LARGEFILE" | cut -f1)
+
+log_must zpool initialize $TESTPOOL
+
+while [[ "$(initialize_progress $TESTPOOL $LARGEFILE)" -lt "100" ]]; do
+ sleep 0.5
+done
+
+new_size=$(du -B1 "$LARGEFILE" | cut -f1)
+log_must within_tolerance $new_size $LARGESIZE $((128 * 1024 * 1024))
+
+log_must zpool trim $TESTPOOL
+
+while [[ "$(trim_progress $TESTPOOL $LARGEFILE)" -lt "100" ]]; do
+ sleep 0.5
+done
+
+new_size=$(du -B1 "$LARGEFILE" | cut -f1)
+log_must within_tolerance $new_size $original_size $((128 * 1024 * 1024))
+
+log_pass "Trimmed appropriate amount of disk space"
diff --git a/tests/zfs-tests/tests/functional/trim/Makefile.am b/tests/zfs-tests/tests/functional/trim/Makefile.am
new file mode 100644
index 000000000..4f260a8e4
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/trim/Makefile.am
@@ -0,0 +1,11 @@
+pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/trim
+dist_pkgdata_SCRIPTS = \
+ setup.ksh \
+ cleanup.ksh \
+ trim.kshlib \
+ trim.cfg \
+ autotrim_integrity.ksh \
+ autotrim_config.ksh \
+ autotrim_trim_integrity.ksh \
+ trim_integrity.ksh \
+ trim_config.ksh
diff --git a/tests/zfs-tests/tests/functional/trim/autotrim_config.ksh b/tests/zfs-tests/tests/functional/trim/autotrim_config.ksh
new file mode 100755
index 000000000..6ce396a38
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/trim/autotrim_config.ksh
@@ -0,0 +1,103 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/trim/trim.kshlib
+. $STF_SUITE/tests/functional/trim/trim.cfg
+
+#
+# DESCRIPTION:
+# Check various pool geometries stripe, mirror, raidz)
+#
+# STRATEGY:
+# 1. Create a pool on file vdevs to trim.
+# 2. Set 'autotrim=on' on pool.
+# 3. Fill the pool to a known percentage of capacity.
+# 4. Verify the vdevs contain 75% or more allocated blocks.
+# 5. Remove all files making it possible to trim the entire pool.
+# 6. Wait for auto trim to issue trim IOs for the free blocks.
+# 7. Verify the disks contain 30% or less allocated blocks.
+# 8. Repeat for test for striped, mirrored, and RAIDZ pools.
+
+verify_runnable "global"
+
+log_assert "Set 'autotrim=on' verify pool disks were trimmed"
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+
+ log_must rm -f $TRIM_VDEVS
+
+ log_must set_tunable64 zfs_trim_extent_bytes_min $trim_extent_bytes_min
+ log_must set_tunable64 zfs_trim_txg_batch $trim_txg_batch
+ log_must set_tunable64 zfs_vdev_min_ms_count $vdev_min_ms_count
+}
+log_onexit cleanup
+
+# Minimum trim size is decreased to verify all trim sizes.
+typeset trim_extent_bytes_min=$(get_tunable zfs_trim_extent_bytes_min)
+log_must set_tunable64 zfs_trim_extent_bytes_min 4096
+
+# Reduced zfs_trim_txg_batch to make trimming more frequent.
+typeset trim_txg_batch=$(get_tunable zfs_trim_txg_batch)
+log_must set_tunable64 zfs_trim_txg_batch 8
+
+# Increased metaslabs to better simulate larger more realistic devices.
+typeset vdev_min_ms_count=$(get_tunable zfs_vdev_min_ms_count)
+log_must set_tunable64 zfs_vdev_min_ms_count 32
+
+typeset VDEV_MAX_MB=$(( floor(4 * MINVDEVSIZE * 0.75 / 1024 / 1024) ))
+typeset VDEV_MIN_MB=$(( floor(4 * MINVDEVSIZE * 0.30 / 1024 / 1024) ))
+
+for type in "" "mirror" "raidz2"; do
+
+ if [[ "$type" = "" ]]; then
+ VDEVS="$TRIM_VDEV1"
+ elif [[ "$type" = "mirror" ]]; then
+ VDEVS="$TRIM_VDEV1 $TRIM_VDEV2"
+ else
+ VDEVS="$TRIM_VDEV1 $TRIM_VDEV2 $TRIM_VDEV3"
+ fi
+
+ log_must truncate -s $((4 * MINVDEVSIZE)) $VDEVS
+ log_must zpool create -f $TESTPOOL $VDEVS
+ log_must zpool set autotrim=on $TESTPOOL
+
+ typeset availspace=$(get_prop available $TESTPOOL)
+ typeset fill_mb=$(( floor(availspace * 0.90 / 1024 / 1024) ))
+
+ # Fill the pool, verify the vdevs are no longer sparse.
+ file_write -o create -f /$TESTPOOL/file -b 1048576 -c $fill_mb -d R
+ verify_vdevs "-gt" "$VDEV_MAX_MB" $VDEVS
+
+ # Remove the file, wait for trim, verify the vdevs are now sparse.
+ log_must rm /$TESTPOOL/file
+ wait_trim_io $TESTPOOL "ind" 64
+ verify_vdevs "-le" "$VDEV_MIN_MB" $VDEVS
+
+ log_must zpool destroy $TESTPOOL
+ log_must rm -f $VDEVS
+done
+
+log_pass "Auto trim successfully shrunk vdevs"
diff --git a/tests/zfs-tests/tests/functional/trim/autotrim_integrity.ksh b/tests/zfs-tests/tests/functional/trim/autotrim_integrity.ksh
new file mode 100755
index 000000000..c7b3da7c0
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/trim/autotrim_integrity.ksh
@@ -0,0 +1,87 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/trim/trim.kshlib
+. $STF_SUITE/tests/functional/trim/trim.cfg
+
+#
+# DESCRIPTION:
+# Verify automatic trim pool data integrity.
+#
+# STRATEGY:
+# 1. Create a pool on sparse file vdevs to trim.
+# 2. Set autotrim=on to enable asynchronous pool trimming.
+# 3. Generate some interesting pool data which can be trimmed.
+# 4. Verify trim IOs of the expected type were issued for the pool.
+# 5. Verify data integrity of the pool after trim.
+# 6. Repeat test for striped, mirrored, and RAIDZ pools.
+
+verify_runnable "global"
+
+log_assert "Set 'autotrim=on' pool property verify pool data integrity"
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+
+ log_must rm -f $TRIM_VDEVS
+
+ log_must set_tunable64 zfs_trim_extent_bytes_min $trim_extent_bytes_min
+ log_must set_tunable64 zfs_trim_txg_batch $trim_txg_batch
+}
+log_onexit cleanup
+
+# Minimum trim size is decreased to verify all trim sizes.
+typeset trim_extent_bytes_min=$(get_tunable zfs_trim_extent_bytes_min)
+log_must set_tunable64 zfs_trim_extent_bytes_min 4096
+
+# Reduced zfs_trim_txg_batch to make trimming more frequent.
+typeset trim_txg_batch=$(get_tunable zfs_trim_txg_batch)
+log_must set_tunable64 zfs_trim_txg_batch 8
+
+for type in "" "mirror" "raidz" "raidz2" "raidz3"; do
+ log_must truncate -s 1G $TRIM_VDEVS
+
+ log_must zpool create -f $TESTPOOL $type $TRIM_VDEVS
+ log_must zpool set autotrim=on $TESTPOOL
+
+ # Add and remove data from the pool in a random fashion in order
+ # to generate a variety of interesting ranges to be auto trimmed.
+ for n in {0..10}; do
+ dir="/$TESTPOOL/autotrim-$((RANDOM % 5))"
+ filesize=$((4096 + ((RANDOM * 691) % 131072) ))
+ log_must rm -rf $dir
+ log_must fill_fs $dir 10 10 $filesize 1 R
+ zpool sync
+ done
+ log_must du -hs /$TESTPOOL
+
+ verify_trim_io $TESTPOOL "ind" 10
+ verify_pool $TESTPOOL
+
+ log_must zpool destroy $TESTPOOL
+ log_must rm -f $TRIM_VDEVS
+done
+
+log_pass "Automatic trim successfully validated"
diff --git a/tests/zfs-tests/tests/functional/trim/autotrim_trim_integrity.ksh b/tests/zfs-tests/tests/functional/trim/autotrim_trim_integrity.ksh
new file mode 100755
index 000000000..c0e850c48
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/trim/autotrim_trim_integrity.ksh
@@ -0,0 +1,93 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/trim/trim.kshlib
+. $STF_SUITE/tests/functional/trim/trim.cfg
+
+#
+# DESCRIPTION:
+# Verify automatic trim and manual trim coexist correctly.
+#
+# STRATEGY:
+# 1. Create a pool on sparse file vdevs to trim.
+# 2. Set autotrim=on to enable asynchronous pool trimming.
+# 3. Generate some interesting pool data which can be trimmed.
+# 4. While generating data issue manual trims.
+# 4. Verify trim IOs of the expected type were issued for the pool.
+# 5. Verify data integrity of the pool after trim.
+# 6. Repeat test for striped, mirrored, and RAIDZ pools.
+
+verify_runnable "global"
+
+log_assert "Set 'autotrim=on', run 'zpool trim' and verify pool data integrity"
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+
+ log_must rm -f $TRIM_VDEVS
+
+ log_must set_tunable64 zfs_trim_extent_bytes_min $trim_extent_bytes_min
+ log_must set_tunable64 zfs_trim_txg_batch $trim_txg_batch
+}
+log_onexit cleanup
+
+# Minimum trim size is decreased to verify all trim sizes.
+typeset trim_extent_bytes_min=$(get_tunable zfs_trim_extent_bytes_min)
+log_must set_tunable64 zfs_trim_extent_bytes_min 4096
+
+# Reduced zfs_trim_txg_batch to make trimming more frequent.
+typeset trim_txg_batch=$(get_tunable zfs_trim_txg_batch)
+log_must set_tunable64 zfs_trim_txg_batch 8
+
+for type in "" "mirror" "raidz" "raidz2" "raidz3"; do
+ log_must truncate -s 1G $TRIM_VDEVS
+
+ log_must zpool create -f $TESTPOOL $type $TRIM_VDEVS
+ log_must zpool set autotrim=on $TESTPOOL
+
+ # Add and remove data from the pool in a random fashion in order
+ # to generate a variety of interesting ranges to be auto trimmed.
+ for n in {0..10}; do
+ dir="/$TESTPOOL/autotrim-$((RANDOM % 5))"
+ filesize=$((4096 + ((RANDOM * 691) % 131072) ))
+ log_must rm -rf $dir
+ log_must fill_fs $dir 10 10 $filesize 1 R
+ zpool sync
+
+ if [[ $((n % 4)) -eq 0 ]]; then
+ log_must zpool trim $TESTPOOL
+ wait_trim $TESTPOOL $TRIM_VDEVS
+ fi
+ done
+ log_must du -hs /$TESTPOOL
+
+ verify_trim_io $TESTPOOL "ind" 10
+ verify_pool $TESTPOOL
+
+ log_must zpool destroy $TESTPOOL
+ log_must rm -f $TRIM_VDEVS
+done
+
+log_pass "Automatic trim and manual trim coexistence successfully validated"
diff --git a/tests/zfs-tests/tests/functional/trim/cleanup.ksh b/tests/zfs-tests/tests/functional/trim/cleanup.ksh
new file mode 100755
index 000000000..29d14044a
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/trim/cleanup.ksh
@@ -0,0 +1,48 @@
+#!/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.
+#
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+verify_runnable "global"
+
+if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+fi
+
+if poolexists $TESTPOOL1; then
+ destroy_pool $TESTPOOL1
+fi
+
+TRIM_DIR="$TEST_BASE_DIR"
+TRIM_VDEVS="$TRIM_DIR/trim-vdev1 $TRIM_DIR/trim-vdev2 \
+ $TRIM_DIR/trim-vdev3 $TRIM_DIR/trim-vdev4"
+
+rm -rf $TRIM_VDEVS
+
+default_cleanup
diff --git a/tests/zfs-tests/tests/functional/trim/setup.ksh b/tests/zfs-tests/tests/functional/trim/setup.ksh
new file mode 100755
index 000000000..cdcf038ad
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/trim/setup.ksh
@@ -0,0 +1,37 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+
+verify_runnable "global"
+
+DISK1=${DISKS%% *}
+
+typeset -i max_discard=0
+if [[ -b $DEV_RDSKDIR/$DISK1 ]]; then
+ max_discard=$(lsblk -Dbn $DEV_RDSKDIR/$DISK1 | awk '{ print $4; exit }')
+fi
+
+if test $max_discard -eq 0; then
+ log_unsupported "DISKS do not support discard (TRIM/UNMAP)"
+fi
+
+log_pass
diff --git a/tests/zfs-tests/tests/functional/trim/trim.cfg b/tests/zfs-tests/tests/functional/trim/trim.cfg
new file mode 100644
index 000000000..91adb7624
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/trim/trim.cfg
@@ -0,0 +1,33 @@
+#!/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) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+TRIM_DIR="$TEST_BASE_DIR"
+TRIM_VDEV1="$TRIM_DIR/trim-vdev1"
+TRIM_VDEV2="$TRIM_DIR/trim-vdev2"
+TRIM_VDEV3="$TRIM_DIR/trim-vdev3"
+TRIM_VDEV4="$TRIM_DIR/trim-vdev4"
+TRIM_VDEVS="$TRIM_VDEV1 $TRIM_VDEV2 $TRIM_VDEV3 $TRIM_VDEV4"
diff --git a/tests/zfs-tests/tests/functional/trim/trim.kshlib b/tests/zfs-tests/tests/functional/trim/trim.kshlib
new file mode 100644
index 000000000..02802d8c9
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/trim/trim.kshlib
@@ -0,0 +1,154 @@
+#!/bin/ksh -p
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/tests/functional/cli_root/zpool_trim/zpool_trim.kshlib
+
+#
+# Get the actual on disk disk for the provided file.
+#
+function get_size_mb
+{
+ typeset rval=$(du --block-size 1048576 -s "$1" | awk '{print $1}')
+ echo -n "$rval"
+}
+
+#
+# Get the number of trim IOs issued for the pool (ind or agg).
+#
+function get_trim_io
+{
+ typeset pool="${1-:$TESTPOOL}"
+ typeset type="${2-:ind}"
+ typeset rval
+
+ # Sum the ind or agg columns of the trim request size histogram.
+ case "$type" in
+ "ind")
+ rval=$(zpool iostat -pr $pool | awk \
+ '$1 ~ /[0-9].*/ { sum += $12 } END { print sum }')
+ echo -n "$rval"
+ ;;
+ "agg")
+ rval=$(zpool iostat -pr $pool | awk \
+ '$1 ~ /[0-9].*/ { sum += $13 } END { print sum }')
+ echo -n "$rval"
+ ;;
+ *)
+ log_fail "Type must be 'ind' or 'agg'"
+ ;;
+ esac
+}
+
+#
+# Verify that trim IOs were send to devices in the pool.
+#
+function verify_trim_io
+{
+ typeset pool="${1:-$TESTPOOL}"
+ typeset type="${2:-ind}"
+ typeset min_trim_ios=${3:-100}
+ typeset ios
+
+ ios=$(get_trim_io $pool $type)
+ if [[ $ios -ge $min_trim_ios ]]; then
+ log_note "Issued $ios $type trim IOs for pool $pool"
+ else
+ log_fail "Too few trim IOs issued $ios/$min_trim_ios"
+ fi
+}
+
+#
+# Run N txgs which should be enough to trim the entire pool.
+#
+function wait_trim_io # pool type txgs
+{
+ typeset pool="${1-:$TESTPOOL}"
+ typeset type="${2-:ind}"
+ typeset txgs=${3:-10}
+ typeset timeout=120
+ typeset stop_time=$(( $(date +%s) + $timeout ))
+
+ typeset -i i=0
+ while [[ $i -lt $txgs ]]; do
+ if [ "$(date +%s)" -ge $stop_time ]; then
+ log_fail "Exceeded trim time limit of ${timeout}s"
+ return
+ fi
+
+ zpool sync -f
+ ((i = i + 1))
+ done
+
+ typeset ios=$(get_trim_io $pool $type)
+ log_note "Waited for $txgs txgs, $ios $type TRIM IOs"
+}
+
+#
+# Verify that file vdevs against a target value.
+#
+function verify_vdevs # op size vdevs
+{
+ typeset tgt_op=$1
+ typeset tgt_size=$2
+ shift 2
+ typeset vdevs=$@
+
+ for vdev in $vdevs; do
+ typeset size=$(get_size_mb $vdev)
+ if test $size $tgt_op $tgt_size; then
+ log_note "Success $vdev is $size MB which is $tgt_op" \
+ "than $tgt_size MB"
+ else
+ log_fail "Failure $vdev is $size MB which is not" \
+ "$tgt_op than $tgt_size MB"
+ fi
+ done
+}
+
+#
+# Wait for up to 120 seconds for trimming of the listed vdevs to complete.
+#
+function wait_trim # pool vdevs
+{
+ typeset stop_time=$(( $(date +%s) + 120 ))
+ typeset pool="$1"
+ shift
+ typeset vdevs=$@
+ typeset complete
+
+ while [[ $complete -eq 0 ]]; do
+ complete=1
+
+ for vdev in $vdevs; do
+ if [[ "$(trim_progress $pool $vdev)" -lt "100" ]]; then
+ complete=0
+ break
+ else
+ log_must eval "trim_prog_line $pool $vdev | \
+ grep complete"
+ fi
+ done
+
+ if [ "$(date +%s)" -ge $stop_time ]; then
+ log_fail "Exceeded trim time limit of 120s"
+ fi
+
+ sleep 0.5
+ done
+
+ log_note "Pool completed trim successfully."
+}
diff --git a/tests/zfs-tests/tests/functional/trim/trim_config.ksh b/tests/zfs-tests/tests/functional/trim/trim_config.ksh
new file mode 100755
index 000000000..e56bd6248
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/trim/trim_config.ksh
@@ -0,0 +1,103 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/trim/trim.kshlib
+. $STF_SUITE/tests/functional/trim/trim.cfg
+
+#
+# DESCRIPTION:
+# Check various pool geometries stripe, mirror, raidz
+#
+# STRATEGY:
+# 1. Create a pool on file vdevs to trim.
+# 2. Fill the pool to a known percentage of capacity.
+# 3. Verify the vdevs contain 75% or more allocated blocks.
+# 4. Remove all files making it possible to trim the entire pool.
+# 5. Manually trim the pool.
+# 6. Wait for trim to issue trim IOs for the free blocks.
+# 7. Verify the disks contain 30% or less allocated blocks.
+# 8. Repeat for test for striped, mirrored, and RAIDZ pools.
+
+verify_runnable "global"
+
+log_assert "Run 'zpool trim' verify pool disks were trimmed"
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+
+ log_must rm -f $TRIM_VDEVS
+
+ log_must set_tunable64 zfs_trim_extent_bytes_min $trim_extent_bytes_min
+ log_must set_tunable64 zfs_trim_txg_batch $trim_txg_batch
+ log_must set_tunable64 zfs_vdev_min_ms_count $vdev_min_ms_count
+}
+log_onexit cleanup
+
+# Minimum trim size is decreased to verify all trim sizes.
+typeset trim_extent_bytes_min=$(get_tunable zfs_trim_extent_bytes_min)
+log_must set_tunable64 zfs_trim_extent_bytes_min 4096
+
+# Reduced zfs_trim_txg_batch to make trimming more frequent.
+typeset trim_txg_batch=$(get_tunable zfs_trim_txg_batch)
+log_must set_tunable64 zfs_trim_txg_batch 8
+
+# Increased metaslabs to better simulate larger more realistic devices.
+typeset vdev_min_ms_count=$(get_tunable zfs_vdev_min_ms_count)
+log_must set_tunable64 zfs_vdev_min_ms_count 32
+
+typeset VDEV_MAX_MB=$(( floor(4 * MINVDEVSIZE * 0.75 / 1024 / 1024) ))
+typeset VDEV_MIN_MB=$(( floor(4 * MINVDEVSIZE * 0.30 / 1024 / 1024) ))
+
+for type in "" "mirror" "raidz2"; do
+
+ if [[ "$type" = "" ]]; then
+ VDEVS="$TRIM_VDEV1"
+ elif [[ "$type" = "mirror" ]]; then
+ VDEVS="$TRIM_VDEV1 $TRIM_VDEV2"
+ else
+ VDEVS="$TRIM_VDEV1 $TRIM_VDEV2 $TRIM_VDEV3"
+ fi
+
+ log_must truncate -s $((4 * MINVDEVSIZE)) $VDEVS
+ log_must zpool create -f $TESTPOOL $type $VDEVS
+
+ typeset availspace=$(get_prop available $TESTPOOL)
+ typeset fill_mb=$(( floor(availspace * 0.90 / 1024 / 1024) ))
+
+ # Fill the pool, verify the vdevs are no longer sparse.
+ file_write -o create -f /$TESTPOOL/file -b 1048576 -c $fill_mb -d R
+ verify_vdevs "-gt" "$VDEV_MAX_MB" $VDEVS
+
+ # Remove the file, issue trim, verify the vdevs are now sparse.
+ log_must rm /$TESTPOOL/file
+ log_must zpool trim $TESTPOOL
+ wait_trim $TESTPOOL $VDEVS
+ verify_vdevs "-le" "$VDEV_MIN_MB" $VDEVS
+
+ log_must zpool destroy $TESTPOOL
+ log_must rm -f $VDEVS
+done
+
+log_pass "Manual trim successfully shrunk vdevs"
diff --git a/tests/zfs-tests/tests/functional/trim/trim_integrity.ksh b/tests/zfs-tests/tests/functional/trim/trim_integrity.ksh
new file mode 100755
index 000000000..0bbc439ee
--- /dev/null
+++ b/tests/zfs-tests/tests/functional/trim/trim_integrity.ksh
@@ -0,0 +1,89 @@
+#!/bin/ksh -p
+#
+# CDDL HEADER START
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2019 by Tim Chase. All rights reserved.
+# Copyright (c) 2019 Lawrence Livermore National Security, LLC.
+#
+
+. $STF_SUITE/include/libtest.shlib
+. $STF_SUITE/tests/functional/trim/trim.kshlib
+. $STF_SUITE/tests/functional/trim/trim.cfg
+
+#
+# DESCRIPTION:
+# Verify manual trim pool data integrity.
+#
+# STRATEGY:
+# 1. Create a pool on sparse file vdevs to trim.
+# 2. Generate some interesting pool data which can be trimmed.
+# 3. Manually trim the pool.
+# 4. Verify trim IOs of the expected type were issued for the pool.
+# 5. Verify data integrity of the pool after trim.
+# 6. Repeat test for striped, mirrored, and RAIDZ pools.
+
+verify_runnable "global"
+
+log_assert "Run 'zpool trim' and verify pool data integrity"
+
+function cleanup
+{
+ if poolexists $TESTPOOL; then
+ destroy_pool $TESTPOOL
+ fi
+
+ log_must rm -f $TRIM_VDEVS
+
+ log_must set_tunable64 zfs_trim_extent_bytes_min $trim_extent_bytes_min
+ log_must set_tunable64 zfs_trim_txg_batch $trim_txg_batch
+}
+log_onexit cleanup
+
+# Minimum trim size is decreased to verify all trim sizes.
+typeset trim_extent_bytes_min=$(get_tunable zfs_trim_extent_bytes_min)
+log_must set_tunable64 zfs_trim_extent_bytes_min 4096
+
+# Reduced zfs_trim_txg_batch to make trimming more frequent.
+typeset trim_txg_batch=$(get_tunable zfs_trim_txg_batch)
+log_must set_tunable64 zfs_trim_txg_batch 8
+
+for type in "" "mirror" "raidz" "raidz2" "raidz3"; do
+ log_must truncate -s 1G $TRIM_VDEVS
+
+ log_must zpool create -f $TESTPOOL $type $TRIM_VDEVS
+
+ # Add and remove data from the pool in a random fashion in order
+ # to generate a variety of interesting ranges to be manually trimmed.
+ for n in {0..10}; do
+ dir="/$TESTPOOL/trim-$((RANDOM % 5))"
+ filesize=$((4096 + ((RANDOM * 691) % 131072) ))
+ log_must rm -rf $dir
+ log_must fill_fs $dir 10 10 $filesize 1 R
+ zpool sync
+ done
+ log_must du -hs /$TESTPOOL
+
+ log_must zpool trim $TESTPOOL
+ wait_trim $TESTPOOL $TRIM_VDEVS
+
+ verify_trim_io $TESTPOOL "ind" 10
+ verify_pool $TESTPOOL
+
+ log_must zpool destroy $TESTPOOL
+ log_must rm -f $TRIM_VDEVS
+done
+
+log_pass "Manual trim successfully validated"