summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kent.overstreet@gmail.com>2015-10-07 23:36:09 -0800
committerKent Overstreet <kent.overstreet@gmail.com>2017-01-18 21:35:34 -0900
commit5db4e32856ad5d58aff74245f568cfa6c6c5ac90 (patch)
tree61a1f054ffd6921ecab7ae3e830d7f8c2fa6c0e0
parent04dda119831bc015b734eedcf483a6d85726f479 (diff)
bcachefs: RENAME_EXCHANGE
-rw-r--r--drivers/md/bcache/dirent.c140
-rw-r--r--drivers/md/bcache/dirent.h12
-rw-r--r--drivers/md/bcache/fs.c82
3 files changed, 145 insertions, 89 deletions
diff --git a/drivers/md/bcache/dirent.c b/drivers/md/bcache/dirent.c
index 5f3f031c6b40..a730742efb44 100644
--- a/drivers/md/bcache/dirent.c
+++ b/drivers/md/bcache/dirent.c
@@ -132,22 +132,20 @@ const struct bkey_ops bch_bkey_dirent_ops = {
.val_to_text = bch_dirent_to_text,
};
-static struct bkey_i_dirent *dirent_create_key(struct keylist *keys, u8 type,
- const struct qstr *name, u64 dst)
+static struct bkey_i_dirent *dirent_create_key(u8 type,
+ const struct qstr *name,
+ u64 dst)
{
struct bkey_i_dirent *dirent;
unsigned u64s = BKEY_U64s +
DIV_ROUND_UP(sizeof(struct bch_dirent) + name->len,
sizeof(u64));
- bch_keylist_init(keys, NULL, 0);
-
- /* XXX: should try to do this without a kmalloc (in keylist_realloc()) */
-
- if (bch_keylist_realloc(keys, u64s))
+ dirent = kmalloc(u64s * sizeof(u64), GFP_KERNEL);
+ if (!dirent)
return NULL;
- dirent = bkey_dirent_init(keys->top);
+ bkey_dirent_init(&dirent->k_i);
dirent->k.u64s = u64s;
dirent->v.d_inum = dst;
dirent->v.d_type = type;
@@ -160,7 +158,6 @@ static struct bkey_i_dirent *dirent_create_key(struct keylist *keys, u8 type,
EBUG_ON(dirent_name_bytes(dirent_i_to_s_c(dirent)) != name->len);
EBUG_ON(dirent_cmp(dirent_i_to_s_c(dirent), name));
- bch_keylist_enqueue(keys);
return dirent;
}
@@ -226,11 +223,10 @@ int bch_dirent_create(struct cache_set *c, u64 dir_inum, u8 type,
{
struct btree_iter iter;
struct bkey_s_c k;
- struct keylist keys;
struct bkey_i_dirent *dirent;
int ret;
- dirent = dirent_create_key(&keys, type, name, dst_inum);
+ dirent = dirent_create_key(type, name, dst_inum);
if (!dirent)
return -ENOMEM;
@@ -246,8 +242,8 @@ int bch_dirent_create(struct cache_set *c, u64 dir_inum, u8 type,
dirent->k.p = k.k->p;
- ret = bch_btree_insert_at(&iter, &keys, NULL,
- journal_seq,
+ ret = bch_btree_insert_at(&iter, &keylist_single(&dirent->k_i),
+ NULL, journal_seq,
BTREE_INSERT_ATOMIC);
/*
* XXX: if we ever cleanup whiteouts, we may need to rewind
@@ -256,7 +252,7 @@ int bch_dirent_create(struct cache_set *c, u64 dir_inum, u8 type,
} while (ret == -EINTR);
bch_btree_iter_unlock(&iter);
- bch_keylist_free(&keys);
+ kfree(dirent);
return ret;
}
@@ -264,20 +260,27 @@ int bch_dirent_create(struct cache_set *c, u64 dir_inum, u8 type,
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)
+ u64 *journal_seq, enum bch_rename_mode mode)
{
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_s_c old_src, old_dst;
+ struct bkey_s_c_dirent old_src_d, old_dst_d;
struct bkey_i delete;
- struct keylist keys;
- int ret;
+ struct bkey_i_dirent *new_src = NULL, *new_dst = NULL;
+ int ret = -ENOMEM;
+
+ if (mode == BCH_RENAME_EXCHANGE) {
+ new_src = dirent_create_key(0, src_name, 0);
+ if (!new_src)
+ goto out;
+ } else {
+ new_src = (void *) &delete;
+ }
- dst = dirent_create_key(&keys, 0, dst_name, 0);
- if (!dst)
- return -ENOMEM;
+ new_dst = dirent_create_key(0, dst_name, 0);
+ if (!new_dst)
+ goto out;
bch_btree_iter_init_intent(&src_iter, c, BTREE_ID_DIRENTS,
POS(src_dir, bch_dirent_hash(src_name)));
@@ -301,65 +304,68 @@ int bch_dirent_rename(struct cache_set *c,
* 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;
+ old_src = __dirent_find(&src_iter, src_dir, src_name);
+
+ old_dst = mode == BCH_RENAME
+ ? __dirent_find_hole(&dst_iter, dst_dir, dst_name)
+ : __dirent_find(&dst_iter, dst_dir, dst_name);
} 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);
+ old_dst = mode == BCH_RENAME
+ ? __dirent_find_hole(&dst_iter, dst_dir, dst_name)
+ : __dirent_find(&dst_iter, dst_dir, dst_name);
+
+ old_src = __dirent_find(&src_iter, src_dir, src_name);
}
- bkey_init(&delete.k);
- delete.k.p = src.k->p;
- delete.k.type = BCH_DIRENT_WHITEOUT;
+ if (IS_ERR(old_src.k)) {
+ ret = PTR_ERR(old_src.k);
+ goto err;
+ }
+
+ if (IS_ERR(old_dst.k)) {
+ ret = PTR_ERR(old_dst.k);
+ goto err;
+ }
+
+ switch (mode) {
+ case BCH_RENAME:
+ case BCH_RENAME_OVERWRITE:
+ bkey_init(&delete.k);
+ delete.k.p = old_src.k->p;
+ delete.k.type = BCH_DIRENT_WHITEOUT;
+ break;
+ case BCH_RENAME_EXCHANGE:
+ old_dst_d = bkey_s_c_to_dirent(old_dst);
- dst->v.d_inum = src.v->d_inum;
- dst->v.d_type = src.v->d_type;
+ new_src->k.p = old_src.k->p;
+ new_src->v.d_inum = old_dst_d.v->d_inum;
+ new_src->v.d_type = old_dst_d.v->d_type;
+ break;
+ }
+
+ old_src_d = bkey_s_c_to_dirent(old_src);
+
+ new_dst->k.p = old_dst.k->p;
+ new_dst->v.d_inum = old_src_d.v->d_inum;
+ new_dst->v.d_type = old_src_d.v->d_type;
ret = bch_btree_insert_at_multi((struct btree_insert_multi[]) {
- { &src_iter, &delete, },
- { &dst_iter, &dst->k_i, }}, 2,
+ { &src_iter, &new_src->k_i, },
+ { &dst_iter, &new_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);
+out:
+ if (new_src != (void *) &delete)
+ kfree(new_src);
+ kfree(new_dst);
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;
+ goto out;
}
int bch_dirent_delete(struct cache_set *c, u64 dir_inum,
diff --git a/drivers/md/bcache/dirent.h b/drivers/md/bcache/dirent.h
index 9095bc844a4e..f7a1d08421d8 100644
--- a/drivers/md/bcache/dirent.h
+++ b/drivers/md/bcache/dirent.h
@@ -12,8 +12,16 @@ struct cache_set;
int bch_dirent_create(struct cache_set *, u64, u8, 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);
+
+enum bch_rename_mode {
+ BCH_RENAME,
+ BCH_RENAME_OVERWRITE,
+ BCH_RENAME_EXCHANGE,
+};
+
+int bch_dirent_rename(struct cache_set *, u64, const struct qstr *, u64,
+ const struct qstr *, u64 *, enum bch_rename_mode);
+
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 93cd56c243bc..3322dda37e50 100644
--- a/drivers/md/bcache/fs.c
+++ b/drivers/md/bcache/fs.c
@@ -643,8 +643,7 @@ static int bch_mknod(struct inode *dir, struct dentry *dentry,
}
static int bch_rename(struct inode *old_dir, struct dentry *old_dentry,
- struct inode *new_dir, struct dentry *new_dentry,
- unsigned flags)
+ struct inode *new_dir, struct dentry *new_dentry)
{
struct cache_set *c = old_dir->i_sb->s_fs_info;
struct inode *old_inode = old_dentry->d_inode;
@@ -653,17 +652,9 @@ static int bch_rename(struct inode *old_dir, struct dentry *old_dentry,
struct timespec now = CURRENT_TIME;
int ret;
- if (flags)
- return -EINVAL;
-
lockdep_assert_held(&old_dir->i_rwsem);
lockdep_assert_held(&new_dir->i_rwsem);
- /*
- * XXX: This isn't atomic w.r.t. unclean shutdowns, and we'd really like
- * it to be
- */
-
if (new_inode && S_ISDIR(old_inode->i_mode)) {
lockdep_assert_held(&new_inode->i_rwsem);
@@ -676,7 +667,7 @@ static int bch_rename(struct inode *old_dir, struct dentry *old_dentry,
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);
+ &ei->journal_seq, BCH_RENAME_OVERWRITE);
if (unlikely(ret))
return ret;
@@ -688,7 +679,7 @@ static int bch_rename(struct inode *old_dir, struct dentry *old_dentry,
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);
+ &ei->journal_seq, BCH_RENAME_OVERWRITE);
if (unlikely(ret))
return ret;
@@ -698,7 +689,7 @@ static int bch_rename(struct inode *old_dir, struct dentry *old_dentry,
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);
+ &ei->journal_seq, BCH_RENAME);
if (unlikely(ret))
return ret;
@@ -708,7 +699,7 @@ static int bch_rename(struct inode *old_dir, struct dentry *old_dentry,
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);
+ &ei->journal_seq, BCH_RENAME);
if (unlikely(ret))
return ret;
}
@@ -718,16 +709,67 @@ static int bch_rename(struct inode *old_dir, struct dentry *old_dentry,
mark_inode_dirty_sync(old_dir);
mark_inode_dirty_sync(new_dir);
- mutex_lock(&ei->update_lock);
old_inode->i_ctime = now;
- if (new_inode)
- old_inode->i_mtime = now;
- ret = bch_write_inode(c, ei);
- mutex_unlock(&ei->update_lock);
+ mark_inode_dirty_sync(old_inode);
return 0;
}
+static int bch_rename_exchange(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry)
+{
+ struct cache_set *c = old_dir->i_sb->s_fs_info;
+ struct inode *old_inode = old_dentry->d_inode;
+ struct inode *new_inode = new_dentry->d_inode;
+ struct bch_inode_info *ei = to_bch_ei(old_inode);
+ struct timespec now = CURRENT_TIME;
+ int ret;
+
+ ret = bch_dirent_rename(c,
+ old_dir->i_ino, &old_dentry->d_name,
+ new_dir->i_ino, &new_dentry->d_name,
+ &ei->journal_seq, BCH_RENAME_EXCHANGE);
+ if (unlikely(ret))
+ return ret;
+
+ if (S_ISDIR(old_inode->i_mode) !=
+ S_ISDIR(new_inode->i_mode)) {
+ if (S_ISDIR(old_inode->i_mode)) {
+ inode_inc_link_count(new_dir);
+ inode_dec_link_count(old_dir);
+ } else {
+ inode_dec_link_count(new_dir);
+ inode_inc_link_count(old_dir);
+ }
+ }
+
+ old_dir->i_ctime = old_dir->i_mtime = now;
+ new_dir->i_ctime = new_dir->i_mtime = now;
+ mark_inode_dirty_sync(old_dir);
+ mark_inode_dirty_sync(new_dir);
+
+ old_inode->i_ctime = now;
+ new_inode->i_ctime = now;
+ mark_inode_dirty_sync(old_inode);
+ mark_inode_dirty_sync(new_inode);
+
+ return 0;
+}
+
+static int bch_rename2(struct inode *old_dir, struct dentry *old_dentry,
+ struct inode *new_dir, struct dentry *new_dentry,
+ unsigned flags)
+{
+ if (flags & ~(RENAME_NOREPLACE|RENAME_EXCHANGE))
+ return -EINVAL;
+
+ if (flags & RENAME_EXCHANGE)
+ return bch_rename_exchange(old_dir, old_dentry,
+ new_dir, new_dentry);
+
+ return bch_rename(old_dir, old_dentry, new_dir, new_dentry);
+}
+
static int bch_truncate_page(struct address_space *mapping, loff_t from)
{
unsigned offset = from & (PAGE_SIZE - 1);
@@ -1240,7 +1282,7 @@ static const struct inode_operations bch_dir_inode_operations = {
.mkdir = bch_mkdir,
.rmdir = bch_rmdir,
.mknod = bch_mknod,
- .rename = bch_rename,
+ .rename = bch_rename2,
.setattr = bch_setattr,
.tmpfile = bch_tmpfile,
.listxattr = bch_xattr_list,