diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2015-07-09 19:28:20 -0700 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2016-10-07 12:34:42 -0800 |
commit | 73f2fd7670ccc5bf354ee425588519a30ba99d70 (patch) | |
tree | aaf4ffa498bf1fa01a13d47a46afe93de608fcb4 | |
parent | f20eb0b7f398f50a9a53801554589485241998ba (diff) |
bcachefs: Atomic renames
-rw-r--r-- | drivers/md/bcache/dirent.c | 96 | ||||
-rw-r--r-- | drivers/md/bcache/dirent.h | 3 | ||||
-rw-r--r-- | drivers/md/bcache/fs.c | 35 |
3 files changed, 94 insertions, 40 deletions
diff --git a/drivers/md/bcache/dirent.c b/drivers/md/bcache/dirent.c index e917bde7a6e7..e5ecc073825c 100644 --- a/drivers/md/bcache/dirent.c +++ b/drivers/md/bcache/dirent.c @@ -261,38 +261,94 @@ int bch_dirent_create(struct cache_set *c, u64 dir_inum, u8 type, return ret; } -int bch_dirent_update(struct cache_set *c, u64 dir_inum, - const struct qstr *name, u64 dst_inum, - u64 *journal_seq) +int bch_dirent_rename(struct cache_set *c, + u64 src_dir, const struct qstr *src_name, + u64 dst_dir, const struct qstr *dst_name, + u64 *journal_seq, bool overwriting) { - struct btree_iter iter; + struct btree_iter src_iter; + struct btree_iter dst_iter; struct bkey_s_c k; + struct bkey_s_c_dirent src; + struct bkey_i_dirent *dst; + struct bkey_i delete; struct keylist keys; - struct bkey_i_dirent *dirent; - int ret = -ENOENT; + int ret; - dirent = dirent_create_key(&keys, 0, name, dst_inum); - if (!dirent) + dst = dirent_create_key(&keys, 0, dst_name, 0); + if (!dst) return -ENOMEM; - bch_btree_iter_init_intent(&iter, c, BTREE_ID_DIRENTS, - POS(dir_inum, bch_dirent_hash(name))); + bch_btree_iter_init_intent(&src_iter, c, BTREE_ID_DIRENTS, + POS(src_dir, bch_dirent_hash(src_name))); + bch_btree_iter_init_intent(&dst_iter, c, BTREE_ID_DIRENTS, + POS(dst_dir, bch_dirent_hash(dst_name))); + bch_btree_iter_link(&src_iter, &dst_iter); do { - k = __dirent_find(&iter, dir_inum, name); - if (IS_ERR(k.k)) - return bch_btree_iter_unlock(&iter) ?: PTR_ERR(k.k); + /* + * Have to traverse lower btree nodes before higher - due to + * lock ordering. + */ + if (bkey_cmp(src_iter.pos, dst_iter.pos) < 0) { + k = __dirent_find(&src_iter, src_dir, src_name); + if (IS_ERR(k.k)) { + ret = PTR_ERR(k.k); + goto err; + } + + src = bkey_s_c_to_dirent(k); + + k = overwriting + ? __dirent_find(&dst_iter, dst_dir, dst_name) + : __dirent_find_hole(&dst_iter, dst_dir, dst_name); + if (IS_ERR(k.k)) { + ret = PTR_ERR(k.k); + goto err; + } + + dst->k.p = k.k->p; + } else { + k = overwriting + ? __dirent_find(&dst_iter, dst_dir, dst_name) + : __dirent_find_hole(&dst_iter, dst_dir, dst_name); + if (IS_ERR(k.k)) { + ret = PTR_ERR(k.k); + goto err; + } + + dst->k.p = k.k->p; + + k = __dirent_find(&src_iter, src_dir, src_name); + if (IS_ERR(k.k)) { + ret = PTR_ERR(k.k); + goto err; + } + + src = bkey_s_c_to_dirent(k); + } - dirent->k.p = k.k->p; - dirent->v.d_type = bkey_s_c_to_dirent(k).v->d_type; + bkey_init(&delete.k); + delete.k.p = src.k->p; + delete.k.type = BCH_DIRENT_WHITEOUT; - ret = bch_btree_insert_at(&iter, &keys, NULL, - journal_seq, - BTREE_INSERT_ATOMIC); - } while (ret == -EINTR); + dst->v.d_inum = src.v->d_inum; + dst->v.d_type = src.v->d_type; - bch_btree_iter_unlock(&iter); + ret = bch_btree_insert_at_multi((struct btree_insert_multi[]) { + { &src_iter, &delete, }, + { &dst_iter, &dst->k_i, }}, 2, + journal_seq, 0); + bch_btree_iter_unlock(&src_iter); + bch_btree_iter_unlock(&dst_iter); + } while (ret == -EINTR); + bch_keylist_free(&keys); + return ret; +err: + ret = bch_btree_iter_unlock(&src_iter) ?: ret; + ret = bch_btree_iter_unlock(&dst_iter) ?: ret; + bch_keylist_free(&keys); return ret; } diff --git a/drivers/md/bcache/dirent.h b/drivers/md/bcache/dirent.h index 4de22a53c875..9095bc844a4e 100644 --- a/drivers/md/bcache/dirent.h +++ b/drivers/md/bcache/dirent.h @@ -11,8 +11,9 @@ struct cache_set; int bch_dirent_create(struct cache_set *, u64, u8, const struct qstr *, u64, u64 *); -int bch_dirent_update(struct cache_set *, u64, const struct qstr *, u64, u64 *); int bch_dirent_delete(struct cache_set *, u64, const struct qstr *); +int bch_dirent_rename(struct cache_set *, u64, const struct qstr *, + u64, const struct qstr *, u64 *, bool); u64 bch_dirent_lookup(struct cache_set *, u64, const struct qstr *); int bch_empty_dir(struct cache_set *, u64); int bch_readdir(struct file *, struct dir_context *); diff --git a/drivers/md/bcache/fs.c b/drivers/md/bcache/fs.c index cf3c1bef8667..1388d915014a 100644 --- a/drivers/md/bcache/fs.c +++ b/drivers/md/bcache/fs.c @@ -542,10 +542,10 @@ static int bch_rename(struct inode *old_dir, struct dentry *old_dentry, if (bch_empty_dir(c, new_inode->i_ino)) return -ENOTEMPTY; - ret = bch_dirent_update(c, new_dir->i_ino, - &new_dentry->d_name, - old_inode->i_ino, - &ei->journal_seq); + ret = bch_dirent_rename(c, + old_dir->i_ino, &old_dentry->d_name, + new_dir->i_ino, &new_dentry->d_name, + &ei->journal_seq, true); if (unlikely(ret)) return ret; @@ -554,30 +554,30 @@ static int bch_rename(struct inode *old_dir, struct dentry *old_dentry, } else if (new_inode) { lockdep_assert_held(&new_inode->i_rwsem); - ret = bch_dirent_update(c, new_dir->i_ino, - &new_dentry->d_name, - old_inode->i_ino, - &ei->journal_seq); + ret = bch_dirent_rename(c, + old_dir->i_ino, &old_dentry->d_name, + new_dir->i_ino, &new_dentry->d_name, + &ei->journal_seq, true); if (unlikely(ret)) return ret; new_inode->i_ctime = now; inode_dec_link_count(new_inode); } else if (S_ISDIR(old_inode->i_mode)) { - ret = bch_vfs_dirent_create(c, new_dir, - mode_to_type(old_inode->i_mode), - &new_dentry->d_name, - old_inode); + ret = bch_dirent_rename(c, + old_dir->i_ino, &old_dentry->d_name, + new_dir->i_ino, &new_dentry->d_name, + &ei->journal_seq, false); if (unlikely(ret)) return ret; inode_inc_link_count(new_dir); inode_dec_link_count(old_dir); } else { - ret = bch_vfs_dirent_create(c, new_dir, - mode_to_type(old_inode->i_mode), - &new_dentry->d_name, - old_inode); + ret = bch_dirent_rename(c, + old_dir->i_ino, &old_dentry->d_name, + new_dir->i_ino, &new_dentry->d_name, + &ei->journal_seq, false); if (unlikely(ret)) return ret; } @@ -587,9 +587,6 @@ static int bch_rename(struct inode *old_dir, struct dentry *old_dentry, mark_inode_dirty_sync(old_dir); mark_inode_dirty_sync(new_dir); - /* XXX: error handling */ - bch_dirent_delete(c, old_dir->i_ino, &old_dentry->d_name); - mutex_lock(&ei->update_lock); old_inode->i_ctime = now; if (new_inode) |