summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2015-07-09 19:28:20 -0700
committerKent Overstreet <kent.overstreet@gmail.com>2016-10-07 12:34:42 -0800
commit73f2fd7670ccc5bf354ee425588519a30ba99d70 (patch)
treeaaf4ffa498bf1fa01a13d47a46afe93de608fcb4
parentf20eb0b7f398f50a9a53801554589485241998ba (diff)
bcachefs: Atomic renames
-rw-r--r--drivers/md/bcache/dirent.c96
-rw-r--r--drivers/md/bcache/dirent.h3
-rw-r--r--drivers/md/bcache/fs.c35
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)