]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
netfilter: nf_tables: allow clone callbacks to sleep
authorFlorian Westphal <fw@strlen.de>
Mon, 12 Aug 2024 10:24:45 +0000 (12:24 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 19 Aug 2024 03:45:50 +0000 (05:45 +0200)
commit fa23e0d4b756d25829e124d6b670a4c6bbd4bf7e upstream.

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: Florian Westphal <fw@strlen.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/net/netfilter/nf_tables.h
net/netfilter/nf_tables_api.c
net/netfilter/nft_connlimit.c
net/netfilter/nft_counter.c
net/netfilter/nft_dynset.c
net/netfilter/nft_last.c
net/netfilter/nft_limit.c
net/netfilter/nft_quota.c

index d241c6d8174ab72785469941138cdf5b076d2837..3e92331d8c9a02519ea7839e796b943d3b7a4793 100644 (file)
@@ -374,7 +374,7 @@ static inline void *nft_expr_priv(const struct nft_expr *expr)
        return (void *)expr->data;
 }
 
-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);
@@ -872,7 +872,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,
index 395bd8c1f5c341b201a0888eb0ebfcb20571bbbe..4c5dcd655122a3b6e3dba4519b5b47dd1759e020 100644 (file)
@@ -3049,7 +3049,7 @@ err_expr_parse:
        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;
 
@@ -3057,7 +3057,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;
 
@@ -5954,7 +5954,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;
@@ -5982,7 +5982,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;
 
index f5df535bcbd08f1fa7d1f44ec13e54aca3d7e4ca..403fffa14fa3bb127e2106353252c59196122c1c 100644 (file)
@@ -209,12 +209,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;
 
index 9f78f8ad4ee1065e178ef417a0fb7d13432b87b0..1b468a16b523751be4cebdd1f234b26b5bc22c2c 100644 (file)
@@ -225,7 +225,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);
@@ -235,7 +235,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;
 
index e714e0efa736314ce198c29574017b1693d589b4..ecdd4a60db9c58b69e0f62ea01883d0a811c8de4 100644 (file)
@@ -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;
index 63145a5e69ef68e92d748421c0c4510e31a2bf60..0959e5adfe96ad6789a7cda9d25f8a71af8573f0 100644 (file)
@@ -101,12 +101,12 @@ static void nft_last_destroy(const struct nft_ctx *ctx,
        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_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)
                return -ENOMEM;
 
index b23a671fa9d8f62cf96fcfcbe38cd9425d23ae5e..9f69be2016c4a10e1dd5620b975733af42a7d873 100644 (file)
@@ -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,
-                          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->rate = priv_src->rate;
@@ -158,7 +158,7 @@ static int nft_limit_clone(struct nft_limit_priv *priv_dst,
        priv_dst->burst = priv_src->burst;
        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)
                return -ENOMEM;
 
@@ -222,14 +222,15 @@ static void nft_limit_pkts_destroy(const struct nft_ctx *ctx,
        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_src = nft_expr_priv(src);
 
        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;
@@ -279,12 +280,13 @@ static void nft_limit_bytes_destroy(const struct nft_ctx *ctx,
        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_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 = {
index 73de40007dfe9a16b1b1a91644300bc9fd7cc6c6..586a6df645bcba19c75f1f8c353588e677dd7269 100644 (file)
@@ -232,7 +232,7 @@ static void nft_quota_destroy(const struct nft_ctx *ctx,
        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_src = nft_expr_priv(src);
@@ -240,7 +240,7 @@ static int nft_quota_clone(struct nft_expr *dst, const struct nft_expr *src)
        priv_dst->quota = priv_src->quota;
        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)
                return -ENOMEM;