Commit 33336c18 authored by Boris Burkov's avatar Boris Burkov Committed by David Sterba
Browse files

btrfs: preallocate ulist memory for qgroup rsv



When qgroups are enabled, during data reservation, we allocate the
ulist_nodes that track the exact reserved extents with GFP_ATOMIC
unconditionally. This is unnecessary, and we can follow the model
already employed by the struct extent_state we preallocate in the non
qgroups case, which should reduce the risk of allocation failures with
GFP_ATOMIC.

Add a prealloc node to struct ulist which ulist_add will grab when it is
present, and try to allocate it before taking the tree lock while we can
still take advantage of a less strict gfp mask. The lifetime of that
node belongs to the new prealloc field, until it is used, at which point
it belongs to the ulist linked list.

Reviewed-by: default avatarQu Wenruo <wqu@suse.com>
Reviewed-by: default avatarFilipe Manana <fdmanana@suse.com>
Signed-off-by: default avatarBoris Burkov <boris@bur.io>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 28cb13f2
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
#include <trace/events/btrfs.h>
#include "messages.h"
#include "ctree.h"
#include "extent_io.h"
#include "extent-io-tree.h"
#include "btrfs_inode.h"

@@ -1084,6 +1085,9 @@ static int __set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
		 */
		prealloc = alloc_extent_state(mask);
	}
	/* Optimistically preallocate the extent changeset ulist node. */
	if (changeset)
		extent_changeset_prealloc(changeset, mask);

	spin_lock(&tree->lock);
	if (cached_state && *cached_state) {
+5 −0
Original line number Diff line number Diff line
@@ -215,6 +215,11 @@ static inline struct extent_changeset *extent_changeset_alloc(void)
	return ret;
}

static inline void extent_changeset_prealloc(struct extent_changeset *changeset, gfp_t gfp_mask)
{
	ulist_prealloc(&changeset->range_changed, gfp_mask);
}

static inline void extent_changeset_release(struct extent_changeset *changeset)
{
	if (!changeset)
+18 −3
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ void ulist_init(struct ulist *ulist)
	INIT_LIST_HEAD(&ulist->nodes);
	ulist->root = RB_ROOT;
	ulist->nnodes = 0;
	ulist->prealloc = NULL;
}

/*
@@ -68,6 +69,8 @@ void ulist_release(struct ulist *ulist)
	list_for_each_entry_safe(node, next, &ulist->nodes, list) {
		kfree(node);
	}
	kfree(ulist->prealloc);
	ulist->prealloc = NULL;
	ulist->root = RB_ROOT;
	INIT_LIST_HEAD(&ulist->nodes);
}
@@ -105,6 +108,12 @@ struct ulist *ulist_alloc(gfp_t gfp_mask)
	return ulist;
}

void ulist_prealloc(struct ulist *ulist, gfp_t gfp_mask)
{
	if (!ulist->prealloc)
		ulist->prealloc = kzalloc(sizeof(*ulist->prealloc), gfp_mask);
}

/*
 * Free dynamically allocated ulist.
 *
@@ -206,9 +215,15 @@ int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux,
			*old_aux = node->aux;
		return 0;
	}

	if (ulist->prealloc) {
		node = ulist->prealloc;
		ulist->prealloc = NULL;
	} else {
		node = kmalloc(sizeof(*node), gfp_mask);
		if (!node)
			return -ENOMEM;
	}

	node->val = val;
	node->aux = aux;
+2 −0
Original line number Diff line number Diff line
@@ -41,12 +41,14 @@ struct ulist {

	struct list_head nodes;
	struct rb_root root;
	struct ulist_node *prealloc;
};

void ulist_init(struct ulist *ulist);
void ulist_release(struct ulist *ulist);
void ulist_reinit(struct ulist *ulist);
struct ulist *ulist_alloc(gfp_t gfp_mask);
void ulist_prealloc(struct ulist *ulist, gfp_t mask);
void ulist_free(struct ulist *ulist);
int ulist_add(struct ulist *ulist, u64 val, u64 aux, gfp_t gfp_mask);
int ulist_add_merge(struct ulist *ulist, u64 val, u64 aux,