diff options
author | Kent Overstreet <kent.overstreet@gmail.com> | 2015-10-07 23:36:09 -0800 |
---|---|---|
committer | Kent Overstreet <kent.overstreet@gmail.com> | 2017-01-18 21:35:34 -0900 |
commit | 5db4e32856ad5d58aff74245f568cfa6c6c5ac90 (patch) | |
tree | 61a1f054ffd6921ecab7ae3e830d7f8c2fa6c0e0 | |
parent | 04dda119831bc015b734eedcf483a6d85726f479 (diff) |
bcachefs: RENAME_EXCHANGE
-rw-r--r-- | drivers/md/bcache/dirent.c | 140 | ||||
-rw-r--r-- | drivers/md/bcache/dirent.h | 12 | ||||
-rw-r--r-- | drivers/md/bcache/fs.c | 82 |
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, |