From patchwork Thu Jan 28 22:52:29 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dan Williams X-Patchwork-Id: 8154851 Return-Path: X-Original-To: patchwork-linux-nvdimm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 61914BEEE5 for ; Thu, 28 Jan 2016 22:53:10 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 18A6820256 for ; Thu, 28 Jan 2016 22:53:08 +0000 (UTC) Received: from ml01.01.org (ml01.01.org [198.145.21.10]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 99581202FE for ; Thu, 28 Jan 2016 22:53:05 +0000 (UTC) Received: from ml01.vlan14.01.org (localhost [IPv6:::1]) by ml01.01.org (Postfix) with ESMTP id 8E2A51A231C; Thu, 28 Jan 2016 14:53:05 -0800 (PST) X-Original-To: linux-nvdimm@lists.01.org Delivered-To: linux-nvdimm@lists.01.org Received: from mga04.intel.com (mga04.intel.com [192.55.52.120]) by ml01.01.org (Postfix) with ESMTP id 2656C1A2453 for ; Thu, 28 Jan 2016 14:53:04 -0800 (PST) Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by fmsmga104.fm.intel.com with ESMTP; 28 Jan 2016 14:52:54 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.22,360,1449561600"; d="scan'208";a="891462376" Received: from dwillia2-desk3.jf.intel.com (HELO dwillia2-desk3.amr.corp.intel.com) ([10.54.39.136]) by fmsmga001.fm.intel.com with ESMTP; 28 Jan 2016 14:52:54 -0800 Subject: [ndctl PATCH 06/13] ndctl: create namespace From: Dan Williams To: linux-nvdimm@lists.01.org Date: Thu, 28 Jan 2016 14:52:29 -0800 Message-ID: <20160128225229.17855.88143.stgit@dwillia2-desk3.amr.corp.intel.com> In-Reply-To: <20160128225157.17855.5190.stgit@dwillia2-desk3.amr.corp.intel.com> References: <20160128225157.17855.5190.stgit@dwillia2-desk3.amr.corp.intel.com> User-Agent: StGit/0.17.1-9-g687f MIME-Version: 1.0 X-BeenThere: linux-nvdimm@lists.01.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: "Linux-nvdimm developer list." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: linux-nvdimm-bounces@lists.01.org Sender: "Linux-nvdimm" X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP 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: null dereference fix] Signed-off-by: Dan Williams --- 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' [] + +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 #include #include +#include #include @@ -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 -#include -#include -#include -#include -#include -#include - -static const char *namespace_bus; -static const char *namespace_region; - -static const struct option namespace_options[] = { - OPT_STRING('b', "bus", &namespace_bus, "bus-id", - " must be on a bus with an id/provider of "), - OPT_STRING('r', "region", &namespace_region, "region-id", - " must be a child of a region with an id/name of "), - 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 []"; - 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 []"; - 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_NDCTL_H +#include +#else +#include +#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 "), \ +OPT_STRING('r', "region", ¶m.region, "region-id", \ + "limit namespace to a region with an id or name of ") + +#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 []"; + 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 []"; + 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 []"; + 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 +#include +#include + +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_ */