Commit eb10d85e authored by Filipe Manana's avatar Filipe Manana Committed by David Sterba
Browse files

btrfs: factor out the copying loop of dir items from log_dir_items()



In preparation for the next change, move the loop that processes a leaf
and copies its directory items to the log, into a separate helper
function. This makes the next change simpler and it also helps making
log_dir_items() a bit shorter (specially after the next change).

This patch is part of a patchset comprised of the following 5 patches:

  btrfs: remove root argument from btrfs_log_inode() and its callees
  btrfs: remove redundant log root assignment from log_dir_items()
  btrfs: factor out the copying loop of dir items from log_dir_items()
  btrfs: insert items in batches when logging a directory when possible
  btrfs: keep track of the last logged keys when logging a directory

This is patch 3/5. The change log of the last patch (5/5) has performance
results.

Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent d46fb845
Loading
Loading
Loading
Loading
+75 −60
Original line number Diff line number Diff line
@@ -3632,6 +3632,66 @@ static noinline int insert_dir_log_key(struct btrfs_trans_handle *trans,
	return 0;
}

static int process_dir_items_leaf(struct btrfs_trans_handle *trans,
				  struct btrfs_inode *inode,
				  struct btrfs_path *path,
				  struct btrfs_path *dst_path,
				  int key_type,
				  struct btrfs_log_ctx *ctx)
{
	struct btrfs_root *log = inode->root->log_root;
	struct extent_buffer *src = path->nodes[0];
	const int nritems = btrfs_header_nritems(src);
	const u64 ino = btrfs_ino(inode);
	int i;

	for (i = path->slots[0]; i < nritems; i++) {
		struct btrfs_key key;
		struct btrfs_dir_item *di;
		int ret;

		btrfs_item_key_to_cpu(src, &key, i);

		if (key.objectid != ino || key.type != key_type)
			return 1;

		ret = overwrite_item(trans, log, dst_path, src, i, &key);
		if (ret < 0)
			return ret;

		/*
		 * We must make sure that when we log a directory entry, the
		 * corresponding inode, after log replay, has a matching link
		 * count. For example:
		 *
		 * touch foo
		 * mkdir mydir
		 * sync
		 * ln foo mydir/bar
		 * xfs_io -c "fsync" mydir
		 * <crash>
		 * <mount fs and log replay>
		 *
		 * Would result in a fsync log that when replayed, our file inode
		 * would have a link count of 1, but we get two directory entries
		 * pointing to the same inode. After removing one of the names,
		 * it would not be possible to remove the other name, which
		 * resulted always in stale file handle errors, and would not be
		 * possible to rmdir the parent directory, since its i_size could
		 * never be decremented to the value BTRFS_EMPTY_DIR_SIZE,
		 * resulting in -ENOTEMPTY errors.
		 */
		di = btrfs_item_ptr(src, i, struct btrfs_dir_item);
		btrfs_dir_item_key_to_cpu(src, di, &key);
		if ((btrfs_dir_transid(src, di) == trans->transid ||
		     btrfs_dir_type(src, di) == BTRFS_FT_DIR) &&
		    key.type != BTRFS_ROOT_ITEM_KEY)
			ctx->log_new_dentries = true;
	}

	return 0;
}

/*
 * log all the items included in the current transaction for a given
 * directory.  This also creates the range items in the log tree required
@@ -3647,11 +3707,8 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
	struct btrfs_key min_key;
	struct btrfs_root *root = inode->root;
	struct btrfs_root *log = root->log_root;
	struct extent_buffer *src;
	int err = 0;
	int ret;
	int i;
	int nritems;
	u64 first_offset = min_offset;
	u64 last_offset = (u64)-1;
	u64 ino = btrfs_ino(inode);
@@ -3729,61 +3786,14 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
	 * from our directory
	 */
	while (1) {
		struct btrfs_key tmp;
		src = path->nodes[0];
		nritems = btrfs_header_nritems(src);
		for (i = path->slots[0]; i < nritems; i++) {
			struct btrfs_dir_item *di;

			btrfs_item_key_to_cpu(src, &min_key, i);

			if (min_key.objectid != ino || min_key.type != key_type)
				goto done;

			if (need_resched()) {
				btrfs_release_path(path);
				cond_resched();
				goto search;
			}

			ret = overwrite_item(trans, log, dst_path, src, i,
					     &min_key);
			if (ret) {
		ret = process_dir_items_leaf(trans, inode, path, dst_path,
					     key_type, ctx);
		if (ret != 0) {
			if (ret < 0)
				err = ret;
			goto done;
		}

			/*
			 * We must make sure that when we log a directory entry,
			 * the corresponding inode, after log replay, has a
			 * matching link count. For example:
			 *
			 * touch foo
			 * mkdir mydir
			 * sync
			 * ln foo mydir/bar
			 * xfs_io -c "fsync" mydir
			 * <crash>
			 * <mount fs and log replay>
			 *
			 * Would result in a fsync log that when replayed, our
			 * file inode would have a link count of 1, but we get
			 * two directory entries pointing to the same inode.
			 * After removing one of the names, it would not be
			 * possible to remove the other name, which resulted
			 * always in stale file handle errors, and would not
			 * be possible to rmdir the parent directory, since
			 * its i_size could never decrement to the value
			 * BTRFS_EMPTY_DIR_SIZE, resulting in -ENOTEMPTY errors.
			 */
			di = btrfs_item_ptr(src, i, struct btrfs_dir_item);
			btrfs_dir_item_key_to_cpu(src, di, &tmp);
			if ((btrfs_dir_transid(src, di) == trans->transid ||
			     btrfs_dir_type(src, di) == BTRFS_FT_DIR) &&
			    tmp.type != BTRFS_ROOT_ITEM_KEY)
				ctx->log_new_dentries = true;
		}
		path->slots[0] = nritems;
		path->slots[0] = btrfs_header_nritems(path->nodes[0]);

		/*
		 * look ahead to the next item and see if it is also
@@ -3797,21 +3807,26 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
				err = ret;
			goto done;
		}
		btrfs_item_key_to_cpu(path->nodes[0], &tmp, path->slots[0]);
		if (tmp.objectid != ino || tmp.type != key_type) {
		btrfs_item_key_to_cpu(path->nodes[0], &min_key, path->slots[0]);
		if (min_key.objectid != ino || min_key.type != key_type) {
			last_offset = (u64)-1;
			goto done;
		}
		if (btrfs_header_generation(path->nodes[0]) != trans->transid) {
			ret = overwrite_item(trans, log, dst_path,
					     path->nodes[0], path->slots[0],
					     &tmp);
					     &min_key);
			if (ret)
				err = ret;
			else
				last_offset = tmp.offset;
				last_offset = min_key.offset;
			goto done;
		}
		if (need_resched()) {
			btrfs_release_path(path);
			cond_resched();
			goto search;
		}
	}
done:
	btrfs_release_path(path);