Skip to content

Commit 193df62

Browse files
fdmananakdave
authored andcommitted
btrfs: search for last logged dir index if it's not cached in the inode
The key offset of the last dir index item that was logged is stored in the inode's last_dir_index_offset field. However that field is not persisted in the inode item or elsewhere, so if the inode gets evicted and reloaded, it gets a value of (u64)-1, so that when we are logging dir index items we check if they were logged before, to avoid attempts to insert duplicated keys and fallback to a transaction commit. Improve on this by searching for the last dir index that was logged when we start logging a directory if the inode's last_dir_index_offset is not set (has a value of (u64)-1) and it was logged before. This avoids checking if each dir index item we find was already logged before, and simplifies the logging of dir index items (process_dir_items_leaf()). This will also be needed for an incoming change where we start logging delayed items directly, without flushing them first. Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 4c46979 commit 193df62

File tree

1 file changed

+74
-45
lines changed

1 file changed

+74
-45
lines changed

fs/btrfs/tree-log.c

Lines changed: 74 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3875,6 +3875,11 @@ static int process_dir_items_leaf(struct btrfs_trans_handle *trans,
38753875
*last_old_dentry_offset = key.offset;
38763876
continue;
38773877
}
3878+
3879+
/* If we logged this dir index item before, we can skip it. */
3880+
if (key.offset <= inode->last_dir_index_offset)
3881+
continue;
3882+
38783883
/*
38793884
* We must make sure that when we log a directory entry, the
38803885
* corresponding inode, after log replay, has a matching link
@@ -3905,51 +3910,6 @@ static int process_dir_items_leaf(struct btrfs_trans_handle *trans,
39053910
ctx->log_new_dentries = true;
39063911
}
39073912

3908-
if (!ctx->logged_before)
3909-
goto add_to_batch;
3910-
3911-
/*
3912-
* If we were logged before and have logged dir items, we can skip
3913-
* checking if any item with a key offset larger than the last one
3914-
* we logged is in the log tree, saving time and avoiding adding
3915-
* contention on the log tree. We can only rely on the value of
3916-
* last_dir_index_offset when we know for sure that the inode was
3917-
* previously logged in the current transaction.
3918-
*/
3919-
if (key.offset > inode->last_dir_index_offset)
3920-
goto add_to_batch;
3921-
/*
3922-
* Check if the key was already logged before. If not we can add
3923-
* it to a batch for bulk insertion.
3924-
*/
3925-
ret = btrfs_search_slot(NULL, log, &key, dst_path, 0, 0);
3926-
if (ret < 0) {
3927-
return ret;
3928-
} else if (ret > 0) {
3929-
btrfs_release_path(dst_path);
3930-
goto add_to_batch;
3931-
}
3932-
3933-
/*
3934-
* Item exists in the log. Overwrite the item in the log if it
3935-
* has different content or do nothing if it has exactly the same
3936-
* content. And then flush the current batch if any - do it after
3937-
* overwriting the current item, or we would deadlock otherwise,
3938-
* since we are holding a path for the existing item.
3939-
*/
3940-
ret = do_overwrite_item(trans, log, dst_path, src, i, &key);
3941-
if (ret < 0)
3942-
return ret;
3943-
3944-
if (batch_size > 0) {
3945-
ret = flush_dir_items_batch(trans, log, src, dst_path,
3946-
batch_start, batch_size);
3947-
if (ret < 0)
3948-
return ret;
3949-
batch_size = 0;
3950-
}
3951-
continue;
3952-
add_to_batch:
39533913
if (batch_size == 0)
39543914
batch_start = i;
39553915
batch_size++;
@@ -4135,6 +4095,71 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
41354095
return err;
41364096
}
41374097

4098+
/*
4099+
* If the inode was logged before and it was evicted, then its
4100+
* last_dir_index_offset is (u64)-1, so we don't the value of the last index
4101+
* key offset. If that's the case, search for it and update the inode. This
4102+
* is to avoid lookups in the log tree every time we try to insert a dir index
4103+
* key from a leaf changed in the current transaction, and to allow us to always
4104+
* do batch insertions of dir index keys.
4105+
*/
4106+
static int update_last_dir_index_offset(struct btrfs_inode *inode,
4107+
struct btrfs_path *path,
4108+
const struct btrfs_log_ctx *ctx)
4109+
{
4110+
const u64 ino = btrfs_ino(inode);
4111+
struct btrfs_key key;
4112+
int ret;
4113+
4114+
lockdep_assert_held(&inode->log_mutex);
4115+
4116+
if (inode->last_dir_index_offset != (u64)-1)
4117+
return 0;
4118+
4119+
if (!ctx->logged_before) {
4120+
inode->last_dir_index_offset = BTRFS_DIR_START_INDEX - 1;
4121+
return 0;
4122+
}
4123+
4124+
key.objectid = ino;
4125+
key.type = BTRFS_DIR_INDEX_KEY;
4126+
key.offset = (u64)-1;
4127+
4128+
ret = btrfs_search_slot(NULL, inode->root->log_root, &key, path, 0, 0);
4129+
/*
4130+
* An error happened or we actually have an index key with an offset
4131+
* value of (u64)-1. Bail out, we're done.
4132+
*/
4133+
if (ret <= 0)
4134+
goto out;
4135+
4136+
ret = 0;
4137+
inode->last_dir_index_offset = BTRFS_DIR_START_INDEX - 1;
4138+
4139+
/*
4140+
* No dir index items, bail out and leave last_dir_index_offset with
4141+
* the value right before the first valid index value.
4142+
*/
4143+
if (path->slots[0] == 0)
4144+
goto out;
4145+
4146+
/*
4147+
* btrfs_search_slot() left us at one slot beyond the slot with the last
4148+
* index key, or beyond the last key of the directory that is not an
4149+
* index key. If we have an index key before, set last_dir_index_offset
4150+
* to its offset value, otherwise leave it with a value right before the
4151+
* first valid index value, as it means we have an empty directory.
4152+
*/
4153+
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0] - 1);
4154+
if (key.objectid == ino && key.type == BTRFS_DIR_INDEX_KEY)
4155+
inode->last_dir_index_offset = key.offset;
4156+
4157+
out:
4158+
btrfs_release_path(path);
4159+
4160+
return ret;
4161+
}
4162+
41384163
/*
41394164
* logging directories is very similar to logging inodes, We find all the items
41404165
* from the current transaction and write them to the log.
@@ -4157,6 +4182,10 @@ static noinline int log_directory_changes(struct btrfs_trans_handle *trans,
41574182
u64 max_key;
41584183
int ret;
41594184

4185+
ret = update_last_dir_index_offset(inode, path, ctx);
4186+
if (ret)
4187+
return ret;
4188+
41604189
min_key = BTRFS_DIR_START_INDEX;
41614190
max_key = 0;
41624191
ctx->last_dir_item_offset = inode->last_dir_index_offset;

0 commit comments

Comments
 (0)