Skip to content

Commit 3f1d886

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: nft_set_pipapo: move cloning of match info to insert/removal path
This set type keeps two copies of the sets' content, priv->match (live version, used to match from packet path) priv->clone (work-in-progress version of the 'future' priv->match). All additions and removals are done on priv->clone. When transaction completes, priv->clone becomes priv->match and a new clone is allocated for use by next transaction. Problem is that the cloning requires GFP_KERNEL allocations but we cannot fail at either commit or abort time. This patch defers the clone until we get an insertion or removal request. This allows us to handle OOM situations correctly. This also allows to remove ->dirty in a followup change: If ->clone exists, ->dirty is always true If ->clone is NULL, ->dirty is always false, no elements were added or removed (except catchall elements which are external to the specific set backend). Signed-off-by: Florian Westphal <[email protected]> Reviewed-by: Stefano Brivio <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent a238106 commit 3f1d886

File tree

1 file changed

+49
-21
lines changed

1 file changed

+49
-21
lines changed

net/netfilter/nft_set_pipapo.c

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,29 @@ static bool nft_pipapo_transaction_mutex_held(const struct nft_set *set)
12591259
#endif
12601260
}
12611261

1262+
static struct nft_pipapo_match *pipapo_clone(struct nft_pipapo_match *old);
1263+
1264+
/**
1265+
* pipapo_maybe_clone() - Build clone for pending data changes, if not existing
1266+
* @set: nftables API set representation
1267+
*
1268+
* Return: newly created or existing clone, if any. NULL on allocation failure
1269+
*/
1270+
static struct nft_pipapo_match *pipapo_maybe_clone(const struct nft_set *set)
1271+
{
1272+
struct nft_pipapo *priv = nft_set_priv(set);
1273+
struct nft_pipapo_match *m;
1274+
1275+
if (priv->clone)
1276+
return priv->clone;
1277+
1278+
m = rcu_dereference_protected(priv->match,
1279+
nft_pipapo_transaction_mutex_held(set));
1280+
priv->clone = pipapo_clone(m);
1281+
1282+
return priv->clone;
1283+
}
1284+
12621285
/**
12631286
* nft_pipapo_insert() - Validate and insert ranged elements
12641287
* @net: Network namespace
@@ -1275,15 +1298,18 @@ static int nft_pipapo_insert(const struct net *net, const struct nft_set *set,
12751298
const struct nft_set_ext *ext = nft_set_elem_ext(set, elem->priv);
12761299
union nft_pipapo_map_bucket rulemap[NFT_PIPAPO_MAX_FIELDS];
12771300
const u8 *start = (const u8 *)elem->key.val.data, *end;
1301+
struct nft_pipapo_match *m = pipapo_maybe_clone(set);
12781302
struct nft_pipapo *priv = nft_set_priv(set);
1279-
struct nft_pipapo_match *m = priv->clone;
12801303
u8 genmask = nft_genmask_next(net);
12811304
struct nft_pipapo_elem *e, *dup;
12821305
u64 tstamp = nft_net_tstamp(net);
12831306
struct nft_pipapo_field *f;
12841307
const u8 *start_p, *end_p;
12851308
int i, bsize_max, err = 0;
12861309

1310+
if (!m)
1311+
return -ENOMEM;
1312+
12871313
if (nft_set_ext_exists(ext, NFT_SET_EXT_KEY_END))
12881314
end = (const u8 *)nft_set_ext_key_end(ext)->data;
12891315
else
@@ -1789,46 +1815,38 @@ static void pipapo_reclaim_match(struct rcu_head *rcu)
17891815
static void nft_pipapo_commit(struct nft_set *set)
17901816
{
17911817
struct nft_pipapo *priv = nft_set_priv(set);
1792-
struct nft_pipapo_match *new_clone, *old;
1818+
struct nft_pipapo_match *old;
1819+
1820+
if (!priv->clone)
1821+
return;
17931822

17941823
if (time_after_eq(jiffies, priv->last_gc + nft_set_gc_interval(set)))
17951824
pipapo_gc(set, priv->clone);
17961825

17971826
if (!priv->dirty)
17981827
return;
17991828

1800-
new_clone = pipapo_clone(priv->clone);
1801-
if (!new_clone)
1802-
return;
1803-
1829+
old = rcu_replace_pointer(priv->match, priv->clone,
1830+
nft_pipapo_transaction_mutex_held(set));
1831+
priv->clone = NULL;
18041832
priv->dirty = false;
18051833

1806-
old = rcu_access_pointer(priv->match);
1807-
rcu_assign_pointer(priv->match, priv->clone);
18081834
if (old)
18091835
call_rcu(&old->rcu, pipapo_reclaim_match);
1810-
1811-
priv->clone = new_clone;
18121836
}
18131837

18141838
static void nft_pipapo_abort(const struct nft_set *set)
18151839
{
18161840
struct nft_pipapo *priv = nft_set_priv(set);
1817-
struct nft_pipapo_match *new_clone, *m;
18181841

18191842
if (!priv->dirty)
18201843
return;
18211844

1822-
m = rcu_dereference_protected(priv->match, nft_pipapo_transaction_mutex_held(set));
1823-
1824-
new_clone = pipapo_clone(m);
1825-
if (!new_clone)
1845+
if (!priv->clone)
18261846
return;
1827-
18281847
priv->dirty = false;
1829-
18301848
pipapo_free_match(priv->clone);
1831-
priv->clone = new_clone;
1849+
priv->clone = NULL;
18321850
}
18331851

18341852
/**
@@ -1863,10 +1881,15 @@ static struct nft_elem_priv *
18631881
nft_pipapo_deactivate(const struct net *net, const struct nft_set *set,
18641882
const struct nft_set_elem *elem)
18651883
{
1866-
const struct nft_pipapo *priv = nft_set_priv(set);
1867-
struct nft_pipapo_match *m = priv->clone;
1884+
struct nft_pipapo_match *m = pipapo_maybe_clone(set);
18681885
struct nft_pipapo_elem *e;
18691886

1887+
/* removal must occur on priv->clone, if we are low on memory
1888+
* we have no choice and must fail the removal request.
1889+
*/
1890+
if (!m)
1891+
return NULL;
1892+
18701893
e = pipapo_get(net, set, m, (const u8 *)elem->key.val.data,
18711894
nft_genmask_next(net), nft_net_tstamp(net), GFP_KERNEL);
18721895
if (IS_ERR(e))
@@ -2145,7 +2168,12 @@ static void nft_pipapo_walk(const struct nft_ctx *ctx, struct nft_set *set,
21452168

21462169
switch (iter->type) {
21472170
case NFT_ITER_UPDATE:
2148-
m = priv->clone;
2171+
m = pipapo_maybe_clone(set);
2172+
if (!m) {
2173+
iter->err = -ENOMEM;
2174+
return;
2175+
}
2176+
21492177
nft_pipapo_do_walk(ctx, set, m, iter);
21502178
break;
21512179
case NFT_ITER_READ:

0 commit comments

Comments
 (0)