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


Reply via email to