Skip to content

Commit e6a9e59

Browse files
Hou TaoSasha Levin
authored andcommitted
bpf: Handle in-place update for full LPM trie correctly
[ Upstream commit 532d6b3 ] When a LPM trie is full, in-place updates of existing elements incorrectly return -ENOSPC. Fix this by deferring the check of trie->n_entries. For new insertions, n_entries must not exceed max_entries. However, in-place updates are allowed even when the trie is full. Fixes: b95a5c4 ("bpf: add a longest prefix match trie map implementation") Reviewed-by: Toke Høiland-Jørgensen <[email protected]> Signed-off-by: Hou Tao <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]> Signed-off-by: Sasha Levin <[email protected]>
1 parent 615085a commit e6a9e59

File tree

1 file changed

+21
-23
lines changed

1 file changed

+21
-23
lines changed

kernel/bpf/lpm_trie.c

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,16 @@ static struct lpm_trie_node *lpm_trie_node_alloc(const struct lpm_trie *trie,
310310
return node;
311311
}
312312

313+
static int trie_check_add_elem(struct lpm_trie *trie, u64 flags)
314+
{
315+
if (flags == BPF_EXIST)
316+
return -ENOENT;
317+
if (trie->n_entries == trie->map.max_entries)
318+
return -ENOSPC;
319+
trie->n_entries++;
320+
return 0;
321+
}
322+
313323
/* Called from syscall or from eBPF program */
314324
static long trie_update_elem(struct bpf_map *map,
315325
void *_key, void *value, u64 flags)
@@ -333,20 +343,12 @@ static long trie_update_elem(struct bpf_map *map,
333343
spin_lock_irqsave(&trie->lock, irq_flags);
334344

335345
/* Allocate and fill a new node */
336-
337-
if (trie->n_entries == trie->map.max_entries) {
338-
ret = -ENOSPC;
339-
goto out;
340-
}
341-
342346
new_node = lpm_trie_node_alloc(trie, value);
343347
if (!new_node) {
344348
ret = -ENOMEM;
345349
goto out;
346350
}
347351

348-
trie->n_entries++;
349-
350352
new_node->prefixlen = key->prefixlen;
351353
RCU_INIT_POINTER(new_node->child[0], NULL);
352354
RCU_INIT_POINTER(new_node->child[1], NULL);
@@ -376,10 +378,10 @@ static long trie_update_elem(struct bpf_map *map,
376378
* simply assign the @new_node to that slot and be done.
377379
*/
378380
if (!node) {
379-
if (flags == BPF_EXIST) {
380-
ret = -ENOENT;
381+
ret = trie_check_add_elem(trie, flags);
382+
if (ret)
381383
goto out;
382-
}
384+
383385
rcu_assign_pointer(*slot, new_node);
384386
goto out;
385387
}
@@ -393,10 +395,10 @@ static long trie_update_elem(struct bpf_map *map,
393395
ret = -EEXIST;
394396
goto out;
395397
}
396-
trie->n_entries--;
397-
} else if (flags == BPF_EXIST) {
398-
ret = -ENOENT;
399-
goto out;
398+
} else {
399+
ret = trie_check_add_elem(trie, flags);
400+
if (ret)
401+
goto out;
400402
}
401403

402404
new_node->child[0] = node->child[0];
@@ -408,10 +410,9 @@ static long trie_update_elem(struct bpf_map *map,
408410
goto out;
409411
}
410412

411-
if (flags == BPF_EXIST) {
412-
ret = -ENOENT;
413+
ret = trie_check_add_elem(trie, flags);
414+
if (ret)
413415
goto out;
414-
}
415416

416417
/* If the new node matches the prefix completely, it must be inserted
417418
* as an ancestor. Simply insert it between @node and *@slot.
@@ -425,6 +426,7 @@ static long trie_update_elem(struct bpf_map *map,
425426

426427
im_node = lpm_trie_node_alloc(trie, NULL);
427428
if (!im_node) {
429+
trie->n_entries--;
428430
ret = -ENOMEM;
429431
goto out;
430432
}
@@ -446,12 +448,8 @@ static long trie_update_elem(struct bpf_map *map,
446448
rcu_assign_pointer(*slot, im_node);
447449

448450
out:
449-
if (ret) {
450-
if (new_node)
451-
trie->n_entries--;
451+
if (ret)
452452
kfree(new_node);
453-
}
454-
455453
spin_unlock_irqrestore(&trie->lock, irq_flags);
456454
kfree_rcu(free_node, rcu);
457455

0 commit comments

Comments
 (0)