From: Gregory Price <[email protected]> When the kernel provides daxX.Y/state, offline by writing 'unplugged'. The kernel offlines and removes the whole device atomically, so no agent can online blocks before a later unbind. On failure the device is left unchanged.
The memory blocks are removed rather than left offline, including when they are all offline already; 'daxctl online-memory' restores them. If they are all offline and the unplug fails (e.g. no memory hotremove support), this remains a no-op. Unplugging a node's last memory removes its sysfs directory, so treat a missing node directory as zero blocks when the device is unplugged. 'daxctl list' likewise omits 'movable' for a device with no blocks. Signed-off-by: Gregory Price <[email protected]> --- daxctl/device.c | 51 +++++++++++++++++++++++++++++++++++++++--- daxctl/json.c | 6 ++++- daxctl/lib/libdaxctl.c | 12 ++++++++-- 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/daxctl/device.c b/daxctl/device.c index fd84799..99c0d96 100644 --- a/daxctl/device.c +++ b/daxctl/device.c @@ -694,7 +694,8 @@ 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; + int num_sections, num_on, num_off, rc, unplug; + enum daxctl_kmem_state state; if (!mem) { fprintf(stderr, "%s: failed to get the memory object\n", @@ -717,17 +718,61 @@ static int dev_offline_memory(struct daxctl_dev *dev) return num_on; } + /* + * Unplug atomically where supported, even if already offline, so + * no agent can online blocks before a later unbind. + */ + state = daxctl_dev_get_kmem_state(dev); + unplug = state != DAXCTL_KMEM_UNKNOWN && state != DAXCTL_KMEM_UNPLUGGED; + num_off = num_sections - num_on; - if (num_off == num_sections) { + if (num_off == num_sections && !unplug) { fprintf(stderr, "%s: all memory sections (%d) already offline\n", devname, num_off); return 1; } - if (num_off) + if (num_off && num_on) fprintf(stderr, "%s: %d memory section%s already offline\n", devname, num_off, num_off == 1 ? "" : "s"); + if (unplug) { + rc = daxctl_dev_set_kmem_state(dev, DAXCTL_KMEM_UNPLUGGED); + if (rc < 0 && num_on == 0) { + /* already offline, unplug is best effort */ + if (param.verbose) + fprintf(stderr, "%s: unplug failed: %s\n", + devname, strerror(-rc)); + fprintf(stderr, "%s: all memory sections (%d) already offline\n", + devname, num_off); + return 1; + } + 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 unplugged\n", + devname, num_sections, + num_sections == 1 ? "" : "s"); + + /* blocks are removed, none should remain 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) { + fprintf(stderr, "%s: failed to offline %d memory sections\n", + devname, num_on); + return -ENXIO; + } + + return 0; + } + /* offline the remaining sections */ rc = daxctl_memory_offline(mem); if (rc < 0) { diff --git a/daxctl/json.c b/daxctl/json.c index 8da91b1..cf81fe6 100644 --- a/daxctl/json.c +++ b/daxctl/json.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2015-2020 Intel Corporation. All rights reserved. +#include <errno.h> #include <limits.h> #include <string.h> #include <util/json.h> @@ -79,7 +80,10 @@ struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev, if (jobj) json_object_object_add(jdev, "total_memblocks", jobj); - movable = daxctl_memory_is_movable(mem); + /* an unplugged device has no zone */ + movable = -ENXIO; + if (num_sections > 0) + movable = daxctl_memory_is_movable(mem); if (movable == 1) jobj = json_object_new_boolean(true); else if (movable == 0) diff --git a/daxctl/lib/libdaxctl.c b/daxctl/lib/libdaxctl.c index cbb42e3..eedf4dd 100644 --- a/daxctl/lib/libdaxctl.c +++ b/daxctl/lib/libdaxctl.c @@ -1644,8 +1644,16 @@ static int daxctl_memory_op(struct daxctl_memory *mem, enum memory_op op) } node_dir = opendir(node_path); - if (!node_dir) - return -errno; + if (!node_dir) { + rc = -errno; + /* unplugging a node's last memory removes its directory */ + if (rc == -ENOENT && + daxctl_dev_get_kmem_state(dev) == DAXCTL_KMEM_UNPLUGGED) { + dbg(ctx, "%s: unplugged, no blocks to count\n", devname); + return 0; + } + return rc; + } errno = 0; while ((de = readdir(node_dir)) != NULL) { -- 2.53.0-Meta
