* [PATCH V5 0/2] daxctl: Add support for famfs mode [not found] <20260430153331.84139-1-john@jagalactic.com> @ 2026-04-30 15:33 ` John Groves 2026-04-30 15:34 ` [PATCH V5 1/2] " John Groves 2026-04-30 15:34 ` [PATCH V5 2/2] Add test/daxctl-famfs.sh to test famfs mode transitions: John Groves 0 siblings, 2 replies; 6+ messages in thread From: John Groves @ 2026-04-30 15:33 UTC (permalink / raw) To: John Groves, John Groves, Dan Williams, Alison Schofield Cc: John Groves, Vishal Verma, Dave Jiang, Jonathan Cameron, Aravind Ramesh, Ajay Joshi, venkataravis@micron.com, dev.srinivasulu@gmail.com, linux-kernel@vger.kernel.org, nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org, John Groves From: John Groves <john@groves.net> This series adds famfs mode support to daxctl, alongside the existing devdax and system-ram modes. A daxdev is in famfs mode when it is bound to fsdev_dax.ko (drivers/dax/fsdev.c). Patch 1 adds the library plumbing: mode detection helpers, an enable function, and the device.c reconfigure-device wiring. Patch 2 adds a test that exercises mode transitions. This series depends on the fsdev_dax kernel driver (which provides famfs mode) and on the famfs kernel patch series. Patches: 1/2 daxctl: Add support for famfs mode 2/2 Add test/daxctl-famfs.sh to test famfs mode transitions Changes since V4 (addressing Alison and Ira's comments): - Consolidate the per-mode driver-symlink lookups into a single static helper daxctl_dev_bound_to_module(); the three mode predicates now delegate to it. - Use PATH_MAX instead of a hardcoded char[200] in the mode-check path. - Use path_basename() instead of basename(). - Emit a dbg() message when realpath() on the driver symlink returns NULL. - Revert the json.c change that reported unbound devices as 'unknown'. An unbound device is reported as 'devdax' again, matching the prior behavior; the configured-vs-active distinction Alison raised goes away. - Add missing fprintf(stderr) "disable failed" messages in the "already in target mode, just re-enable" branches of reconfig_mode_devdax() and reconfig_mode_famfs(), matching the pattern used by the disable_*_device() helpers. - Reword commit messages: clarify that the test is in a separate patch, drop the stale "json.c shows 'unknown'" line, and replace "Fix mode transition logic" with "Update mode transition logic" since these are extensions for the new mode rather than fixes to broken existing behavior. John Groves (2): daxctl: Add support for famfs mode Add test/daxctl-famfs.sh to test famfs mode transitions: daxctl/device.c | 132 +++++++++++++++-- daxctl/json.c | 13 +- daxctl/lib/libdaxctl-private.h | 2 + daxctl/lib/libdaxctl.c | 39 ++++- daxctl/lib/libdaxctl.sym | 7 + daxctl/libdaxctl.h | 3 + test/daxctl-famfs.sh | 253 +++++++++++++++++++++++++++++++++ test/meson.build | 2 + 8 files changed, 436 insertions(+), 15 deletions(-) create mode 100755 test/daxctl-famfs.sh base-commit: 8ad90e54f0ff4f7291e7f21d44d769d10f24e2b6 -- 2.53.0 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH V5 1/2] daxctl: Add support for famfs mode 2026-04-30 15:33 ` [PATCH V5 0/2] daxctl: Add support for famfs mode John Groves @ 2026-04-30 15:34 ` John Groves 2026-04-30 16:20 ` Dave Jiang 2026-04-30 15:34 ` [PATCH V5 2/2] Add test/daxctl-famfs.sh to test famfs mode transitions: John Groves 1 sibling, 1 reply; 6+ messages in thread From: John Groves @ 2026-04-30 15:34 UTC (permalink / raw) To: John Groves, John Groves, Dan Williams, Alison Schofield Cc: John Groves, Vishal Verma, Dave Jiang, Jonathan Cameron, Aravind Ramesh, Ajay Joshi, venkataravis@micron.com, dev.srinivasulu@gmail.com, linux-kernel@vger.kernel.org, nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org, John Groves From: John Groves <John@Groves.net> Putting a daxdev in famfs mode means binding it to fsdev_dax.ko (drivers/dax/fsdev.c). Finding a daxdev bound to fsdev_dax means it is in famfs mode. A test for this functionality is added in the next commit. With devdax, famfs, and system-ram modes, the previous logic that assumed 'not in mode X means in mode Y' needed to get slightly more complicated. Add explicit mode detection functions: - daxctl_dev_is_famfs_mode(): check if bound to fsdev_dax driver - daxctl_dev_is_devdax_mode(): check if bound to device_dax driver Both delegate to a shared static helper daxctl_dev_bound_to_module() to avoid duplicating the driver-symlink lookup, as does the pre-existing daxctl_dev_is_system_ram_capable(). Update mode transition logic in device.c: - disable_devdax_device(): verify device is actually in devdax mode - disable_famfs_device(): verify device is actually in famfs mode - All reconfig_mode_*() functions explicitly check each mode - Handle unrecognized mode with an error instead of wrong assumption Update json.c to report fsdev_dax-bound devices as 'famfs' mode. An unbound device continues to be reported as 'devdax' (the legacy default when no driver is bound), to preserve existing behavior. Signed-off-by: John Groves <john@groves.net> --- daxctl/device.c | 132 ++++++++++++++++++++++++++++++--- daxctl/json.c | 13 +++- daxctl/lib/libdaxctl-private.h | 2 + daxctl/lib/libdaxctl.c | 39 +++++++++- daxctl/lib/libdaxctl.sym | 7 ++ daxctl/libdaxctl.h | 3 + 6 files changed, 181 insertions(+), 15 deletions(-) diff --git a/daxctl/device.c b/daxctl/device.c index a4e36b1..003609e 100644 --- a/daxctl/device.c +++ b/daxctl/device.c @@ -42,6 +42,7 @@ enum dev_mode { DAXCTL_DEV_MODE_UNKNOWN, DAXCTL_DEV_MODE_DEVDAX, DAXCTL_DEV_MODE_RAM, + DAXCTL_DEV_MODE_FAMFS, }; struct mapping { @@ -471,6 +472,13 @@ static const char *parse_device_options(int argc, const char **argv, "--no-online is incompatible with --mode=devdax\n"); rc = -EINVAL; } + } else if (strcmp(param.mode, "famfs") == 0) { + reconfig_mode = DAXCTL_DEV_MODE_FAMFS; + if (param.no_online) { + fprintf(stderr, + "--no-online is incompatible with --mode=famfs\n"); + rc = -EINVAL; + } } break; case ACTION_CREATE: @@ -696,8 +704,42 @@ static int disable_devdax_device(struct daxctl_dev *dev) int rc; if (mem) { - fprintf(stderr, "%s was already in system-ram mode\n", - devname); + fprintf(stderr, "%s is in system-ram mode\n", devname); + return 1; + } + if (daxctl_dev_is_famfs_mode(dev)) { + fprintf(stderr, "%s is in famfs mode\n", devname); + return 1; + } + if (!daxctl_dev_is_devdax_mode(dev)) { + fprintf(stderr, "%s is not in devdax mode\n", devname); + return 1; + } + rc = daxctl_dev_disable(dev); + if (rc) { + fprintf(stderr, "%s: disable failed: %s\n", + daxctl_dev_get_devname(dev), strerror(-rc)); + return rc; + } + return 0; +} + +static int disable_famfs_device(struct daxctl_dev *dev) +{ + struct daxctl_memory *mem = daxctl_dev_get_memory(dev); + const char *devname = daxctl_dev_get_devname(dev); + int rc; + + if (mem) { + fprintf(stderr, "%s is in system-ram mode\n", devname); + return 1; + } + if (daxctl_dev_is_devdax_mode(dev)) { + fprintf(stderr, "%s is in devdax mode\n", devname); + return 1; + } + if (!daxctl_dev_is_famfs_mode(dev)) { + fprintf(stderr, "%s is not in famfs mode\n", devname); return 1; } rc = daxctl_dev_disable(dev); @@ -711,6 +753,7 @@ static int disable_devdax_device(struct daxctl_dev *dev) static int reconfig_mode_system_ram(struct daxctl_dev *dev) { + struct daxctl_memory *mem = daxctl_dev_get_memory(dev); const char *devname = daxctl_dev_get_devname(dev); int rc, skip_enable = 0; @@ -724,11 +767,21 @@ static int reconfig_mode_system_ram(struct daxctl_dev *dev) } if (daxctl_dev_is_enabled(dev)) { - rc = disable_devdax_device(dev); - if (rc < 0) - return rc; - if (rc > 0) + if (mem) { + /* already in system-ram mode */ skip_enable = 1; + } else if (daxctl_dev_is_famfs_mode(dev)) { + rc = disable_famfs_device(dev); + if (rc) + return rc; + } else if (daxctl_dev_is_devdax_mode(dev)) { + rc = disable_devdax_device(dev); + if (rc) + return rc; + } else { + fprintf(stderr, "%s: unknown mode\n", devname); + return -EINVAL; + } } if (!skip_enable) { @@ -750,7 +803,7 @@ static int disable_system_ram_device(struct daxctl_dev *dev) int rc; if (!mem) { - fprintf(stderr, "%s was already in devdax mode\n", devname); + fprintf(stderr, "%s is not in system-ram mode\n", devname); return 1; } @@ -786,12 +839,31 @@ static int disable_system_ram_device(struct daxctl_dev *dev) static int reconfig_mode_devdax(struct daxctl_dev *dev) { + struct daxctl_memory *mem = daxctl_dev_get_memory(dev); + const char *devname = daxctl_dev_get_devname(dev); int rc; if (daxctl_dev_is_enabled(dev)) { - rc = disable_system_ram_device(dev); - if (rc) - return rc; + if (mem) { + rc = disable_system_ram_device(dev); + if (rc) + return rc; + } else if (daxctl_dev_is_famfs_mode(dev)) { + rc = disable_famfs_device(dev); + if (rc) + return rc; + } else if (daxctl_dev_is_devdax_mode(dev)) { + /* already in devdax mode, just re-enable */ + rc = daxctl_dev_disable(dev); + if (rc) { + fprintf(stderr, "%s: disable failed: %s\n", + devname, strerror(-rc)); + return rc; + } + } else { + fprintf(stderr, "%s: unknown mode\n", devname); + return -EINVAL; + } } rc = daxctl_dev_enable_devdax(dev); @@ -801,6 +873,43 @@ static int reconfig_mode_devdax(struct daxctl_dev *dev) return 0; } +static int reconfig_mode_famfs(struct daxctl_dev *dev) +{ + struct daxctl_memory *mem = daxctl_dev_get_memory(dev); + const char *devname = daxctl_dev_get_devname(dev); + int rc; + + if (daxctl_dev_is_enabled(dev)) { + if (mem) { + fprintf(stderr, + "%s is in system-ram mode; must be in devdax mode to convert to famfs\n", + devname); + return -EINVAL; + } else if (daxctl_dev_is_famfs_mode(dev)) { + /* already in famfs mode, just re-enable */ + rc = daxctl_dev_disable(dev); + if (rc) { + fprintf(stderr, "%s: disable failed: %s\n", + devname, strerror(-rc)); + return rc; + } + } else if (daxctl_dev_is_devdax_mode(dev)) { + rc = disable_devdax_device(dev); + if (rc) + return rc; + } else { + fprintf(stderr, "%s: unknown mode\n", devname); + return -EINVAL; + } + } + + rc = daxctl_dev_enable_famfs(dev); + if (rc) + return rc; + + return 0; +} + static int do_create(struct daxctl_region *region, long long val, struct json_object **jdevs) { @@ -887,6 +996,9 @@ static int do_reconfig(struct daxctl_dev *dev, enum dev_mode mode, case DAXCTL_DEV_MODE_DEVDAX: rc = reconfig_mode_devdax(dev); break; + case DAXCTL_DEV_MODE_FAMFS: + rc = reconfig_mode_famfs(dev); + break; default: fprintf(stderr, "%s: unknown mode requested: %d\n", devname, mode); diff --git a/daxctl/json.c b/daxctl/json.c index 3cbce9d..2a4b12c 100644 --- a/daxctl/json.c +++ b/daxctl/json.c @@ -48,8 +48,19 @@ struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev, if (mem) jobj = json_object_new_string("system-ram"); - else + else if (daxctl_dev_is_famfs_mode(dev)) + jobj = json_object_new_string("famfs"); + else if (daxctl_dev_is_devdax_mode(dev)) jobj = json_object_new_string("devdax"); + else { + /* Legacy condition; if a daxdev is not in any "mode", that + * means no driver is bound. We report that as a disabled + * device in devdax mode. (the disabled modifier is added later + * in this function if applicable) + */ + jobj = json_object_new_string("devdax"); + } + if (jobj) json_object_object_add(jdev, "mode", jobj); diff --git a/daxctl/lib/libdaxctl-private.h b/daxctl/lib/libdaxctl-private.h index ae45311..0bb73e8 100644 --- a/daxctl/lib/libdaxctl-private.h +++ b/daxctl/lib/libdaxctl-private.h @@ -21,12 +21,14 @@ static const char *dax_subsystems[] = { enum daxctl_dev_mode { DAXCTL_DEV_MODE_DEVDAX = 0, DAXCTL_DEV_MODE_RAM, + DAXCTL_DEV_MODE_FAMFS, DAXCTL_DEV_MODE_END, }; static const char *dax_modules[] = { [DAXCTL_DEV_MODE_DEVDAX] = "device_dax", [DAXCTL_DEV_MODE_RAM] = "kmem", + [DAXCTL_DEV_MODE_FAMFS] = "fsdev_dax", }; enum memory_op { diff --git a/daxctl/lib/libdaxctl.c b/daxctl/lib/libdaxctl.c index 02ae7e5..33121dc 100644 --- a/daxctl/lib/libdaxctl.c +++ b/daxctl/lib/libdaxctl.c @@ -385,13 +385,13 @@ static bool device_model_is_dax_bus(struct daxctl_dev *dev) return false; } -DAXCTL_EXPORT int daxctl_dev_is_system_ram_capable(struct daxctl_dev *dev) +static int daxctl_dev_bound_to_module(struct daxctl_dev *dev, const char *mod_name) { const char *devname = daxctl_dev_get_devname(dev); struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); const char *mod_base; char *mod_path; - char path[200]; + char path[PATH_MAX]; const int len = sizeof(path); if (!device_model_is_dax_bus(dev)) @@ -406,11 +406,13 @@ DAXCTL_EXPORT int daxctl_dev_is_system_ram_capable(struct daxctl_dev *dev) } mod_path = realpath(path, NULL); - if (!mod_path) + if (!mod_path) { + dbg(ctx, "%s: realpath failed for driver link\n", devname); return false; + } mod_base = path_basename(mod_path); - if (strcmp(mod_base, dax_modules[DAXCTL_DEV_MODE_RAM]) == 0) { + if (strcmp(mod_base, mod_name) == 0) { free(mod_path); return true; } @@ -419,6 +421,30 @@ DAXCTL_EXPORT int daxctl_dev_is_system_ram_capable(struct daxctl_dev *dev) return false; } +DAXCTL_EXPORT int daxctl_dev_is_system_ram_capable(struct daxctl_dev *dev) +{ + return daxctl_dev_bound_to_module(dev, dax_modules[DAXCTL_DEV_MODE_RAM]); +} + +/* + * Check if device is currently in famfs mode (bound to fsdev_dax driver). + * Returns false for disabled devices: the DAX bus does not retain the previous + * driver binding after unbind, so mode cannot be determined without a driver. + */ +DAXCTL_EXPORT int daxctl_dev_is_famfs_mode(struct daxctl_dev *dev) +{ + return daxctl_dev_bound_to_module(dev, dax_modules[DAXCTL_DEV_MODE_FAMFS]); +} + +/* + * Check if device is currently in devdax mode (bound to device_dax driver). + * Returns false for disabled devices; see daxctl_dev_is_famfs_mode(). + */ +DAXCTL_EXPORT int daxctl_dev_is_devdax_mode(struct daxctl_dev *dev) +{ + return daxctl_dev_bound_to_module(dev, dax_modules[DAXCTL_DEV_MODE_DEVDAX]); +} + /* * This checks for the device to be in system-ram mode, so calling * daxctl_dev_get_memory() on a devdax mode device will always return NULL. @@ -983,6 +1009,11 @@ DAXCTL_EXPORT int daxctl_dev_enable_ram(struct daxctl_dev *dev) return daxctl_dev_enable(dev, DAXCTL_DEV_MODE_RAM); } +DAXCTL_EXPORT int daxctl_dev_enable_famfs(struct daxctl_dev *dev) +{ + return daxctl_dev_enable(dev, DAXCTL_DEV_MODE_FAMFS); +} + DAXCTL_EXPORT int daxctl_dev_disable(struct daxctl_dev *dev) { const char *devname = daxctl_dev_get_devname(dev); diff --git a/daxctl/lib/libdaxctl.sym b/daxctl/lib/libdaxctl.sym index 3098811..2a812c6 100644 --- a/daxctl/lib/libdaxctl.sym +++ b/daxctl/lib/libdaxctl.sym @@ -104,3 +104,10 @@ LIBDAXCTL_10 { global: daxctl_dev_is_system_ram_capable; } LIBDAXCTL_9; + +LIBDAXCTL_11 { +global: + daxctl_dev_enable_famfs; + daxctl_dev_is_famfs_mode; + daxctl_dev_is_devdax_mode; +} LIBDAXCTL_10; diff --git a/daxctl/libdaxctl.h b/daxctl/libdaxctl.h index 53c6bbd..84fcdb4 100644 --- a/daxctl/libdaxctl.h +++ b/daxctl/libdaxctl.h @@ -72,12 +72,15 @@ 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_enable_famfs(struct daxctl_dev *dev); int daxctl_dev_get_target_node(struct daxctl_dev *dev); int daxctl_dev_will_auto_online_memory(struct daxctl_dev *dev); int daxctl_dev_has_online_memory(struct daxctl_dev *dev); struct daxctl_memory; int daxctl_dev_is_system_ram_capable(struct daxctl_dev *dev); +int daxctl_dev_is_famfs_mode(struct daxctl_dev *dev); +int daxctl_dev_is_devdax_mode(struct daxctl_dev *dev); struct daxctl_memory *daxctl_dev_get_memory(struct daxctl_dev *dev); struct daxctl_dev *daxctl_memory_get_dev(struct daxctl_memory *mem); const char *daxctl_memory_get_node_path(struct daxctl_memory *mem); -- 2.53.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH V5 1/2] daxctl: Add support for famfs mode 2026-04-30 15:34 ` [PATCH V5 1/2] " John Groves @ 2026-04-30 16:20 ` Dave Jiang 0 siblings, 0 replies; 6+ messages in thread From: Dave Jiang @ 2026-04-30 16:20 UTC (permalink / raw) To: John Groves, John Groves, John Groves, Dan Williams, Alison Schofield Cc: John Groves, Vishal Verma, Jonathan Cameron, Aravind Ramesh, Ajay Joshi, venkataravis@micron.com, dev.srinivasulu@gmail.com, linux-kernel@vger.kernel.org, nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org On 4/30/26 8:34 AM, John Groves wrote: > From: John Groves <John@Groves.net> > > Putting a daxdev in famfs mode means binding it to fsdev_dax.ko > (drivers/dax/fsdev.c). Finding a daxdev bound to fsdev_dax means > it is in famfs mode. > > A test for this functionality is added in the next commit. > > With devdax, famfs, and system-ram modes, the previous logic that assumed > 'not in mode X means in mode Y' needed to get slightly more complicated. > > Add explicit mode detection functions: > - daxctl_dev_is_famfs_mode(): check if bound to fsdev_dax driver > - daxctl_dev_is_devdax_mode(): check if bound to device_dax driver > Both delegate to a shared static helper daxctl_dev_bound_to_module() to > avoid duplicating the driver-symlink lookup, as does the pre-existing > daxctl_dev_is_system_ram_capable(). > > Update mode transition logic in device.c: > - disable_devdax_device(): verify device is actually in devdax mode > - disable_famfs_device(): verify device is actually in famfs mode > - All reconfig_mode_*() functions explicitly check each mode > - Handle unrecognized mode with an error instead of wrong assumption > > Update json.c to report fsdev_dax-bound devices as 'famfs' mode. An > unbound device continues to be reported as 'devdax' (the legacy default > when no driver is bound), to preserve existing behavior. > > Signed-off-by: John Groves <john@groves.net> Reviewed-by: Dave Jiang <dave.jiang@intel.com> > --- > daxctl/device.c | 132 ++++++++++++++++++++++++++++++--- > daxctl/json.c | 13 +++- > daxctl/lib/libdaxctl-private.h | 2 + > daxctl/lib/libdaxctl.c | 39 +++++++++- > daxctl/lib/libdaxctl.sym | 7 ++ > daxctl/libdaxctl.h | 3 + > 6 files changed, 181 insertions(+), 15 deletions(-) > > diff --git a/daxctl/device.c b/daxctl/device.c > index a4e36b1..003609e 100644 > --- a/daxctl/device.c > +++ b/daxctl/device.c > @@ -42,6 +42,7 @@ enum dev_mode { > DAXCTL_DEV_MODE_UNKNOWN, > DAXCTL_DEV_MODE_DEVDAX, > DAXCTL_DEV_MODE_RAM, > + DAXCTL_DEV_MODE_FAMFS, > }; > > struct mapping { > @@ -471,6 +472,13 @@ static const char *parse_device_options(int argc, const char **argv, > "--no-online is incompatible with --mode=devdax\n"); > rc = -EINVAL; > } > + } else if (strcmp(param.mode, "famfs") == 0) { > + reconfig_mode = DAXCTL_DEV_MODE_FAMFS; > + if (param.no_online) { > + fprintf(stderr, > + "--no-online is incompatible with --mode=famfs\n"); > + rc = -EINVAL; > + } > } > break; > case ACTION_CREATE: > @@ -696,8 +704,42 @@ static int disable_devdax_device(struct daxctl_dev *dev) > int rc; > > if (mem) { > - fprintf(stderr, "%s was already in system-ram mode\n", > - devname); > + fprintf(stderr, "%s is in system-ram mode\n", devname); > + return 1; > + } > + if (daxctl_dev_is_famfs_mode(dev)) { > + fprintf(stderr, "%s is in famfs mode\n", devname); > + return 1; > + } > + if (!daxctl_dev_is_devdax_mode(dev)) { > + fprintf(stderr, "%s is not in devdax mode\n", devname); > + return 1; > + } > + rc = daxctl_dev_disable(dev); > + if (rc) { > + fprintf(stderr, "%s: disable failed: %s\n", > + daxctl_dev_get_devname(dev), strerror(-rc)); > + return rc; > + } > + return 0; > +} > + > +static int disable_famfs_device(struct daxctl_dev *dev) > +{ > + struct daxctl_memory *mem = daxctl_dev_get_memory(dev); > + const char *devname = daxctl_dev_get_devname(dev); > + int rc; > + > + if (mem) { > + fprintf(stderr, "%s is in system-ram mode\n", devname); > + return 1; > + } > + if (daxctl_dev_is_devdax_mode(dev)) { > + fprintf(stderr, "%s is in devdax mode\n", devname); > + return 1; > + } > + if (!daxctl_dev_is_famfs_mode(dev)) { > + fprintf(stderr, "%s is not in famfs mode\n", devname); > return 1; > } > rc = daxctl_dev_disable(dev); > @@ -711,6 +753,7 @@ static int disable_devdax_device(struct daxctl_dev *dev) > > static int reconfig_mode_system_ram(struct daxctl_dev *dev) > { > + struct daxctl_memory *mem = daxctl_dev_get_memory(dev); > const char *devname = daxctl_dev_get_devname(dev); > int rc, skip_enable = 0; > > @@ -724,11 +767,21 @@ static int reconfig_mode_system_ram(struct daxctl_dev *dev) > } > > if (daxctl_dev_is_enabled(dev)) { > - rc = disable_devdax_device(dev); > - if (rc < 0) > - return rc; > - if (rc > 0) > + if (mem) { > + /* already in system-ram mode */ > skip_enable = 1; > + } else if (daxctl_dev_is_famfs_mode(dev)) { > + rc = disable_famfs_device(dev); > + if (rc) > + return rc; > + } else if (daxctl_dev_is_devdax_mode(dev)) { > + rc = disable_devdax_device(dev); > + if (rc) > + return rc; > + } else { > + fprintf(stderr, "%s: unknown mode\n", devname); > + return -EINVAL; > + } > } > > if (!skip_enable) { > @@ -750,7 +803,7 @@ static int disable_system_ram_device(struct daxctl_dev *dev) > int rc; > > if (!mem) { > - fprintf(stderr, "%s was already in devdax mode\n", devname); > + fprintf(stderr, "%s is not in system-ram mode\n", devname); > return 1; > } > > @@ -786,12 +839,31 @@ static int disable_system_ram_device(struct daxctl_dev *dev) > > static int reconfig_mode_devdax(struct daxctl_dev *dev) > { > + struct daxctl_memory *mem = daxctl_dev_get_memory(dev); > + const char *devname = daxctl_dev_get_devname(dev); > int rc; > > if (daxctl_dev_is_enabled(dev)) { > - rc = disable_system_ram_device(dev); > - if (rc) > - return rc; > + if (mem) { > + rc = disable_system_ram_device(dev); > + if (rc) > + return rc; > + } else if (daxctl_dev_is_famfs_mode(dev)) { > + rc = disable_famfs_device(dev); > + if (rc) > + return rc; > + } else if (daxctl_dev_is_devdax_mode(dev)) { > + /* already in devdax mode, just re-enable */ > + rc = daxctl_dev_disable(dev); > + if (rc) { > + fprintf(stderr, "%s: disable failed: %s\n", > + devname, strerror(-rc)); > + return rc; > + } > + } else { > + fprintf(stderr, "%s: unknown mode\n", devname); > + return -EINVAL; > + } > } > > rc = daxctl_dev_enable_devdax(dev); > @@ -801,6 +873,43 @@ static int reconfig_mode_devdax(struct daxctl_dev *dev) > return 0; > } > > +static int reconfig_mode_famfs(struct daxctl_dev *dev) > +{ > + struct daxctl_memory *mem = daxctl_dev_get_memory(dev); > + const char *devname = daxctl_dev_get_devname(dev); > + int rc; > + > + if (daxctl_dev_is_enabled(dev)) { > + if (mem) { > + fprintf(stderr, > + "%s is in system-ram mode; must be in devdax mode to convert to famfs\n", > + devname); > + return -EINVAL; > + } else if (daxctl_dev_is_famfs_mode(dev)) { > + /* already in famfs mode, just re-enable */ > + rc = daxctl_dev_disable(dev); > + if (rc) { > + fprintf(stderr, "%s: disable failed: %s\n", > + devname, strerror(-rc)); > + return rc; > + } > + } else if (daxctl_dev_is_devdax_mode(dev)) { > + rc = disable_devdax_device(dev); > + if (rc) > + return rc; > + } else { > + fprintf(stderr, "%s: unknown mode\n", devname); > + return -EINVAL; > + } > + } > + > + rc = daxctl_dev_enable_famfs(dev); > + if (rc) > + return rc; > + > + return 0; > +} > + > static int do_create(struct daxctl_region *region, long long val, > struct json_object **jdevs) > { > @@ -887,6 +996,9 @@ static int do_reconfig(struct daxctl_dev *dev, enum dev_mode mode, > case DAXCTL_DEV_MODE_DEVDAX: > rc = reconfig_mode_devdax(dev); > break; > + case DAXCTL_DEV_MODE_FAMFS: > + rc = reconfig_mode_famfs(dev); > + break; > default: > fprintf(stderr, "%s: unknown mode requested: %d\n", > devname, mode); > diff --git a/daxctl/json.c b/daxctl/json.c > index 3cbce9d..2a4b12c 100644 > --- a/daxctl/json.c > +++ b/daxctl/json.c > @@ -48,8 +48,19 @@ struct json_object *util_daxctl_dev_to_json(struct daxctl_dev *dev, > > if (mem) > jobj = json_object_new_string("system-ram"); > - else > + else if (daxctl_dev_is_famfs_mode(dev)) > + jobj = json_object_new_string("famfs"); > + else if (daxctl_dev_is_devdax_mode(dev)) > jobj = json_object_new_string("devdax"); > + else { > + /* Legacy condition; if a daxdev is not in any "mode", that > + * means no driver is bound. We report that as a disabled > + * device in devdax mode. (the disabled modifier is added later > + * in this function if applicable) > + */ > + jobj = json_object_new_string("devdax"); > + } > + > if (jobj) > json_object_object_add(jdev, "mode", jobj); > > diff --git a/daxctl/lib/libdaxctl-private.h b/daxctl/lib/libdaxctl-private.h > index ae45311..0bb73e8 100644 > --- a/daxctl/lib/libdaxctl-private.h > +++ b/daxctl/lib/libdaxctl-private.h > @@ -21,12 +21,14 @@ static const char *dax_subsystems[] = { > enum daxctl_dev_mode { > DAXCTL_DEV_MODE_DEVDAX = 0, > DAXCTL_DEV_MODE_RAM, > + DAXCTL_DEV_MODE_FAMFS, > DAXCTL_DEV_MODE_END, > }; > > static const char *dax_modules[] = { > [DAXCTL_DEV_MODE_DEVDAX] = "device_dax", > [DAXCTL_DEV_MODE_RAM] = "kmem", > + [DAXCTL_DEV_MODE_FAMFS] = "fsdev_dax", > }; > > enum memory_op { > diff --git a/daxctl/lib/libdaxctl.c b/daxctl/lib/libdaxctl.c > index 02ae7e5..33121dc 100644 > --- a/daxctl/lib/libdaxctl.c > +++ b/daxctl/lib/libdaxctl.c > @@ -385,13 +385,13 @@ static bool device_model_is_dax_bus(struct daxctl_dev *dev) > return false; > } > > -DAXCTL_EXPORT int daxctl_dev_is_system_ram_capable(struct daxctl_dev *dev) > +static int daxctl_dev_bound_to_module(struct daxctl_dev *dev, const char *mod_name) > { > const char *devname = daxctl_dev_get_devname(dev); > struct daxctl_ctx *ctx = daxctl_dev_get_ctx(dev); > const char *mod_base; > char *mod_path; > - char path[200]; > + char path[PATH_MAX]; > const int len = sizeof(path); > > if (!device_model_is_dax_bus(dev)) > @@ -406,11 +406,13 @@ DAXCTL_EXPORT int daxctl_dev_is_system_ram_capable(struct daxctl_dev *dev) > } > > mod_path = realpath(path, NULL); > - if (!mod_path) > + if (!mod_path) { > + dbg(ctx, "%s: realpath failed for driver link\n", devname); > return false; > + } > > mod_base = path_basename(mod_path); > - if (strcmp(mod_base, dax_modules[DAXCTL_DEV_MODE_RAM]) == 0) { > + if (strcmp(mod_base, mod_name) == 0) { > free(mod_path); > return true; > } > @@ -419,6 +421,30 @@ DAXCTL_EXPORT int daxctl_dev_is_system_ram_capable(struct daxctl_dev *dev) > return false; > } > > +DAXCTL_EXPORT int daxctl_dev_is_system_ram_capable(struct daxctl_dev *dev) > +{ > + return daxctl_dev_bound_to_module(dev, dax_modules[DAXCTL_DEV_MODE_RAM]); > +} > + > +/* > + * Check if device is currently in famfs mode (bound to fsdev_dax driver). > + * Returns false for disabled devices: the DAX bus does not retain the previous > + * driver binding after unbind, so mode cannot be determined without a driver. > + */ > +DAXCTL_EXPORT int daxctl_dev_is_famfs_mode(struct daxctl_dev *dev) > +{ > + return daxctl_dev_bound_to_module(dev, dax_modules[DAXCTL_DEV_MODE_FAMFS]); > +} > + > +/* > + * Check if device is currently in devdax mode (bound to device_dax driver). > + * Returns false for disabled devices; see daxctl_dev_is_famfs_mode(). > + */ > +DAXCTL_EXPORT int daxctl_dev_is_devdax_mode(struct daxctl_dev *dev) > +{ > + return daxctl_dev_bound_to_module(dev, dax_modules[DAXCTL_DEV_MODE_DEVDAX]); > +} > + > /* > * This checks for the device to be in system-ram mode, so calling > * daxctl_dev_get_memory() on a devdax mode device will always return NULL. > @@ -983,6 +1009,11 @@ DAXCTL_EXPORT int daxctl_dev_enable_ram(struct daxctl_dev *dev) > return daxctl_dev_enable(dev, DAXCTL_DEV_MODE_RAM); > } > > +DAXCTL_EXPORT int daxctl_dev_enable_famfs(struct daxctl_dev *dev) > +{ > + return daxctl_dev_enable(dev, DAXCTL_DEV_MODE_FAMFS); > +} > + > DAXCTL_EXPORT int daxctl_dev_disable(struct daxctl_dev *dev) > { > const char *devname = daxctl_dev_get_devname(dev); > diff --git a/daxctl/lib/libdaxctl.sym b/daxctl/lib/libdaxctl.sym > index 3098811..2a812c6 100644 > --- a/daxctl/lib/libdaxctl.sym > +++ b/daxctl/lib/libdaxctl.sym > @@ -104,3 +104,10 @@ LIBDAXCTL_10 { > global: > daxctl_dev_is_system_ram_capable; > } LIBDAXCTL_9; > + > +LIBDAXCTL_11 { > +global: > + daxctl_dev_enable_famfs; > + daxctl_dev_is_famfs_mode; > + daxctl_dev_is_devdax_mode; > +} LIBDAXCTL_10; > diff --git a/daxctl/libdaxctl.h b/daxctl/libdaxctl.h > index 53c6bbd..84fcdb4 100644 > --- a/daxctl/libdaxctl.h > +++ b/daxctl/libdaxctl.h > @@ -72,12 +72,15 @@ 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_enable_famfs(struct daxctl_dev *dev); > int daxctl_dev_get_target_node(struct daxctl_dev *dev); > int daxctl_dev_will_auto_online_memory(struct daxctl_dev *dev); > int daxctl_dev_has_online_memory(struct daxctl_dev *dev); > > struct daxctl_memory; > int daxctl_dev_is_system_ram_capable(struct daxctl_dev *dev); > +int daxctl_dev_is_famfs_mode(struct daxctl_dev *dev); > +int daxctl_dev_is_devdax_mode(struct daxctl_dev *dev); > struct daxctl_memory *daxctl_dev_get_memory(struct daxctl_dev *dev); > struct daxctl_dev *daxctl_memory_get_dev(struct daxctl_memory *mem); > const char *daxctl_memory_get_node_path(struct daxctl_memory *mem); ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH V5 2/2] Add test/daxctl-famfs.sh to test famfs mode transitions: 2026-04-30 15:33 ` [PATCH V5 0/2] daxctl: Add support for famfs mode John Groves 2026-04-30 15:34 ` [PATCH V5 1/2] " John Groves @ 2026-04-30 15:34 ` John Groves 2026-04-30 16:27 ` Dave Jiang 1 sibling, 1 reply; 6+ messages in thread From: John Groves @ 2026-04-30 15:34 UTC (permalink / raw) To: John Groves, John Groves, Dan Williams, Alison Schofield Cc: John Groves, Vishal Verma, Dave Jiang, Jonathan Cameron, Aravind Ramesh, Ajay Joshi, venkataravis@micron.com, dev.srinivasulu@gmail.com, linux-kernel@vger.kernel.org, nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org, John Groves From: John Groves <John@Groves.net> - devdax <-> famfs mode switches - Verify famfs -> system-ram is rejected (must go via devdax) - Test JSON output shows correct mode - Test error handling for invalid modes Signed-off-by: John Groves <john@groves.net> --- test/daxctl-famfs.sh | 253 +++++++++++++++++++++++++++++++++++++++++++ test/meson.build | 2 + 2 files changed, 255 insertions(+) create mode 100755 test/daxctl-famfs.sh diff --git a/test/daxctl-famfs.sh b/test/daxctl-famfs.sh new file mode 100755 index 0000000..12fbfef --- /dev/null +++ b/test/daxctl-famfs.sh @@ -0,0 +1,253 @@ +#!/bin/bash -Ex +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2025 Micron Technology, Inc. All rights reserved. +# +# Test daxctl famfs mode transitions and mode detection + +rc=77 +. $(dirname $0)/common + +trap 'cleanup $LINENO' ERR + +daxdev="" +original_mode="" + +cleanup() +{ + printf "Error at line %d\n" "$1" + # Try to restore to original mode if we know it + if [[ $daxdev && $original_mode ]]; then + "$DAXCTL" reconfigure-device -f -m "$original_mode" "$daxdev" 2>/dev/null || true + fi + exit $rc +} + +# Check if fsdev_dax module is available +check_fsdev_dax() +{ + if modinfo fsdev_dax &>/dev/null; then + return 0 + fi + if grep -qF "fsdev_dax" "/lib/modules/$(uname -r)/modules.builtin" 2>/dev/null; then + return 0 + fi + printf "fsdev_dax module not available, skipping\n" + exit 77 +} + +# Check if kmem module is available (needed for system-ram mode tests) +check_kmem() +{ + if modinfo kmem &>/dev/null; then + return 0 + fi + if grep -qF "kmem" "/lib/modules/$(uname -r)/modules.builtin" 2>/dev/null; then + return 0 + fi + printf "kmem module not available, skipping system-ram tests\n" + return 1 +} + +# Find an existing dax device to test with +find_daxdev() +{ + # Look for any available dax device + daxdev=$("$DAXCTL" list | jq -er '.[0].chardev // empty' 2>/dev/null) || true + + if [[ ! $daxdev ]]; then + printf "No dax device found, skipping\n" + exit 77 + fi + + # Save the original mode so we can restore it + original_mode=$("$DAXCTL" list -d "$daxdev" | jq -er '.[].mode') + + printf "Found dax device: %s (current mode: %s)\n" "$daxdev" "$original_mode" +} + +daxctl_get_mode() +{ + "$DAXCTL" list -d "$1" | jq -er '.[].mode' +} + +# Ensure device is in devdax mode for testing +ensure_devdax_mode() +{ + local mode + mode=$(daxctl_get_mode "$daxdev") + + if [[ "$mode" == "devdax" ]]; then + return 0 + fi + + if [[ "$mode" == "system-ram" ]]; then + printf "Device is in system-ram mode, attempting to convert to devdax...\n" + "$DAXCTL" reconfigure-device -f -m devdax "$daxdev" + elif [[ "$mode" == "famfs" ]]; then + printf "Device is in famfs mode, converting to devdax...\n" + "$DAXCTL" reconfigure-device -m devdax "$daxdev" + else + printf "Device is in unknown mode: %s\n" "$mode" + return 1 + fi + + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] +} + +# +# Test basic mode transitions involving famfs +# +test_famfs_mode_transitions() +{ + printf "\n=== Testing famfs mode transitions ===\n" + + # Ensure starting in devdax mode + ensure_devdax_mode + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] + printf "Initial mode: devdax - OK\n" + + # Test: devdax -> famfs + printf "Testing devdax -> famfs... " + "$DAXCTL" reconfigure-device -m famfs "$daxdev" + [[ $(daxctl_get_mode "$daxdev") == "famfs" ]] + printf "OK\n" + + # Test: famfs -> famfs (re-enable in same mode) + printf "Testing famfs -> famfs (re-enable)... " + "$DAXCTL" reconfigure-device -m famfs "$daxdev" + [[ $(daxctl_get_mode "$daxdev") == "famfs" ]] + printf "OK\n" + + # Test: famfs -> devdax + printf "Testing famfs -> devdax... " + "$DAXCTL" reconfigure-device -m devdax "$daxdev" + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] + printf "OK\n" + + # Test: devdax -> devdax (re-enable in same mode) + printf "Testing devdax -> devdax (re-enable)... " + "$DAXCTL" reconfigure-device -m devdax "$daxdev" + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] + printf "OK\n" +} + +# +# Test mode transitions with system-ram (requires kmem) +# +test_system_ram_transitions() +{ + printf "\n=== Testing system-ram transitions with famfs ===\n" + + # Ensure we start in devdax mode + ensure_devdax_mode + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] + + # Test: devdax -> system-ram + printf "Testing devdax -> system-ram... " + "$DAXCTL" reconfigure-device -N -m system-ram "$daxdev" + [[ $(daxctl_get_mode "$daxdev") == "system-ram" ]] + printf "OK\n" + + # Test: system-ram -> famfs should fail + printf "Testing system-ram -> famfs (should fail)... " + if "$DAXCTL" reconfigure-device -m famfs "$daxdev" 2>/dev/null; then + printf "FAILED - should have been rejected\n" + return 1 + fi + printf "OK (correctly rejected)\n" + + # Test: system-ram -> devdax -> famfs (proper path) + printf "Testing system-ram -> devdax -> famfs... " + "$DAXCTL" reconfigure-device -f -m devdax "$daxdev" + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] + "$DAXCTL" reconfigure-device -m famfs "$daxdev" + [[ $(daxctl_get_mode "$daxdev") == "famfs" ]] + printf "OK\n" + + # Restore to devdax for subsequent tests + "$DAXCTL" reconfigure-device -m devdax "$daxdev" +} + +# +# Test JSON output shows correct mode +# +test_json_output() +{ + printf "\n=== Testing JSON output for mode field ===\n" + + # Test devdax mode in JSON + ensure_devdax_mode + printf "Testing JSON output for devdax mode... " + mode=$("$DAXCTL" list -d "$daxdev" | jq -er '.[].mode') + [[ "$mode" == "devdax" ]] + printf "OK\n" + + # Test famfs mode in JSON + "$DAXCTL" reconfigure-device -m famfs "$daxdev" + printf "Testing JSON output for famfs mode... " + mode=$("$DAXCTL" list -d "$daxdev" | jq -er '.[].mode') + [[ "$mode" == "famfs" ]] + printf "OK\n" + + # Restore to devdax + "$DAXCTL" reconfigure-device -m devdax "$daxdev" +} + +# +# Test error messages for invalid transitions +# +test_error_handling() +{ + printf "\n=== Testing error handling ===\n" + + # Ensure we're in famfs mode + "$DAXCTL" reconfigure-device -m famfs "$daxdev" + + # Test that invalid mode is rejected + printf "Testing invalid mode rejection... " + if "$DAXCTL" reconfigure-device -m invalidmode "$daxdev" 2>/dev/null; then + printf "FAILED - invalid mode should be rejected\n" + return 1 + fi + printf "OK (correctly rejected)\n" + + # Restore to devdax + "$DAXCTL" reconfigure-device -m devdax "$daxdev" +} + +# +# Main test sequence +# +main() +{ + check_fsdev_dax + find_daxdev + + rc=1 # From here on, failures are real failures + + test_famfs_mode_transitions + test_json_output + test_error_handling + + # System-ram tests require kmem module + if check_kmem; then + # Save and disable online policy for system-ram tests + saved_policy="$(cat /sys/devices/system/memory/auto_online_blocks)" + echo "offline" > /sys/devices/system/memory/auto_online_blocks + + test_system_ram_transitions + + # Restore online policy + echo "$saved_policy" > /sys/devices/system/memory/auto_online_blocks + fi + + # Restore original mode + printf "\nRestoring device to original mode: %s\n" "$original_mode" + "$DAXCTL" reconfigure-device -f -m "$original_mode" "$daxdev" + + printf "\n=== All famfs tests passed ===\n" + + exit 0 +} + +main diff --git a/test/meson.build b/test/meson.build index 8a3718d..5b75c07 100644 --- a/test/meson.build +++ b/test/meson.build @@ -213,6 +213,7 @@ if get_option('destructive').enabled() device_dax_fio = find_program('device-dax-fio.sh') daxctl_devices = find_program('daxctl-devices.sh') daxctl_create = find_program('daxctl-create.sh') + daxctl_famfs = find_program('daxctl-famfs.sh') dm = find_program('dm.sh') mmap_test = find_program('mmap.sh') @@ -230,6 +231,7 @@ if get_option('destructive').enabled() [ 'device-dax-fio.sh', device_dax_fio, 'dax' ], [ 'daxctl-devices.sh', daxctl_devices, 'dax' ], [ 'daxctl-create.sh', daxctl_create, 'dax' ], + [ 'daxctl-famfs.sh', daxctl_famfs, 'dax' ], [ 'dm.sh', dm, 'dax' ], [ 'mmap.sh', mmap_test, 'dax' ], ] -- 2.53.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH V5 2/2] Add test/daxctl-famfs.sh to test famfs mode transitions: 2026-04-30 15:34 ` [PATCH V5 2/2] Add test/daxctl-famfs.sh to test famfs mode transitions: John Groves @ 2026-04-30 16:27 ` Dave Jiang 2026-04-30 16:50 ` John Groves 0 siblings, 1 reply; 6+ messages in thread From: Dave Jiang @ 2026-04-30 16:27 UTC (permalink / raw) To: John Groves, John Groves, John Groves, Dan Williams, Alison Schofield Cc: John Groves, Vishal Verma, Jonathan Cameron, Aravind Ramesh, Ajay Joshi, venkataravis@micron.com, dev.srinivasulu@gmail.com, linux-kernel@vger.kernel.org, nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org On 4/30/26 8:34 AM, John Groves wrote: > From: John Groves <John@Groves.net> > > - devdax <-> famfs mode switches > - Verify famfs -> system-ram is rejected (must go via devdax) > - Test JSON output shows correct mode > - Test error handling for invalid modes > > Signed-off-by: John Groves <john@groves.net> > --- > test/daxctl-famfs.sh | 253 +++++++++++++++++++++++++++++++++++++++++++ > test/meson.build | 2 + > 2 files changed, 255 insertions(+) > create mode 100755 test/daxctl-famfs.sh > > diff --git a/test/daxctl-famfs.sh b/test/daxctl-famfs.sh > new file mode 100755 > index 0000000..12fbfef > --- /dev/null > +++ b/test/daxctl-famfs.sh > @@ -0,0 +1,253 @@ > +#!/bin/bash -Ex > +# SPDX-License-Identifier: GPL-2.0 > +# Copyright (C) 2025 Micron Technology, Inc. All rights reserved. > +# > +# Test daxctl famfs mode transitions and mode detection > + > +rc=77 > +. $(dirname $0)/common > + > +trap 'cleanup $LINENO' ERR > + > +daxdev="" > +original_mode="" > + > +cleanup() > +{ > + printf "Error at line %d\n" "$1" > + # Try to restore to original mode if we know it > + if [[ $daxdev && $original_mode ]]; then > + "$DAXCTL" reconfigure-device -f -m "$original_mode" "$daxdev" 2>/dev/null || true > + fi > + exit $rc > +} > + > +# Check if fsdev_dax module is available > +check_fsdev_dax() > +{ > + if modinfo fsdev_dax &>/dev/null; then > + return 0 > + fi > + if grep -qF "fsdev_dax" "/lib/modules/$(uname -r)/modules.builtin" 2>/dev/null; then > + return 0 > + fi > + printf "fsdev_dax module not available, skipping\n" > + exit 77 > +} > + > +# Check if kmem module is available (needed for system-ram mode tests) > +check_kmem() > +{ > + if modinfo kmem &>/dev/null; then > + return 0 > + fi > + if grep -qF "kmem" "/lib/modules/$(uname -r)/modules.builtin" 2>/dev/null; then > + return 0 > + fi > + printf "kmem module not available, skipping system-ram tests\n" > + return 1 > +} > + > +# Find an existing dax device to test with > +find_daxdev() > +{ > + # Look for any available dax device > + daxdev=$("$DAXCTL" list | jq -er '.[0].chardev // empty' 2>/dev/null) || true > + > + if [[ ! $daxdev ]]; then > + printf "No dax device found, skipping\n" > + exit 77 Can you use 'do_skip' here? DJ > + fi > + > + # Save the original mode so we can restore it > + original_mode=$("$DAXCTL" list -d "$daxdev" | jq -er '.[].mode') > + > + printf "Found dax device: %s (current mode: %s)\n" "$daxdev" "$original_mode" > +} > + > +daxctl_get_mode() > +{ > + "$DAXCTL" list -d "$1" | jq -er '.[].mode' > +} > + > +# Ensure device is in devdax mode for testing > +ensure_devdax_mode() > +{ > + local mode > + mode=$(daxctl_get_mode "$daxdev") > + > + if [[ "$mode" == "devdax" ]]; then > + return 0 > + fi > + > + if [[ "$mode" == "system-ram" ]]; then > + printf "Device is in system-ram mode, attempting to convert to devdax...\n" > + "$DAXCTL" reconfigure-device -f -m devdax "$daxdev" > + elif [[ "$mode" == "famfs" ]]; then > + printf "Device is in famfs mode, converting to devdax...\n" > + "$DAXCTL" reconfigure-device -m devdax "$daxdev" > + else > + printf "Device is in unknown mode: %s\n" "$mode" > + return 1 > + fi > + > + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] > +} > + > +# > +# Test basic mode transitions involving famfs > +# > +test_famfs_mode_transitions() > +{ > + printf "\n=== Testing famfs mode transitions ===\n" > + > + # Ensure starting in devdax mode > + ensure_devdax_mode > + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] > + printf "Initial mode: devdax - OK\n" > + > + # Test: devdax -> famfs > + printf "Testing devdax -> famfs... " > + "$DAXCTL" reconfigure-device -m famfs "$daxdev" > + [[ $(daxctl_get_mode "$daxdev") == "famfs" ]] > + printf "OK\n" > + > + # Test: famfs -> famfs (re-enable in same mode) > + printf "Testing famfs -> famfs (re-enable)... " > + "$DAXCTL" reconfigure-device -m famfs "$daxdev" > + [[ $(daxctl_get_mode "$daxdev") == "famfs" ]] > + printf "OK\n" > + > + # Test: famfs -> devdax > + printf "Testing famfs -> devdax... " > + "$DAXCTL" reconfigure-device -m devdax "$daxdev" > + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] > + printf "OK\n" > + > + # Test: devdax -> devdax (re-enable in same mode) > + printf "Testing devdax -> devdax (re-enable)... " > + "$DAXCTL" reconfigure-device -m devdax "$daxdev" > + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] > + printf "OK\n" > +} > + > +# > +# Test mode transitions with system-ram (requires kmem) > +# > +test_system_ram_transitions() > +{ > + printf "\n=== Testing system-ram transitions with famfs ===\n" > + > + # Ensure we start in devdax mode > + ensure_devdax_mode > + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] > + > + # Test: devdax -> system-ram > + printf "Testing devdax -> system-ram... " > + "$DAXCTL" reconfigure-device -N -m system-ram "$daxdev" > + [[ $(daxctl_get_mode "$daxdev") == "system-ram" ]] > + printf "OK\n" > + > + # Test: system-ram -> famfs should fail > + printf "Testing system-ram -> famfs (should fail)... " > + if "$DAXCTL" reconfigure-device -m famfs "$daxdev" 2>/dev/null; then > + printf "FAILED - should have been rejected\n" > + return 1 > + fi > + printf "OK (correctly rejected)\n" > + > + # Test: system-ram -> devdax -> famfs (proper path) > + printf "Testing system-ram -> devdax -> famfs... " > + "$DAXCTL" reconfigure-device -f -m devdax "$daxdev" > + [[ $(daxctl_get_mode "$daxdev") == "devdax" ]] > + "$DAXCTL" reconfigure-device -m famfs "$daxdev" > + [[ $(daxctl_get_mode "$daxdev") == "famfs" ]] > + printf "OK\n" > + > + # Restore to devdax for subsequent tests > + "$DAXCTL" reconfigure-device -m devdax "$daxdev" > +} > + > +# > +# Test JSON output shows correct mode > +# > +test_json_output() > +{ > + printf "\n=== Testing JSON output for mode field ===\n" > + > + # Test devdax mode in JSON > + ensure_devdax_mode > + printf "Testing JSON output for devdax mode... " > + mode=$("$DAXCTL" list -d "$daxdev" | jq -er '.[].mode') > + [[ "$mode" == "devdax" ]] > + printf "OK\n" > + > + # Test famfs mode in JSON > + "$DAXCTL" reconfigure-device -m famfs "$daxdev" > + printf "Testing JSON output for famfs mode... " > + mode=$("$DAXCTL" list -d "$daxdev" | jq -er '.[].mode') > + [[ "$mode" == "famfs" ]] > + printf "OK\n" > + > + # Restore to devdax > + "$DAXCTL" reconfigure-device -m devdax "$daxdev" > +} > + > +# > +# Test error messages for invalid transitions > +# > +test_error_handling() > +{ > + printf "\n=== Testing error handling ===\n" > + > + # Ensure we're in famfs mode > + "$DAXCTL" reconfigure-device -m famfs "$daxdev" > + > + # Test that invalid mode is rejected > + printf "Testing invalid mode rejection... " > + if "$DAXCTL" reconfigure-device -m invalidmode "$daxdev" 2>/dev/null; then > + printf "FAILED - invalid mode should be rejected\n" > + return 1 > + fi > + printf "OK (correctly rejected)\n" > + > + # Restore to devdax > + "$DAXCTL" reconfigure-device -m devdax "$daxdev" > +} > + > +# > +# Main test sequence > +# > +main() > +{ > + check_fsdev_dax > + find_daxdev > + > + rc=1 # From here on, failures are real failures > + > + test_famfs_mode_transitions > + test_json_output > + test_error_handling > + > + # System-ram tests require kmem module > + if check_kmem; then > + # Save and disable online policy for system-ram tests > + saved_policy="$(cat /sys/devices/system/memory/auto_online_blocks)" > + echo "offline" > /sys/devices/system/memory/auto_online_blocks > + > + test_system_ram_transitions > + > + # Restore online policy > + echo "$saved_policy" > /sys/devices/system/memory/auto_online_blocks > + fi > + > + # Restore original mode > + printf "\nRestoring device to original mode: %s\n" "$original_mode" > + "$DAXCTL" reconfigure-device -f -m "$original_mode" "$daxdev" > + > + printf "\n=== All famfs tests passed ===\n" > + > + exit 0 > +} > + > +main > diff --git a/test/meson.build b/test/meson.build > index 8a3718d..5b75c07 100644 > --- a/test/meson.build > +++ b/test/meson.build > @@ -213,6 +213,7 @@ if get_option('destructive').enabled() > device_dax_fio = find_program('device-dax-fio.sh') > daxctl_devices = find_program('daxctl-devices.sh') > daxctl_create = find_program('daxctl-create.sh') > + daxctl_famfs = find_program('daxctl-famfs.sh') > dm = find_program('dm.sh') > mmap_test = find_program('mmap.sh') > > @@ -230,6 +231,7 @@ if get_option('destructive').enabled() > [ 'device-dax-fio.sh', device_dax_fio, 'dax' ], > [ 'daxctl-devices.sh', daxctl_devices, 'dax' ], > [ 'daxctl-create.sh', daxctl_create, 'dax' ], > + [ 'daxctl-famfs.sh', daxctl_famfs, 'dax' ], > [ 'dm.sh', dm, 'dax' ], > [ 'mmap.sh', mmap_test, 'dax' ], > ] ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH V5 2/2] Add test/daxctl-famfs.sh to test famfs mode transitions: 2026-04-30 16:27 ` Dave Jiang @ 2026-04-30 16:50 ` John Groves 0 siblings, 0 replies; 6+ messages in thread From: John Groves @ 2026-04-30 16:50 UTC (permalink / raw) To: Dave Jiang, John Groves, John Groves, Dan Williams, Alison Schofield Cc: John Groves, Vishal Verma, Jonathan Cameron, Aravind Ramesh, Ajay Joshi, venkataravis@micron.com, dev.srinivasulu@gmail.com, linux-kernel@vger.kernel.org, nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org On Thu, Apr 30, 2026, at 11:27 AM, Dave Jiang wrote: > > > On 4/30/26 8:34 AM, John Groves wrote: > > From: John Groves <John@Groves.net> > > > > - devdax <-> famfs mode switches > > - Verify famfs -> system-ram is rejected (must go via devdax) > > - Test JSON output shows correct mode > > - Test error handling for invalid modes > > > > Signed-off-by: John Groves <john@groves.net> > > --- > > test/daxctl-famfs.sh | 253 +++++++++++++++++++++++++++++++++++++++++++ > > test/meson.build | 2 + > > 2 files changed, 255 insertions(+) > > create mode 100755 test/daxctl-famfs.sh > > > > diff --git a/test/daxctl-famfs.sh b/test/daxctl-famfs.sh > > new file mode 100755 > > index 0000000..12fbfef > > --- /dev/null > > +++ b/test/daxctl-famfs.sh > > @@ -0,0 +1,253 @@ > > +#!/bin/bash -Ex > > +# SPDX-License-Identifier: GPL-2.0 > > +# Copyright (C) 2025 Micron Technology, Inc. All rights reserved. > > +# > > +# Test daxctl famfs mode transitions and mode detection > > + > > +rc=77 > > +. $(dirname $0)/common > > + > > +trap 'cleanup $LINENO' ERR > > + > > +daxdev="" > > +original_mode="" > > + > > +cleanup() > > +{ > > + printf "Error at line %d\n" "$1" > > + # Try to restore to original mode if we know it > > + if [[ $daxdev && $original_mode ]]; then > > + "$DAXCTL" reconfigure-device -f -m "$original_mode" "$daxdev" 2>/dev/null || true > > + fi > > + exit $rc > > +} > > + > > +# Check if fsdev_dax module is available > > +check_fsdev_dax() > > +{ > > + if modinfo fsdev_dax &>/dev/null; then > > + return 0 > > + fi > > + if grep -qF "fsdev_dax" "/lib/modules/$(uname -r)/modules.builtin" 2>/dev/null; then > > + return 0 > > + fi > > + printf "fsdev_dax module not available, skipping\n" > > + exit 77 > > +} > > + > > +# Check if kmem module is available (needed for system-ram mode tests) > > +check_kmem() > > +{ > > + if modinfo kmem &>/dev/null; then > > + return 0 > > + fi > > + if grep -qF "kmem" "/lib/modules/$(uname -r)/modules.builtin" 2>/dev/null; then > > + return 0 > > + fi > > + printf "kmem module not available, skipping system-ram tests\n" > > + return 1 > > +} > > + > > +# Find an existing dax device to test with > > +find_daxdev() > > +{ > > + # Look for any available dax device > > + daxdev=$("$DAXCTL" list | jq -er '.[0].chardev // empty' 2>/dev/null) || true > > + > > + if [[ ! $daxdev ]]; then > > + printf "No dax device found, skipping\n" > > + exit 77 > > Can you use 'do_skip' here? > > DJ Yes! Done in 2 places... <snip> Thanks, John ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-04-30 16:51 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260430153331.84139-1-john@jagalactic.com>
2026-04-30 15:33 ` [PATCH V5 0/2] daxctl: Add support for famfs mode John Groves
2026-04-30 15:34 ` [PATCH V5 1/2] " John Groves
2026-04-30 16:20 ` Dave Jiang
2026-04-30 15:34 ` [PATCH V5 2/2] Add test/daxctl-famfs.sh to test famfs mode transitions: John Groves
2026-04-30 16:27 ` Dave Jiang
2026-04-30 16:50 ` John Groves
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox