diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2019-09-07 18:04:23 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@linux.dev> | 2023-10-22 17:08:27 -0400 |
commit | 5f786787adf57c7597925a9df9897238cb3bc60e (patch) | |
tree | b652c8289372d1bc1f16addcf6d31f96b7597afa /fs/bcachefs/fs-io.c | |
parent | ccf5a1095892633bdb4bd1ac6f7f60aa9c4f327b (diff) | |
download | lwn-5f786787adf57c7597925a9df9897238cb3bc60e.tar.gz lwn-5f786787adf57c7597925a9df9897238cb3bc60e.zip |
bcachefs: Add support for FALLOC_FL_INSERT_RANGE
Somewhat tricky and ugly, because iterating over extents backwards is a
pain.
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 | 131 |
1 files changed, 91 insertions, 40 deletions
diff --git a/fs/bcachefs/fs-io.c b/fs/bcachefs/fs-io.c index f05950da6957..142eb0c3cbbc 100644 --- a/fs/bcachefs/fs-io.c +++ b/fs/bcachefs/fs-io.c @@ -2618,14 +2618,16 @@ err: return ret; } -static long bch2_fcollapse(struct bch_inode_info *inode, - loff_t offset, loff_t len) +static long bch2_fcollapse_finsert(struct bch_inode_info *inode, + loff_t offset, loff_t len, + bool insert) { struct bch_fs *c = inode->v.i_sb->s_fs_info; struct address_space *mapping = inode->v.i_mapping; struct btree_trans trans; struct btree_iter *src, *dst, *del = NULL; - loff_t new_size; + loff_t shift, new_size; + u64 src_start; int ret; if ((offset | len) & (block_bytes(c) - 1)) @@ -2643,34 +2645,53 @@ static long bch2_fcollapse(struct bch_inode_info *inode, inode_dio_wait(&inode->v); bch2_pagecache_block_get(&inode->ei_pagecache_lock); - ret = -EINVAL; - if (offset + len >= inode->v.i_size) - goto err; + if (insert) { + ret = -EFBIG; + if (inode->v.i_sb->s_maxbytes - inode->v.i_size < len) + goto err; - if (inode->v.i_size < len) - goto err; + ret = -EINVAL; + if (offset >= inode->v.i_size) + goto err; - new_size = inode->v.i_size - len; + src_start = U64_MAX; + shift = len; + } else { + ret = -EINVAL; + if (offset + len >= inode->v.i_size) + goto err; - ret = write_invalidate_inode_pages_range(mapping, offset, LLONG_MAX); - if (ret) - goto err; + src_start = offset + len; + shift = -len; + } + + new_size = inode->v.i_size + shift; - ret = __bch2_fpunch(c, inode, offset >> 9, - (offset + len) >> 9); + ret = write_invalidate_inode_pages_range(mapping, offset, LLONG_MAX); if (ret) goto err; - dst = bch2_trans_get_iter(&trans, BTREE_ID_EXTENTS, - POS(inode->v.i_ino, offset >> 9), - BTREE_ITER_INTENT); - BUG_ON(IS_ERR_OR_NULL(dst)); + if (insert) { + i_size_write(&inode->v, new_size); + mutex_lock(&inode->ei_update_lock); + ret = bch2_write_inode_size(c, inode, new_size, + ATTR_MTIME|ATTR_CTIME); + mutex_unlock(&inode->ei_update_lock); + } else { + ret = __bch2_fpunch(c, inode, offset >> 9, + (offset + len) >> 9); + if (ret) + goto err; + } src = bch2_trans_get_iter(&trans, BTREE_ID_EXTENTS, - POS(inode->v.i_ino, (offset + len) >> 9), + POS(inode->v.i_ino, src_start >> 9), BTREE_ITER_INTENT); BUG_ON(IS_ERR_OR_NULL(src)); + dst = bch2_trans_copy_iter(&trans, src); + BUG_ON(IS_ERR_OR_NULL(dst)); + while (1) { struct disk_reservation disk_res = bch2_disk_reservation_init(c, 0); @@ -2678,38 +2699,61 @@ static long bch2_fcollapse(struct bch_inode_info *inode, struct bkey_i delete; struct bkey_s_c k; struct bpos next_pos; + struct bpos move_pos = POS(inode->v.i_ino, offset >> 9); + struct bpos atomic_end; unsigned commit_flags = BTREE_INSERT_NOFAIL| BTREE_INSERT_ATOMIC| BTREE_INSERT_USE_RESERVE; - k = bch2_btree_iter_peek(src); + k = insert + ? bch2_btree_iter_peek_prev(src) + : bch2_btree_iter_peek(src); if ((ret = bkey_err(k))) goto bkey_err; if (!k.k || k.k->p.inode != inode->v.i_ino) break; - BUG_ON(src->pos.offset != bkey_start_offset(k.k)); + BUG_ON(bkey_cmp(src->pos, bkey_start_pos(k.k))); + + if (insert && + bkey_cmp(k.k->p, POS(inode->v.i_ino, offset >> 9)) <= 0) + break; +reassemble: + bkey_reassemble(©.k, k); + + if (insert && + bkey_cmp(bkey_start_pos(k.k), move_pos) < 0) { + bch2_cut_front(move_pos, ©.k); + bch2_btree_iter_set_pos(src, bkey_start_pos(©.k.k)); + } - bch2_btree_iter_set_pos(dst, - POS(inode->v.i_ino, src->pos.offset - (len >> 9))); + copy.k.k.p.offset += shift >> 9; + bch2_btree_iter_set_pos(dst, bkey_start_pos(©.k.k)); ret = bch2_btree_iter_traverse(dst); if (ret) goto bkey_err; - bkey_reassemble(©.k, k); - copy.k.k.p = dst->pos; - copy.k.k.p.offset += copy.k.k.size; - ret = bch2_extent_trim_atomic(©.k, dst); + ret = bch2_extent_atomic_end(dst, ©.k, &atomic_end); if (ret) goto bkey_err; + if (bkey_cmp(atomic_end, copy.k.k.p)) { + if (insert) { + move_pos = atomic_end; + move_pos.offset -= shift >> 9; + goto reassemble; + } else { + bch2_cut_back(atomic_end, ©.k.k); + } + } + bkey_init(&delete.k); delete.k.p = src->pos; bch2_key_resize(&delete.k, copy.k.k.size); - next_pos = delete.k.p; + next_pos = insert ? bkey_start_pos(&delete.k) : delete.k.p; /* * If the new and old keys overlap (because we're moving an @@ -2719,7 +2763,12 @@ static long bch2_fcollapse(struct bch_inode_info *inode, * triggers are run, so the overwrite will get double counted * by the triggers machinery: */ - if (bkey_cmp(copy.k.k.p, bkey_start_pos(&delete.k)) > 0) { + if (insert && + bkey_cmp(bkey_start_pos(©.k.k), delete.k.p) < 0) { + bch2_cut_back(bkey_start_pos(©.k.k), &delete.k); + } else if (!insert && + bkey_cmp(copy.k.k.p, + bkey_start_pos(&delete.k)) > 0) { bch2_cut_front(copy.k.k.p, &delete); del = bch2_trans_copy_iter(&trans, src); @@ -2727,14 +2776,11 @@ static long bch2_fcollapse(struct bch_inode_info *inode, bch2_btree_iter_set_pos(del, bkey_start_pos(&delete.k)); - bch2_trans_update(&trans, - BTREE_INSERT_ENTRY(del, &delete)); - } else { - bch2_trans_update(&trans, - BTREE_INSERT_ENTRY(src, &delete)); } bch2_trans_update(&trans, BTREE_INSERT_ENTRY(dst, ©.k)); + bch2_trans_update(&trans, + BTREE_INSERT_ENTRY(del ?: src, &delete)); if (copy.k.k.size == k.k->size) { /* @@ -2774,11 +2820,13 @@ bkey_err: } bch2_trans_unlock(&trans); - i_size_write(&inode->v, new_size); - mutex_lock(&inode->ei_update_lock); - ret = bch2_write_inode_size(c, inode, new_size, - ATTR_MTIME|ATTR_CTIME); - mutex_unlock(&inode->ei_update_lock); + if (!insert) { + i_size_write(&inode->v, new_size); + mutex_lock(&inode->ei_update_lock); + ret = bch2_write_inode_size(c, inode, new_size, + ATTR_MTIME|ATTR_CTIME); + mutex_unlock(&inode->ei_update_lock); + } err: bch2_trans_exit(&trans); bch2_pagecache_block_put(&inode->ei_pagecache_lock); @@ -2947,8 +2995,11 @@ long bch2_fallocate_dispatch(struct file *file, int mode, if (mode == (FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE)) return bch2_fpunch(inode, offset, len); + if (mode == FALLOC_FL_INSERT_RANGE) + return bch2_fcollapse_finsert(inode, offset, len, true); + if (mode == FALLOC_FL_COLLAPSE_RANGE) - return bch2_fcollapse(inode, offset, len); + return bch2_fcollapse_finsert(inode, offset, len, false); return -EOPNOTSUPP; } |