From: Gregory Price <[email protected]>

When the kernel provides daxX.Y/state, online a fully offline device
by writing the online policy to it rather than onlining each block.
This avoids racing other agents (udev, auto-online) that online blocks
as they appear. The kernel only onlines from 'unplugged', so a device
in 'offline' is unplugged first; if that fails (e.g. no memory
hotremove support), the device is unchanged and is onlined per-block.

Memory onlined by another agent is left alone: the 'offline' case
re-checks that no block is online before unplugging, and losing a
racing online falls back to the per-block path. A small window remains
between that check and the unplug.

The per-block path now fails when the device has no memory blocks,
rather than reporting zero sections already online.

Signed-off-by: Gregory Price <[email protected]>
---
 daxctl/device.c | 105 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 105 insertions(+)

diff --git a/daxctl/device.c b/daxctl/device.c
index 47942f1..fd84799 100644
--- a/daxctl/device.c
+++ b/daxctl/device.c
@@ -510,10 +510,99 @@ static const char *parse_device_options(int argc, const 
char **argv,
        return device;
 }
 
+static int dev_online_memory_atomic(struct daxctl_dev *dev,
+               struct daxctl_memory *mem, enum daxctl_kmem_state state)
+{
+       const char *devname = daxctl_dev_get_devname(dev);
+       enum daxctl_kmem_state target;
+       int num_sections, num_on, rc;
+
+       if (param.no_movable)
+               target = DAXCTL_KMEM_ONLINE;
+       else
+               target = DAXCTL_KMEM_ONLINE_MOVABLE;
+
+       if (state == DAXCTL_KMEM_OFFLINE) {
+               /*
+                * Onlining requires 'unplugged'. Don't unplug memory another
+                * agent onlined meanwhile (a small window remains).
+                */
+               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 > 0)
+                       return -EAGAIN;
+               rc = daxctl_dev_set_kmem_state(dev, DAXCTL_KMEM_UNPLUGGED);
+               if (rc < 0) {
+                       /* unchanged (e.g. no hotremove support); go per-block 
*/
+                       if (param.verbose)
+                               fprintf(stderr, "%s: unplug failed: %s\n",
+                                       devname, strerror(-rc));
+                       return -EAGAIN;
+               }
+       }
+
+       rc = daxctl_dev_set_kmem_state(dev, target);
+       if (rc == -EBUSY) {
+               /* back off only if another writer onlined it */
+               switch (daxctl_dev_get_kmem_state(dev)) {
+               case DAXCTL_KMEM_ONLINE:
+               case DAXCTL_KMEM_ONLINE_KERNEL:
+               case DAXCTL_KMEM_ONLINE_MOVABLE:
+                       return -EAGAIN;
+               default:
+                       break;
+               }
+       }
+       if (rc < 0) {
+               fprintf(stderr, "%s: failed to online memory: %s\n",
+                       devname, strerror(-rc));
+               if (state == DAXCTL_KMEM_OFFLINE)
+                       fprintf(stderr,
+                               "%s: device left unplugged; retry to recover\n",
+                               devname);
+               return rc;
+       }
+
+       /* all sections should now be 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;
+       }
+       if (num_sections == 0) {
+               fprintf(stderr, "%s: no memory sections present after online\n",
+                       devname);
+               return -ENXIO;
+       }
+
+       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;
+       }
+       if (param.verbose)
+               fprintf(stderr, "%s: %d memory section%s onlined\n", devname,
+                       num_on, num_on == 1 ? "" : "s");
+
+       return 0;
+}
+
 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);
+       enum daxctl_kmem_state state;
        int num_sections, num_on, rc;
 
        if (!mem) {
@@ -522,6 +611,18 @@ static int dev_online_memory(struct daxctl_dev *dev)
                return -ENXIO;
        }
 
+       /*
+        * Online a fully offline device atomically where supported.
+        * -EAGAIN means another agent onlined memory, or the device
+        * could not be unplugged first; go per-block.
+        */
+       state = daxctl_dev_get_kmem_state(dev);
+       if (state == DAXCTL_KMEM_UNPLUGGED || state == DAXCTL_KMEM_OFFLINE) {
+               rc = dev_online_memory_atomic(dev, mem, state);
+               if (rc != -EAGAIN)
+                       return rc;
+       }
+
        /* get total number of sections and sections already online */
        num_sections = daxctl_memory_num_sections(mem);
        if (num_sections < 0) {
@@ -529,6 +630,10 @@ static int dev_online_memory(struct daxctl_dev *dev)
                        devname);
                return num_sections;
        }
+       if (num_sections == 0) {
+               fprintf(stderr, "%s: no memory sections present\n", devname);
+               return -ENXIO;
+       }
 
        num_on = daxctl_memory_is_online(mem);
        if (num_on < 0) {
-- 
2.53.0-Meta


Reply via email to