get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/865630/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 865630,
    "url": "http://patchwork.ozlabs.org/api/patches/865630/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/netdev/patch/20180125000941.2763-1-pablo@netfilter.org/",
    "project": {
        "id": 7,
        "url": "http://patchwork.ozlabs.org/api/projects/7/?format=api",
        "name": "Linux network development",
        "link_name": "netdev",
        "list_id": "netdev.vger.kernel.org",
        "list_email": "netdev@vger.kernel.org",
        "web_url": null,
        "scm_url": null,
        "webscm_url": null,
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20180125000941.2763-1-pablo@netfilter.org>",
    "list_archive_url": null,
    "date": "2018-01-25T00:09:41",
    "name": "[nf-next,RFC,v4] netfilter: nf_flow_table: add hardware offload support",
    "commit_ref": null,
    "pull_url": null,
    "state": "rfc",
    "archived": true,
    "hash": "019f74a08c27ed6b73e8facbaee59d2c4d032a70",
    "submitter": {
        "id": 1315,
        "url": "http://patchwork.ozlabs.org/api/people/1315/?format=api",
        "name": "Pablo Neira Ayuso",
        "email": "pablo@netfilter.org"
    },
    "delegate": {
        "id": 34,
        "url": "http://patchwork.ozlabs.org/api/users/34/?format=api",
        "username": "davem",
        "first_name": "David",
        "last_name": "Miller",
        "email": "davem@davemloft.net"
    },
    "mbox": "http://patchwork.ozlabs.org/project/netdev/patch/20180125000941.2763-1-pablo@netfilter.org/mbox/",
    "series": [
        {
            "id": 25234,
            "url": "http://patchwork.ozlabs.org/api/series/25234/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/netdev/list/?series=25234",
            "date": "2018-01-25T00:09:41",
            "name": "[nf-next,RFC,v4] netfilter: nf_flow_table: add hardware offload support",
            "version": 4,
            "mbox": "http://patchwork.ozlabs.org/series/25234/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/865630/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/865630/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<netdev-owner@vger.kernel.org>",
        "X-Original-To": "patchwork-incoming@ozlabs.org",
        "Delivered-To": "patchwork-incoming@ozlabs.org",
        "Authentication-Results": "ozlabs.org;\n\tspf=none (mailfrom) smtp.mailfrom=vger.kernel.org\n\t(client-ip=209.132.180.67; helo=vger.kernel.org;\n\tenvelope-from=netdev-owner@vger.kernel.org;\n\treceiver=<UNKNOWN>)",
        "Received": [
            "from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3zRj8j5pMCz9t34\n\tfor <patchwork-incoming@ozlabs.org>;\n\tThu, 25 Jan 2018 11:09:57 +1100 (AEDT)",
            "(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S932637AbeAYAJy (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tWed, 24 Jan 2018 19:09:54 -0500",
            "from mail.us.es ([193.147.175.20]:44494 \"EHLO mail.us.es\"\n\trhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP\n\tid S932293AbeAYAJx (ORCPT <rfc822;netdev@vger.kernel.org>);\n\tWed, 24 Jan 2018 19:09:53 -0500",
            "from antivirus1-rhel7.int (unknown [192.168.2.11])\n\tby mail.us.es (Postfix) with ESMTP id 6AB3D1BFA8C\n\tfor <netdev@vger.kernel.org>; Thu, 25 Jan 2018 01:09:51 +0100 (CET)",
            "from antivirus1-rhel7.int (localhost [127.0.0.1])\n\tby antivirus1-rhel7.int (Postfix) with ESMTP id 5A25EB7FE3\n\tfor <netdev@vger.kernel.org>; Thu, 25 Jan 2018 01:09:51 +0100 (CET)",
            "by antivirus1-rhel7.int (Postfix, from userid 99)\n\tid 44C5AB7FEA; Thu, 25 Jan 2018 01:09:51 +0100 (CET)",
            "from antivirus1-rhel7.int (localhost [127.0.0.1])\n\tby antivirus1-rhel7.int (Postfix) with ESMTP id C0F1FDA729;\n\tThu, 25 Jan 2018 01:09:48 +0100 (CET)",
            "from 192.168.1.97 (192.168.1.97) by antivirus1-rhel7.int\n\t(F-Secure/fsigk_smtp/550/antivirus1-rhel7.int); \n\tThu, 25 Jan 2018 01:09:48 +0100 (CET)",
            "from salvia.here (sys.soleta.eu [212.170.55.40])\n\t(Authenticated sender: pneira@us.es)\n\tby entrada.int (Postfix) with ESMTPA id 470344265A2F;\n\tThu, 25 Jan 2018 01:09:48 +0100 (CET)"
        ],
        "X-Spam-Checker-Version": "SpamAssassin 3.4.1 (2015-04-28) on\n\tantivirus1-rhel7.int",
        "X-Spam-Level": "",
        "X-Spam-Status": "No, score=-108.2 required=7.5 tests=ALL_TRUSTED,BAYES_50,\n\tSMTPAUTH_US2,USER_IN_WHITELIST autolearn=disabled version=3.4.1",
        "X-Virus-Status": "clean(F-Secure/fsigk_smtp/550/antivirus1-rhel7.int)",
        "X-SMTPAUTHUS": "auth mail.us.es",
        "From": "Pablo Neira Ayuso <pablo@netfilter.org>",
        "To": "netfilter-devel@vger.kernel.org",
        "Cc": "netdev@vger.kernel.org, f.fainelli@gmail.com, ronye@mellanox.com,\n\tsimon.horman@netronome.com, jiri@mellanox.com, nbd@nbd.name,\n\tjohn@phrozen.org, kubakici@wp.pl, fw@strlen.de",
        "Subject": "[PATCH nf-next,\n\tRFC v4] netfilter: nf_flow_table: add hardware offload support",
        "Date": "Thu, 25 Jan 2018 01:09:41 +0100",
        "Message-Id": "<20180125000941.2763-1-pablo@netfilter.org>",
        "X-Mailer": "git-send-email 2.11.0",
        "X-Virus-Scanned": "ClamAV using ClamSMTP",
        "Sender": "netdev-owner@vger.kernel.org",
        "Precedence": "bulk",
        "List-ID": "<netdev.vger.kernel.org>",
        "X-Mailing-List": "netdev@vger.kernel.org"
    },
    "content": "This patch adds the infrastructure to offload flows to hardware, in case\nthe nic/switch comes with built-in flow tables capabilities.\n\nIf the hardware comes with no hardware flow tables or they have\nlimitations in terms of features, the existing infrastructure falls back\nto the software flow table implementation.\n\nThe software flow table garbage collector skips entries that resides in\nthe hardware, so the hardware will be responsible for releasing this\nflow table entry too via flow_offload_dead().\n\nHardware configuration, either to add or to delete entries, is done from\nthe hardware offload workqueue, to ensure this is done from user context\ngiven that we may sleep when grabbing the mdio mutex.\n\nSigned-off-by: Pablo Neira Ayuso <pablo@netfilter.org>\n---\nv4: More work in progress\n- Decouple nf_flow_table_hw from nft_flow_offload via rcu hooks\n- Consolidate ->ndo invocations, now they happen from the hw worker.\n- Fix bug in list handling, use list_replace_init()\n- cleanup entries on nf_flow_table_hw module removal\n- add NFT_FLOWTABLE_F_HW flag to flowtables to explicit signal that user wants\n  to offload entries to hardware.\n\n include/linux/netdevice.h                |   9 ++\n include/net/netfilter/nf_flow_table.h    |  16 +++\n include/uapi/linux/netfilter/nf_tables.h |  11 ++\n net/netfilter/Kconfig                    |   9 ++\n net/netfilter/Makefile                   |   1 +\n net/netfilter/nf_flow_table.c            |  60 +++++++++++\n net/netfilter/nf_flow_table_hw.c         | 174 +++++++++++++++++++++++++++++++\n net/netfilter/nf_tables_api.c            |  12 ++-\n net/netfilter/nft_flow_offload.c         |   5 +\n 9 files changed, 296 insertions(+), 1 deletion(-)\n create mode 100644 net/netfilter/nf_flow_table_hw.c",
    "diff": "diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h\nindex ed0799a12bf2..be0c12acc3f0 100644\n--- a/include/linux/netdevice.h\n+++ b/include/linux/netdevice.h\n@@ -859,6 +859,13 @@ struct dev_ifalias {\n \tchar ifalias[];\n };\n \n+struct flow_offload;\n+\n+enum flow_offload_type {\n+\tFLOW_OFFLOAD_ADD\t= 0,\n+\tFLOW_OFFLOAD_DEL,\n+};\n+\n /*\n  * This structure defines the management hooks for network devices.\n  * The following hooks can be defined; unless noted otherwise, they are\n@@ -1316,6 +1323,8 @@ struct net_device_ops {\n \tint\t\t\t(*ndo_bridge_dellink)(struct net_device *dev,\n \t\t\t\t\t\t      struct nlmsghdr *nlh,\n \t\t\t\t\t\t      u16 flags);\n+\tint\t\t\t(*ndo_flow_offload)(enum flow_offload_type type,\n+\t\t\t\t\t\t    struct flow_offload *flow);\n \tint\t\t\t(*ndo_change_carrier)(struct net_device *dev,\n \t\t\t\t\t\t      bool new_carrier);\n \tint\t\t\t(*ndo_get_phys_port_id)(struct net_device *dev,\ndiff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h\nindex ed49cd169ecf..69067deb61b6 100644\n--- a/include/net/netfilter/nf_flow_table.h\n+++ b/include/net/netfilter/nf_flow_table.h\n@@ -22,7 +22,9 @@ struct nf_flowtable_type {\n struct nf_flowtable {\n \tstruct rhashtable\t\trhashtable;\n \tconst struct nf_flowtable_type\t*type;\n+\tu32\t\t\t\tflags;\n \tstruct delayed_work\t\tgc_work;\n+\tpossible_net_t\t\t\tft_net;\n };\n \n enum flow_offload_tuple_dir {\n@@ -65,6 +67,7 @@ struct flow_offload_tuple_rhash {\n #define FLOW_OFFLOAD_SNAT\t0x1\n #define FLOW_OFFLOAD_DNAT\t0x2\n #define FLOW_OFFLOAD_DYING\t0x4\n+#define FLOW_OFFLOAD_HW\t\t0x8\n \n struct flow_offload {\n \tstruct flow_offload_tuple_rhash\t\ttuplehash[FLOW_OFFLOAD_DIR_MAX];\n@@ -119,6 +122,19 @@ unsigned int nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,\n unsigned int nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,\n \t\t\t\t       const struct nf_hook_state *state);\n \n+void nf_flow_offload_hw_add(struct net *net, struct flow_offload *flow,\n+\t\t\t    struct nf_conn *ct);\n+void nf_flow_offload_hw_del(struct net *net, struct flow_offload *flow);\n+\n+struct nf_flow_table_hw {\n+\tvoid (*add)(struct net *net, struct flow_offload *flow,\n+\t\t    struct nf_conn *ct);\n+\tvoid (*del)(struct net *net, struct flow_offload *flow);\n+};\n+\n+int nf_flow_table_hw_register(const struct nf_flow_table_hw *offload);\n+void nf_flow_table_hw_unregister(const struct nf_flow_table_hw *offload);\n+\n #define MODULE_ALIAS_NF_FLOWTABLE(family)\t\\\n \tMODULE_ALIAS(\"nf-flowtable-\" __stringify(family))\n \ndiff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h\nindex 66dceee0ae30..1974829d6440 100644\n--- a/include/uapi/linux/netfilter/nf_tables.h\n+++ b/include/uapi/linux/netfilter/nf_tables.h\n@@ -1334,6 +1334,15 @@ enum nft_object_attributes {\n #define NFTA_OBJ_MAX\t\t(__NFTA_OBJ_MAX - 1)\n \n /**\n+ * enum nft_flowtable_flags - nf_tables table flags\n+ *\n+ * @NFT_FLOWTABLE_F_HW: this flowtable resides in hardware\n+ */\n+enum nft_flowtable_flags {\n+\tNFT_FLOWTABLE_F_HW\t= 0x1,\n+};\n+\n+/**\n  * enum nft_flowtable_attributes - nf_tables flow table netlink attributes\n  *\n  * @NFTA_FLOWTABLE_TABLE: name of the table containing the expression (NLA_STRING)\n@@ -1341,6 +1350,7 @@ enum nft_object_attributes {\n  * @NFTA_FLOWTABLE_HOOK: netfilter hook configuration(NLA_U32)\n  * @NFTA_FLOWTABLE_USE: number of references to this flow table (NLA_U32)\n  * @NFTA_FLOWTABLE_HANDLE: object handle (NLA_U64)\n+ * @NFTA_FLOWTABLE_FLAGS: flags (NLA_U32)\n  */\n enum nft_flowtable_attributes {\n \tNFTA_FLOWTABLE_UNSPEC,\n@@ -1350,6 +1360,7 @@ enum nft_flowtable_attributes {\n \tNFTA_FLOWTABLE_USE,\n \tNFTA_FLOWTABLE_HANDLE,\n \tNFTA_FLOWTABLE_PAD,\n+\tNFTA_FLOWTABLE_FLAGS,\n \t__NFTA_FLOWTABLE_MAX\n };\n #define NFTA_FLOWTABLE_MAX\t(__NFTA_FLOWTABLE_MAX - 1)\ndiff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig\nindex 9019fa98003d..2351f563214c 100644\n--- a/net/netfilter/Kconfig\n+++ b/net/netfilter/Kconfig\n@@ -681,6 +681,15 @@ config NF_FLOW_TABLE\n \n \t  To compile it as a module, choose M here.\n \n+config NF_FLOW_TABLE_HW\n+\ttristate \"Netfilter flow table hardware offload module\"\n+\tdepends on NF_FLOW_TABLE\n+\thelp\n+\t  This option adds hardware offload support for the flow table core\n+\t  infrastructure.\n+\n+\t  To compile it as a module, choose M here.\n+\n config NETFILTER_XTABLES\n \ttristate \"Netfilter Xtables support (required for ip_tables)\"\n \tdefault m if NETFILTER_ADVANCED=n\ndiff --git a/net/netfilter/Makefile b/net/netfilter/Makefile\nindex 5d9b8b959e58..77604f1046c0 100644\n--- a/net/netfilter/Makefile\n+++ b/net/netfilter/Makefile\n@@ -113,6 +113,7 @@ obj-$(CONFIG_NFT_FWD_NETDEV)\t+= nft_fwd_netdev.o\n # flow table infrastructure\n obj-$(CONFIG_NF_FLOW_TABLE)\t+= nf_flow_table.o\n obj-$(CONFIG_NF_FLOW_TABLE_INET) += nf_flow_table_inet.o\n+obj-$(CONFIG_NF_FLOW_TABLE_HW)\t+= nf_flow_table_hw.o\n \n # generic X tables \n obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o\ndiff --git a/net/netfilter/nf_flow_table.c b/net/netfilter/nf_flow_table.c\nindex 29978866af10..78fb5c377a33 100644\n--- a/net/netfilter/nf_flow_table.c\n+++ b/net/netfilter/nf_flow_table.c\n@@ -232,15 +232,22 @@ static inline bool nf_flow_is_dying(const struct flow_offload *flow)\n \treturn flow->flags & FLOW_OFFLOAD_DYING;\n }\n \n+static inline bool nf_flow_in_hw(const struct flow_offload *flow)\n+{\n+\treturn flow->flags & FLOW_OFFLOAD_HW;\n+}\n+\n void nf_flow_offload_work_gc(struct work_struct *work)\n {\n \tstruct flow_offload_tuple_rhash *tuplehash;\n \tstruct nf_flowtable *flow_table;\n \tstruct rhashtable_iter hti;\n \tstruct flow_offload *flow;\n+\tstruct net *net;\n \tint err;\n \n \tflow_table = container_of(work, struct nf_flowtable, gc_work.work);\n+\tnet = read_pnet(&flow_table->ft_net);\n \n \terr = rhashtable_walk_init(&flow_table->rhashtable, &hti, GFP_KERNEL);\n \tif (err)\n@@ -261,10 +268,16 @@ void nf_flow_offload_work_gc(struct work_struct *work)\n \n \t\tflow = container_of(tuplehash, struct flow_offload, tuplehash[0]);\n \n+\t\tif (nf_flow_in_hw(flow) &&\n+\t\t    !nf_flow_is_dying(flow))\n+\t\t\tcontinue;\n+\n \t\tif (nf_flow_has_expired(flow) ||\n \t\t    nf_flow_is_dying(flow)) {\n \t\t\tflow_offload_del(flow_table, flow);\n \t\t\tnf_flow_release_ct(flow);\n+\t\t\tif (nf_flow_in_hw(flow))\n+\t\t\t\tnf_flow_offload_hw_del(net, flow);\n \t\t}\n \t}\n out:\n@@ -448,5 +461,52 @@ void nf_flow_table_cleanup(struct net *net, struct net_device *dev)\n }\n EXPORT_SYMBOL_GPL(nf_flow_table_cleanup);\n \n+static const struct nf_flow_table_hw __rcu *nf_flow_table_hw_hook;\n+\n+/* Must be called from user context. */\n+void nf_flow_offload_hw_add(struct net *net, struct flow_offload *flow,\n+\t\t\t    struct nf_conn *ct)\n+{\n+\tconst struct nf_flow_table_hw *offload;\n+\n+\trcu_read_lock();\n+\toffload = rcu_dereference(nf_flow_table_hw_hook);\n+\tif (offload)\n+\t\toffload->add(net, flow, ct);\n+\trcu_read_unlock();\n+}\n+EXPORT_SYMBOL_GPL(nf_flow_offload_hw_add);\n+\n+/* Must be called from user context. */\n+void nf_flow_offload_hw_del(struct net *net, struct flow_offload *flow)\n+{\n+\tconst struct nf_flow_table_hw *offload;\n+\n+\trcu_read_lock();\n+\toffload = rcu_dereference(nf_flow_table_hw_hook);\n+\tif (offload)\n+\t\toffload->del(net, flow);\n+\trcu_read_unlock();\n+}\n+EXPORT_SYMBOL_GPL(nf_flow_offload_hw_del);\n+\n+int nf_flow_table_hw_register(const struct nf_flow_table_hw *offload)\n+{\n+\tif (rcu_access_pointer(nf_flow_table_hw_hook))\n+\t\treturn -EBUSY;\n+\n+\trcu_assign_pointer(nf_flow_table_hw_hook, offload);\n+\n+\treturn 0;\n+}\n+EXPORT_SYMBOL_GPL(nf_flow_table_hw_register);\n+\n+void nf_flow_table_hw_unregister(const struct nf_flow_table_hw *offload)\n+{\n+\tWARN_ON(rcu_access_pointer(nf_flow_table_hw_hook) != offload);\n+\trcu_assign_pointer(nf_flow_table_hw_hook, NULL);\n+}\n+EXPORT_SYMBOL_GPL(nf_flow_table_hw_unregister);\n+\n MODULE_LICENSE(\"GPL\");\n MODULE_AUTHOR(\"Pablo Neira Ayuso <pablo@netfilter.org>\");\ndiff --git a/net/netfilter/nf_flow_table_hw.c b/net/netfilter/nf_flow_table_hw.c\nnew file mode 100644\nindex 000000000000..5876800f4a36\n--- /dev/null\n+++ b/net/netfilter/nf_flow_table_hw.c\n@@ -0,0 +1,174 @@\n+#include <linux/kernel.h>\n+#include <linux/init.h>\n+#include <linux/module.h>\n+#include <linux/netfilter.h>\n+#include <linux/rhashtable.h>\n+#include <linux/netdevice.h>\n+#include <net/netfilter/nf_flow_table.h>\n+#include <net/netfilter/nf_conntrack.h>\n+#include <net/netfilter/nf_conntrack_core.h>\n+#include <net/netfilter/nf_conntrack_tuple.h>\n+\n+static DEFINE_SPINLOCK(flow_offload_hw_pending_list_lock);\n+static LIST_HEAD(flow_offload_hw_pending_list);\n+\n+static DEFINE_MUTEX(nf_flow_offload_hw_mutex);\n+static struct work_struct nft_flow_offload_hw_work;\n+\n+struct flow_offload_hw {\n+\tstruct list_head\tlist;\n+\tenum flow_offload_type\ttype;\n+\tstruct flow_offload\t*flow;\n+\tstruct nf_conn\t\t*ct;\n+\tpossible_net_t\t\tflow_hw_net;\n+};\n+\n+static int do_flow_offload_hw(struct net *net, struct flow_offload *flow,\n+\t\t\t      int type)\n+{\n+\tstruct net_device *indev;\n+\tint ret, ifindex;\n+\n+\tifindex = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.iifidx;\n+\tindev = dev_get_by_index(net, ifindex);\n+\tif (WARN_ON(!indev))\n+\t\treturn 0;\n+\n+\tmutex_lock(&nf_flow_offload_hw_mutex);\n+\tret = indev->netdev_ops->ndo_flow_offload(type, flow);\n+\tmutex_unlock(&nf_flow_offload_hw_mutex);\n+\n+\tdev_put(indev);\n+\n+\treturn ret;\n+}\n+\n+static void flow_offload_hw_work_add(struct flow_offload_hw *offload)\n+{\n+\tstruct net *net;\n+\tint ret;\n+\n+\tif (nf_ct_is_dying(offload->ct))\n+\t\treturn;\n+\n+\tnet = read_pnet(&offload->flow_hw_net);\n+\tret = do_flow_offload_hw(net, offload->flow, FLOW_OFFLOAD_ADD);\n+\tif (ret >= 0)\n+\t\toffload->flow->flags |= FLOW_OFFLOAD_HW;\n+}\n+\n+static void flow_offload_hw_work_del(struct flow_offload_hw *offload)\n+{\n+\tstruct net *net = read_pnet(&offload->flow_hw_net);\n+\n+\tdo_flow_offload_hw(net, offload->flow, FLOW_OFFLOAD_DEL);\n+}\n+\n+static void flow_offload_hw_work(struct work_struct *work)\n+{\n+\tstruct flow_offload_hw *offload, *next;\n+\tLIST_HEAD(hw_offload_pending);\n+\n+\tspin_lock_bh(&flow_offload_hw_pending_list_lock);\n+\tlist_replace_init(&flow_offload_hw_pending_list, &hw_offload_pending);\n+\tspin_unlock_bh(&flow_offload_hw_pending_list_lock);\n+\n+\tlist_for_each_entry_safe(offload, next, &hw_offload_pending, list) {\n+\t\tswitch (offload->type) {\n+\t\tcase FLOW_OFFLOAD_ADD:\n+\t\t\tflow_offload_hw_work_add(offload);\n+\t\t\tbreak;\n+\t\tcase FLOW_OFFLOAD_DEL:\n+\t\t\tflow_offload_hw_work_del(offload);\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (offload->ct)\n+\t\t\tnf_conntrack_put(&offload->ct->ct_general);\n+\t\tlist_del(&offload->list);\n+\t\tkfree(offload);\n+\t}\n+}\n+\n+static void flow_offload_queue_work(struct flow_offload_hw *offload)\n+{\n+\tspin_lock_bh(&flow_offload_hw_pending_list_lock);\n+\tlist_add_tail(&offload->list, &flow_offload_hw_pending_list);\n+\tspin_unlock_bh(&flow_offload_hw_pending_list_lock);\n+\n+\tschedule_work(&nft_flow_offload_hw_work);\n+}\n+\n+static void flow_offload_hw_add(struct net *net, struct flow_offload *flow,\n+\t\t\t\tstruct nf_conn *ct)\n+{\n+\tstruct flow_offload_hw *offload;\n+\n+\toffload = kmalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);\n+\tif (!offload)\n+\t\treturn;\n+\n+\tnf_conntrack_get(&ct->ct_general);\n+\toffload->type = FLOW_OFFLOAD_ADD;\n+\toffload->ct = ct;\n+\toffload->flow = flow;\n+\twrite_pnet(&offload->flow_hw_net, net);\n+\n+\tflow_offload_queue_work(offload);\n+}\n+\n+static void flow_offload_hw_del(struct net *net, struct flow_offload *flow)\n+{\n+\tstruct flow_offload_hw *offload;\n+\n+\toffload = kmalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);\n+\tif (!offload)\n+\t\treturn;\n+\n+\toffload->type = FLOW_OFFLOAD_DEL;\n+\toffload->ct = NULL;\n+\toffload->flow = flow;\n+\twrite_pnet(&offload->flow_hw_net, net);\n+\n+\tflow_offload_queue_work(offload);\n+}\n+\n+static const struct nf_flow_table_hw flow_offload_hw = {\n+\t.add\t= flow_offload_hw_add,\n+\t.del\t= flow_offload_hw_del,\n+};\n+\n+static int __init nf_flow_table_hw_module_init(void)\n+{\n+\tINIT_WORK(&nft_flow_offload_hw_work, flow_offload_hw_work);\n+\tnf_flow_table_hw_register(&flow_offload_hw);\n+\n+\treturn 0;\n+}\n+\n+static void __exit nf_flow_table_hw_module_exit(void)\n+{\n+\tstruct flow_offload_hw *offload, *next;\n+\tLIST_HEAD(hw_offload_pending);\n+\tstruct net *net;\n+\n+\tnf_flow_table_hw_unregister(&flow_offload_hw);\n+\tcancel_work_sync(&nft_flow_offload_hw_work);\n+\n+\tlist_for_each_entry_safe(offload, next, &hw_offload_pending, list) {\n+\t\tif (offload->ct)\n+\t\t\tnf_conntrack_put(&offload->ct->ct_general);\n+\t\tlist_del(&offload->list);\n+\t\tkfree(offload);\n+\t}\n+\n+\trtnl_lock();\n+\tfor_each_net(net)\n+\t\tnf_flow_table_cleanup(net, NULL);\n+\trtnl_unlock();\n+}\n+\n+module_init(nf_flow_table_hw_module_init);\n+module_exit(nf_flow_table_hw_module_exit);\n+\n+MODULE_LICENSE(\"GPL\");\n+MODULE_AUTHOR(\"Pablo Neira Ayuso <pablo@netfilter.org>\");\ndiff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c\nindex 0791813a1e7d..a31c59ce3049 100644\n--- a/net/netfilter/nf_tables_api.c\n+++ b/net/netfilter/nf_tables_api.c\n@@ -5089,10 +5089,19 @@ static int nf_tables_newflowtable(struct net *net, struct sock *nlsk,\n \t}\n \n \tflowtable->data.type = type;\n+\twrite_pnet(&flowtable->data.ft_net, net);\n+\n \terr = rhashtable_init(&flowtable->data.rhashtable, type->params);\n \tif (err < 0)\n \t\tgoto err3;\n \n+\tif (nla[NFTA_FLOWTABLE_FLAGS]) {\n+\t\tflowtable->data.flags =\n+\t\t\tntohl(nla_get_be32(nla[NFTA_FLOWTABLE_FLAGS]));\n+\t\tif (flowtable->data.flags & ~NFT_FLOWTABLE_F_HW)\n+\t\t\treturn -EINVAL;\n+\t}\n+\n \terr = nf_tables_flowtable_parse_hook(&ctx, nla[NFTA_FLOWTABLE_HOOK],\n \t\t\t\t\t     flowtable);\n \tif (err < 0)\n@@ -5192,7 +5201,8 @@ static int nf_tables_fill_flowtable_info(struct sk_buff *skb, struct net *net,\n \t    nla_put_string(skb, NFTA_FLOWTABLE_NAME, flowtable->name) ||\n \t    nla_put_be32(skb, NFTA_FLOWTABLE_USE, htonl(flowtable->use)) ||\n \t    nla_put_be64(skb, NFTA_FLOWTABLE_HANDLE, cpu_to_be64(flowtable->handle),\n-\t\t\t NFTA_FLOWTABLE_PAD))\n+\t\t\t NFTA_FLOWTABLE_PAD) ||\n+\t    nla_put_be32(skb, NFTA_FLOWTABLE_FLAGS, htonl(flowtable->data.flags)))\n \t\tgoto nla_put_failure;\n \n \tnest = nla_nest_start(skb, NFTA_FLOWTABLE_HOOK);\ndiff --git a/net/netfilter/nft_flow_offload.c b/net/netfilter/nft_flow_offload.c\nindex ea24a17d4df9..7647fd83a68d 100644\n--- a/net/netfilter/nft_flow_offload.c\n+++ b/net/netfilter/nft_flow_offload.c\n@@ -66,6 +66,7 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,\n {\n \tstruct nft_flow_offload *priv = nft_expr_priv(expr);\n \tstruct nf_flowtable *flowtable = &priv->flowtable->data;\n+\tconst struct net_device *indev = nft_in(pkt);\n \tenum ip_conntrack_info ctinfo;\n \tstruct nf_flow_route route;\n \tstruct flow_offload *flow;\n@@ -110,6 +111,10 @@ static void nft_flow_offload_eval(const struct nft_expr *expr,\n \tif (ret < 0)\n \t\tgoto err_flow_add;\n \n+\tif (flowtable->flags & NFT_FLOWTABLE_F_HW &&\n+\t    indev->netdev_ops->ndo_flow_offload)\n+\t\tnf_flow_offload_hw_add(nft_net(pkt), flow, ct);\n+\n \treturn;\n \n err_flow_add:\n",
    "prefixes": [
        "nf-next",
        "RFC",
        "v4"
    ]
}