Skip to content

Commit 6908665

Browse files
kaberummakynes
authored andcommitted
netfilter: nf_tables: add GC synchronization helpers
GC is expected to happen asynchrously to the netlink interface. In the netlink path, both insertion and removal of elements consist of two steps, insertion followed by activation or deactivation followed by removal, during which the element must not be freed by GC. The synchronization helpers use an unused bit in the genmask field to atomically mark an element as "busy", meaning it is either currently being handled through the netlink API or by GC. Elements being processed by GC will never survive, netlink will simply ignore them. Elements being currently processed through netlink will be skipped by GC and reprocessed during the next run. Signed-off-by: Patrick McHardy <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent cfed7e1 commit 6908665

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

include/net/netfilter/nf_tables.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,41 @@ static inline void nft_set_elem_change_active(const struct nft_set *set,
852852
ext->genmask ^= nft_genmask_next(read_pnet(&set->pnet));
853853
}
854854

855+
/*
856+
* We use a free bit in the genmask field to indicate the element
857+
* is busy, meaning it is currently being processed either by
858+
* the netlink API or GC.
859+
*
860+
* Even though the genmask is only a single byte wide, this works
861+
* because the extension structure if fully constant once initialized,
862+
* so there are no non-atomic write accesses unless it is already
863+
* marked busy.
864+
*/
865+
#define NFT_SET_ELEM_BUSY_MASK (1 << 2)
866+
867+
#if defined(__LITTLE_ENDIAN_BITFIELD)
868+
#define NFT_SET_ELEM_BUSY_BIT 2
869+
#elif defined(__BIG_ENDIAN_BITFIELD)
870+
#define NFT_SET_ELEM_BUSY_BIT (BITS_PER_LONG - BITS_PER_BYTE + 2)
871+
#else
872+
#error
873+
#endif
874+
875+
static inline int nft_set_elem_mark_busy(struct nft_set_ext *ext)
876+
{
877+
unsigned long *word = (unsigned long *)ext;
878+
879+
BUILD_BUG_ON(offsetof(struct nft_set_ext, genmask) != 0);
880+
return test_and_set_bit(NFT_SET_ELEM_BUSY_BIT, word);
881+
}
882+
883+
static inline void nft_set_elem_clear_busy(struct nft_set_ext *ext)
884+
{
885+
unsigned long *word = (unsigned long *)ext;
886+
887+
clear_bit(NFT_SET_ELEM_BUSY_BIT, word);
888+
}
889+
855890
/**
856891
* struct nft_trans - nf_tables object update in transaction
857892
*

net/netfilter/nf_tables_api.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3338,7 +3338,7 @@ static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
33383338
if (trans == NULL)
33393339
goto err4;
33403340

3341-
ext->genmask = nft_genmask_cur(ctx->net);
3341+
ext->genmask = nft_genmask_cur(ctx->net) | NFT_SET_ELEM_BUSY_MASK;
33423342
err = set->ops->insert(set, &elem);
33433343
if (err < 0)
33443344
goto err5;

0 commit comments

Comments
 (0)