btrfs: refactor extent buffer bitmaps operations
authorQu Wenruo <wqu@suse.com>
Sat, 15 Jul 2023 11:08:29 +0000 (19:08 +0800)
committerDavid Sterba <dsterba@suse.com>
Mon, 21 Aug 2023 12:52:16 +0000 (14:52 +0200)
[BACKGROUND]
Currently we handle extent bitmaps manually in
extent_buffer_bitmap_set() and extent_buffer_bitmap_clear().

Although with various helpers like eb_bitmap_offset() it's still a little
messy to read.  The code seems to be a copy of bitmap_set(), but with
all the cross-page handling embedded into the code.

[ENHANCEMENT]
This patch would enhance the readability by introducing two helpers:

- memset_extent_buffer()
  To handle the byte aligned range, thus all the cross-page handling is
  done there.

- extent_buffer_get_byte()
  This for the first and the last byte operations, which only need to
  grab one byte, thus no need for any cross-page handling.

So we can split both extent_buffer_bitmap_set() and
extent_buffer_bitmap_clear() into 3 parts:

- Handle the first byte
  If the range fits inside the first byte, we can exit early.

- Handle the byte aligned part
  This is the part which can have cross-page operations, and it would
  be handled by memset_extent_buffer().

- Handle the last byte

This refactoring does not only make the code a little easier to read,
but also makes later folio/page switch much easier, as the switch only
needs to be done inside memset_extent_buffer() and extent_buffer_get_byte().

Reviewed-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/extent_io.c

index 90a58a3..3e9f39b 100644 (file)
@@ -4183,34 +4183,32 @@ void write_extent_buffer(const struct extent_buffer *eb, const void *srcv,
        }
 }
 
-void memzero_extent_buffer(const struct extent_buffer *eb, unsigned long start,
-               unsigned long len)
+static void memset_extent_buffer(const struct extent_buffer *eb, int c,
+                                unsigned long start, unsigned long len)
 {
-       size_t cur;
-       size_t offset;
-       struct page *page;
-       char *kaddr;
-       unsigned long i = get_eb_page_index(start);
+       unsigned long cur = start;
 
-       if (check_eb_range(eb, start, len))
-               return;
-
-       offset = get_eb_offset_in_page(eb, start);
+       while (cur < start + len) {
+               unsigned long index = get_eb_page_index(cur);
+               unsigned int offset = get_eb_offset_in_page(eb, cur);
+               unsigned int cur_len = min(start + len - cur, PAGE_SIZE - offset);
+               struct page *page = eb->pages[index];
 
-       while (len > 0) {
-               page = eb->pages[i];
                assert_eb_page_uptodate(eb, page);
+               memset(page_address(page) + offset, c, cur_len);
 
-               cur = min(len, PAGE_SIZE - offset);
-               kaddr = page_address(page);
-               memset(kaddr + offset, 0, cur);
-
-               len -= cur;
-               offset = 0;
-               i++;
+               cur += cur_len;
        }
 }
 
+void memzero_extent_buffer(const struct extent_buffer *eb, unsigned long start,
+                          unsigned long len)
+{
+       if (check_eb_range(eb, start, len))
+               return;
+       return memset_extent_buffer(eb, 0, start, len);
+}
+
 void copy_extent_buffer_full(const struct extent_buffer *dst,
                             const struct extent_buffer *src)
 {
@@ -4325,6 +4323,15 @@ int extent_buffer_test_bit(const struct extent_buffer *eb, unsigned long start,
        return 1U & (kaddr[offset] >> (nr & (BITS_PER_BYTE - 1)));
 }
 
+static u8 *extent_buffer_get_byte(const struct extent_buffer *eb, unsigned long bytenr)
+{
+       unsigned long index = get_eb_page_index(bytenr);
+
+       if (check_eb_range(eb, bytenr, 1))
+               return NULL;
+       return page_address(eb->pages[index]) + get_eb_offset_in_page(eb, bytenr);
+}
+
 /*
  * Set an area of a bitmap to 1.
  *
@@ -4336,35 +4343,28 @@ int extent_buffer_test_bit(const struct extent_buffer *eb, unsigned long start,
 void extent_buffer_bitmap_set(const struct extent_buffer *eb, unsigned long start,
                              unsigned long pos, unsigned long len)
 {
+       unsigned int first_byte = start + BIT_BYTE(pos);
+       unsigned int last_byte = start + BIT_BYTE(pos + len - 1);
+       const bool same_byte = (first_byte == last_byte);
+       u8 mask = BITMAP_FIRST_BYTE_MASK(pos);
        u8 *kaddr;
-       struct page *page;
-       unsigned long i;
-       size_t offset;
-       const unsigned int size = pos + len;
-       int bits_to_set = BITS_PER_BYTE - (pos % BITS_PER_BYTE);
-       u8 mask_to_set = BITMAP_FIRST_BYTE_MASK(pos);
 
-       eb_bitmap_offset(eb, start, pos, &i, &offset);
-       page = eb->pages[i];
-       assert_eb_page_uptodate(eb, page);
-       kaddr = page_address(page);
+       if (same_byte)
+               mask &= BITMAP_LAST_BYTE_MASK(pos + len);
 
-       while (len >= bits_to_set) {
-               kaddr[offset] |= mask_to_set;
-               len -= bits_to_set;
-               bits_to_set = BITS_PER_BYTE;
-               mask_to_set = ~0;
-               if (++offset >= PAGE_SIZE && len > 0) {
-                       offset = 0;
-                       page = eb->pages[++i];
-                       assert_eb_page_uptodate(eb, page);
-                       kaddr = page_address(page);
-               }
-       }
-       if (len) {
-               mask_to_set &= BITMAP_LAST_BYTE_MASK(size);
-               kaddr[offset] |= mask_to_set;
-       }
+       /* Handle the first byte. */
+       kaddr = extent_buffer_get_byte(eb, first_byte);
+       *kaddr |= mask;
+       if (same_byte)
+               return;
+
+       /* Handle the byte aligned part. */
+       ASSERT(first_byte + 1 <= last_byte);
+       memset_extent_buffer(eb, 0xff, first_byte + 1, last_byte - first_byte - 1);
+
+       /* Handle the last byte. */
+       kaddr = extent_buffer_get_byte(eb, last_byte);
+       *kaddr |= BITMAP_LAST_BYTE_MASK(pos + len);
 }
 
 
@@ -4380,35 +4380,28 @@ void extent_buffer_bitmap_clear(const struct extent_buffer *eb,
                                unsigned long start, unsigned long pos,
                                unsigned long len)
 {
+       unsigned int first_byte = start + BIT_BYTE(pos);
+       unsigned int last_byte = start + BIT_BYTE(pos + len - 1);
+       const bool same_byte = (first_byte == last_byte);
+       u8 mask = BITMAP_FIRST_BYTE_MASK(pos);
        u8 *kaddr;
-       struct page *page;
-       unsigned long i;
-       size_t offset;
-       const unsigned int size = pos + len;
-       int bits_to_clear = BITS_PER_BYTE - (pos % BITS_PER_BYTE);
-       u8 mask_to_clear = BITMAP_FIRST_BYTE_MASK(pos);
 
-       eb_bitmap_offset(eb, start, pos, &i, &offset);
-       page = eb->pages[i];
-       assert_eb_page_uptodate(eb, page);
-       kaddr = page_address(page);
+       if (same_byte)
+               mask &= BITMAP_LAST_BYTE_MASK(pos + len);
 
-       while (len >= bits_to_clear) {
-               kaddr[offset] &= ~mask_to_clear;
-               len -= bits_to_clear;
-               bits_to_clear = BITS_PER_BYTE;
-               mask_to_clear = ~0;
-               if (++offset >= PAGE_SIZE && len > 0) {
-                       offset = 0;
-                       page = eb->pages[++i];
-                       assert_eb_page_uptodate(eb, page);
-                       kaddr = page_address(page);
-               }
-       }
-       if (len) {
-               mask_to_clear &= BITMAP_LAST_BYTE_MASK(size);
-               kaddr[offset] &= ~mask_to_clear;
-       }
+       /* Handle the first byte. */
+       kaddr = extent_buffer_get_byte(eb, first_byte);
+       *kaddr &= ~mask;
+       if (same_byte)
+               return;
+
+       /* Handle the byte aligned part. */
+       ASSERT(first_byte + 1 <= last_byte);
+       memset_extent_buffer(eb, 0, first_byte + 1, last_byte - first_byte - 1);
+
+       /* Handle the last byte. */
+       kaddr = extent_buffer_get_byte(eb, last_byte);
+       *kaddr &= ~BITMAP_LAST_BYTE_MASK(pos + len);
 }
 
 static inline bool areas_overlap(unsigned long src, unsigned long dst, unsigned long len)