13

I thought I might experement with btrfs as the root partition to see how it handles file corruption during power cuts. But I can't get it to boot.

What I did:

  1. on the PI before switching:

    apt-get install btrfs-tools 2. From a linux computer:

    btrfs-convert /dev/sda2

  2. In /etc/fstab change ext4 to btrfs

  3. In /cmdline.txt change ext4 to btrfs

I get a kernel panic if I try do boot. Should I do anything else?

GuySoft
  • 895
  • 2
  • 9
  • 25
  • check this post, it provides a step by step method to convert the raspbian root partition into a btrfs file system: https://feldspaten.org/2019/07/05/raspbian-and-btrfs/ ther is also that one which is much more comprehensive: https://hackmd.io/FP-7sHiPTJGaJvzSa3nw8A found here (with a video on why to do this): https://hackaday.com/2017/06/18/btrfs-for-the-pi/ – cainbantam Nov 21 '20 at 20:39
  • *"I thought I might experement with btrfs as the root partition to see how it handles file corruption during power cuts."* It won't. That protection applies only to writes in progress, which on most mediums probably would be adequate protection. But not, I think, with an SD card. Worth a read: https://unix.stackexchange.com/a/520063/25985 Note that it requires certain functionality from hardware which I do not think an SD card can provide; the problem with them is that you stand to loose an entire 4MB block *containing arbitrary data*, not simply a write in progress. – goldilocks Nov 22 '20 at 15:04
  • ...To guard against that you'd have to combine it with something like RAID, and even then it is unlikely to be bulletproof. – goldilocks Nov 22 '20 at 15:04

4 Answers4

8

If btrfs is compiled as a kernel module, then you need to create an initramfs to load the module at boot. On Raspian (and other debian derivatives), update-initramfs is the easiest method to do this.

If initramfs-tools is installed, then any time apt-get installs a new kernel, it should trigger update-initramfs automatically.

sudo apt-get update
sudo apt-get install initramfs-tools

However, if you use rpi-update to install a new kernel, then you'll need to run update-initramfs manually before rebooting into the new kernel:

sudo update-initramfs -u -k <kernel-version>

This will create or update the initramfs in /boot/initrd.img-<kernel-version>.

The final step is to add it to your boot config: add the following line to /boot/config.txt:

initramfs initrd.img-<kernel-version> followkernel

initrd-<kernel-version> must match exactly the name of the file in /boot.

You'll need to repeat these steps every time you run rpi-update.

bennettp123
  • 261
  • 2
  • 5
5

Raspbian kernel doesn't include support btrfs by default; the initial boot stages run normally, but when the kernel loads, it won't see any filesystem which it could mount - and panics. A solution exists: add btrfs as a kernel module, in initramfs. Largely thanks to three different articles, I have set it up thus:

  • Install the required packages - the kernel module, and the tools to update initramfs with it: sudo apt install btrfs-tools initramfs-tools
  • Tell initramfs to load the btrfs module (should be happening automagically, for some reason, didn't work on my RPi1) - append a line with "btrfs" to the list of required modules: echo 'btrfs' | sudo tee -a /etc/initramfs-tools/modules
  • Create an initramfs hook (for building the image) and script (for booting) for btrfs - defaults are provided, but in my testing, they were not used automagically, had to copy them into /etc. sudo mkdir -p /etc/initramfs-tools/hooks ; sudo mkdir -p /etc/initramfs-tools/scripts/local-premount ; sudo cp /usr/share/initramfs-tools/hooks/btrfs /etc/initramfs-tools/hooks ; sudo cp /usr/share/initramfs-tools/scripts/local-premount/btrfs /etc/initramfs-tools/scripts/local-premount; sudo chmod +x /etc/initramfs-tools/hooks/btrfs /etc/initramfs-tools/scripts/local-premount/btrfs
  • Create (-c) the new initramfs for current kernel version (uname -r) - if you're updating an existing one, you'll need to use update (-u) instead. This will create a file named like /boot/initrd.img-*, where the * is the current kernel version. Note the name generated (the script will output it), we'll use it in next step. update-initramfs -c -k $(uname -r)
  • Edit /boot/config.txt to use this initramfs, adding initramfs initrd.img-3.11.0+ followkernel The file name is without path, it's the one generated in previous step; "followkernel" controls location in memory (config.txt documentation).
  • That solves the current kernel, but as @Ingo pointed out, upgrading the kernel would break the system. To fix this, I used his kernel-install hook scripts:

    • Edit /etc/default/raspberrypi-kernel and uncomment INITRD=Yes
    • delete /etc/kernel/postinst.d/initramfs-tools
    • add rpi-initramfs-tools to /etc/kernel/postinst.d/ and chmod +x it
    • optionally, download update-rpi-initramfs for simpler manual updates of the initramfs.
  • At this point, we have a system which could use btrfs as root device. Test by rebooting: the system will still boot from the ext4 partition (or whatever is in your /boot/cmdline.txt), but dmesg | grep -i btrfs should now show a line containing "Btrfs loaded". Now we need to actually make and use a btrfs partition.

  • Make a backup of the / (ext4) partition - assuming this is /dev/mmcblk0p2 - typically: shutdown the RPi, take out the SD card, mount it somewhere else (in this example sudo mount /dev/mmcblk0p2 /mnt on a Linux computer) and archive the contents; note that you need to use a tool which preserves the ownership and permissions, e.g. tar: cd /mnt; sudo tar -czvf ~/rpi-rootfs-backup.tgz * (and then unmount the SD card again)

  • Create a btrfs partition somewhere - I have re-used the SD card, replacing the ext4 partition (/dev/mmcblk0p2); if you're looking to create a btrfs-raid array, this is the time to do this (it's one of the arguments to mkfs.btrfs, beyond the scope of this answer): mkfs.btrfs /dev/mmcblk0p2
  • Mount the btrfs partition and restore the backup into it: sudo partprobe; sudo mount /dev/mmcblk0p2 /mnt; cd /mnt; tar -xzvf ~/rpi-rootfs-backup.tgz
  • Edit fstab on the btrfs partition: sudo nano /mnt/etc/fstab

There should be a row similar to this:

/dev/mmcblk0p2  / ext4 foo,bar,baz 0 1

Change it to this (the new FS type is btrfs, and it uses default options):

/dev/mmcblk0p2  / btrfs defaults 0 1
  • Unmount the partition, but don't remove the SD card yet! sudo umount /mnt
  • We need to tell the RPi that it's going to boot from btrfs
  • Find the UUID of your new btrfs partition - find the line with /dev/mmcblk0p2, and copy the UUID= part, with the (not UUID_SUB, not PARTUUID! That would trigger a bug in the bootloader, and the kernel wouldn't boot.): sudo blkid

    /dev/mmcblk0p2: UUID="cafebeef-0000-1234-aaaa-12346589" UUID_SUB="ababccdd-2345-cafe-beee-587989991110" TYPE="btrfs" PARTUUID="beef0bee-02"

  • Mount the boot (FAT32) partition: sudo mount /dev/mmcblk0p1 /mnt

  • Edit cmdline.txt: sudo nano /mnt/cmdline.txt

Find these two parameters

 root=PARTUUID=1234-5678 rootfstype=ext4

And replace with

 root=UUID=cafebeef-0000-1234-aaaa-12346589 rootfstype=btrfs

Note that the UUID is the one we copied earlier, just without quotes.

  • Unmount the RPi boot partition: sudo umount /mnt
  • Replace the SD card into RPi, and boot.
  • On the RPi, see that you're indeed running from a btrfs root mount: mount

    /dev/mmcblk0p2 on / type btrfs (rw,space_cache,subvol=/)

  • Et voilà! Not quite point-and-click, but by standing on the shoulders of giants, I could make it work. (Made this into a repo, too.)

  • 1
    With the first `sudo apt upgrade` if it also upgrade the kernel this setup will fail dramatically on boot because the new kernel tries to load the old initramfs that will fail and the kernel cannot load btrfs drivers. And it is not an easy way to fix it, at least with a `chroot` on an armhf system. – Ingo Feb 25 '19 at 21:22
  • Wouldn't update-initramfs be invoked on kernel upgrade? – Piskvor left the building Feb 25 '19 at 21:25
  • ...and it will generate a new initramfs, under a different name, which won't match the one in config.txt. Good catch, that was a huge blind spot. – Piskvor left the building Feb 25 '19 at 21:36
  • 1
    No, default Raspbian fails to generate a new initramfs. It isn't configured for this. You always have to monitor with your eyes what `apt upgrade` is doing and have do generate an initramfs by hand if required - before booting the new kernel. Not a doable task for a beginner because failing it is dramatic. You may have a look at [How can I use an init ramdisk (initramfs) on boot up Raspberry Pi?](https://raspberrypi.stackexchange.com/a/92558/79866) – Ingo Feb 25 '19 at 22:12
  • Excellent, thank you; that looks like a good starting point. – Piskvor left the building Feb 25 '19 at 22:52
  • 1
    It has a little bug I have just found but not fixed so far. The kernel supports two models, e.g. `4.14.98+` and `4.14.98-v7+`. If update-initramfs is triggered by a kernel update it will generate two initrd.img*, one for each model. This does not fit on the `/boot` partition (error - out of space) and generation does not finish. – Ingo Feb 25 '19 at 23:03
  • /boot can be resized (impractical) or initramfs generated elsewhere and then copied (the method I use when generating manually - more coding required). – Piskvor left the building Feb 26 '19 at 10:52
  • Sure, but my goal is to make it running on default Raspbian, flashed from the image. Another way is to configure initramfs-tools just to pack only the needed driver, not all by default. Can be defined in `/etc/initramfs-tools/initramfs.conf`. – Ingo Feb 26 '19 at 11:07
  • `MODULES=dep` helps, but a) not a lot and b) two initramfs-s still wouldn't fit into my /boot – Piskvor left the building Feb 26 '19 at 11:10
  • 1
    I consider to use `MODULES=list`. – Ingo Feb 26 '19 at 11:15
  • Ah, that would help, too. As for "runs on default" - I guess the easiest way would be to make this into an installable package. Still not entirely out-of-the-box, but could be installed in an unattended way. – Piskvor left the building Feb 26 '19 at 11:17
  • Let us [continue this discussion in chat](https://chat.stackexchange.com/rooms/90242/discussion-between-ingo-and-piskvor). – Ingo Feb 26 '19 at 11:22
2

My quick test shows that btrfs support is built as a external module in raspbian, not linked directly into the kernel.

That means that the kernel has to be able to load that module (which is stored on the root file system) before it knows how to mount the root file system. Obviously, this doesn't work.

Approach 1:

Build your own kernel, and tweak it's build config to pre-link btrfs. Tweaking the config is easy if you've figured out how to build and load your own kernel.

Approach 2:

Readjust things so the kernel and modules are on an ext4 filesystem and the data you most want to compress is on a btrfs partition.

Approach 2A:

Leave the root partition as ext4 and creating a new partition that's btrfs based, but that doesn't help shrink the OS installation (if that's your goal).

Approach 2B:

Create a boot partition that is small, and holds the kernel and modules, while leaving everything else on the btrfs. I have no idea how to do this for a Pi's bootloader, or what the limitations are around this.

DonGar
  • 273
  • 1
  • 3
  • 9
  • What about copying the btrfs modules to the boot partition and loading them from there beforehand? – GuySoft Jul 03 '13 at 23:56
  • 3
    Isn't it also possible to start with a initrd.img? – Anders Jul 04 '13 at 19:42
  • Yes, and initrd.img looks like the easiest way to solve it! I've just never used it. Look for docs on "mkinitrd". – DonGar Jul 05 '13 at 05:26
  • Hmm seems like CONFIG_BLK_DEV_INITRD is not enabled in latest Raspbian. This means you need to recompile the kernel in order to enable initd support. – GuySoft Jul 07 '13 at 12:56
  • 1
    See http://paxswill.com/blog/2013/11/04/encrypted-raspberry-pi/ - There initramfs is used to allow encrypted root. Similarly, support for cryptsetup (here btrfs) is needed before root is available. – Robert Cutajar Sep 17 '14 at 09:47
1

To get it to find my external BTRFS root partition, I needed to explicitly specify the root partition's UUID in the boot partition's cmdline.txt. For example:

dwc_otg.lpm_enable=0 console=tty1 root=PARTUUID=123e4567-e89b-12d3-a456-426655440000 rootfstype=btrfs elevator=deadline rootwait quiet splash

You can determine the BTRFS partition's UUID using lsblk -f.

Geremia
  • 153
  • 1
  • 9