Skip to content

Commit 9f439bd

Browse files
author
Florian Westphal
committed
netfilter: nft_set_pipapo: speed up bulk element insertions
Insertions into the set are slow when we try to add many elements. For 800k elements I get: time nft -f pipapo_800k real 19m34.849s user 0m2.390s sys 19m12.828s perf stats: --95.39%--nft_pipapo_insert |--76.60%--pipapo_insert | --76.37%--pipapo_resize | |--72.87%--memcpy_orig | |--1.88%--__free_pages_ok | | --0.89%--free_tail_page_prepare | --1.38%--kvmalloc_node .. --18.56%--pipapo_get.isra.0 |--13.91%--__bitmap_and |--3.01%--pipapo_refill |--0.81%--__kmalloc | --0.74%--__kmalloc_large_node | --0.66%--__alloc_pages .. --0.52%--memset_orig So lots of time is spent in copying exising elements to make space for the next one. Instead of allocating to the exact size of the new rule count, allocate extra slack to reduce alloc/copy/free overhead. After: time nft -f pipapo_800k real 1m54.110s user 0m2.515s sys 1m51.377s --80.46%--nft_pipapo_insert |--73.45%--pipapo_get.isra.0 |--57.63%--__bitmap_and | |--8.52%--pipapo_refill |--3.45%--__kmalloc | --3.05%--__kmalloc_large_node | --2.58%--__alloc_pages --2.59%--memset_orig |--6.51%--pipapo_insert --5.96%--pipapo_resize |--3.63%--memcpy_orig --2.13%--kvmalloc_node The new @rules_alloc fills a hole, so struct size doesn't go up. Also make it so rule removal doesn't shrink unless the free/extra space exceeds two pages. This should be safe as well: When a rule gets removed, the attempt to lower the allocated size is already allowed to fail. Exception: do exact allocations as long as set is very small (less than one page needed). v2: address comments from Stefano: kdoc comment formatting changes remove redundant assignment switch back to PAGE_SIZE Link: https://lore.kernel.org/netfilter-devel/20240213141753.17ef27a6@elisabeth/ Reviewed-by: Stefano Brivio <[email protected]> Signed-off-by: Florian Westphal <[email protected]>
1 parent aac14d5 commit 9f439bd

File tree

2 files changed

+69
-16
lines changed

2 files changed

+69
-16
lines changed

net/netfilter/nft_set_pipapo.c

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,65 @@ nft_pipapo_get(const struct net *net, const struct nft_set *set,
621621
return &e->priv;
622622
}
623623

624+
/**
625+
* pipapo_realloc_mt() - Reallocate mapping table if needed upon resize
626+
* @f: Field containing mapping table
627+
* @old_rules: Amount of existing mapped rules
628+
* @rules: Amount of new rules to map
629+
*
630+
* Return: 0 on success, negative error code on failure.
631+
*/
632+
static int pipapo_realloc_mt(struct nft_pipapo_field *f,
633+
unsigned int old_rules, unsigned int rules)
634+
{
635+
union nft_pipapo_map_bucket *new_mt = NULL, *old_mt = f->mt;
636+
const unsigned int extra = PAGE_SIZE / sizeof(*new_mt);
637+
unsigned int rules_alloc = rules;
638+
639+
might_sleep();
640+
641+
if (unlikely(rules == 0))
642+
goto out_free;
643+
644+
/* growing and enough space left, no action needed */
645+
if (rules > old_rules && f->rules_alloc > rules)
646+
return 0;
647+
648+
/* downsize and extra slack has not grown too large */
649+
if (rules < old_rules) {
650+
unsigned int remove = f->rules_alloc - rules;
651+
652+
if (remove < (2u * extra))
653+
return 0;
654+
}
655+
656+
/* If set needs more than one page of memory for rules then
657+
* allocate another extra page to avoid frequent reallocation.
658+
*/
659+
if (rules > extra &&
660+
check_add_overflow(rules, extra, &rules_alloc))
661+
return -EOVERFLOW;
662+
663+
new_mt = kvmalloc_array(rules_alloc, sizeof(*new_mt), GFP_KERNEL);
664+
if (!new_mt)
665+
return -ENOMEM;
666+
667+
if (old_mt)
668+
memcpy(new_mt, old_mt, min(old_rules, rules) * sizeof(*new_mt));
669+
670+
if (rules > old_rules) {
671+
memset(new_mt + old_rules, 0,
672+
(rules - old_rules) * sizeof(*new_mt));
673+
}
674+
out_free:
675+
f->rules_alloc = rules_alloc;
676+
f->mt = new_mt;
677+
678+
kvfree(old_mt);
679+
680+
return 0;
681+
}
682+
624683
/**
625684
* pipapo_resize() - Resize lookup or mapping table, or both
626685
* @f: Field containing lookup and mapping tables
@@ -637,9 +696,8 @@ static int pipapo_resize(struct nft_pipapo_field *f,
637696
unsigned int old_rules, unsigned int rules)
638697
{
639698
long *new_lt = NULL, *new_p, *old_lt = f->lt, *old_p;
640-
union nft_pipapo_map_bucket *new_mt, *old_mt = f->mt;
641699
unsigned int new_bucket_size, copy;
642-
int group, bucket;
700+
int group, bucket, err;
643701

644702
if (rules >= NFT_PIPAPO_RULE0_MAX)
645703
return -ENOSPC;
@@ -682,16 +740,10 @@ static int pipapo_resize(struct nft_pipapo_field *f,
682740
}
683741

684742
mt:
685-
new_mt = kvmalloc(rules * sizeof(*new_mt), GFP_KERNEL);
686-
if (!new_mt) {
743+
err = pipapo_realloc_mt(f, old_rules, rules);
744+
if (err) {
687745
kvfree(new_lt);
688-
return -ENOMEM;
689-
}
690-
691-
memcpy(new_mt, f->mt, min(old_rules, rules) * sizeof(*new_mt));
692-
if (rules > old_rules) {
693-
memset(new_mt + old_rules, 0,
694-
(rules - old_rules) * sizeof(*new_mt));
746+
return err;
695747
}
696748

697749
if (new_lt) {
@@ -700,9 +752,6 @@ static int pipapo_resize(struct nft_pipapo_field *f,
700752
kvfree(old_lt);
701753
}
702754

703-
f->mt = new_mt;
704-
kvfree(old_mt);
705-
706755
return 0;
707756
}
708757

@@ -1382,14 +1431,15 @@ static struct nft_pipapo_match *pipapo_clone(struct nft_pipapo_match *old)
13821431
src->groups * NFT_PIPAPO_BUCKETS(src->bb));
13831432

13841433
if (src->rules > 0) {
1385-
dst->mt = kvmalloc_array(src->rules, sizeof(*src->mt),
1386-
GFP_KERNEL);
1434+
dst->mt = kvmalloc_array(src->rules_alloc,
1435+
sizeof(*src->mt), GFP_KERNEL);
13871436
if (!dst->mt)
13881437
goto out_mt;
13891438

13901439
memcpy(dst->mt, src->mt, src->rules * sizeof(*src->mt));
13911440
} else {
13921441
dst->mt = NULL;
1442+
dst->rules_alloc = 0;
13931443
}
13941444

13951445
src++;
@@ -2205,6 +2255,7 @@ static int nft_pipapo_init(const struct nft_set *set,
22052255

22062256
f->bsize = 0;
22072257
f->rules = 0;
2258+
f->rules_alloc = 0;
22082259
f->lt = NULL;
22092260
f->mt = NULL;
22102261
}

net/netfilter/nft_set_pipapo.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ union nft_pipapo_map_bucket {
106106
* struct nft_pipapo_field - Lookup, mapping tables and related data for a field
107107
* @rules: Number of inserted rules
108108
* @bsize: Size of each bucket in lookup table, in longs
109+
* @rules_alloc: Number of allocated rules, always >= rules
109110
* @groups: Amount of bit groups
110111
* @bb: Number of bits grouped together in lookup table buckets
111112
* @lt: Lookup table: 'groups' rows of buckets
@@ -114,6 +115,7 @@ union nft_pipapo_map_bucket {
114115
struct nft_pipapo_field {
115116
unsigned int rules;
116117
unsigned int bsize;
118+
unsigned int rules_alloc;
117119
u8 groups;
118120
u8 bb;
119121
unsigned long *lt;

0 commit comments

Comments
 (0)