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: Qu Wenruo <wqu@suse.com> Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Boris Burkov <boris@bur.io> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
28cb13f29f
commit
33336c1805
@ -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 @@ again:
|
||||
*/
|
||||
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) {
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
node = kmalloc(sizeof(*node), gfp_mask);
|
||||
if (!node)
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user