memblock_in_dev() treats a DAX device as one contiguous physical address
range, even when the device has multiple ranges separated by gaps. This
misses blocks in later ranges and may incorrectly include blocks belonging
to another device.

Memory block counts and per-block online/offline operations use this
lookup, so daxctl may operate on memory outside the requested device.

Match blocks against each individual mapping, retaining the single-range
fallback for older kernels.

Fixes: e8bf803e359b ("libdaxctl: add a 'daxctl_memory' object for memory based 
operations")
Signed-off-by: Alison Schofield <[email protected]>
---
 daxctl/lib/libdaxctl.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/daxctl/lib/libdaxctl.c b/daxctl/lib/libdaxctl.c
index f7cbf3fa12e7..b6b411dd916a 100644
--- a/daxctl/lib/libdaxctl.c
+++ b/daxctl/lib/libdaxctl.c
@@ -1615,6 +1615,7 @@ static int memblock_in_dev(struct daxctl_memory *mem, 
const char *memblock)
        const char *devname = daxctl_dev_get_devname(dev);
        struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev);
        int rc, path_len = mem->buf_len;
+       struct daxctl_mapping *mapping;
        unsigned long memblock_size;
        char buf[SYSFS_ATTR_SIZE];
        unsigned long phys_index;
@@ -1654,8 +1655,18 @@ static int memblock_in_dev(struct daxctl_memory *mem, 
const char *memblock)
        }
        memblock_res = phys_index * memblock_size;
 
-       if (memblock_res >= dev_start && memblock_res <= dev_end)
-               return 1;
+       rc = mappings_init(dev);
+       if (rc < 0)
+               return rc;
+
+       /* kernels without mappingN report a single range */
+       if (list_empty(&dev->mappings))
+               return memblock_res >= dev_start && memblock_res <= dev_end;
+
+       daxctl_mapping_foreach(dev, mapping)
+               if (memblock_res >= mapping->start &&
+                   memblock_res <= mapping->end)
+                       return 1;
 
        return 0;
 }
-- 
2.37.3


Reply via email to