/
githubmirror
/
ndctl
Обзор
Документация
Войти
/
githubmirror
/
ndctl
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
daxctl/device.c
1 256 строк
29 KB
John Groves
daxctl: Add support for famfs mode
03 июн 2026, 00:17
03 июн 2026, 00:17
2738397
Код
Авторство
О чём код?
// SPDX-License-Identifier: GPL-2.0 /* Copyright (C) 2019-2020 Intel Corporation. All rights reserved. */ #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <syslog.h> #include <unistd.h> #include <limits.h> #include <sys/stat.h> #include <sys/types.h> #include <uuid/uuid.h> #include <sys/sysmacros.h> #include <util/size.h> #include <util/json.h> #include <json-c/json.h> #include <json-c/json_util.h> #include <ndctl/libndctl.h> #include <daxctl/libdaxctl.h> #include <util/parse-options.h> #include <util/parse-configs.h> #include <ccan/array_size/array_size.h> #include "filter.h" #include "json.h" static struct { const char *dev; const char *mode; const char *region; const char *size; const char *align; const char *input; bool check_config; bool no_online; bool no_movable; bool force; bool human; bool verbose; } param; enum reconfig_mode { RECONFIG_MODE_UNKNOWN, RECONFIG_MODE_DEVDAX, RECONFIG_MODE_RAM, RECONFIG_MODE_FAMFS, }; struct mapping { unsigned long long start, end, pgoff; }; static enum reconfig_mode reconfig_mode = RECONFIG_MODE_UNKNOWN; static long long align = -1; static long long size = -1; static unsigned long flags; static struct mapping *maps = NULL; static long long nmaps = -1; enum device_action { ACTION_RECONFIG, ACTION_ONLINE, ACTION_OFFLINE, ACTION_CREATE, ACTION_DISABLE, ACTION_ENABLE, ACTION_DESTROY, }; #define CONF_SECTION "reconfigure-device" #define CONF_NVDIMM_UUID_STR "nvdimm.uuid" #define BASE_OPTIONS() \ OPT_STRING('r', "region", ¶m.region, "region-id", "filter by region"), \ OPT_BOOLEAN('u', "human", ¶m.human, "use human friendly number formats"), \ OPT_BOOLEAN('v', "verbose", ¶m.verbose, "emit more debug messages") #define RECONFIG_OPTIONS() \ OPT_STRING('m', "mode", ¶m.mode, "mode", "mode to switch the device to"), \ OPT_BOOLEAN('N', "no-online", ¶m.no_online, \ "don't auto-online memory sections"), \ OPT_BOOLEAN('f', "force", ¶m.force, \ "attempt to offline memory sections before reconfiguration"), \ OPT_BOOLEAN('C', "check-config", ¶m.check_config, \ "use config files to determine parameters for the operation") #define CREATE_OPTIONS() \ OPT_STRING('s', "size", ¶m.size, "size", "size to switch the device to"), \ OPT_STRING('a', "align", ¶m.align, "align", "alignment to switch the device to"), \ OPT_STRING('\0', "input", ¶m.input, "input", "input device JSON file") #define DESTROY_OPTIONS() \ OPT_BOOLEAN('f', "force", ¶m.force, \ "attempt to disable before destroying device") #define ZONE_OPTIONS() \ OPT_BOOLEAN('\0', "no-movable", ¶m.no_movable, \ "online memory in ZONE_NORMAL") static const struct option create_options[] = { BASE_OPTIONS(), CREATE_OPTIONS(), OPT_END(), }; static const struct option reconfig_options[] = { BASE_OPTIONS(), CREATE_OPTIONS(), RECONFIG_OPTIONS(), ZONE_OPTIONS(), OPT_END(), }; static const struct option online_options[] = { BASE_OPTIONS(), ZONE_OPTIONS(), OPT_END(), }; static const struct option offline_options[] = { BASE_OPTIONS(), OPT_END(), }; static const struct option disable_options[] = { BASE_OPTIONS(), OPT_END(), }; static const struct option enable_options[] = { BASE_OPTIONS(), OPT_END(), }; static const struct option destroy_options[] = { BASE_OPTIONS(), DESTROY_OPTIONS(), OPT_END(), }; static int sort_mappings(const void *a, const void *b) { json_object **jsoa, **jsob; struct json_object *va, *vb; unsigned long long pga, pgb; jsoa = (json_object **)a; jsob = (json_object **)b; if (!*jsoa && !*jsob) return 0; if (!json_object_object_get_ex(*jsoa, "page_offset", &va) || !json_object_object_get_ex(*jsob, "page_offset", &vb)) return 0; pga = json_object_get_int64(va); pgb = json_object_get_int64(vb); return pga > pgb; } static int parse_device_file(const char *filename) { struct json_object *jobj, *jval = NULL, *jmappings = NULL; int i, rc = -EINVAL, region_id, id; const char *chardev; char *region = NULL; jobj = json_object_from_file(filename); if (!jobj) return rc; if (!json_object_object_get_ex(jobj, "align", &jval)) return rc; param.align = json_object_get_string(jval); if (!json_object_object_get_ex(jobj, "size", &jval)) return rc; param.size = json_object_get_string(jval); if (!json_object_object_get_ex(jobj, "chardev", &jval)) return rc; chardev = json_object_get_string(jval); if (sscanf(chardev, "dax%u.%u", ®ion_id, &id) != 2) return rc; if (asprintf(®ion, "%u", region_id) < 0) return rc; param.region = region; if (!json_object_object_get_ex(jobj, "mappings", &jmappings)) return rc; json_object_array_sort(jmappings, sort_mappings); nmaps = json_object_array_length(jmappings); maps = calloc(nmaps, sizeof(*maps)); if (!maps) return -ENOMEM; for (i = 0; i < nmaps; i++) { struct json_object *j, *val; j = json_object_array_get_idx(jmappings, i); if (!j) goto err; if (!json_object_object_get_ex(j, "start", &val)) goto err; maps[i].start = json_object_get_int64(val); if (!json_object_object_get_ex(j, "end", &val)) goto err; maps[i].end = json_object_get_int64(val); if (!json_object_object_get_ex(j, "page_offset", &val)) goto err; maps[i].pgoff = json_object_get_int64(val); } return 0; err: free(maps); return rc; } static int conf_string_to_bool(const char *str) { if (!str) return INT_MAX; if (strncmp(str, "t", 1) == 0 || strncmp(str, "T", 1) == 0 || strncmp(str, "y", 1) == 0 || strncmp(str, "Y", 1) == 0 || strncmp(str, "1", 1) == 0) return true; if (strncmp(str, "f", 1) == 0 || strncmp(str, "F", 1) == 0 || strncmp(str, "n", 1) == 0 || strncmp(str, "N", 1) == 0 || strncmp(str, "0", 1) == 0) return false; return INT_MAX; } #define conf_assign_inverted_bool(p, conf_var) \ do { \ if (conf_string_to_bool(conf_var) != INT_MAX) \ param.p = !conf_string_to_bool(conf_var); \ } while(0) static int parse_config_reconfig_set_params(struct daxctl_ctx *ctx, const char *device, const char *uuid) { const char *conf_online = NULL, *conf_movable = NULL; const struct config configs[] = { CONF_SEARCH(CONF_SECTION, CONF_NVDIMM_UUID_STR, uuid, "mode", ¶m.mode, NULL), CONF_SEARCH(CONF_SECTION, CONF_NVDIMM_UUID_STR, uuid, "online", &conf_online, NULL), CONF_SEARCH(CONF_SECTION, CONF_NVDIMM_UUID_STR, uuid, "movable", &conf_movable, NULL), CONF_END(), }; const char *prefix = "./", *daxctl_configs; int rc; daxctl_configs = daxctl_get_config_path(ctx); if (daxctl_configs == NULL) return 0; rc = parse_configs_prefix(daxctl_configs, prefix, configs); if (rc < 0) return rc; conf_assign_inverted_bool(no_online, conf_online); conf_assign_inverted_bool(no_movable, conf_movable); return 0; } static bool daxctl_ndns_has_device(struct ndctl_namespace *ndns, const char *device) { struct daxctl_region *dax_region; struct ndctl_dax *dax; dax = ndctl_namespace_get_dax(ndns); if (!dax) return false; dax_region = ndctl_dax_get_daxctl_region(dax); if (dax_region) { struct daxctl_dev *dev; dev = daxctl_dev_get_first(dax_region); if (dev) { if (strcmp(daxctl_dev_get_devname(dev), device) == 0) return true; } } return false; } static int parse_config_reconfig(struct daxctl_ctx *ctx, const char *device) { struct ndctl_namespace *ndns; struct ndctl_ctx *ndctl_ctx; struct ndctl_region *region; struct ndctl_bus *bus; struct ndctl_dax *dax; int rc, found = 0; char uuid_buf[40]; uuid_t uuid; if (strcmp(device, "all") == 0) return 0; rc = ndctl_new(&ndctl_ctx); if (rc < 0) return rc; ndctl_bus_foreach(ndctl_ctx, bus) { ndctl_region_foreach(bus, region) { ndctl_namespace_foreach(region, ndns) { if (daxctl_ndns_has_device(ndns, device)) { dax = ndctl_namespace_get_dax(ndns); if (!dax) continue; ndctl_dax_get_uuid(dax, uuid); found = 1; } } } } if (!found) { fprintf(stderr, "no UUID match for %s found in config files\n", device); return 0; } uuid_unparse(uuid, uuid_buf); return parse_config_reconfig_set_params(ctx, device, uuid_buf); } static int parse_device_config(struct daxctl_ctx *ctx, const char *device, enum device_action action) { switch (action) { case ACTION_RECONFIG: return parse_config_reconfig(ctx, device); default: return 0; } } static const char *parse_device_options(int argc, const char **argv, enum device_action action, const struct option *options, const char *usage, struct daxctl_ctx *ctx) { const char * const u[] = { usage, NULL }; unsigned long long units = 1; int i, rc = 0; const char *device = NULL; argc = parse_options(argc, argv, options, u, 0); if (argc > 0) device = path_basename(argv[0]); /* Handle action-agnostic non-option arguments */ if (argc == 0 && action != ACTION_CREATE) { char *action_string; switch (action) { case ACTION_RECONFIG: action_string = "reconfigure"; break; case ACTION_ONLINE: action_string = "online memory for"; break; case ACTION_OFFLINE: action_string = "offline memory for"; break; case ACTION_DISABLE: action_string = "disable"; break; case ACTION_ENABLE: action_string = "enable"; break; case ACTION_DESTROY: action_string = "destroy"; break; default: action_string = "<>"; break; } fprintf(stderr, "specify a device to %s, or \"all\"\n", action_string); rc = -EINVAL; } /* ACTION_CREATE expects 0 parameters */ i = action == ACTION_CREATE ? 0 : 1; for (; i < argc; i++) { fprintf(stderr, "unknown extra parameter \"%s\"\n", argv[i]); rc = -EINVAL; } if (rc) { usage_with_options(u, options); return NULL; } /* Handle action-agnostic options */ if (param.verbose) daxctl_set_log_priority(ctx, LOG_DEBUG); if (param.human) flags |= UTIL_JSON_HUMAN; /* Scan config file(s) for options. This sets param.foo accordingly */ if (device && param.check_config) { if (param.mode || param.no_online || param.no_movable) { fprintf(stderr, "%s: -C cannot be used with --mode, --(no-)movable, or --(no-)online\n", device); usage_with_options(u, options); } rc = parse_device_config(ctx, device, action); if (rc) { fprintf(stderr, "error parsing config file: %s\n", strerror(-rc)); return NULL; } if (!param.mode && !param.no_online && !param.no_movable) { fprintf(stderr, "%s: missing or malformed config section\n", device); /* * Exit with success since the most common case is there is * no config defined for this device, and we don't want to * treat that as an error. There isn't an easy way currently * to distinguish between a malformed config entry from a * completely missing config section. */ exit(0); } } /* Handle action-specific options */ switch (action) { case ACTION_RECONFIG: if (!param.size && !param.align && !param.mode) { fprintf(stderr, "error: a 'align', 'mode' or 'size' option is required\n"); usage_with_options(u, reconfig_options); rc = -EINVAL; } if (param.size || param.align) { if (param.size) size = __parse_size64(param.size, &units); if (param.align) align = __parse_size64(param.align, &units); } else if (strcmp(param.mode, "system-ram") == 0) { reconfig_mode = RECONFIG_MODE_RAM; } else if (strcmp(param.mode, "devdax") == 0) { reconfig_mode = RECONFIG_MODE_DEVDAX; if (param.no_online) { fprintf(stderr, "--no-online is incompatible with --mode=devdax\n"); rc = -EINVAL; } } else if (strcmp(param.mode, "famfs") == 0) { reconfig_mode = RECONFIG_MODE_FAMFS; if (param.no_online) { fprintf(stderr, "--no-online is incompatible with --mode=famfs\n"); rc = -EINVAL; } } break; case ACTION_CREATE: if (param.input && (rc = parse_device_file(param.input)) != 0) { fprintf(stderr, "error: failed to parse device file: %s\n", strerror(-rc)); break; } if (param.size) size = __parse_size64(param.size, &units); if (param.align) align = __parse_size64(param.align, &units); /* fall through */ case ACTION_ONLINE: case ACTION_DESTROY: case ACTION_OFFLINE: case ACTION_DISABLE: case ACTION_ENABLE: /* nothing special */ break; } if (rc) { usage_with_options(u, options); return NULL; } return device; } static int dev_online_memory(struct daxctl_dev *dev) { struct daxctl_memory *mem = daxctl_dev_get_memory(dev); const char *devname = daxctl_dev_get_devname(dev); int num_sections, num_on, rc; if (!mem) { fprintf(stderr, "%s: failed to get the memory object\n", devname); return -ENXIO; } /* get total number of sections and sections already online */ num_sections = daxctl_memory_num_sections(mem); if (num_sections < 0) { fprintf(stderr, "%s: failed to get number of memory sections\n", devname); return num_sections; } num_on = daxctl_memory_is_online(mem); if (num_on < 0) { fprintf(stderr, "%s: failed to determine online state: %s\n", devname, strerror(-num_on)); return num_on; } if (num_on) fprintf(stderr, "%s:\n WARNING: detected a race while onlining memory\n" " Some memory may not be in the expected zone. It is\n" " recommended to disable any other onlining mechanisms,\n" " and retry. If onlining is to be left to other agents,\n" " use the --no-online option to suppress this warning\n", devname); if (num_on == num_sections) { fprintf(stderr, "%s: all memory sections (%d) already online\n", devname, num_on); return 1; } if (num_on > 0) fprintf(stderr, "%s: %d memory section%s already online\n", devname, num_on, num_on == 1 ? "" : "s"); /* online the remaining sections */ if (param.no_movable) rc = daxctl_memory_online_no_movable(mem); else rc = daxctl_memory_online(mem); if (rc < 0) { fprintf(stderr, "%s: failed to online memory: %s\n", devname, strerror(-rc)); return rc; } if (param.verbose) fprintf(stderr, "%s: %d memory section%s onlined\n", devname, rc, rc == 1 ? "" : "s"); /* all sections should now be online */ num_on = daxctl_memory_is_online(mem); if (num_on < 0) { fprintf(stderr, "%s: failed to determine online state: %s\n", devname, strerror(-num_on)); return num_on; } if (num_on < num_sections) { fprintf(stderr, "%s: failed to online %d memory sections\n", devname, num_sections - num_on); return -ENXIO; } return 0; } static int dev_offline_memory(struct daxctl_dev *dev) { struct daxctl_memory *mem = daxctl_dev_get_memory(dev); const char *devname = daxctl_dev_get_devname(dev); int num_sections, num_on, num_off, rc; if (!mem) { fprintf(stderr, "%s: failed to get the memory object\n", devname); return -ENXIO; } /* get total number of sections and sections already offline */ num_sections = daxctl_memory_num_sections(mem); if (num_sections < 0) { fprintf(stderr, "%s: failed to get number of memory sections\n", devname); return num_sections; } num_on = daxctl_memory_is_online(mem); if (num_on < 0) { fprintf(stderr, "%s: failed to determine online state: %s\n", devname, strerror(-num_on)); return num_on; } num_off = num_sections - num_on; if (num_off == num_sections) { fprintf(stderr, "%s: all memory sections (%d) already offline\n", devname, num_off); return 1; } if (num_off) fprintf(stderr, "%s: %d memory section%s already offline\n", devname, num_off, num_off == 1 ? "" : "s"); /* offline the remaining sections */ rc = daxctl_memory_offline(mem); if (rc < 0) { fprintf(stderr, "%s: failed to offline memory: %s\n", devname, strerror(-rc)); return rc; } if (param.verbose) fprintf(stderr, "%s: %d memory section%s offlined\n", devname, rc, rc == 1 ? "" : "s"); /* all sections should now be ofline */ num_on = daxctl_memory_is_online(mem); if (num_on < 0) { fprintf(stderr, "%s: failed to determine online state: %s\n", devname, strerror(-num_on)); return num_on; } if (num_on) { fprintf(stderr, "%s: failed to offline %d memory sections\n", devname, num_on); return -ENXIO; } return 0; } static int dev_resize(struct daxctl_dev *dev, unsigned long long val) { int rc; rc = daxctl_dev_set_size(dev, val); if (rc < 0) return rc; return 0; } static int dev_destroy(struct daxctl_dev *dev) { const char *devname = daxctl_dev_get_devname(dev); int rc; if (daxctl_dev_is_enabled(dev) && !param.force) { fprintf(stderr, "%s is active, specify --force for deletion\n", devname); return -ENXIO; } else { rc = daxctl_dev_disable(dev); if (rc) { fprintf(stderr, "%s: disable failed: %s\n", daxctl_dev_get_devname(dev), strerror(-rc)); return rc; } } rc = daxctl_dev_set_size(dev, 0); if (rc < 0) return rc; rc = daxctl_region_destroy_dev(daxctl_dev_get_region(dev), dev); /* * The kernel treats daxX.0 specially. It can't be deleted to ensure * there is always a /sys/bus/dax/ present. If this happens, an * EBUSY is returned. Expect it and don't treat it as an error. */ if (daxctl_dev_get_id(dev) == 0 && rc == -EBUSY) return 0; if (rc < 0) return rc; return 0; } static int disable_mode_device(struct daxctl_dev *dev) { int rc; rc = daxctl_dev_disable(dev); if (rc) { fprintf(stderr, "%s: disable failed: %s\n", daxctl_dev_get_devname(dev), strerror(-rc)); return rc; } return 0; } static int reconfig_mode_system_ram(struct daxctl_dev *dev) { const char *devname = daxctl_dev_get_devname(dev); int rc, skip_enable = 0; if (param.no_online || !param.no_movable) { if (!param.force && daxctl_dev_will_auto_online_memory(dev)) { fprintf(stderr, "%s: error: kernel policy will auto-online memory, aborting\n", devname); return -EBUSY; } } if (daxctl_dev_is_enabled(dev)) { switch (daxctl_dev_get_mode(dev)) { case DAXCTL_DEV_MODE_RAM: /* already in system-ram mode */ skip_enable = 1; break; case DAXCTL_DEV_MODE_FAMFS: case DAXCTL_DEV_MODE_DEVDAX: rc = disable_mode_device(dev); if (rc) return rc; break; default: fprintf(stderr, "%s: unknown mode\n", devname); return -EINVAL; } } if (!skip_enable) { rc = daxctl_dev_enable_ram(dev); if (rc) return rc; } if (param.no_online) return 0; return dev_online_memory(dev); } static int disable_system_ram_device(struct daxctl_dev *dev) { struct daxctl_memory *mem = daxctl_dev_get_memory(dev); const char *devname = daxctl_dev_get_devname(dev); int rc; if (!mem) { fprintf(stderr, "%s is not in system-ram mode\n", devname); return 1; } if (param.force) { rc = dev_offline_memory(dev); if (rc < 0) return rc; } rc = daxctl_memory_is_online(mem); if (rc < 0) { fprintf(stderr, "%s: failed to determine online state: %s\n", devname, strerror(-rc)); return rc; } if (rc > 0) { if (param.verbose) { fprintf(stderr, "%s: found %d memory sections online\n", devname, rc); fprintf(stderr, "%s: refusing to change modes\n", devname); } return -EBUSY; } rc = daxctl_dev_disable(dev); if (rc) { fprintf(stderr, "%s: disable failed: %s\n", daxctl_dev_get_devname(dev), strerror(-rc)); return rc; } return 0; } static int reconfig_mode_devdax(struct daxctl_dev *dev) { const char *devname = daxctl_dev_get_devname(dev); int rc; if (daxctl_dev_is_enabled(dev)) { switch (daxctl_dev_get_mode(dev)) { case DAXCTL_DEV_MODE_RAM: rc = disable_system_ram_device(dev); if (rc) return rc; break; case DAXCTL_DEV_MODE_FAMFS: case DAXCTL_DEV_MODE_DEVDAX: rc = disable_mode_device(dev); if (rc) return rc; break; default: fprintf(stderr, "%s: unknown mode\n", devname); return -EINVAL; } } rc = daxctl_dev_enable_devdax(dev); if (rc) return rc; return 0; } static int reconfig_mode_famfs(struct daxctl_dev *dev) { const char *devname = daxctl_dev_get_devname(dev); int rc; if (daxctl_dev_is_enabled(dev)) { switch (daxctl_dev_get_mode(dev)) { case DAXCTL_DEV_MODE_RAM: fprintf(stderr, "%s is in system-ram mode; must be in devdax mode to convert to famfs\n", devname); return -EINVAL; case DAXCTL_DEV_MODE_FAMFS: case DAXCTL_DEV_MODE_DEVDAX: rc = disable_mode_device(dev); if (rc) return rc; break; default: fprintf(stderr, "%s: unknown mode\n", devname); return -EINVAL; } } rc = daxctl_dev_enable_famfs(dev); if (rc) return rc; return 0; } static int do_create(struct daxctl_region *region, long long val, struct json_object **jdevs) { struct json_object *jdev; struct daxctl_dev *dev; int i, rc = 0; long long alloc = 0; if (daxctl_region_create_dev(region)) return -ENOSPC; dev = daxctl_region_get_dev_seed(region); if (!dev) return -ENOSPC; if (val == -1) val = daxctl_region_get_available_size(region); if (val <= 0) return -ENOSPC; if (align > 0) { rc = daxctl_dev_set_align(dev, align); if (rc < 0) return rc; } /* @maps is ordered by page_offset */ for (i = 0; i < nmaps; i++) { rc = daxctl_dev_set_mapping(dev, maps[i].start, maps[i].end); if (rc < 0) return rc; alloc += (maps[i].end - maps[i].start + 1); } if (nmaps > 0 && val > 0 && alloc != val) { fprintf(stderr, "%s: allocated %lld but specified size %lld\n", daxctl_dev_get_devname(dev), alloc, val); } else { rc = daxctl_dev_set_size(dev, val); if (rc < 0) return rc; } rc = daxctl_dev_enable_devdax(dev); if (rc) { fprintf(stderr, "%s: enable failed: %s\n", daxctl_dev_get_devname(dev), strerror(-rc)); return rc; } *jdevs = json_object_new_array(); if (*jdevs) { jdev = util_daxctl_dev_to_json(dev, flags); if (jdev) json_object_array_add(*jdevs, jdev); } return 0; } static int do_reconfig(struct daxctl_dev *dev, enum reconfig_mode mode, struct json_object **jdevs) { const char *devname = daxctl_dev_get_devname(dev); struct json_object *jdev; int rc = 0; if (align > 0) { rc = daxctl_dev_set_align(dev, align); if (rc < 0) return rc; } if (size >= 0) { rc = dev_resize(dev, size); return rc; } switch (mode) { case RECONFIG_MODE_RAM: rc = reconfig_mode_system_ram(dev); break; case RECONFIG_MODE_DEVDAX: rc = reconfig_mode_devdax(dev); break; case RECONFIG_MODE_FAMFS: rc = reconfig_mode_famfs(dev); break; default: fprintf(stderr, "%s: unknown mode requested: %d\n", devname, mode); rc = -EINVAL; } if (rc < 0) return rc; *jdevs = json_object_new_array(); if (*jdevs) { jdev = util_daxctl_dev_to_json(dev, flags); if (jdev) json_object_array_add(*jdevs, jdev); } return 0; } static int do_xline(struct daxctl_dev *dev, enum device_action action) { struct daxctl_memory *mem = daxctl_dev_get_memory(dev); const char *devname = daxctl_dev_get_devname(dev); int rc; if (!mem) { fprintf(stderr, "%s: memory operations are not applicable in devdax mode\n", devname); return -ENXIO; } switch (action) { case ACTION_ONLINE: rc = dev_online_memory(dev); break; case ACTION_OFFLINE: rc = dev_offline_memory(dev); break; default: fprintf(stderr, "%s: invalid action: %d\n", devname, action); rc = -EINVAL; } return rc; } static int do_xble(struct daxctl_dev *dev, enum device_action action) { struct daxctl_memory *mem = daxctl_dev_get_memory(dev); const char *devname = daxctl_dev_get_devname(dev); int rc; if (mem) { fprintf(stderr, "%s: status operations are only applicable in devdax mode\n", devname); return -ENXIO; } switch (action) { case ACTION_ENABLE: rc = daxctl_dev_enable_devdax(dev); if (rc) { fprintf(stderr, "%s: enable failed: %s\n", daxctl_dev_get_devname(dev), strerror(-rc)); return rc; } break; case ACTION_DISABLE: rc = daxctl_dev_disable(dev); if (rc) { fprintf(stderr, "%s: disable failed: %s\n", daxctl_dev_get_devname(dev), strerror(-rc)); return rc; } break; default: fprintf(stderr, "%s: invalid action: %d\n", devname, action); rc = -EINVAL; } return rc; } static int do_xaction_region(enum device_action action, struct daxctl_ctx *ctx, int *processed) { struct json_object *jdevs = NULL; struct daxctl_region *region; int rc = -ENXIO; *processed = 0; daxctl_region_foreach(ctx, region) { if (!util_daxctl_region_filter(region, param.region)) continue; switch (action) { case ACTION_CREATE: rc = do_create(region, size, &jdevs); if (rc == 0) (*processed)++; break; default: rc = -EINVAL; break; } } free(maps); /* * jdevs is the containing json array for all devices we are reporting * on. It therefore needs to be outside the region/device iterators, * and passed in to the do_<action> functions to add their objects to */ if (jdevs) util_display_json_array(stdout, jdevs, flags); return rc; } static int do_xaction_device(const char *device, enum device_action action, struct daxctl_ctx *ctx, int *processed) { struct json_object *jdevs = NULL; struct daxctl_region *region; struct daxctl_dev *dev; int rc = -ENXIO, saved_rc = 0; *processed = 0; daxctl_region_foreach(ctx, region) { if (!util_daxctl_region_filter(region, param.region)) continue; daxctl_dev_foreach(region, dev) { if (!util_daxctl_dev_filter(dev, device)) continue; switch (action) { case ACTION_RECONFIG: rc = do_reconfig(dev, reconfig_mode, &jdevs); if (rc == 0) (*processed)++; break; case ACTION_ONLINE: rc = do_xline(dev, action); if (rc == 0) (*processed)++; break; case ACTION_OFFLINE: rc = do_xline(dev, action); if (rc == 0) (*processed)++; break; case ACTION_ENABLE: rc = do_xble(dev, action); if (rc == 0) (*processed)++; break; case ACTION_DISABLE: rc = do_xble(dev, action); if (rc == 0) (*processed)++; break; case ACTION_DESTROY: rc = dev_destroy(dev); if (rc == 0) (*processed)++; break; default: rc = -EINVAL; break; } if (rc) saved_rc = rc; } } /* * jdevs is the containing json array for all devices we are reporting * on. It therefore needs to be outside the region/device iterators, * and passed in to the do_<action> functions to add their objects to */ if (jdevs) util_display_json_array(stdout, jdevs, flags); return saved_rc; } int cmd_create_device(int argc, const char **argv, struct daxctl_ctx *ctx) { char *usage = "daxctl create-device [<options>]"; int processed, rc; parse_device_options(argc, argv, ACTION_CREATE, create_options, usage, ctx); rc = do_xaction_region(ACTION_CREATE, ctx, &processed); if (rc < 0) fprintf(stderr, "error creating devices: %s\n", strerror(-rc)); fprintf(stderr, "created %d device%s\n", processed, processed == 1 ? "" : "s"); return rc; } int cmd_destroy_device(int argc, const char **argv, struct daxctl_ctx *ctx) { char *usage = "daxctl destroy-device <device> [<options>]"; const char *device = parse_device_options(argc, argv, ACTION_DESTROY, destroy_options, usage, ctx); int processed, rc; rc = do_xaction_device(device, ACTION_DESTROY, ctx, &processed); if (rc < 0) fprintf(stderr, "error destroying devices: %s\n", strerror(-rc)); fprintf(stderr, "destroyed %d device%s\n", processed, processed == 1 ? "" : "s"); return rc; } int cmd_reconfig_device(int argc, const char **argv, struct daxctl_ctx *ctx) { char *usage = "daxctl reconfigure-device <device> [<options>]"; const char *device = parse_device_options(argc, argv, ACTION_RECONFIG, reconfig_options, usage, ctx); int processed, rc; rc = do_xaction_device(device, ACTION_RECONFIG, ctx, &processed); if (rc < 0) fprintf(stderr, "error reconfiguring devices: %s\n", strerror(-rc)); fprintf(stderr, "reconfigured %d device%s\n", processed, processed == 1 ? "" : "s"); return rc; } int cmd_disable_device(int argc, const char **argv, struct daxctl_ctx *ctx) { char *usage = "daxctl disable-device <device>"; const char *device = parse_device_options(argc, argv, ACTION_DISABLE, disable_options, usage, ctx); int processed, rc; rc = do_xaction_device(device, ACTION_DISABLE, ctx, &processed); if (rc < 0) fprintf(stderr, "error disabling device: %s\n", strerror(-rc)); fprintf(stderr, "disabled %d device%s\n", processed, processed == 1 ? "" : "s"); return rc; } int cmd_enable_device(int argc, const char **argv, struct daxctl_ctx *ctx) { char *usage = "daxctl enable-device <device>"; const char *device = parse_device_options(argc, argv, ACTION_DISABLE, enable_options, usage, ctx); int processed, rc; rc = do_xaction_device(device, ACTION_ENABLE, ctx, &processed); if (rc < 0) fprintf(stderr, "error enabling device: %s\n", strerror(-rc)); fprintf(stderr, "enabled %d device%s\n", processed, processed == 1 ? "" : "s"); return rc; } int cmd_online_memory(int argc, const char **argv, struct daxctl_ctx *ctx) { char *usage = "daxctl online-memory <device> [<options>]"; const char *device = parse_device_options(argc, argv, ACTION_ONLINE, online_options, usage, ctx); int processed, rc; rc = do_xaction_device(device, ACTION_ONLINE, ctx, &processed); if (rc < 0) fprintf(stderr, "error onlining memory: %s\n", strerror(-rc)); fprintf(stderr, "onlined memory for %d device%s\n", processed, processed == 1 ? "" : "s"); return rc; } int cmd_offline_memory(int argc, const char **argv, struct daxctl_ctx *ctx) { char *usage = "daxctl offline-memory <device> [<options>]"; const char *device = parse_device_options(argc, argv, ACTION_OFFLINE, offline_options, usage, ctx); int processed, rc; rc = do_xaction_device(device, ACTION_OFFLINE, ctx, &processed); if (rc < 0) fprintf(stderr, "error offlining memory: %s\n", strerror(-rc)); fprintf(stderr, "offlined memory for %d device%s\n", processed, processed == 1 ? "" : "s"); return rc; }