Message ID | 20160128225229.17855.88143.stgit@dwillia2-desk3.amr.corp.intel.com (mailing list archive) |
---|---|
State | Accepted |
Commit | 3139dfda1158 |
Headers | show |
Dan Williams <dan.j.williams@intel.com> writes: > A helper utility for instantiating a namespace. With no arguments it > defaults to a maximally sized pmem namespace in memory mode. The search > for available capacity can be constrained by bus, region, and/or > namespace type using the same mechanism to filter namespaces as 'ndctl > enable-namespace' and 'ndctl disable-namespace'. > > See the include man page for more details. > > Cc: Jeff Moyer <jmoyer@redhat.com> > [jmoyer: null dereference fix] Hi, Dan, If that's the only fix since last I saw this patch, then it's still got bugs. ;-) Sorry I can't test right this second, but I'll dedicate some time to it tomorrow. I'm pretty sure I hit another null pointer dereference. Did you end up writing unit tests for this? Cheers, Jeff > Signed-off-by: Dan Williams <dan.j.williams@intel.com> > --- > Documentation/Makefile.am | 3 > Documentation/ndctl-create-namespace.txt | 127 +++++ > Documentation/ndctl.txt | 2 > Makefile.am | 4 > builtin-create-nfit.c | 45 -- > builtin-xable-namespace.c | 153 ------- > builtin-xaction-namespace.c | 693 ++++++++++++++++++++++++++++++ > builtin.h | 3 > ndctl.c | 1 > util/size.c | 42 ++ > util/size.h | 12 + > 11 files changed, 882 insertions(+), 203 deletions(-) > create mode 100644 Documentation/ndctl-create-namespace.txt > delete mode 100644 builtin-xable-namespace.c > create mode 100644 builtin-xaction-namespace.c > create mode 100644 util/size.c > create mode 100644 util/size.h > > diff --git a/Documentation/Makefile.am b/Documentation/Makefile.am > index bd43714d5215..51d186505b44 100644 > --- a/Documentation/Makefile.am > +++ b/Documentation/Makefile.am > @@ -4,7 +4,8 @@ man1_MANS = \ > ndctl-enable-region.1 \ > ndctl-disable-region.1 \ > ndctl-enable-namespace.1 \ > - ndctl-disable-namespace.1 > + ndctl-disable-namespace.1 \ > + ndctl-create-namespace.1 > > XML_DEPS = \ > $(top_srcdir)/version.m4 \ > diff --git a/Documentation/ndctl-create-namespace.txt b/Documentation/ndctl-create-namespace.txt > new file mode 100644 > index 000000000000..3a9f911c231c > --- /dev/null > +++ b/Documentation/ndctl-create-namespace.txt > @@ -0,0 +1,127 @@ > +ndctl-create-namespace(1) > +========================= > + > +NAME > +---- > +ndctl-create-namespace - provision or reconfigure a namespace > + > +SYNOPSIS > +-------- > +[verse] > +'ndctl create-namespace' [<options>] > + > +include::namespace-description.txt[] > + > +EXAMPLES > +-------- > + > +Create a maximally sized pmem namespace in 'memory' mode > +[verse] > +ndctl create-namespace > + > +Convert namespace0.0 to 'safe' mode > +[verse] > +ndctl create-namespace -f -e namespace0.0 --mode=safe > + > +OPTIONS > +------- > +-t:: > +--type=:: > + Create a 'pmem' or 'blk' namespace (subject to available > + capacity). A pmem namespace supports the DAX (direct access) > + capability to linkndctl:mmap[2] persistent memory directly into > + a process address space. A blk namespace access persistent > + memory through a block-window-aperture. Compared to pmem it > + supports a traditional storage error model (EIO on error rather > + than a cpu exception on a bad memory access), but it does not > + support DAX. > + > +-m:: > +--mode=:: > + - "safe": persistent memory, given that it is byte addressable, > + does not support sector atomicity. The problematic aspect of > + sector tearing is that most applications do not know they have > + a atomic sector update dependency. At least a disk rarely > + ever tears sectors and if it does it almost certainly returns > + a checksum error on access. Persistent memory devices will > + always tear and always silently. Until an application is > + audited to be robust in the presence of sector-tearing "safe" > + mode is recommended. This imposes some performance overhead > + and disables the DAX capability. > + > + - "memory": A pmem namespace in this mode supports direct I/O > + to/from DAX mappings. Depending on the kernel this mode may > + come at the cost of allocating per-pmem-page metadata. If that > + allocation is required the capacity can be allocated from > + "System RAM" or from a reserved portion of pmem (see the --map= > + option). > + > + - "raw": expose the namespace capacity directly with some > + limitations. Neither a raw pmem namepace nor raw blk namespace > + support sector atomicity by default (see "safe" mode below). A > + raw pmem namespace may have limited support for passing a DAX > + mapping to other syscalls. I.e. direct-I/O to/from a DAX buffer > + may fail for a pmem namespace in raw mode. > + > +-s:: > +--size=:: > + For NVDIMM devices that support namespace labels, set the > + namespace size. Otherwise it defaults to the maximum size > + specified by platform firmware. > + > +-e:: > +--reconfig=:: > + Reconfigure an existing namespace (change the mode, sector size, > + etc...). All namespace parameters, save uuid, default to the > + current attributes of the specified namespace. The namespace is > + then re-created with the specified modifications. The uuid is > + refreshed to a new value by default whenever the data layout of > + a namespace is changed, see --uuid= to set a specific uuid. > + > +-u:: > +--uuid=:: > + This option is not recommended as a new uuid should be generated > + every time a namespace is (re-)created. For recovery scenarios > + however the uuid may be specified. > + > +-n:: > +--name=:: > + For NVDIMM devices that support namespace labels, > + specify a human friendly name for a namespace. This name is > + available as device attribute for use in udev rules or > + elsewhere. > + > +-l:: > +--sector-size:: > + Specify the logical sector size (LBA size) of the block storage > + device associated with a namespace. > + > +-M:: > +--map=:: > + A pmem namespace in "memory" mode may require allocation > + of per-page metadata. The allocation can be drawn from either: > + - "mem": typical system memory > + - "dev": persistent memory reserved from the namespace > + > +-f:: > +--force:: > + Unless this option is specified a 'reconfigure > + namespace' operation will fail if the namespace is presently > + active. Specifying --force causes the namespace to be disabled > + before reconfiguring. > + > +-v:: > +--verbose:: > + Emit debug messages for the namespace creation process > + > +-r:: > +--region=:: > +include::xable-region-options.txt[] > + > +SEE ALSO > +-------- > +linkndctl:ndctl-zero-labels[1], > +linkndctl:ndctl-disable-namespace[1], > +linkndctl:ndctl-enable-namespace[1], > +http://pmem.io/documents/NVDIMM_Namespace_Spec.pdf[NVDIMM Namespace > +Specification] > diff --git a/Documentation/ndctl.txt b/Documentation/ndctl.txt > index 87d2fbfcbcf9..13a0e36627f3 100644 > --- a/Documentation/ndctl.txt > +++ b/Documentation/ndctl.txt > @@ -32,7 +32,7 @@ namspaces) associated with an NVDIMM bus. > > SEE ALSO > -------- > -linkndctl:ndctl-zero-labels[1], > +linkndctl:ndctl-create-namespace[1], > linkndctl:ndctl-enable-region[1], > linkndctl:ndctl-disable-region[1], > linkndctl:ndctl-enable-namespace[1], > diff --git a/Makefile.am b/Makefile.am > index ffbbd044050f..baae4d0c1ee4 100644 > --- a/Makefile.am > +++ b/Makefile.am > @@ -64,7 +64,7 @@ bin_PROGRAMS = ndctl > > ndctl_SOURCES = ndctl.c \ > builtin-create-nfit.c \ > - builtin-xable-namespace.c \ > + builtin-xaction-namespace.c \ > builtin-xable-region.c \ > builtin-test.c \ > builtin-help.c \ > @@ -72,8 +72,8 @@ ndctl_SOURCES = ndctl.c \ > util/parse-options.c \ > util/parse-options.h \ > util/usage.c \ > + util/size.c \ > util/strbuf.c \ > - util/strbuf.h \ > util/wrapper.c > > if ENABLE_TEST > diff --git a/builtin-create-nfit.c b/builtin-create-nfit.c > index 6884b28db340..fad6b9a4b65d 100644 > --- a/builtin-create-nfit.c > +++ b/builtin-create-nfit.c > @@ -8,6 +8,7 @@ > #include <sys/stat.h> > #include <ccan/list/list.h> > #include <util/parse-options.h> > +#include <util/size.h> > > #include <nfit.h> > > @@ -20,50 +21,6 @@ struct spa { > unsigned long long size, offset; > }; > > -#define SZ_1K 0x00000400 > -#define SZ_1M 0x00100000 > -#define SZ_1G 0x40000000 > -#define SZ_1T 0x10000000000ULL > - > -static unsigned long long parse_size64(const char *str) > -{ > - unsigned long long val, check; > - char *end; > - > - val = strtoull(str, &end, 0); > - if (val == ULLONG_MAX) > - return val; > - check = val; > - switch (*end) { > - case 'k': > - case 'K': > - val *= SZ_1K; > - end++; > - break; > - case 'm': > - case 'M': > - val *= SZ_1M; > - end++; > - break; > - case 'g': > - case 'G': > - val *= SZ_1G; > - end++; > - break; > - case 't': > - case 'T': > - val *= SZ_1T; > - end++; > - break; > - default: > - break; > - } > - > - if (val < check || *end != '\0') > - val = ULLONG_MAX; > - return val; > -} > - > static int parse_add_spa(const struct option *option, const char *__arg, int unset) > { > struct spa *s = calloc(1, sizeof(struct spa)); > diff --git a/builtin-xable-namespace.c b/builtin-xable-namespace.c > deleted file mode 100644 > index 13a08633487a..000000000000 > --- a/builtin-xable-namespace.c > +++ /dev/null > @@ -1,153 +0,0 @@ > -#include <stdio.h> > -#include <errno.h> > -#include <stdlib.h> > -#include <unistd.h> > -#include <limits.h> > -#include <util/parse-options.h> > -#include <ndctl/libndctl.h> > - > -static const char *namespace_bus; > -static const char *namespace_region; > - > -static const struct option namespace_options[] = { > - OPT_STRING('b', "bus", &namespace_bus, "bus-id", > - "<namespace> must be on a bus with an id/provider of <bus-id>"), > - OPT_STRING('r', "region", &namespace_region, "region-id", > - "<namespace> must be a child of a region with an id/name of <region-id>"), > - OPT_END(), > -}; > - > -static const char *parse_namespace_options(int argc, const char **argv, > - char *xable_usage) > -{ > - const char * const u[] = { > - xable_usage, > - NULL > - }; > - int i; > - > - argc = parse_options(argc, argv, namespace_options, u, 0); > - > - if (argc == 0) > - error("specify a namespace to delete, or \"all\"\n"); > - for (i = 1; i < argc; i++) > - error("unknown extra parameter \"%s\"\n", argv[i]); > - if (argc == 0 || argc > 1) { > - usage_with_options(u, namespace_options); > - return NULL; /* we won't return from usage_with_options() */ > - } > - return argv[0]; > -} > - > -static int do_xable_namespace(const char *namespace, > - int (*xable_fn)(struct ndctl_namespace *)) > -{ > - unsigned long bus_id = ULONG_MAX, region_id = ULONG_MAX, id; > - const char *provider, *region_name, *ndns_name; > - int rc = -ENXIO, success = 0; > - struct ndctl_namespace *ndns; > - struct ndctl_region *region; > - struct ndctl_ctx *ctx; > - struct ndctl_bus *bus; > - > - if (!namespace) > - goto out; > - > - rc = ndctl_new(&ctx); > - if (rc < 0) > - goto out; > - > - if (namespace_bus) { > - char *end = NULL; > - > - bus_id = strtoul(namespace_bus, &end, 0); > - if (end) > - bus_id = ULONG_MAX; > - } > - > - if (namespace_region) { > - char *end = NULL; > - > - region_id = strtoul(namespace_region, &end, 0); > - if (end) > - region_id = ULONG_MAX; > - } > - > - ndctl_bus_foreach(ctx, bus) { > - provider = ndctl_bus_get_provider(bus); > - id = ndctl_bus_get_id(bus); > - > - if (bus_id < ULONG_MAX && bus_id != id) > - continue; > - else if (bus_id == ULONG_MAX && namespace_bus > - && strcmp(namespace_bus, provider) != 0) > - continue; > - > - ndctl_region_foreach(bus, region) { > - region_name = ndctl_region_get_devname(region); > - id = ndctl_region_get_id(region); > - > - if (region_id < ULONG_MAX && region_id != id) > - continue; > - else if (region_id == ULONG_MAX && namespace_region > - && strcmp(namespace_region, region_name) != 0) > - continue; > - ndctl_namespace_foreach(region, ndns) { > - ndns_name = ndctl_namespace_get_devname(ndns); > - > - if (strcmp(namespace, "all") != 0 > - && strcmp(namespace, ndns_name) != 0) > - continue; > - if (xable_fn(ndns) == 0) > - success++; > - } > - } > - } > - > - rc = success; > - ndctl_unref(ctx); > - out: > - namespace_bus = NULL; > - namespace_region = NULL; > - return rc; > -} > - > -int cmd_disable_namespace(int argc, const char **argv) > -{ > - char *xable_usage = "ndctl disable-namespace <namespace> [<options>]"; > - const char *namespace = parse_namespace_options(argc, argv, xable_usage); > - int disabled = do_xable_namespace(namespace, ndctl_namespace_disable_invalidate); > - > - if (disabled < 0) { > - fprintf(stderr, "error disabling namespaces: %s\n", > - strerror(-disabled)); > - return disabled; > - } else if (disabled == 0) { > - fprintf(stderr, "disabled 0 namespaces\n"); > - return -ENXIO; > - } else { > - fprintf(stderr, "disabled %d namespace%s\n", disabled, > - disabled > 1 ? "s" : ""); > - return disabled; > - } > -} > - > -int cmd_enable_namespace(int argc, const char **argv) > -{ > - char *xable_usage = "ndctl enable-namespace <namespace> [<options>]"; > - const char *namespace = parse_namespace_options(argc, argv, xable_usage); > - int enabled = do_xable_namespace(namespace, ndctl_namespace_enable); > - > - if (enabled < 0) { > - fprintf(stderr, "error enabling namespaces: %s\n", > - strerror(-enabled)); > - return enabled; > - } else if (enabled == 0) { > - fprintf(stderr, "enabled 0 namespaces\n"); > - return -ENXIO; > - } else { > - fprintf(stderr, "enabled %d namespace%s\n", enabled, > - enabled > 1 ? "s" : ""); > - return enabled; > - } > -} > diff --git a/builtin-xaction-namespace.c b/builtin-xaction-namespace.c > new file mode 100644 > index 000000000000..fdf20dfc01af > --- /dev/null > +++ b/builtin-xaction-namespace.c > @@ -0,0 +1,693 @@ > +/* > + * Copyright(c) 2015 Intel Corporation. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of version 2 of the GNU General Public License as > + * published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > +#include <stdio.h> > +#include <fcntl.h> > +#include <errno.h> > +#include <stdlib.h> > +#include <unistd.h> > +#include <limits.h> > +#include <syslog.h> > +#include <sys/stat.h> > +#include <uuid/uuid.h> > +#include <sys/types.h> > +#include <util/size.h> > +#include <ndctl/libndctl.h> > +#include <util/parse-options.h> > +#include <ccan/array_size/array_size.h> > + > +#ifdef HAVE_NDCTL_H > +#include <linux/ndctl.h> > +#else > +#include <ndctl.h> > +#endif > + > +static bool verbose; > +static bool force; > +static struct parameters { > + const char *bus; > + const char *map; > + const char *type; > + const char *uuid; > + const char *name; > + const char *size; > + const char *mode; > + const char *region; > + const char *reconfig; > + const char *sector_size; > +} param; > + > +#define NSLABEL_NAME_LEN 64 > +struct parsed_parameters { > + enum ndctl_pfn_loc loc; > + uuid_t uuid; > + char name[NSLABEL_NAME_LEN]; > + enum ndctl_namespace_mode mode; > + unsigned long long size; > + unsigned long sector_size; > +}; > + > +#define debug(fmt, ...) \ > + if (verbose) { \ > + fprintf(stderr, "%s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__); \ > + } else { \ > + do { } while (0); \ > + } > + > +#define BASE_OPTIONS() \ > +OPT_STRING('b', "bus", ¶m.bus, "bus-id", \ > + "limit namespace to a bus with an id or provider of <bus-id>"), \ > +OPT_STRING('r', "region", ¶m.region, "region-id", \ > + "limit namespace to a region with an id or name of <region-id>") > + > +#define CREATE_OPTIONS() \ > +OPT_STRING('e', "reconfig", ¶m.reconfig, "reconfig namespace", \ > + "reconfigure existing namespace"), \ > +OPT_STRING('u', "uuid", ¶m.uuid, "uuid", \ > + "specify the uuid for the namespace (default: autogenerate)"), \ > +OPT_STRING('n', "name", ¶m.name, "name", \ > + "specify an optional free form name for the namespace"), \ > +OPT_STRING('s', "size", ¶m.size, "size", \ > + "specify the namespace size in bytes (default: available capacity)"), \ > +OPT_STRING('m', "mode", ¶m.mode, "operation-mode", \ > + "specify a mode for the namespace, 'safe', 'memory', or 'raw'"), \ > +OPT_STRING('M', "map", ¶m.map, "memmap-location", \ > + "specify 'mem' or 'dev' for the location of the memmap"), \ > +OPT_STRING('l', "sector-size", ¶m.sector_size, "lba-size", \ > + "specify the logical sector size in bytes"), \ > +OPT_STRING('t', "type", ¶m.type, "type", \ > + "specify the type of namespace to create 'pmem' or 'blk'"), \ > +OPT_BOOLEAN('f', "force", &force, "reconfigure namespace even if currently active"), \ > +OPT_BOOLEAN('v', "verbose", &verbose, "emit extra debug messages to stderr") > + > +static const struct option base_options[] = { > + BASE_OPTIONS(), > + OPT_END(), > +}; > + > +static const struct option create_options[] = { > + BASE_OPTIONS(), > + CREATE_OPTIONS(), > + OPT_END(), > +}; > + > +enum namespace_action { > + ACTION_ENABLE, > + ACTION_DISABLE, > + ACTION_CREATE, > +}; > + > +/* > + * parse_namespace_options - basic parsing sanity checks before we start > + * looking at actual namespace devices and available resources. > + */ > +static const char *parse_namespace_options(int argc, const char **argv, > + enum namespace_action mode, const struct option *options, > + char *xable_usage) > +{ > + const char * const u[] = { > + xable_usage, > + NULL > + }; > + int i, rc = 0; > + > + argc = parse_options(argc, argv, options, u, 0); > + > + if (param.type) { > + if (strcmp(param.type, "pmem") == 0) > + /* pass */; > + else if (strcmp(param.type, "blk") == 0) > + /* pass */; > + else { > + error("invalid type '%s', must be either 'pmem' or 'blk'\n", > + param.type); > + rc = -EINVAL; > + } > + } else if (!param.reconfig) > + param.type = "pmem"; > + > + if (param.mode) { > + if (strcmp(param.mode, "safe") == 0) > + /* pass */; > + else if (strcmp(param.mode, "sector") == 0) > + param.mode = "safe"; /* pass */ > + else if (strcmp(param.mode, "memory") == 0) > + /* pass */; > + else if (strcmp(param.mode, "raw") == 0) > + /* pass */; > + else { > + error("invalid mode '%s'\n", param.mode); > + rc = -EINVAL; > + } > + } else if (!param.reconfig) { > + if (strcmp(param.type, "pmem") == 0) > + param.mode = "memory"; > + else > + param.mode = "safe"; > + } > + > + if (param.map) { > + if (strcmp(param.map, "mem") == 0) > + /* pass */; > + else if (strcmp(param.map, "dev") == 0) > + /* pass */; > + else { > + error("invalid map location '%s'\n", param.map); > + rc = -EINVAL; > + } > + > + if (!param.reconfig && strcmp(param.mode, "memory") != 0) { > + error("--map only valid for a memory mode pmem namespace\n"); > + rc = -EINVAL; > + } > + } else if (!param.reconfig) > + param.map = "dev"; > + > + /* check for incompatible mode and type combinations */ > + if (param.type && param.mode && strcmp(param.type, "blk") == 0 > + && strcmp(param.mode, "memory") == 0) { > + error("only 'pmem' namespaces can be placed into 'memory' mode\n"); > + rc = -ENXIO; > + } > + > + if (param.size && parse_size64(param.size) == ULLONG_MAX) { > + error("failed to parse namespace size '%s'\n", > + param.size); > + rc = -EINVAL; > + } > + > + if (param.uuid) { > + uuid_t uuid; > + > + if (uuid_parse(param.uuid, uuid)) { > + error("failed to parse uuid: '%s'\n", param.uuid); > + rc = -EINVAL; > + } > + } > + > + if (param.sector_size) { > + if (parse_size64(param.sector_size) == ULLONG_MAX) { > + error("invalid sector size: %s\n", param.sector_size); > + rc = -EINVAL; > + } > + > + if (param.type && param.mode && strcmp(param.type, "pmem") == 0 > + && strcmp(param.mode, "safe") != 0) { > + error("'pmem' namespaces do not support setting 'sector size'\n"); > + rc = -EINVAL; > + } > + } else if (!param.reconfig) > + param.sector_size = "4096"; > + > + if (argc == 0 && mode != ACTION_CREATE) { > + error("specify a namespace to %s, or \"all\"\n", > + mode == ACTION_ENABLE ? "enable" : "disable"); > + rc = -EINVAL; > + } > + for (i = mode == ACTION_CREATE ? 0 : 1; i < argc; i++) { > + error("unknown extra parameter \"%s\"\n", argv[i]); > + rc = -EINVAL; > + } > + > + if (rc) { > + usage_with_options(u, options); > + return NULL; /* we won't return from usage_with_options() */ > + } > + > + return mode == ACTION_CREATE ? param.reconfig : argv[0]; > +} > + > +#define try(prefix, op, dev, p) \ > +do { \ > + int __rc = prefix##_##op(dev, p); \ > + if (__rc) { \ > + debug("%s: " #op " failed: %d\n", \ > + prefix##_get_devname(dev), __rc); \ > + return __rc; \ > + } \ > +} while (0) > + > +static int setup_namespace(struct ndctl_region *region, > + struct ndctl_namespace *ndns, struct parsed_parameters *p) > +{ > + uuid_t uuid; > + int rc; > + > + if (ndctl_namespace_get_type(ndns) != ND_DEVICE_NAMESPACE_IO) { > + try(ndctl_namespace, set_uuid, ndns, p->uuid); > + try(ndctl_namespace, set_alt_name, ndns, p->name); > + try(ndctl_namespace, set_size, ndns, p->size); > + } > + > + if (ndctl_namespace_get_type(ndns) == ND_DEVICE_NAMESPACE_BLK) > + try(ndctl_namespace, set_sector_size, ndns, p->sector_size); > + > + uuid_generate(uuid); > + if (ndctl_namespace_get_mode(ndns) != NDCTL_NS_MODE_MEMORY > + && p->mode == NDCTL_NS_MODE_MEMORY) { > + struct ndctl_pfn *pfn = ndctl_region_get_pfn_seed(region); > + > + try(ndctl_pfn, set_uuid, pfn, uuid); > + try(ndctl_pfn, set_location, pfn, p->loc); > + try(ndctl_pfn, set_align, pfn, SZ_2M); > + try(ndctl_pfn, set_namespace, pfn, ndns); > + rc = ndctl_pfn_enable(pfn); > + } else if (p->mode == NDCTL_NS_MODE_SAFE) { > + struct ndctl_btt *btt = ndctl_region_get_btt_seed(region); > + > + try(ndctl_btt, set_uuid, btt, uuid); > + try(ndctl_btt, set_sector_size, btt, p->sector_size); > + try(ndctl_btt, set_namespace, btt, ndns); > + rc = ndctl_btt_enable(btt); > + } else > + rc = ndctl_namespace_enable(ndns); > + > + if (rc) { > + error("%s: failed to enable\n", > + ndctl_namespace_get_devname(ndns)); > + } > + return rc; > +} > + > +static int is_namespace_active(struct ndctl_namespace *ndns) > +{ > + return ndns && (ndctl_namespace_is_enabled(ndns) > + || ndctl_namespace_get_pfn(ndns) > + || ndctl_namespace_get_btt(ndns)); > +} > + > +/* > + * validate_namespace_options - init parameters for setup_namespace > + * @ndns: specified when we are reconfiguring, NULL otherwise > + * @p: parameters to fill > + * > + * parse_namespace_options() will have already done basic verification > + * of the parameters and set defaults in the !reconfigure case. When > + * reconfiguring fill in any unset options with defaults from the > + * namespace itself. > + * > + * Given that parse_namespace_options() runs before we have identified > + * the target namespace we need to do basic sanity checks here for > + * pmem-only attributes specified for blk namespace and vice versa. > + */ > +static int validate_namespace_options(struct ndctl_namespace *ndns, > + struct parsed_parameters *p) > +{ > + int rc = 0; > + > + memset(p, 0, sizeof(*p)); > + > + if (param.size) > + p->size = parse_size64(param.size); > + else if (ndns) > + p->size = ndctl_namespace_get_size(ndns); > + > + if (param.uuid) { > + if (uuid_parse(param.uuid, p->uuid) != 0) { > + debug("%s: invalid uuid\n", __func__); > + return -EINVAL; > + } > + } else > + uuid_generate(p->uuid); > + > + if (param.name) > + rc = snprintf(p->name, sizeof(p->name), "%s", param.name); > + else if (ndns) > + rc = snprintf(p->name, sizeof(p->name), "%s", > + ndctl_namespace_get_alt_name(ndns)); > + if (rc >= (int) sizeof(p->name)) { > + debug("%s: alt name overflow\n", __func__); > + return -EINVAL; > + } > + > + if (param.mode) { > + if (strcmp(param.mode, "memory") == 0) > + p->mode = NDCTL_NS_MODE_MEMORY; > + else if (strcmp(param.mode, "safe") == 0) > + p->mode = NDCTL_NS_MODE_SAFE; > + else > + p->mode = NDCTL_NS_MODE_RAW; > + > + if (ndns && ndctl_namespace_get_type(ndns) > + == ND_DEVICE_NAMESPACE_BLK > + && p->mode == NDCTL_NS_MODE_MEMORY) { > + debug("%s: blk namespace do not support memory mode\n", > + ndctl_namespace_get_devname(ndns)); > + return -EINVAL; > + } > + } else if (ndns) > + p->mode = ndctl_namespace_get_mode(ndns); > + > + if (param.sector_size) { > + p->sector_size = parse_size64(param.sector_size); > + > + if (ndns && p->mode != NDCTL_NS_MODE_SAFE > + && ndctl_namespace_get_type(ndns) > + == ND_DEVICE_NAMESPACE_PMEM) { > + debug("%s: does not support sector_size modification\n", > + ndctl_namespace_get_devname(ndns)); > + return -EINVAL; > + } > + } else if (ndns) { > + struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns); > + > + if (btt) > + p->sector_size = ndctl_btt_get_sector_size(btt); > + else if (ndctl_namespace_get_type(ndns) > + == ND_DEVICE_NAMESPACE_BLK) > + p->sector_size = ndctl_namespace_get_sector_size(ndns); > + } > + > + if (param.map) { > + if (strcmp(param.map, "mem")) > + p->loc = NDCTL_PFN_LOC_RAM; > + else > + p->loc = NDCTL_PFN_LOC_PMEM; > + > + if (ndns && p->mode != NDCTL_NS_MODE_MEMORY) { > + debug("%s: --map= only valid for memory mode namespace\n", > + ndctl_namespace_get_devname(ndns)); > + return -EINVAL; > + } > + } else if (p->mode == NDCTL_NS_MODE_MEMORY) > + p->loc = NDCTL_PFN_LOC_PMEM; > + > + return 0; > +} > + > +static int namespace_create(struct ndctl_region *region) > +{ > + const char *devname = ndctl_region_get_devname(region); > + unsigned long long available; > + struct ndctl_namespace *ndns; > + struct parsed_parameters p; > + > + if (validate_namespace_options(NULL, &p)) > + return -EINVAL; > + > + if (ndctl_region_get_ro(region)) { > + debug("%s: read-only, inelligible for namespace creation\n", > + devname); > + return -EAGAIN; > + } > + > + available = ndctl_region_get_available_size(region); > + if (!available || p.size > available) { > + debug("%s: insufficient capacity size: %llx avail: %llx\n", > + devname, p.size, available); > + return -EAGAIN; > + } > + > + if (p.size == 0) > + p.size = available; > + > + ndns = ndctl_region_get_namespace_seed(region); > + if (is_namespace_active(ndns)) { > + debug("%s: no %s namespace seed\n", devname, > + ndns ? "idle" : "available"); > + return -ENODEV; > + } > + > + return setup_namespace(region, ndns, &p); > +} > + > +static int zero_info_block(struct ndctl_namespace *ndns) > +{ > + const char *devname = ndctl_namespace_get_devname(ndns); > + int fd, rc = -ENXIO; > + char path[50]; > + void *buf; > + > + if (posix_memalign(&buf, 4096, 4096) != 0) > + return -ENXIO; > + > + ndctl_namespace_set_raw_mode(ndns, 1); > + ndctl_namespace_enable(ndns); > + > + sprintf(path, "/dev/%s", ndctl_namespace_get_block_device(ndns)); > + fd = open(path, O_RDWR|O_DIRECT|O_EXCL); > + if (fd < 0) { > + debug("%s: failed to open %s to zero info block\n", > + devname, path); > + goto out; > + } > + > + memset(buf, 0, 4096); > + rc = pwrite(fd, buf, 4096, 4096); > + if (rc < 4096) { > + debug("%s: failed to zero info block %s\n", > + devname, path); > + rc = -ENXIO; > + } else > + rc = 0; > + close(fd); > + out: > + ndctl_namespace_set_raw_mode(ndns, 0); > + ndctl_namespace_disable_invalidate(ndns); > + free(buf); > + return rc; > +} > + > +static int namespace_reconfig(struct ndctl_region *region, > + struct ndctl_namespace *ndns) > +{ > + const char *devname = ndctl_namespace_get_devname(ndns); > + struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns); > + struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns); > + struct parsed_parameters p; > + const char *bdev = NULL; > + char path[50]; > + int fd, rc; > + > + if (validate_namespace_options(ndns, &p)) > + return -EINVAL; > + > + if (ndctl_region_get_ro(region)) { > + error("%s: read-only, re-configuration disabled\n", > + devname); > + return -ENXIO; > + } > + > + if (pfn && ndctl_pfn_is_enabled(pfn)) > + bdev = ndctl_pfn_get_block_device(pfn); > + else if (btt && ndctl_btt_is_enabled(btt)) > + bdev = ndctl_btt_get_block_device(btt); > + else if (ndctl_namespace_is_enabled(ndns)) > + bdev = ndctl_namespace_get_block_device(ndns); > + > + if (bdev && !force) { > + error("%s is active, specify --force for re-configuration\n", > + devname); > + return -EBUSY; > + } else if (bdev) { > + sprintf(path, "/dev/%s", bdev); > + fd = open(path, O_RDWR|O_EXCL); > + if (fd >= 0) { > + /* > + * Got it, now block new mounts while we have it > + * pinned. > + */ > + ndctl_namespace_disable_invalidate(ndns); > + close(fd); > + } else { > + /* > + * Yes, TOCTOU hole, but if you're racing namespace > + * creation you have other problems, and there's nothing > + * stopping the !bdev case from racing to mount an fs or > + * re-enabling the namepace. > + */ > + return -EBUSY; > + } > + } > + > + if (pfn || btt) { > + rc = zero_info_block(ndns); > + if (rc) > + return rc; > + } > + > + rc = ndctl_namespace_delete(ndns); > + if (rc) { > + error("%s: failed to reclaim\n", devname); > + return rc; > + } > + > + ndns = ndctl_region_get_namespace_seed(region); > + if (is_namespace_active(ndns)) { > + debug("%s: no %s namespace seed\n", devname, > + ndns ? "idle" : "available"); > + return -ENODEV; > + } > + > + return setup_namespace(region, ndns, &p); > +} > + > +static int do_xaction_namespace(const char *namespace, > + enum namespace_action action) > +{ > + unsigned long bus_id = ULONG_MAX, region_id = ULONG_MAX, id; > + const char *provider, *region_name, *ndns_name; > + int rc = -ENXIO, success = 0; > + struct ndctl_namespace *ndns; > + struct ndctl_region *region; > + struct ndctl_ctx *ctx; > + struct ndctl_bus *bus; > + > + if (!namespace && action != ACTION_CREATE) > + return rc; > + > + rc = ndctl_new(&ctx); > + if (rc < 0) > + return rc; > + > + if (verbose) > + ndctl_set_log_priority(ctx, LOG_DEBUG); > + > + if (param.bus) { > + char *end = NULL; > + > + bus_id = strtoul(param.bus, &end, 0); > + if (end) > + bus_id = ULONG_MAX; > + } > + > + if (param.region) { > + char *end = NULL; > + > + region_id = strtoul(param.region, &end, 0); > + if (end) > + region_id = ULONG_MAX; > + } > + > + ndctl_bus_foreach(ctx, bus) { > + provider = ndctl_bus_get_provider(bus); > + id = ndctl_bus_get_id(bus); > + > + if (bus_id < ULONG_MAX && bus_id != id) > + continue; > + else if (bus_id == ULONG_MAX && param.bus > + && strcmp(param.bus, provider) != 0) > + continue; > + > + ndctl_region_foreach(bus, region) { > + region_name = ndctl_region_get_devname(region); > + id = ndctl_region_get_id(region); > + > + if (region_id < ULONG_MAX && region_id != id) > + continue; > + else if (region_id == ULONG_MAX && param.region > + && strcmp(param.region, region_name) != 0) > + continue; > + > + if (param.type) { > + if (strcmp(param.type, "pmem") == 0 > + && ndctl_region_get_type(region) > + == ND_DEVICE_REGION_PMEM) > + /* pass */; > + else if (strcmp(param.type, "blk") == 0 > + && ndctl_region_get_type(region) > + == ND_DEVICE_REGION_BLK) > + /* pass */; > + else > + continue; > + } > + > + if (action == ACTION_CREATE && !namespace) { > + rc = namespace_create(region); > + if (rc == -EAGAIN) { > + rc = 0; > + continue; > + } > + goto done; > + } > + ndctl_namespace_foreach(region, ndns) { > + ndns_name = ndctl_namespace_get_devname(ndns); > + > + if (strcmp(namespace, "all") != 0 > + && strcmp(namespace, ndns_name) != 0) > + continue; > + switch (action) { > + case ACTION_DISABLE: > + rc = ndctl_namespace_disable_invalidate(ndns); > + break; > + case ACTION_ENABLE: > + rc = ndctl_namespace_enable(ndns); > + break; > + case ACTION_CREATE: > + return namespace_reconfig(region, ndns); > + } > + if (rc == 0) > + success++; > + } > + } > + } > + > + rc = success; > + done: > + ndctl_unref(ctx); > + return rc; > +} > + > +int cmd_disable_namespace(int argc, const char **argv) > +{ > + char *xable_usage = "ndctl disable-namespace <namespace> [<options>]"; > + const char *namespace = parse_namespace_options(argc, argv, > + ACTION_DISABLE, base_options, xable_usage); > + int disabled = do_xaction_namespace(namespace, ACTION_DISABLE); > + > + if (disabled < 0) { > + fprintf(stderr, "error disabling namespaces: %s\n", > + strerror(-disabled)); > + return disabled; > + } else if (disabled == 0) { > + fprintf(stderr, "disabled 0 namespaces\n"); > + return -ENXIO; > + } else { > + fprintf(stderr, "disabled %d namespace%s\n", disabled, > + disabled > 1 ? "s" : ""); > + return disabled; > + } > +} > + > +int cmd_enable_namespace(int argc, const char **argv) > +{ > + char *xable_usage = "ndctl enable-namespace <namespace> [<options>]"; > + const char *namespace = parse_namespace_options(argc, argv, > + ACTION_ENABLE, base_options, xable_usage); > + int enabled = do_xaction_namespace(namespace, ACTION_ENABLE); > + > + if (enabled < 0) { > + fprintf(stderr, "error enabling namespaces: %s\n", > + strerror(-enabled)); > + return enabled; > + } else if (enabled == 0) { > + fprintf(stderr, "enabled 0 namespaces\n"); > + return -ENXIO; > + } else { > + fprintf(stderr, "enabled %d namespace%s\n", enabled, > + enabled > 1 ? "s" : ""); > + return enabled; > + } > +} > + > +int cmd_create_namespace(int argc, const char **argv) > +{ > + char *xable_usage = "ndctl create-namespace [<options>]"; > + const char *namespace = parse_namespace_options(argc, argv, > + ACTION_CREATE, create_options, xable_usage); > + int created = do_xaction_namespace(namespace, ACTION_CREATE); > + > + if (created < 0) > + fprintf(stderr, "failed to %s namespace\n", namespace > + ? "reconfigure" : "create"); > + return created; > +} > diff --git a/builtin.h b/builtin.h > index ed0b7e497a43..27ff248a6d84 100644 > --- a/builtin.h > +++ b/builtin.h > @@ -10,6 +10,7 @@ struct cmd_struct { > > int cmd_create_nfit(int argc, const char **argv); > int cmd_enable_namespace(int argc, const char **argv); > +int cmd_create_namespace(int argc, const char **argv); > int cmd_disable_namespace(int argc, const char **argv); > int cmd_enable_region(int argc, const char **argv); > int cmd_disable_region(int argc, const char **argv); > @@ -21,6 +22,4 @@ int cmd_test(int argc, const char **argv); > #ifdef ENABLE_DESTRUCTIVE > int cmd_bat(int argc, const char **argv); > #endif > - > #endif /* _NDCTL_BUILTIN_H_ */ > - > diff --git a/ndctl.c b/ndctl.c > index f4e2746f85e3..0c117d7fbd2f 100644 > --- a/ndctl.c > +++ b/ndctl.c > @@ -25,6 +25,7 @@ static struct cmd_struct commands[] = { > { "version", cmd_version }, > { "create-nfit", cmd_create_nfit }, > { "enable-namespace", cmd_enable_namespace }, > + { "create-namespace", cmd_create_namespace }, > { "disable-namespace", cmd_disable_namespace }, > { "enable-region", cmd_enable_region }, > { "disable-region", cmd_disable_region }, > diff --git a/util/size.c b/util/size.c > new file mode 100644 > index 000000000000..1ca2dcff5e28 > --- /dev/null > +++ b/util/size.c > @@ -0,0 +1,42 @@ > +#include <stdlib.h> > +#include <limits.h> > +#include <util/size.h> > + > +unsigned long long parse_size64(const char *str) > +{ > + unsigned long long val, check; > + char *end; > + > + val = strtoull(str, &end, 0); > + if (val == ULLONG_MAX) > + return val; > + check = val; > + switch (*end) { > + case 'k': > + case 'K': > + val *= SZ_1K; > + end++; > + break; > + case 'm': > + case 'M': > + val *= SZ_1M; > + end++; > + break; > + case 'g': > + case 'G': > + val *= SZ_1G; > + end++; > + break; > + case 't': > + case 'T': > + val *= SZ_1T; > + end++; > + break; > + default: > + break; > + } > + > + if (val < check || *end != '\0') > + val = ULLONG_MAX; > + return val; > +} > diff --git a/util/size.h b/util/size.h > new file mode 100644 > index 000000000000..64c146480ef0 > --- /dev/null > +++ b/util/size.h > @@ -0,0 +1,12 @@ > +#ifndef _NDCTL_SIZE_H_ > +#define _NDCTL_SIZE_H_ > + > +#define SZ_1K 0x00000400 > +#define SZ_1M 0x00100000 > +#define SZ_2M 0x00200000 > +#define SZ_1G 0x40000000 > +#define SZ_1T 0x10000000000ULL > + > +unsigned long long parse_size64(const char *str); > + > +#endif /* _NDCTL_SIZE_H_ */
On Thu, Jan 28, 2016 at 3:39 PM, Jeff Moyer <jmoyer@redhat.com> wrote: > Dan Williams <dan.j.williams@intel.com> writes: > >> A helper utility for instantiating a namespace. With no arguments it >> defaults to a maximally sized pmem namespace in memory mode. The search >> for available capacity can be constrained by bus, region, and/or >> namespace type using the same mechanism to filter namespaces as 'ndctl >> enable-namespace' and 'ndctl disable-namespace'. >> >> See the include man page for more details. >> >> Cc: Jeff Moyer <jmoyer@redhat.com> >> [jmoyer: null dereference fix] > > Hi, Dan, > > If that's the only fix since last I saw this patch, then it's still got > bugs. ;-) Oh yes, there were indeed a pile more. I neglected to comment on them in this update. > Sorry I can't test right this second, but I'll dedicate > some time to it tomorrow. I'm pretty sure I hit another null pointer > dereference. Did you end up writing unit tests for this? Yes, see patch 13: https://lists.01.org/pipermail/linux-nvdimm/2016-January/004215.html
diff --git a/Documentation/Makefile.am b/Documentation/Makefile.am index bd43714d5215..51d186505b44 100644 --- a/Documentation/Makefile.am +++ b/Documentation/Makefile.am @@ -4,7 +4,8 @@ man1_MANS = \ ndctl-enable-region.1 \ ndctl-disable-region.1 \ ndctl-enable-namespace.1 \ - ndctl-disable-namespace.1 + ndctl-disable-namespace.1 \ + ndctl-create-namespace.1 XML_DEPS = \ $(top_srcdir)/version.m4 \ diff --git a/Documentation/ndctl-create-namespace.txt b/Documentation/ndctl-create-namespace.txt new file mode 100644 index 000000000000..3a9f911c231c --- /dev/null +++ b/Documentation/ndctl-create-namespace.txt @@ -0,0 +1,127 @@ +ndctl-create-namespace(1) +========================= + +NAME +---- +ndctl-create-namespace - provision or reconfigure a namespace + +SYNOPSIS +-------- +[verse] +'ndctl create-namespace' [<options>] + +include::namespace-description.txt[] + +EXAMPLES +-------- + +Create a maximally sized pmem namespace in 'memory' mode +[verse] +ndctl create-namespace + +Convert namespace0.0 to 'safe' mode +[verse] +ndctl create-namespace -f -e namespace0.0 --mode=safe + +OPTIONS +------- +-t:: +--type=:: + Create a 'pmem' or 'blk' namespace (subject to available + capacity). A pmem namespace supports the DAX (direct access) + capability to linkndctl:mmap[2] persistent memory directly into + a process address space. A blk namespace access persistent + memory through a block-window-aperture. Compared to pmem it + supports a traditional storage error model (EIO on error rather + than a cpu exception on a bad memory access), but it does not + support DAX. + +-m:: +--mode=:: + - "safe": persistent memory, given that it is byte addressable, + does not support sector atomicity. The problematic aspect of + sector tearing is that most applications do not know they have + a atomic sector update dependency. At least a disk rarely + ever tears sectors and if it does it almost certainly returns + a checksum error on access. Persistent memory devices will + always tear and always silently. Until an application is + audited to be robust in the presence of sector-tearing "safe" + mode is recommended. This imposes some performance overhead + and disables the DAX capability. + + - "memory": A pmem namespace in this mode supports direct I/O + to/from DAX mappings. Depending on the kernel this mode may + come at the cost of allocating per-pmem-page metadata. If that + allocation is required the capacity can be allocated from + "System RAM" or from a reserved portion of pmem (see the --map= + option). + + - "raw": expose the namespace capacity directly with some + limitations. Neither a raw pmem namepace nor raw blk namespace + support sector atomicity by default (see "safe" mode below). A + raw pmem namespace may have limited support for passing a DAX + mapping to other syscalls. I.e. direct-I/O to/from a DAX buffer + may fail for a pmem namespace in raw mode. + +-s:: +--size=:: + For NVDIMM devices that support namespace labels, set the + namespace size. Otherwise it defaults to the maximum size + specified by platform firmware. + +-e:: +--reconfig=:: + Reconfigure an existing namespace (change the mode, sector size, + etc...). All namespace parameters, save uuid, default to the + current attributes of the specified namespace. The namespace is + then re-created with the specified modifications. The uuid is + refreshed to a new value by default whenever the data layout of + a namespace is changed, see --uuid= to set a specific uuid. + +-u:: +--uuid=:: + This option is not recommended as a new uuid should be generated + every time a namespace is (re-)created. For recovery scenarios + however the uuid may be specified. + +-n:: +--name=:: + For NVDIMM devices that support namespace labels, + specify a human friendly name for a namespace. This name is + available as device attribute for use in udev rules or + elsewhere. + +-l:: +--sector-size:: + Specify the logical sector size (LBA size) of the block storage + device associated with a namespace. + +-M:: +--map=:: + A pmem namespace in "memory" mode may require allocation + of per-page metadata. The allocation can be drawn from either: + - "mem": typical system memory + - "dev": persistent memory reserved from the namespace + +-f:: +--force:: + Unless this option is specified a 'reconfigure + namespace' operation will fail if the namespace is presently + active. Specifying --force causes the namespace to be disabled + before reconfiguring. + +-v:: +--verbose:: + Emit debug messages for the namespace creation process + +-r:: +--region=:: +include::xable-region-options.txt[] + +SEE ALSO +-------- +linkndctl:ndctl-zero-labels[1], +linkndctl:ndctl-disable-namespace[1], +linkndctl:ndctl-enable-namespace[1], +http://pmem.io/documents/NVDIMM_Namespace_Spec.pdf[NVDIMM Namespace +Specification] diff --git a/Documentation/ndctl.txt b/Documentation/ndctl.txt index 87d2fbfcbcf9..13a0e36627f3 100644 --- a/Documentation/ndctl.txt +++ b/Documentation/ndctl.txt @@ -32,7 +32,7 @@ namspaces) associated with an NVDIMM bus. SEE ALSO -------- -linkndctl:ndctl-zero-labels[1], +linkndctl:ndctl-create-namespace[1], linkndctl:ndctl-enable-region[1], linkndctl:ndctl-disable-region[1], linkndctl:ndctl-enable-namespace[1], diff --git a/Makefile.am b/Makefile.am index ffbbd044050f..baae4d0c1ee4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -64,7 +64,7 @@ bin_PROGRAMS = ndctl ndctl_SOURCES = ndctl.c \ builtin-create-nfit.c \ - builtin-xable-namespace.c \ + builtin-xaction-namespace.c \ builtin-xable-region.c \ builtin-test.c \ builtin-help.c \ @@ -72,8 +72,8 @@ ndctl_SOURCES = ndctl.c \ util/parse-options.c \ util/parse-options.h \ util/usage.c \ + util/size.c \ util/strbuf.c \ - util/strbuf.h \ util/wrapper.c if ENABLE_TEST diff --git a/builtin-create-nfit.c b/builtin-create-nfit.c index 6884b28db340..fad6b9a4b65d 100644 --- a/builtin-create-nfit.c +++ b/builtin-create-nfit.c @@ -8,6 +8,7 @@ #include <sys/stat.h> #include <ccan/list/list.h> #include <util/parse-options.h> +#include <util/size.h> #include <nfit.h> @@ -20,50 +21,6 @@ struct spa { unsigned long long size, offset; }; -#define SZ_1K 0x00000400 -#define SZ_1M 0x00100000 -#define SZ_1G 0x40000000 -#define SZ_1T 0x10000000000ULL - -static unsigned long long parse_size64(const char *str) -{ - unsigned long long val, check; - char *end; - - val = strtoull(str, &end, 0); - if (val == ULLONG_MAX) - return val; - check = val; - switch (*end) { - case 'k': - case 'K': - val *= SZ_1K; - end++; - break; - case 'm': - case 'M': - val *= SZ_1M; - end++; - break; - case 'g': - case 'G': - val *= SZ_1G; - end++; - break; - case 't': - case 'T': - val *= SZ_1T; - end++; - break; - default: - break; - } - - if (val < check || *end != '\0') - val = ULLONG_MAX; - return val; -} - static int parse_add_spa(const struct option *option, const char *__arg, int unset) { struct spa *s = calloc(1, sizeof(struct spa)); diff --git a/builtin-xable-namespace.c b/builtin-xable-namespace.c deleted file mode 100644 index 13a08633487a..000000000000 --- a/builtin-xable-namespace.c +++ /dev/null @@ -1,153 +0,0 @@ -#include <stdio.h> -#include <errno.h> -#include <stdlib.h> -#include <unistd.h> -#include <limits.h> -#include <util/parse-options.h> -#include <ndctl/libndctl.h> - -static const char *namespace_bus; -static const char *namespace_region; - -static const struct option namespace_options[] = { - OPT_STRING('b', "bus", &namespace_bus, "bus-id", - "<namespace> must be on a bus with an id/provider of <bus-id>"), - OPT_STRING('r', "region", &namespace_region, "region-id", - "<namespace> must be a child of a region with an id/name of <region-id>"), - OPT_END(), -}; - -static const char *parse_namespace_options(int argc, const char **argv, - char *xable_usage) -{ - const char * const u[] = { - xable_usage, - NULL - }; - int i; - - argc = parse_options(argc, argv, namespace_options, u, 0); - - if (argc == 0) - error("specify a namespace to delete, or \"all\"\n"); - for (i = 1; i < argc; i++) - error("unknown extra parameter \"%s\"\n", argv[i]); - if (argc == 0 || argc > 1) { - usage_with_options(u, namespace_options); - return NULL; /* we won't return from usage_with_options() */ - } - return argv[0]; -} - -static int do_xable_namespace(const char *namespace, - int (*xable_fn)(struct ndctl_namespace *)) -{ - unsigned long bus_id = ULONG_MAX, region_id = ULONG_MAX, id; - const char *provider, *region_name, *ndns_name; - int rc = -ENXIO, success = 0; - struct ndctl_namespace *ndns; - struct ndctl_region *region; - struct ndctl_ctx *ctx; - struct ndctl_bus *bus; - - if (!namespace) - goto out; - - rc = ndctl_new(&ctx); - if (rc < 0) - goto out; - - if (namespace_bus) { - char *end = NULL; - - bus_id = strtoul(namespace_bus, &end, 0); - if (end) - bus_id = ULONG_MAX; - } - - if (namespace_region) { - char *end = NULL; - - region_id = strtoul(namespace_region, &end, 0); - if (end) - region_id = ULONG_MAX; - } - - ndctl_bus_foreach(ctx, bus) { - provider = ndctl_bus_get_provider(bus); - id = ndctl_bus_get_id(bus); - - if (bus_id < ULONG_MAX && bus_id != id) - continue; - else if (bus_id == ULONG_MAX && namespace_bus - && strcmp(namespace_bus, provider) != 0) - continue; - - ndctl_region_foreach(bus, region) { - region_name = ndctl_region_get_devname(region); - id = ndctl_region_get_id(region); - - if (region_id < ULONG_MAX && region_id != id) - continue; - else if (region_id == ULONG_MAX && namespace_region - && strcmp(namespace_region, region_name) != 0) - continue; - ndctl_namespace_foreach(region, ndns) { - ndns_name = ndctl_namespace_get_devname(ndns); - - if (strcmp(namespace, "all") != 0 - && strcmp(namespace, ndns_name) != 0) - continue; - if (xable_fn(ndns) == 0) - success++; - } - } - } - - rc = success; - ndctl_unref(ctx); - out: - namespace_bus = NULL; - namespace_region = NULL; - return rc; -} - -int cmd_disable_namespace(int argc, const char **argv) -{ - char *xable_usage = "ndctl disable-namespace <namespace> [<options>]"; - const char *namespace = parse_namespace_options(argc, argv, xable_usage); - int disabled = do_xable_namespace(namespace, ndctl_namespace_disable_invalidate); - - if (disabled < 0) { - fprintf(stderr, "error disabling namespaces: %s\n", - strerror(-disabled)); - return disabled; - } else if (disabled == 0) { - fprintf(stderr, "disabled 0 namespaces\n"); - return -ENXIO; - } else { - fprintf(stderr, "disabled %d namespace%s\n", disabled, - disabled > 1 ? "s" : ""); - return disabled; - } -} - -int cmd_enable_namespace(int argc, const char **argv) -{ - char *xable_usage = "ndctl enable-namespace <namespace> [<options>]"; - const char *namespace = parse_namespace_options(argc, argv, xable_usage); - int enabled = do_xable_namespace(namespace, ndctl_namespace_enable); - - if (enabled < 0) { - fprintf(stderr, "error enabling namespaces: %s\n", - strerror(-enabled)); - return enabled; - } else if (enabled == 0) { - fprintf(stderr, "enabled 0 namespaces\n"); - return -ENXIO; - } else { - fprintf(stderr, "enabled %d namespace%s\n", enabled, - enabled > 1 ? "s" : ""); - return enabled; - } -} diff --git a/builtin-xaction-namespace.c b/builtin-xaction-namespace.c new file mode 100644 index 000000000000..fdf20dfc01af --- /dev/null +++ b/builtin-xaction-namespace.c @@ -0,0 +1,693 @@ +/* + * Copyright(c) 2015 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> +#include <syslog.h> +#include <sys/stat.h> +#include <uuid/uuid.h> +#include <sys/types.h> +#include <util/size.h> +#include <ndctl/libndctl.h> +#include <util/parse-options.h> +#include <ccan/array_size/array_size.h> + +#ifdef HAVE_NDCTL_H +#include <linux/ndctl.h> +#else +#include <ndctl.h> +#endif + +static bool verbose; +static bool force; +static struct parameters { + const char *bus; + const char *map; + const char *type; + const char *uuid; + const char *name; + const char *size; + const char *mode; + const char *region; + const char *reconfig; + const char *sector_size; +} param; + +#define NSLABEL_NAME_LEN 64 +struct parsed_parameters { + enum ndctl_pfn_loc loc; + uuid_t uuid; + char name[NSLABEL_NAME_LEN]; + enum ndctl_namespace_mode mode; + unsigned long long size; + unsigned long sector_size; +}; + +#define debug(fmt, ...) \ + if (verbose) { \ + fprintf(stderr, "%s:%d: " fmt, __func__, __LINE__, ##__VA_ARGS__); \ + } else { \ + do { } while (0); \ + } + +#define BASE_OPTIONS() \ +OPT_STRING('b', "bus", ¶m.bus, "bus-id", \ + "limit namespace to a bus with an id or provider of <bus-id>"), \ +OPT_STRING('r', "region", ¶m.region, "region-id", \ + "limit namespace to a region with an id or name of <region-id>") + +#define CREATE_OPTIONS() \ +OPT_STRING('e', "reconfig", ¶m.reconfig, "reconfig namespace", \ + "reconfigure existing namespace"), \ +OPT_STRING('u', "uuid", ¶m.uuid, "uuid", \ + "specify the uuid for the namespace (default: autogenerate)"), \ +OPT_STRING('n', "name", ¶m.name, "name", \ + "specify an optional free form name for the namespace"), \ +OPT_STRING('s', "size", ¶m.size, "size", \ + "specify the namespace size in bytes (default: available capacity)"), \ +OPT_STRING('m', "mode", ¶m.mode, "operation-mode", \ + "specify a mode for the namespace, 'safe', 'memory', or 'raw'"), \ +OPT_STRING('M', "map", ¶m.map, "memmap-location", \ + "specify 'mem' or 'dev' for the location of the memmap"), \ +OPT_STRING('l', "sector-size", ¶m.sector_size, "lba-size", \ + "specify the logical sector size in bytes"), \ +OPT_STRING('t', "type", ¶m.type, "type", \ + "specify the type of namespace to create 'pmem' or 'blk'"), \ +OPT_BOOLEAN('f', "force", &force, "reconfigure namespace even if currently active"), \ +OPT_BOOLEAN('v', "verbose", &verbose, "emit extra debug messages to stderr") + +static const struct option base_options[] = { + BASE_OPTIONS(), + OPT_END(), +}; + +static const struct option create_options[] = { + BASE_OPTIONS(), + CREATE_OPTIONS(), + OPT_END(), +}; + +enum namespace_action { + ACTION_ENABLE, + ACTION_DISABLE, + ACTION_CREATE, +}; + +/* + * parse_namespace_options - basic parsing sanity checks before we start + * looking at actual namespace devices and available resources. + */ +static const char *parse_namespace_options(int argc, const char **argv, + enum namespace_action mode, const struct option *options, + char *xable_usage) +{ + const char * const u[] = { + xable_usage, + NULL + }; + int i, rc = 0; + + argc = parse_options(argc, argv, options, u, 0); + + if (param.type) { + if (strcmp(param.type, "pmem") == 0) + /* pass */; + else if (strcmp(param.type, "blk") == 0) + /* pass */; + else { + error("invalid type '%s', must be either 'pmem' or 'blk'\n", + param.type); + rc = -EINVAL; + } + } else if (!param.reconfig) + param.type = "pmem"; + + if (param.mode) { + if (strcmp(param.mode, "safe") == 0) + /* pass */; + else if (strcmp(param.mode, "sector") == 0) + param.mode = "safe"; /* pass */ + else if (strcmp(param.mode, "memory") == 0) + /* pass */; + else if (strcmp(param.mode, "raw") == 0) + /* pass */; + else { + error("invalid mode '%s'\n", param.mode); + rc = -EINVAL; + } + } else if (!param.reconfig) { + if (strcmp(param.type, "pmem") == 0) + param.mode = "memory"; + else + param.mode = "safe"; + } + + if (param.map) { + if (strcmp(param.map, "mem") == 0) + /* pass */; + else if (strcmp(param.map, "dev") == 0) + /* pass */; + else { + error("invalid map location '%s'\n", param.map); + rc = -EINVAL; + } + + if (!param.reconfig && strcmp(param.mode, "memory") != 0) { + error("--map only valid for a memory mode pmem namespace\n"); + rc = -EINVAL; + } + } else if (!param.reconfig) + param.map = "dev"; + + /* check for incompatible mode and type combinations */ + if (param.type && param.mode && strcmp(param.type, "blk") == 0 + && strcmp(param.mode, "memory") == 0) { + error("only 'pmem' namespaces can be placed into 'memory' mode\n"); + rc = -ENXIO; + } + + if (param.size && parse_size64(param.size) == ULLONG_MAX) { + error("failed to parse namespace size '%s'\n", + param.size); + rc = -EINVAL; + } + + if (param.uuid) { + uuid_t uuid; + + if (uuid_parse(param.uuid, uuid)) { + error("failed to parse uuid: '%s'\n", param.uuid); + rc = -EINVAL; + } + } + + if (param.sector_size) { + if (parse_size64(param.sector_size) == ULLONG_MAX) { + error("invalid sector size: %s\n", param.sector_size); + rc = -EINVAL; + } + + if (param.type && param.mode && strcmp(param.type, "pmem") == 0 + && strcmp(param.mode, "safe") != 0) { + error("'pmem' namespaces do not support setting 'sector size'\n"); + rc = -EINVAL; + } + } else if (!param.reconfig) + param.sector_size = "4096"; + + if (argc == 0 && mode != ACTION_CREATE) { + error("specify a namespace to %s, or \"all\"\n", + mode == ACTION_ENABLE ? "enable" : "disable"); + rc = -EINVAL; + } + for (i = mode == ACTION_CREATE ? 0 : 1; i < argc; i++) { + error("unknown extra parameter \"%s\"\n", argv[i]); + rc = -EINVAL; + } + + if (rc) { + usage_with_options(u, options); + return NULL; /* we won't return from usage_with_options() */ + } + + return mode == ACTION_CREATE ? param.reconfig : argv[0]; +} + +#define try(prefix, op, dev, p) \ +do { \ + int __rc = prefix##_##op(dev, p); \ + if (__rc) { \ + debug("%s: " #op " failed: %d\n", \ + prefix##_get_devname(dev), __rc); \ + return __rc; \ + } \ +} while (0) + +static int setup_namespace(struct ndctl_region *region, + struct ndctl_namespace *ndns, struct parsed_parameters *p) +{ + uuid_t uuid; + int rc; + + if (ndctl_namespace_get_type(ndns) != ND_DEVICE_NAMESPACE_IO) { + try(ndctl_namespace, set_uuid, ndns, p->uuid); + try(ndctl_namespace, set_alt_name, ndns, p->name); + try(ndctl_namespace, set_size, ndns, p->size); + } + + if (ndctl_namespace_get_type(ndns) == ND_DEVICE_NAMESPACE_BLK) + try(ndctl_namespace, set_sector_size, ndns, p->sector_size); + + uuid_generate(uuid); + if (ndctl_namespace_get_mode(ndns) != NDCTL_NS_MODE_MEMORY + && p->mode == NDCTL_NS_MODE_MEMORY) { + struct ndctl_pfn *pfn = ndctl_region_get_pfn_seed(region); + + try(ndctl_pfn, set_uuid, pfn, uuid); + try(ndctl_pfn, set_location, pfn, p->loc); + try(ndctl_pfn, set_align, pfn, SZ_2M); + try(ndctl_pfn, set_namespace, pfn, ndns); + rc = ndctl_pfn_enable(pfn); + } else if (p->mode == NDCTL_NS_MODE_SAFE) { + struct ndctl_btt *btt = ndctl_region_get_btt_seed(region); + + try(ndctl_btt, set_uuid, btt, uuid); + try(ndctl_btt, set_sector_size, btt, p->sector_size); + try(ndctl_btt, set_namespace, btt, ndns); + rc = ndctl_btt_enable(btt); + } else + rc = ndctl_namespace_enable(ndns); + + if (rc) { + error("%s: failed to enable\n", + ndctl_namespace_get_devname(ndns)); + } + return rc; +} + +static int is_namespace_active(struct ndctl_namespace *ndns) +{ + return ndns && (ndctl_namespace_is_enabled(ndns) + || ndctl_namespace_get_pfn(ndns) + || ndctl_namespace_get_btt(ndns)); +} + +/* + * validate_namespace_options - init parameters for setup_namespace + * @ndns: specified when we are reconfiguring, NULL otherwise + * @p: parameters to fill + * + * parse_namespace_options() will have already done basic verification + * of the parameters and set defaults in the !reconfigure case. When + * reconfiguring fill in any unset options with defaults from the + * namespace itself. + * + * Given that parse_namespace_options() runs before we have identified + * the target namespace we need to do basic sanity checks here for + * pmem-only attributes specified for blk namespace and vice versa. + */ +static int validate_namespace_options(struct ndctl_namespace *ndns, + struct parsed_parameters *p) +{ + int rc = 0; + + memset(p, 0, sizeof(*p)); + + if (param.size) + p->size = parse_size64(param.size); + else if (ndns) + p->size = ndctl_namespace_get_size(ndns); + + if (param.uuid) { + if (uuid_parse(param.uuid, p->uuid) != 0) { + debug("%s: invalid uuid\n", __func__); + return -EINVAL; + } + } else + uuid_generate(p->uuid); + + if (param.name) + rc = snprintf(p->name, sizeof(p->name), "%s", param.name); + else if (ndns) + rc = snprintf(p->name, sizeof(p->name), "%s", + ndctl_namespace_get_alt_name(ndns)); + if (rc >= (int) sizeof(p->name)) { + debug("%s: alt name overflow\n", __func__); + return -EINVAL; + } + + if (param.mode) { + if (strcmp(param.mode, "memory") == 0) + p->mode = NDCTL_NS_MODE_MEMORY; + else if (strcmp(param.mode, "safe") == 0) + p->mode = NDCTL_NS_MODE_SAFE; + else + p->mode = NDCTL_NS_MODE_RAW; + + if (ndns && ndctl_namespace_get_type(ndns) + == ND_DEVICE_NAMESPACE_BLK + && p->mode == NDCTL_NS_MODE_MEMORY) { + debug("%s: blk namespace do not support memory mode\n", + ndctl_namespace_get_devname(ndns)); + return -EINVAL; + } + } else if (ndns) + p->mode = ndctl_namespace_get_mode(ndns); + + if (param.sector_size) { + p->sector_size = parse_size64(param.sector_size); + + if (ndns && p->mode != NDCTL_NS_MODE_SAFE + && ndctl_namespace_get_type(ndns) + == ND_DEVICE_NAMESPACE_PMEM) { + debug("%s: does not support sector_size modification\n", + ndctl_namespace_get_devname(ndns)); + return -EINVAL; + } + } else if (ndns) { + struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns); + + if (btt) + p->sector_size = ndctl_btt_get_sector_size(btt); + else if (ndctl_namespace_get_type(ndns) + == ND_DEVICE_NAMESPACE_BLK) + p->sector_size = ndctl_namespace_get_sector_size(ndns); + } + + if (param.map) { + if (strcmp(param.map, "mem")) + p->loc = NDCTL_PFN_LOC_RAM; + else + p->loc = NDCTL_PFN_LOC_PMEM; + + if (ndns && p->mode != NDCTL_NS_MODE_MEMORY) { + debug("%s: --map= only valid for memory mode namespace\n", + ndctl_namespace_get_devname(ndns)); + return -EINVAL; + } + } else if (p->mode == NDCTL_NS_MODE_MEMORY) + p->loc = NDCTL_PFN_LOC_PMEM; + + return 0; +} + +static int namespace_create(struct ndctl_region *region) +{ + const char *devname = ndctl_region_get_devname(region); + unsigned long long available; + struct ndctl_namespace *ndns; + struct parsed_parameters p; + + if (validate_namespace_options(NULL, &p)) + return -EINVAL; + + if (ndctl_region_get_ro(region)) { + debug("%s: read-only, inelligible for namespace creation\n", + devname); + return -EAGAIN; + } + + available = ndctl_region_get_available_size(region); + if (!available || p.size > available) { + debug("%s: insufficient capacity size: %llx avail: %llx\n", + devname, p.size, available); + return -EAGAIN; + } + + if (p.size == 0) + p.size = available; + + ndns = ndctl_region_get_namespace_seed(region); + if (is_namespace_active(ndns)) { + debug("%s: no %s namespace seed\n", devname, + ndns ? "idle" : "available"); + return -ENODEV; + } + + return setup_namespace(region, ndns, &p); +} + +static int zero_info_block(struct ndctl_namespace *ndns) +{ + const char *devname = ndctl_namespace_get_devname(ndns); + int fd, rc = -ENXIO; + char path[50]; + void *buf; + + if (posix_memalign(&buf, 4096, 4096) != 0) + return -ENXIO; + + ndctl_namespace_set_raw_mode(ndns, 1); + ndctl_namespace_enable(ndns); + + sprintf(path, "/dev/%s", ndctl_namespace_get_block_device(ndns)); + fd = open(path, O_RDWR|O_DIRECT|O_EXCL); + if (fd < 0) { + debug("%s: failed to open %s to zero info block\n", + devname, path); + goto out; + } + + memset(buf, 0, 4096); + rc = pwrite(fd, buf, 4096, 4096); + if (rc < 4096) { + debug("%s: failed to zero info block %s\n", + devname, path); + rc = -ENXIO; + } else + rc = 0; + close(fd); + out: + ndctl_namespace_set_raw_mode(ndns, 0); + ndctl_namespace_disable_invalidate(ndns); + free(buf); + return rc; +} + +static int namespace_reconfig(struct ndctl_region *region, + struct ndctl_namespace *ndns) +{ + const char *devname = ndctl_namespace_get_devname(ndns); + struct ndctl_pfn *pfn = ndctl_namespace_get_pfn(ndns); + struct ndctl_btt *btt = ndctl_namespace_get_btt(ndns); + struct parsed_parameters p; + const char *bdev = NULL; + char path[50]; + int fd, rc; + + if (validate_namespace_options(ndns, &p)) + return -EINVAL; + + if (ndctl_region_get_ro(region)) { + error("%s: read-only, re-configuration disabled\n", + devname); + return -ENXIO; + } + + if (pfn && ndctl_pfn_is_enabled(pfn)) + bdev = ndctl_pfn_get_block_device(pfn); + else if (btt && ndctl_btt_is_enabled(btt)) + bdev = ndctl_btt_get_block_device(btt); + else if (ndctl_namespace_is_enabled(ndns)) + bdev = ndctl_namespace_get_block_device(ndns); + + if (bdev && !force) { + error("%s is active, specify --force for re-configuration\n", + devname); + return -EBUSY; + } else if (bdev) { + sprintf(path, "/dev/%s", bdev); + fd = open(path, O_RDWR|O_EXCL); + if (fd >= 0) { + /* + * Got it, now block new mounts while we have it + * pinned. + */ + ndctl_namespace_disable_invalidate(ndns); + close(fd); + } else { + /* + * Yes, TOCTOU hole, but if you're racing namespace + * creation you have other problems, and there's nothing + * stopping the !bdev case from racing to mount an fs or + * re-enabling the namepace. + */ + return -EBUSY; + } + } + + if (pfn || btt) { + rc = zero_info_block(ndns); + if (rc) + return rc; + } + + rc = ndctl_namespace_delete(ndns); + if (rc) { + error("%s: failed to reclaim\n", devname); + return rc; + } + + ndns = ndctl_region_get_namespace_seed(region); + if (is_namespace_active(ndns)) { + debug("%s: no %s namespace seed\n", devname, + ndns ? "idle" : "available"); + return -ENODEV; + } + + return setup_namespace(region, ndns, &p); +} + +static int do_xaction_namespace(const char *namespace, + enum namespace_action action) +{ + unsigned long bus_id = ULONG_MAX, region_id = ULONG_MAX, id; + const char *provider, *region_name, *ndns_name; + int rc = -ENXIO, success = 0; + struct ndctl_namespace *ndns; + struct ndctl_region *region; + struct ndctl_ctx *ctx; + struct ndctl_bus *bus; + + if (!namespace && action != ACTION_CREATE) + return rc; + + rc = ndctl_new(&ctx); + if (rc < 0) + return rc; + + if (verbose) + ndctl_set_log_priority(ctx, LOG_DEBUG); + + if (param.bus) { + char *end = NULL; + + bus_id = strtoul(param.bus, &end, 0); + if (end) + bus_id = ULONG_MAX; + } + + if (param.region) { + char *end = NULL; + + region_id = strtoul(param.region, &end, 0); + if (end) + region_id = ULONG_MAX; + } + + ndctl_bus_foreach(ctx, bus) { + provider = ndctl_bus_get_provider(bus); + id = ndctl_bus_get_id(bus); + + if (bus_id < ULONG_MAX && bus_id != id) + continue; + else if (bus_id == ULONG_MAX && param.bus + && strcmp(param.bus, provider) != 0) + continue; + + ndctl_region_foreach(bus, region) { + region_name = ndctl_region_get_devname(region); + id = ndctl_region_get_id(region); + + if (region_id < ULONG_MAX && region_id != id) + continue; + else if (region_id == ULONG_MAX && param.region + && strcmp(param.region, region_name) != 0) + continue; + + if (param.type) { + if (strcmp(param.type, "pmem") == 0 + && ndctl_region_get_type(region) + == ND_DEVICE_REGION_PMEM) + /* pass */; + else if (strcmp(param.type, "blk") == 0 + && ndctl_region_get_type(region) + == ND_DEVICE_REGION_BLK) + /* pass */; + else + continue; + } + + if (action == ACTION_CREATE && !namespace) { + rc = namespace_create(region); + if (rc == -EAGAIN) { + rc = 0; + continue; + } + goto done; + } + ndctl_namespace_foreach(region, ndns) { + ndns_name = ndctl_namespace_get_devname(ndns); + + if (strcmp(namespace, "all") != 0 + && strcmp(namespace, ndns_name) != 0) + continue; + switch (action) { + case ACTION_DISABLE: + rc = ndctl_namespace_disable_invalidate(ndns); + break; + case ACTION_ENABLE: + rc = ndctl_namespace_enable(ndns); + break; + case ACTION_CREATE: + return namespace_reconfig(region, ndns); + } + if (rc == 0) + success++; + } + } + } + + rc = success; + done: + ndctl_unref(ctx); + return rc; +} + +int cmd_disable_namespace(int argc, const char **argv) +{ + char *xable_usage = "ndctl disable-namespace <namespace> [<options>]"; + const char *namespace = parse_namespace_options(argc, argv, + ACTION_DISABLE, base_options, xable_usage); + int disabled = do_xaction_namespace(namespace, ACTION_DISABLE); + + if (disabled < 0) { + fprintf(stderr, "error disabling namespaces: %s\n", + strerror(-disabled)); + return disabled; + } else if (disabled == 0) { + fprintf(stderr, "disabled 0 namespaces\n"); + return -ENXIO; + } else { + fprintf(stderr, "disabled %d namespace%s\n", disabled, + disabled > 1 ? "s" : ""); + return disabled; + } +} + +int cmd_enable_namespace(int argc, const char **argv) +{ + char *xable_usage = "ndctl enable-namespace <namespace> [<options>]"; + const char *namespace = parse_namespace_options(argc, argv, + ACTION_ENABLE, base_options, xable_usage); + int enabled = do_xaction_namespace(namespace, ACTION_ENABLE); + + if (enabled < 0) { + fprintf(stderr, "error enabling namespaces: %s\n", + strerror(-enabled)); + return enabled; + } else if (enabled == 0) { + fprintf(stderr, "enabled 0 namespaces\n"); + return -ENXIO; + } else { + fprintf(stderr, "enabled %d namespace%s\n", enabled, + enabled > 1 ? "s" : ""); + return enabled; + } +} + +int cmd_create_namespace(int argc, const char **argv) +{ + char *xable_usage = "ndctl create-namespace [<options>]"; + const char *namespace = parse_namespace_options(argc, argv, + ACTION_CREATE, create_options, xable_usage); + int created = do_xaction_namespace(namespace, ACTION_CREATE); + + if (created < 0) + fprintf(stderr, "failed to %s namespace\n", namespace + ? "reconfigure" : "create"); + return created; +} diff --git a/builtin.h b/builtin.h index ed0b7e497a43..27ff248a6d84 100644 --- a/builtin.h +++ b/builtin.h @@ -10,6 +10,7 @@ struct cmd_struct { int cmd_create_nfit(int argc, const char **argv); int cmd_enable_namespace(int argc, const char **argv); +int cmd_create_namespace(int argc, const char **argv); int cmd_disable_namespace(int argc, const char **argv); int cmd_enable_region(int argc, const char **argv); int cmd_disable_region(int argc, const char **argv); @@ -21,6 +22,4 @@ int cmd_test(int argc, const char **argv); #ifdef ENABLE_DESTRUCTIVE int cmd_bat(int argc, const char **argv); #endif - #endif /* _NDCTL_BUILTIN_H_ */ - diff --git a/ndctl.c b/ndctl.c index f4e2746f85e3..0c117d7fbd2f 100644 --- a/ndctl.c +++ b/ndctl.c @@ -25,6 +25,7 @@ static struct cmd_struct commands[] = { { "version", cmd_version }, { "create-nfit", cmd_create_nfit }, { "enable-namespace", cmd_enable_namespace }, + { "create-namespace", cmd_create_namespace }, { "disable-namespace", cmd_disable_namespace }, { "enable-region", cmd_enable_region }, { "disable-region", cmd_disable_region }, diff --git a/util/size.c b/util/size.c new file mode 100644 index 000000000000..1ca2dcff5e28 --- /dev/null +++ b/util/size.c @@ -0,0 +1,42 @@ +#include <stdlib.h> +#include <limits.h> +#include <util/size.h> + +unsigned long long parse_size64(const char *str) +{ + unsigned long long val, check; + char *end; + + val = strtoull(str, &end, 0); + if (val == ULLONG_MAX) + return val; + check = val; + switch (*end) { + case 'k': + case 'K': + val *= SZ_1K; + end++; + break; + case 'm': + case 'M': + val *= SZ_1M; + end++; + break; + case 'g': + case 'G': + val *= SZ_1G; + end++; + break; + case 't': + case 'T': + val *= SZ_1T; + end++; + break; + default: + break; + } + + if (val < check || *end != '\0') + val = ULLONG_MAX; + return val; +} diff --git a/util/size.h b/util/size.h new file mode 100644 index 000000000000..64c146480ef0 --- /dev/null +++ b/util/size.h @@ -0,0 +1,12 @@ +#ifndef _NDCTL_SIZE_H_ +#define _NDCTL_SIZE_H_ + +#define SZ_1K 0x00000400 +#define SZ_1M 0x00100000 +#define SZ_2M 0x00200000 +#define SZ_1G 0x40000000 +#define SZ_1T 0x10000000000ULL + +unsigned long long parse_size64(const char *str); + +#endif /* _NDCTL_SIZE_H_ */
A helper utility for instantiating a namespace. With no arguments it defaults to a maximally sized pmem namespace in memory mode. The search for available capacity can be constrained by bus, region, and/or namespace type using the same mechanism to filter namespaces as 'ndctl enable-namespace' and 'ndctl disable-namespace'. See the include man page for more details. Cc: Jeff Moyer <jmoyer@redhat.com> [jmoyer: null dereference fix] Signed-off-by: Dan Williams <dan.j.williams@intel.com> --- Documentation/Makefile.am | 3 Documentation/ndctl-create-namespace.txt | 127 +++++ Documentation/ndctl.txt | 2 Makefile.am | 4 builtin-create-nfit.c | 45 -- builtin-xable-namespace.c | 153 ------- builtin-xaction-namespace.c | 693 ++++++++++++++++++++++++++++++ builtin.h | 3 ndctl.c | 1 util/size.c | 42 ++ util/size.h | 12 + 11 files changed, 882 insertions(+), 203 deletions(-) create mode 100644 Documentation/ndctl-create-namespace.txt delete mode 100644 builtin-xable-namespace.c create mode 100644 builtin-xaction-namespace.c create mode 100644 util/size.c create mode 100644 util/size.h