Thread (68 messages) 68 messages, 4 authors, 2021-01-27

Re: [PATCH v4 08/18] btrfs: introduce helper for subpage uptodate status

From: Qu Wenruo <hidden>
Date: 2021-01-21 05:26:41


On 2021/1/20 下午11:00, Josef Bacik wrote:
On 1/16/21 2:15 AM, Qu Wenruo wrote:
quoted
This patch introduce the following functions to handle btrfs subpage
uptodate status:
- btrfs_subpage_set_uptodate()
- btrfs_subpage_clear_uptodate()
- btrfs_subpage_test_uptodate()
   Those helpers can only be called when the range is ensured to be
   inside the page.

- btrfs_page_set_uptodate()
- btrfs_page_clear_uptodate()
- btrfs_page_test_uptodate()
   Those helpers can handle both regular sector size and subpage without
   problem.
   Although caller should still ensure that the range is inside the page.

Signed-off-by: Qu Wenruo <redacted>
---
  fs/btrfs/subpage.h | 115 +++++++++++++++++++++++++++++++++++++++++++++
  1 file changed, 115 insertions(+)
diff --git a/fs/btrfs/subpage.h b/fs/btrfs/subpage.h
index d8b34879368d..3373ef4ffec1 100644
--- a/fs/btrfs/subpage.h
+++ b/fs/btrfs/subpage.h
@@ -23,6 +23,7 @@
  struct btrfs_subpage {
      /* Common members for both data and metadata pages */
      spinlock_t lock;
+    u16 uptodate_bitmap;
      union {
          /* Structures only used by metadata */
          bool under_alloc;
@@ -78,4 +79,118 @@ static inline void 
btrfs_page_end_meta_alloc(struct btrfs_fs_info *fs_info,
  int btrfs_attach_subpage(struct btrfs_fs_info *fs_info, struct page 
*page);
  void btrfs_detach_subpage(struct btrfs_fs_info *fs_info, struct page 
*page);
+/*
+ * Convert the [start, start + len) range into a u16 bitmap
+ *
+ * E.g. if start == page_offset() + 16K, len = 16K, we get 0x00f0.
+ */
+static inline u16 btrfs_subpage_calc_bitmap(struct btrfs_fs_info 
*fs_info,
+            struct page *page, u64 start, u32 len)
+{
+    int bit_start = offset_in_page(start) >> fs_info->sectorsize_bits;
+    int nbits = len >> fs_info->sectorsize_bits;
+
+    /* Basic checks */
+    ASSERT(PagePrivate(page) && page->private);
+    ASSERT(IS_ALIGNED(start, fs_info->sectorsize) &&
+           IS_ALIGNED(len, fs_info->sectorsize));
+
+    /*
+     * The range check only works for mapped page, we can
+     * still have unampped page like dummy extent buffer pages.
+     */
+    if (page->mapping)
+        ASSERT(page_offset(page) <= start &&
+            start + len <= page_offset(page) + PAGE_SIZE);
+    /*
+     * Here nbits can be 16, thus can go beyond u16 range. Here we 
make the
+     * first left shift to be calculated in unsigned long (u32), then
+     * truncate the result to u16.
+     */
+    return (u16)(((1UL << nbits) - 1) << bit_start);
+}
+
+static inline void btrfs_subpage_set_uptodate(struct btrfs_fs_info 
*fs_info,
+            struct page *page, u64 start, u32 len)
+{
+    struct btrfs_subpage *subpage = (struct btrfs_subpage 
*)page->private;
+    u16 tmp = btrfs_subpage_calc_bitmap(fs_info, page, start, len);
+    unsigned long flags;
+
+    spin_lock_irqsave(&subpage->lock, flags);
+    subpage->uptodate_bitmap |= tmp;
+    if (subpage->uptodate_bitmap == U16_MAX)
+        SetPageUptodate(page);
+    spin_unlock_irqrestore(&subpage->lock, flags);
+}
+
+static inline void btrfs_subpage_clear_uptodate(struct btrfs_fs_info 
*fs_info,
+            struct page *page, u64 start, u32 len)
+{
+    struct btrfs_subpage *subpage = (struct btrfs_subpage 
*)page->private;
+    u16 tmp = btrfs_subpage_calc_bitmap(fs_info, page, start, len);
+    unsigned long flags;
+
+    spin_lock_irqsave(&subpage->lock, flags);
+    subpage->uptodate_bitmap &= ~tmp;
+    ClearPageUptodate(page);
+    spin_unlock_irqrestore(&subpage->lock, flags);
+}
+
+/*
+ * Unlike set/clear which is dependent on each page status, for test 
all bits
+ * are tested in the same way.
+ */
+#define DECLARE_BTRFS_SUBPAGE_TEST_OP(name)                \
+static inline bool btrfs_subpage_test_##name(struct btrfs_fs_info 
*fs_info, \
+            struct page *page, u64 start, u32 len)        \
+{                                    \
+    struct btrfs_subpage *subpage = (struct btrfs_subpage 
*)page->private; \
+    u16 tmp = btrfs_subpage_calc_bitmap(fs_info, page, start, len); \
+    unsigned long flags;                        \
+    bool ret;                            \
+                                    \
+    spin_lock_irqsave(&subpage->lock, flags);            \
+    ret = ((subpage->name##_bitmap & tmp) == tmp);            \
+    spin_unlock_irqrestore(&subpage->lock, flags);            \
+    return ret;                            \
+}
+DECLARE_BTRFS_SUBPAGE_TEST_OP(uptodate);
+
+/*
+ * Note that, in selftest, especially extent-io-tests, we can have empty
+ * fs_info passed in.
+ * Thankfully in selftest, we only test sectorsize == PAGE_SIZE cases 
so far,
+ * thus we can fall back to regular sectorsize branch.
+ */
+#define DECLARE_BTRFS_PAGE_OPS(name, set_page_func, 
clear_page_func,    \
+                   test_page_func)                \
+static inline void btrfs_page_set_##name(struct btrfs_fs_info 
*fs_info,    \
+            struct page *page, u64 start, u32 len)        \
+{                                    \
+    if (unlikely(!fs_info) || fs_info->sectorsize == PAGE_SIZE) {    \
+        set_page_func(page);                    \
+        return;                            \
+    }                                \
+    btrfs_subpage_set_##name(fs_info, page, start, len);        \
+}                                    \
+static inline void btrfs_page_clear_##name(struct btrfs_fs_info 
*fs_info, \
+            struct page *page, u64 start, u32 len)        \
+{                                    \
+    if (unlikely(!fs_info) || fs_info->sectorsize == PAGE_SIZE) {    \
+        clear_page_func(page);                    \
+        return;                            \
+    }                                \
+    btrfs_subpage_clear_##name(fs_info, page, start, len);        \
+}                                    \
+static inline bool btrfs_page_test_##name(struct btrfs_fs_info 
*fs_info, \
+            struct page *page, u64 start, u32 len)        \
+{                                    \
+    if (unlikely(!fs_info) || fs_info->sectorsize == PAGE_SIZE)    \
+        return test_page_func(page);                \
+    return btrfs_subpage_test_##name(fs_info, page, start, len);    \
+}
Another thing I just realized is you're doing this

btrfs_page_set_uptodate(fs_info, page, eb->start, eb->len);

but we default to a nodesize > PAGE_SIZE on x86.  This is fine, because 
you're checking fs_info->sectorsize == PAGE_SIZE, which will mean we do 
the right thing.

But what happens if fs_info->nodesize < PAGE_SIZE && fs_info->sectorsize 
== PAGE_SIZE?  We by default have fs'es that ->nodesize != ->sectorsize, 
so really what we should be doing is checking if len == PAGE_SIZE here, 
but then you need to take into account the case that eb->len > 
PAGE_SIZE.  Fix this to do the right thing in either of those cases.  
Thanks,
Impossible.

Nodesize must be >= sectorsize.

As sectorsize is currently the minimal access unit for both data and 
metadata.

Thanks,
Qu
Josef
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help