diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2018-07-06 20:25:14 -0400 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2018-07-16 03:57:03 -0400 |
commit | 7d6e154b71f88c0529b59c06e52aa536e64931e3 (patch) | |
tree | 655d4a44701451c9c8cf08889c87d21e580dce54 | |
parent | c767c31b951212e31dc13ba8f7a61c0ee9523934 (diff) |
bcachefs: Make unlink path atomic
We'll still need to scan for inodes flagged as BCH_INODE_UNLINKED on
recovery, but we'll no longer need to scan the dirents to and
recalculate i_nlink for every inode.
Signed-off-by: Kent Overstreet <kent.overstreet@gmail.com>
-rw-r--r-- | fs/bcachefs/fs.c | 73 |
1 files changed, 60 insertions, 13 deletions
diff --git a/fs/bcachefs/fs.c b/fs/bcachefs/fs.c index 0a34306723c2..eeabb3603a10 100644 --- a/fs/bcachefs/fs.c +++ b/fs/bcachefs/fs.c @@ -605,33 +605,80 @@ retry: return 0; } +static int inode_update_dir_for_unlink_fn(struct bch_inode_info *inode, + struct bch_inode_unpacked *bi, + void *p) +{ + struct bch_fs *c = inode->v.i_sb->s_fs_info; + struct bch_inode_info *unlink_inode = p; + struct timespec now = current_time(&inode->v); + + bi->bi_mtime = bi->bi_ctime = timespec_to_bch2_time(c, now); + + bi->bi_nlink -= S_ISDIR(unlink_inode->v.i_mode); + + return 0; +} + +static int inode_update_for_unlink_fn(struct bch_inode_info *inode, + struct bch_inode_unpacked *bi, + void *p) +{ + struct bch_fs *c = inode->v.i_sb->s_fs_info; + struct timespec now = current_time(&inode->v); + + bi->bi_ctime = timespec_to_bch2_time(c, now); + if (bi->bi_nlink) + bi->bi_nlink--; + else + bi->bi_flags |= BCH_INODE_UNLINKED; + + return 0; +} + static int bch2_unlink(struct inode *vdir, struct dentry *dentry) { struct bch_fs *c = vdir->i_sb->s_fs_info; struct bch_inode_info *dir = to_bch_ei(vdir); struct bch_inode_info *inode = to_bch_ei(dentry->d_inode); + struct bch_inode_unpacked dir_u, inode_u; + struct btree_trans trans; int ret; - lockdep_assert_held(&inode->v.i_rwsem); + bch2_trans_init(&trans, c); +retry: + bch2_trans_begin(&trans); - ret = bch2_dirent_delete(c, dir->v.i_ino, &dir->ei_str_hash, - &dentry->d_name, &dir->ei_journal_seq); + ret = __bch2_dirent_delete(&trans, dir->v.i_ino, + &dir->ei_str_hash, + &dentry->d_name) ?: + bch2_write_inode_trans(&trans, dir, &dir_u, + inode_update_dir_for_unlink_fn, + inode) ?: + bch2_write_inode_trans(&trans, inode, &inode_u, + inode_update_for_unlink_fn, + NULL) ?: + bch2_trans_commit(&trans, NULL, NULL, + &dir->ei_journal_seq, + BTREE_INSERT_ATOMIC| + BTREE_INSERT_NOUNLOCK| + BTREE_INSERT_NOFAIL); + if (ret == -EINTR) + goto retry; if (ret) - return ret; + goto err; if (dir->ei_journal_seq > inode->ei_journal_seq) inode->ei_journal_seq = dir->ei_journal_seq; - inode->v.i_ctime = dir->v.i_ctime; - - if (S_ISDIR(inode->v.i_mode)) { - bch2_dec_nlink(c, dir); - drop_nlink(&inode->v); - } - - bch2_dec_nlink(c, inode); + bch2_inode_update_after_write(c, dir, &dir_u, + ATTR_MTIME|ATTR_CTIME); + bch2_inode_update_after_write(c, inode, &inode_u, + ATTR_MTIME); +err: + bch2_trans_exit(&trans); - return 0; + return ret; } static int bch2_symlink(struct inode *vdir, struct dentry *dentry, |