From nobody Sat Feb 7 17:48:34 2026 Received: from mail-qv1-f41.google.com (mail-qv1-f41.google.com [209.85.219.41]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1B52632863E for ; Wed, 14 Jan 2026 23:32:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.41 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768433549; cv=none; b=tsAvAbHgbE0NGc3g+WzSt0nok4b13vhTyGi2hv7YxUhAiEGFWRY6zs5jiiznL9hlCE5HKcD5/v/qMTiedtw49WgVFL5lkLDc9XxyxO/nwwjqoQHUGlo9/rs/XF3l5DAe1XQk4fwu8nkGuYm3HdOTn+5VM8OYyXVplHm4qspjBf0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768433549; c=relaxed/simple; bh=r9IqegKKXF0hOodqnrc7EmAalu0NN7c//lGhFFS/Wr4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=uX9eDy0+qeRfb1FGpEkRQoprzp6amSZrq4tVEJOmldGVsANvm49ac2AC2o3i3trxo00twE/fB0tPD3y5XZ7f9rVRhVeWcZ28YJVzX5QGBQiE6dV+Lt/TeP96Mhs73rz4AMxJz1a+zK1MvK8bjUkfbi/LZKCDaSiAnxsiaAnbznw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=Groves.net; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=XaH22aEq; arc=none smtp.client-ip=209.85.219.41 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=Groves.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="XaH22aEq" Received: by mail-qv1-f41.google.com with SMTP id 6a1803df08f44-88a367a1dbbso5112346d6.0 for ; Wed, 14 Jan 2026 15:32:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1768433528; x=1769038328; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:from:to:cc:subject:date :message-id:reply-to; bh=dJ1yqTOSIYfzgCIPomxp4mh5lXIFW6Vqr0XKillgvQc=; b=XaH22aEqI4tcQDTO0qMUY04OYdb2BUQU+g1rmPYPlfgmAoVPvB+RNiHw0tKtxjFutK urvh+wmGEHkghIy4fV5fF0kZ++ZTWMIdHef3lyhg/Wg2yimxQPcbNemXItKOcUYP2En5 Z6naH6v07YqN5FqBYaUa/txb1c5jYRdh6uskww6GHhXk5SSCHYTuFkD/g9zZtCNyVSq+ gLKSlXTHNwQbvC7kvAsk1Jyts7/j0s4IJ5ZUeq6NX+7WyblGoAWyfSU7KYASKi3DZj+l KHAfe8e/p+x501C/KmlxdOzRsfLNaJNpfUEIvZk0BB76yazOuL+sztBjlfEZEPpBQ1p2 Gm+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768433528; x=1769038328; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=dJ1yqTOSIYfzgCIPomxp4mh5lXIFW6Vqr0XKillgvQc=; b=re4lrZSgHKqRthOwB5XP5m5lM2rAQb8IlbOglqkHwBYZoYY3DkbUqNEc6z5J/y91NZ oGsH/yZBujqpJizcEvUbgtq9pcJWfTeqngp6MzaB8wq/gDKK8zj/E+YR7Oj2mVPD/BPB 9xlw2wnpbmtMsDygTKBkYrvBKwDbtrJYiRXRwYvWAjD7yAe9QJg/w4Msk5DBOZ8rVxoz rmLcj0YUDwR4leZXPzhnkhz62gZqXDjr3gtftlPR2uIWkJ8SIN+1vF7lpKvNIXJ+O6L6 Xcc4+GakK/FqHd9oyB2vuPuhPEmV8QAI0sJMApVzdJB14ov1Oh25hC4sNJ4Lk9+L/xld BfwQ== X-Forwarded-Encrypted: i=1; AJvYcCVSGnX+Dsm3TC8DSKADtzmq3QgCFzMPPhAjYXVJ7k2v1DT/ycXalu2ZKTPYV0e+Twn5/wv0xhlrLgC97tQ=@vger.kernel.org X-Gm-Message-State: AOJu0YzY9gHN+PRkFfjY71vWj8XFCEKDT+1Hnv9sz3lPZwtcK3OEM/Dv yQbMW4p729pBK0DyrrvHUHSZ4RMILc0bdRwMh34AnqvxJhepe1LOP+rynH3F4Q== X-Gm-Gg: AY/fxX5pbNpqMO/UB/Kcpj6q+TjD/DbDDxzTVxk0AdZTxkO1UnMJp2Ol9K/Wub8oEjt O9MDE3Q3x4pM33mrPCgeGQl2lmrSZkydBDYNKQzqzlvs4kZYk9kOMuzXCuxpA3ypN91uS7zRt82 l0Ad8yq51ayxyrolqEMS9Dur2kX8i9pqTjCZgFBmGzONT2locaxBWv5iaFjTTdIwsY1NIWJS+K4 9ZYe0h/W4qrPPZQ0mDcKLQZp5A+/htyjualPF3bxCzLh68OUzrAmYHTBRGBlSsIbKmv7t3I8kqW idlLSnTwPloJAFT+xirYZFuwonUssJLcIjVH5VeobTc+AGCyCVtOgZ1zK5+N4eKg7+ThKshLIlS OqphCwDzADIyZSJIkf1zHccbhhWVra+ZzhH00T6gzxzck+EC5hfkXtMRtLiAAzo068FP/ihsKcm OYN8PcJFxLQisfr5v7d74cB1w+UQDuj0dOQrjL7jDBLo0l X-Received: by 2002:a05:6820:2224:b0:65f:6582:6b23 with SMTP id 006d021491bc7-661006caf9dmr3084461eaf.38.1768427154787; Wed, 14 Jan 2026 13:45:54 -0800 (PST) Received: from localhost.localdomain ([2603:8080:1500:3d89:4c85:2962:e438:72c4]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-65f48cb03d4sm10935674eaf.12.2026.01.14.13.45.52 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 14 Jan 2026 13:45:54 -0800 (PST) Sender: John Groves From: John Groves X-Google-Original-From: John Groves To: John Groves , Miklos Szeredi , Dan Williams , Bernd Schubert , Alison Schofield Cc: John Groves , Jonathan Corbet , Vishal Verma , Dave Jiang , Matthew Wilcox , Jan Kara , Alexander Viro , David Hildenbrand , Christian Brauner , "Darrick J . Wong" , Randy Dunlap , Jeff Layton , Amir Goldstein , Jonathan Cameron , Stefan Hajnoczi , Joanne Koong , Josef Bacik , Bagas Sanjaya , James Morse , Fuad Tabba , Sean Christopherson , Shivank Garg , Ackerley Tng , Gregory Price , Aravind Ramesh , Ajay Joshi , venkataravis@micron.com, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org, linux-fsdevel@vger.kernel.org Subject: [PATCH V2 1/2] daxctl: Add support for famfs mode Date: Wed, 14 Jan 2026 15:45:18 -0600 Message-ID: <20260114214519.29999-2-john@groves.net> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114214519.29999-1-john@groves.net> References: <20260114153133.29420.compound@groves.net> <20260114214519.29999-1-john@groves.net> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: John Groves 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. The test is added to the destructive test suite since it modifies device modes. 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 Fix 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 now explicitly check each mode - Handle unknown mode with error instead of wrong assumption Modify json.c to show 'unknown' if device is not in a recognized mode. Signed-off-by: John Groves --- daxctl/device.c | 126 ++++++++++++++++++++++++++++++--- daxctl/json.c | 6 +- daxctl/lib/libdaxctl-private.h | 2 + daxctl/lib/libdaxctl.c | 77 ++++++++++++++++++++ daxctl/lib/libdaxctl.sym | 7 ++ daxctl/libdaxctl.h | 3 + 6 files changed, 210 insertions(+), 11 deletions(-) diff --git a/daxctl/device.c b/daxctl/device.c index e3993b1..14e1796 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, }; =20 struct mapping { @@ -471,6 +472,13 @@ static const char *parse_device_options(int argc, cons= t char **argv, "--no-online is incompatible with --mode=3Ddevdax\n"); rc =3D -EINVAL; } + } else if (strcmp(param.mode, "famfs") =3D=3D 0) { + reconfig_mode =3D DAXCTL_DEV_MODE_FAMFS; + if (param.no_online) { + fprintf(stderr, + "--no-online is incompatible with --mode=3Dfamfs\n"); + rc =3D -EINVAL; + } } break; case ACTION_CREATE: @@ -696,8 +704,42 @@ static int disable_devdax_device(struct daxctl_dev *de= v) int rc; =20 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 =3D 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 =3D daxctl_dev_get_memory(dev); + const char *devname =3D 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 =3D daxctl_dev_disable(dev); @@ -711,6 +753,7 @@ static int disable_devdax_device(struct daxctl_dev *dev) =20 static int reconfig_mode_system_ram(struct daxctl_dev *dev) { + struct daxctl_memory *mem =3D daxctl_dev_get_memory(dev); const char *devname =3D daxctl_dev_get_devname(dev); int rc, skip_enable =3D 0; =20 @@ -724,11 +767,21 @@ static int reconfig_mode_system_ram(struct daxctl_dev= *dev) } =20 if (daxctl_dev_is_enabled(dev)) { - rc =3D disable_devdax_device(dev); - if (rc < 0) - return rc; - if (rc > 0) + if (mem) { + /* already in system-ram mode */ skip_enable =3D 1; + } else if (daxctl_dev_is_famfs_mode(dev)) { + rc =3D disable_famfs_device(dev); + if (rc) + return rc; + } else if (daxctl_dev_is_devdax_mode(dev)) { + rc =3D disable_devdax_device(dev); + if (rc) + return rc; + } else { + fprintf(stderr, "%s: unknown mode\n", devname); + return -EINVAL; + } } =20 if (!skip_enable) { @@ -750,7 +803,7 @@ static int disable_system_ram_device(struct daxctl_dev = *dev) int rc; =20 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; } =20 @@ -786,12 +839,28 @@ static int disable_system_ram_device(struct daxctl_de= v *dev) =20 static int reconfig_mode_devdax(struct daxctl_dev *dev) { + struct daxctl_memory *mem =3D daxctl_dev_get_memory(dev); + const char *devname =3D daxctl_dev_get_devname(dev); int rc; =20 if (daxctl_dev_is_enabled(dev)) { - rc =3D disable_system_ram_device(dev); - if (rc) - return rc; + if (mem) { + rc =3D disable_system_ram_device(dev); + if (rc) + return rc; + } else if (daxctl_dev_is_famfs_mode(dev)) { + rc =3D disable_famfs_device(dev); + if (rc) + return rc; + } else if (daxctl_dev_is_devdax_mode(dev)) { + /* already in devdax mode, just re-enable */ + rc =3D daxctl_dev_disable(dev); + if (rc) + return rc; + } else { + fprintf(stderr, "%s: unknown mode\n", devname); + return -EINVAL; + } } =20 rc =3D daxctl_dev_enable_devdax(dev); @@ -801,6 +870,40 @@ static int reconfig_mode_devdax(struct daxctl_dev *dev) return 0; } =20 +static int reconfig_mode_famfs(struct daxctl_dev *dev) +{ + struct daxctl_memory *mem =3D daxctl_dev_get_memory(dev); + const char *devname =3D 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 =3D daxctl_dev_disable(dev); + if (rc) + return rc; + } else if (daxctl_dev_is_devdax_mode(dev)) { + rc =3D disable_devdax_device(dev); + if (rc) + return rc; + } else { + fprintf(stderr, "%s: unknown mode\n", devname); + return -EINVAL; + } + } + + rc =3D 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 +990,9 @@ static int do_reconfig(struct daxctl_dev *dev, enum dev= _mode mode, case DAXCTL_DEV_MODE_DEVDAX: rc =3D reconfig_mode_devdax(dev); break; + case DAXCTL_DEV_MODE_FAMFS: + rc =3D 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..01f139b 100644 --- a/daxctl/json.c +++ b/daxctl/json.c @@ -48,8 +48,12 @@ struct json_object *util_daxctl_dev_to_json(struct daxct= l_dev *dev, =20 if (mem) jobj =3D json_object_new_string("system-ram"); - else + else if (daxctl_dev_is_famfs_mode(dev)) + jobj =3D json_object_new_string("famfs"); + else if (daxctl_dev_is_devdax_mode(dev)) jobj =3D json_object_new_string("devdax"); + else + jobj =3D json_object_new_string("unknown"); if (jobj) json_object_object_add(jdev, "mode", jobj); =20 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[] =3D { enum daxctl_dev_mode { DAXCTL_DEV_MODE_DEVDAX =3D 0, DAXCTL_DEV_MODE_RAM, + DAXCTL_DEV_MODE_FAMFS, DAXCTL_DEV_MODE_END, }; =20 static const char *dax_modules[] =3D { [DAXCTL_DEV_MODE_DEVDAX] =3D "device_dax", [DAXCTL_DEV_MODE_RAM] =3D "kmem", + [DAXCTL_DEV_MODE_FAMFS] =3D "fsdev_dax", }; =20 enum memory_op { diff --git a/daxctl/lib/libdaxctl.c b/daxctl/lib/libdaxctl.c index b7fa0de..0a6cbfe 100644 --- a/daxctl/lib/libdaxctl.c +++ b/daxctl/lib/libdaxctl.c @@ -418,6 +418,78 @@ DAXCTL_EXPORT int daxctl_dev_is_system_ram_capable(str= uct daxctl_dev *dev) return false; } =20 +/* + * Check if device is currently in famfs mode (bound to fsdev_dax driver) + */ +DAXCTL_EXPORT int daxctl_dev_is_famfs_mode(struct daxctl_dev *dev) +{ + const char *devname =3D daxctl_dev_get_devname(dev); + struct daxctl_ctx *ctx =3D daxctl_dev_get_ctx(dev); + char *mod_path, *mod_base; + char path[200]; + const int len =3D sizeof(path); + + if (!device_model_is_dax_bus(dev)) + return false; + + if (!daxctl_dev_is_enabled(dev)) + return false; + + if (snprintf(path, len, "%s/driver", dev->dev_path) >=3D len) { + err(ctx, "%s: buffer too small!\n", devname); + return false; + } + + mod_path =3D realpath(path, NULL); + if (!mod_path) + return false; + + mod_base =3D basename(mod_path); + if (strcmp(mod_base, dax_modules[DAXCTL_DEV_MODE_FAMFS]) =3D=3D 0) { + free(mod_path); + return true; + } + + free(mod_path); + return false; +} + +/* + * Check if device is currently in devdax mode (bound to device_dax driver) + */ +DAXCTL_EXPORT int daxctl_dev_is_devdax_mode(struct daxctl_dev *dev) +{ + const char *devname =3D daxctl_dev_get_devname(dev); + struct daxctl_ctx *ctx =3D daxctl_dev_get_ctx(dev); + char *mod_path, *mod_base; + char path[200]; + const int len =3D sizeof(path); + + if (!device_model_is_dax_bus(dev)) + return false; + + if (!daxctl_dev_is_enabled(dev)) + return false; + + if (snprintf(path, len, "%s/driver", dev->dev_path) >=3D len) { + err(ctx, "%s: buffer too small!\n", devname); + return false; + } + + mod_path =3D realpath(path, NULL); + if (!mod_path) + return false; + + mod_base =3D basename(mod_path); + if (strcmp(mod_base, dax_modules[DAXCTL_DEV_MODE_DEVDAX]) =3D=3D 0) { + free(mod_path); + return true; + } + + free(mod_path); + return false; +} + /* * 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. @@ -982,6 +1054,11 @@ DAXCTL_EXPORT int daxctl_dev_enable_ram(struct daxctl= _dev *dev) return daxctl_dev_enable(dev, DAXCTL_DEV_MODE_RAM); } =20 +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 =3D 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); =20 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); --=20 2.49.0 From nobody Sat Feb 7 17:48:34 2026 Received: from mail-ot1-f47.google.com (mail-ot1-f47.google.com [209.85.210.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6D7FF3A0B33 for ; Wed, 14 Jan 2026 21:46:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.47 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768427220; cv=none; b=YwAqFHCqO3x7CvJAW8rWI9LBB0YBte8PpYLIqMk7CGjwSbV7EvhAH9T7CzTfo9lNLsQSqIGske0xw2FaI6OFNGlBIk/esCttqDZWZK3KIH+z4Sd2Jx+9fTxQvRfYXo83KMuktkejZkH2I5CDeXyW/lo9MuOOQ3mJaa/Yu9QHHNo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768427220; c=relaxed/simple; bh=UH7UVmm/ifZMz2a7ctzEVAj9QL1YqJfv9TEsJAmqvl0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=t7tm8PwlfTor+FLdes50WuGeXm7T7cjhxNisfR+xamCR6RRFkl+DFsrsEyY9ZZqeOXYtUgpHUTrtQ/wu2As5si22+xEa61iWXKYFNoMfhcpwj3ZRSSnc7pZ78GY23qo1xJDCaqQ65jSKMYjUTwqKv/Nu3O0pD6pMjy0bI+xkDU4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=Groves.net; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=U1x22Y2N; arc=none smtp.client-ip=209.85.210.47 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=Groves.net Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="U1x22Y2N" Received: by mail-ot1-f47.google.com with SMTP id 46e09a7af769-7cdf7529bb2so174988a34.2 for ; Wed, 14 Jan 2026 13:46:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1768427188; x=1769031988; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:from:to:cc:subject:date :message-id:reply-to; bh=ezmCIUYTCpc47N9MRFviC1UdzpKfkRUPsUPtXb573Uk=; b=U1x22Y2NCadasD+0/b0bPw8Jn26D+SpJIsuZ4rfvGJuun0FUtpy0yEVTNC3SEZP49O kQVrOW6VEUFevmJ8Tfwfloc26DmUxyJoZ481dP5SY8Vnr0yaGCBUcA9VTM7MT7dob2CR oiiiZipKAFt6EypCdSmvuGhN7YU6+rNC/Omt2OxUb6Bpqs/brbeXxwniyW2pSoKXlr1j 5Nmyyb9fjC0sh1XqduP4U+K+mNhMYvlCQ/YkkA6Ud9AjV5bJHdFrgw5fg/85+1DcnwsH sECOWV2MlG2C9LRm5loBKJOi81lh/7zVpq8T+4On6fvrdPc3x9pbcjsq464wKeTHGJhD hpWw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768427188; x=1769031988; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:sender:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=ezmCIUYTCpc47N9MRFviC1UdzpKfkRUPsUPtXb573Uk=; b=n0Gfx80qrI5MgVbnWTgY+3rcLPnuwVQ5YibQxHePKP9sBw8k8mfn0Dan5N5bpr315l nA1OHsf5J5IOpGPFMOLV0YdHlHxb3mW1yZrypJdceKrI71amnPBw2wCgl1uQS+ofUPvB 3ckTeUsvRwYrTqPhJpSirWheXmWHHgHpo40HGP84iQD5MgRJ3bPUoc7sbWp/+yfmGi4I vEFcAInMQKNg+7v6uTTajwSnfx7xIo5vi9QoGO7CMIcVw1XG0xK1cUOqaGY1j6eErd3X iRwOf5rC8mpogd349hkUeMRw0WE1X1SzliP+eTCyOdRV8wZbeUxdn4mbM7pAc5TFJRMH Q4lQ== X-Forwarded-Encrypted: i=1; AJvYcCUatklgyDfaK8GazQ1NL9qreN9S3XW/lKUxUeIamavWKnD8/Hyd5RuMOLebT0DGVQydNbVzN1KlkeyPcgg=@vger.kernel.org X-Gm-Message-State: AOJu0YyPDJMGlFG9pz+mGxHIXCxIrh2GiOBHYkTDHwXSAeCzU7y109OA xEiqY5TE0C+1uEDM0QNPrIRQLjVSDWcbPKDoGPKlI4DqWhKLjOXEmLWN X-Gm-Gg: AY/fxX6/+0yZbJspNjSEtBkBgay3FerFHZK4grrSzr7VriLKgXju4YEBNtXCcDTMPff xejZ4PCUthyfh4Eaq1nWkxdNxSkaSP5uFz/jUGmpoTRWu9toaGNM1Vv7V57VRgcu/ke5SIBGvjg SfAK+jUFEApDwgyWEgdTWiMyrSniqhdOZY+gxkcemlhqyobploM1v52WTlzyu+kwAo79Qqr7axd hLILylN86Px4YakQZirbhT/kdGrewLWCP/WdZUzCVQ1XxInQ3/Ixvq1CFrLhj0aXX7Z3DfbvUbk x5rzSo+iyZExd6IOhwSFR5V3hlJG6YDGPG4Ls2j1rAOD9lADGUuNSYRquUYBe34vTY15B/nNIyR bchBL2cyhFWzL6EKUSWmKtogC842uk2kqDzAZcKku7s+HHrZWM06jcqXApQjtvaZyjQnukKCb43 ETpWgi9wI1cSG3c2Olxc7SpLI1tXbNcZWGHbzP9o7RCcSG X-Received: by 2002:a05:6830:2541:b0:7cf:cc11:f7cc with SMTP id 46e09a7af769-7cfcc11f982mr2409830a34.36.1768427188084; Wed, 14 Jan 2026 13:46:28 -0800 (PST) Received: from localhost.localdomain ([2603:8080:1500:3d89:4c85:2962:e438:72c4]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7cfc3692f3asm3936197a34.10.2026.01.14.13.46.25 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 14 Jan 2026 13:46:27 -0800 (PST) Sender: John Groves From: John Groves X-Google-Original-From: John Groves To: John Groves , Miklos Szeredi , Dan Williams , Bernd Schubert , Alison Schofield Cc: John Groves , Jonathan Corbet , Vishal Verma , Dave Jiang , Matthew Wilcox , Jan Kara , Alexander Viro , David Hildenbrand , Christian Brauner , "Darrick J . Wong" , Randy Dunlap , Jeff Layton , Amir Goldstein , Jonathan Cameron , Stefan Hajnoczi , Joanne Koong , Josef Bacik , Bagas Sanjaya , James Morse , Fuad Tabba , Sean Christopherson , Shivank Garg , Ackerley Tng , Gregory Price , Aravind Ramesh , Ajay Joshi , venkataravis@micron.com, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, nvdimm@lists.linux.dev, linux-cxl@vger.kernel.org, linux-fsdevel@vger.kernel.org Subject: [PATCH V2 2/2] Add test/daxctl-famfs.sh to test famfs mode transitions: Date: Wed, 14 Jan 2026 15:45:19 -0600 Message-ID: <20260114214519.29999-3-john@groves.net> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20260114214519.29999-1-john@groves.net> References: <20260114153133.29420.compound@groves.net> <20260114214519.29999-1-john@groves.net> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: John Groves - 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 The test is added to the destructive test suite since it modifies device modes. Signed-off-by: John Groves --- 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=3D77 +. $(dirname $0)/common + +trap 'cleanup $LINENO' ERR + +daxdev=3D"" +original_mode=3D"" + +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/nul= l || 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=3D$("$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=3D$("$DAXCTL" list -d "$daxdev" | jq -er '.[].mode') + + printf "Found dax device: %s (current mode: %s)\n" "$daxdev" "$original_m= ode" +} + +daxctl_get_mode() +{ + "$DAXCTL" list -d "$1" | jq -er '.[].mode' +} + +# Ensure device is in devdax mode for testing +ensure_devdax_mode() +{ + local mode + mode=3D$(daxctl_get_mode "$daxdev") + + if [[ "$mode" =3D=3D "devdax" ]]; then + return 0 + fi + + if [[ "$mode" =3D=3D "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" =3D=3D "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") =3D=3D "devdax" ]] +} + +# +# Test basic mode transitions involving famfs +# +test_famfs_mode_transitions() +{ + printf "\n=3D=3D=3D Testing famfs mode transitions =3D=3D=3D\n" + + # Ensure starting in devdax mode + ensure_devdax_mode + [[ $(daxctl_get_mode "$daxdev") =3D=3D "devdax" ]] + printf "Initial mode: devdax - OK\n" + + # Test: devdax -> famfs + printf "Testing devdax -> famfs... " + "$DAXCTL" reconfigure-device -m famfs "$daxdev" + [[ $(daxctl_get_mode "$daxdev") =3D=3D "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") =3D=3D "famfs" ]] + printf "OK\n" + + # Test: famfs -> devdax + printf "Testing famfs -> devdax... " + "$DAXCTL" reconfigure-device -m devdax "$daxdev" + [[ $(daxctl_get_mode "$daxdev") =3D=3D "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") =3D=3D "devdax" ]] + printf "OK\n" +} + +# +# Test mode transitions with system-ram (requires kmem) +# +test_system_ram_transitions() +{ + printf "\n=3D=3D=3D Testing system-ram transitions with famfs =3D=3D=3D\n" + + # Ensure we start in devdax mode + ensure_devdax_mode + [[ $(daxctl_get_mode "$daxdev") =3D=3D "devdax" ]] + + # Test: devdax -> system-ram + printf "Testing devdax -> system-ram... " + "$DAXCTL" reconfigure-device -N -m system-ram "$daxdev" + [[ $(daxctl_get_mode "$daxdev") =3D=3D "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") =3D=3D "devdax" ]] + "$DAXCTL" reconfigure-device -m famfs "$daxdev" + [[ $(daxctl_get_mode "$daxdev") =3D=3D "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=3D=3D=3D Testing JSON output for mode field =3D=3D=3D\n" + + # Test devdax mode in JSON + ensure_devdax_mode + printf "Testing JSON output for devdax mode... " + mode=3D$("$DAXCTL" list -d "$daxdev" | jq -er '.[].mode') + [[ "$mode" =3D=3D "devdax" ]] + printf "OK\n" + + # Test famfs mode in JSON + "$DAXCTL" reconfigure-device -m famfs "$daxdev" + printf "Testing JSON output for famfs mode... " + mode=3D$("$DAXCTL" list -d "$daxdev" | jq -er '.[].mode') + [[ "$mode" =3D=3D "famfs" ]] + printf "OK\n" + + # Restore to devdax + "$DAXCTL" reconfigure-device -m devdax "$daxdev" +} + +# +# Test error messages for invalid transitions +# +test_error_handling() +{ + printf "\n=3D=3D=3D Testing error handling =3D=3D=3D\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=3D1 # 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=3D"$(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=3D=3D=3D All famfs tests passed =3D=3D=3D\n" + + exit 0 +} + +main diff --git a/test/meson.build b/test/meson.build index 615376e..ad1d393 100644 --- a/test/meson.build +++ b/test/meson.build @@ -209,6 +209,7 @@ if get_option('destructive').enabled() device_dax_fio =3D find_program('device-dax-fio.sh') daxctl_devices =3D find_program('daxctl-devices.sh') daxctl_create =3D find_program('daxctl-create.sh') + daxctl_famfs =3D find_program('daxctl-famfs.sh') dm =3D find_program('dm.sh') mmap_test =3D find_program('mmap.sh') =20 @@ -226,6 +227,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' ], ] --=20 2.49.0