public inbox for lvm-devel@lists.linux.dev
 help / color / mirror / Atom feed
* [PATCH 0/4] lvm2: add btrfs support for lvresize
@ 2025-05-13  6:14 Su Yue
  2025-05-13  6:14 ` [PATCH 1/4] filesystem: get device uuid in fs_get_blkid Su Yue
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Su Yue @ 2025-05-13  6:14 UTC (permalink / raw)
  To: lvm-devel; +Cc: teigland, zkabelac, heming.zhao, glass.su, l

Hi,
  This patchset adds btrfs support for lvresize.
  I tried to split patches as small as minimal for review but 3rd is still
complicated for bisecting.
  The patchset passed shell/lvresize-xfs.sh and shell/lvresize-fs.sh on
CentOS stream, fedora 42 and archlinux to make sure that other fs resize
functionalties are not broken by the feature.

Su Yue (4):
  filesystem: get device uuid in fs_get_blkid
  filesystem: factor out get mount point logic from fs_get_info() into
    _fs_get_mnt()
  lvresize: add btrfs support
  test: addm shell/lvresize-btrfs.sh

 lib/device/dev-type.c         |  10 ++
 lib/device/filesystem.c       | 189 +++++++++++++++++++++-----
 lib/device/filesystem.h       |   2 +
 lib/metadata/lv_manip.c       |  73 ++++++----
 scripts/lvresize_fs_helper.sh | 110 +++++++++++++--
 test/shell/lvresize-btrfs.sh  | 244 ++++++++++++++++++++++++++++++++++
 6 files changed, 556 insertions(+), 72 deletions(-)
 create mode 100644 test/shell/lvresize-btrfs.sh

-- 
2.48.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/4] filesystem: get device uuid in fs_get_blkid
  2025-05-13  6:14 [PATCH 0/4] lvm2: add btrfs support for lvresize Su Yue
@ 2025-05-13  6:14 ` Su Yue
  2025-05-13  6:14 ` [PATCH 2/4] filesystem: factor out get mount point logic from fs_get_info() into _fs_get_mnt() Su Yue
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Su Yue @ 2025-05-13  6:14 UTC (permalink / raw)
  To: lvm-devel; +Cc: teigland, zkabelac, heming.zhao, glass.su, l

Add new field fs_info::uuid to record device uuid when calling
fs_get_blkid() for further use.

No functional change.

Signed-off-by: Su Yue <glass.su@suse.com>
---
 lib/device/dev-type.c   | 3 +++
 lib/device/filesystem.h | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/lib/device/dev-type.c b/lib/device/dev-type.c
index c4a366d0089d..08848ca0f216 100644
--- a/lib/device/dev-type.c
+++ b/lib/device/dev-type.c
@@ -999,6 +999,9 @@ int fs_get_blkid(const char *pathname, struct fs_info *fsi)
 	if (!blkid_probe_lookup_value(probe, "FSSIZE", &str, &len) && len)
 		fssize = strtoull(str, NULL, 0);
 
+	if (!blkid_probe_lookup_value(probe, "UUID", &str, &len) && len)
+		memcpy(fsi->uuid, str, UUID_LEN);
+
 	blkid_free_probe(probe);
 
 	if (fslastblock && fsblocksize)
diff --git a/lib/device/filesystem.h b/lib/device/filesystem.h
index 024c9974a19d..4ba6f0db99b7 100644
--- a/lib/device/filesystem.h
+++ b/lib/device/filesystem.h
@@ -19,10 +19,12 @@
 #include "lib/device/device.h"
 
 #define FSTYPE_MAX 16
+#define UUID_LEN 37
 
 struct fs_info {
 	char fstype[FSTYPE_MAX];
 	char mount_dir[PATH_MAX];
+	char uuid[UUID_LEN];
 	char fs_dev_path[PATH_MAX]; /* usually lv dev, can be crypt dev */
 	unsigned int fs_block_size_bytes; /* 512 or 4k */
 	uint64_t fs_last_byte; /* last byte on the device used by the fs */
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/4] filesystem: factor out get mount point logic from fs_get_info() into _fs_get_mnt()
  2025-05-13  6:14 [PATCH 0/4] lvm2: add btrfs support for lvresize Su Yue
  2025-05-13  6:14 ` [PATCH 1/4] filesystem: get device uuid in fs_get_blkid Su Yue
@ 2025-05-13  6:14 ` Su Yue
  2025-05-13  6:14 ` [PATCH 3/4] lvresize: add btrfs support Su Yue
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Su Yue @ 2025-05-13  6:14 UTC (permalink / raw)
  To: lvm-devel; +Cc: teigland, zkabelac, heming.zhao, glass.su, l

The new _fs_get_mnt() is used to get mount point info for fses.
No functional change.

Signed-off-by: Su Yue <glass.su@suse.com>
---
 lib/device/filesystem.c | 69 ++++++++++++++++++++++-------------------
 1 file changed, 37 insertions(+), 32 deletions(-)

diff --git a/lib/device/filesystem.c b/lib/device/filesystem.c
index 49771bddf326..9c865b25fb57 100644
--- a/lib/device/filesystem.c
+++ b/lib/device/filesystem.c
@@ -108,6 +108,42 @@ int lv_crypt_is_active(struct cmd_context *cmd, char *lv_path)
 	return _get_crypt_path(st_lv.st_rdev, lv_path, crypt_path);
 }
 
+static int _fs_get_mnt(struct fs_info *fsi, dev_t devt)
+{
+	struct stat stme;
+	FILE *fme = NULL;
+	struct mntent *me;
+
+	/*
+	 * Note: used swap devices are not considered as mount points,
+	 * hence they're not listed in /etc/mtab, we'd need to read the
+	 * /proc/swaps instead. We don't need it at this moment though,
+	 * but if we do once, read the /proc/swaps here if fsi->fstype == "swap".
+	 */
+	if (!(fme = setmntent("/etc/mtab", "r")))
+		return_0;
+
+	while ((me = getmntent(fme))) {
+		if (strcmp(me->mnt_type, fsi->fstype))
+			continue;
+		if (me->mnt_dir[0] != '/')
+			continue;
+		if (me->mnt_fsname[0] != '/')
+			continue;
+		if (stat(me->mnt_dir, &stme) < 0)
+			continue;
+		if (stme.st_dev != devt)
+			continue;
+
+		log_debug("fs_get_info %s is mounted \"%s\"", fsi->fs_dev_path, me->mnt_dir);
+		fsi->mounted = 1;
+		strncpy(fsi->mount_dir, me->mnt_dir, PATH_MAX-1);
+	}
+	endmntent(fme);
+
+	return 1;
+}
+
 int fs_get_info(struct cmd_context *cmd, struct logical_volume *lv,
 		struct fs_info *fsi, int include_mount)
 {
@@ -116,10 +152,7 @@ int fs_get_info(struct cmd_context *cmd, struct logical_volume *lv,
 	struct stat st_lv;
 	struct stat st_crypt;
 	struct stat st_top;
-	struct stat stme;
 	struct fs_info info;
-	FILE *fme = NULL;
-	struct mntent *me;
 	int fd;
 	int ret;
 
@@ -201,35 +234,7 @@ int fs_get_info(struct cmd_context *cmd, struct logical_volume *lv,
 	if (!include_mount)
 		return 1;
 
-	/*
-	 * Note: used swap devices are not considered as mount points,
-	 * hence they're not listed in /etc/mtab, we'd need to read the
-	 * /proc/swaps instead. We don't need it at this moment though,
-	 * but if we do once, read the /proc/swaps here if fsi->fstype == "swap".
-	 */
-
-	if (!(fme = setmntent("/etc/mtab", "r")))
-		return_0;
-
-	ret = 1;
-
-	while ((me = getmntent(fme))) {
-		if (strcmp(me->mnt_type, fsi->fstype))
-			continue;
-		if (me->mnt_dir[0] != '/')
-			continue;
-		if (me->mnt_fsname[0] != '/')
-			continue;
-		if (stat(me->mnt_dir, &stme) < 0)
-			continue;
-		if (stme.st_dev != st_top.st_rdev)
-			continue;
-
-		log_debug("fs_get_info %s is mounted \"%s\"", fsi->fs_dev_path, me->mnt_dir);
-		fsi->mounted = 1;
-		strncpy(fsi->mount_dir, me->mnt_dir, PATH_MAX-1);
-	}
-	endmntent(fme);
+	ret = _fs_get_mnt(fsi, st_top.st_rdev);
 
 	fsi->unmounted = !fsi->mounted;
 	return ret;
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/4] lvresize: add btrfs support
  2025-05-13  6:14 [PATCH 0/4] lvm2: add btrfs support for lvresize Su Yue
  2025-05-13  6:14 ` [PATCH 1/4] filesystem: get device uuid in fs_get_blkid Su Yue
  2025-05-13  6:14 ` [PATCH 2/4] filesystem: factor out get mount point logic from fs_get_info() into _fs_get_mnt() Su Yue
@ 2025-05-13  6:14 ` Su Yue
  2025-05-13  6:14 ` [PATCH 4/4] test: addm shell/lvresize-btrfs.sh Su Yue
  2025-05-13 17:35 ` [PATCH 0/4] lvm2: add btrfs support for lvresize David Teigland
  4 siblings, 0 replies; 6+ messages in thread
From: Su Yue @ 2025-05-13  6:14 UTC (permalink / raw)
  To: lvm-devel; +Cc: teigland, zkabelac, heming.zhao, glass.su, l

This commit adds lvresize/lvextend/lvreduce support for btrfs.
'btrfs filesystem resize [devid:][+/-]<newsize>[kKmMgGtTpPeE]|[devid:]max <path>'
is used to resize one device only when it's mounted.
The code pattern is like xfs but it supports shrink.

For multi-devices btrfs, There is one difficulty to be handled:

If `lvreduce --fs resize` is given, lvm2 will check newsize vs current fs size
to judge if it's need to shrink fs or not.
For one device btrfs, fslastblock * fsblocksize/FSSIZE is the correct value like
ext* and xfs. But for multi-devices btrfs, the two values are whole fs size.
There is no other way without relying btrfs superblock parse. It's too
complicated and inproper to implemnt the logic in lvm2.
So here just sets fs_last_byte to 0 for btrfs and skips boundary check in
_fs_reduce_allow(). It's safe as btrfs will handle it well.

The another complicated part is how to get mount point info if multi-devices.
There is only one mnt entry per mounted fs in /etc/mtab even it's a
multi-devices btrfs. So we first get uuid from lv device then traverse devices
under /sys/fs/btrfs/$uuid/devices and compare them to the mnt entry to get the
mount point.

Signed-off-by: Su Yue <glass.su@suse.com>
---
 lib/device/dev-type.c         |   7 ++
 lib/device/filesystem.c       | 132 ++++++++++++++++++++++++++++++++--
 lib/metadata/lv_manip.c       |  73 ++++++++++++-------
 scripts/lvresize_fs_helper.sh | 110 ++++++++++++++++++++++++----
 4 files changed, 276 insertions(+), 46 deletions(-)

diff --git a/lib/device/dev-type.c b/lib/device/dev-type.c
index 08848ca0f216..4c1040c9f055 100644
--- a/lib/device/dev-type.c
+++ b/lib/device/dev-type.c
@@ -1018,6 +1018,13 @@ int fs_get_blkid(const char *pathname, struct fs_info *fsi)
 			fsi->fs_last_byte += fsblocksize;
 
 	}
+	/*
+	 * For a multi-devices btrfs, fslastblock * fsblocksize means the whole fs size.
+	 * Thus here fs_last_byte can't be used as a device size boundary.
+	 * Let btrfs handle it.
+	 */
+	if (!strcmp(fsi->fstype, "btrfs"))
+		fsi->fs_last_byte = 0;
 
 	log_debug("libblkid TYPE %s BLOCK_SIZE %d FSLASTBLOCK %llu FSBLOCKSIZE %u fs_last_byte %llu",
 		  fsi->fstype, fsi->fs_block_size_bytes, (unsigned long long)fslastblock, fsblocksize,
diff --git a/lib/device/filesystem.c b/lib/device/filesystem.c
index 9c865b25fb57..e6aa8c570763 100644
--- a/lib/device/filesystem.c
+++ b/lib/device/filesystem.c
@@ -130,13 +130,25 @@ static int _fs_get_mnt(struct fs_info *fsi, dev_t devt)
 			continue;
 		if (me->mnt_fsname[0] != '/')
 			continue;
-		if (stat(me->mnt_dir, &stme) < 0)
-			continue;
-		if (stme.st_dev != devt)
-			continue;
+
+		/*
+		 * st_dev of mnt_dir in btrfs is an anonymous device number,
+		 * use mnt_fsname instead.
+		 */
+		if (!strcmp(fsi->fstype, "btrfs")) {
+			if (stat(me->mnt_fsname, &stme) < 0)
+				log_sys_error("stat", me->mnt_fsname);
+			if (stme.st_rdev != devt)
+				continue;
+		} else {
+			if (stat(me->mnt_dir, &stme) < 0)
+				continue;
+			if (stme.st_dev != devt)
+				continue;
+			fsi->mounted = 1;
+		}
 
 		log_debug("fs_get_info %s is mounted \"%s\"", fsi->fs_dev_path, me->mnt_dir);
-		fsi->mounted = 1;
 		strncpy(fsi->mount_dir, me->mnt_dir, PATH_MAX-1);
 	}
 	endmntent(fme);
@@ -144,6 +156,103 @@ static int _fs_get_mnt(struct fs_info *fsi, dev_t devt)
 	return 1;
 }
 
+static int _btrfs_get_mnt(struct fs_info *fsi, dev_t lv_devt)
+{
+	char devices_path[PATH_MAX];
+	char rdev_path[PATH_MAX];
+	unsigned major, minor;
+	dev_t devt;
+	char buffer[16];
+	char *device_name;
+	DIR *dr;
+	struct dirent *de;
+	int ret = 1;
+	int fd = -1;
+	int r;
+	bool found = false;
+
+	/* For a mounted btrfs, there will be a sys dir like /sys/fs/btrfs/$uuid/devices */
+	if (!dm_snprintf(devices_path, sizeof(devices_path), "%sfs/btrfs/%s/devices",
+			dm_sysfs_dir(), fsi->uuid)) {
+		log_error("Couldn't create btrfs devices path for %s.", fsi->fs_dev_path);
+		return 0;
+	}
+
+	/* btrfs module is not avaiable or the device is not mounted */
+	if (!(dr = opendir(devices_path))) {
+		if (errno == ENOENT) {
+			fsi->mounted = 0;
+			return 1;
+		}
+	}
+
+	/*
+	 * Here iterates entries under /sys/fs/btrfs/$uuid/devices and read devt.
+	 * There is only one mnt entry per mounted fs even it's a multi-devices fs.
+	 * So also call _fs_get_mnt for every devices to find a matched mount point.
+	 */
+	while ((de = readdir(dr))) {
+		if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+			continue;
+
+		device_name = de->d_name;
+
+		if (!dm_snprintf(rdev_path, sizeof(devices_path), "%s/%s/dev",
+				 devices_path, device_name)) {
+			    log_error("Couldn't create rdev path for %s.", fsi->fs_dev_path);
+			    ret = 0;
+			    break;
+		}
+
+		if ((fd = open(rdev_path, O_RDONLY)) < 0) {
+			log_sys_debug("open", rdev_path);
+			ret = 0;
+			break;
+		}
+
+		r = read(fd, buffer, sizeof(buffer));
+		if (r < 0) {
+			ret = 0;
+			close(fd);
+			log_sys_debug("read", rdev_path);
+			break;
+		}
+
+		buffer[r - 1] = 0;
+
+		if (sscanf(buffer, "%u:%u", &major, &minor) != 2) {
+			ret = 0;
+			log_sys_debug("sscanf", rdev_path);
+			break;
+		}
+
+		devt = MKDEV(major, minor);
+		if (devt == lv_devt)
+			found = true;
+
+		if (fsi->mount_dir[0] == 0)
+			_fs_get_mnt(fsi, devt);
+
+		if (fsi->mounted && fsi->mount_dir[0])
+			break;
+	}
+
+	if (fd >= 0)
+		close(fd);
+
+	if (closedir(dr))
+		log_sys_debug("closedir", devices_path);
+
+	fsi->mounted = !!found;
+
+	if (fsi->mounted && fsi->mount_dir[0] == 0) {
+		log_error("Couldn't get mount point for %s.", fsi->fs_dev_path);
+		ret = 0;
+	}
+
+	return ret;
+}
+
 int fs_get_info(struct cmd_context *cmd, struct logical_volume *lv,
 		struct fs_info *fsi, int include_mount)
 {
@@ -234,7 +343,10 @@ int fs_get_info(struct cmd_context *cmd, struct logical_volume *lv,
 	if (!include_mount)
 		return 1;
 
-	ret = _fs_get_mnt(fsi, st_top.st_rdev);
+	if (!strcmp(fsi->fstype, "btrfs"))
+		ret = _btrfs_get_mnt(fsi, st_lv.st_rdev);
+	else
+		ret = _fs_get_mnt(fsi, st_top.st_rdev);
 
 	fsi->unmounted = !fsi->mounted;
 	return ret;
@@ -525,11 +637,15 @@ int fs_extend_script(struct cmd_context *cmd, struct logical_volume *lv, struct
 {
 	char lv_path[PATH_MAX];
 	char crypt_path[PATH_MAX];
+	char newsize_str[16] = { 0 };
 	const char *argv[FS_CMD_MAX_ARGS + 4];
 	char *devpath;
 	int args = 0;
 	int status;
 
+	if (dm_snprintf(newsize_str, sizeof(newsize_str), "%llu", (unsigned long long)fsi->new_size_bytes) < 0)
+		return_0;
+
 	if (dm_snprintf(lv_path, PATH_MAX, "%s%s/%s", lv->vg->cmd->dev_dir, lv->vg->name, lv->name) < 0)
 		return_0;
 
@@ -540,6 +656,10 @@ int fs_extend_script(struct cmd_context *cmd, struct logical_volume *lv, struct
 	argv[++args] = "--lvpath";
 	argv[++args] = lv_path;
 
+	if (fsi->new_size_bytes) {
+		argv[++args] = "--newsizebytes";
+		argv[++args] = newsize_str;
+	}
 	if (fsi->mounted) {
 		argv[++args] = "--mountdir";
 		argv[++args] = fsi->mount_dir;
diff --git a/lib/metadata/lv_manip.c b/lib/metadata/lv_manip.c
index b188ea28055f..193b1f4038ca 100644
--- a/lib/metadata/lv_manip.c
+++ b/lib/metadata/lv_manip.c
@@ -6071,6 +6071,7 @@ static int _fs_reduce_allow(struct cmd_context *cmd, struct logical_volume *lv,
 	int equal = 0, smaller = 0, larger = 0;
 	int is_ext_fstype = 0;
 	int confirm_mount_change = 0;
+	int check_boundary = 1;
 
 	/*
 	 * Allow reducing the LV for other fs types if the fs is not using
@@ -6079,7 +6080,8 @@ static int _fs_reduce_allow(struct cmd_context *cmd, struct logical_volume *lv,
 	if (!strcmp(fsi->fstype, "ext2") ||
 	    !strcmp(fsi->fstype, "ext3") ||
 	    !strcmp(fsi->fstype, "ext4") ||
-	    !strcmp(fsi->fstype, "xfs")) {
+	    !strcmp(fsi->fstype, "xfs") ||
+	    !strcmp(fsi->fstype, "btrfs")) {
 		log_debug("Found fs %s last_byte %llu new_size_bytes %llu",
 			  fsi->fstype,
 			  (unsigned long long)fsi->fs_last_byte,
@@ -6087,6 +6089,9 @@ static int _fs_reduce_allow(struct cmd_context *cmd, struct logical_volume *lv,
 		if (!strncmp(fsi->fstype, "ext", 3)) {
 			is_ext_fstype = 1;
 			fs_reduce_cmd = " resize2fs";
+		} else if (!strncmp(fsi->fstype, "btrfs", 5)) {
+			/* let btrfs handle it */
+			check_boundary = 0;
 		}
 	}
 
@@ -6103,29 +6108,34 @@ static int _fs_reduce_allow(struct cmd_context *cmd, struct logical_volume *lv,
 		if (!strcmp(fsi->fstype, "reiserfs")) {
 			log_error("File system reduce for reiserfs requires --fs resize_fsadm.");
 			return 0;
+		} else if (!strcmp(fsi->fstype, "btrfs")) {
+			log_print_unless_silent("Skipping check used device size for btrfs.");
+		} else {
+			log_error("File system device usage is not available from libblkid.");
+			return 0;
 		}
-		log_error("File system device usage is not available from libblkid.");
-		return 0;
 	}
 
-	if ((equal = (fsi->fs_last_byte == fsi->new_size_bytes)))
-		cmp_desc = "equal to";
-	else if ((smaller = (fsi->fs_last_byte < fsi->new_size_bytes)))
-		cmp_desc = "smaller than";
-	else if ((larger = (fsi->fs_last_byte > fsi->new_size_bytes)))
-		cmp_desc = "larger than";
+	if (check_boundary) {
+		if ((equal = (fsi->fs_last_byte == fsi->new_size_bytes)))
+			cmp_desc = "equal to";
+		else if ((smaller = (fsi->fs_last_byte < fsi->new_size_bytes)))
+			cmp_desc = "smaller than";
+		else if ((larger = (fsi->fs_last_byte > fsi->new_size_bytes)))
+			cmp_desc = "larger than";
 
-	log_print_unless_silent("File system size (%s) is %s the requested size (%s).",
-				display_size(cmd, fsi->fs_last_byte/512), cmp_desc,
-				display_size(cmd, fsi->new_size_bytes/512));
+		log_print_unless_silent("File system size (%s) is %s the requested size (%s).",
+					display_size(cmd, fsi->fs_last_byte/512), cmp_desc,
+					display_size(cmd, fsi->new_size_bytes/512));
 
-	/*
-	 * FS reduce is not needed, it's not using the affected space.
-	 */
-	if (smaller || equal) {
-		log_print_unless_silent("File system reduce is not needed, skipping.");
-		fsi->needs_reduce = 0;
-		return 1;
+		/*
+		 * FS reduce is not needed, it's not using the affected space.
+		 */
+		if (smaller || equal) {
+			log_print_unless_silent("File system reduce is not needed, skipping.");
+			fsi->needs_reduce = 0;
+			return 1;
+		}
 	}
 
 	/*
@@ -6134,19 +6144,23 @@ static int _fs_reduce_allow(struct cmd_context *cmd, struct logical_volume *lv,
 	if (!strcmp(lp->fsopt, "checksize")) {
 		if (is_ext_fstype)
 			log_error("File system reduce is required (see resize2fs or --resizefs.)");
+		else if (!strcmp(fsi->fstype, "btrfs"))
+			log_error("File system reduce is required (see btrfs-progs or --resizefs.)");
 		else
 			log_error("File system reduce is required and not supported (%s).", fsi->fstype);
 		return 0;
 	}
 
 	/*
-	 * FS reduce required, ext* supports it, xfs does not.
+	 * FS reduce required, ext* and btrfs support it, xfs does not.
 	 */
 	if (is_ext_fstype) {
 		log_print_unless_silent("File system reduce is required using resize2fs.");
 	} else if (!strcmp(fsi->fstype, "reiserfs")) {
 		log_error("File system reduce for reiserfs requires --fs resize_fsadm.");
 		return 0;
+	} else if (!strcmp(fsi->fstype, "btrfs")) {
+		log_print_unless_silent("File system reduce is required using btrfs-progs.");
 	} else {
 		log_error("File system reduce is required and not supported (%s).", fsi->fstype);
 		return 0;
@@ -6169,6 +6183,11 @@ static int _fs_reduce_allow(struct cmd_context *cmd, struct logical_volume *lv,
 		fsi->needs_reduce = 1;
 	} else if (!strcmp(fsi->fstype, "swap")) {
 		fsi->needs_reduce = 1;
+	} else if (!strcmp(fsi->fstype, "btrfs")) {
+		/* btrfs requires fs to be mounted to shrink */
+		if (fsi->unmounted)
+			fsi->needs_mount = 1;
+		fsi->needs_reduce = 1;
 	} else {
 		/*
 		 * Shouldn't reach here since no other fs types get this far.
@@ -6261,7 +6280,8 @@ static int _fs_extend_allow(struct cmd_context *cmd, struct logical_volume *lv,
 	if (!strcmp(fsi->fstype, "ext2") ||
 	    !strcmp(fsi->fstype, "ext3") ||
 	    !strcmp(fsi->fstype, "ext4") ||
-	    !strcmp(fsi->fstype, "xfs")) {
+	    !strcmp(fsi->fstype, "xfs") ||
+	    !strcmp(fsi->fstype, "btrfs")) {
 		log_debug("Found fs %s last_byte %llu",
 			  fsi->fstype, (unsigned long long)fsi->fs_last_byte);
 		if (!strncmp(fsi->fstype, "ext", 3))
@@ -6340,11 +6360,13 @@ static int _fs_extend_allow(struct cmd_context *cmd, struct logical_volume *lv,
 
 	} else if (!strcmp(fsi->fstype, "swap")) {
 		fsi->needs_extend = 1;
-	} else if (!strcmp(fsi->fstype, "xfs")) {
-		fs_extend_cmd = " xfs_growfs";
-
+	} else if (!strcmp(fsi->fstype, "xfs") || !strcmp(fsi->fstype, "btrfs")) {
+		if (!strcmp(fsi->fstype, "xfs"))
+			fs_extend_cmd = " xfs_growfs";
+		else
+			fs_extend_cmd = " btrfs filesystem resize";
 		/*
-		 * xfs must be mounted to extend.
+		 * xfs and btrfs must be mounted to extend.
 		 *
 		 * --fs resize --fsmode nochange: don't change mount condition.
 		 * if mounted: fs_extend
@@ -6377,7 +6399,6 @@ static int _fs_extend_allow(struct cmd_context *cmd, struct logical_volume *lv,
 				fsi->needs_extend = 1;
 			}
 		}
-
 	} else {
 		/* shouldn't reach here */
 		log_error("File system type %s not handled.", fsi->fstype);
diff --git a/scripts/lvresize_fs_helper.sh b/scripts/lvresize_fs_helper.sh
index 90b1a978220b..7a8c58b97741 100755
--- a/scripts/lvresize_fs_helper.sh
+++ b/scripts/lvresize_fs_helper.sh
@@ -12,6 +12,8 @@
 # along with this program; if not, write to the Free Software Foundation,
 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
+DM_DEV_DIR="${DM_DEV_DIR:-/dev}"
+
 errorexit() {
 	echo "$1" >&2
 	exit 1
@@ -22,6 +24,34 @@ logmsg() {
 	logger "${SCRIPTNAME}: $1"
 }
 
+btrfs_devid() {
+	local devpath="$1"
+	local devid
+
+	devpath="$(readlink "$devpath")"
+
+	# It could be a multi-devices btrfs so call grep.
+	devid="$(LC_ALL=C btrfs filesystem show "$devpath" | grep "$devpath"$ )"
+
+	# if DM_DEV_DIR is not /dev/ e.g /tmp, output of btrfs filesystem show would be like:
+	# Label: none  uuid: d17f6974-267f-4140-8d71-83d4afd36a72
+	# 		 Total devices 1 FS bytes used 144.00KiB
+	# 		 devid    1 size 256.00MiB used 16.75MiB path /dev/mapper/LVMTEST120665vg-LV1
+	#
+	# But the VOLUME here is /tmp/mapper/LVMTEST120665vg-LV1
+	if [ -z "$devid" ];then
+		tmp_path="${devpath/#${DM_DEV_DIR}//dev}"
+		devid="$(LC_ALL=C btrfs filesystem show "$devpath" | grep "$tmp_path"$)"
+		devid=${devid##*devid}
+	fi
+
+	devid=${devid##*devid}
+	devid=${devid%%size*}
+	devid="$(echo "$devid" |sed 's/^[ \t]*//g'|sed 's/[ \t]*$'//g)"
+
+	echo "$devid"
+}
+
 # Set to 1 while the fs is temporarily mounted on $TMPDIR
 TMP_MOUNT_DONE=0
 # Set to 1 if the fs resize command fails
@@ -39,12 +69,22 @@ fsextend() {
 	fi
 
 	if [ "$DO_FSCK" -eq 1 ]; then
-		logmsg "e2fsck ${DEVPATH}"
-		if e2fsck -f -p "$DEVPATH"; then
-			logmsg "e2fsck done"
-		else
-			logmsg "e2fsck failed"
-			exit 1
+		if [[ "$FSTYPE" == "ext"* ]]; then
+			logmsg "e2fsck ${DEVPATH}"
+			if e2fsck -f -p "$DEVPATH"; then
+				logmsg "e2fsck done"
+			else
+				logmsg "e2fsck failed"
+				exit 1
+			fi
+		elif [[ "$FSTYPE" == "btrfs" ]]; then
+			logmsg "btrfsck ${DEVPATH}"
+			if btrfsck "$DEVPATH"; then
+				logmsg "btrfsck done"
+			else
+				logmsg "btrfsck failed"
+				exit 1
+			fi
 		fi
 	fi
 	
@@ -85,6 +125,22 @@ fsextend() {
 			logmsg "xfs_growfs failed"
 			RESIZEFS_FAILED=1
 		fi
+	elif [[ "$FSTYPE" == "btrfs" ]]; then
+		NEWSIZEBYTES=${NEWSIZEBYTES:-max}
+		BTRFS_DEVID="$(btrfs_devid "$DEVPATH")"
+		REAL_MOUNTPOINT="$MOUNTDIR"
+
+		if [ $TMP_MOUNT_DONE -eq 1 ]; then
+			REAL_MOUNTPOINT="$TMPDIR"
+		fi
+
+		logmsg "btrfs filesystem resize ${BTRFS_DEVID}:${NEWSIZEBYTES} ${REAL_MOUNTPOINT}"
+		if btrfs filesystem resize "$BTRFS_DEVID":"$NEWSIZEBYTES" "$REAL_MOUNTPOINT"; then
+			logmsg "btrfs filesystem resize done"
+		else
+			logmsg "btrfs filesystem resize failed"
+			RESIZEFS_FAILED=1
+		fi
 	fi
 
 	# If the fs was temporarily mounted, now unmount it.
@@ -131,12 +187,22 @@ fsreduce() {
 	fi
 
 	if [ "$DO_FSCK" -eq 1 ]; then
-		logmsg "e2fsck ${DEVPATH}"
-		if e2fsck -f -p "$DEVPATH"; then
-			logmsg "e2fsck done"
-		else
-			logmsg "e2fsck failed"
-			exit 1
+		if [[ "$FSTYPE" == "ext"* ]]; then
+			logmsg "e2fsck ${DEVPATH}"
+			if e2fsck -f -p "$DEVPATH"; then
+				logmsg "e2fsck done"
+			else
+				logmsg "e2fsck failed"
+				exit 1
+			fi
+		elif [[ "$FSTYPE" == "btrfs" ]]; then
+			logmsg "btrfsck ${DEVPATH}"
+			if btrfsck "$DEVPATH"; then
+				logmsg "btrfsck done"
+			else
+				logmsg "btrfsck failed"
+				exit 1
+			fi
 		fi
 	fi
 	
@@ -161,6 +227,21 @@ fsreduce() {
 			# will exit after cleanup unmount
 			RESIZEFS_FAILED=1
 		fi
+	elif [[ "$FSTYPE" == "btrfs" ]]; then
+		BTRFS_DEVID="$(btrfs_devid "$DEVPATH")"
+		REAL_MOUNTPOINT="$MOUNTDIR"
+
+		if [ $TMP_MOUNT_DONE -eq 1 ]; then
+			REAL_MOUNTPOINT="$TMPDIR"
+		fi
+
+		logmsg "btrfs filesystem resize ${BTRFS_DEVID}:${NEWSIZEBYTES} ${REAL_MOUNTPOINT}"
+		if btrfs filesystem resize "$BTRFS_DEVID":"$NEWSIZEBYTES" "$REAL_MOUNTPOINT"; then
+			logmsg "btrfs filesystem resize done"
+		else
+			logmsg "btrfs filesystem resize failed"
+			RESIZEFS_FAILED=1
+		fi
 	fi
 
 	# If the fs was temporarily mounted, now unmount it.
@@ -230,6 +311,7 @@ usage() {
 	echo "    [ --fsck ]"
 	echo "    [ --cryptresize ]"
 	echo "    [ --cryptpath path ]"
+	echo "    [ --newsizebytes num ]"
 	echo ""
 	echo "${SCRIPTNAME} --fsreduce --fstype name --lvpath path"
 	echo "    [ --newsizebytes num ]"
@@ -249,7 +331,7 @@ usage() {
 	echo "    --fsreduce"
 	echo "        Reduce the file system."
 	echo "    --fstype name"
-	echo "        The type of file system (ext*, xfs)."
+	echo "        The type of file system (ext*, xfs, btrfs.)"
 	echo "    --lvpath path"
 	echo "        The path to the LV being resized."
 	echo "    --mountdir path"
@@ -380,6 +462,7 @@ if [[ "$DO_FSEXTEND" -eq 1 || "$DO_FSREDUCE" -eq 1 ]]; then
 	case "$FSTYPE" in
 	  ext[234]) ;;
 	  "xfs")    ;;
+	  "btrfs")  ;;
 	  *) errorexit "Cannot resize --fstype \"$FSTYPE\"."
 	esac
 
@@ -447,4 +530,3 @@ elif [ "$DO_FSREDUCE" -eq 1 ]; then
 elif [ "$DO_CRYPTRESIZE" -eq 1 ]; then
 	cryptresize
 fi
-
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/4] test: addm shell/lvresize-btrfs.sh
  2025-05-13  6:14 [PATCH 0/4] lvm2: add btrfs support for lvresize Su Yue
                   ` (2 preceding siblings ...)
  2025-05-13  6:14 ` [PATCH 3/4] lvresize: add btrfs support Su Yue
@ 2025-05-13  6:14 ` Su Yue
  2025-05-13 17:35 ` [PATCH 0/4] lvm2: add btrfs support for lvresize David Teigland
  4 siblings, 0 replies; 6+ messages in thread
From: Su Yue @ 2025-05-13  6:14 UTC (permalink / raw)
  To: lvm-devel; +Cc: teigland, zkabelac, heming.zhao, glass.su, l

refer legacy patche:
- Ondrej Kozina <okozina@redhat.com>
https://listman.redhat.com/archives/lvm-devel/2012-November/msg00055.html

Signed-off-by: Heming Zhao <heming.zhao@suse.com>
[Adjust to lvresize]
Signed-off-by: Su Yue <glass.su@suse.com>
---
 test/shell/lvresize-btrfs.sh | 244 +++++++++++++++++++++++++++++++++++
 1 file changed, 244 insertions(+)
 create mode 100644 test/shell/lvresize-btrfs.sh

diff --git a/test/shell/lvresize-btrfs.sh b/test/shell/lvresize-btrfs.sh
new file mode 100644
index 000000000000..408a84e34200
--- /dev/null
+++ b/test/shell/lvresize-btrfs.sh
@@ -0,0 +1,244 @@
+#!/usr/bin/env bash
+
+# Copyright (C) 2025 Red Hat, Inc. All rights reserved.
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions
+# of the GNU General Public License v.2.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+test_description='Exercise btrfs resize'
+
+. lib/inittest
+
+aux have_fsinfo || skip "Test needs --fs checksize support"
+
+aux prepare_vg 1 1024
+
+which mkfs.btrfs || skip
+which btrfs	 || skip
+grep btrfs /proc/filesystems || skip
+
+vg_lv=$vg/$lv1
+vg_lv2=$vg/${lv1}bar
+vg_lv3=$vg/${lv1}bar2
+dev_vg_lv="$DM_DEV_DIR/$vg_lv"
+dev_vg_lv2="$DM_DEV_DIR/$vg_lv2"
+dev_vg_lv3="$DM_DEV_DIR/$vg_lv3"
+mount_dir="mnt"
+mount_space_dir="mnt space dir"
+
+test ! -d "$mount_dir" && mkdir "$mount_dir"
+test ! -d "$mount_space_dir" && mkdir "$mount_space_dir"
+
+cleanup_mounted_and_teardown()
+{
+	umount "$mount_dir" || true
+	umount "$mount_space_dir" || true
+	aux teardown
+}
+
+reset_lvs()
+{
+	# Since we call mkfs.btrfs with '-f', lvreduce to 64M is enough
+	lvreduce -L64M -nf $vg_lv || true
+	lvreduce -L64M -nf $vg_lv2 || true
+	lvreduce -L64M -nf $vg_lv3 || true
+}
+
+fscheck_btrfs() {
+	btrfsck "$1"
+}
+scrub_btrfs() {
+	btrfs scrub start -B "$1"
+}
+
+btrfs_devid() {
+	local devpath="$1"
+	local devid
+
+	devpath="$(readlink "$devpath")"
+
+	# It could be a multi-devices btrfs so call grep.
+	devid="$(LC_ALL=C btrfs filesystem show "$devpath" | grep "$devpath"$ || true)"
+
+	# if DM_DEV_DIR is not /dev/ e.g /tmp, output of btrfs filesystem show would be like:
+	# Label: none  uuid: d17f6974-267f-4140-8d71-83d4afd36a72
+	# 		 Total devices 1 FS bytes used 144.00KiB
+	# 		 devid    1 size 256.00MiB used 16.75MiB path /dev/mapper/LVMTEST120665vg-LV1
+	#
+	# But the VOLUME here is /tmp/mapper/LVMTEST120665vg-LV1
+	if [ -z "$devid" ];then
+		tmp_path="${devpath/#${DM_DEV_DIR}//dev}"
+		devid="$(LC_ALL=C btrfs filesystem show "$devpath" | grep "$tmp_path"$)"
+		devid=${devid##*devid}
+	fi
+
+	devid=${devid##*devid}
+	devid=${devid%%size*}
+	devid="$(echo "$devid" |sed 's/^[ \t]*//g'|sed 's/[ \t]*$'//g)"
+
+	echo "$devid"
+}
+
+# in MiB
+btrfs_device_size() {
+	local dev="$1"
+	local mnt="$2"
+	local devid
+
+	devid="$(btrfs_devid $dev)"
+	btrfs device usage -b "$mnt" | grep -A1 "ID: $devid"$ | grep 'Device size:' \
+		| awk '{print $NF}'
+}
+
+verify_mounted_device_size() {
+	local dev="$1"
+	local mnt="$2"
+	local size="$(echo $3 | numfmt --from=iec)"
+	local used_size
+
+	used_size=$(btrfs_device_size "$dev" "$mnt")
+
+	[[ "$used_size" == "$size"  ]]
+}
+
+verify_device_size() {
+	local dev="$1"
+	local size="$2"
+	local mnt="$mount_dir"
+
+	mount "$dev" "$mnt"
+	verify_mounted_device_size "$dev" "$mnt" "$size"
+	umount "$mnt"
+}
+
+# btrfs minimal size calculation is complex, we use 64M here.
+lvcreate -n $lv1 -L64M $vg
+lvcreate -n ${lv1}bar -L64M $vg
+lvcreate -n ${lv1}bar2 -L64M $vg
+trap 'cleanup_mounted_and_teardown' EXIT
+
+single_device_test() {
+	mkfs.btrfs -m single "$dev_vg_lv"
+	mkfs.btrfs -m single "$dev_vg_lv2"
+
+
+	# kernel limits 256 MB as minimal btrfs resizable size
+	# you can grow fs from 64MB->256MB
+	# but you can't grow from 64MB->180MB
+	lvresize -y --fs resize $vg_lv -L 256M
+	verify_device_size $dev_vg_lv 256M
+	lvresize -y --fs resize $vg_lv2 -L 256M
+	verify_device_size $dev_vg_lv2 256M
+
+	not lvresize -y --fs resize $vg_lv -L 200M
+	lvresize -y -L+12M --fs resize $vg_lv
+	verify_device_size $dev_vg_lv 268M
+	# lvreduce default behavior is fs checksize, must fail
+	not lvreduce -y -L256M $vg_lv
+	lvreduce -y -L256M --fs resize $vg_lv
+	fscheck_btrfs $dev_vg_lv
+
+	# do test on mounted state
+	mount "$dev_vg_lv" "$mount_dir"
+	mount "$dev_vg_lv2" "$mount_space_dir"
+
+	# 'not': we expect a failure here.
+	not lvresize --fs resize $vg_lv -L 200M
+	lvresize -L+12M --fs resize $vg_lv
+
+	verify_mounted_device_size $dev_vg_lv "$mount_dir" 268M
+	# lvreduce default behavior is fs checksize, must fail
+	not lvreduce -L256M $vg_lv
+	lvreduce -L256M --fs resize $vg_lv
+	verify_mounted_device_size $dev_vg_lv "$mount_dir" 256M
+	scrub_btrfs $dev_vg_lv
+	umount "$mount_dir"
+
+	not lvresize --fs resize $vg_lv2 -L 200M
+	lvresize -L+12M --fs resize $vg_lv2
+	verify_mounted_device_size $dev_vg_lv2 "$mount_space_dir" 268M
+	not lvreduce -L256M --fs checksize $vg_lv2
+	lvreduce -L256M --fs resize $vg_lv2
+	verify_mounted_device_size $dev_vg_lv2 "$mount_space_dir" 256M
+	scrub_btrfs $dev_vg_lv2
+	umount "$mount_space_dir"
+}
+
+multiple_devices_test() {
+
+	# fs size is the sum of the three LVs size
+	mkfs.btrfs -m single -d single -f "$dev_vg_lv" "$dev_vg_lv2" "$dev_vg_lv3"
+
+	# The VG is 1GB size, we expect success here.
+	# lv,lv2,lv3 size are changed from 64M to 256M
+	lvresize --fs resize $vg_lv -L 256M --fsmode manage
+	lvresize --fs resize $vg_lv2 -L 256M --fsmode manage
+	lvresize --fs resize $vg_lv3 -L 256M --fsmode manage
+
+	verify_device_size $dev_vg_lv 256M
+	verify_device_size $dev_vg_lv2 256M
+	verify_device_size $dev_vg_lv3 256M
+
+	# check if lvextend/lvreduce is able to get/resize btrfs on
+	# the right device
+	lvextend -L+150M --fs resize $vg_lv --fsmode manage
+	lvreduce --fs resize $vg_lv -L 300M --fsmode manage
+	verify_device_size $dev_vg_lv 300M
+	not lvreduce -y -L256M --fs checksize $vg_lv --fsmode manage
+	lvreduce -L256M --fs resize $vg_lv --fsmode manage
+	verify_device_size $dev_vg_lv 256M
+	fscheck_btrfs $dev_vg_lv
+
+	lvextend -L+150M --fs resize $vg_lv2 --fsmode manage
+	lvreduce --fs resize $vg_lv2 -L 300M --fsmode manage
+	verify_device_size $dev_vg_lv2 300M
+	not lvreduce -y -L256M --fs checksize $vg_lv2 --fsmode manage
+	lvreduce -y -L256M --fs resize $vg_lv2 --fsmode manage
+	verify_device_size $dev_vg_lv2 256M
+	fscheck_btrfs $dev_vg_lv2
+
+	lvextend -L+150M --fs resize $vg_lv3 --fsmode manage
+	lvreduce --fs resize $vg_lv3 -L 300M --fsmode manage
+	verify_device_size $dev_vg_lv3 300M
+	not lvreduce -y -L256M --fs checksize $vg_lv3 --fsmode manage
+	lvreduce -y -L256M --fs resize $vg_lv3 --fsmode manage
+	verify_device_size $dev_vg_lv3 256M
+	fscheck_btrfs $dev_vg_lv3
+
+	# repeat with mounted fs
+	mount "$dev_vg_lv" "$mount_dir"
+
+	lvresize -L300M --fs resize $vg_lv
+	verify_mounted_device_size $dev_vg_lv "$mount_dir" 300M
+	lvreduce -y -L256M --fs resize $vg_lv
+	verify_mounted_device_size $dev_vg_lv "$mount_dir" 256M
+
+	lvresize -L300M --fs resize $vg_lv2
+	verify_mounted_device_size $dev_vg_lv2 "$mount_dir" 300M
+	lvreduce -y -L256M --fs resize $vg_lv2
+	verify_mounted_device_size $dev_vg_lv2 "$mount_dir" 256M
+
+	lvresize -L300M --fs resize $vg_lv3
+	verify_mounted_device_size $dev_vg_lv3 "$mount_dir" 300M
+	lvreduce -y -L256M --fs resize $vg_lv3
+	verify_mounted_device_size $dev_vg_lv3 "$mount_dir" 256M
+
+	scrub_btrfs $dev_vg_lv
+	umount "$mount_dir"
+
+	lvresize -nf -L300M $vg_lv
+	lvresize -nf -L300M $vg_lv2
+}
+
+single_device_test
+# after each test, reset_lv_size should be called to make sure
+# all lvs are in same state/size.
+reset_lvs
+multiple_devices_test
+
+
+vgremove -ff $vg
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/4] lvm2: add btrfs support for lvresize
  2025-05-13  6:14 [PATCH 0/4] lvm2: add btrfs support for lvresize Su Yue
                   ` (3 preceding siblings ...)
  2025-05-13  6:14 ` [PATCH 4/4] test: addm shell/lvresize-btrfs.sh Su Yue
@ 2025-05-13 17:35 ` David Teigland
  4 siblings, 0 replies; 6+ messages in thread
From: David Teigland @ 2025-05-13 17:35 UTC (permalink / raw)
  To: Su Yue; +Cc: lvm-devel, zkabelac, heming.zhao, l

On Tue, May 13, 2025 at 02:14:23PM +0800, Su Yue wrote:
> Hi,
>   This patchset adds btrfs support for lvresize.
>   I tried to split patches as small as minimal for review but 3rd is still
> complicated for bisecting.
>   The patchset passed shell/lvresize-xfs.sh and shell/lvresize-fs.sh on
> CentOS stream, fedora 42 and archlinux to make sure that other fs resize
> functionalties are not broken by the feature.

Thanks, it looks nice, I've pushed these to the main branch.
Dave


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2025-05-13 17:35 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-13  6:14 [PATCH 0/4] lvm2: add btrfs support for lvresize Su Yue
2025-05-13  6:14 ` [PATCH 1/4] filesystem: get device uuid in fs_get_blkid Su Yue
2025-05-13  6:14 ` [PATCH 2/4] filesystem: factor out get mount point logic from fs_get_info() into _fs_get_mnt() Su Yue
2025-05-13  6:14 ` [PATCH 3/4] lvresize: add btrfs support Su Yue
2025-05-13  6:14 ` [PATCH 4/4] test: addm shell/lvresize-btrfs.sh Su Yue
2025-05-13 17:35 ` [PATCH 0/4] lvm2: add btrfs support for lvresize David Teigland

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox