diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2019-07-30 13:49:17 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2023-10-22 17:08:24 -0400 |
commit | 543ef2ebcd90686d999f18b0a874690b7976b239 (patch) | |
tree | 3d0ba0d4b8dbdf2d5936ca047697491f96c3e0a6 /fs/bcachefs/fs-io.c | |
parent | d1542e0362de069f677dfb0e9336438afb8fae74 (diff) | |
download | lwn-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.c | 85 |
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); |