Skip to content

Commit d8d871a

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: nft_set_pipapo: merge pipapo_get/lookup
The matching algorithm has implemented thrice: 1. data path lookup, generic version 2. data path lookup, avx2 version 3. control plane lookup Merge 1 and 3 by refactoring pipapo_get as a common helper, then make nft_pipapo_lookup and nft_pipapo_get both call the common helper. Aside from the code savings this has the benefit that we no longer allocate temporary scratch maps for each control plane get and insertion operation. Signed-off-by: Florian Westphal <fw@strlen.de> Reviewed-by: Stefano Brivio <sbrivio@redhat.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent 531e613 commit d8d871a

File tree

1 file changed

+58
-130
lines changed

1 file changed

+58
-130
lines changed

net/netfilter/nft_set_pipapo.c

Lines changed: 58 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -397,35 +397,36 @@ int pipapo_refill(unsigned long *map, unsigned int len, unsigned int rules,
397397
}
398398

399399
/**
400-
* nft_pipapo_lookup() - Lookup function
401-
* @net: Network namespace
402-
* @set: nftables API set representation
403-
* @key: nftables API element representation containing key data
404-
* @ext: nftables API extension pointer, filled with matching reference
400+
* pipapo_get() - Get matching element reference given key data
401+
* @m: storage containing the set elements
402+
* @data: Key data to be matched against existing elements
403+
* @genmask: If set, check that element is active in given genmask
404+
* @tstamp: timestamp to check for expired elements
405405
*
406406
* For more details, see DOC: Theory of Operation.
407407
*
408-
* Return: true on match, false otherwise.
408+
* This is the main lookup function. It matches key data against either
409+
* the working match set or the uncommitted copy, depending on what the
410+
* caller passed to us.
411+
* nft_pipapo_get (lookup from userspace/control plane) and nft_pipapo_lookup
412+
* (datapath lookup) pass the active copy.
413+
* The insertion path will pass the uncommitted working copy.
414+
*
415+
* Return: pointer to &struct nft_pipapo_elem on match, NULL otherwise.
409416
*/
410-
const struct nft_set_ext *
411-
nft_pipapo_lookup(const struct net *net, const struct nft_set *set,
412-
const u32 *key)
417+
static struct nft_pipapo_elem *pipapo_get(const struct nft_pipapo_match *m,
418+
const u8 *data, u8 genmask,
419+
u64 tstamp)
413420
{
414-
struct nft_pipapo *priv = nft_set_priv(set);
415421
struct nft_pipapo_scratch *scratch;
416422
unsigned long *res_map, *fill_map;
417-
u8 genmask = nft_genmask_cur(net);
418-
const struct nft_pipapo_match *m;
419423
const struct nft_pipapo_field *f;
420-
const u8 *rp = (const u8 *)key;
421424
bool map_index;
422425
int i;
423426

424427
local_bh_disable();
425428

426-
m = rcu_dereference(priv->match);
427-
428-
if (unlikely(!m || !*raw_cpu_ptr(m->scratch)))
429+
if (unlikely(!raw_cpu_ptr(m->scratch)))
429430
goto out;
430431

431432
scratch = *raw_cpu_ptr(m->scratch);
@@ -445,12 +446,12 @@ nft_pipapo_lookup(const struct net *net, const struct nft_set *set,
445446
* packet bytes value, then AND bucket value
446447
*/
447448
if (likely(f->bb == 8))
448-
pipapo_and_field_buckets_8bit(f, res_map, rp);
449+
pipapo_and_field_buckets_8bit(f, res_map, data);
449450
else
450-
pipapo_and_field_buckets_4bit(f, res_map, rp);
451+
pipapo_and_field_buckets_4bit(f, res_map, data);
451452
NFT_PIPAPO_GROUP_BITS_ARE_8_OR_4;
452453

453-
rp += f->groups / NFT_PIPAPO_GROUPS_PER_BYTE(f);
454+
data += f->groups / NFT_PIPAPO_GROUPS_PER_BYTE(f);
454455

455456
/* Now populate the bitmap for the next field, unless this is
456457
* the last field, in which case return the matched 'ext'
@@ -470,11 +471,11 @@ nft_pipapo_lookup(const struct net *net, const struct nft_set *set,
470471
}
471472

472473
if (last) {
473-
const struct nft_set_ext *ext;
474+
struct nft_pipapo_elem *e;
474475

475-
ext = &f->mt[b].e->ext;
476-
if (unlikely(nft_set_elem_expired(ext) ||
477-
!nft_set_elem_active(ext, genmask)))
476+
e = f->mt[b].e;
477+
if (unlikely(__nft_set_elem_expired(&e->ext, tstamp) ||
478+
!nft_set_elem_active(&e->ext, genmask)))
478479
goto next_match;
479480

480481
/* Last field: we're just returning the key without
@@ -484,8 +485,7 @@ nft_pipapo_lookup(const struct net *net, const struct nft_set *set,
484485
*/
485486
scratch->map_index = map_index;
486487
local_bh_enable();
487-
488-
return ext;
488+
return e;
489489
}
490490

491491
/* Swap bitmap indices: res_map is the initial bitmap for the
@@ -495,7 +495,7 @@ nft_pipapo_lookup(const struct net *net, const struct nft_set *set,
495495
map_index = !map_index;
496496
swap(res_map, fill_map);
497497

498-
rp += NFT_PIPAPO_GROUPS_PADDING(f);
498+
data += NFT_PIPAPO_GROUPS_PADDING(f);
499499
}
500500

501501
out:
@@ -504,99 +504,29 @@ nft_pipapo_lookup(const struct net *net, const struct nft_set *set,
504504
}
505505

506506
/**
507-
* pipapo_get() - Get matching element reference given key data
508-
* @m: storage containing active/existing elements
509-
* @data: Key data to be matched against existing elements
510-
* @genmask: If set, check that element is active in given genmask
511-
* @tstamp: timestamp to check for expired elements
512-
* @gfp: the type of memory to allocate (see kmalloc).
507+
* nft_pipapo_lookup() - Dataplane fronted for main lookup function
508+
* @net: Network namespace
509+
* @set: nftables API set representation
510+
* @key: pointer to nft registers containing key data
513511
*
514-
* This is essentially the same as the lookup function, except that it matches
515-
* key data against the uncommitted copy and doesn't use preallocated maps for
516-
* bitmap results.
512+
* This function is called from the data path. It will search for
513+
* an element matching the given key in the current active copy.
517514
*
518-
* Return: pointer to &struct nft_pipapo_elem on match, error pointer otherwise.
515+
* Return: ntables API extension pointer or NULL if no match.
519516
*/
520-
static struct nft_pipapo_elem *pipapo_get(const struct nft_pipapo_match *m,
521-
const u8 *data, u8 genmask,
522-
u64 tstamp, gfp_t gfp)
517+
const struct nft_set_ext *
518+
nft_pipapo_lookup(const struct net *net, const struct nft_set *set,
519+
const u32 *key)
523520
{
524-
struct nft_pipapo_elem *ret = ERR_PTR(-ENOENT);
525-
unsigned long *res_map, *fill_map = NULL;
526-
const struct nft_pipapo_field *f;
527-
int i;
528-
529-
if (m->bsize_max == 0)
530-
return ret;
531-
532-
res_map = kmalloc_array(m->bsize_max, sizeof(*res_map), gfp);
533-
if (!res_map) {
534-
ret = ERR_PTR(-ENOMEM);
535-
goto out;
536-
}
537-
538-
fill_map = kcalloc(m->bsize_max, sizeof(*res_map), gfp);
539-
if (!fill_map) {
540-
ret = ERR_PTR(-ENOMEM);
541-
goto out;
542-
}
543-
544-
pipapo_resmap_init(m, res_map);
545-
546-
nft_pipapo_for_each_field(f, i, m) {
547-
bool last = i == m->field_count - 1;
548-
int b;
549-
550-
/* For each bit group: select lookup table bucket depending on
551-
* packet bytes value, then AND bucket value
552-
*/
553-
if (f->bb == 8)
554-
pipapo_and_field_buckets_8bit(f, res_map, data);
555-
else if (f->bb == 4)
556-
pipapo_and_field_buckets_4bit(f, res_map, data);
557-
else
558-
BUG();
559-
560-
data += f->groups / NFT_PIPAPO_GROUPS_PER_BYTE(f);
561-
562-
/* Now populate the bitmap for the next field, unless this is
563-
* the last field, in which case return the matched 'ext'
564-
* pointer if any.
565-
*
566-
* Now res_map contains the matching bitmap, and fill_map is the
567-
* bitmap for the next field.
568-
*/
569-
next_match:
570-
b = pipapo_refill(res_map, f->bsize, f->rules, fill_map, f->mt,
571-
last);
572-
if (b < 0)
573-
goto out;
574-
575-
if (last) {
576-
if (__nft_set_elem_expired(&f->mt[b].e->ext, tstamp))
577-
goto next_match;
578-
if ((genmask &&
579-
!nft_set_elem_active(&f->mt[b].e->ext, genmask)))
580-
goto next_match;
581-
582-
ret = f->mt[b].e;
583-
goto out;
584-
}
585-
586-
data += NFT_PIPAPO_GROUPS_PADDING(f);
521+
struct nft_pipapo *priv = nft_set_priv(set);
522+
u8 genmask = nft_genmask_cur(net);
523+
const struct nft_pipapo_match *m;
524+
const struct nft_pipapo_elem *e;
587525

588-
/* Swap bitmap indices: fill_map will be the initial bitmap for
589-
* the next field (i.e. the new res_map), and res_map is
590-
* guaranteed to be all-zeroes at this point, ready to be filled
591-
* according to the next mapping table.
592-
*/
593-
swap(res_map, fill_map);
594-
}
526+
m = rcu_dereference(priv->match);
527+
e = pipapo_get(m, (const u8 *)key, genmask, get_jiffies_64());
595528

596-
out:
597-
kfree(fill_map);
598-
kfree(res_map);
599-
return ret;
529+
return e ? &e->ext : NULL;
600530
}
601531

602532
/**
@@ -605,6 +535,11 @@ static struct nft_pipapo_elem *pipapo_get(const struct nft_pipapo_match *m,
605535
* @set: nftables API set representation
606536
* @elem: nftables API element representation containing key data
607537
* @flags: Unused
538+
*
539+
* This function is called from the control plane path under
540+
* RCU read lock.
541+
*
542+
* Return: set element private pointer or ERR_PTR(-ENOENT).
608543
*/
609544
static struct nft_elem_priv *
610545
nft_pipapo_get(const struct net *net, const struct nft_set *set,
@@ -615,10 +550,9 @@ nft_pipapo_get(const struct net *net, const struct nft_set *set,
615550
struct nft_pipapo_elem *e;
616551

617552
e = pipapo_get(m, (const u8 *)elem->key.val.data,
618-
nft_genmask_cur(net), get_jiffies_64(),
619-
GFP_ATOMIC);
620-
if (IS_ERR(e))
621-
return ERR_CAST(e);
553+
nft_genmask_cur(net), get_jiffies_64());
554+
if (!e)
555+
return ERR_PTR(-ENOENT);
622556

623557
return &e->priv;
624558
}
@@ -1344,8 +1278,8 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,
13441278
else
13451279
end = start;
13461280

1347-
dup = pipapo_get(m, start, genmask, tstamp, GFP_KERNEL);
1348-
if (!IS_ERR(dup)) {
1281+
dup = pipapo_get(m, start, genmask, tstamp);
1282+
if (dup) {
13491283
/* Check if we already have the same exact entry */
13501284
const struct nft_data *dup_key, *dup_end;
13511285

@@ -1364,15 +1298,9 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,
13641298
return -ENOTEMPTY;
13651299
}
13661300

1367-
if (PTR_ERR(dup) == -ENOENT) {
1368-
/* Look for partially overlapping entries */
1369-
dup = pipapo_get(m, end, nft_genmask_next(net), tstamp,
1370-
GFP_KERNEL);
1371-
}
1372-
1373-
if (PTR_ERR(dup) != -ENOENT) {
1374-
if (IS_ERR(dup))
1375-
return PTR_ERR(dup);
1301+
/* Look for partially overlapping entries */
1302+
dup = pipapo_get(m, end, nft_genmask_next(net), tstamp);
1303+
if (dup) {
13761304
*elem_priv = &dup->priv;
13771305
return -ENOTEMPTY;
13781306
}
@@ -1914,8 +1842,8 @@ nft_pipapo_deactivate(const struct net *net, const struct nft_set *set,
19141842
return NULL;
19151843

19161844
e = pipapo_get(m, (const u8 *)elem->key.val.data,
1917-
nft_genmask_next(net), nft_net_tstamp(net), GFP_KERNEL);
1918-
if (IS_ERR(e))
1845+
nft_genmask_next(net), nft_net_tstamp(net));
1846+
if (!e)
19191847
return NULL;
19201848

19211849
nft_set_elem_change_active(net, set, &e->ext);

0 commit comments

Comments
 (0)