aboutsummaryrefslogtreecommitdiff
path: root/cmd/zfs/zfs_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/zfs/zfs_main.c')
-rw-r--r--cmd/zfs/zfs_main.c166
1 files changed, 144 insertions, 22 deletions
diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index 42c180890fec..ab2b006ae460 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -270,7 +270,7 @@ get_usage(zfs_help_t idx)
return (gettext("\tclone [-p] [-o property=value] ... "
"<snapshot> <filesystem|volume>\n"));
case HELP_CREATE:
- return (gettext("\tcreate [-Pnpv] [-o property=value] ... "
+ return (gettext("\tcreate [-Pnpuv] [-o property=value] ... "
"<filesystem>\n"
"\tcreate [-Pnpsv] [-b blocksize] [-o property=value] ... "
"-V <size> <volume>\n"));
@@ -893,6 +893,107 @@ usage:
}
/*
+ * Return a default volblocksize for the pool which always uses more than
+ * half of the data sectors. This primarily applies to dRAID which always
+ * writes full stripe widths.
+ */
+static uint64_t
+default_volblocksize(zpool_handle_t *zhp, nvlist_t *props)
+{
+ uint64_t volblocksize, asize = SPA_MINBLOCKSIZE;
+ nvlist_t *tree, **vdevs;
+ uint_t nvdevs;
+
+ nvlist_t *config = zpool_get_config(zhp, NULL);
+
+ if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &tree) != 0 ||
+ nvlist_lookup_nvlist_array(tree, ZPOOL_CONFIG_CHILDREN,
+ &vdevs, &nvdevs) != 0) {
+ return (ZVOL_DEFAULT_BLOCKSIZE);
+ }
+
+ for (int i = 0; i < nvdevs; i++) {
+ nvlist_t *nv = vdevs[i];
+ uint64_t ashift, ndata, nparity;
+
+ if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_ASHIFT, &ashift) != 0)
+ continue;
+
+ if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_DRAID_NDATA,
+ &ndata) == 0) {
+ /* dRAID minimum allocation width */
+ asize = MAX(asize, ndata * (1ULL << ashift));
+ } else if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_NPARITY,
+ &nparity) == 0) {
+ /* raidz minimum allocation width */
+ if (nparity == 1)
+ asize = MAX(asize, 2 * (1ULL << ashift));
+ else
+ asize = MAX(asize, 4 * (1ULL << ashift));
+ } else {
+ /* mirror or (non-redundant) leaf vdev */
+ asize = MAX(asize, 1ULL << ashift);
+ }
+ }
+
+ /*
+ * Calculate the target volblocksize such that more than half
+ * of the asize is used. The following table is for 4k sectors.
+ *
+ * n asize blksz used | n asize blksz used
+ * -------------------------+---------------------------------
+ * 1 4,096 8,192 100% | 9 36,864 32,768 88%
+ * 2 8,192 8,192 100% | 10 40,960 32,768 80%
+ * 3 12,288 8,192 66% | 11 45,056 32,768 72%
+ * 4 16,384 16,384 100% | 12 49,152 32,768 66%
+ * 5 20,480 16,384 80% | 13 53,248 32,768 61%
+ * 6 24,576 16,384 66% | 14 57,344 32,768 57%
+ * 7 28,672 16,384 57% | 15 61,440 32,768 53%
+ * 8 32,768 32,768 100% | 16 65,536 65,636 100%
+ *
+ * This is primarily a concern for dRAID which always allocates
+ * a full stripe width. For dRAID the default stripe width is
+ * n=8 in which case the volblocksize is set to 32k. Ignoring
+ * compression there are no unused sectors. This same reasoning
+ * applies to raidz[2,3] so target 4 sectors to minimize waste.
+ */
+ uint64_t tgt_volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
+ while (tgt_volblocksize * 2 <= asize)
+ tgt_volblocksize *= 2;
+
+ const char *prop = zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE);
+ if (nvlist_lookup_uint64(props, prop, &volblocksize) == 0) {
+
+ /* Issue a warning when a non-optimal size is requested. */
+ if (volblocksize < ZVOL_DEFAULT_BLOCKSIZE) {
+ (void) fprintf(stderr, gettext("Warning: "
+ "volblocksize (%llu) is less than the default "
+ "minimum block size (%llu).\nTo reduce wasted "
+ "space a volblocksize of %llu is recommended.\n"),
+ (u_longlong_t)volblocksize,
+ (u_longlong_t)ZVOL_DEFAULT_BLOCKSIZE,
+ (u_longlong_t)tgt_volblocksize);
+ } else if (volblocksize < tgt_volblocksize) {
+ (void) fprintf(stderr, gettext("Warning: "
+ "volblocksize (%llu) is much less than the "
+ "minimum allocation\nunit (%llu), which wastes "
+ "at least %llu%% of space. To reduce wasted "
+ "space,\nuse a larger volblocksize (%llu is "
+ "recommended), fewer dRAID data disks\n"
+ "per group, or smaller sector size (ashift).\n"),
+ (u_longlong_t)volblocksize, (u_longlong_t)asize,
+ (u_longlong_t)((100 * (asize - volblocksize)) /
+ asize), (u_longlong_t)tgt_volblocksize);
+ }
+ } else {
+ volblocksize = tgt_volblocksize;
+ fnvlist_add_uint64(props, prop, volblocksize);
+ }
+
+ return (volblocksize);
+}
+
+/*
* zfs create [-Pnpv] [-o prop=value] ... fs
* zfs create [-Pnpsv] [-b blocksize] [-o prop=value] ... -V vol size
*
@@ -911,6 +1012,8 @@ usage:
* check of arguments and properties, but does not check for permissions,
* available space, etc.
*
+ * The '-u' flag prevents the newly created file system from being mounted.
+ *
* The '-v' flag is for verbose output.
*
* The '-P' flag is used for parseable output. It implies '-v'.
@@ -927,17 +1030,19 @@ zfs_do_create(int argc, char **argv)
boolean_t bflag = B_FALSE;
boolean_t parents = B_FALSE;
boolean_t dryrun = B_FALSE;
+ boolean_t nomount = B_FALSE;
boolean_t verbose = B_FALSE;
boolean_t parseable = B_FALSE;
int ret = 1;
nvlist_t *props;
uint64_t intval;
+ char *strval;
if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
nomem();
/* check options */
- while ((c = getopt(argc, argv, ":PV:b:nso:pv")) != -1) {
+ while ((c = getopt(argc, argv, ":PV:b:nso:puv")) != -1) {
switch (c) {
case 'V':
type = ZFS_TYPE_VOLUME;
@@ -984,6 +1089,9 @@ zfs_do_create(int argc, char **argv)
case 's':
noreserve = B_TRUE;
break;
+ case 'u':
+ nomount = B_TRUE;
+ break;
case 'v':
verbose = B_TRUE;
break;
@@ -1003,6 +1111,11 @@ zfs_do_create(int argc, char **argv)
"used when creating a volume\n"));
goto badusage;
}
+ if (nomount && type != ZFS_TYPE_FILESYSTEM) {
+ (void) fprintf(stderr, gettext("'-u' can only be "
+ "used when creating a filesystem\n"));
+ goto badusage;
+ }
argc -= optind;
argv += optind;
@@ -1018,7 +1131,7 @@ zfs_do_create(int argc, char **argv)
goto badusage;
}
- if (dryrun || (type == ZFS_TYPE_VOLUME && !noreserve)) {
+ if (dryrun || type == ZFS_TYPE_VOLUME) {
char msg[ZFS_MAX_DATASET_NAME_LEN * 2];
char *p;
@@ -1040,18 +1153,24 @@ zfs_do_create(int argc, char **argv)
}
}
- /*
- * if volsize is not a multiple of volblocksize, round it up to the
- * nearest multiple of the volblocksize
- */
if (type == ZFS_TYPE_VOLUME) {
- uint64_t volblocksize;
+ const char *prop = zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE);
+ uint64_t volblocksize = default_volblocksize(zpool_handle,
+ real_props);
- if (nvlist_lookup_uint64(props,
- zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
- &volblocksize) != 0)
- volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
+ if (volblocksize != ZVOL_DEFAULT_BLOCKSIZE &&
+ nvlist_lookup_string(props, prop, &strval) != 0) {
+ if (asprintf(&strval, "%llu",
+ (u_longlong_t)volblocksize) == -1)
+ nomem();
+ nvlist_add_string(props, prop, strval);
+ free(strval);
+ }
+ /*
+ * If volsize is not a multiple of volblocksize, round it
+ * up to the nearest multiple of the volblocksize.
+ */
if (volsize % volblocksize) {
volsize = P2ROUNDUP_TYPED(volsize, volblocksize,
uint64_t);
@@ -1064,11 +1183,9 @@ zfs_do_create(int argc, char **argv)
}
}
-
if (type == ZFS_TYPE_VOLUME && !noreserve) {
uint64_t spa_version;
zfs_prop_t resv_prop;
- char *strval;
spa_version = zpool_get_prop_int(zpool_handle,
ZPOOL_PROP_VERSION, NULL);
@@ -1159,6 +1276,11 @@ zfs_do_create(int argc, char **argv)
log_history = B_FALSE;
}
+ if (nomount) {
+ ret = 0;
+ goto error;
+ }
+
ret = zfs_mount_and_share(g_zfs, argv[0], ZFS_TYPE_DATASET);
error:
nvlist_free(props);
@@ -6596,9 +6718,9 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
(void) fprintf(stderr, gettext("cannot share '%s': "
"legacy share\n"), zfs_get_name(zhp));
- (void) fprintf(stderr, gettext("use share(1M) to "
- "share this filesystem, or set "
- "sharenfs property on\n"));
+ (void) fprintf(stderr, gettext("use exports(5) or "
+ "smb.conf(5) to share this filesystem, or set "
+ "the sharenfs or sharesmb property\n"));
return (1);
}
@@ -6613,7 +6735,7 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
(void) fprintf(stderr, gettext("cannot %s '%s': "
"legacy mountpoint\n"), cmdname, zfs_get_name(zhp));
- (void) fprintf(stderr, gettext("use %s(1M) to "
+ (void) fprintf(stderr, gettext("use %s(8) to "
"%s this filesystem\n"), cmdname, cmdname);
return (1);
}
@@ -7416,8 +7538,8 @@ unshare_unmount(int op, int argc, char **argv)
"unshare '%s': legacy share\n"),
zfs_get_name(zhp));
(void) fprintf(stderr, gettext("use "
- "unshare(1M) to unshare this "
- "filesystem\n"));
+ "exports(5) or smb.conf(5) to unshare "
+ "this filesystem\n"));
ret = 1;
} else if (!zfs_is_shared(zhp)) {
(void) fprintf(stderr, gettext("cannot "
@@ -7435,7 +7557,7 @@ unshare_unmount(int op, int argc, char **argv)
"unmount '%s': legacy "
"mountpoint\n"), zfs_get_name(zhp));
(void) fprintf(stderr, gettext("use "
- "umount(1M) to unmount this "
+ "umount(8) to unmount this "
"filesystem\n"));
ret = 1;
} else if (!zfs_is_mounted(zhp, NULL)) {
@@ -8370,7 +8492,7 @@ zfs_do_wait(int argc, char **argv)
{
boolean_t enabled[ZFS_WAIT_NUM_ACTIVITIES];
int error, i;
- char c;
+ int c;
/* By default, wait for all types of activity. */
for (i = 0; i < ZFS_WAIT_NUM_ACTIVITIES; i++)