bcachefs: Ensure proper write alignment
authorKent Overstreet <kent.overstreet@linux.dev>
Sun, 4 May 2025 20:31:40 +0000 (16:31 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Mon, 5 May 2025 18:19:01 +0000 (14:19 -0400)
There was a buggy version of bcachefs-tools which picked misaligned
bucket sizes when formatting, and we're also about to do dynamic block
sizes - which will allow picking logical block size or physical block
size of the device per-write, allowing for better compression ratios at
the cost of slightly worse write performance (i.e. forcing the device to
do RMW or extra buffering).

To account for this, tweak bch2_alloc_sectors_start() to properly align
open_buckets to the blocksize of the write we're about to do.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/alloc_foreground.c

index effafc3..7ec022e 100644 (file)
@@ -1422,11 +1422,31 @@ alloc_done:
 
        wp->sectors_free = UINT_MAX;
 
-       open_bucket_for_each(c, &wp->ptrs, ob, i)
+       open_bucket_for_each(c, &wp->ptrs, ob, i) {
+               /*
+                * Ensure proper write alignment - either due to misaligned
+                * bucket sizes (from buggy bcachefs-tools), or writes that mix
+                * logical/physical alignment:
+                */
+               struct bch_dev *ca = ob_dev(c, ob);
+               u64 offset = bucket_to_sector(ca, ob->bucket) +
+                       ca->mi.bucket_size -
+                       ob->sectors_free;
+               unsigned align = round_up(offset, block_sectors(c)) - offset;
+
+               ob->sectors_free = max_t(int, 0, ob->sectors_free - align);
+
                wp->sectors_free = min(wp->sectors_free, ob->sectors_free);
+       }
 
        wp->sectors_free = rounddown(wp->sectors_free, block_sectors(c));
 
+       /* Did alignment use up space in an open_bucket? */
+       if (unlikely(!wp->sectors_free)) {
+               bch2_alloc_sectors_done(c, wp);
+               goto retry;
+       }
+
        BUG_ON(!wp->sectors_free || wp->sectors_free == UINT_MAX);
 
        return 0;