Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4a500fd1 authored by Yan, Zheng's avatar Yan, Zheng Committed by Chris Mason
Browse files

Btrfs: Metadata ENOSPC handling for tree log



Previous patches make the allocater return -ENOSPC if there is no
unreserved free metadata space. This patch updates tree log code
and various other places to propagate/handle the ENOSPC error.

Signed-off-by: default avatarYan Zheng <zheng.yan@oracle.com>
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent d68fc57b
Loading
Loading
Loading
Loading
+0 −36
Original line number Diff line number Diff line
@@ -972,42 +972,6 @@ static int find_and_setup_root(struct btrfs_root *tree_root,
	return 0;
}

int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
			     struct btrfs_fs_info *fs_info)
{
	struct extent_buffer *eb;
	struct btrfs_root *log_root_tree = fs_info->log_root_tree;
	u64 start = 0;
	u64 end = 0;
	int ret;

	if (!log_root_tree)
		return 0;

	while (1) {
		ret = find_first_extent_bit(&log_root_tree->dirty_log_pages,
				0, &start, &end, EXTENT_DIRTY | EXTENT_NEW);
		if (ret)
			break;

		clear_extent_bits(&log_root_tree->dirty_log_pages, start, end,
				  EXTENT_DIRTY | EXTENT_NEW, GFP_NOFS);
	}
	eb = fs_info->log_root_tree->node;

	WARN_ON(btrfs_header_level(eb) != 0);
	WARN_ON(btrfs_header_nritems(eb) != 0);

	ret = btrfs_free_reserved_extent(fs_info->tree_root,
				eb->start, eb->len);
	BUG_ON(ret);

	free_extent_buffer(eb);
	kfree(fs_info->log_root_tree);
	fs_info->log_root_tree = NULL;
	return 0;
}

static struct btrfs_root *alloc_log_tree(struct btrfs_trans_handle *trans,
					 struct btrfs_fs_info *fs_info)
{
+0 −2
Original line number Diff line number Diff line
@@ -95,8 +95,6 @@ int btrfs_congested_async(struct btrfs_fs_info *info, int iodone);
unsigned long btrfs_async_submit_limit(struct btrfs_fs_info *info);
int btrfs_write_tree_block(struct extent_buffer *buf);
int btrfs_wait_tree_block_writeback(struct extent_buffer *buf);
int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
			     struct btrfs_fs_info *fs_info);
int btrfs_init_log_root_tree(struct btrfs_trans_handle *trans,
			     struct btrfs_fs_info *fs_info);
int btrfs_add_log_tree(struct btrfs_trans_handle *trans,
+3 −0
Original line number Diff line number Diff line
@@ -657,6 +657,9 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
		goto found;
	}
	ret = PTR_ERR(item);
	if (ret != -EFBIG && ret != -ENOENT)
		goto fail_unlock;

	if (ret == -EFBIG) {
		u32 item_size;
		/* we found one, but it isn't big enough yet */
+151 −90
Original line number Diff line number Diff line
@@ -135,6 +135,7 @@ static int start_log_trans(struct btrfs_trans_handle *trans,
			   struct btrfs_root *root)
{
	int ret;
	int err = 0;

	mutex_lock(&root->log_mutex);
	if (root->log_root) {
@@ -155,17 +156,19 @@ static int start_log_trans(struct btrfs_trans_handle *trans,
	mutex_lock(&root->fs_info->tree_log_mutex);
	if (!root->fs_info->log_root_tree) {
		ret = btrfs_init_log_root_tree(trans, root->fs_info);
		BUG_ON(ret);
		if (ret)
			err = ret;
	}
	if (!root->log_root) {
	if (err == 0 && !root->log_root) {
		ret = btrfs_add_log_tree(trans, root);
		BUG_ON(ret);
		if (ret)
			err = ret;
	}
	mutex_unlock(&root->fs_info->tree_log_mutex);
	root->log_batch++;
	atomic_inc(&root->log_writers);
	mutex_unlock(&root->log_mutex);
	return 0;
	return err;
}

/*
@@ -376,7 +379,7 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans,
			BUG_ON(ret);
		}
	} else if (ret) {
		BUG();
		return ret;
	}
	dst_ptr = btrfs_item_ptr_offset(path->nodes[0],
					path->slots[0]);
@@ -1699,9 +1702,9 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,

		next = btrfs_find_create_tree_block(root, bytenr, blocksize);

		if (*level == 1) {
			wc->process_func(root, next, wc, ptr_gen);

		if (*level == 1) {
			path->slots[*level]++;
			if (wc->free) {
				btrfs_read_buffer(next, ptr_gen);
@@ -1734,35 +1737,7 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,
	WARN_ON(*level < 0);
	WARN_ON(*level >= BTRFS_MAX_LEVEL);

	if (path->nodes[*level] == root->node)
		parent = path->nodes[*level];
	else
		parent = path->nodes[*level + 1];

	bytenr = path->nodes[*level]->start;

	blocksize = btrfs_level_size(root, *level);
	root_owner = btrfs_header_owner(parent);
	root_gen = btrfs_header_generation(parent);

	wc->process_func(root, path->nodes[*level], wc,
			 btrfs_header_generation(path->nodes[*level]));

	if (wc->free) {
		next = path->nodes[*level];
		btrfs_tree_lock(next);
		clean_tree_block(trans, root, next);
		btrfs_set_lock_blocking(next);
		btrfs_wait_tree_block_writeback(next);
		btrfs_tree_unlock(next);

		WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID);
		ret = btrfs_free_reserved_extent(root, bytenr, blocksize);
		BUG_ON(ret);
	}
	free_extent_buffer(path->nodes[*level]);
	path->nodes[*level] = NULL;
	*level += 1;
	path->slots[*level] = btrfs_header_nritems(path->nodes[*level]);

	cond_resched();
	return 0;
@@ -1781,7 +1756,7 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,

	for (i = *level; i < BTRFS_MAX_LEVEL - 1 && path->nodes[i]; i++) {
		slot = path->slots[i];
		if (slot < btrfs_header_nritems(path->nodes[i]) - 1) {
		if (slot + 1 < btrfs_header_nritems(path->nodes[i])) {
			struct extent_buffer *node;
			node = path->nodes[i];
			path->slots[i]++;
@@ -2047,7 +2022,6 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
	mutex_unlock(&log_root_tree->log_mutex);

	ret = update_log_root(trans, log);
	BUG_ON(ret);

	mutex_lock(&log_root_tree->log_mutex);
	if (atomic_dec_and_test(&log_root_tree->log_writers)) {
@@ -2056,6 +2030,15 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
			wake_up(&log_root_tree->log_writer_wait);
	}

	if (ret) {
		BUG_ON(ret != -ENOSPC);
		root->fs_info->last_trans_log_full_commit = trans->transid;
		btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
		mutex_unlock(&log_root_tree->log_mutex);
		ret = -EAGAIN;
		goto out;
	}

	index2 = log_root_tree->log_transid % 2;
	if (atomic_read(&log_root_tree->log_commit[index2])) {
		btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);
@@ -2129,15 +2112,10 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,
	return 0;
}

/*
 * free all the extents used by the tree log.  This should be called
 * at commit time of the full transaction
 */
int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root)
static void free_log_tree(struct btrfs_trans_handle *trans,
			  struct btrfs_root *log)
{
	int ret;
	struct btrfs_root *log;
	struct key;
	u64 start;
	u64 end;
	struct walk_control wc = {
@@ -2145,10 +2123,6 @@ int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root)
		.process_func = process_one_buffer
	};

	if (!root->log_root || root->fs_info->log_root_recovering)
		return 0;

	log = root->log_root;
	ret = walk_log_tree(trans, log, &wc);
	BUG_ON(ret);

@@ -2162,14 +2136,30 @@ int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root)
				  EXTENT_DIRTY | EXTENT_NEW, GFP_NOFS);
	}

	if (log->log_transid > 0) {
		ret = btrfs_del_root(trans, root->fs_info->log_root_tree,
				     &log->root_key);
		BUG_ON(ret);
	}
	root->log_root = NULL;
	free_extent_buffer(log->node);
	kfree(log);
}

/*
 * free all the extents used by the tree log.  This should be called
 * at commit time of the full transaction
 */
int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root)
{
	if (root->log_root) {
		free_log_tree(trans, root->log_root);
		root->log_root = NULL;
	}
	return 0;
}

int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
			     struct btrfs_fs_info *fs_info)
{
	if (fs_info->log_root_tree) {
		free_log_tree(trans, fs_info->log_root_tree);
		fs_info->log_root_tree = NULL;
	}
	return 0;
}

@@ -2203,6 +2193,7 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
	struct btrfs_dir_item *di;
	struct btrfs_path *path;
	int ret;
	int err = 0;
	int bytes_del = 0;

	if (BTRFS_I(dir)->logged_trans < trans->transid)
@@ -2218,7 +2209,11 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
	path = btrfs_alloc_path();
	di = btrfs_lookup_dir_item(trans, log, path, dir->i_ino,
				   name, name_len, -1);
	if (di && !IS_ERR(di)) {
	if (IS_ERR(di)) {
		err = PTR_ERR(di);
		goto fail;
	}
	if (di) {
		ret = btrfs_delete_one_dir_name(trans, log, path, di);
		bytes_del += name_len;
		BUG_ON(ret);
@@ -2226,7 +2221,11 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
	btrfs_release_path(log, path);
	di = btrfs_lookup_dir_index_item(trans, log, path, dir->i_ino,
					 index, name, name_len, -1);
	if (di && !IS_ERR(di)) {
	if (IS_ERR(di)) {
		err = PTR_ERR(di);
		goto fail;
	}
	if (di) {
		ret = btrfs_delete_one_dir_name(trans, log, path, di);
		bytes_del += name_len;
		BUG_ON(ret);
@@ -2244,6 +2243,10 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
		btrfs_release_path(log, path);

		ret = btrfs_search_slot(trans, log, &key, path, 0, 1);
		if (ret < 0) {
			err = ret;
			goto fail;
		}
		if (ret == 0) {
			struct btrfs_inode_item *item;
			u64 i_size;
@@ -2261,9 +2264,13 @@ int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
			ret = 0;
		btrfs_release_path(log, path);
	}

fail:
	btrfs_free_path(path);
	mutex_unlock(&BTRFS_I(dir)->log_mutex);
	if (ret == -ENOSPC) {
		root->fs_info->last_trans_log_full_commit = trans->transid;
		ret = 0;
	}
	btrfs_end_log_trans(root);

	return 0;
@@ -2291,6 +2298,10 @@ int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
	ret = btrfs_del_inode_ref(trans, log, name, name_len, inode->i_ino,
				  dirid, &index);
	mutex_unlock(&BTRFS_I(inode)->log_mutex);
	if (ret == -ENOSPC) {
		root->fs_info->last_trans_log_full_commit = trans->transid;
		ret = 0;
	}
	btrfs_end_log_trans(root);

	return ret;
@@ -2318,7 +2329,8 @@ static noinline int insert_dir_log_key(struct btrfs_trans_handle *trans,
	else
		key.type = BTRFS_DIR_LOG_INDEX_KEY;
	ret = btrfs_insert_empty_item(trans, log, path, &key, sizeof(*item));
	BUG_ON(ret);
	if (ret)
		return ret;

	item = btrfs_item_ptr(path->nodes[0], path->slots[0],
			      struct btrfs_dir_log_item);
@@ -2343,6 +2355,7 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
	struct btrfs_key max_key;
	struct btrfs_root *log = root->log_root;
	struct extent_buffer *src;
	int err = 0;
	int ret;
	int i;
	int nritems;
@@ -2405,6 +2418,10 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
			ret = overwrite_item(trans, log, dst_path,
					     path->nodes[0], path->slots[0],
					     &tmp);
			if (ret) {
				err = ret;
				goto done;
			}
		}
	}
	btrfs_release_path(root, path);
@@ -2432,7 +2449,10 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
				goto done;
			ret = overwrite_item(trans, log, dst_path, src, i,
					     &min_key);
			BUG_ON(ret);
			if (ret) {
				err = ret;
				goto done;
			}
		}
		path->slots[0] = nritems;

@@ -2454,22 +2474,30 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,
			ret = overwrite_item(trans, log, dst_path,
					     path->nodes[0], path->slots[0],
					     &tmp);

			BUG_ON(ret);
			if (ret)
				err = ret;
			else
				last_offset = tmp.offset;
			goto done;
		}
	}
done:
	*last_offset_ret = last_offset;
	btrfs_release_path(root, path);
	btrfs_release_path(log, dst_path);

	/* insert the log range keys to indicate where the log is valid */
	ret = insert_dir_log_key(trans, log, path, key_type, inode->i_ino,
				 first_offset, last_offset);
	BUG_ON(ret);
	return 0;
	if (err == 0) {
		*last_offset_ret = last_offset;
		/*
		 * insert the log range keys to indicate where the log
		 * is valid
		 */
		ret = insert_dir_log_key(trans, log, path, key_type,
					 inode->i_ino, first_offset,
					 last_offset);
		if (ret)
			err = ret;
	}
	return err;
}

/*
@@ -2501,7 +2529,8 @@ static noinline int log_directory_changes(struct btrfs_trans_handle *trans,
		ret = log_dir_items(trans, root, inode, path,
				    dst_path, key_type, min_key,
				    &max_key);
		BUG_ON(ret);
		if (ret)
			return ret;
		if (max_key == (u64)-1)
			break;
		min_key = max_key + 1;
@@ -2535,8 +2564,8 @@ static int drop_objectid_items(struct btrfs_trans_handle *trans,

	while (1) {
		ret = btrfs_search_slot(trans, log, &key, path, -1, 1);

		if (ret != 1)
		BUG_ON(ret == 0);
		if (ret < 0)
			break;

		if (path->slots[0] == 0)
@@ -2554,7 +2583,7 @@ static int drop_objectid_items(struct btrfs_trans_handle *trans,
		btrfs_release_path(log, path);
	}
	btrfs_release_path(log, path);
	return 0;
	return ret;
}

static noinline int copy_items(struct btrfs_trans_handle *trans,
@@ -2587,7 +2616,10 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
	}
	ret = btrfs_insert_empty_items(trans, log, dst_path,
				       ins_keys, ins_sizes, nr);
	BUG_ON(ret);
	if (ret) {
		kfree(ins_data);
		return ret;
	}

	for (i = 0; i < nr; i++, dst_path->slots[0]++) {
		dst_offset = btrfs_item_ptr_offset(dst_path->nodes[0],
@@ -2660,16 +2692,17 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,
	 * we have to do this after the loop above to avoid changing the
	 * log tree while trying to change the log tree.
	 */
	ret = 0;
	while (!list_empty(&ordered_sums)) {
		struct btrfs_ordered_sum *sums = list_entry(ordered_sums.next,
						   struct btrfs_ordered_sum,
						   list);
		if (!ret)
			ret = btrfs_csum_file_blocks(trans, log, sums);
		BUG_ON(ret);
		list_del(&sums->list);
		kfree(sums);
	}
	return 0;
	return ret;
}

/* log a single inode in the tree log.
@@ -2697,6 +2730,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
	struct btrfs_root *log = root->log_root;
	struct extent_buffer *src = NULL;
	u32 size;
	int err = 0;
	int ret;
	int nritems;
	int ins_start_slot = 0;
@@ -2739,7 +2773,10 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
	} else {
		ret = btrfs_truncate_inode_items(trans, log, inode, 0, 0);
	}
	BUG_ON(ret);
	if (ret) {
		err = ret;
		goto out_unlock;
	}
	path->keep_locks = 1;

	while (1) {
@@ -2768,7 +2805,10 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,

		ret = copy_items(trans, log, dst_path, src, ins_start_slot,
				 ins_nr, inode_only);
		BUG_ON(ret);
		if (ret) {
			err = ret;
			goto out_unlock;
		}
		ins_nr = 1;
		ins_start_slot = path->slots[0];
next_slot:
@@ -2784,7 +2824,10 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
			ret = copy_items(trans, log, dst_path, src,
					 ins_start_slot,
					 ins_nr, inode_only);
			BUG_ON(ret);
			if (ret) {
				err = ret;
				goto out_unlock;
			}
			ins_nr = 0;
		}
		btrfs_release_path(root, path);
@@ -2802,7 +2845,10 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
		ret = copy_items(trans, log, dst_path, src,
				 ins_start_slot,
				 ins_nr, inode_only);
		BUG_ON(ret);
		if (ret) {
			err = ret;
			goto out_unlock;
		}
		ins_nr = 0;
	}
	WARN_ON(ins_nr);
@@ -2810,14 +2856,18 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,
		btrfs_release_path(root, path);
		btrfs_release_path(log, dst_path);
		ret = log_directory_changes(trans, root, inode, path, dst_path);
		BUG_ON(ret);
		if (ret) {
			err = ret;
			goto out_unlock;
		}
	}
	BTRFS_I(inode)->logged_trans = trans->transid;
out_unlock:
	mutex_unlock(&BTRFS_I(inode)->log_mutex);

	btrfs_free_path(path);
	btrfs_free_path(dst_path);
	return 0;
	return err;
}

/*
@@ -2942,10 +2992,13 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
		goto end_no_trans;
	}

	start_log_trans(trans, root);
	ret = start_log_trans(trans, root);
	if (ret)
		goto end_trans;

	ret = btrfs_log_inode(trans, root, inode, inode_only);
	BUG_ON(ret);
	if (ret)
		goto end_trans;

	/*
	 * for regular files, if its inode is already on disk, we don't
@@ -2955,8 +3008,10 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
	 */
	if (S_ISREG(inode->i_mode) &&
	    BTRFS_I(inode)->generation <= last_committed &&
	    BTRFS_I(inode)->last_unlink_trans <= last_committed)
			goto no_parent;
	    BTRFS_I(inode)->last_unlink_trans <= last_committed) {
		ret = 0;
		goto end_trans;
	}

	inode_only = LOG_INODE_EXISTS;
	while (1) {
@@ -2970,15 +3025,21 @@ int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
		if (BTRFS_I(inode)->generation >
		    root->fs_info->last_trans_committed) {
			ret = btrfs_log_inode(trans, root, inode, inode_only);
			BUG_ON(ret);
			if (ret)
				goto end_trans;
		}
		if (IS_ROOT(parent))
			break;

		parent = parent->d_parent;
	}
no_parent:
	ret = 0;
end_trans:
	if (ret < 0) {
		BUG_ON(ret != -ENOSPC);
		root->fs_info->last_trans_log_full_commit = trans->transid;
		ret = 1;
	}
	btrfs_end_log_trans(root);
end_no_trans:
	return ret;
@@ -3020,7 +3081,7 @@ int btrfs_recover_log_trees(struct btrfs_root *log_root_tree)
	path = btrfs_alloc_path();
	BUG_ON(!path);

	trans = btrfs_start_transaction(fs_info->tree_root, 1);
	trans = btrfs_start_transaction(fs_info->tree_root, 0);

	wc.trans = trans;
	wc.pin = 1;
+2 −0
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@
int btrfs_sync_log(struct btrfs_trans_handle *trans,
		   struct btrfs_root *root);
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 btrfs_root *root, struct dentry *dentry);