forked from mirrors/linux
		
	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: Filipe Manana <fdmanana@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
		
							parent
							
								
									d46fb845af
								
							
						
					
					
						commit
						eb10d85ee7
					
				
					 1 changed files with 75 additions and 60 deletions
				
			
		| 
						 | 
				
			
			@ -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;
 | 
			
		||||
			goto done;
 | 
			
		||||
		}
 | 
			
		||||
		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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue