I have a two-disk btrfs filesystem, with the data and metadata both in RAID1 (via the btrfs feature, not mdraid). The disks are USB3 drives, with dm-crypt on top. One of the disks is failing (it has several thousand bad sectors, and writes often timeout). I have obtained a third USB drive to replace the failing one, how can I replace it?
Linux – How to Replace a Failing Disk in RAID1 Btrfs
btrfshard-disklinux
Related Solutions
Btrfs supports duplicated data blocks if you enable mixed block groups:
mkfs.btrfs --mixed --metadata dup --data dup /dev/<device>
EDIT: Notice that there is a patch so that we can do this without using mixed mode. Following that thread of Nov. 2015, it appears it is being added to the mainline btrfs code.
Warning: Don't use GParted to resize a multi-device BTRFS filesystem!
Data loss likely to happen!
Looks like GParted is the culprit. Apparently, it doesn't know how to resize multi-device btrfs filesystems and just runs btrfs filesystem resize
but does not take care of each device using resize 1:49g
and resize 2:49g
.
This is what btrfs filesystem show
said:
Total devices 2 FS bytes used 40.95GiB
devid 1 size 50.00GiB used 43.03GiB path /dev/sda2
devid 2 size 439.45GiB used 43.03GiB path /dev/sdb2
Obviously, GParted has not resized the filesystem on the second device, so Btrfs still thinks it's a 500 GB filesystem.
At some point, Btrfs would have tried to read/write data outside of the disk. This would have caused corruption, it might even have rendered the whole filesystem useless.
Fix
First, a live system was booted to reduce the risk of damage while the system is running on a filesystem that's bigger than the partition it's on.
Mount the filesystem:
mount /dev/sda2 /mnt/tmp
First shrink the second device to 50 GB - 1 GB = 49 GB to avoid rounding errors:
btrfs filesystem resize 2:49g /mnt/tmp
Resize it to fill the 50 GB partition:
btrfs filesystem resize 2:max /mnt/tmp
Check btrfs filesystem show
, both devices should have the right size:
devid 1 size 50.00GiB used 43.03GiB path /dev/sda2
devid 2 size 50.00GiB used 43.03GiB path /dev/sdb2
Unmount, reboot, the error should be gone.
If nothing was damaged while the system was running in that bad state, the filesystem should be healthy now.
(Thanks to darkling who helped me fix this.)
Best Answer
This has turned out to be a royal PITA. First, it's important to note that btrfs now has a proper replace command, which is very much better than add new, remove failing.
First, start by partitioning the new disk and setting up dm-crypt on it. Go ahead and unlock it.
If your disk wasn't having writes time out (which takes 360s each, apparently!) you could do a simple:
However, that will wind up doing somewhat routine writes to the bad disk, and if those cause a timeout—you'll see around 30s of fast copy, followed by 6–12 minutes of idle, waiting for timeout.
In order to avoid writes to it, it's possible to set up a snapshot using device-mapper. Reads will go to the underlying bad device (which is mostly OK with reads); writes will go to the copy-on-write (COW) storage. First, you need a suitably large block device for the COW storage. I created a new logical volume for it (
Watt-sdj1_dmsnap
). Any block device should work—even a loop device should be fine. I personally suggest a persistent one, just in case something goes wrong, but if you're living dangerously and have sufficient RAM, a RAM disk would work.Mine would up requiring ~1.7GB of COW space (to move 2.24 TiB off a 3TB drive). I'd recommend being generous with the COW space; running out would probably be a bad thing, and you get to free it all up once you're done.
Next, you need to unmount the btrfs filesystem if it was mounted, and lock (stop) the dm-crypt device. I'm putting the snapshot below the encryption because I don't want unencrypted data written to disk.
In my case, the partition is
/dev/sdj1
. First, to avoid any mistakes, set it read-only:(You can later set it back with
--setrw
). Now, actually set up the snapshot:To quickly explain what that means, a device-mapper table has the format: start-sector number-of-sectors target-type target-arguments. The start sector is 0; the number of sectors is the same as the size of sdj1 (we want to do the entire thing after all); the target type is snapshot. The snapshot target takes several arguments: source-dev cow-dev mode chunk-size. We're giving a source device of
/dev/sdj1
; the COW device is that logical volume I created; the mode PO means persistent (metadata is written to disk, so it can be set back up after a reboot) and overflow (if we write to much to the snapshot, recovery is possible). The chunk size is how granular the snapshot is; if we write even one byte, the entire chunk around that byte will be copied over (and consume space in the snapshot). 8 is 4K, so there won't be alignment issues.Now, finally, unlock the device again—but instead of unlocking
/dev/sdj1
, unlock/dev/mapper/sdj_divert
. Then go ahead and mount the btrfs filesystem again.You can check the snapshot usage with
dmsetup status sdj_divert
; that should give something like (but with a much lower number before the slash):The first three things are the start-sector, number-of-sectors, and target-type. The next number is the number of sectors used (before the slash) and then the number of sectors total (after the slash). So that's a fraction of the space used. The final number is the number of sectors used for metadata, which is already included in the number used.
Now, finally, you can use that simple
btrfs replace start
command at the top of the answer. That will return immediately; watch the status by runningbtrfs replace status /mount/path
.When the replace is done, confirm the bad device has been dropped out of the filesystem (e.g.,
btrfs fi show /mount/path
) and then you can lock/close the failing drive and then remove the snapshot (dmsetup remove sdj_divert
). Then you can free up the COW space (after wiping it, if you're paranoid).There is one final, technically optional, step: my replacement device is larger, but btrfs isn't yet using the extra space. In order to make it available to btrfs, look find the devid in the
btrfs fi show
output, and then runthat should be nearly instant.