commit 6afaed53cc9adde69d8a76ff5b4d740d5efbc54c upstream. When logging a directory, we always set the inode's last_dir_index_offset to the offset of the last dir index item we found. This is using an extra field in the log context structure, and it makes more sense to update it only after we insert dir index items, and we could directly update the inode's last_dir_index_offset field instead. So make this simpler by updating the inode's last_dir_index_offset only when we actually insert dir index keys in the log tree, and getting rid of the last_dir_item_offset field in the log context structure. Reported-by: David Arendt <admin@prnet.org> Link: https://lore.kernel.org/linux-btrfs/ae169fc6-f504-28f0-a098-6fa6a4dfb612@leemhuis.info/ Reported-by: Maxim Mikityanskiy <maxtram95@gmail.com> Link: https://lore.kernel.org/linux-btrfs/Y8voyTXdnPDz8xwY@mail.gmail.com/ Reported-by: Hunter Wardlaw <wardlawhunter@gmail.com> Link: https://bugzilla.suse.com/show_bug.cgi?id=1207231 Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=216851 CC: stable@vger.kernel.org # 6.1+ Reviewed-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
105 lines
3.3 KiB
C
105 lines
3.3 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* Copyright (C) 2008 Oracle. All rights reserved.
|
|
*/
|
|
|
|
#ifndef BTRFS_TREE_LOG_H
|
|
#define BTRFS_TREE_LOG_H
|
|
|
|
#include "ctree.h"
|
|
#include "transaction.h"
|
|
|
|
/* return value for btrfs_log_dentry_safe that means we don't need to log it at all */
|
|
#define BTRFS_NO_LOG_SYNC 256
|
|
|
|
/* We can't use the tree log for whatever reason, force a transaction commit */
|
|
#define BTRFS_LOG_FORCE_COMMIT (1)
|
|
|
|
struct btrfs_log_ctx {
|
|
int log_ret;
|
|
int log_transid;
|
|
bool log_new_dentries;
|
|
bool logging_new_name;
|
|
bool logging_new_delayed_dentries;
|
|
/* Indicate if the inode being logged was logged before. */
|
|
bool logged_before;
|
|
struct inode *inode;
|
|
struct list_head list;
|
|
/* Only used for fast fsyncs. */
|
|
struct list_head ordered_extents;
|
|
struct list_head conflict_inodes;
|
|
int num_conflict_inodes;
|
|
bool logging_conflict_inodes;
|
|
};
|
|
|
|
static inline void btrfs_init_log_ctx(struct btrfs_log_ctx *ctx,
|
|
struct inode *inode)
|
|
{
|
|
ctx->log_ret = 0;
|
|
ctx->log_transid = 0;
|
|
ctx->log_new_dentries = false;
|
|
ctx->logging_new_name = false;
|
|
ctx->logging_new_delayed_dentries = false;
|
|
ctx->logged_before = false;
|
|
ctx->inode = inode;
|
|
INIT_LIST_HEAD(&ctx->list);
|
|
INIT_LIST_HEAD(&ctx->ordered_extents);
|
|
INIT_LIST_HEAD(&ctx->conflict_inodes);
|
|
ctx->num_conflict_inodes = 0;
|
|
ctx->logging_conflict_inodes = false;
|
|
}
|
|
|
|
static inline void btrfs_release_log_ctx_extents(struct btrfs_log_ctx *ctx)
|
|
{
|
|
struct btrfs_ordered_extent *ordered;
|
|
struct btrfs_ordered_extent *tmp;
|
|
|
|
ASSERT(inode_is_locked(ctx->inode));
|
|
|
|
list_for_each_entry_safe(ordered, tmp, &ctx->ordered_extents, log_list) {
|
|
list_del_init(&ordered->log_list);
|
|
btrfs_put_ordered_extent(ordered);
|
|
}
|
|
}
|
|
|
|
static inline void btrfs_set_log_full_commit(struct btrfs_trans_handle *trans)
|
|
{
|
|
WRITE_ONCE(trans->fs_info->last_trans_log_full_commit, trans->transid);
|
|
}
|
|
|
|
static inline int btrfs_need_log_full_commit(struct btrfs_trans_handle *trans)
|
|
{
|
|
return READ_ONCE(trans->fs_info->last_trans_log_full_commit) ==
|
|
trans->transid;
|
|
}
|
|
|
|
int btrfs_sync_log(struct btrfs_trans_handle *trans,
|
|
struct btrfs_root *root, struct btrfs_log_ctx *ctx);
|
|
int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root);
|
|
int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
|
|
struct btrfs_fs_info *fs_info);
|
|
int btrfs_recover_log_trees(struct btrfs_root *tree_root);
|
|
int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans,
|
|
struct dentry *dentry,
|
|
struct btrfs_log_ctx *ctx);
|
|
void btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
|
|
struct btrfs_root *root,
|
|
const char *name, int name_len,
|
|
struct btrfs_inode *dir, u64 index);
|
|
void btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
|
|
struct btrfs_root *root,
|
|
const char *name, int name_len,
|
|
struct btrfs_inode *inode, u64 dirid);
|
|
void btrfs_end_log_trans(struct btrfs_root *root);
|
|
void btrfs_pin_log_trans(struct btrfs_root *root);
|
|
void btrfs_record_unlink_dir(struct btrfs_trans_handle *trans,
|
|
struct btrfs_inode *dir, struct btrfs_inode *inode,
|
|
int for_rename);
|
|
void btrfs_record_snapshot_destroy(struct btrfs_trans_handle *trans,
|
|
struct btrfs_inode *dir);
|
|
void btrfs_log_new_name(struct btrfs_trans_handle *trans,
|
|
struct dentry *old_dentry, struct btrfs_inode *old_dir,
|
|
u64 old_dir_index, struct dentry *parent);
|
|
|
|
#endif
|