How do I convert an existing LVM LV to a bcache backing device?

The official tool for converting an existing device to bcache is blocks, but it hasn't been updated since December 2014.

At a minimum, issues 13 and 36 prevent it from working with maintboot and btrfs.

How can I manually convert a LVM LV to bcache?



Having looked at the source for blocks, what it does is:

  1. Shrink the LV's filesystem is shrunk by one LVM PE
  2. Shrink the LV itself by one LVM PE (this guarantees one free PE to be used for the bcache header)
  3. Edit the VG config, insert a new first segment of size 1 being the PE that was freed in the previous step
  4. Create a bcache backing device with --data-offset being the size of one LVM PE.

Gather data; shrink LV; make VG configuration backup

First get the PE size of the VG (usually 4MiB):

sudo vgdisplay /dev/mapper/VG | grep 'PE Size'

Shrink the LV's filesystem by this amount (filesystem specific). Be careful to not confuse the 4MiB with 4MB.

Then shrink the LV itself. lvresize uses ^2 units, so 4M=4MiB:

sudo lvresize -L -4M /dev/mapper/VG-LV

Next, find the Start of the newly unallocated PE:

sudo pvs --segments -o +lv_name,seg_start_pe,seg_size_pe,seg_pe_ranges,seg_le_ranges

In my (very, very ugly) allocation, the wanted Start column figure is 177406:

  PV         VG  Fmt  Attr PSize    PFree Start  SSize  LV     Start  SSize  PE Ranges               LE Ranges
  /dev/sdb1  2TB lvm2 a--  <693.00g 4.00m      0 177406 backup 299499 177406 /dev/sdb1:0-177405      /dev/sdb1:0-177405
  /dev/sdb1  2TB lvm2 a--  <693.00g 4.00m 177406      1             0      1
  /dev/sdb2  2TB lvm2 a--  <662.96g    0       0 149749 backup 149750 149749 /dev/sdb2:0-149748      /dev/sdb2:0-149748
  /dev/sdb2  2TB lvm2 a--  <662.96g    0  149749  19968 backup 129782  19968 /dev/sdb2:149749-169716 /dev/sdb2:149749-169716
  /dev/sdb3  2TB lvm2 a--   506.96g    0       0 129782 backup      0 129782 /dev/sdb3:0-129781      /dev/sdb3:0-129781

Note also that 177406 is one greater than the highest LE Ranges number for my LV which is named backup.

Disable the VG as it is about to be edited:

sudo vgchange -an VG

Dump the configuration of the VG:

sudo vgcfgbackup --file vg-config VG
sudo chown $USER vg-config

At this point make a backup of the config.

Edit VG configuration; insert newly freed PE as first LE

Find the LV in the logical_volumes part of the config file.

Increment segment_count to allow for creating a new segment with the newly freed extent.

Increment all existing segments (eg old segment1 becomes segment2)

Make a new first segment containing the single logical extent freed up by lvresize:

        segment1 {
            start_extent = 0
            extent_count = 1

            type = "striped"
            stripe_count = 1    # linear

            stripes = [
                "pv2", 177406

The number 177406 is the PE freed by shrinking the LV above. Make sure that the pv# next to it matches the correct physical device from the physical_volumes part of the config file.

For segment2 onwards, increment only the start_extents by 1. This accounts for the new Logical Extent we inserted in segment1. (Hint: Use ^A in vi to avoid human error) (The last segment's extent_count is already correct as the backup was taken after the LV was shrunken).

You can see the patch of my VG configuration here.

Check the new VG configuration

Write the new configuration to the VG metadata:

sudo vgcfgrestore --file vg-config

Re-enable the VG:

sudo vgchange -ay VG

Loopback mount the LV with a single PE offset to check that nothing got screwed up:

sudo losetup --find --show --offset 4M --read-only /dev/mapper/VG-LV

(above we use the example PE size of 4MiB)

fsck all filesystems in the VG to check that nothing got screwed up in the segment mapping.

See rollback section (below) if something went wrong. Else, sudo losetup --detach /dev/loop#

Write bcache header

Make the bcache device with appropriate block size and data offset to match one LVM PE:

Get the data offset in sectors:

sudo vgdisplay --units s 2TB | grep 'PE Size'

For a usual 4MiB PE size, this will be 8192.

Write the bcache 8KiB header:

make-bcache --bdev --data-offset 8192 --block "$(blockdev --getpbsz "$dev")" "$dev" # --writeback --cset-uuid # see `make-bcache --help`

Attach the newly setup backing device to a cache device.

fsck only the LV's filesystem (the other VG filesystems haven't been changed since last fsck)

Be proud that fsck takes only a fraction of the previous running time :)


If fsck complains and you can't work out what you did wrong in the VG configuration file, you can just restore the backup that you took:

sudo vgcfgrestore -f vg-config.orig

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.