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

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
...@@ -416,7 +416,7 @@ struct nft_expr_info; ...@@ -416,7 +416,7 @@ struct nft_expr_info;
int nft_expr_inner_parse(const struct nft_ctx *ctx, const struct nlattr *nla, int nft_expr_inner_parse(const struct nft_ctx *ctx, const struct nlattr *nla,
struct nft_expr_info *info); 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); void nft_expr_destroy(const struct nft_ctx *ctx, struct nft_expr *expr);
int nft_expr_dump(struct sk_buff *skb, unsigned int attr, int nft_expr_dump(struct sk_buff *skb, unsigned int attr,
const struct nft_expr *expr, bool reset); const struct nft_expr *expr, bool reset);
...@@ -935,7 +935,7 @@ struct nft_expr_ops { ...@@ -935,7 +935,7 @@ struct nft_expr_ops {
struct nft_regs *regs, struct nft_regs *regs,
const struct nft_pktinfo *pkt); const struct nft_pktinfo *pkt);
int (*clone)(struct nft_expr *dst, int (*clone)(struct nft_expr *dst,
const struct nft_expr *src); const struct nft_expr *src, gfp_t gfp);
unsigned int size; unsigned int size;
int (*init)(const struct nft_ctx *ctx, int (*init)(const struct nft_ctx *ctx,
......
...@@ -3333,7 +3333,7 @@ static struct nft_expr *nft_expr_init(const struct nft_ctx *ctx, ...@@ -3333,7 +3333,7 @@ static struct nft_expr *nft_expr_init(const struct nft_ctx *ctx,
return ERR_PTR(err); 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; int err;
...@@ -3341,7 +3341,7 @@ int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src) ...@@ -3341,7 +3341,7 @@ int nft_expr_clone(struct nft_expr *dst, struct nft_expr *src)
return -EINVAL; return -EINVAL;
dst->ops = src->ops; dst->ops = src->ops;
err = src->ops->clone(dst, src); err = src->ops->clone(dst, src, gfp);
if (err < 0) if (err < 0)
return err; return err;
...@@ -6525,7 +6525,7 @@ int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set, ...@@ -6525,7 +6525,7 @@ int nft_set_elem_expr_clone(const struct nft_ctx *ctx, struct nft_set *set,
if (!expr) if (!expr)
goto err_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) { if (err < 0) {
kfree(expr); kfree(expr);
goto err_expr; goto err_expr;
...@@ -6564,7 +6564,7 @@ static int nft_set_elem_expr_setup(struct nft_ctx *ctx, ...@@ -6564,7 +6564,7 @@ static int nft_set_elem_expr_setup(struct nft_ctx *ctx,
for (i = 0; i < num_exprs; i++) { for (i = 0; i < num_exprs; i++) {
expr = nft_setelem_expr_at(elem_expr, elem_expr->size); 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) if (err < 0)
goto err_elem_expr_setup; goto err_elem_expr_setup;
......
...@@ -210,12 +210,12 @@ static void nft_connlimit_destroy(const struct nft_ctx *ctx, ...@@ -210,12 +210,12 @@ static void nft_connlimit_destroy(const struct nft_ctx *ctx,
nft_connlimit_do_destroy(ctx, priv); 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_dst = nft_expr_priv(dst);
struct nft_connlimit *priv_src = nft_expr_priv(src); 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) if (!priv_dst->list)
return -ENOMEM; return -ENOMEM;
......
...@@ -226,7 +226,7 @@ static void nft_counter_destroy(const struct nft_ctx *ctx, ...@@ -226,7 +226,7 @@ static void nft_counter_destroy(const struct nft_ctx *ctx,
nft_counter_do_destroy(priv); 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 = nft_expr_priv(src);
struct nft_counter_percpu_priv *priv_clone = nft_expr_priv(dst); 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) ...@@ -236,7 +236,7 @@ static int nft_counter_clone(struct nft_expr *dst, const struct nft_expr *src)
nft_counter_fetch(priv, &total); 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) if (cpu_stats == NULL)
return -ENOMEM; return -ENOMEM;
......
...@@ -35,7 +35,7 @@ static int nft_dynset_expr_setup(const struct nft_dynset *priv, ...@@ -35,7 +35,7 @@ static int nft_dynset_expr_setup(const struct nft_dynset *priv,
for (i = 0; i < priv->num_exprs; i++) { for (i = 0; i < priv->num_exprs; i++) {
expr = nft_setelem_expr_at(elem_expr, elem_expr->size); 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; return -1;
elem_expr->size += priv->expr_array[i]->ops->size; elem_expr->size += priv->expr_array[i]->ops->size;
......
...@@ -102,12 +102,12 @@ static void nft_last_destroy(const struct nft_ctx *ctx, ...@@ -102,12 +102,12 @@ static void nft_last_destroy(const struct nft_ctx *ctx,
kfree(priv->last); kfree(priv->last);
} }
static int nft_last_clone(struct nft_expr *dst, const struct nft_expr *src) static int nft_last_clone(struct nft_expr *dst, const struct nft_expr *src, gfp_t gfp)
{ {
struct nft_last_priv *priv_dst = nft_expr_priv(dst); struct nft_last_priv *priv_dst = nft_expr_priv(dst);
struct nft_last_priv *priv_src = nft_expr_priv(src); struct nft_last_priv *priv_src = nft_expr_priv(src);
priv_dst->last = kzalloc(sizeof(*priv_dst->last), GFP_ATOMIC); priv_dst->last = kzalloc(sizeof(*priv_dst->last), gfp);
if (!priv_dst->last) if (!priv_dst->last)
return -ENOMEM; return -ENOMEM;
......
...@@ -150,7 +150,7 @@ static void nft_limit_destroy(const struct nft_ctx *ctx, ...@@ -150,7 +150,7 @@ static void nft_limit_destroy(const struct nft_ctx *ctx,
} }
static int nft_limit_clone(struct nft_limit_priv *priv_dst, static int nft_limit_clone(struct nft_limit_priv *priv_dst,
const struct nft_limit_priv *priv_src) const struct nft_limit_priv *priv_src, gfp_t gfp)
{ {
priv_dst->tokens_max = priv_src->tokens_max; priv_dst->tokens_max = priv_src->tokens_max;
priv_dst->rate = priv_src->rate; priv_dst->rate = priv_src->rate;
...@@ -158,7 +158,7 @@ static int nft_limit_clone(struct nft_limit_priv *priv_dst, ...@@ -158,7 +158,7 @@ static int nft_limit_clone(struct nft_limit_priv *priv_dst,
priv_dst->burst = priv_src->burst; priv_dst->burst = priv_src->burst;
priv_dst->invert = priv_src->invert; priv_dst->invert = priv_src->invert;
priv_dst->limit = kmalloc(sizeof(*priv_dst->limit), GFP_ATOMIC); priv_dst->limit = kmalloc(sizeof(*priv_dst->limit), gfp);
if (!priv_dst->limit) if (!priv_dst->limit)
return -ENOMEM; return -ENOMEM;
...@@ -223,14 +223,15 @@ static void nft_limit_pkts_destroy(const struct nft_ctx *ctx, ...@@ -223,14 +223,15 @@ static void nft_limit_pkts_destroy(const struct nft_ctx *ctx,
nft_limit_destroy(ctx, &priv->limit); nft_limit_destroy(ctx, &priv->limit);
} }
static int nft_limit_pkts_clone(struct nft_expr *dst, const struct nft_expr *src) static int nft_limit_pkts_clone(struct nft_expr *dst, const struct nft_expr *src,
gfp_t gfp)
{ {
struct nft_limit_priv_pkts *priv_dst = nft_expr_priv(dst); struct nft_limit_priv_pkts *priv_dst = nft_expr_priv(dst);
struct nft_limit_priv_pkts *priv_src = nft_expr_priv(src); struct nft_limit_priv_pkts *priv_src = nft_expr_priv(src);
priv_dst->cost = priv_src->cost; priv_dst->cost = priv_src->cost;
return nft_limit_clone(&priv_dst->limit, &priv_src->limit); return nft_limit_clone(&priv_dst->limit, &priv_src->limit, gfp);
} }
static struct nft_expr_type nft_limit_type; static struct nft_expr_type nft_limit_type;
...@@ -281,12 +282,13 @@ static void nft_limit_bytes_destroy(const struct nft_ctx *ctx, ...@@ -281,12 +282,13 @@ static void nft_limit_bytes_destroy(const struct nft_ctx *ctx,
nft_limit_destroy(ctx, priv); nft_limit_destroy(ctx, priv);
} }
static int nft_limit_bytes_clone(struct nft_expr *dst, const struct nft_expr *src) static int nft_limit_bytes_clone(struct nft_expr *dst, const struct nft_expr *src,
gfp_t gfp)
{ {
struct nft_limit_priv *priv_dst = nft_expr_priv(dst); struct nft_limit_priv *priv_dst = nft_expr_priv(dst);
struct nft_limit_priv *priv_src = nft_expr_priv(src); struct nft_limit_priv *priv_src = nft_expr_priv(src);
return nft_limit_clone(priv_dst, priv_src); return nft_limit_clone(priv_dst, priv_src, gfp);
} }
static const struct nft_expr_ops nft_limit_bytes_ops = { static const struct nft_expr_ops nft_limit_bytes_ops = {
......
...@@ -233,7 +233,7 @@ static void nft_quota_destroy(const struct nft_ctx *ctx, ...@@ -233,7 +233,7 @@ static void nft_quota_destroy(const struct nft_ctx *ctx,
return nft_quota_do_destroy(ctx, priv); return nft_quota_do_destroy(ctx, priv);
} }
static int nft_quota_clone(struct nft_expr *dst, const struct nft_expr *src) static int nft_quota_clone(struct nft_expr *dst, const struct nft_expr *src, gfp_t gfp)
{ {
struct nft_quota *priv_dst = nft_expr_priv(dst); struct nft_quota *priv_dst = nft_expr_priv(dst);
struct nft_quota *priv_src = nft_expr_priv(src); struct nft_quota *priv_src = nft_expr_priv(src);
...@@ -241,7 +241,7 @@ static int nft_quota_clone(struct nft_expr *dst, const struct nft_expr *src) ...@@ -241,7 +241,7 @@ static int nft_quota_clone(struct nft_expr *dst, const struct nft_expr *src)
priv_dst->quota = priv_src->quota; priv_dst->quota = priv_src->quota;
priv_dst->flags = priv_src->flags; priv_dst->flags = priv_src->flags;
priv_dst->consumed = kmalloc(sizeof(*priv_dst->consumed), GFP_ATOMIC); priv_dst->consumed = kmalloc(sizeof(*priv_dst->consumed), gfp);
if (!priv_dst->consumed) if (!priv_dst->consumed)
return -ENOMEM; return -ENOMEM;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment