summaryrefslogtreecommitdiff
path: root/fs/bcachefs/fs-io.c
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2019-07-30 13:49:17 -0400
committerKent Overstreet <kent.overstreet@linux.dev>2023-10-22 17:08:24 -0400
commit543ef2ebcd90686d999f18b0a874690b7976b239 (patch)
tree3d0ba0d4b8dbdf2d5936ca047697491f96c3e0a6 /fs/bcachefs/fs-io.c
parentd1542e0362de069f677dfb0e9336438afb8fae74 (diff)
downloadlwn-543ef2ebcd90686d999f18b0a874690b7976b239.tar.gz
lwn-543ef2ebcd90686d999f18b0a874690b7976b239.zip
bcachefs: Handle partial pages in seek data/hole
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
Diffstat (limited to 'fs/bcachefs/fs-io.c')
-rw-r--r--fs/bcachefs/fs-io.c85
1 files changed, 55 insertions, 30 deletions
diff --git a/fs/bcachefs/fs-io.c b/fs/bcachefs/fs-io.c
index 4efe985da96b..f59c6321f530 100644
--- a/fs/bcachefs/fs-io.c
+++ b/fs/bcachefs/fs-io.c
@@ -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);