bcachefs: Handle partial pages in seek data/hole
authorKent Overstreet <kent.overstreet@gmail.com>
Tue, 30 Jul 2019 17:49:17 +0000 (13:49 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:08:24 +0000 (17:08 -0400)
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/fs-io.c

index 4efe985da96bfc758fb2aa1b5a8dc945f92e1c68..f59c6321f530f5dd55937e55cd379558bc95e633 100644 (file)
@@ -2833,22 +2833,20 @@ long bch2_fallocate_dispatch(struct file *file, int mode,
 
 /* fseek: */
 
-static bool folio_is_data(struct folio *folio)
+static int folio_data_offset(struct folio *folio, unsigned offset)
 {
        struct bch_page_state *s = bch2_page_state(&folio->page);
        unsigned i;
 
-       if (!s)
-               return false;
-
-       for (i = 0; i < PAGE_SECTORS; i++)
-               if (s->s[i].state >= SECTOR_DIRTY)
-                       return true;
+       if (s)
+               for (i = offset >> 9; i < PAGE_SECTORS; i++)
+                       if (s->s[i].state >= SECTOR_DIRTY)
+                               return i << 9;
 
-       return false;
+       return -1;
 }
 
-static loff_t bch2_next_pagecache_data(struct inode *vinode,
+static loff_t bch2_seek_pagecache_data(struct inode *vinode,
                                       loff_t start_offset,
                                       loff_t end_offset)
 {
@@ -2857,6 +2855,8 @@ static loff_t bch2_next_pagecache_data(struct inode *vinode,
        pgoff_t end_index       = end_offset >> PAGE_SHIFT;
        pgoff_t index           = start_index;
        unsigned i;
+       loff_t ret;
+       int offset;
 
        folio_batch_init(&fbatch);
 
@@ -2866,14 +2866,17 @@ static loff_t bch2_next_pagecache_data(struct inode *vinode,
                        struct folio *folio = fbatch.folios[i];
 
                        folio_lock(folio);
-                       if (folio_is_data(folio)) {
-                               end_offset =
-                                       min(end_offset,
-                                           max(start_offset,
-                                               ((loff_t) folio->index) << PAGE_SHIFT));
+                       offset = folio_data_offset(folio,
+                                       folio->index == start_index
+                                       ? start_offset & (PAGE_SIZE - 1)
+                                       : 0);
+                       if (offset >= 0) {
+                               ret = clamp(((loff_t) folio->index << PAGE_SHIFT) +
+                                           offset,
+                                           start_offset, end_offset);
                                folio_unlock(folio);
                                folio_batch_release(&fbatch);
-                               return end_offset;
+                               return ret;
                        }
                        folio_unlock(folio);
                }
@@ -2916,7 +2919,7 @@ static loff_t bch2_seek_data(struct file *file, u64 offset)
                return ret;
 
        if (next_data > offset)
-               next_data = bch2_next_pagecache_data(&inode->v,
+               next_data = bch2_seek_pagecache_data(&inode->v,
                                                     offset, next_data);
 
        if (next_data >= isize)
@@ -2925,34 +2928,56 @@ static loff_t bch2_seek_data(struct file *file, u64 offset)
        return vfs_setpos(file, next_data, MAX_LFS_FILESIZE);
 }
 
-static bool page_slot_is_data(struct address_space *mapping, pgoff_t index)
+static int __page_hole_offset(struct page *page, unsigned offset)
 {
+       struct bch_page_state *s = bch2_page_state(page);
+       unsigned i;
+
+       if (!s)
+               return 0;
+
+       for (i = offset >> 9; i < PAGE_SECTORS; i++)
+               if (s->s[i].state < SECTOR_DIRTY)
+                       return i << 9;
+
+       return -1;
+}
+
+static loff_t page_hole_offset(struct address_space *mapping, loff_t offset)
+{
+       pgoff_t index = offset >> PAGE_SHIFT;
        struct page *page;
-       bool ret;
+       int pg_offset;
+       loff_t ret = -1;
 
        page = find_lock_page(mapping, index);
        if (!page)
-               return false;
+               return offset;
+
+       pg_offset = __page_hole_offset(page, offset & (PAGE_SIZE - 1));
+       if (pg_offset >= 0)
+               ret = ((loff_t) index << PAGE_SHIFT) + pg_offset;
 
-       ret = folio_is_data(page_folio(page));
        unlock_page(page);
 
        return ret;
 }
 
-static loff_t bch2_next_pagecache_hole(struct inode *vinode,
+static loff_t bch2_seek_pagecache_hole(struct inode *vinode,
                                       loff_t start_offset,
                                       loff_t end_offset)
 {
        struct address_space *mapping = vinode->i_mapping;
-       pgoff_t index;
+       loff_t offset = start_offset, hole;
 
-       for (index = start_offset >> PAGE_SHIFT;
-            index < end_offset >> PAGE_SHIFT;
-            index++)
-               if (!page_slot_is_data(mapping, index))
-                       end_offset = max(start_offset,
-                                        ((loff_t) index) << PAGE_SHIFT);
+       while (offset < end_offset) {
+               hole = page_hole_offset(mapping, offset);
+               if (hole >= 0 && hole <= end_offset)
+                       return max(start_offset, hole);
+
+               offset += PAGE_SIZE;
+               offset &= PAGE_MASK;
+       }
 
        return end_offset;
 }
@@ -2977,11 +3002,11 @@ static loff_t bch2_seek_hole(struct file *file, u64 offset)
                           POS(inode->v.i_ino, offset >> 9),
                           BTREE_ITER_SLOTS, k, ret) {
                if (k.k->p.inode != inode->v.i_ino) {
-                       next_hole = bch2_next_pagecache_hole(&inode->v,
+                       next_hole = bch2_seek_pagecache_hole(&inode->v,
                                        offset, MAX_LFS_FILESIZE);
                        break;
                } else if (!bkey_extent_is_data(k.k)) {
-                       next_hole = bch2_next_pagecache_hole(&inode->v,
+                       next_hole = bch2_seek_pagecache_hole(&inode->v,
                                        max(offset, bkey_start_offset(k.k) << 9),
                                        k.k->p.offset << 9);