mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	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;
 | 
						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
 | 
					 * log all the items included in the current transaction for a given
 | 
				
			||||||
 * directory.  This also creates the range items in the log tree required
 | 
					 * 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_key min_key;
 | 
				
			||||||
	struct btrfs_root *root = inode->root;
 | 
						struct btrfs_root *root = inode->root;
 | 
				
			||||||
	struct btrfs_root *log = root->log_root;
 | 
						struct btrfs_root *log = root->log_root;
 | 
				
			||||||
	struct extent_buffer *src;
 | 
					 | 
				
			||||||
	int err = 0;
 | 
						int err = 0;
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
	int i;
 | 
					 | 
				
			||||||
	int nritems;
 | 
					 | 
				
			||||||
	u64 first_offset = min_offset;
 | 
						u64 first_offset = min_offset;
 | 
				
			||||||
	u64 last_offset = (u64)-1;
 | 
						u64 last_offset = (u64)-1;
 | 
				
			||||||
	u64 ino = btrfs_ino(inode);
 | 
						u64 ino = btrfs_ino(inode);
 | 
				
			||||||
| 
						 | 
					@ -3729,61 +3786,14 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
 | 
				
			||||||
	 * from our directory
 | 
						 * from our directory
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	while (1) {
 | 
						while (1) {
 | 
				
			||||||
		struct btrfs_key tmp;
 | 
							ret = process_dir_items_leaf(trans, inode, path, dst_path,
 | 
				
			||||||
		src = path->nodes[0];
 | 
										     key_type, ctx);
 | 
				
			||||||
		nritems = btrfs_header_nritems(src);
 | 
							if (ret != 0) {
 | 
				
			||||||
		for (i = path->slots[0]; i < nritems; i++) {
 | 
								if (ret < 0)
 | 
				
			||||||
			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) {
 | 
					 | 
				
			||||||
				err = ret;
 | 
									err = ret;
 | 
				
			||||||
				goto done;
 | 
								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
 | 
							 * 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;
 | 
									err = ret;
 | 
				
			||||||
			goto done;
 | 
								goto done;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		btrfs_item_key_to_cpu(path->nodes[0], &tmp, path->slots[0]);
 | 
							btrfs_item_key_to_cpu(path->nodes[0], &min_key, path->slots[0]);
 | 
				
			||||||
		if (tmp.objectid != ino || tmp.type != key_type) {
 | 
							if (min_key.objectid != ino || min_key.type != key_type) {
 | 
				
			||||||
			last_offset = (u64)-1;
 | 
								last_offset = (u64)-1;
 | 
				
			||||||
			goto done;
 | 
								goto done;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if (btrfs_header_generation(path->nodes[0]) != trans->transid) {
 | 
							if (btrfs_header_generation(path->nodes[0]) != trans->transid) {
 | 
				
			||||||
			ret = overwrite_item(trans, log, dst_path,
 | 
								ret = overwrite_item(trans, log, dst_path,
 | 
				
			||||||
					     path->nodes[0], path->slots[0],
 | 
										     path->nodes[0], path->slots[0],
 | 
				
			||||||
					     &tmp);
 | 
										     &min_key);
 | 
				
			||||||
			if (ret)
 | 
								if (ret)
 | 
				
			||||||
				err = ret;
 | 
									err = ret;
 | 
				
			||||||
			else
 | 
								else
 | 
				
			||||||
				last_offset = tmp.offset;
 | 
									last_offset = min_key.offset;
 | 
				
			||||||
			goto done;
 | 
								goto done;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if (need_resched()) {
 | 
				
			||||||
 | 
								btrfs_release_path(path);
 | 
				
			||||||
 | 
								cond_resched();
 | 
				
			||||||
 | 
								goto search;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
done:
 | 
					done:
 | 
				
			||||||
	btrfs_release_path(path);
 | 
						btrfs_release_path(path);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue