Commit fa23e0d4 authored by Florian Westphal's avatar Florian Westphal Committed by Pablo Neira Ayuso
Browse files

netfilter: nf_tables: allow clone callbacks to sleep



Sven Auhagen reports transaction failures with following error:
  ./main.nft:13:1-26: Error: Could not process rule: Cannot allocate memory
  percpu: allocation failed, size=16 align=8 atomic=1, atomic alloc failed, no space left

This points to failing pcpu allocation with GFP_ATOMIC flag.
However, transactions happen from user context and are allowed to sleep.

One case where we can call into percpu allocator with GFP_ATOMIC is
nft_counter expression.

Normally this happens from control plane, so this could use GFP_KERNEL
instead.  But one use case, element insertion from packet path,
needs to use GFP_ATOMIC allocations (nft_dynset expression).

At this time, .clone callbacks always use GFP_ATOMIC for this reason.

Add gfp_t argument to the .clone function and pass GFP_KERNEL or
GFP_ATOMIC flag depending on context, this allows all clone memory
allocations to sleep for the normal (transaction) case.

Cc: Sven Auhagen <sven.auhagen@voleatech.de>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarPablo Neira Ayuso <pablo@netfilter.org>
parent a8a388c2
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -416,7 +416,7 @@ struct nft_expr_info;

int nft_expr_inner_parse(const struct nft_ctx *ctx, const struct nlattr *nla,
			 struct nft_expr_info *info);
int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src);
int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src, gfp_t gfp);
void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr);
int nft_expr_dump(struct sk_buff *skb, unsigned int attr,
		  const struct nft_expr *expr, bool reset);
@@ -935,7 +935,7 @@ struct nft_expr_ops {
						struct nft_regs *regs,
						const struct nft_pktinfo *pkt);
	int				(*clone)(struct nft_expr *dst,
						 const struct nft_expr *src);
						 const struct nft_expr *src, gfp_t gfp);
	unsigned int			size;

	int				(*init)(const struct nft_ctx *ctx,
+4 −4
Original line number Diff line number Diff line
@@ -3333,7 +3333,7 @@ static struct nft_expr *nft_expr_init(const struct nft_ctx *ctx,
	return ERR_PTR(err);
}

int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src)
int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src, gfp_t gfp)
{
	int err;

@@ -3341,7 +3341,7 @@ int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src)
		return -EINVAL;

	dst->ops = src->ops;
	err = src->ops->clone(dst, src);
	err = src->ops->clone(dst, src, gfp);
	if (err < 0)
		return err;

@@ -6525,7 +6525,7 @@ int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set,
		if (!expr)
			goto err_expr;

		err = nft_expr_clone(expr, set->exprs[i]);
		err = nft_expr_clone(expr, set->exprs[i], GFP_KERNEL_ACCOUNT);
		if (err < 0) {
			kfree(expr);
			goto err_expr;
@@ -6564,7 +6564,7 @@ static int nft_set_elem_expr_setup(struct nft_ctx *ctx,

	for (i = 0; i < num_exprs; i++) {
		expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
		err = nft_expr_clone(expr, expr_array[i]);
		err = nft_expr_clone(expr, expr_array[i], GFP_KERNEL_ACCOUNT);
		if (err < 0)
			goto err_elem_expr_setup;

+2 −2
Original line number Diff line number Diff line
@@ -210,12 +210,12 @@ static void nft_connlimit_destroy(const struct nft_ctx *ctx,
	nft_connlimit_do_destroy(ctx, priv);
}

static int nft_connlimit_clone(struct nft_expr *dst, const struct nft_expr *src)
static int nft_connlimit_clone(struct nft_expr *dst, const struct nft_expr *src, gfp_t gfp)
{
	struct nft_connlimit *priv_dst = nft_expr_priv(dst);
	struct nft_connlimit *priv_src = nft_expr_priv(src);

	priv_dst->list = kmalloc(sizeof(*priv_dst->list), GFP_ATOMIC);
	priv_dst->list = kmalloc(sizeof(*priv_dst->list), gfp);
	if (!priv_dst->list)
		return -ENOMEM;

+2 −2
Original line number Diff line number Diff line
@@ -226,7 +226,7 @@ static void nft_counter_destroy(const struct nft_ctx *ctx,
	nft_counter_do_destroy(priv);
}

static int nft_counter_clone(struct nft_expr *dst, const struct nft_expr *src)
static int nft_counter_clone(struct nft_expr *dst, const struct nft_expr *src, gfp_t gfp)
{
	struct nft_counter_percpu_priv *priv = nft_expr_priv(src);
	struct nft_counter_percpu_priv *priv_clone = nft_expr_priv(dst);
@@ -236,7 +236,7 @@ static int nft_counter_clone(struct nft_expr *dst, const struct nft_expr *src)

	nft_counter_fetch(priv, &total);

	cpu_stats = alloc_percpu_gfp(struct nft_counter, GFP_ATOMIC);
	cpu_stats = alloc_percpu_gfp(struct nft_counter, gfp);
	if (cpu_stats == NULL)
		return -ENOMEM;

+1 −1
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@ static int nft_dynset_expr_setup(const struct nft_dynset *priv,

	for (i = 0; i < priv->num_exprs; i++) {
		expr = nft_setelem_expr_at(elem_expr, elem_expr->size);
		if (nft_expr_clone(expr, priv->expr_array[i]) < 0)
		if (nft_expr_clone(expr, priv->expr_array[i], GFP_ATOMIC) < 0)
			return -1;

		elem_expr->size += priv->expr_array[i]->ops->size;
Loading