Accepting request 329153 from filesystems
1 OBS-URL: https://build.opensuse.org/request/show/329153 OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/btrfsprogs?expand=0&rev=62
This commit is contained in:
commit
d66ce03a18
@ -1,445 +0,0 @@
|
||||
From: Filipe Manana <fdmanana@suse.com>
|
||||
Date: Wed, 17 Jun 2015 10:34:52 +0100
|
||||
Subject: [PATCH v3] Btrfs-progs: add feature to get mininum size for resizing a
|
||||
fs/device
|
||||
|
||||
Currently there is not way for a user to know what is the minimum size a
|
||||
device of a btrfs filesystem can be resized to. Sometimes the value of
|
||||
total allocated space (sum of all allocated chunks/device extents), which
|
||||
can be parsed from 'btrfs filesystem show' and 'btrfs filesystem usage',
|
||||
works as the minimum size, but sometimes it does not, namely when device
|
||||
extents have to relocated to holes (unallocated space) within the new
|
||||
size of the device (the total allocated space sum).
|
||||
|
||||
This change adds the ability to reliably compute such minimum value and
|
||||
extents 'btrfs filesystem resize' with the following syntax to get such
|
||||
value:
|
||||
|
||||
btrfs filesystem resize [devid:]get_min_size
|
||||
|
||||
Signed-off-by: Filipe Manana <fdmanana@suse.com>
|
||||
---
|
||||
|
||||
V2: Check if device holes contain the location of superblock mirrors and
|
||||
correct the minimum size accounting accordingly.
|
||||
Added missing sudo calls to test, rebeased against development branch
|
||||
and moved it into the misc-tests category.
|
||||
|
||||
V3: Added missing changes to cmds-filesystems.c in v2. I forgot to add them
|
||||
to V2 (left unstaged in local repository).
|
||||
|
||||
Documentation/btrfs-filesystem.asciidoc | 4 +-
|
||||
cmds-filesystem.c | 255 +++++++++++++++++++++++++++++++-
|
||||
ctree.h | 3 +
|
||||
tests/misc-tests.sh | 2 +
|
||||
tests/misc-tests/004-shrink-fs/test.sh | 69 +++++++++
|
||||
5 files changed, 331 insertions(+), 2 deletions(-)
|
||||
create mode 100755 tests/misc-tests/004-shrink-fs/test.sh
|
||||
|
||||
diff --git a/Documentation/btrfs-filesystem.asciidoc b/Documentation/btrfs-filesystem.asciidoc
|
||||
index 31cd51b..2b34242 100644
|
||||
--- a/Documentation/btrfs-filesystem.asciidoc
|
||||
+++ b/Documentation/btrfs-filesystem.asciidoc
|
||||
@@ -93,7 +93,7 @@ If a newlabel optional argument is passed, the label is changed.
|
||||
NOTE: the maximum allowable length shall be less than 256 chars
|
||||
|
||||
// Some wording are extracted by the resize2fs man page
|
||||
-*resize* [<devid>:][+/-]<size>[kKmMgGtTpPeE]|[<devid>:]max <path>::
|
||||
+*resize* [<devid>:][+/-]<size>[kKmMgGtTpPeE]|[<devid>:]max|[<devid>:]get_min_size <path>::
|
||||
Resize a mounted filesystem identified by directory <path>. A particular device
|
||||
can be resized by specifying a <devid>.
|
||||
+
|
||||
@@ -113,6 +113,8 @@ KiB, MiB, GiB, TiB, PiB, or EiB, respectively. Case does not matter.
|
||||
+
|
||||
If \'max' is passed, the filesystem will occupy all available space on the
|
||||
device devid.
|
||||
+If \'get_min_size' is passed, return the minimum size the device can be
|
||||
+shrunk to, without performing any resize operation.
|
||||
+
|
||||
The resize command does not manipulate the size of underlying
|
||||
partition. If you wish to enlarge/reduce a filesystem, you must make sure you
|
||||
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
|
||||
index 800aa4d..b44a655 100644
|
||||
--- a/cmds-filesystem.c
|
||||
+++ b/cmds-filesystem.c
|
||||
@@ -1271,14 +1271,264 @@ static int cmd_defrag(int argc, char **argv)
|
||||
}
|
||||
|
||||
static const char * const cmd_resize_usage[] = {
|
||||
- "btrfs filesystem resize [devid:][+/-]<newsize>[kKmMgGtTpPeE]|[devid:]max <path>",
|
||||
+ "btrfs filesystem resize [devid:][+/-]<newsize>[kKmMgGtTpPeE]|[devid:]max|[devid:]get_min_size <path>",
|
||||
"Resize a filesystem",
|
||||
"If 'max' is passed, the filesystem will occupy all available space",
|
||||
"on the device 'devid'.",
|
||||
+ "If 'get_min_size' is passed, return the minimum size the device can",
|
||||
+ "be shrunk to.",
|
||||
"[kK] means KiB, which denotes 1KiB = 1024B, 1MiB = 1024KiB, etc.",
|
||||
NULL
|
||||
};
|
||||
|
||||
+struct dev_extent_elem {
|
||||
+ u64 start;
|
||||
+ /* inclusive end */
|
||||
+ u64 end;
|
||||
+ struct list_head list;
|
||||
+};
|
||||
+
|
||||
+static int add_dev_extent(struct list_head *list,
|
||||
+ const u64 start, const u64 end,
|
||||
+ const int append)
|
||||
+{
|
||||
+ struct dev_extent_elem *e;
|
||||
+
|
||||
+ e = malloc(sizeof(*e));
|
||||
+ if (!e)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ e->start = start;
|
||||
+ e->end = end;
|
||||
+
|
||||
+ if (append)
|
||||
+ list_add_tail(&e->list, list);
|
||||
+ else
|
||||
+ list_add(&e->list, list);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void free_dev_extent_list(struct list_head *list)
|
||||
+{
|
||||
+ while (!list_empty(list)) {
|
||||
+ struct dev_extent_elem *e;
|
||||
+
|
||||
+ e = list_first_entry(list, struct dev_extent_elem, list);
|
||||
+ list_del(&e->list);
|
||||
+ free(e);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int hole_includes_sb_mirror(const u64 start, const u64 end)
|
||||
+{
|
||||
+ int i;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
|
||||
+ u64 bytenr = btrfs_sb_offset(i);
|
||||
+
|
||||
+ if (bytenr >= start && bytenr <= end) {
|
||||
+ ret = 1;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void adjust_dev_min_size(struct list_head *extents,
|
||||
+ struct list_head *holes,
|
||||
+ u64 *min_size)
|
||||
+{
|
||||
+ /*
|
||||
+ * If relocation of the block group of a device extent must happen (see
|
||||
+ * below) scratch space is used for the relocation. So track here the
|
||||
+ * size of the largest device extent that has to be relocated. We track
|
||||
+ * only the largest and not the sum of the sizes of all relocated block
|
||||
+ * groups because after each block group is relocated the running
|
||||
+ * transaction is committed so that pinned space is released.
|
||||
+ */
|
||||
+ u64 scratch_space = 0;
|
||||
+
|
||||
+ /*
|
||||
+ * List of device extents is sorted by descending order of the extent's
|
||||
+ * end offset. If some extent goes beyond the computed minimum size,
|
||||
+ * which initially matches the sum of the lenghts of all extents,
|
||||
+ * we need to check if the extent can be relocated to an hole in the
|
||||
+ * device between [0, *min_size[ (which is what the resize ioctl does).
|
||||
+ */
|
||||
+ while (!list_empty(extents)) {
|
||||
+ struct dev_extent_elem *e;
|
||||
+ struct dev_extent_elem *h;
|
||||
+ int found = 0;
|
||||
+ u64 extent_len;
|
||||
+ u64 hole_len = 0;
|
||||
+
|
||||
+ e = list_first_entry(extents, struct dev_extent_elem, list);
|
||||
+ if (e->end <= *min_size)
|
||||
+ break;
|
||||
+
|
||||
+ /*
|
||||
+ * Our extent goes beyond the computed *min_size. See if we can
|
||||
+ * find a hole large enough to relocate it to. If not we must stop
|
||||
+ * and set *min_size to the end of the extent.
|
||||
+ */
|
||||
+ extent_len = e->end - e->start + 1;
|
||||
+ list_for_each_entry(h, holes, list) {
|
||||
+ hole_len = h->end - h->start + 1;
|
||||
+ if (hole_len >= extent_len) {
|
||||
+ found = 1;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!found) {
|
||||
+ *min_size = e->end + 1;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * If the hole found contains the location for a superblock
|
||||
+ * mirror, we are pessimistic and require allocating one
|
||||
+ * more extent of the same size. This is because the block
|
||||
+ * group could be in the worst case used by a single extent
|
||||
+ * with a size >= (block_group.length - superblock.size).
|
||||
+ */
|
||||
+ if (hole_includes_sb_mirror(h->start,
|
||||
+ h->start + extent_len - 1))
|
||||
+ *min_size += extent_len;
|
||||
+
|
||||
+ if (hole_len > extent_len) {
|
||||
+ h->start += extent_len;
|
||||
+ } else {
|
||||
+ list_del(&h->list);
|
||||
+ free(h);
|
||||
+ }
|
||||
+
|
||||
+ list_del(&e->list);
|
||||
+ free(e);
|
||||
+
|
||||
+ if (extent_len > scratch_space)
|
||||
+ scratch_space = extent_len;
|
||||
+ }
|
||||
+
|
||||
+ if (scratch_space) {
|
||||
+ *min_size += scratch_space;
|
||||
+ /*
|
||||
+ * Chunk allocation requires inserting/updating items in the
|
||||
+ * chunk tree, so often this can lead to the need of allocating
|
||||
+ * a new system chunk too, which has a maximum size of 32Mb.
|
||||
+ */
|
||||
+ *min_size += 32 * 1024 * 1024;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int get_min_size(int fd, DIR *dirstream, const char *amount)
|
||||
+{
|
||||
+ int ret = 1;
|
||||
+ char *p = strstr(amount, ":");
|
||||
+ u64 devid = 1;
|
||||
+ /*
|
||||
+ * Device allocations starts at 1Mb or at the value passed through the
|
||||
+ * mount option alloc_start if it's bigger than 1Mb. The alloc_start
|
||||
+ * option is used for debugging and testing only, and recently the
|
||||
+ * possibility of deprecating/removing it has been discussed, so we
|
||||
+ * ignore it here.
|
||||
+ */
|
||||
+ u64 min_size = 1 * 1024 * 1024ull;
|
||||
+ struct btrfs_ioctl_search_args args;
|
||||
+ struct btrfs_ioctl_search_key *sk = &args.key;
|
||||
+ u64 last_pos = (u64)-1;
|
||||
+ LIST_HEAD(extents);
|
||||
+ LIST_HEAD(holes);
|
||||
+
|
||||
+ if (p && sscanf(amount, "%llu:get_min_size", &devid) != 1) {
|
||||
+ fprintf(stderr, "Invalid parameter: %s\n", amount);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ memset(&args, 0, sizeof(args));
|
||||
+ sk->tree_id = BTRFS_DEV_TREE_OBJECTID;
|
||||
+ sk->min_objectid = devid;
|
||||
+ sk->max_objectid = devid;
|
||||
+ sk->max_type = BTRFS_DEV_EXTENT_KEY;
|
||||
+ sk->min_type = BTRFS_DEV_EXTENT_KEY;
|
||||
+ sk->min_offset = 0;
|
||||
+ sk->max_offset = (u64)-1;
|
||||
+ sk->min_transid = 0;
|
||||
+ sk->max_transid = (u64)-1;
|
||||
+ sk->nr_items = 4096;
|
||||
+
|
||||
+ while (1) {
|
||||
+ int i;
|
||||
+ struct btrfs_ioctl_search_header *sh;
|
||||
+ unsigned long off = 0;
|
||||
+
|
||||
+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
|
||||
+ if (ret < 0) {
|
||||
+ fprintf(stderr,
|
||||
+ "Error invoking tree search ioctl: %s\n",
|
||||
+ strerror(errno));
|
||||
+ ret = 1;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ if (sk->nr_items == 0)
|
||||
+ break;
|
||||
+
|
||||
+ for (i = 0; i < sk->nr_items; i++) {
|
||||
+ struct btrfs_dev_extent *extent;
|
||||
+ u64 len;
|
||||
+
|
||||
+ sh = (struct btrfs_ioctl_search_header *)(args.buf +
|
||||
+ off);
|
||||
+ off += sizeof(*sh);
|
||||
+ extent = (struct btrfs_dev_extent *)(args.buf + off);
|
||||
+ off += sh->len;
|
||||
+
|
||||
+ sk->min_objectid = sh->objectid;
|
||||
+ sk->min_type = sh->type;
|
||||
+ sk->min_offset = sh->offset + 1;
|
||||
+
|
||||
+ if (sh->objectid != devid ||
|
||||
+ sh->type != BTRFS_DEV_EXTENT_KEY)
|
||||
+ continue;
|
||||
+
|
||||
+ len = btrfs_stack_dev_extent_length(extent);
|
||||
+ min_size += len;
|
||||
+ ret = add_dev_extent(&extents, sh->offset,
|
||||
+ sh->offset + len - 1, 0);
|
||||
+
|
||||
+ if (!ret && last_pos != (u64)-1 &&
|
||||
+ last_pos != sh->offset)
|
||||
+ ret = add_dev_extent(&holes, last_pos,
|
||||
+ sh->offset - 1, 1);
|
||||
+ if (ret) {
|
||||
+ fprintf(stderr, "Error: %s\n", strerror(-ret));
|
||||
+ ret = 1;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ last_pos = sh->offset + len;
|
||||
+ }
|
||||
+
|
||||
+ if (sk->min_type != BTRFS_DEV_EXTENT_KEY ||
|
||||
+ sk->min_objectid != devid)
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ adjust_dev_min_size(&extents, &holes, &min_size);
|
||||
+ printf("%llu bytes (%s)\n", min_size, pretty_size(min_size));
|
||||
+ ret = 0;
|
||||
+out:
|
||||
+ close_file_or_dir(fd, dirstream);
|
||||
+ free_dev_extent_list(&extents);
|
||||
+ free_dev_extent_list(&holes);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int cmd_resize(int argc, char **argv)
|
||||
{
|
||||
struct btrfs_ioctl_vol_args args;
|
||||
@@ -1320,6 +1570,9 @@ static int cmd_resize(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
+ if (strstr(amount, "get_min_size"))
|
||||
+ return get_min_size(fd, dirstream, amount);
|
||||
+
|
||||
printf("Resize '%s' of '%s'\n", path, amount);
|
||||
memset(&args, 0, sizeof(args));
|
||||
strncpy_null(args.name, amount);
|
||||
diff --git a/ctree.h b/ctree.h
|
||||
index 5550d45..227a00b 100644
|
||||
--- a/ctree.h
|
||||
+++ b/ctree.h
|
||||
@@ -1491,6 +1491,9 @@ BTRFS_SETGET_FUNCS(dev_extent_chunk_offset, struct btrfs_dev_extent,
|
||||
chunk_offset, 64);
|
||||
BTRFS_SETGET_FUNCS(dev_extent_length, struct btrfs_dev_extent, length, 64);
|
||||
|
||||
+BTRFS_SETGET_STACK_FUNCS(stack_dev_extent_length, struct btrfs_dev_extent,
|
||||
+ length, 64);
|
||||
+
|
||||
static inline u8 *btrfs_dev_extent_chunk_tree_uuid(struct btrfs_dev_extent *dev)
|
||||
{
|
||||
unsigned long ptr = offsetof(struct btrfs_dev_extent, chunk_tree_uuid);
|
||||
diff --git a/tests/misc-tests.sh b/tests/misc-tests.sh
|
||||
index 5bbe914..2ae99db 100755
|
||||
--- a/tests/misc-tests.sh
|
||||
+++ b/tests/misc-tests.sh
|
||||
@@ -20,6 +20,8 @@ export RESULTS
|
||||
# For custom script needs to verfiy recovery
|
||||
export TEST_MNT
|
||||
export LANG
|
||||
+# For tests that only use a loop device
|
||||
+export IMAGE
|
||||
|
||||
rm -f $RESULTS
|
||||
mkdir -p $TEST_MNT || _fail "unable to create mount point on $TEST_MNT"
|
||||
diff --git a/tests/misc-tests/004-shrink-fs/test.sh b/tests/misc-tests/004-shrink-fs/test.sh
|
||||
new file mode 100755
|
||||
index 0000000..393cccf
|
||||
--- /dev/null
|
||||
+++ b/tests/misc-tests/004-shrink-fs/test.sh
|
||||
@@ -0,0 +1,69 @@
|
||||
+#!/bin/bash
|
||||
+#
|
||||
+# Test getting the minimum size a filesystem can be resized to and verify we
|
||||
+# are able to resize (shrink) it to that size.
|
||||
+#
|
||||
+
|
||||
+source $TOP/tests/common
|
||||
+
|
||||
+check_prereq mkfs.btrfs
|
||||
+setup_root_helper
|
||||
+
|
||||
+shrink_test()
|
||||
+{
|
||||
+ min_size=$($SUDO_HELPER $TOP/btrfs filesystem resize get_min_size $TEST_MNT)
|
||||
+ if [ $? != 0 ]; then
|
||||
+ _fail "Failed to get minimum size"
|
||||
+ fi
|
||||
+ min_size=$(echo $min_size | cut -d ' ' -f 1)
|
||||
+ echo "min size = ${min_size}" >> $RESULTS
|
||||
+ run_check $SUDO_HELPER $TOP/btrfs filesystem resize $min_size $TEST_MNT
|
||||
+}
|
||||
+
|
||||
+run_check truncate -s 20G $IMAGE
|
||||
+run_check $TOP/mkfs.btrfs -f $IMAGE
|
||||
+run_check $SUDO_HELPER mount $IMAGE $TEST_MNT
|
||||
+run_check $SUDO_HELPER chmod a+rw $TEST_MNT
|
||||
+
|
||||
+# Create 7 data block groups, each with a size of 1Gb.
|
||||
+for ((i = 1; i <= 7; i++)); do
|
||||
+ run_check fallocate -l 1G $TEST_MNT/foo$i
|
||||
+done
|
||||
+
|
||||
+# Make sure they are persisted (all the chunk, device and block group items
|
||||
+# added to the chunk/dev/extent trees).
|
||||
+run_check $TOP/btrfs filesystem sync $TEST_MNT
|
||||
+
|
||||
+# Now remove 3 of those 1G files. This will result in 3 block groups becoming
|
||||
+# unused, which will be automatically deleted by the cleaner kthread, and this
|
||||
+# will result in 3 holes (unallocated space) in the device (each with a size
|
||||
+# of 1Gb).
|
||||
+
|
||||
+run_check rm -f $TEST_MNT/foo2
|
||||
+run_check rm -f $TEST_MNT/foo4
|
||||
+run_check rm -f $TEST_MNT/foo6
|
||||
+
|
||||
+# Sync once to wake up the cleaner kthread which will delete the unused block
|
||||
+# groups - it could have been sleeping when they became unused. Then wait a bit
|
||||
+# to allow the cleaner kthread to delete them and then finally ensure the
|
||||
+# transaction started by the cleaner kthread is committed.
|
||||
+run_check $TOP/btrfs filesystem sync $TEST_MNT
|
||||
+sleep 3
|
||||
+run_check $TOP/btrfs filesystem sync $TEST_MNT
|
||||
+
|
||||
+# Now attempt to get the minimum size we can resize the filesystem to and verify
|
||||
+# the resize operation succeeds. This size closely matches the sum of the size
|
||||
+# of all the allocated device extents.
|
||||
+for ((i = 1; i <= 3; i++)); do
|
||||
+ shrink_test
|
||||
+done
|
||||
+
|
||||
+# Now convert metadata and system chunks to the single profile and check we are
|
||||
+# still able to get a correct minimum size and shrink to that size.
|
||||
+run_check $SUDO_HELPER $TOP/btrfs balance start -mconvert=single \
|
||||
+ -sconvert=single -f $TEST_MNT
|
||||
+for ((i = 1; i <= 3; i++)); do
|
||||
+ shrink_test
|
||||
+done
|
||||
+
|
||||
+run_check $SUDO_HELPER umount $TEST_MNT
|
||||
--
|
||||
1.8.4.5
|
||||
|
@ -1,548 +0,0 @@
|
||||
From 670f1d97d708285bf3bb973c5c865fedcbbe9ab0 Mon Sep 17 00:00:00 2001
|
||||
From: David Sterba <dsterba@suse.com>
|
||||
Date: Mon, 20 Jul 2015 17:29:24 +0200
|
||||
Subject: [PATCH 1/2] btrfs-progs: move min-resize implementation to
|
||||
inspect-internal
|
||||
|
||||
Signed-off-by: David Sterba <dsterba@suse.com>
|
||||
Signed-off-by: Filipe Manana <fdmanana@suse.com>
|
||||
---
|
||||
cmds-filesystem.c | 255 +-----------------------------------------------------
|
||||
cmds-inspect.c | 241 +++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 242 insertions(+), 254 deletions(-)
|
||||
|
||||
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
|
||||
index b44a655..800aa4d 100644
|
||||
--- a/cmds-filesystem.c
|
||||
+++ b/cmds-filesystem.c
|
||||
@@ -1271,264 +1271,14 @@ static int cmd_defrag(int argc, char **argv)
|
||||
}
|
||||
|
||||
static const char * const cmd_resize_usage[] = {
|
||||
- "btrfs filesystem resize [devid:][+/-]<newsize>[kKmMgGtTpPeE]|[devid:]max|[devid:]get_min_size <path>",
|
||||
+ "btrfs filesystem resize [devid:][+/-]<newsize>[kKmMgGtTpPeE]|[devid:]max <path>",
|
||||
"Resize a filesystem",
|
||||
"If 'max' is passed, the filesystem will occupy all available space",
|
||||
"on the device 'devid'.",
|
||||
- "If 'get_min_size' is passed, return the minimum size the device can",
|
||||
- "be shrunk to.",
|
||||
"[kK] means KiB, which denotes 1KiB = 1024B, 1MiB = 1024KiB, etc.",
|
||||
NULL
|
||||
};
|
||||
|
||||
-struct dev_extent_elem {
|
||||
- u64 start;
|
||||
- /* inclusive end */
|
||||
- u64 end;
|
||||
- struct list_head list;
|
||||
-};
|
||||
-
|
||||
-static int add_dev_extent(struct list_head *list,
|
||||
- const u64 start, const u64 end,
|
||||
- const int append)
|
||||
-{
|
||||
- struct dev_extent_elem *e;
|
||||
-
|
||||
- e = malloc(sizeof(*e));
|
||||
- if (!e)
|
||||
- return -ENOMEM;
|
||||
-
|
||||
- e->start = start;
|
||||
- e->end = end;
|
||||
-
|
||||
- if (append)
|
||||
- list_add_tail(&e->list, list);
|
||||
- else
|
||||
- list_add(&e->list, list);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static void free_dev_extent_list(struct list_head *list)
|
||||
-{
|
||||
- while (!list_empty(list)) {
|
||||
- struct dev_extent_elem *e;
|
||||
-
|
||||
- e = list_first_entry(list, struct dev_extent_elem, list);
|
||||
- list_del(&e->list);
|
||||
- free(e);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static int hole_includes_sb_mirror(const u64 start, const u64 end)
|
||||
-{
|
||||
- int i;
|
||||
- int ret = 0;
|
||||
-
|
||||
- for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
|
||||
- u64 bytenr = btrfs_sb_offset(i);
|
||||
-
|
||||
- if (bytenr >= start && bytenr <= end) {
|
||||
- ret = 1;
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- return ret;
|
||||
-}
|
||||
-
|
||||
-static void adjust_dev_min_size(struct list_head *extents,
|
||||
- struct list_head *holes,
|
||||
- u64 *min_size)
|
||||
-{
|
||||
- /*
|
||||
- * If relocation of the block group of a device extent must happen (see
|
||||
- * below) scratch space is used for the relocation. So track here the
|
||||
- * size of the largest device extent that has to be relocated. We track
|
||||
- * only the largest and not the sum of the sizes of all relocated block
|
||||
- * groups because after each block group is relocated the running
|
||||
- * transaction is committed so that pinned space is released.
|
||||
- */
|
||||
- u64 scratch_space = 0;
|
||||
-
|
||||
- /*
|
||||
- * List of device extents is sorted by descending order of the extent's
|
||||
- * end offset. If some extent goes beyond the computed minimum size,
|
||||
- * which initially matches the sum of the lenghts of all extents,
|
||||
- * we need to check if the extent can be relocated to an hole in the
|
||||
- * device between [0, *min_size[ (which is what the resize ioctl does).
|
||||
- */
|
||||
- while (!list_empty(extents)) {
|
||||
- struct dev_extent_elem *e;
|
||||
- struct dev_extent_elem *h;
|
||||
- int found = 0;
|
||||
- u64 extent_len;
|
||||
- u64 hole_len = 0;
|
||||
-
|
||||
- e = list_first_entry(extents, struct dev_extent_elem, list);
|
||||
- if (e->end <= *min_size)
|
||||
- break;
|
||||
-
|
||||
- /*
|
||||
- * Our extent goes beyond the computed *min_size. See if we can
|
||||
- * find a hole large enough to relocate it to. If not we must stop
|
||||
- * and set *min_size to the end of the extent.
|
||||
- */
|
||||
- extent_len = e->end - e->start + 1;
|
||||
- list_for_each_entry(h, holes, list) {
|
||||
- hole_len = h->end - h->start + 1;
|
||||
- if (hole_len >= extent_len) {
|
||||
- found = 1;
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- if (!found) {
|
||||
- *min_size = e->end + 1;
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
- /*
|
||||
- * If the hole found contains the location for a superblock
|
||||
- * mirror, we are pessimistic and require allocating one
|
||||
- * more extent of the same size. This is because the block
|
||||
- * group could be in the worst case used by a single extent
|
||||
- * with a size >= (block_group.length - superblock.size).
|
||||
- */
|
||||
- if (hole_includes_sb_mirror(h->start,
|
||||
- h->start + extent_len - 1))
|
||||
- *min_size += extent_len;
|
||||
-
|
||||
- if (hole_len > extent_len) {
|
||||
- h->start += extent_len;
|
||||
- } else {
|
||||
- list_del(&h->list);
|
||||
- free(h);
|
||||
- }
|
||||
-
|
||||
- list_del(&e->list);
|
||||
- free(e);
|
||||
-
|
||||
- if (extent_len > scratch_space)
|
||||
- scratch_space = extent_len;
|
||||
- }
|
||||
-
|
||||
- if (scratch_space) {
|
||||
- *min_size += scratch_space;
|
||||
- /*
|
||||
- * Chunk allocation requires inserting/updating items in the
|
||||
- * chunk tree, so often this can lead to the need of allocating
|
||||
- * a new system chunk too, which has a maximum size of 32Mb.
|
||||
- */
|
||||
- *min_size += 32 * 1024 * 1024;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static int get_min_size(int fd, DIR *dirstream, const char *amount)
|
||||
-{
|
||||
- int ret = 1;
|
||||
- char *p = strstr(amount, ":");
|
||||
- u64 devid = 1;
|
||||
- /*
|
||||
- * Device allocations starts at 1Mb or at the value passed through the
|
||||
- * mount option alloc_start if it's bigger than 1Mb. The alloc_start
|
||||
- * option is used for debugging and testing only, and recently the
|
||||
- * possibility of deprecating/removing it has been discussed, so we
|
||||
- * ignore it here.
|
||||
- */
|
||||
- u64 min_size = 1 * 1024 * 1024ull;
|
||||
- struct btrfs_ioctl_search_args args;
|
||||
- struct btrfs_ioctl_search_key *sk = &args.key;
|
||||
- u64 last_pos = (u64)-1;
|
||||
- LIST_HEAD(extents);
|
||||
- LIST_HEAD(holes);
|
||||
-
|
||||
- if (p && sscanf(amount, "%llu:get_min_size", &devid) != 1) {
|
||||
- fprintf(stderr, "Invalid parameter: %s\n", amount);
|
||||
- goto out;
|
||||
- }
|
||||
-
|
||||
- memset(&args, 0, sizeof(args));
|
||||
- sk->tree_id = BTRFS_DEV_TREE_OBJECTID;
|
||||
- sk->min_objectid = devid;
|
||||
- sk->max_objectid = devid;
|
||||
- sk->max_type = BTRFS_DEV_EXTENT_KEY;
|
||||
- sk->min_type = BTRFS_DEV_EXTENT_KEY;
|
||||
- sk->min_offset = 0;
|
||||
- sk->max_offset = (u64)-1;
|
||||
- sk->min_transid = 0;
|
||||
- sk->max_transid = (u64)-1;
|
||||
- sk->nr_items = 4096;
|
||||
-
|
||||
- while (1) {
|
||||
- int i;
|
||||
- struct btrfs_ioctl_search_header *sh;
|
||||
- unsigned long off = 0;
|
||||
-
|
||||
- ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
|
||||
- if (ret < 0) {
|
||||
- fprintf(stderr,
|
||||
- "Error invoking tree search ioctl: %s\n",
|
||||
- strerror(errno));
|
||||
- ret = 1;
|
||||
- goto out;
|
||||
- }
|
||||
-
|
||||
- if (sk->nr_items == 0)
|
||||
- break;
|
||||
-
|
||||
- for (i = 0; i < sk->nr_items; i++) {
|
||||
- struct btrfs_dev_extent *extent;
|
||||
- u64 len;
|
||||
-
|
||||
- sh = (struct btrfs_ioctl_search_header *)(args.buf +
|
||||
- off);
|
||||
- off += sizeof(*sh);
|
||||
- extent = (struct btrfs_dev_extent *)(args.buf + off);
|
||||
- off += sh->len;
|
||||
-
|
||||
- sk->min_objectid = sh->objectid;
|
||||
- sk->min_type = sh->type;
|
||||
- sk->min_offset = sh->offset + 1;
|
||||
-
|
||||
- if (sh->objectid != devid ||
|
||||
- sh->type != BTRFS_DEV_EXTENT_KEY)
|
||||
- continue;
|
||||
-
|
||||
- len = btrfs_stack_dev_extent_length(extent);
|
||||
- min_size += len;
|
||||
- ret = add_dev_extent(&extents, sh->offset,
|
||||
- sh->offset + len - 1, 0);
|
||||
-
|
||||
- if (!ret && last_pos != (u64)-1 &&
|
||||
- last_pos != sh->offset)
|
||||
- ret = add_dev_extent(&holes, last_pos,
|
||||
- sh->offset - 1, 1);
|
||||
- if (ret) {
|
||||
- fprintf(stderr, "Error: %s\n", strerror(-ret));
|
||||
- ret = 1;
|
||||
- goto out;
|
||||
- }
|
||||
-
|
||||
- last_pos = sh->offset + len;
|
||||
- }
|
||||
-
|
||||
- if (sk->min_type != BTRFS_DEV_EXTENT_KEY ||
|
||||
- sk->min_objectid != devid)
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
- adjust_dev_min_size(&extents, &holes, &min_size);
|
||||
- printf("%llu bytes (%s)\n", min_size, pretty_size(min_size));
|
||||
- ret = 0;
|
||||
-out:
|
||||
- close_file_or_dir(fd, dirstream);
|
||||
- free_dev_extent_list(&extents);
|
||||
- free_dev_extent_list(&holes);
|
||||
-
|
||||
- return ret;
|
||||
-}
|
||||
-
|
||||
static int cmd_resize(int argc, char **argv)
|
||||
{
|
||||
struct btrfs_ioctl_vol_args args;
|
||||
@@ -1570,9 +1320,6 @@ static int cmd_resize(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
- if (strstr(amount, "get_min_size"))
|
||||
- return get_min_size(fd, dirstream, amount);
|
||||
-
|
||||
printf("Resize '%s' of '%s'\n", path, amount);
|
||||
memset(&args, 0, sizeof(args));
|
||||
strncpy_null(args.name, amount);
|
||||
diff --git a/cmds-inspect.c b/cmds-inspect.c
|
||||
index 71451fe..05f1ccf 100644
|
||||
--- a/cmds-inspect.c
|
||||
+++ b/cmds-inspect.c
|
||||
@@ -338,6 +338,247 @@ out:
|
||||
return !!ret;
|
||||
}
|
||||
|
||||
+struct dev_extent_elem {
|
||||
+ u64 start;
|
||||
+ /* inclusive end */
|
||||
+ u64 end;
|
||||
+ struct list_head list;
|
||||
+};
|
||||
+
|
||||
+static int add_dev_extent(struct list_head *list,
|
||||
+ const u64 start, const u64 end,
|
||||
+ const int append)
|
||||
+{
|
||||
+ struct dev_extent_elem *e;
|
||||
+
|
||||
+ e = malloc(sizeof(*e));
|
||||
+ if (!e)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ e->start = start;
|
||||
+ e->end = end;
|
||||
+
|
||||
+ if (append)
|
||||
+ list_add_tail(&e->list, list);
|
||||
+ else
|
||||
+ list_add(&e->list, list);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void free_dev_extent_list(struct list_head *list)
|
||||
+{
|
||||
+ while (!list_empty(list)) {
|
||||
+ struct dev_extent_elem *e;
|
||||
+
|
||||
+ e = list_first_entry(list, struct dev_extent_elem, list);
|
||||
+ list_del(&e->list);
|
||||
+ free(e);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int hole_includes_sb_mirror(const u64 start, const u64 end)
|
||||
+{
|
||||
+ int i;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ for (i = 0; i < BTRFS_SUPER_MIRROR_MAX; i++) {
|
||||
+ u64 bytenr = btrfs_sb_offset(i);
|
||||
+
|
||||
+ if (bytenr >= start && bytenr <= end) {
|
||||
+ ret = 1;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void adjust_dev_min_size(struct list_head *extents,
|
||||
+ struct list_head *holes,
|
||||
+ u64 *min_size)
|
||||
+{
|
||||
+ /*
|
||||
+ * If relocation of the block group of a device extent must happen (see
|
||||
+ * below) scratch space is used for the relocation. So track here the
|
||||
+ * size of the largest device extent that has to be relocated. We track
|
||||
+ * only the largest and not the sum of the sizes of all relocated block
|
||||
+ * groups because after each block group is relocated the running
|
||||
+ * transaction is committed so that pinned space is released.
|
||||
+ */
|
||||
+ u64 scratch_space = 0;
|
||||
+
|
||||
+ /*
|
||||
+ * List of device extents is sorted by descending order of the extent's
|
||||
+ * end offset. If some extent goes beyond the computed minimum size,
|
||||
+ * which initially matches the sum of the lenghts of all extents,
|
||||
+ * we need to check if the extent can be relocated to an hole in the
|
||||
+ * device between [0, *min_size[ (which is what the resize ioctl does).
|
||||
+ */
|
||||
+ while (!list_empty(extents)) {
|
||||
+ struct dev_extent_elem *e;
|
||||
+ struct dev_extent_elem *h;
|
||||
+ int found = 0;
|
||||
+ u64 extent_len;
|
||||
+ u64 hole_len = 0;
|
||||
+
|
||||
+ e = list_first_entry(extents, struct dev_extent_elem, list);
|
||||
+ if (e->end <= *min_size)
|
||||
+ break;
|
||||
+
|
||||
+ /*
|
||||
+ * Our extent goes beyond the computed *min_size. See if we can
|
||||
+ * find a hole large enough to relocate it to. If not we must stop
|
||||
+ * and set *min_size to the end of the extent.
|
||||
+ */
|
||||
+ extent_len = e->end - e->start + 1;
|
||||
+ list_for_each_entry(h, holes, list) {
|
||||
+ hole_len = h->end - h->start + 1;
|
||||
+ if (hole_len >= extent_len) {
|
||||
+ found = 1;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!found) {
|
||||
+ *min_size = e->end + 1;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * If the hole found contains the location for a superblock
|
||||
+ * mirror, we are pessimistic and require allocating one
|
||||
+ * more extent of the same size. This is because the block
|
||||
+ * group could be in the worst case used by a single extent
|
||||
+ * with a size >= (block_group.length - superblock.size).
|
||||
+ */
|
||||
+ if (hole_includes_sb_mirror(h->start,
|
||||
+ h->start + extent_len - 1))
|
||||
+ *min_size += extent_len;
|
||||
+
|
||||
+ if (hole_len > extent_len) {
|
||||
+ h->start += extent_len;
|
||||
+ } else {
|
||||
+ list_del(&h->list);
|
||||
+ free(h);
|
||||
+ }
|
||||
+
|
||||
+ list_del(&e->list);
|
||||
+ free(e);
|
||||
+
|
||||
+ if (extent_len > scratch_space)
|
||||
+ scratch_space = extent_len;
|
||||
+ }
|
||||
+
|
||||
+ if (scratch_space) {
|
||||
+ *min_size += scratch_space;
|
||||
+ /*
|
||||
+ * Chunk allocation requires inserting/updating items in the
|
||||
+ * chunk tree, so often this can lead to the need of allocating
|
||||
+ * a new system chunk too, which has a maximum size of 32Mb.
|
||||
+ */
|
||||
+ *min_size += 32 * 1024 * 1024;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int get_min_size(int fd, DIR *dirstream, u64 devid)
|
||||
+{
|
||||
+ int ret = 1;
|
||||
+ /*
|
||||
+ * Device allocations starts at 1Mb or at the value passed through the
|
||||
+ * mount option alloc_start if it's bigger than 1Mb. The alloc_start
|
||||
+ * option is used for debugging and testing only, and recently the
|
||||
+ * possibility of deprecating/removing it has been discussed, so we
|
||||
+ * ignore it here.
|
||||
+ */
|
||||
+ u64 min_size = 1 * 1024 * 1024ull;
|
||||
+ struct btrfs_ioctl_search_args args;
|
||||
+ struct btrfs_ioctl_search_key *sk = &args.key;
|
||||
+ u64 last_pos = (u64)-1;
|
||||
+ LIST_HEAD(extents);
|
||||
+ LIST_HEAD(holes);
|
||||
+
|
||||
+ memset(&args, 0, sizeof(args));
|
||||
+ sk->tree_id = BTRFS_DEV_TREE_OBJECTID;
|
||||
+ sk->min_objectid = devid;
|
||||
+ sk->max_objectid = devid;
|
||||
+ sk->max_type = BTRFS_DEV_EXTENT_KEY;
|
||||
+ sk->min_type = BTRFS_DEV_EXTENT_KEY;
|
||||
+ sk->min_offset = 0;
|
||||
+ sk->max_offset = (u64)-1;
|
||||
+ sk->min_transid = 0;
|
||||
+ sk->max_transid = (u64)-1;
|
||||
+ sk->nr_items = 4096;
|
||||
+
|
||||
+ while (1) {
|
||||
+ int i;
|
||||
+ struct btrfs_ioctl_search_header *sh;
|
||||
+ unsigned long off = 0;
|
||||
+
|
||||
+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
|
||||
+ if (ret < 0) {
|
||||
+ fprintf(stderr,
|
||||
+ "Error invoking tree search ioctl: %s\n",
|
||||
+ strerror(errno));
|
||||
+ ret = 1;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ if (sk->nr_items == 0)
|
||||
+ break;
|
||||
+
|
||||
+ for (i = 0; i < sk->nr_items; i++) {
|
||||
+ struct btrfs_dev_extent *extent;
|
||||
+ u64 len;
|
||||
+
|
||||
+ sh = (struct btrfs_ioctl_search_header *)(args.buf +
|
||||
+ off);
|
||||
+ off += sizeof(*sh);
|
||||
+ extent = (struct btrfs_dev_extent *)(args.buf + off);
|
||||
+ off += sh->len;
|
||||
+
|
||||
+ sk->min_objectid = sh->objectid;
|
||||
+ sk->min_type = sh->type;
|
||||
+ sk->min_offset = sh->offset + 1;
|
||||
+
|
||||
+ if (sh->objectid != devid ||
|
||||
+ sh->type != BTRFS_DEV_EXTENT_KEY)
|
||||
+ continue;
|
||||
+
|
||||
+ len = btrfs_stack_dev_extent_length(extent);
|
||||
+ min_size += len;
|
||||
+ ret = add_dev_extent(&extents, sh->offset,
|
||||
+ sh->offset + len - 1, 0);
|
||||
+
|
||||
+ if (!ret && last_pos != (u64)-1 &&
|
||||
+ last_pos != sh->offset)
|
||||
+ ret = add_dev_extent(&holes, last_pos,
|
||||
+ sh->offset - 1, 1);
|
||||
+ if (ret) {
|
||||
+ fprintf(stderr, "Error: %s\n", strerror(-ret));
|
||||
+ ret = 1;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ last_pos = sh->offset + len;
|
||||
+ }
|
||||
+
|
||||
+ if (sk->min_type != BTRFS_DEV_EXTENT_KEY ||
|
||||
+ sk->min_objectid != devid)
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ adjust_dev_min_size(&extents, &holes, &min_size);
|
||||
+ printf("%llu bytes (%s)\n", min_size, pretty_size(min_size));
|
||||
+ ret = 0;
|
||||
+out:
|
||||
+ close_file_or_dir(fd, dirstream);
|
||||
+ free_dev_extent_list(&extents);
|
||||
+ free_dev_extent_list(&holes);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static const char inspect_cmd_group_info[] =
|
||||
"query various internal information";
|
||||
|
||||
--
|
||||
1.8.4.5
|
||||
|
@ -1,216 +0,0 @@
|
||||
From 017403c727f30a45cb1fb81e025f7e6962da246f Mon Sep 17 00:00:00 2001
|
||||
From: David Sterba <dsterba@suse.com>
|
||||
Date: Mon, 20 Jul 2015 17:31:43 +0200
|
||||
Subject: [PATCH 2/2] btrfs-progs: inspect: add command min-dev-size
|
||||
|
||||
Previously in 'filesystem resize get_min_size', now
|
||||
'inspect-internal min-dev-size'. We'd like to avoid cluttering the
|
||||
'resize' syntax further.
|
||||
|
||||
The test has been updated to exercise the new option.
|
||||
|
||||
Signed-off-by: David Sterba <dsterba@suse.com>
|
||||
Signed-off-by: Filipe Manana <fdmanana@suse.com>
|
||||
---
|
||||
Documentation/btrfs-filesystem.asciidoc | 4 +-
|
||||
Documentation/btrfs-inspect-internal.asciidoc | 9 ++++
|
||||
btrfs-completion | 2 +-
|
||||
cmds-inspect.c | 60 +++++++++++++++++++++++++--
|
||||
tests/misc-tests/004-shrink-fs/test.sh | 11 ++---
|
||||
5 files changed, 74 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/Documentation/btrfs-filesystem.asciidoc b/Documentation/btrfs-filesystem.asciidoc
|
||||
index 2b34242..31cd51b 100644
|
||||
--- a/Documentation/btrfs-filesystem.asciidoc
|
||||
+++ b/Documentation/btrfs-filesystem.asciidoc
|
||||
@@ -93,7 +93,7 @@ If a newlabel optional argument is passed, the label is changed.
|
||||
NOTE: the maximum allowable length shall be less than 256 chars
|
||||
|
||||
// Some wording are extracted by the resize2fs man page
|
||||
-*resize* [<devid>:][+/-]<size>[kKmMgGtTpPeE]|[<devid>:]max|[<devid>:]get_min_size <path>::
|
||||
+*resize* [<devid>:][+/-]<size>[kKmMgGtTpPeE]|[<devid>:]max <path>::
|
||||
Resize a mounted filesystem identified by directory <path>. A particular device
|
||||
can be resized by specifying a <devid>.
|
||||
+
|
||||
@@ -113,8 +113,6 @@ KiB, MiB, GiB, TiB, PiB, or EiB, respectively. Case does not matter.
|
||||
+
|
||||
If \'max' is passed, the filesystem will occupy all available space on the
|
||||
device devid.
|
||||
-If \'get_min_size' is passed, return the minimum size the device can be
|
||||
-shrunk to, without performing any resize operation.
|
||||
+
|
||||
The resize command does not manipulate the size of underlying
|
||||
partition. If you wish to enlarge/reduce a filesystem, you must make sure you
|
||||
diff --git a/Documentation/btrfs-inspect-internal.asciidoc b/Documentation/btrfs-inspect-internal.asciidoc
|
||||
index 9f6ffac..f3f915b 100644
|
||||
--- a/Documentation/btrfs-inspect-internal.asciidoc
|
||||
+++ b/Documentation/btrfs-inspect-internal.asciidoc
|
||||
@@ -41,6 +41,15 @@ set inode container's size.
|
||||
This is used to increase inode container's size in case it is
|
||||
not enough to read all the resolved results. The max value one can set is 64k.
|
||||
|
||||
+*min-dev-size* [options] <path>::
|
||||
+Return the minimum size the device can be shrunk to, without performing any
|
||||
+resize operation.
|
||||
++
|
||||
+`Options`
|
||||
++
|
||||
+--id::::
|
||||
+specify the device id to query, default is 1
|
||||
+
|
||||
*rootid* <path>::
|
||||
For a given file or directory, return the containing tree root id. For a
|
||||
subvolume return it's own tree id.
|
||||
diff --git a/btrfs-completion b/btrfs-completion
|
||||
index 884d2e8..a34191b 100644
|
||||
--- a/btrfs-completion
|
||||
+++ b/btrfs-completion
|
||||
@@ -36,7 +36,7 @@ _btrfs()
|
||||
commands_device='scan add delete remove ready stats usage'
|
||||
commands_scrub='start cancel resume status'
|
||||
commands_rescue='chunk-recover super-recover'
|
||||
- commands_inspect_internal='inode-resolve logical-resolve subvolid-resolve rootid'
|
||||
+ commands_inspect_internal='inode-resolve logical-resolve subvolid-resolve rootid min-dev-size'
|
||||
commands_property='get set list'
|
||||
commands_quota='enable disable rescan'
|
||||
commands_qgroup='assign remove create destroy show limit'
|
||||
diff --git a/cmds-inspect.c b/cmds-inspect.c
|
||||
index 05f1ccf..1823584 100644
|
||||
--- a/cmds-inspect.c
|
||||
+++ b/cmds-inspect.c
|
||||
@@ -20,13 +20,14 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
+#include <getopt.h>
|
||||
|
||||
#include "kerncompat.h"
|
||||
#include "ioctl.h"
|
||||
#include "utils.h"
|
||||
#include "ctree.h"
|
||||
#include "send-utils.h"
|
||||
-
|
||||
+#include "disk-io.h"
|
||||
#include "commands.h"
|
||||
#include "btrfs-list.h"
|
||||
|
||||
@@ -481,7 +482,7 @@ static void adjust_dev_min_size(struct list_head *extents,
|
||||
}
|
||||
}
|
||||
|
||||
-static int get_min_size(int fd, DIR *dirstream, u64 devid)
|
||||
+static int print_min_dev_size(int fd, u64 devid)
|
||||
{
|
||||
int ret = 1;
|
||||
/*
|
||||
@@ -572,13 +573,64 @@ static int get_min_size(int fd, DIR *dirstream, u64 devid)
|
||||
printf("%llu bytes (%s)\n", min_size, pretty_size(min_size));
|
||||
ret = 0;
|
||||
out:
|
||||
- close_file_or_dir(fd, dirstream);
|
||||
free_dev_extent_list(&extents);
|
||||
free_dev_extent_list(&holes);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static const char* const cmd_inspect_min_dev_size_usage[] = {
|
||||
+ "btrfs inspect-internal min-dev-size [options] <path>",
|
||||
+ "Get the minimum size the device can be shrunk to. The",
|
||||
+ "device id 1 is used by default.",
|
||||
+ "--id DEVID specify the device id to query",
|
||||
+ NULL
|
||||
+};
|
||||
+
|
||||
+static int cmd_inspect_min_dev_size(int argc, char **argv)
|
||||
+{
|
||||
+ int ret;
|
||||
+ int fd = -1;
|
||||
+ DIR *dirstream = NULL;
|
||||
+ u64 devid = 1;
|
||||
+
|
||||
+ while (1) {
|
||||
+ int c;
|
||||
+ enum { GETOPT_VAL_DEVID = 256 };
|
||||
+ static const struct option long_options[] = {
|
||||
+ { "id", required_argument, NULL, GETOPT_VAL_DEVID },
|
||||
+ {NULL, 0, NULL, 0}
|
||||
+ };
|
||||
+
|
||||
+ c = getopt_long(argc, argv, "", long_options, NULL);
|
||||
+ if (c < 0)
|
||||
+ break;
|
||||
+
|
||||
+ switch (c) {
|
||||
+ case GETOPT_VAL_DEVID:
|
||||
+ devid = arg_strtou64(optarg);
|
||||
+ break;
|
||||
+ default:
|
||||
+ usage(cmd_inspect_min_dev_size_usage);
|
||||
+ }
|
||||
+ }
|
||||
+ if (check_argc_exact(argc - optind, 1))
|
||||
+ usage(cmd_inspect_min_dev_size_usage);
|
||||
+
|
||||
+ fd = open_file_or_dir(argv[optind], &dirstream);
|
||||
+ if (fd < 0) {
|
||||
+ fprintf(stderr, "ERROR: can't access '%s'\n", argv[optind]);
|
||||
+ ret = -ENOENT;
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ ret = print_min_dev_size(fd, devid);
|
||||
+out:
|
||||
+ close_file_or_dir(fd, dirstream);
|
||||
+
|
||||
+ return !!ret;
|
||||
+}
|
||||
+
|
||||
static const char inspect_cmd_group_info[] =
|
||||
"query various internal information";
|
||||
|
||||
@@ -591,6 +643,8 @@ const struct cmd_group inspect_cmd_group = {
|
||||
{ "subvolid-resolve", cmd_subvolid_resolve,
|
||||
cmd_subvolid_resolve_usage, NULL, 0 },
|
||||
{ "rootid", cmd_rootid, cmd_rootid_usage, NULL, 0 },
|
||||
+ { "min-dev-size", cmd_inspect_min_dev_size,
|
||||
+ cmd_inspect_min_dev_size_usage, NULL, 0 },
|
||||
NULL_CMD_STRUCT
|
||||
}
|
||||
};
|
||||
diff --git a/tests/misc-tests/004-shrink-fs/test.sh b/tests/misc-tests/004-shrink-fs/test.sh
|
||||
index 393cccf..b132152 100644
|
||||
--- a/tests/misc-tests/004-shrink-fs/test.sh
|
||||
+++ b/tests/misc-tests/004-shrink-fs/test.sh
|
||||
@@ -9,14 +9,15 @@ source $TOP/tests/common
|
||||
check_prereq mkfs.btrfs
|
||||
setup_root_helper
|
||||
|
||||
+# Optionally take id of the device to shrink
|
||||
shrink_test()
|
||||
{
|
||||
- min_size=$($SUDO_HELPER $TOP/btrfs filesystem resize get_min_size $TEST_MNT)
|
||||
- if [ $? != 0 ]; then
|
||||
- _fail "Failed to get minimum size"
|
||||
- fi
|
||||
+ min_size=$(run_check_stdout $SUDO_HELPER $TOP/btrfs inspect-internal min-dev-size ${1:+--id $1} $TEST_MNT)
|
||||
min_size=$(echo $min_size | cut -d ' ' -f 1)
|
||||
echo "min size = ${min_size}" >> $RESULTS
|
||||
+ if [ -z "$min_size" ]; then
|
||||
+ _fail "Failed to parse minimum size"
|
||||
+ fi
|
||||
run_check $SUDO_HELPER $TOP/btrfs filesystem resize $min_size $TEST_MNT
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ done
|
||||
run_check $SUDO_HELPER $TOP/btrfs balance start -mconvert=single \
|
||||
-sconvert=single -f $TEST_MNT
|
||||
for ((i = 1; i <= 3; i++)); do
|
||||
- shrink_test
|
||||
+ shrink_test 1
|
||||
done
|
||||
|
||||
run_check $SUDO_HELPER umount $TEST_MNT
|
||||
--
|
||||
1.8.4.5
|
||||
|
@ -1,3 +0,0 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:c7434037d9d0a1f580822798f67c2c30e1696f595f54314e703c9e7d2407fc58
|
||||
size 1362800
|
3
btrfs-progs-v4.2.tar.gz
Normal file
3
btrfs-progs-v4.2.tar.gz
Normal file
@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fa4c00f124d531bf420d3bbb0b0e14f4663322bdaa631558d42f7106cf3ee3a6
|
||||
size 1370590
|
@ -1,3 +1,36 @@
|
||||
-------------------------------------------------------------------
|
||||
Thu Sep 3 00:00:00 CEST 2015 - dsterba@suse.cz
|
||||
|
||||
- version 4.2
|
||||
* enhancements:
|
||||
* mkfs: do not create extra single chunks on multiple devices
|
||||
* resize: try to guess the minimal size, 'inspect min-dev-size'
|
||||
* qgroup assign: add option to schedule rescan
|
||||
* chunk-recover: be more verbose about the scanning process
|
||||
* fixes:
|
||||
* check:
|
||||
* find stripes crossing stripe boundary -- created by convert
|
||||
* print correct range for file hole when there are no extents
|
||||
and learn how to fix it
|
||||
* replace: more sanity checks
|
||||
* convert: concurrency fixes related to reporting progress
|
||||
* find-root: option -a will not skip the current root anymore
|
||||
* subvol list: fix occasional crash
|
||||
* do not create stripes crossing stripe boundary
|
||||
* build:
|
||||
* fixes for musl libc
|
||||
* preliminary support for android (not working yet, more code changes needed)
|
||||
* new EXTRA_CFLAGS and EXTRA_LDFLAGS
|
||||
* other:
|
||||
* lots of cleanups
|
||||
* tests: lots of updates, new tests, framework improvements
|
||||
* documentation updates
|
||||
* debugging: print-tree shows stripe length
|
||||
- Removed patches (upstreamed):
|
||||
* 2104-get-min-size-for-resize.patch
|
||||
* 2105-move-min-resize-implementation-to-inspec.patch
|
||||
* 2106-inspect-add-command-min-dev-size.patch
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Tue Sep 1 00:00:00 CEST 2015 - dsterba@suse.cz
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
|
||||
Name: btrfsprogs
|
||||
Version: 4.1.2
|
||||
Version: 4.2
|
||||
Release: 0
|
||||
Summary: Utilities for the Btrfs filesystem
|
||||
License: GPL-2.0
|
||||
@ -33,9 +33,6 @@ Source4: setup-btrfs.sh
|
||||
Patch163: 0163-btrfs-progs-fsck-fix-segfault.patch
|
||||
Patch167: 0167-Btrfs-progs-make-find_and_setup_root-return-an-error.patch
|
||||
Patch168: 0168-Btrfs-progs-don-t-bug-out-if-we-can-t-find-the-last-.patch
|
||||
Patch2104: 2104-get-min-size-for-resize.patch
|
||||
Patch2105: 2105-move-min-resize-implementation-to-inspec.patch
|
||||
Patch2106: 2106-inspect-add-command-min-dev-size.patch
|
||||
|
||||
Patch1000: local-version-override.patch
|
||||
Patch1001: fix-doc-build-on-SLE11SP3.diff
|
||||
@ -85,9 +82,6 @@ build applications to interface with btrfs.
|
||||
%patch163 -p1
|
||||
%patch167 -p1
|
||||
%patch168 -p1
|
||||
%patch2104 -p1
|
||||
%patch2105 -p1
|
||||
%patch2106 -p1
|
||||
%patch1000 -p1
|
||||
%patch1001 -p1
|
||||
%patch1002 -p1
|
||||
@ -212,6 +206,7 @@ done
|
||||
%{_mandir}/man8/btrfs-scrub.8.gz
|
||||
%{_mandir}/man8/btrfs-send.8.gz
|
||||
%{_mandir}/man8/btrfs-subvolume.8.gz
|
||||
%{_mandir}/man8/btrfs-select-super.8.gz
|
||||
%dir %{_datadir}/bash-completion
|
||||
%dir %{_datadir}/bash-completion/completions
|
||||
%{_datadir}/bash-completion/completions/btrfs
|
||||
|
@ -6,8 +6,8 @@ Index: btrfs-progs-v4.1/version.sh
|
||||
# Copyright 2008, Oracle
|
||||
# Released under the GNU GPLv2
|
||||
|
||||
-v="v4.1.2"
|
||||
+v="v4.1.2+20150724"
|
||||
-v="v4.2"
|
||||
+v="v4.2+20150903"
|
||||
|
||||
opt=$1
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user