diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2016-02-25 19:30:32 -0900 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2017-01-18 21:38:27 -0900 |
commit | 0e5a7f452abbe58cf98e3b8a48efe76c6dc6cee0 (patch) | |
tree | f6c1d709f7f3bccebb2350178f4656affc2834c4 | |
parent | 70beb52ed9be1d89a7c177330ad57458f12fdedb (diff) |
bcachefs: SEEK_DATA/SEEK_HOLE
-rw-r--r-- | drivers/md/bcache/fs-io.c | 162 | ||||
-rw-r--r-- | drivers/md/bcache/fs-io.h | 2 | ||||
-rw-r--r-- | drivers/md/bcache/fs.c | 2 |
3 files changed, 165 insertions, 1 deletions
diff --git a/drivers/md/bcache/fs-io.c b/drivers/md/bcache/fs-io.c index 5a4f164b4211..8cd99b972012 100644 --- a/drivers/md/bcache/fs-io.c +++ b/drivers/md/bcache/fs-io.c @@ -2369,3 +2369,165 @@ long bch_fallocate_dispatch(struct file *file, int mode, return -EOPNOTSUPP; } + +static bool page_is_data(struct page *page) +{ + return PagePrivate(page) && + page_state(page)->alloc_state != BCH_PAGE_UNALLOCATED; +} + +static loff_t bch_next_pagecache_data(struct inode *inode, + loff_t start_offset, + loff_t end_offset) +{ + struct address_space *mapping = inode->i_mapping; + struct page *page; + pgoff_t index; + + for (index = start_offset >> PAGE_SHIFT; + index < end_offset >> PAGE_SHIFT; + index++) { + if (find_get_pages(mapping, index, 1, &page)) { + lock_page(page); + index = page->index; + + if (page_is_data(page)) + end_offset = + min(end_offset, + max(start_offset, + ((loff_t) index) << PAGE_SHIFT)); + unlock_page(page); + put_page(page); + } else { + break; + } + } + + return end_offset; +} + +static loff_t bch_seek_data(struct file *file, u64 offset) +{ + struct inode *inode = file->f_mapping->host; + struct cache_set *c = inode->i_sb->s_fs_info; + struct btree_iter iter; + struct bkey_s_c k; + u64 isize, next_data = MAX_LFS_FILESIZE; + int ret; + + isize = i_size_read(inode); + if (offset >= isize) + return -ENXIO; + + for_each_btree_key(&iter, c, BTREE_ID_EXTENTS, + POS(inode->i_ino, offset >> 9), k) { + if (k.k->p.inode != inode->i_ino) { + break; + } else if (bkey_extent_is_data(k.k)) { + next_data = max(offset, bkey_start_offset(k.k) << 9); + break; + } else if (k.k->p.offset >> 9 > isize) + break; + } + + ret = bch_btree_iter_unlock(&iter); + if (ret) + return ret; + + if (next_data > offset) + next_data = bch_next_pagecache_data(inode, offset, next_data); + + if (next_data > isize) + return -ENXIO; + + return vfs_setpos(file, next_data, MAX_LFS_FILESIZE); +} + +static bool page_slot_is_data(struct address_space *mapping, pgoff_t index) +{ + struct page *page; + bool ret; + + page = find_lock_entry(mapping, index); + if (!page) + return false; + + ret = page_is_data(page); + unlock_page(page); + + return ret; +} + +static loff_t bch_next_pagecache_hole(struct inode *inode, + loff_t start_offset, + loff_t end_offset) +{ + struct address_space *mapping = inode->i_mapping; + pgoff_t index; + + 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); + + return end_offset; +} + +static loff_t bch_seek_hole(struct file *file, u64 offset) +{ + struct inode *inode = file->f_mapping->host; + struct cache_set *c = inode->i_sb->s_fs_info; + struct btree_iter iter; + struct bkey_s_c k; + u64 isize, next_hole = MAX_LFS_FILESIZE; + int ret; + + isize = i_size_read(inode); + if (offset >= isize) + return -ENXIO; + + for_each_btree_key_with_holes(&iter, c, BTREE_ID_EXTENTS, + POS(inode->i_ino, offset >> 9), k) { + if (k.k->p.inode != inode->i_ino) { + next_hole = bch_next_pagecache_hole(inode, + offset, MAX_LFS_FILESIZE); + break; + } else if (!bkey_extent_is_data(k.k)) { + next_hole = bch_next_pagecache_hole(inode, + max(offset, bkey_start_offset(k.k) << 9), + k.k->p.offset << 9); + + if (next_hole < k.k->p.offset << 9) + break; + } else { + offset = max(offset, bkey_start_offset(k.k) << 9); + } + } + + ret = bch_btree_iter_unlock(&iter); + if (ret) + return ret; + + if (next_hole > isize) + next_hole = isize; + + return vfs_setpos(file, next_hole, MAX_LFS_FILESIZE); +} + +loff_t bch_llseek(struct file *file, loff_t offset, int whence) +{ + switch (whence) { + case SEEK_SET: + case SEEK_CUR: + case SEEK_END: + return generic_file_llseek(file, offset, whence); + case SEEK_DATA: + return bch_seek_data(file, offset); + case SEEK_HOLE: + return bch_seek_hole(file, offset); + } + + return -EINVAL; +} diff --git a/drivers/md/bcache/fs-io.h b/drivers/md/bcache/fs-io.h index 28fc4a64132b..310508d35a89 100644 --- a/drivers/md/bcache/fs-io.h +++ b/drivers/md/bcache/fs-io.h @@ -26,6 +26,8 @@ int bch_fsync(struct file *, loff_t, loff_t, int); int bch_truncate(struct inode *, struct iattr *); long bch_fallocate_dispatch(struct file *, int, loff_t, loff_t); +loff_t bch_llseek(struct file *, loff_t, int); + int bch_page_mkwrite(struct vm_area_struct *, struct vm_fault *); void bch_invalidatepage(struct page *, unsigned int, unsigned int); int bch_releasepage(struct page *, gfp_t); diff --git a/drivers/md/bcache/fs.c b/drivers/md/bcache/fs.c index ad00dbbb8a5f..4a6cb2b51143 100644 --- a/drivers/md/bcache/fs.c +++ b/drivers/md/bcache/fs.c @@ -916,7 +916,7 @@ static loff_t bch_dir_llseek(struct file *file, loff_t offset, int whence) } static const struct file_operations bch_file_operations = { - .llseek = generic_file_llseek, + .llseek = bch_llseek, .read_iter = generic_file_read_iter, .write_iter = bch_write_iter, .mmap = bch_mmap, |