In preparation for converting DAX devices for use as system-ram via the kernel's 'kmem' facility, add libndctl helpers to manipulate the sysfs interfaces to get the target_node of a DAX device, and to online, offline, and query the state of hotplugged memory sections associated with a given node.
This adds the following new interfaces: daxctl_dev_get_target_node daxctl_dev_online_node daxctl_dev_offline_node daxctl_dev_node_is_online Cc: Pavel Tatashin <pasha.tatas...@soleen.com> Cc: Dave Hansen <dave.han...@linux.intel.com> Cc: Dan Williams <dan.j.willi...@intel.com> Signed-off-by: Vishal Verma <vishal.l.ve...@intel.com> --- daxctl/lib/libdaxctl-private.h | 6 + daxctl/lib/libdaxctl.c | 228 +++++++++++++++++++++++++++++++++ daxctl/lib/libdaxctl.sym | 4 + daxctl/libdaxctl.h | 4 + 4 files changed, 242 insertions(+) diff --git a/daxctl/lib/libdaxctl-private.h b/daxctl/lib/libdaxctl-private.h index e64d0a7..ef443aa 100644 --- a/daxctl/lib/libdaxctl-private.h +++ b/daxctl/lib/libdaxctl-private.h @@ -33,6 +33,11 @@ static const char *dax_modules[] = { [DAXCTL_DEV_MODE_RAM] = "kmem", }; +enum node_state { + NODE_OFFLINE, + NODE_ONLINE, +}; + /** * struct daxctl_region - container for dax_devices */ @@ -63,6 +68,7 @@ struct daxctl_dev { struct kmod_module *module; struct kmod_list *kmod_list; struct daxctl_region *region; + int target_node; }; static inline int check_kmod(struct kmod_ctx *kmod_ctx) diff --git a/daxctl/lib/libdaxctl.c b/daxctl/lib/libdaxctl.c index d50b321..aab2364 100644 --- a/daxctl/lib/libdaxctl.c +++ b/daxctl/lib/libdaxctl.c @@ -28,6 +28,7 @@ #include "libdaxctl-private.h" static const char *attrs = "dax_region"; +static const char *node_base = "/sys/devices/system/node"; static void free_region(struct daxctl_region *region, struct list_head *head); @@ -397,6 +398,12 @@ static void *add_dax_dev(void *parent, int id, const char *daxdev_base) } else dbg(ctx, "%s: modalias attribute missing\n", devname); + sprintf(path, "%s/target_node", daxdev_base); + if (sysfs_read_attr(ctx, path, buf) == 0) + dev->target_node = strtol(buf, NULL, 0); + else + dev->target_node = -1; + daxctl_dev_foreach(region, dev_dup) if (dev_dup->id == dev->id) { free_dev(dev, NULL); @@ -897,3 +904,224 @@ DAXCTL_EXPORT unsigned long long daxctl_dev_get_size(struct daxctl_dev *dev) { return dev->size; } + +DAXCTL_EXPORT int daxctl_dev_get_target_node(struct daxctl_dev *dev) +{ + return dev->target_node; +} + +static int online_one_memblock(struct daxctl_dev *dev, char *path) +{ + const char *devname = daxctl_dev_get_devname(dev); + struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); + const char *mode = "online_movable"; + char buf[SYSFS_ATTR_SIZE]; + int rc; + + rc = sysfs_read_attr(ctx, path, buf); + if (rc) { + err(ctx, "%s: Failed to read %s: %s\n", + devname, path, strerror(-rc)); + return rc; + } + + /* + * if already online, possibly due to kernel config or a udev rule, + * there is nothing to do and we can skip over the memblock + */ + if (strncmp(buf, "online", 6) == 0) + return 0; + + rc = sysfs_write_attr_quiet(ctx, path, mode); + if (rc) + err(ctx, "%s: Failed to online %s: %s\n", + devname, path, strerror(-rc)); + return rc; +} + +static int offline_one_memblock(struct daxctl_dev *dev, char *path) +{ + const char *devname = daxctl_dev_get_devname(dev); + struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); + const char *mode = "offline"; + char buf[SYSFS_ATTR_SIZE]; + int rc; + + rc = sysfs_read_attr(ctx, path, buf); + if (rc) { + err(ctx, "%s: Failed to read %s: %s\n", + devname, path, strerror(-rc)); + return rc; + } + + /* if already offline, there is nothing to do */ + if (strncmp(buf, "offline", 6) == 0) + return 0; + + rc = sysfs_write_attr_quiet(ctx, path, mode); + if (rc) + err(ctx, "%s: Failed to offline %s: %s\n", + devname, path, strerror(-rc)); + return rc; +} + +static int daxctl_dev_node_set_state(struct daxctl_dev *dev, + enum node_state state) +{ + struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); + int target_node, rc, changed = 0; + struct dirent *de; + char *node_path; + DIR *node_dir; + + target_node = daxctl_dev_get_target_node(dev); + if (target_node < 0) { + err(ctx, "%s: Unable to get target node\n", + daxctl_dev_get_devname(dev)); + return -ENXIO; + } + + rc = asprintf(&node_path, "%s/node%d", node_base, target_node); + if (rc < 0) + return -ENOMEM; + + node_dir = opendir(node_path); + if (!node_dir) { + rc = -errno; + goto out_path; + } + + errno = 0; + while ((de = readdir(node_dir)) != NULL) { + char *mem_path; + + if (strcmp(de->d_name, ".") == 0 || + strcmp(de->d_name, "..") == 0) + continue; + if (strncmp(de->d_name, "memory", 6) == 0) { + rc = asprintf(&mem_path, "%s/%s/state", + node_path, de->d_name); + if (rc < 0) { + rc = -ENOMEM; + goto out_dir; + } + if (state == NODE_ONLINE) + rc = online_one_memblock(dev, mem_path); + else if (state == NODE_OFFLINE) + rc = offline_one_memblock(dev, mem_path); + else + rc = -EINVAL; + free(mem_path); + if (rc) + goto out_dir; + changed++; + } + errno = 0; + } + if (errno) { + rc = -errno; + goto out_dir; + } + rc = changed; + +out_dir: + closedir(node_dir); +out_path: + free(node_path); + return rc; +} + +DAXCTL_EXPORT int daxctl_dev_online_node(struct daxctl_dev *dev) +{ + return daxctl_dev_node_set_state(dev, NODE_ONLINE); +} + +DAXCTL_EXPORT int daxctl_dev_offline_node(struct daxctl_dev *dev) +{ + return daxctl_dev_node_set_state(dev, NODE_OFFLINE); +} + +static int memblock_is_online(struct daxctl_dev *dev, char *path) +{ + const char *devname = daxctl_dev_get_devname(dev); + struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); + char buf[SYSFS_ATTR_SIZE]; + int rc; + + rc = sysfs_read_attr(ctx, path, buf); + if (rc) { + err(ctx, "%s: Failed to read %s: %s\n", + devname, path, strerror(-rc)); + return rc; + } + + if (strncmp(buf, "online", 6) == 0) + return 1; + + return 0; +} + +DAXCTL_EXPORT int daxctl_dev_node_is_online(struct daxctl_dev *dev) +{ + const char *devname = daxctl_dev_get_devname(dev); + struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); + int target_node, rc, num_online = 0; + struct dirent *de; + char *node_path; + DIR *node_dir; + + target_node = daxctl_dev_get_target_node(dev); + if (target_node < 0) { + err(ctx, "%s: Unable to get target node\n", devname); + return -ENXIO; + } + + rc = asprintf(&node_path, "%s/node%d", node_base, target_node); + if (rc < 0) + return -ENOMEM; + + node_dir = opendir(node_path); + if (!node_dir) { + rc = -errno; + goto out_path; + } + + errno = 0; + while ((de = readdir(node_dir)) != NULL) { + char *mem_path; + + if (strcmp(de->d_name, ".") == 0 || + strcmp(de->d_name, "..") == 0) + continue; + if (strncmp(de->d_name, "memory", 6) == 0) { + rc = asprintf(&mem_path, "%s/%s/state", + node_path, de->d_name); + if (rc < 0) { + rc = -ENOMEM; + goto out_dir; + } + rc = memblock_is_online(dev, mem_path); + if (rc < 0) { + err(ctx, "%s: Unable to determine state: %s\n", + devname, mem_path); + goto out_dir; + } + if (rc > 0) + num_online++; + free(mem_path); + } + errno = 0; + } + if (errno) { + rc = -errno; + goto out_dir; + } + rc = num_online; + +out_dir: + closedir(node_dir); +out_path: + free(node_path); + return rc; + +} diff --git a/daxctl/lib/libdaxctl.sym b/daxctl/lib/libdaxctl.sym index 19904a2..cc47ed6 100644 --- a/daxctl/lib/libdaxctl.sym +++ b/daxctl/lib/libdaxctl.sym @@ -54,8 +54,12 @@ global: LIBDAXCTL_6 { global: daxctl_dev_get_ctx; + daxctl_dev_get_target_node; daxctl_dev_is_enabled; daxctl_dev_disable; daxctl_dev_enable_devdax; daxctl_dev_enable_ram; + daxctl_dev_online_node; + daxctl_dev_offline_node; + daxctl_dev_node_is_online; } LIBDAXCTL_5; diff --git a/daxctl/libdaxctl.h b/daxctl/libdaxctl.h index b80488e..db0d4ea 100644 --- a/daxctl/libdaxctl.h +++ b/daxctl/libdaxctl.h @@ -68,10 +68,14 @@ int daxctl_dev_get_major(struct daxctl_dev *dev); int daxctl_dev_get_minor(struct daxctl_dev *dev); unsigned long long daxctl_dev_get_size(struct daxctl_dev *dev); struct daxctl_ctx *daxctl_dev_get_ctx(struct daxctl_dev *dev); +int daxctl_dev_get_target_node(struct daxctl_dev *dev); int daxctl_dev_is_enabled(struct daxctl_dev *dev); int daxctl_dev_disable(struct daxctl_dev *dev); int daxctl_dev_enable_devdax(struct daxctl_dev *dev); int daxctl_dev_enable_ram(struct daxctl_dev *dev); +int daxctl_dev_online_node(struct daxctl_dev *dev); +int daxctl_dev_offline_node(struct daxctl_dev *dev); +int daxctl_dev_node_is_online(struct daxctl_dev *dev); #define daxctl_dev_foreach(region, dev) \ for (dev = daxctl_dev_get_first(region); \ -- 2.20.1 _______________________________________________ Linux-nvdimm mailing list Linux-nvdimm@lists.01.org https://lists.01.org/mailman/listinfo/linux-nvdimm