Skip to contentSkip to conversation

btrfs-progs: prevent mkfs from aborting with small volume

PATCH New Hidetoshi Seto 1 participant 4 messages Sep 5, 2013 Btrfs file system

lore
Share & tools
lore
#0 lore raw

v2 cover btrfs-progs: prevent mkfs from aborting with small volume

Here are 3 patches to avoid undesired aborts of mkfs.btrfs. These are based on top of Chris's btrfs-progs.git:

git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-progs.git

Thanks,
H.Seto

Hidetoshi Seto (3):
btrfs-progs: error if device for mkfs is too small
btrfs-progs: error if device have no space to make primary chunks
btrfs-progs: calculate available blocks on device properly

 ctree.h   |    8 +++++
 mkfs.c    |   23 +++++++++++++
 volumes.c |  104 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 129 insertions(+), 6 deletions(-)
#1 lore raw

PATCH v2 1/3 btrfs-progs: error if device for mkfs is too small

Eric pointed out that mkfs abort if specified volume is too small:

  # truncate --size=2m testfile
  # ./mkfs.btrfs testfile
   :
  SMALL VOLUME: forcing mixed metadata/data groups
  mkfs.btrfs: volumes.c:852: btrfs_alloc_chunk: Assertion `!(ret)' failed.
  Aborted (core dumped)

As the first step to fix problems around there, let mkfs to report error if the size of target volume is less than the size of the first system block group, BTRFS_MKFS_SYSTEM_GROUP_SIZE (= 4MB).

---
 mkfs.c |    6 ++++++
 1 files changed, 6 insertions(+), 0 deletions(-)
mkfs.c +6 -0
index b412b7e..a98fe54 100644
@@ -1422,6 +1422,12 @@ int main(int ac, char **av)
 		}
 	}
 
+	/* To create the first block group and chunk 0 in make_btrfs */
+	if (dev_block_count < BTRFS_MKFS_SYSTEM_GROUP_SIZE) {
+		fprintf(stderr, "device is too small to make filesystem\n");
+		exit(1);
+	}
+
 	blocks[0] = BTRFS_SUPER_INFO_OFFSET;
 	for (i = 1; i < 7; i++) {
 		blocks[i] = BTRFS_SUPER_INFO_OFFSET + 1024 * 1024 +
Signature · 1 line
1.7.1
#2 lore raw

PATCH v2 2/3 btrfs-progs: error if device have no space to make primary chunks

In reply to Hidetoshi Seto

The previous patch works fine if the size of specified volume to mkfs is less than 4MB. However usually btrfs requires more than 4MB to work, and the minimum preferred size is depending on the raid setting etc.

This patch let mkfs print error message if it cannot allocate one of chunks should be there at first.

[before]

  # truncate --size=4500K testfile
  # ./mkfs.btrfs -f testfile
   :
  SMALL VOLUME: forcing mixed metadata/data groups
  mkfs.btrfs: mkfs.c:84: make_root_dir: Assertion `!(ret)' failed.
  Aborted (core dumped)

[After]

  # truncate --size=4500K testfile
  # ./mkfs.btrfs -f testfile
   :
  SMALL VOLUME: forcing mixed metadata/data groups
  no space to alloc data/metadata chunk
  failed to setup the root directory

TBD is calculate minimum size for setting and put it in the error message to let user know how large amount of volume is required.

---
 mkfs.c |   17 +++++++++++++++++
 1 files changed, 17 insertions(+), 0 deletions(-)
mkfs.c +17 -0
index a98fe54..bac122f 100644
@@ -81,6 +81,11 @@ static int make_root_dir(struct btrfs_root *root, int mixed)
 					&chunk_start, &chunk_size,
 					BTRFS_BLOCK_GROUP_METADATA |
 					BTRFS_BLOCK_GROUP_DATA);
+		if (ret == -ENOSPC) {
+			fprintf(stderr,
+				"no space to alloc data/metadata chunk\n");
+			goto err;
+		}
 		BUG_ON(ret);
 		ret = btrfs_make_block_group(trans, root, 0,
 					     BTRFS_BLOCK_GROUP_METADATA |
@@ -93,6 +98,10 @@ static int make_root_dir(struct btrfs_root *root, int mixed)
 		ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
 					&chunk_start, &chunk_size,
 					BTRFS_BLOCK_GROUP_METADATA);
+		if (ret == -ENOSPC) {
+			fprintf(stderr, "no space to alloc metadata chunk\n");
+			goto err;
+		}
 		BUG_ON(ret);
 		ret = btrfs_make_block_group(trans, root, 0,
 					     BTRFS_BLOCK_GROUP_METADATA,
@@ -110,6 +119,10 @@ static int make_root_dir(struct btrfs_root *root, int mixed)
 		ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
 					&chunk_start, &chunk_size,
 					BTRFS_BLOCK_GROUP_DATA);
+		if (ret == -ENOSPC) {
+			fprintf(stderr, "no space to alloc data chunk\n");
+			goto err;
+		}
 		BUG_ON(ret);
 		ret = btrfs_make_block_group(trans, root, 0,
 					     BTRFS_BLOCK_GROUP_DATA,
@@ -181,6 +194,10 @@ static int create_one_raid_group(struct btrfs_trans_handle *trans,
 
 	ret = btrfs_alloc_chunk(trans, root->fs_info->extent_root,
 				&chunk_start, &chunk_size, type);
+	if (ret == -ENOSPC) {
+		fprintf(stderr, "not enough free space\n");
+		exit(1);
+	}
 	BUG_ON(ret);
 	ret = btrfs_make_block_group(trans, root->fs_info->extent_root, 0,
 				     type, BTRFS_FIRST_CHUNK_TREE_OBJECTID,
Signature · 1 line
1.7.1
#3 lore raw

PATCH v2 3/3 btrfs-progs: calculate available blocks on device properly

In reply to Hidetoshi Seto

I found that mkfs.btrfs aborts when assigned multi volumes contain a small volume:

  # parted /dev/sdf p
  Model: LSI MegaRAID SAS RMB (scsi)
  Disk /dev/sdf: 72.8GB
  Sector size (logical/physical): 512B/512B
  Partition Table: msdos
  Number  Start   End     Size    Type     File system  Flags
   1      32.3kB  72.4GB  72.4GB  primary
   2      72.4GB  72.8GB  461MB   primary
  # ./mkfs.btrfs -f /dev/sdf1 /dev/sdf2
  :
  SMALL VOLUME: forcing mixed metadata/data groups
  adding device /dev/sdf2 id 2
  mkfs.btrfs: volumes.c:852: btrfs_alloc_chunk: Assertion `!(ret)' failed.
  Aborted (core dumped)

This failure of btrfs_alloc_chunk was caused by following steps:
1) since there is only small space in the small device, mkfs was going to allocate a chunk from free space as much as available. So mkfs called btrfs_alloc_chunk with
size = device->total_bytes - device->used_bytes.
2) (According to the comment in source code, to avoid overwriting superblock,) btrfs_alloc_chunk starts taking chunks at an offset of 1MB. It means that the layout of a disk will be like:
[[1MB at beginning for sb][allocated chunks]* ... free space ... ]
and you can see that the available free space for allocation is:
avail = device->total_bytes - device->used_bytes - 1MB.
3) Therefore there is only free space 1MB less than requested. damn.

Quoted 1 line

From further investigations I also found that this issue is easily

reproduced by using -A, --alloc-start option:

  # truncate --size=1G testfile
  # ./mkfs.btrfs -A900M -f testfile
   :
  mkfs.btrfs: volumes.c:852: btrfs_alloc_chunk: Assertion `!(ret)' failed.
  Aborted (core dumped)

In this case there is only 100MB for allocation but btrfs_alloc_chunk was going to allocate more than the 100MB.

The root cause of both of above troubles is a same simple bug:
btrfs_chunk_alloc does not calculate available bytes properly even though it researches how many devices have enough room to have a chunk to be allocated.

So this patch introduces new function btrfs_device_avail_bytes() which returns available bytes for allocation in specified device.

---
 ctree.h   |    8 +++++
 volumes.c |  104 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 106 insertions(+), 6 deletions(-)
ctree.h +8 -0
index 0b0d701..90be7ab 100644
@@ -811,6 +811,14 @@ struct btrfs_csum_item {
 	u8 csum;
 } __attribute__ ((__packed__));
 
+/*
+ * We don't want to overwrite 1M at the beginning of device, even though
+ * there is our 1st superblock at 64k. Some possible reasons:
+ *  - the first 64k blank is useful for some boot loader/manager
+ *  - the first 1M could be scratched by buggy partitioner or somesuch
+ */
+#define BTRFS_BLOCK_RESERVED_1M_FOR_SUPER	((u64)1024 * 1024)
+
 /* tag for the radix tree of block groups in ram */
 #define BTRFS_BLOCK_GROUP_DATA		(1ULL << 0)
 #define BTRFS_BLOCK_GROUP_SYSTEM	(1ULL << 1)
volumes.c +98 -6

135 lines not on this page · raw message

Signature · 1 line
1.7.1