diff options
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/Makefile | 6 | ||||
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/flower/action.c | 211 | ||||
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/flower/cmsg.c | 11 | ||||
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/flower/cmsg.h | 201 | ||||
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/flower/main.c | 39 | ||||
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/flower/main.h | 159 | ||||
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/flower/match.c | 292 | ||||
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/flower/metadata.c | 438 | ||||
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/flower/offload.c | 400 | ||||
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/nfp_net_common.c | 17 | ||||
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/nfp_net_repr.c | 8 | ||||
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/nfp_net_repr.h | 9 | ||||
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/nfp_port.c | 44 | ||||
-rw-r--r-- | drivers/net/ethernet/netronome/nfp/nfp_port.h | 6 |
14 files changed, 1812 insertions, 29 deletions
diff --git a/drivers/net/ethernet/netronome/nfp/Makefile b/drivers/net/ethernet/netronome/nfp/Makefile index 43bdbc228969..b8e1358868bd 100644 --- a/drivers/net/ethernet/netronome/nfp/Makefile +++ b/drivers/net/ethernet/netronome/nfp/Makefile @@ -31,8 +31,12 @@ nfp-objs := \ ifeq ($(CONFIG_NFP_APP_FLOWER),y) nfp-objs += \ + flower/action.o \ flower/cmsg.o \ - flower/main.o + flower/main.o \ + flower/match.o \ + flower/metadata.o \ + flower/offload.o endif ifeq ($(CONFIG_BPF_SYSCALL),y) diff --git a/drivers/net/ethernet/netronome/nfp/flower/action.c b/drivers/net/ethernet/netronome/nfp/flower/action.c new file mode 100644 index 000000000000..db9750695dc7 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/flower/action.c @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/bitfield.h> +#include <net/pkt_cls.h> +#include <net/switchdev.h> +#include <net/tc_act/tc_gact.h> +#include <net/tc_act/tc_mirred.h> +#include <net/tc_act/tc_vlan.h> + +#include "cmsg.h" +#include "main.h" +#include "../nfp_net_repr.h" + +static void nfp_fl_pop_vlan(struct nfp_fl_pop_vlan *pop_vlan) +{ + size_t act_size = sizeof(struct nfp_fl_pop_vlan); + u16 tmp_pop_vlan_op; + + tmp_pop_vlan_op = + FIELD_PREP(NFP_FL_ACT_LEN_LW, act_size >> NFP_FL_LW_SIZ) | + FIELD_PREP(NFP_FL_ACT_JMP_ID, NFP_FL_ACTION_OPCODE_POP_VLAN); + + pop_vlan->a_op = cpu_to_be16(tmp_pop_vlan_op); + pop_vlan->reserved = 0; +} + +static void +nfp_fl_push_vlan(struct nfp_fl_push_vlan *push_vlan, + const struct tc_action *action) +{ + size_t act_size = sizeof(struct nfp_fl_push_vlan); + struct tcf_vlan *vlan = to_vlan(action); + u16 tmp_push_vlan_tci; + u16 tmp_push_vlan_op; + + tmp_push_vlan_op = + FIELD_PREP(NFP_FL_ACT_LEN_LW, act_size >> NFP_FL_LW_SIZ) | + FIELD_PREP(NFP_FL_ACT_JMP_ID, NFP_FL_ACTION_OPCODE_PUSH_VLAN); + + push_vlan->a_op = cpu_to_be16(tmp_push_vlan_op); + /* Set action push vlan parameters. */ + push_vlan->reserved = 0; + push_vlan->vlan_tpid = tcf_vlan_push_proto(action); + + tmp_push_vlan_tci = + FIELD_PREP(NFP_FL_PUSH_VLAN_PRIO, vlan->tcfv_push_prio) | + FIELD_PREP(NFP_FL_PUSH_VLAN_VID, vlan->tcfv_push_vid) | + NFP_FL_PUSH_VLAN_CFI; + push_vlan->vlan_tci = cpu_to_be16(tmp_push_vlan_tci); +} + +static int +nfp_fl_output(struct nfp_fl_output *output, const struct tc_action *action, + struct nfp_fl_payload *nfp_flow, bool last, + struct net_device *in_dev) +{ + size_t act_size = sizeof(struct nfp_fl_output); + struct net_device *out_dev; + u16 tmp_output_op; + int ifindex; + + /* Set action opcode to output action. */ + tmp_output_op = + FIELD_PREP(NFP_FL_ACT_LEN_LW, act_size >> NFP_FL_LW_SIZ) | + FIELD_PREP(NFP_FL_ACT_JMP_ID, NFP_FL_ACTION_OPCODE_OUTPUT); + + output->a_op = cpu_to_be16(tmp_output_op); + + /* Set action output parameters. */ + output->flags = cpu_to_be16(last ? NFP_FL_OUT_FLAGS_LAST : 0); + + ifindex = tcf_mirred_ifindex(action); + out_dev = __dev_get_by_index(dev_net(in_dev), ifindex); + if (!out_dev) + return -EOPNOTSUPP; + + /* Only offload egress ports are on the same device as the ingress + * port. + */ + if (!switchdev_port_same_parent_id(in_dev, out_dev)) + return -EOPNOTSUPP; + + output->port = cpu_to_be32(nfp_repr_get_port_id(out_dev)); + if (!output->port) + return -EOPNOTSUPP; + + nfp_flow->meta.shortcut = output->port; + + return 0; +} + +static int +nfp_flower_loop_action(const struct tc_action *a, + struct nfp_fl_payload *nfp_fl, int *a_len, + struct net_device *netdev) +{ + struct nfp_fl_push_vlan *psh_v; + struct nfp_fl_pop_vlan *pop_v; + struct nfp_fl_output *output; + int err; + + if (is_tcf_gact_shot(a)) { + nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_DROP); + } else if (is_tcf_mirred_egress_redirect(a)) { + if (*a_len + sizeof(struct nfp_fl_output) > NFP_FL_MAX_A_SIZ) + return -EOPNOTSUPP; + + output = (struct nfp_fl_output *)&nfp_fl->action_data[*a_len]; + err = nfp_fl_output(output, a, nfp_fl, true, netdev); + if (err) + return err; + + *a_len += sizeof(struct nfp_fl_output); + } else if (is_tcf_mirred_egress_mirror(a)) { + if (*a_len + sizeof(struct nfp_fl_output) > NFP_FL_MAX_A_SIZ) + return -EOPNOTSUPP; + + output = (struct nfp_fl_output *)&nfp_fl->action_data[*a_len]; + err = nfp_fl_output(output, a, nfp_fl, false, netdev); + if (err) + return err; + + *a_len += sizeof(struct nfp_fl_output); + } else if (is_tcf_vlan(a) && tcf_vlan_action(a) == TCA_VLAN_ACT_POP) { + if (*a_len + sizeof(struct nfp_fl_pop_vlan) > NFP_FL_MAX_A_SIZ) + return -EOPNOTSUPP; + + pop_v = (struct nfp_fl_pop_vlan *)&nfp_fl->action_data[*a_len]; + nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_POPV); + + nfp_fl_pop_vlan(pop_v); + *a_len += sizeof(struct nfp_fl_pop_vlan); + } else if (is_tcf_vlan(a) && tcf_vlan_action(a) == TCA_VLAN_ACT_PUSH) { + if (*a_len + sizeof(struct nfp_fl_push_vlan) > NFP_FL_MAX_A_SIZ) + return -EOPNOTSUPP; + + psh_v = (struct nfp_fl_push_vlan *)&nfp_fl->action_data[*a_len]; + nfp_fl->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL); + + nfp_fl_push_vlan(psh_v, a); + *a_len += sizeof(struct nfp_fl_push_vlan); + } else { + /* Currently we do not handle any other actions. */ + return -EOPNOTSUPP; + } + + return 0; +} + +int nfp_flower_compile_action(struct tc_cls_flower_offload *flow, + struct net_device *netdev, + struct nfp_fl_payload *nfp_flow) +{ + int act_len, act_cnt, err; + const struct tc_action *a; + LIST_HEAD(actions); + + memset(nfp_flow->action_data, 0, NFP_FL_MAX_A_SIZ); + nfp_flow->meta.act_len = 0; + act_len = 0; + act_cnt = 0; + + tcf_exts_to_list(flow->exts, &actions); + list_for_each_entry(a, &actions, list) { + err = nfp_flower_loop_action(a, nfp_flow, &act_len, netdev); + if (err) + return err; + act_cnt++; + } + + /* We optimise when the action list is small, this can unfortunately + * not happen once we have more than one action in the action list. + */ + if (act_cnt > 1) + nfp_flow->meta.shortcut = cpu_to_be32(NFP_FL_SC_ACT_NULL); + + nfp_flow->meta.act_len = act_len; + + return 0; +} diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c index 916a6196d2ba..dd7fa9cf225f 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c @@ -36,6 +36,7 @@ #include <linux/skbuff.h> #include <net/dst_metadata.h> +#include "main.h" #include "../nfpcore/nfp_cpp.h" #include "../nfp_net_repr.h" #include "./cmsg.h" @@ -52,12 +53,7 @@ nfp_flower_cmsg_get_hdr(struct sk_buff *skb) return (struct nfp_flower_cmsg_hdr *)skb->data; } -static void *nfp_flower_cmsg_get_data(struct sk_buff *skb) -{ - return (unsigned char *)skb->data + NFP_FLOWER_CMSG_HLEN; -} - -static struct sk_buff * +struct sk_buff * nfp_flower_cmsg_alloc(struct nfp_app *app, unsigned int size, enum nfp_flower_cmsg_type_port type) { @@ -148,6 +144,9 @@ void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb) case NFP_FLOWER_CMSG_TYPE_PORT_MOD: nfp_flower_cmsg_portmod_rx(app, skb); break; + case NFP_FLOWER_CMSG_TYPE_FLOW_STATS: + nfp_flower_rx_flow_stats(app, skb); + break; default: nfp_flower_cmsg_warn(app, "Cannot handle invalid repr control type %u\n", type); diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h index c10ae7631941..cf738de170ab 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h @@ -40,6 +40,196 @@ #include "../nfp_app.h" +#define NFP_FLOWER_LAYER_META BIT(0) +#define NFP_FLOWER_LAYER_PORT BIT(1) +#define NFP_FLOWER_LAYER_MAC BIT(2) +#define NFP_FLOWER_LAYER_TP BIT(3) +#define NFP_FLOWER_LAYER_IPV4 BIT(4) +#define NFP_FLOWER_LAYER_IPV6 BIT(5) +#define NFP_FLOWER_LAYER_CT BIT(6) +#define NFP_FLOWER_LAYER_VXLAN BIT(7) + +#define NFP_FLOWER_LAYER_ETHER BIT(3) +#define NFP_FLOWER_LAYER_ARP BIT(4) + +#define NFP_FLOWER_MASK_VLAN_PRIO GENMASK(15, 13) +#define NFP_FLOWER_MASK_VLAN_CFI BIT(12) +#define NFP_FLOWER_MASK_VLAN_VID GENMASK(11, 0) + +#define NFP_FL_SC_ACT_DROP 0x80000000 +#define NFP_FL_SC_ACT_USER 0x7D000000 +#define NFP_FL_SC_ACT_POPV 0x6A000000 +#define NFP_FL_SC_ACT_NULL 0x00000000 + +/* The maximum action list size (in bytes) supported by the NFP. + */ +#define NFP_FL_MAX_A_SIZ 1216 +#define NFP_FL_LW_SIZ 2 + +/* Action opcodes */ +#define NFP_FL_ACTION_OPCODE_OUTPUT 0 +#define NFP_FL_ACTION_OPCODE_PUSH_VLAN 1 +#define NFP_FL_ACTION_OPCODE_POP_VLAN 2 +#define NFP_FL_ACTION_OPCODE_NUM 32 + +#define NFP_FL_ACT_JMP_ID GENMASK(15, 8) +#define NFP_FL_ACT_LEN_LW GENMASK(7, 0) + +#define NFP_FL_OUT_FLAGS_LAST BIT(15) +#define NFP_FL_OUT_FLAGS_USE_TUN BIT(4) +#define NFP_FL_OUT_FLAGS_TYPE_IDX GENMASK(2, 0) + +#define NFP_FL_PUSH_VLAN_PRIO GENMASK(15, 13) +#define NFP_FL_PUSH_VLAN_CFI BIT(12) +#define NFP_FL_PUSH_VLAN_VID GENMASK(11, 0) + +struct nfp_fl_output { + __be16 a_op; + __be16 flags; + __be32 port; +}; + +struct nfp_fl_push_vlan { + __be16 a_op; + __be16 reserved; + __be16 vlan_tpid; + __be16 vlan_tci; +}; + +struct nfp_fl_pop_vlan { + __be16 a_op; + __be16 reserved; +}; + +/* Metadata without L2 (1W/4B) + * ---------------------------------------------------------------- + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | key_layers | mask_id | reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct nfp_flower_meta_one { + u8 nfp_flow_key_layer; + u8 mask_id; + u16 reserved; +}; + +/* Metadata with L2 (1W/4B) + * ---------------------------------------------------------------- + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | key_type | mask_id | PCP |p| vlan outermost VID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ^ ^ + * NOTE: | TCI | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct nfp_flower_meta_two { + u8 nfp_flow_key_layer; + u8 mask_id; + __be16 tci; +}; + +/* Port details (1W/4B) + * ---------------------------------------------------------------- + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | port_ingress | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct nfp_flower_in_port { + __be32 in_port; +}; + +/* L2 details (4W/16B) + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | mac_addr_dst, 31 - 0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | mac_addr_dst, 47 - 32 | mac_addr_src, 15 - 0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | mac_addr_src, 47 - 16 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | mpls outermost label | TC |B| reserved |q| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct nfp_flower_mac_mpls { + u8 mac_dst[6]; + u8 mac_src[6]; + __be32 mpls_lse; +}; + +/* L4 ports (for UDP, TCP, SCTP) (1W/4B) + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | port_src | port_dst | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct nfp_flower_tp_ports { + __be16 port_src; + __be16 port_dst; +}; + +/* L3 IPv4 details (3W/12B) + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | DSCP |ECN| protocol | reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv4_addr_src | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv4_addr_dst | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct nfp_flower_ipv4 { + u8 tos; + u8 proto; + u8 ttl; + u8 reserved; + __be32 ipv4_src; + __be32 ipv4_dst; +}; + +/* L3 IPv6 details (10W/40B) + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | DSCP |ECN| protocol | reserved | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_exthdr | res | ipv6_flow_label | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_src, 31 - 0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_src, 63 - 32 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_src, 95 - 64 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_src, 127 - 96 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_dst, 31 - 0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_dst, 63 - 32 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_dst, 95 - 64 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ipv6_addr_dst, 127 - 96 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct nfp_flower_ipv6 { + u8 tos; + u8 proto; + u8 ttl; + u8 reserved; + __be32 ipv6_flow_label_exthdr; + struct in6_addr ipv6_src; + struct in6_addr ipv6_dst; +}; + /* The base header for a control message packet. * Defines an 8-bit version, and an 8-bit type, padded * to a 32-bit word. Rest of the packet is type-specific. @@ -55,7 +245,10 @@ struct nfp_flower_cmsg_hdr { /* Types defined for port related control messages */ enum nfp_flower_cmsg_type_port { + NFP_FLOWER_CMSG_TYPE_FLOW_ADD = 0, + NFP_FLOWER_CMSG_TYPE_FLOW_DEL = 2, NFP_FLOWER_CMSG_TYPE_PORT_MOD = 8, + NFP_FLOWER_CMSG_TYPE_FLOW_STATS = 15, NFP_FLOWER_CMSG_TYPE_PORT_ECHO = 16, NFP_FLOWER_CMSG_TYPE_MAX = 32, }; @@ -110,7 +303,15 @@ nfp_flower_cmsg_pcie_port(u8 nfp_pcie, enum nfp_flower_cmsg_port_vnic_type type, NFP_FLOWER_CMSG_PORT_TYPE_PCIE_PORT); } +static inline void *nfp_flower_cmsg_get_data(struct sk_buff *skb) +{ + return (unsigned char *)skb->data + NFP_FLOWER_CMSG_HLEN; +} + int nfp_flower_cmsg_portmod(struct nfp_repr *repr, bool carrier_ok); void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb); +struct sk_buff * +nfp_flower_cmsg_alloc(struct nfp_app *app, unsigned int size, + enum nfp_flower_cmsg_type_port type); #endif diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.c b/drivers/net/ethernet/netronome/nfp/flower/main.c index ab68a8f58862..5fe6d3582597 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.c +++ b/drivers/net/ethernet/netronome/nfp/flower/main.c @@ -34,10 +34,13 @@ #include <linux/etherdevice.h> #include <linux/pci.h> #include <linux/skbuff.h> +#include <linux/vmalloc.h> #include <net/devlink.h> #include <net/dst_metadata.h> +#include "main.h" #include "../nfpcore/nfp_cpp.h" +#include "../nfpcore/nfp_nffw.h" #include "../nfpcore/nfp_nsp.h" #include "../nfp_app.h" #include "../nfp_main.h" @@ -46,13 +49,7 @@ #include "../nfp_port.h" #include "./cmsg.h" -/** - * struct nfp_flower_priv - Flower APP per-vNIC priv data - * @nn: Pointer to vNIC - */ -struct nfp_flower_priv { - struct nfp_net *nn; -}; +#define NFP_FLOWER_ALLOWED_VER 0x0001000000010000UL static const char *nfp_flower_extra_cap(struct nfp_app *app, struct nfp_net *nn) { @@ -313,6 +310,8 @@ err_invalid_port: static int nfp_flower_init(struct nfp_app *app) { const struct nfp_pf *pf = app->pf; + u64 version; + int err; if (!pf->eth_tbl) { nfp_warn(app->cpp, "FlowerNIC requires eth table\n"); @@ -329,16 +328,36 @@ static int nfp_flower_init(struct nfp_app *app) return -EINVAL; } - app->priv = kzalloc(sizeof(struct nfp_flower_priv), GFP_KERNEL); + version = nfp_rtsym_read_le(app->pf->rtbl, "hw_flower_version", &err); + if (err) { + nfp_warn(app->cpp, "FlowerNIC requires hw_flower_version memory symbol\n"); + return err; + } + + /* We need to ensure hardware has enough flower capabilities. */ + if (version != NFP_FLOWER_ALLOWED_VER) { + nfp_warn(app->cpp, "FlowerNIC: unsupported firmware version\n"); + return -EINVAL; + } + + app->priv = vzalloc(sizeof(struct nfp_flower_priv)); if (!app->priv) return -ENOMEM; + err = nfp_flower_metadata_init(app); + if (err) + goto err_free_app_priv; + return 0; + +err_free_app_priv: + vfree(app->priv); + return err; } static void nfp_flower_clean(struct nfp_app *app) { - kfree(app->priv); + vfree(app->priv); app->priv = NULL; } @@ -367,4 +386,6 @@ const struct nfp_app_type app_flower = { .eswitch_mode_get = eswitch_mode_get, .repr_get = nfp_flower_repr_get, + + .setup_tc = nfp_flower_setup_tc, }; diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h new file mode 100644 index 000000000000..9e64c048e83f --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __NFP_FLOWER_H__ +#define __NFP_FLOWER_H__ 1 + +#include <linux/circ_buf.h> +#include <linux/hashtable.h> +#include <linux/time64.h> +#include <linux/types.h> + +struct tc_to_netdev; +struct net_device; +struct nfp_app; + +#define NFP_FL_STATS_ENTRY_RS BIT(20) +#define NFP_FL_STATS_ELEM_RS 4 +#define NFP_FL_REPEATED_HASH_MAX BIT(17) +#define NFP_FLOWER_HASH_BITS 19 +#define NFP_FLOWER_MASK_ENTRY_RS 256 +#define NFP_FLOWER_MASK_ELEMENT_RS 1 +#define NFP_FLOWER_MASK_HASH_BITS 10 + +#define NFP_FL_META_FLAG_NEW_MASK 128 +#define NFP_FL_META_FLAG_LAST_MASK 1 + +#define NFP_FL_MASK_REUSE_TIME_NS 40000 +#define NFP_FL_MASK_ID_LOCATION 1 + +struct nfp_fl_mask_id { + struct circ_buf mask_id_free_list; + struct timespec64 *last_used; + u8 init_unallocated; +}; + +struct nfp_fl_stats_id { + struct circ_buf free_list; + u32 init_unalloc; + u8 repeated_em_count; +}; + +/** + * struct nfp_flower_priv - Flower APP per-vNIC priv data + * @nn: Pointer to vNIC + * @mask_id_seed: Seed used for mask hash table + * @flower_version: HW version of flower + * @stats_ids: List of free stats ids + * @mask_ids: List of free mask ids + * @mask_table: Hash table used to store masks + * @flow_table: Hash table used to store flower rules + */ +struct nfp_flower_priv { + struct nfp_net *nn; + u32 mask_id_seed; + u64 flower_version; + struct nfp_fl_stats_id stats_ids; + struct nfp_fl_mask_id mask_ids; + DECLARE_HASHTABLE(mask_table, NFP_FLOWER_MASK_HASH_BITS); + DECLARE_HASHTABLE(flow_table, NFP_FLOWER_HASH_BITS); +}; + +struct nfp_fl_key_ls { + u32 key_layer_two; + u8 key_layer; + int key_size; +}; + +struct nfp_fl_rule_metadata { + u8 key_len; + u8 mask_len; + u8 act_len; + u8 flags; + __be32 host_ctx_id; + __be64 host_cookie __packed; + __be64 flow_version __packed; + __be32 shortcut; +}; + +struct nfp_fl_stats { + u64 pkts; + u64 bytes; + u64 used; +}; + +struct nfp_fl_payload { + struct nfp_fl_rule_metadata meta; + unsigned long tc_flower_cookie; + struct hlist_node link; + struct rcu_head rcu; + spinlock_t lock; /* lock stats */ + struct nfp_fl_stats stats; + char *unmasked_data; + char *mask_data; + char *action_data; +}; + +struct nfp_fl_stats_frame { + __be32 stats_con_id; + __be32 pkt_count; + __be64 byte_count; + __be64 stats_cookie; +}; + +int nfp_flower_metadata_init(struct nfp_app *app); +void nfp_flower_metadata_cleanup(struct nfp_app *app); + +int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, + u32 handle, __be16 proto, struct tc_to_netdev *tc); +int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow, + struct nfp_fl_key_ls *key_ls, + struct net_device *netdev, + struct nfp_fl_payload *nfp_flow); +int nfp_flower_compile_action(struct tc_cls_flower_offload *flow, + struct net_device *netdev, + struct nfp_fl_payload *nfp_flow); +int nfp_compile_flow_metadata(struct nfp_app *app, + struct tc_cls_flower_offload *flow, + struct nfp_fl_payload *nfp_flow); +int nfp_modify_flow_metadata(struct nfp_app *app, + struct nfp_fl_payload *nfp_flow); + +struct nfp_fl_payload * +nfp_flower_search_fl_table(struct nfp_app *app, unsigned long tc_flower_cookie); +struct nfp_fl_payload * +nfp_flower_remove_fl_table(struct nfp_app *app, unsigned long tc_flower_cookie); + +void nfp_flower_rx_flow_stats(struct nfp_app *app, struct sk_buff *skb); + +#endif diff --git a/drivers/net/ethernet/netronome/nfp/flower/match.c b/drivers/net/ethernet/netronome/nfp/flower/match.c new file mode 100644 index 000000000000..0e08404480ef --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/flower/match.c @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/bitfield.h> +#include <net/pkt_cls.h> + +#include "cmsg.h" +#include "main.h" + +static void +nfp_flower_compile_meta_tci(struct nfp_flower_meta_two *frame, + struct tc_cls_flower_offload *flow, u8 key_type, + bool mask_version) +{ + struct flow_dissector_key_vlan *flow_vlan; + u16 tmp_tci; + + /* Populate the metadata frame. */ + frame->nfp_flow_key_layer = key_type; + frame->mask_id = ~0; + + if (mask_version) { + frame->tci = cpu_to_be16(~0); + return; + } + + flow_vlan = skb_flow_dissector_target(flow->dissector, + FLOW_DISSECTOR_KEY_VLAN, + flow->key); + + /* Populate the tci field. */ + if (!flow_vlan->vlan_id) { + tmp_tci = 0; + } else { + tmp_tci = FIELD_PREP(NFP_FLOWER_MASK_VLAN_PRIO, + flow_vlan->vlan_priority) | + FIELD_PREP(NFP_FLOWER_MASK_VLAN_VID, + flow_vlan->vlan_id) | + NFP_FLOWER_MASK_VLAN_CFI; + } + frame->tci = cpu_to_be16(tmp_tci); +} + +static void +nfp_flower_compile_meta(struct nfp_flower_meta_one *frame, u8 key_type) +{ + frame->nfp_flow_key_layer = key_type; + frame->mask_id = 0; + frame->reserved = 0; +} + +static int +nfp_flower_compile_port(struct nfp_flower_in_port *frame, u32 cmsg_port, + bool mask_version) +{ + if (mask_version) { + frame->in_port = cpu_to_be32(~0); + return 0; + } + + frame->in_port = cpu_to_be32(cmsg_port); + + return 0; +} + +static void +nfp_flower_compile_mac(struct nfp_flower_mac_mpls *frame, + struct tc_cls_flower_offload *flow, + bool mask_version) +{ + struct fl_flow_key *target = mask_version ? flow->mask : flow->key; + struct flow_dissector_key_eth_addrs *flow_mac; + + flow_mac = skb_flow_dissector_target(flow->dissector, + FLOW_DISSECTOR_KEY_ETH_ADDRS, + target); + + memset(frame, 0, sizeof(struct nfp_flower_mac_mpls)); + + /* Populate mac frame. */ + ether_addr_copy(frame->mac_dst, &flow_mac->dst[0]); + ether_addr_copy(frame->mac_src, &flow_mac->src[0]); + + if (mask_version) + frame->mpls_lse = cpu_to_be32(~0); +} + +static void +nfp_flower_compile_tport(struct nfp_flower_tp_ports *frame, + struct tc_cls_flower_offload *flow, + bool mask_version) +{ + struct fl_flow_key *target = mask_version ? flow->mask : flow->key; + struct flow_dissector_key_ports *flow_tp; + + flow_tp = skb_flow_dissector_target(flow->dissector, + FLOW_DISSECTOR_KEY_PORTS, + target); + + frame->port_src = flow_tp->src; + frame->port_dst = flow_tp->dst; +} + +static void +nfp_flower_compile_ipv4(struct nfp_flower_ipv4 *frame, + struct tc_cls_flower_offload *flow, + bool mask_version) +{ + struct fl_flow_key *target = mask_version ? flow->mask : flow->key; + struct flow_dissector_key_ipv4_addrs *flow_ipv4; + struct flow_dissector_key_basic *flow_basic; + + flow_ipv4 = skb_flow_dissector_target(flow->dissector, + FLOW_DISSECTOR_KEY_IPV4_ADDRS, + target); + + flow_basic = skb_flow_dissector_target(flow->dissector, + FLOW_DISSECTOR_KEY_BASIC, + target); + + /* Populate IPv4 frame. */ + frame->reserved = 0; + frame->ipv4_src = flow_ipv4->src; + frame->ipv4_dst = flow_ipv4->dst; + frame->proto = flow_basic->ip_proto; + /* Wildcard TOS/TTL for now. */ + frame->tos = 0; + frame->ttl = 0; +} + +static void +nfp_flower_compile_ipv6(struct nfp_flower_ipv6 *frame, + struct tc_cls_flower_offload *flow, + bool mask_version) +{ + struct fl_flow_key *target = mask_version ? flow->mask : flow->key; + struct flow_dissector_key_ipv6_addrs *flow_ipv6; + struct flow_dissector_key_basic *flow_basic; + + flow_ipv6 = skb_flow_dissector_target(flow->dissector, + FLOW_DISSECTOR_KEY_IPV6_ADDRS, + target); + + flow_basic = skb_flow_dissector_target(flow->dissector, + FLOW_DISSECTOR_KEY_BASIC, + target); + + /* Populate IPv6 frame. */ + frame->reserved = 0; + frame->ipv6_src = flow_ipv6->src; + frame->ipv6_dst = flow_ipv6->dst; + frame->proto = flow_basic->ip_proto; + /* Wildcard LABEL/TOS/TTL for now. */ + frame->ipv6_flow_label_exthdr = 0; + frame->tos = 0; + frame->ttl = 0; +} + +int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow, + struct nfp_fl_key_ls *key_ls, + struct net_device *netdev, + struct nfp_fl_payload *nfp_flow) +{ + int err; + u8 *ext; + u8 *msk; + + memset(nfp_flow->unmasked_data, 0, key_ls->key_size); + memset(nfp_flow->mask_data, 0, key_ls->key_size); + + ext = nfp_flow->unmasked_data; + msk = nfp_flow->mask_data; + if (NFP_FLOWER_LAYER_PORT & key_ls->key_layer) { + /* Populate Exact Metadata. */ + nfp_flower_compile_meta_tci((struct nfp_flower_meta_two *)ext, + flow, key_ls->key_layer, false); + /* Populate Mask Metadata. */ + nfp_flower_compile_meta_tci((struct nfp_flower_meta_two *)msk, + flow, key_ls->key_layer, true); + ext += sizeof(struct nfp_flower_meta_two); + msk += sizeof(struct nfp_flower_meta_two); + + /* Populate Exact Port data. */ + err = nfp_flower_compile_port((struct nfp_flower_in_port *)ext, + nfp_repr_get_port_id(netdev), + false); + if (err) + return err; + + /* Populate Mask Port Data. */ + err = nfp_flower_compile_port((struct nfp_flower_in_port *)msk, + nfp_repr_get_port_id(netdev), + true); + if (err) + return err; + + ext += sizeof(struct nfp_flower_in_port); + msk += sizeof(struct nfp_flower_in_port); + } else { + /* Populate Exact Metadata. */ + nfp_flower_compile_meta((struct nfp_flower_meta_one *)ext, + key_ls->key_layer); + /* Populate Mask Metadata. */ + nfp_flower_compile_meta((struct nfp_flower_meta_one *)msk, + key_ls->key_layer); + ext += sizeof(struct nfp_flower_meta_one); + msk += sizeof(struct nfp_flower_meta_one); + } + + if (NFP_FLOWER_LAYER_META & key_ls->key_layer) { + /* Additional Metadata Fields. + * Currently unsupported. + */ + return -EOPNOTSUPP; + } + + if (NFP_FLOWER_LAYER_MAC & key_ls->key_layer) { + /* Populate Exact MAC Data. */ + nfp_flower_compile_mac((struct nfp_flower_mac_mpls *)ext, + flow, false); + /* Populate Mask MAC Data. */ + nfp_flower_compile_mac((struct nfp_flower_mac_mpls *)msk, + flow, true); + ext += sizeof(struct nfp_flower_mac_mpls); + msk += sizeof(struct nfp_flower_mac_mpls); + } + + if (NFP_FLOWER_LAYER_TP & key_ls->key_layer) { + /* Populate Exact TP Data. */ + nfp_flower_compile_tport((struct nfp_flower_tp_ports *)ext, + flow, false); + /* Populate Mask TP Data. */ + nfp_flower_compile_tport((struct nfp_flower_tp_ports *)msk, + flow, true); + ext += sizeof(struct nfp_flower_tp_ports); + msk += sizeof(struct nfp_flower_tp_ports); + } + + if (NFP_FLOWER_LAYER_IPV4 & key_ls->key_layer) { + /* Populate Exact IPv4 Data. */ + nfp_flower_compile_ipv4((struct nfp_flower_ipv4 *)ext, + flow, false); + /* Populate Mask IPv4 Data. */ + nfp_flower_compile_ipv4((struct nfp_flower_ipv4 *)msk, + flow, true); + ext += sizeof(struct nfp_flower_ipv4); + msk += sizeof(struct nfp_flower_ipv4); + } + + if (NFP_FLOWER_LAYER_IPV6 & key_ls->key_layer) { + /* Populate Exact IPv4 Data. */ + nfp_flower_compile_ipv6((struct nfp_flower_ipv6 *)ext, + flow, false); + /* Populate Mask IPv4 Data. */ + nfp_flower_compile_ipv6((struct nfp_flower_ipv6 *)msk, + flow, true); + ext += sizeof(struct nfp_flower_ipv6); + msk += sizeof(struct nfp_flower_ipv6); + } + + return 0; +} diff --git a/drivers/net/ethernet/netronome/nfp/flower/metadata.c b/drivers/net/ethernet/netronome/nfp/flower/metadata.c new file mode 100644 index 000000000000..fec0ff2ca94f --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/flower/metadata.c @@ -0,0 +1,438 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/hash.h> +#include <linux/hashtable.h> +#include <linux/jhash.h> +#include <linux/vmalloc.h> +#include <net/pkt_cls.h> + +#include "cmsg.h" +#include "main.h" +#include "../nfp_app.h" + +struct nfp_mask_id_table { + struct hlist_node link; + u32 hash_key; + u32 ref_cnt; + u8 mask_id; +}; + +static int nfp_release_stats_entry(struct nfp_app *app, u32 stats_context_id) +{ + struct nfp_flower_priv *priv = app->priv; + struct circ_buf *ring; + + ring = &priv->stats_ids.free_list; + /* Check if buffer is full. */ + if (!CIRC_SPACE(ring->head, ring->tail, NFP_FL_STATS_ENTRY_RS * + NFP_FL_STATS_ELEM_RS - + NFP_FL_STATS_ELEM_RS + 1)) + return -ENOBUFS; + + memcpy(&ring->buf[ring->head], &stats_context_id, NFP_FL_STATS_ELEM_RS); + ring->head = (ring->head + NFP_FL_STATS_ELEM_RS) % + (NFP_FL_STATS_ENTRY_RS * NFP_FL_STATS_ELEM_RS); + + return 0; +} + +static int nfp_get_stats_entry(struct nfp_app *app, u32 *stats_context_id) +{ + struct nfp_flower_priv *priv = app->priv; + u32 freed_stats_id, temp_stats_id; + struct circ_buf *ring; + + ring = &priv->stats_ids.free_list; + freed_stats_id = NFP_FL_STATS_ENTRY_RS; + /* Check for unallocated entries first. */ + if (priv->stats_ids.init_unalloc > 0) { + *stats_context_id = priv->stats_ids.init_unalloc - 1; + priv->stats_ids.init_unalloc--; + return 0; + } + + /* Check if buffer is empty. */ + if (ring->head == ring->tail) { + *stats_context_id = freed_stats_id; + return -ENOENT; + } + + memcpy(&temp_stats_id, &ring->buf[ring->tail], NFP_FL_STATS_ELEM_RS); + *stats_context_id = temp_stats_id; + memcpy(&ring->buf[ring->tail], &freed_stats_id, NFP_FL_STATS_ELEM_RS); + ring->tail = (ring->tail + NFP_FL_STATS_ELEM_RS) % + (NFP_FL_STATS_ENTRY_RS * NFP_FL_STATS_ELEM_RS); + + return 0; +} + +/* Must be called with either RTNL or rcu_read_lock */ +struct nfp_fl_payload * +nfp_flower_search_fl_table(struct nfp_app *app, unsigned long tc_flower_cookie) +{ + struct nfp_flower_priv *priv = app->priv; + struct nfp_fl_payload *flower_entry; + + hash_for_each_possible_rcu(priv->flow_table, flower_entry, link, + tc_flower_cookie) + if (flower_entry->tc_flower_cookie == tc_flower_cookie) + return flower_entry; + + return NULL; +} + +static void +nfp_flower_update_stats(struct nfp_app *app, struct nfp_fl_stats_frame *stats) +{ + struct nfp_fl_payload *nfp_flow; + unsigned long flower_cookie; + + flower_cookie = be64_to_cpu(stats->stats_cookie); + + rcu_read_lock(); + nfp_flow = nfp_flower_search_fl_table(app, flower_cookie); + if (!nfp_flow) + goto exit_rcu_unlock; + + if (nfp_flow->meta.host_ctx_id != stats->stats_con_id) + goto exit_rcu_unlock; + + spin_lock(&nfp_flow->lock); + nfp_flow->stats.pkts += be32_to_cpu(stats->pkt_count); + nfp_flow->stats.bytes += be64_to_cpu(stats->byte_count); + nfp_flow->stats.used = jiffies; + spin_unlock(&nfp_flow->lock); + +exit_rcu_unlock: + rcu_read_unlock(); +} + +void nfp_flower_rx_flow_stats(struct nfp_app *app, struct sk_buff *skb) +{ + unsigned int msg_len = skb->len - NFP_FLOWER_CMSG_HLEN; + struct nfp_fl_stats_frame *stats_frame; + unsigned char *msg; + int i; + + msg = nfp_flower_cmsg_get_data(skb); + + stats_frame = (struct nfp_fl_stats_frame *)msg; + for (i = 0; i < msg_len / sizeof(*stats_frame); i++) + nfp_flower_update_stats(app, stats_frame + i); +} + +static int nfp_release_mask_id(struct nfp_app *app, u8 mask_id) +{ + struct nfp_flower_priv *priv = app->priv; + struct circ_buf *ring; + struct timespec64 now; + + ring = &priv->mask_ids.mask_id_free_list; + /* Checking if buffer is full. */ + if (CIRC_SPACE(ring->head, ring->tail, NFP_FLOWER_MASK_ENTRY_RS) == 0) + return -ENOBUFS; + + memcpy(&ring->buf[ring->head], &mask_id, NFP_FLOWER_MASK_ELEMENT_RS); + ring->head = (ring->head + NFP_FLOWER_MASK_ELEMENT_RS) % + (NFP_FLOWER_MASK_ENTRY_RS * NFP_FLOWER_MASK_ELEMENT_RS); + + getnstimeofday64(&now); + priv->mask_ids.last_used[mask_id] = now; + + return 0; +} + +static int nfp_mask_alloc(struct nfp_app *app, u8 *mask_id) +{ + struct nfp_flower_priv *priv = app->priv; + struct timespec64 delta, now; + struct circ_buf *ring; + u8 temp_id, freed_id; + + ring = &priv->mask_ids.mask_id_free_list; + freed_id = NFP_FLOWER_MASK_ENTRY_RS - 1; + /* Checking for unallocated entries first. */ + if (priv->mask_ids.init_unallocated > 0) { + *mask_id = priv->mask_ids.init_unallocated; + priv->mask_ids.init_unallocated--; + return 0; + } + + /* Checking if buffer is empty. */ + if (ring->head == ring->tail) + goto err_not_found; + + memcpy(&temp_id, &ring->buf[ring->tail], NFP_FLOWER_MASK_ELEMENT_RS); + *mask_id = temp_id; + + getnstimeofday64(&now); + delta = timespec64_sub(now, priv->mask_ids.last_used[*mask_id]); + + if (timespec64_to_ns(&delta) < NFP_FL_MASK_REUSE_TIME_NS) + goto err_not_found; + + memcpy(&ring->buf[ring->tail], &freed_id, NFP_FLOWER_MASK_ELEMENT_RS); + ring->tail = (ring->tail + NFP_FLOWER_MASK_ELEMENT_RS) % + (NFP_FLOWER_MASK_ENTRY_RS * NFP_FLOWER_MASK_ELEMENT_RS); + + return 0; + +err_not_found: + *mask_id = freed_id; + return -ENOENT; +} + +static int +nfp_add_mask_table(struct nfp_app *app, char *mask_data, u32 mask_len) +{ + struct nfp_flower_priv *priv = app->priv; + struct nfp_mask_id_table *mask_entry; + unsigned long hash_key; + u8 mask_id; + + if (nfp_mask_alloc(app, &mask_id)) + return -ENOENT; + + mask_entry = kmalloc(sizeof(*mask_entry), GFP_KERNEL); + if (!mask_entry) { + nfp_release_mask_id(app, mask_id); + return -ENOMEM; + } + + INIT_HLIST_NODE(&mask_entry->link); + mask_entry->mask_id = mask_id; + hash_key = jhash(mask_data, mask_len, priv->mask_id_seed); + mask_entry->hash_key = hash_key; + mask_entry->ref_cnt = 1; + hash_add(priv->mask_table, &mask_entry->link, hash_key); + + return mask_id; +} + +static struct nfp_mask_id_table * +nfp_search_mask_table(struct nfp_app *app, char *mask_data, u32 mask_len) +{ + struct nfp_flower_priv *priv = app->priv; + struct nfp_mask_id_table *mask_entry; + unsigned long hash_key; + + hash_key = jhash(mask_data, mask_len, priv->mask_id_seed); + + hash_for_each_possible(priv->mask_table, mask_entry, link, hash_key) + if (mask_entry->hash_key == hash_key) + return mask_entry; + + return NULL; +} + +static int +nfp_find_in_mask_table(struct nfp_app *app, char *mask_data, u32 mask_len) +{ + struct nfp_mask_id_table *mask_entry; + + mask_entry = nfp_search_mask_table(app, mask_data, mask_len); + if (!mask_entry) + return -ENOENT; + + mask_entry->ref_cnt++; + + /* Casting u8 to int for later use. */ + return mask_entry->mask_id; +} + +static bool +nfp_check_mask_add(struct nfp_app *app, char *mask_data, u32 mask_len, + u8 *meta_flags, u8 *mask_id) +{ + int id; + + id = nfp_find_in_mask_table(app, mask_data, mask_len); + if (id < 0) { + id = nfp_add_mask_table(app, mask_data, mask_len); + if (id < 0) + return false; + *meta_flags |= NFP_FL_META_FLAG_NEW_MASK; + } + *mask_id = id; + + return true; +} + +static bool +nfp_check_mask_remove(struct nfp_app *app, char *mask_data, u32 mask_len, + u8 *meta_flags, u8 *mask_id) +{ + struct nfp_mask_id_table *mask_entry; + + mask_entry = nfp_search_mask_table(app, mask_data, mask_len); + if (!mask_entry) + return false; + + *mask_id = mask_entry->mask_id; + mask_entry->ref_cnt--; + if (!mask_entry->ref_cnt) { + hash_del(&mask_entry->link); + nfp_release_mask_id(app, *mask_id); + kfree(mask_entry); + if (meta_flags) + *meta_flags |= NFP_FL_META_FLAG_LAST_MASK; + } + + return true; +} + +int nfp_compile_flow_metadata(struct nfp_app *app, + struct tc_cls_flower_offload *flow, + struct nfp_fl_payload *nfp_flow) +{ + struct nfp_flower_priv *priv = app->priv; + struct nfp_fl_payload *check_entry; + u8 new_mask_id; + u32 stats_cxt; + + if (nfp_get_stats_entry(app, &stats_cxt)) + return -ENOENT; + + nfp_flow->meta.host_ctx_id = cpu_to_be32(stats_cxt); + nfp_flow->meta.host_cookie = cpu_to_be64(flow->cookie); + + new_mask_id = 0; + if (!nfp_check_mask_add(app, nfp_flow->mask_data, + nfp_flow->meta.mask_len, + &nfp_flow->meta.flags, &new_mask_id)) { + if (nfp_release_stats_entry(app, stats_cxt)) + return -EINVAL; + return -ENOENT; + } + + nfp_flow->meta.flow_version = cpu_to_be64(priv->flower_version); + priv->flower_version++; + + /* Update flow payload with mask ids. */ + nfp_flow->unmasked_data[NFP_FL_MASK_ID_LOCATION] = new_mask_id; + nfp_flow->stats.pkts = 0; + nfp_flow->stats.bytes = 0; + nfp_flow->stats.used = jiffies; + + check_entry = nfp_flower_search_fl_table(app, flow->cookie); + if (check_entry) { + if (nfp_release_stats_entry(app, stats_cxt)) + return -EINVAL; + + if (!nfp_check_mask_remove(app, nfp_flow->mask_data, + nfp_flow->meta.mask_len, + NULL, &new_mask_id)) + return -EINVAL; + + return -EEXIST; + } + + return 0; +} + +int nfp_modify_flow_metadata(struct nfp_app *app, + struct nfp_fl_payload *nfp_flow) +{ + struct nfp_flower_priv *priv = app->priv; + u8 new_mask_id = 0; + u32 temp_ctx_id; + + nfp_check_mask_remove(app, nfp_flow->mask_data, + nfp_flow->meta.mask_len, &nfp_flow->meta.flags, + &new_mask_id); + + nfp_flow->meta.flow_version = cpu_to_be64(priv->flower_version); + priv->flower_version++; + + /* Update flow payload with mask ids. */ + nfp_flow->unmasked_data[NFP_FL_MASK_ID_LOCATION] = new_mask_id; + + /* Release the stats ctx id. */ + temp_ctx_id = be32_to_cpu(nfp_flow->meta.host_ctx_id); + + return nfp_release_stats_entry(app, temp_ctx_id); +} + +int nfp_flower_metadata_init(struct nfp_app *app) +{ + struct nfp_flower_priv *priv = app->priv; + + hash_init(priv->mask_table); + hash_init(priv->flow_table); + get_random_bytes(&priv->mask_id_seed, sizeof(priv->mask_id_seed)); + + /* Init ring buffer and unallocated mask_ids. */ + priv->mask_ids.mask_id_free_list.buf = + kmalloc_array(NFP_FLOWER_MASK_ENTRY_RS, + NFP_FLOWER_MASK_ELEMENT_RS, GFP_KERNEL); + if (!priv->mask_ids.mask_id_free_list.buf) + return -ENOMEM; + + priv->mask_ids.init_unallocated = NFP_FLOWER_MASK_ENTRY_RS - 1; + + /* Init timestamps for mask id*/ + priv->mask_ids.last_used = + kmalloc_array(NFP_FLOWER_MASK_ENTRY_RS, + sizeof(*priv->mask_ids.last_used), GFP_KERNEL); + if (!priv->mask_ids.last_used) + goto err_free_mask_id; + + /* Init ring buffer and unallocated stats_ids. */ + priv->stats_ids.free_list.buf = + vmalloc(NFP_FL_STATS_ENTRY_RS * NFP_FL_STATS_ELEM_RS); + if (!priv->stats_ids.free_list.buf) + goto err_free_last_used; + + priv->stats_ids.init_unalloc = NFP_FL_REPEATED_HASH_MAX; + + return 0; + +err_free_last_used: + kfree(priv->stats_ids.free_list.buf); +err_free_mask_id: + kfree(priv->mask_ids.mask_id_free_list.buf); + return -ENOMEM; +} + +void nfp_flower_metadata_cleanup(struct nfp_app *app) +{ + struct nfp_flower_priv *priv = app->priv; + + if (!priv) + return; + + kfree(priv->mask_ids.mask_id_free_list.buf); + kfree(priv->mask_ids.last_used); + vfree(priv->stats_ids.free_list.buf); +} diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c new file mode 100644 index 000000000000..4ad10bd5e139 --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/skbuff.h> +#include <net/devlink.h> +#include <net/pkt_cls.h> + +#include "cmsg.h" +#include "main.h" +#include "../nfpcore/nfp_cpp.h" +#include "../nfpcore/nfp_nsp.h" +#include "../nfp_app.h" +#include "../nfp_main.h" +#include "../nfp_net.h" +#include "../nfp_port.h" + +static int +nfp_flower_xmit_flow(struct net_device *netdev, + struct nfp_fl_payload *nfp_flow, u8 mtype) +{ + u32 meta_len, key_len, mask_len, act_len, tot_len; + struct nfp_repr *priv = netdev_priv(netdev); + struct sk_buff *skb; + unsigned char *msg; + + meta_len = sizeof(struct nfp_fl_rule_metadata); + key_len = nfp_flow->meta.key_len; + mask_len = nfp_flow->meta.mask_len; + act_len = nfp_flow->meta.act_len; + + tot_len = meta_len + key_len + mask_len + act_len; + + /* Convert to long words as firmware expects + * lengths in units of NFP_FL_LW_SIZ. + */ + nfp_flow->meta.key_len >>= NFP_FL_LW_SIZ; + nfp_flow->meta.mask_len >>= NFP_FL_LW_SIZ; + nfp_flow->meta.act_len >>= NFP_FL_LW_SIZ; + + skb = nfp_flower_cmsg_alloc(priv->app, tot_len, mtype); + if (!skb) + return -ENOMEM; + + msg = nfp_flower_cmsg_get_data(skb); + memcpy(msg, &nfp_flow->meta, meta_len); + memcpy(&msg[meta_len], nfp_flow->unmasked_data, key_len); + memcpy(&msg[meta_len + key_len], nfp_flow->mask_data, mask_len); + memcpy(&msg[meta_len + key_len + mask_len], + nfp_flow->action_data, act_len); + + /* Convert back to bytes as software expects + * lengths in units of bytes. + */ + nfp_flow->meta.key_len <<= NFP_FL_LW_SIZ; + nfp_flow->meta.mask_len <<= NFP_FL_LW_SIZ; + nfp_flow->meta.act_len <<= NFP_FL_LW_SIZ; + + nfp_ctrl_tx(priv->app->ctrl, skb); + + return 0; +} + +static bool nfp_flower_check_higher_than_mac(struct tc_cls_flower_offload *f) +{ + return dissector_uses_key(f->dissector, + FLOW_DISSECTOR_KEY_IPV4_ADDRS) || + dissector_uses_key(f->dissector, + FLOW_DISSECTOR_KEY_IPV6_ADDRS) || + dissector_uses_key(f->dissector, + FLOW_DISSECTOR_KEY_PORTS) || + dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ICMP); +} + +static int +nfp_flower_calculate_key_layers(struct nfp_fl_key_ls *ret_key_ls, + struct tc_cls_flower_offload *flow) +{ + struct flow_dissector_key_control *mask_enc_ctl; + struct flow_dissector_key_basic *mask_basic; + struct flow_dissector_key_basic *key_basic; + u32 key_layer_two; + u8 key_layer; + int key_size; + + mask_enc_ctl = skb_flow_dissector_target(flow->dissector, + FLOW_DISSECTOR_KEY_ENC_CONTROL, + flow->mask); + + mask_basic = skb_flow_dissector_target(flow->dissector, + FLOW_DISSECTOR_KEY_BASIC, + flow->mask); + + key_basic = skb_flow_dissector_target(flow->dissector, + FLOW_DISSECTOR_KEY_BASIC, + flow->key); + key_layer_two = 0; + key_layer = NFP_FLOWER_LAYER_PORT | NFP_FLOWER_LAYER_MAC; + key_size = sizeof(struct nfp_flower_meta_one) + + sizeof(struct nfp_flower_in_port) + + sizeof(struct nfp_flower_mac_mpls); + + /* We are expecting a tunnel. For now we ignore offloading. */ + if (mask_enc_ctl->addr_type) + return -EOPNOTSUPP; + + if (mask_basic->n_proto) { + /* Ethernet type is present in the key. */ + switch (key_basic->n_proto) { + case cpu_to_be16(ETH_P_IP): + key_layer |= NFP_FLOWER_LAYER_IPV4; + key_size += sizeof(struct nfp_flower_ipv4); + break; + + case cpu_to_be16(ETH_P_IPV6): + key_layer |= NFP_FLOWER_LAYER_IPV6; + key_size += sizeof(struct nfp_flower_ipv6); + break; + + /* Currently we do not offload ARP + * because we rely on it to get to the host. + */ + case cpu_to_be16(ETH_P_ARP): + return -EOPNOTSUPP; + + /* Will be included in layer 2. */ + case cpu_to_be16(ETH_P_8021Q): + break; + + default: + /* Other ethtype - we need check the masks for the + * remainder of the key to ensure we can offload. + */ + if (nfp_flower_check_higher_than_mac(flow)) + return -EOPNOTSUPP; + break; + } + } + + if (mask_basic->ip_proto) { + /* Ethernet type is present in the key. */ + switch (key_basic->ip_proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_SCTP: + case IPPROTO_ICMP: + case IPPROTO_ICMPV6: + key_layer |= NFP_FLOWER_LAYER_TP; + key_size += sizeof(struct nfp_flower_tp_ports); + break; + default: + /* Other ip proto - we need check the masks for the + * remainder of the key to ensure we can offload. + */ + return -EOPNOTSUPP; + } + } + + ret_key_ls->key_layer = key_layer; + ret_key_ls->key_layer_two = key_layer_two; + ret_key_ls->key_size = key_size; + + return 0; +} + +static struct nfp_fl_payload * +nfp_flower_allocate_new(struct nfp_fl_key_ls *key_layer) +{ + struct nfp_fl_payload *flow_pay; + + flow_pay = kmalloc(sizeof(*flow_pay), GFP_KERNEL); + if (!flow_pay) + return NULL; + + flow_pay->meta.key_len = key_layer->key_size; + flow_pay->unmasked_data = kmalloc(key_layer->key_size, GFP_KERNEL); + if (!flow_pay->unmasked_data) + goto err_free_flow; + + flow_pay->meta.mask_len = key_layer->key_size; + flow_pay->mask_data = kmalloc(key_layer->key_size, GFP_KERNEL); + if (!flow_pay->mask_data) + goto err_free_unmasked; + + flow_pay->action_data = kmalloc(NFP_FL_MAX_A_SIZ, GFP_KERNEL); + if (!flow_pay->action_data) + goto err_free_mask; + + flow_pay->meta.flags = 0; + spin_lock_init(&flow_pay->lock); + + return flow_pay; + +err_free_mask: + kfree(flow_pay->mask_data); +err_free_unmasked: + kfree(flow_pay->unmasked_data); +err_free_flow: + kfree(flow_pay); + return NULL; +} + +/** + * nfp_flower_add_offload() - Adds a new flow to hardware. + * @app: Pointer to the APP handle + * @netdev: netdev structure. + * @flow: TC flower classifier offload structure. + * + * Adds a new flow to the repeated hash structure and action payload. + * + * Return: negative value on error, 0 if configured successfully. + */ +static int +nfp_flower_add_offload(struct nfp_app *app, struct net_device *netdev, + struct tc_cls_flower_offload *flow) +{ + struct nfp_flower_priv *priv = app->priv; + struct nfp_fl_payload *flow_pay; + struct nfp_fl_key_ls *key_layer; + int err; + + key_layer = kmalloc(sizeof(*key_layer), GFP_KERNEL); + if (!key_layer) + return -ENOMEM; + + err = nfp_flower_calculate_key_layers(key_layer, flow); + if (err) + goto err_free_key_ls; + + flow_pay = nfp_flower_allocate_new(key_layer); + if (!flow_pay) { + err = -ENOMEM; + goto err_free_key_ls; + } + + err = nfp_flower_compile_flow_match(flow, key_layer, netdev, flow_pay); + if (err) + goto err_destroy_flow; + + err = nfp_flower_compile_action(flow, netdev, flow_pay); + if (err) + goto err_destroy_flow; + + err = nfp_compile_flow_metadata(app, flow, flow_pay); + if (err) + goto err_destroy_flow; + + err = nfp_flower_xmit_flow(netdev, flow_pay, + NFP_FLOWER_CMSG_TYPE_FLOW_ADD); + if (err) + goto err_destroy_flow; + + INIT_HLIST_NODE(&flow_pay->link); + flow_pay->tc_flower_cookie = flow->cookie; + hash_add_rcu(priv->flow_table, &flow_pay->link, flow->cookie); + + /* Deallocate flow payload when flower rule has been destroyed. */ + kfree(key_layer); + + return 0; + +err_destroy_flow: + kfree(flow_pay->action_data); + kfree(flow_pay->mask_data); + kfree(flow_pay->unmasked_data); + kfree(flow_pay); +err_free_key_ls: + kfree(key_layer); + return err; +} + +/** + * nfp_flower_del_offload() - Removes a flow from hardware. + * @app: Pointer to the APP handle + * @netdev: netdev structure. + * @flow: TC flower classifier offload structure + * + * Removes a flow from the repeated hash structure and clears the + * action payload. + * + * Return: negative value on error, 0 if removed successfully. + */ +static int +nfp_flower_del_offload(struct nfp_app *app, struct net_device *netdev, + struct tc_cls_flower_offload *flow) +{ + struct nfp_fl_payload *nfp_flow; + int err; + + nfp_flow = nfp_flower_search_fl_table(app, flow->cookie); + if (!nfp_flow) + return -ENOENT; + + err = nfp_modify_flow_metadata(app, nfp_flow); + if (err) + goto err_free_flow; + + err = nfp_flower_xmit_flow(netdev, nfp_flow, + NFP_FLOWER_CMSG_TYPE_FLOW_DEL); + if (err) + goto err_free_flow; + +err_free_flow: + hash_del_rcu(&nfp_flow->link); + kfree(nfp_flow->action_data); + kfree(nfp_flow->mask_data); + kfree(nfp_flow->unmasked_data); + kfree_rcu(nfp_flow, rcu); + return err; +} + +/** + * nfp_flower_get_stats() - Populates flow stats obtained from hardware. + * @app: Pointer to the APP handle + * @flow: TC flower classifier offload structure + * + * Populates a flow statistics structure which which corresponds to a + * specific flow. + * + * Return: negative value on error, 0 if stats populated successfully. + */ +static int +nfp_flower_get_stats(struct nfp_app *app, struct tc_cls_flower_offload *flow) +{ + struct nfp_fl_payload *nfp_flow; + + nfp_flow = nfp_flower_search_fl_table(app, flow->cookie); + if (!nfp_flow) + return -EINVAL; + + spin_lock_bh(&nfp_flow->lock); + tcf_exts_stats_update(flow->exts, nfp_flow->stats.bytes, + nfp_flow->stats.pkts, nfp_flow->stats.used); + + nfp_flow->stats.pkts = 0; + nfp_flow->stats.bytes = 0; + spin_unlock_bh(&nfp_flow->lock); + + return 0; +} + +static int +nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev, + struct tc_cls_flower_offload *flower) +{ + switch (flower->command) { + case TC_CLSFLOWER_REPLACE: + return nfp_flower_add_offload(app, netdev, flower); + case TC_CLSFLOWER_DESTROY: + return nfp_flower_del_offload(app, netdev, flower); + case TC_CLSFLOWER_STATS: + return nfp_flower_get_stats(app, flower); + } + + return -EOPNOTSUPP; +} + +int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, + u32 handle, __be16 proto, struct tc_to_netdev *tc) +{ + if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) + return -EOPNOTSUPP; + + if (!eth_proto_is_802_3(proto)) + return -EOPNOTSUPP; + + if (tc->type != TC_SETUP_CLSFLOWER) + return -EINVAL; + + return nfp_flower_repr_offload(app, netdev, tc->cls_flower); +} diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 2e728543e840..30f82b41d400 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -64,6 +64,7 @@ #include <linux/vmalloc.h> #include <linux/ktime.h> +#include <net/switchdev.h> #include <net/vxlan.h> #include "nfpcore/nfp_nsp.h" @@ -3096,18 +3097,6 @@ static void nfp_net_stat64(struct net_device *netdev, } } -static int -nfp_net_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) -{ - struct nfp_net *nn = netdev_priv(netdev); - - if (chain_index) - return -EOPNOTSUPP; - - return nfp_app_setup_tc(nn->app, netdev, handle, proto, tc); -} - static int nfp_net_set_features(struct net_device *netdev, netdev_features_t features) { @@ -3423,7 +3412,7 @@ const struct net_device_ops nfp_net_netdev_ops = { .ndo_get_stats64 = nfp_net_stat64, .ndo_vlan_rx_add_vid = nfp_net_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = nfp_net_vlan_rx_kill_vid, - .ndo_setup_tc = nfp_net_setup_tc, + .ndo_setup_tc = nfp_port_setup_tc, .ndo_tx_timeout = nfp_net_tx_timeout, .ndo_set_rx_mode = nfp_net_set_rx_mode, .ndo_change_mtu = nfp_net_change_mtu, @@ -3703,6 +3692,8 @@ static void nfp_net_netdev_init(struct nfp_net *nn) netdev->netdev_ops = &nfp_net_netdev_ops; netdev->watchdog_timeo = msecs_to_jiffies(5 * 1000); + SWITCHDEV_SET_OPS(netdev, &nfp_port_switchdev_ops); + /* MTU range: 68 - hw-specific max */ netdev->min_mtu = ETH_MIN_MTU; netdev->max_mtu = nn->max_mtu; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c index 046b89eb4cf2..8ec5474f4b18 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c @@ -35,6 +35,7 @@ #include <linux/io-64-nonatomic-hi-lo.h> #include <linux/lockdep.h> #include <net/dst_metadata.h> +#include <net/switchdev.h> #include "nfpcore/nfp_cpp.h" #include "nfpcore/nfp_nsp.h" @@ -257,6 +258,7 @@ const struct net_device_ops nfp_repr_netdev_ops = { .ndo_has_offload_stats = nfp_repr_has_offload_stats, .ndo_get_offload_stats = nfp_repr_get_offload_stats, .ndo_get_phys_port_name = nfp_port_get_phys_port_name, + .ndo_setup_tc = nfp_port_setup_tc, }; static void nfp_repr_clean(struct nfp_repr *repr) @@ -299,6 +301,12 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev, repr->dst->u.port_info.lower_dev = pf_netdev; netdev->netdev_ops = &nfp_repr_netdev_ops; + SWITCHDEV_SET_OPS(netdev, &nfp_port_switchdev_ops); + + if (nfp_app_has_tc(app)) { + netdev->features |= NETIF_F_HW_TC; + netdev->hw_features |= NETIF_F_HW_TC; + } err = register_netdev(netdev); if (err) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.h b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.h index 6a6727816010..32179cad062a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.h @@ -38,6 +38,8 @@ struct metadata_dst; struct nfp_net; struct nfp_port; +#include <net/dst_metadata.h> + /** * struct nfp_reprs - container for representor netdevs * @num_reprs: Number of elements in reprs array @@ -104,6 +106,13 @@ static inline bool nfp_netdev_is_nfp_repr(struct net_device *netdev) return netdev->netdev_ops == &nfp_repr_netdev_ops; } +static inline int nfp_repr_get_port_id(struct net_device *netdev) +{ + struct nfp_repr *priv = netdev_priv(netdev); + + return priv->dst->u.port_info.port_id; +} + void nfp_repr_inc_rx_stats(struct net_device *netdev, unsigned int len); int nfp_repr_init(struct nfp_app *app, struct net_device *netdev, u32 cmsg_port_id, struct nfp_port *port, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.c b/drivers/net/ethernet/netronome/nfp/nfp_port.c index 0b44952945d8..776e54dd5dd0 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.c @@ -32,6 +32,7 @@ */ #include <linux/lockdep.h> +#include <net/switchdev.h> #include "nfpcore/nfp_cpp.h" #include "nfpcore/nfp_nsp.h" @@ -59,6 +60,49 @@ struct nfp_port *nfp_port_from_netdev(struct net_device *netdev) return NULL; } +static int +nfp_port_attr_get(struct net_device *netdev, struct switchdev_attr *attr) +{ + struct nfp_port *port; + + port = nfp_port_from_netdev(netdev); + if (!port) + return -EOPNOTSUPP; + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: { + const u8 *serial; + /* N.B: attr->u.ppid.id is binary data */ + attr->u.ppid.id_len = nfp_cpp_serial(port->app->cpp, &serial); + memcpy(&attr->u.ppid.id, serial, attr->u.ppid.id_len); + break; + } + default: + return -EOPNOTSUPP; + } + + return 0; +} + +const struct switchdev_ops nfp_port_switchdev_ops = { + .switchdev_port_attr_get = nfp_port_attr_get, +}; + +int nfp_port_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, + __be16 proto, struct tc_to_netdev *tc) +{ + struct nfp_port *port; + + if (chain_index) + return -EOPNOTSUPP; + + port = nfp_port_from_netdev(netdev); + if (!port) + return -EOPNOTSUPP; + + return nfp_app_setup_tc(port->app, netdev, handle, proto, tc); +} + struct nfp_port * nfp_port_from_id(struct nfp_pf *pf, enum nfp_port_type type, unsigned int id) { diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index 57d852a4ca59..a33d22e18f94 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -36,6 +36,7 @@ #include <net/devlink.h> +struct tc_to_netdev; struct net_device; struct nfp_app; struct nfp_pf; @@ -106,6 +107,11 @@ struct nfp_port { struct list_head port_list; }; +extern const struct switchdev_ops nfp_port_switchdev_ops; + +int nfp_port_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, + __be16 proto, struct tc_to_netdev *tc); + struct nfp_port *nfp_port_from_netdev(struct net_device *netdev); struct nfp_port * nfp_port_from_id(struct nfp_pf *pf, enum nfp_port_type type, unsigned int id); |