{"id":807351,"url":"http://patchwork.ozlabs.org/api/1.0/patches/807351/?format=json","project":{"id":7,"url":"http://patchwork.ozlabs.org/api/1.0/projects/7/?format=json","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},"msgid":"<20170829232711.1465-4-tom@quantonium.net>","date":"2017-08-29T23:27:08","name":"[v2,net-next,3/6] flow_dissector: Add protocol specific flow dissection offload","commit_ref":null,"pull_url":null,"state":"changes-requested","archived":true,"hash":"98ee713849437d287870c9b2ed1cec097db53ed7","submitter":{"id":72064,"url":"http://patchwork.ozlabs.org/api/1.0/people/72064/?format=json","name":"Tom Herbert","email":"tom@quantonium.net"},"delegate":{"id":34,"url":"http://patchwork.ozlabs.org/api/1.0/users/34/?format=json","username":"davem","first_name":"David","last_name":"Miller","email":"davem@davemloft.net"},"mbox":"http://patchwork.ozlabs.org/project/netdev/patch/20170829232711.1465-4-tom@quantonium.net/mbox/","series":[{"id":503,"url":"http://patchwork.ozlabs.org/api/1.0/series/503/?format=json","date":"2017-08-29T23:27:05","name":"flow_dissector: Protocol specific flow dissector offload","version":2,"mbox":"http://patchwork.ozlabs.org/series/503/mbox/"}],"check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/807351/checks/","tags":{},"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>)","ozlabs.org; dkim=pass (2048-bit key;\n\tunprotected) header.d=quantonium-net.20150623.gappssmtp.com\n\theader.i=@quantonium-net.20150623.gappssmtp.com\n\theader.b=\"Ct8ul4XW\"; dkim-atps=neutral"],"Received":["from vger.kernel.org (vger.kernel.org [209.132.180.67])\n\tby ozlabs.org (Postfix) with ESMTP id 3xhlDG68X1z9s9Y\n\tfor <patchwork-incoming@ozlabs.org>;\n\tWed, 30 Aug 2017 09:27:42 +1000 (AEST)","(majordomo@vger.kernel.org) by vger.kernel.org via listexpand\n\tid S1751791AbdH2X1k (ORCPT <rfc822;patchwork-incoming@ozlabs.org>);\n\tTue, 29 Aug 2017 19:27:40 -0400","from mail-pf0-f180.google.com ([209.85.192.180]:33519 \"EHLO\n\tmail-pf0-f180.google.com\" rhost-flags-OK-OK-OK-OK) by vger.kernel.org\n\twith ESMTP id S1751757AbdH2X1i (ORCPT\n\t<rfc822;netdev@vger.kernel.org>); Tue, 29 Aug 2017 19:27:38 -0400","by mail-pf0-f180.google.com with SMTP id r62so14372597pfj.0\n\tfor <netdev@vger.kernel.org>; Tue, 29 Aug 2017 16:27:38 -0700 (PDT)","from localhost.localdomain (67-207-98-108.static.wiline.com.\n\t[67.207.98.108]) by smtp.gmail.com with ESMTPSA id\n\tr73sm6445593pfa.143.2017.08.29.16.27.36\n\t(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);\n\tTue, 29 Aug 2017 16:27:37 -0700 (PDT)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=quantonium-net.20150623.gappssmtp.com; s=20150623;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references;\n\tbh=3f3hmdpJli1jAXzGm7NtR7ggAuEx0JeybBG9viZivNs=;\n\tb=Ct8ul4XWG+oChx5NDM13eysEJ9XcNWGcEjSDaDtKPqxptpflsm8RhccXmuMmP5XBBf\n\tgE7cnBJDplbiVFSD4EbLiu13osc/ouBhPqs1sF4IZ2g1ajyEDRjDX2SdG7+/Vkdek0sz\n\tyYN/wGvm9vnal5u6PwwHt5z4L1Cjxz4PPEde9gINgBamTt3Fz8AY5SfB5nUyO23HbKW5\n\tmOwuTCckd6VLrPA1G9MfiMY86RNNRdBVvPJSENRXlz/vI5llIdjICl/0yN8jmsr63y10\n\treQpeCJXsKiPA5MTVJqG/JKRwIk5gu7JXzWve7Xf+SRYtdZuP0Q4kRwWteKIHMQD5Vr3\n\tVUiA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references;\n\tbh=3f3hmdpJli1jAXzGm7NtR7ggAuEx0JeybBG9viZivNs=;\n\tb=hB3YPwqrcVN9UG5UrctPGqGKLFuDhLaSUVm57eU2LQpj9Ngy2f8BZFU2EJOLtsxTAo\n\tmdfprC9fIm47knN18uzX8iGlRczysatUkfftH9jMumX2KlbLmGCE97RpIHtDtJGpcr9S\n\tqF7Q7n2u5ecTHd2lLxFwQNe+q7vMdSErHKqHLOXzAbXngTmst6UkJgDuPo0MLSR6VxtP\n\tPW2p0nBQgehvJsUe+eBn5E43iDEYneXCyM6qXhw3DWGe0Dpi5XBOefiH8uOFIHsU2zYn\n\t8qPSdYAcbqlwfasf4bEhbfuTe4GptFy7t73pYJPZXrTtTCP1MtCsG3nPqywSxvHlCemI\n\t0nYw==","X-Gm-Message-State":"AHYfb5hc+m7/hATA3CTOO6rkJt3fbB/ItVuyTK8ssEb8xHWxlsW/kz36\n\t1MuZhhWRbma5e1A/pOA=","X-Received":"by 10.84.254.66 with SMTP id a2mr2491124pln.33.1504049258387;\n\tTue, 29 Aug 2017 16:27:38 -0700 (PDT)","From":"Tom Herbert <tom@quantonium.net>","To":"davem@davemloft.net","Cc":"netdev@vger.kernel.org, Tom Herbert <tom@quantonium.net>","Subject":"[PATCH v2 net-next 3/6] flow_dissector: Add protocol specific flow\n\tdissection offload","Date":"Tue, 29 Aug 2017 16:27:08 -0700","Message-Id":"<20170829232711.1465-4-tom@quantonium.net>","X-Mailer":"git-send-email 2.11.0","In-Reply-To":"<20170829232711.1465-1-tom@quantonium.net>","References":"<20170829232711.1465-1-tom@quantonium.net>","Sender":"netdev-owner@vger.kernel.org","Precedence":"bulk","List-ID":"<netdev.vger.kernel.org>","X-Mailing-List":"netdev@vger.kernel.org"},"content":"Add offload capability for performing protocol specific flow dissection\n(either by EtherType or IP protocol).\n\nSpecifically:\n\n- Add flow_dissect to offload callbacks\n- Move flow_dissect_ret enum to flow_dissector.h, cleanup names and add a\n  couple of values\n- Create GOTO_BY_RESULT macro to use in the main flow dissector switch to\n  simplify handling of functions that return flow_dissect_ret enum\n- In __skb_flow_dissect, add default case for switch(proto) as well as\n  switch(ip_proto) that looks up and calls protocol specific flow\n  dissection\n\nSigned-off-by: Tom Herbert <tom@quantonium.net>\n---\n include/linux/netdevice.h    |   7 +++\n include/net/flow_dissector.h |   9 +++\n net/core/dev.c               |  14 +++++\n net/core/flow_dissector.c    | 132 +++++++++++++++++++++++++++++++------------\n net/ipv4/route.c             |   4 +-\n 5 files changed, 128 insertions(+), 38 deletions(-)","diff":"diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h\nindex c5475b37a631..90ccb434e127 100644\n--- a/include/linux/netdevice.h\n+++ b/include/linux/netdevice.h\n@@ -2208,6 +2208,12 @@ struct offload_callbacks {\n \tstruct sk_buff\t\t**(*gro_receive)(struct sk_buff **head,\n \t\t\t\t\t\t struct sk_buff *skb);\n \tint\t\t\t(*gro_complete)(struct sk_buff *skb, int nhoff);\n+\tenum flow_dissect_ret (*flow_dissect)(const struct sk_buff *skb,\n+\t\t\tstruct flow_dissector_key_control *key_control,\n+\t\t\tstruct flow_dissector *flow_dissector,\n+\t\t\tvoid *target_container, void *data,\n+\t\t\t__be16 *p_proto, u8 *p_ip_proto, int *p_nhoff,\n+\t\t\tint *p_hlen, unsigned int flags);\n };\n \n struct packet_offload {\n@@ -3253,6 +3259,7 @@ struct sk_buff *napi_get_frags(struct napi_struct *napi);\n gro_result_t napi_gro_frags(struct napi_struct *napi);\n struct packet_offload *gro_find_receive_by_type(__be16 type);\n struct packet_offload *gro_find_complete_by_type(__be16 type);\n+struct packet_offload *flow_dissect_find_by_type(__be16 type);\n \n static inline void napi_free_frags(struct napi_struct *napi)\n {\ndiff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h\nindex e2663e900b0a..ad75bbfd1c9c 100644\n--- a/include/net/flow_dissector.h\n+++ b/include/net/flow_dissector.h\n@@ -19,6 +19,14 @@ struct flow_dissector_key_control {\n #define FLOW_DIS_FIRST_FRAG\tBIT(1)\n #define FLOW_DIS_ENCAPSULATION\tBIT(2)\n \n+enum flow_dissect_ret {\n+\tFLOW_DISSECT_RET_OUT_GOOD,\n+\tFLOW_DISSECT_RET_OUT_BAD,\n+\tFLOW_DISSECT_RET_PROTO_AGAIN,\n+\tFLOW_DISSECT_RET_IPPROTO_AGAIN,\n+\tFLOW_DISSECT_RET_CONTINUE,\n+};\n+\n /**\n  * struct flow_dissector_key_basic:\n  * @thoff: Transport header offset\n@@ -205,6 +213,7 @@ enum flow_dissector_key_id {\n #define FLOW_DISSECTOR_F_STOP_AT_L3\t\tBIT(1)\n #define FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL\tBIT(2)\n #define FLOW_DISSECTOR_F_STOP_AT_ENCAP\t\tBIT(3)\n+#define FLOW_DISSECTOR_F_STOP_AT_L4\t\tBIT(4)\n \n struct flow_dissector_key {\n \tenum flow_dissector_key_id key_id;\ndiff --git a/net/core/dev.c b/net/core/dev.c\nindex 270b54754821..22ea8daa930c 100644\n--- a/net/core/dev.c\n+++ b/net/core/dev.c\n@@ -4860,6 +4860,20 @@ struct packet_offload *gro_find_receive_by_type(__be16 type)\n }\n EXPORT_SYMBOL(gro_find_receive_by_type);\n \n+struct packet_offload *flow_dissect_find_by_type(__be16 type)\n+{\n+\tstruct list_head *offload_head = &offload_base;\n+\tstruct packet_offload *ptype;\n+\n+\tlist_for_each_entry_rcu(ptype, offload_head, list) {\n+\t\tif (ptype->type != type || !ptype->callbacks.flow_dissect)\n+\t\t\tcontinue;\n+\t\treturn ptype;\n+\t}\n+\treturn NULL;\n+}\n+EXPORT_SYMBOL(flow_dissect_find_by_type);\n+\n struct packet_offload *gro_find_complete_by_type(__be16 type)\n {\n \tstruct list_head *offload_head = &offload_base;\ndiff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c\nindex 12302acdb073..6a2cf240069a 100644\n--- a/net/core/flow_dissector.c\n+++ b/net/core/flow_dissector.c\n@@ -9,6 +9,7 @@\n #include <net/ipv6.h>\n #include <net/gre.h>\n #include <net/pptp.h>\n+#include <net/protocol.h>\n #include <linux/igmp.h>\n #include <linux/icmp.h>\n #include <linux/sctp.h>\n@@ -115,12 +116,6 @@ __be32 __skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto,\n }\n EXPORT_SYMBOL(__skb_flow_get_ports);\n \n-enum flow_dissect_ret {\n-\tFLOW_DISSECT_RET_OUT_GOOD,\n-\tFLOW_DISSECT_RET_OUT_BAD,\n-\tFLOW_DISSECT_RET_OUT_PROTO_AGAIN,\n-};\n-\n static enum flow_dissect_ret\n __skb_flow_dissect_mpls(const struct sk_buff *skb,\n \t\t\tstruct flow_dissector *flow_dissector,\n@@ -322,7 +317,7 @@ __skb_flow_dissect_gre(const struct sk_buff *skb,\n \tif (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP)\n \t\treturn FLOW_DISSECT_RET_OUT_GOOD;\n \n-\treturn FLOW_DISSECT_RET_OUT_PROTO_AGAIN;\n+\treturn FLOW_DISSECT_RET_PROTO_AGAIN;\n }\n \n static void\n@@ -383,6 +378,27 @@ __skb_flow_dissect_ipv6(const struct sk_buff *skb,\n \tkey_ip->ttl = iph->hop_limit;\n }\n \n+#define GOTO_BY_RESULT(ret) do {\t\t\t\t\\\n+\tswitch (ret) {\t\t\t\t\t\t\\\n+\tcase FLOW_DISSECT_RET_OUT_GOOD:\t\t\t\t\\\n+\t\tgoto out_good;\t\t\t\t\t\\\n+\tcase FLOW_DISSECT_RET_PROTO_AGAIN:\t\t\t\\\n+\t\tgoto proto_again;\t\t\t\t\\\n+\tcase FLOW_DISSECT_RET_IPPROTO_AGAIN:\t\t\t\\\n+\t\tgoto ip_proto_again;\t\t\t\t\\\n+\tcase FLOW_DISSECT_RET_OUT_BAD:\t\t\t\t\\\n+\tdefault:\t\t\t\t\t\t\\\n+\t\tgoto out_bad;\t\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\\\n+} while (0)\n+\n+#define GOTO_OR_CONT_BY_RESULT(ret) do {\t\t\t\\\n+\tenum flow_dissect_ret __ret = (ret);\t\t\t\\\n+\t\t\t\t\t\t\t\t\\\n+\tif (__ret != FLOW_DISSECT_RET_CONTINUE)\t\t\t\\\n+\t\tGOTO_BY_RESULT(__ret);\t\t\t\t\\\n+} while (0)\n+\n /**\n  * __skb_flow_dissect - extract the flow_keys struct and return it\n  * @skb: sk_buff to extract the flow from, can be NULL if the rest are specified\n@@ -659,15 +675,10 @@ bool __skb_flow_dissect(const struct sk_buff *skb,\n \tcase htons(ETH_P_MPLS_UC):\n \tcase htons(ETH_P_MPLS_MC):\n mpls:\n-\t\tswitch (__skb_flow_dissect_mpls(skb, flow_dissector,\n-\t\t\t\t\t\ttarget_container, data,\n-\t\t\t\t\t\tnhoff, hlen)) {\n-\t\tcase FLOW_DISSECT_RET_OUT_GOOD:\n-\t\t\tgoto out_good;\n-\t\tcase FLOW_DISSECT_RET_OUT_BAD:\n-\t\tdefault:\n-\t\t\tgoto out_bad;\n-\t\t}\n+\t\tGOTO_BY_RESULT(__skb_flow_dissect_mpls(skb, flow_dissector,\n+\t\t\t\t\t\t       target_container, data,\n+\t\t\t\t\t\t       nhoff, hlen));\n+\n \tcase htons(ETH_P_FCOE):\n \t\tif ((hlen - nhoff) < FCOE_HEADER_LEN)\n \t\t\tgoto out_bad;\n@@ -677,32 +688,44 @@ bool __skb_flow_dissect(const struct sk_buff *skb,\n \n \tcase htons(ETH_P_ARP):\n \tcase htons(ETH_P_RARP):\n-\t\tswitch (__skb_flow_dissect_arp(skb, flow_dissector,\n-\t\t\t\t\t       target_container, data,\n-\t\t\t\t\t       nhoff, hlen)) {\n-\t\tcase FLOW_DISSECT_RET_OUT_GOOD:\n-\t\t\tgoto out_good;\n-\t\tcase FLOW_DISSECT_RET_OUT_BAD:\n-\t\tdefault:\n-\t\t\tgoto out_bad;\n+\t\tGOTO_BY_RESULT(__skb_flow_dissect_arp(skb, flow_dissector,\n+\t\t\t\t\t\t      target_container, data,\n+\t\t\t\t\t\t      nhoff, hlen));\n+\n+\tdefault: {\n+\t\tstruct packet_offload *ptype;\n+\t\tenum flow_dissect_ret ret;\n+\n+\t\trcu_read_lock();\n+\n+\t\tptype = flow_dissect_find_by_type(proto);\n+\n+\t\tif (ptype) {\n+\t\t\tret = ptype->callbacks.flow_dissect(skb, key_control,\n+\t\t\t\t\t\tflow_dissector,\n+\t\t\t\t\t\ttarget_container,\n+\t\t\t\t\t\tdata, &proto, &ip_proto, &nhoff,\n+\t\t\t\t\t\t&hlen, flags);\n+\t\t\trcu_read_unlock();\n+\n+\t\t\tGOTO_BY_RESULT(ret);\n+\t\t} else {\n+\t\t\trcu_read_unlock();\n \t\t}\n-\tdefault:\n+\n \t\tgoto out_bad;\n \t}\n+\t}\n \n ip_proto_again:\n \tswitch (ip_proto) {\n \tcase IPPROTO_GRE:\n-\t\tswitch (__skb_flow_dissect_gre(skb, key_control, flow_dissector,\n-\t\t\t\t\t       target_container, data,\n-\t\t\t\t\t       &proto, &nhoff, &hlen, flags)) {\n-\t\tcase FLOW_DISSECT_RET_OUT_GOOD:\n-\t\t\tgoto out_good;\n-\t\tcase FLOW_DISSECT_RET_OUT_BAD:\n-\t\t\tgoto out_bad;\n-\t\tcase FLOW_DISSECT_RET_OUT_PROTO_AGAIN:\n-\t\t\tgoto proto_again;\n-\t\t}\n+\t\tGOTO_BY_RESULT(__skb_flow_dissect_gre(skb, key_control,\n+\t\t\t\t\t\t      flow_dissector,\n+\t\t\t\t\t\t      target_container, data,\n+\t\t\t\t\t\t      &proto, &nhoff, &hlen,\n+\t\t\t\t\t\t      flags));\n+\n \tcase NEXTHDR_HOP:\n \tcase NEXTHDR_ROUTING:\n \tcase NEXTHDR_DEST: {\n@@ -768,9 +791,43 @@ bool __skb_flow_dissect(const struct sk_buff *skb,\n \t\t__skb_flow_dissect_tcp(skb, flow_dissector, target_container,\n \t\t\t\t       data, nhoff, hlen);\n \t\tbreak;\n-\tdefault:\n+\tdefault: {\n+\t\tconst struct net_offload *ops = NULL;\n+\n+\t\tif (flags & FLOW_DISSECTOR_F_STOP_AT_L4)\n+\t\t\tbreak;\n+\n+\t\trcu_read_lock();\n+\n+\t\tswitch (proto) {\n+\t\tcase htons(ETH_P_IP):\n+\t\t\tops = rcu_dereference(inet_offloads[ip_proto]);\n+\t\t\tbreak;\n+\t\tcase htons(ETH_P_IPV6):\n+\t\t\tops = rcu_dereference(inet6_offloads[ip_proto]);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (ops && ops->callbacks.flow_dissect) {\n+\t\t\tenum flow_dissect_ret ret;\n+\n+\t\t\tret = ops->callbacks.flow_dissect(skb, key_control,\n+\t\t\t\t\t\tflow_dissector,\n+\t\t\t\t\t\ttarget_container,\n+\t\t\t\t\t\tdata, &proto, &ip_proto, &nhoff,\n+\t\t\t\t\t\t&hlen, flags);\n+\t\t\trcu_read_unlock();\n+\n+\t\t\tGOTO_OR_CONT_BY_RESULT(ret);\n+\t\t} else {\n+\t\t\trcu_read_unlock();\n+\t\t}\n+\n \t\tbreak;\n \t}\n+\t}\n \n \tif (dissector_uses_key(flow_dissector,\n \t\t\t       FLOW_DISSECTOR_KEY_PORTS)) {\n@@ -935,7 +992,8 @@ static inline u32 ___skb_get_hash(const struct sk_buff *skb,\n \t\t\t\t  struct flow_keys *keys, u32 keyval)\n {\n \tskb_flow_dissect_flow_keys(skb, keys,\n-\t\t\t\t   FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL);\n+\t\t\t\t   FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL |\n+\t\t\t\t   FLOW_DISSECTOR_F_STOP_AT_L4);\n \n \treturn __flow_hash_from_keys(keys, keyval);\n }\ndiff --git a/net/ipv4/route.c b/net/ipv4/route.c\nindex 94d4cd2d5ea4..85f12b8e0b7f 100644\n--- a/net/ipv4/route.c\n+++ b/net/ipv4/route.c\n@@ -1811,7 +1811,9 @@ int fib_multipath_hash(const struct fib_info *fi, const struct flowi4 *fl4,\n \tcase 1:\n \t\t/* skb is currently provided only when forwarding */\n \t\tif (skb) {\n-\t\t\tunsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP;\n+\t\t\tunsigned int flag = FLOW_DISSECTOR_F_STOP_AT_ENCAP |\n+\t\t\t\t\t    FLOW_DISSECTOR_F_STOP_AT_L4;\n+;\n \t\t\tstruct flow_keys keys;\n \n \t\t\t/* short-circuit if we already have L4 hash present */\n","prefixes":["v2","net-next","3/6"]}