Commit 543ef2eb authored by Kent Overstreet's avatar Kent Overstreet Committed by Kent Overstreet

bcachefs: Handle partial pages in seek data/hole

Signed-off-by: default avatarKent Overstreet <kent.overstreet@linux.dev>
parent d1542e03
...@@ -2833,22 +2833,20 @@ long bch2_fallocate_dispatch(struct file *file, int mode, ...@@ -2833,22 +2833,20 @@ long bch2_fallocate_dispatch(struct file *file, int mode,
/* fseek: */ /* 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); struct bch_page_state *s = bch2_page_state(&folio->page);
unsigned i; unsigned i;
if (!s) if (s)
return false; for (i = offset >> 9; i < PAGE_SECTORS; i++)
for (i = 0; i < PAGE_SECTORS; i++)
if (s->s[i].state >= SECTOR_DIRTY) if (s->s[i].state >= SECTOR_DIRTY)
return true; 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 start_offset,
loff_t end_offset) loff_t end_offset)
{ {
...@@ -2857,6 +2855,8 @@ static loff_t bch2_next_pagecache_data(struct inode *vinode, ...@@ -2857,6 +2855,8 @@ static loff_t bch2_next_pagecache_data(struct inode *vinode,
pgoff_t end_index = end_offset >> PAGE_SHIFT; pgoff_t end_index = end_offset >> PAGE_SHIFT;
pgoff_t index = start_index; pgoff_t index = start_index;
unsigned i; unsigned i;
loff_t ret;
int offset;
folio_batch_init(&fbatch); folio_batch_init(&fbatch);
...@@ -2866,14 +2866,17 @@ static loff_t bch2_next_pagecache_data(struct inode *vinode, ...@@ -2866,14 +2866,17 @@ static loff_t bch2_next_pagecache_data(struct inode *vinode,
struct folio *folio = fbatch.folios[i]; struct folio *folio = fbatch.folios[i];
folio_lock(folio); folio_lock(folio);
if (folio_is_data(folio)) { offset = folio_data_offset(folio,
end_offset = folio->index == start_index
min(end_offset, ? start_offset & (PAGE_SIZE - 1)
max(start_offset, : 0);
((loff_t) folio->index) << PAGE_SHIFT)); if (offset >= 0) {
ret = clamp(((loff_t) folio->index << PAGE_SHIFT) +
offset,
start_offset, end_offset);
folio_unlock(folio); folio_unlock(folio);
folio_batch_release(&fbatch); folio_batch_release(&fbatch);
return end_offset; return ret;
} }
folio_unlock(folio); folio_unlock(folio);
} }
...@@ -2916,7 +2919,7 @@ static loff_t bch2_seek_data(struct file *file, u64 offset) ...@@ -2916,7 +2919,7 @@ static loff_t bch2_seek_data(struct file *file, u64 offset)
return ret; return ret;
if (next_data > offset) if (next_data > offset)
next_data = bch2_next_pagecache_data(&inode->v, next_data = bch2_seek_pagecache_data(&inode->v,
offset, next_data); offset, next_data);
if (next_data >= isize) if (next_data >= isize)
...@@ -2925,34 +2928,56 @@ static loff_t bch2_seek_data(struct file *file, u64 offset) ...@@ -2925,34 +2928,56 @@ static loff_t bch2_seek_data(struct file *file, u64 offset)
return vfs_setpos(file, next_data, MAX_LFS_FILESIZE); 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; struct page *page;
bool ret; int pg_offset;
loff_t ret = -1;
page = find_lock_page(mapping, index); page = find_lock_page(mapping, index);
if (!page) 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); unlock_page(page);
return ret; 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 start_offset,
loff_t end_offset) loff_t end_offset)
{ {
struct address_space *mapping = vinode->i_mapping; struct address_space *mapping = vinode->i_mapping;
pgoff_t index; loff_t offset = start_offset, hole;
while (offset < end_offset) {
hole = page_hole_offset(mapping, offset);
if (hole >= 0 && hole <= end_offset)
return max(start_offset, hole);
for (index = start_offset >> PAGE_SHIFT; offset += PAGE_SIZE;
index < end_offset >> PAGE_SHIFT; offset &= PAGE_MASK;
index++) }
if (!page_slot_is_data(mapping, index))
end_offset = max(start_offset,
((loff_t) index) << PAGE_SHIFT);
return end_offset; return end_offset;
} }
...@@ -2977,11 +3002,11 @@ static loff_t bch2_seek_hole(struct file *file, u64 offset) ...@@ -2977,11 +3002,11 @@ static loff_t bch2_seek_hole(struct file *file, u64 offset)
POS(inode->v.i_ino, offset >> 9), POS(inode->v.i_ino, offset >> 9),
BTREE_ITER_SLOTS, k, ret) { BTREE_ITER_SLOTS, k, ret) {
if (k.k->p.inode != inode->v.i_ino) { 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); offset, MAX_LFS_FILESIZE);
break; break;
} else if (!bkey_extent_is_data(k.k)) { } 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), max(offset, bkey_start_offset(k.k) << 9),
k.k->p.offset << 9); k.k->p.offset << 9);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment