From: Yuan Chen <[email protected]>
range_tree_set() pre-allocates the node needed for a brand-new range
before calling range_tree_clear(), so an allocation failure returns
-ENOMEM without having modified the tree (previously the overlapping
nodes were already removed by range_tree_clear() before the allocation
was attempted, permanently losing the cleared sub-ranges).
Signed-off-by: Yuan Chen <[email protected]>
---
kernel/bpf/range_tree.c | 45 +++++++++++++++++++++++++++++++----------
1 file changed, 34 insertions(+), 11 deletions(-)
diff --git a/kernel/bpf/range_tree.c b/kernel/bpf/range_tree.c
index 15b588377a76..54055b1fe541 100644
--- a/kernel/bpf/range_tree.c
+++ b/kernel/bpf/range_tree.c
@@ -199,6 +199,7 @@ int is_range_tree_set(struct range_tree *rt, u32 start, u32 len)
int range_tree_set(struct range_tree *rt, u32 start, u32 len)
{
u32 last = start + len - 1;
+ struct range_node *new_rn = NULL;
struct range_node *right;
struct range_node *left;
int err;
@@ -208,20 +209,40 @@ int range_tree_set(struct range_tree *rt, u32 start, u32 len)
if (left && left->rn_start <= start && left->rn_last >= last)
return 0;
+ /*
+ * A new node is needed only when the range has no adjacent free
+ * range on either side. This is known before clearing: any range
+ * covering start - 1 or last + 1 survives the clear as an adjacent
+ * piece. Allocate only in that case, before modifying the tree, so
+ * a failure leaves the range tree unmodified
+ */
+ left = range_it_iter_first(rt, start - 1, start - 1);
+ right = range_it_iter_first(rt, last + 1, last + 1);
+ if (!left && !right) {
+ new_rn = kmalloc_nolock(sizeof(struct range_node),
+ __GFP_ACCOUNT, NUMA_NO_NODE);
+ if (!new_rn)
+ return -ENOMEM;
+ }
+
/* Clear out everything in the range we want to set. */
err = range_tree_clear(rt, start, len);
if (err)
- return err;
+ goto out_free_new;
/* Do we have a left-adjacent range ? */
left = range_it_iter_first(rt, start - 1, start - 1);
- if (left && left->rn_last + 1 != start)
- return -EFAULT;
+ if (left && left->rn_last + 1 != start) {
+ err = -EFAULT;
+ goto out_free_new;
+ }
/* Do we have a right-adjacent range ? */
right = range_it_iter_first(rt, last + 1, last + 1);
- if (right && right->rn_start != last + 1)
- return -EFAULT;
+ if (right && right->rn_start != last + 1) {
+ err = -EFAULT;
+ goto out_free_new;
+ }
if (left && right) {
/* Combine left and right adjacent ranges */
@@ -241,14 +262,16 @@ int range_tree_set(struct range_tree *rt, u32 start, u32 len)
right->rn_start = start;
range_it_insert(right, rt);
} else {
- left = kmalloc_nolock(sizeof(struct range_node), __GFP_ACCOUNT, NUMA_NO_NODE);
- if (!left)
- return -ENOMEM;
- left->rn_start = start;
- left->rn_last = last;
- range_it_insert(left, rt);
+ /* No adjacent ranges; use the pre-allocated node */
+ new_rn->rn_start = start;
+ new_rn->rn_last = last;
+ range_it_insert(new_rn, rt);
}
return 0;
+
+out_free_new:
+ kfree_nolock(new_rn);
+ return err;
}
void range_tree_destroy(struct range_tree *rt)
--
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.