196de2506SJakub Kicinski // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
296de2506SJakub Kicinski /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
3fd0dd1abSJohn Hurley
4fd0dd1abSJohn Hurley #include <linux/etherdevice.h>
52d9ad71aSJohn Hurley #include <linux/inetdevice.h>
68e6a9046SJohn Hurley #include <net/netevent.h>
7fd0dd1abSJohn Hurley #include <linux/idr.h>
8fd0dd1abSJohn Hurley #include <net/dst_metadata.h>
9856f5b13SJohn Hurley #include <net/arp.h>
10fd0dd1abSJohn Hurley
11fd0dd1abSJohn Hurley #include "cmsg.h"
12fd0dd1abSJohn Hurley #include "main.h"
13fd0dd1abSJohn Hurley #include "../nfp_net_repr.h"
14fd0dd1abSJohn Hurley #include "../nfp_net.h"
15fd0dd1abSJohn Hurley
16856f5b13SJohn Hurley #define NFP_FL_MAX_ROUTES 32
17856f5b13SJohn Hurley
18f12725d9SJohn Hurley #define NFP_TUN_PRE_TUN_RULE_LIMIT 32
195c4f5e19SLouis Peens #define NFP_TUN_PRE_TUN_RULE_DEL BIT(0)
205c4f5e19SLouis Peens #define NFP_TUN_PRE_TUN_IDX_BIT BIT(3)
215c4f5e19SLouis Peens #define NFP_TUN_PRE_TUN_IPV6_BIT BIT(7)
22f12725d9SJohn Hurley
23f12725d9SJohn Hurley /**
24a507b164SYang Shen * struct nfp_tun_pre_tun_rule - rule matched before decap
25f12725d9SJohn Hurley * @flags: options for the rule offset
26f12725d9SJohn Hurley * @port_idx: index of destination MAC address for the rule
27f12725d9SJohn Hurley * @vlan_tci: VLAN info associated with MAC
28f12725d9SJohn Hurley * @host_ctx_id: stats context of rule to update
29f12725d9SJohn Hurley */
30f12725d9SJohn Hurley struct nfp_tun_pre_tun_rule {
31f12725d9SJohn Hurley __be32 flags;
32f12725d9SJohn Hurley __be16 port_idx;
33f12725d9SJohn Hurley __be16 vlan_tci;
34f12725d9SJohn Hurley __be32 host_ctx_id;
35f12725d9SJohn Hurley };
36f12725d9SJohn Hurley
37856f5b13SJohn Hurley /**
38856f5b13SJohn Hurley * struct nfp_tun_active_tuns - periodic message of active tunnels
39856f5b13SJohn Hurley * @seq: sequence number of the message
40856f5b13SJohn Hurley * @count: number of tunnels report in message
41856f5b13SJohn Hurley * @flags: options part of the request
42703f578aSJakub Kicinski * @tun_info.ipv4: dest IPv4 address of active route
43703f578aSJakub Kicinski * @tun_info.egress_port: port the encapsulated packet egressed
44703f578aSJakub Kicinski * @tun_info.extra: reserved for future use
45856f5b13SJohn Hurley * @tun_info: tunnels that have sent traffic in reported period
46856f5b13SJohn Hurley */
47856f5b13SJohn Hurley struct nfp_tun_active_tuns {
48856f5b13SJohn Hurley __be32 seq;
49856f5b13SJohn Hurley __be32 count;
50856f5b13SJohn Hurley __be32 flags;
51856f5b13SJohn Hurley struct route_ip_info {
52856f5b13SJohn Hurley __be32 ipv4;
53856f5b13SJohn Hurley __be32 egress_port;
54856f5b13SJohn Hurley __be32 extra[2];
55856f5b13SJohn Hurley } tun_info[];
56856f5b13SJohn Hurley };
57856f5b13SJohn Hurley
588e6a9046SJohn Hurley /**
599ea9bfa1SJohn Hurley * struct nfp_tun_active_tuns_v6 - periodic message of active IPv6 tunnels
609ea9bfa1SJohn Hurley * @seq: sequence number of the message
619ea9bfa1SJohn Hurley * @count: number of tunnels report in message
629ea9bfa1SJohn Hurley * @flags: options part of the request
639ea9bfa1SJohn Hurley * @tun_info.ipv6: dest IPv6 address of active route
649ea9bfa1SJohn Hurley * @tun_info.egress_port: port the encapsulated packet egressed
65f614e536SLouis Peens * @tun_info.extra: reserved for future use
669ea9bfa1SJohn Hurley * @tun_info: tunnels that have sent traffic in reported period
679ea9bfa1SJohn Hurley */
689ea9bfa1SJohn Hurley struct nfp_tun_active_tuns_v6 {
699ea9bfa1SJohn Hurley __be32 seq;
709ea9bfa1SJohn Hurley __be32 count;
719ea9bfa1SJohn Hurley __be32 flags;
729ea9bfa1SJohn Hurley struct route_ip_info_v6 {
739ea9bfa1SJohn Hurley struct in6_addr ipv6;
749ea9bfa1SJohn Hurley __be32 egress_port;
75f614e536SLouis Peens __be32 extra[2];
769ea9bfa1SJohn Hurley } tun_info[];
779ea9bfa1SJohn Hurley };
789ea9bfa1SJohn Hurley
799ea9bfa1SJohn Hurley /**
808e6a9046SJohn Hurley * struct nfp_tun_req_route_ipv4 - NFP requests a route/neighbour lookup
818e6a9046SJohn Hurley * @ingress_port: ingress port of packet that signalled request
828e6a9046SJohn Hurley * @ipv4_addr: destination ipv4 address for route
838e6a9046SJohn Hurley * @reserved: reserved for future use
848e6a9046SJohn Hurley */
858e6a9046SJohn Hurley struct nfp_tun_req_route_ipv4 {
868e6a9046SJohn Hurley __be32 ingress_port;
878e6a9046SJohn Hurley __be32 ipv4_addr;
888e6a9046SJohn Hurley __be32 reserved[2];
898e6a9046SJohn Hurley };
908e6a9046SJohn Hurley
918e6a9046SJohn Hurley /**
9250b1c86aSJohn Hurley * struct nfp_tun_req_route_ipv6 - NFP requests an IPv6 route/neighbour lookup
9350b1c86aSJohn Hurley * @ingress_port: ingress port of packet that signalled request
9450b1c86aSJohn Hurley * @ipv6_addr: destination ipv6 address for route
9550b1c86aSJohn Hurley */
9650b1c86aSJohn Hurley struct nfp_tun_req_route_ipv6 {
9750b1c86aSJohn Hurley __be32 ingress_port;
9850b1c86aSJohn Hurley struct in6_addr ipv6_addr;
9950b1c86aSJohn Hurley };
10050b1c86aSJohn Hurley
10150b1c86aSJohn Hurley /**
1026c463a05SJohn Hurley * struct nfp_offloaded_route - routes that are offloaded to the NFP
1038e6a9046SJohn Hurley * @list: list pointer
1046c463a05SJohn Hurley * @ip_add: destination of route - can be IPv4 or IPv6
1058e6a9046SJohn Hurley */
1066c463a05SJohn Hurley struct nfp_offloaded_route {
1078e6a9046SJohn Hurley struct list_head list;
1086c463a05SJohn Hurley u8 ip_add[];
1098e6a9046SJohn Hurley };
1108e6a9046SJohn Hurley
1112d9ad71aSJohn Hurley #define NFP_FL_IPV4_ADDRS_MAX 32
1122d9ad71aSJohn Hurley
1132d9ad71aSJohn Hurley /**
1142d9ad71aSJohn Hurley * struct nfp_tun_ipv4_addr - set the IP address list on the NFP
1152d9ad71aSJohn Hurley * @count: number of IPs populated in the array
1162d9ad71aSJohn Hurley * @ipv4_addr: array of IPV4_ADDRS_MAX 32 bit IPv4 addresses
1172d9ad71aSJohn Hurley */
1182d9ad71aSJohn Hurley struct nfp_tun_ipv4_addr {
1192d9ad71aSJohn Hurley __be32 count;
1202d9ad71aSJohn Hurley __be32 ipv4_addr[NFP_FL_IPV4_ADDRS_MAX];
1212d9ad71aSJohn Hurley };
1222d9ad71aSJohn Hurley
1232d9ad71aSJohn Hurley /**
1242d9ad71aSJohn Hurley * struct nfp_ipv4_addr_entry - cached IPv4 addresses
1252d9ad71aSJohn Hurley * @ipv4_addr: IP address
1262d9ad71aSJohn Hurley * @ref_count: number of rules currently using this IP
1272d9ad71aSJohn Hurley * @list: list pointer
1282d9ad71aSJohn Hurley */
1292d9ad71aSJohn Hurley struct nfp_ipv4_addr_entry {
1302d9ad71aSJohn Hurley __be32 ipv4_addr;
1312d9ad71aSJohn Hurley int ref_count;
1322d9ad71aSJohn Hurley struct list_head list;
1332d9ad71aSJohn Hurley };
1342d9ad71aSJohn Hurley
135cfa18993SJohn Hurley #define NFP_FL_IPV6_ADDRS_MAX 4
136cfa18993SJohn Hurley
137cfa18993SJohn Hurley /**
138cfa18993SJohn Hurley * struct nfp_tun_ipv6_addr - set the IP address list on the NFP
139cfa18993SJohn Hurley * @count: number of IPs populated in the array
140cfa18993SJohn Hurley * @ipv6_addr: array of IPV6_ADDRS_MAX 128 bit IPv6 addresses
141cfa18993SJohn Hurley */
142cfa18993SJohn Hurley struct nfp_tun_ipv6_addr {
143cfa18993SJohn Hurley __be32 count;
144cfa18993SJohn Hurley struct in6_addr ipv6_addr[NFP_FL_IPV6_ADDRS_MAX];
145cfa18993SJohn Hurley };
146cfa18993SJohn Hurley
14749402b0bSJohn Hurley #define NFP_TUN_MAC_OFFLOAD_DEL_FLAG 0x2
14849402b0bSJohn Hurley
149fd0dd1abSJohn Hurley /**
1500115dcc3SJohn Hurley * struct nfp_tun_mac_addr_offload - configure MAC address of tunnel EP on NFP
15149402b0bSJohn Hurley * @flags: MAC address offload options
1520115dcc3SJohn Hurley * @count: number of MAC addresses in the message (should be 1)
1530115dcc3SJohn Hurley * @index: index of MAC address in the lookup table
1540115dcc3SJohn Hurley * @addr: interface MAC address
155fd0dd1abSJohn Hurley */
1560115dcc3SJohn Hurley struct nfp_tun_mac_addr_offload {
15749402b0bSJohn Hurley __be16 flags;
158fd0dd1abSJohn Hurley __be16 count;
159fd0dd1abSJohn Hurley __be16 index;
160fd0dd1abSJohn Hurley u8 addr[ETH_ALEN];
161fd0dd1abSJohn Hurley };
162fd0dd1abSJohn Hurley
1639a89aad0SHui Zhou /**
1649a89aad0SHui Zhou * struct nfp_neigh_update_work - update neighbour information to nfp
1659a89aad0SHui Zhou * @work: Work queue for writing neigh to the nfp
1669a89aad0SHui Zhou * @n: neighbour entry
1679a89aad0SHui Zhou * @app: Back pointer to app
1689a89aad0SHui Zhou */
1699a89aad0SHui Zhou struct nfp_neigh_update_work {
1709a89aad0SHui Zhou struct work_struct work;
1719a89aad0SHui Zhou struct neighbour *n;
1729a89aad0SHui Zhou struct nfp_app *app;
1739a89aad0SHui Zhou };
1749a89aad0SHui Zhou
17549402b0bSJohn Hurley enum nfp_flower_mac_offload_cmd {
17649402b0bSJohn Hurley NFP_TUNNEL_MAC_OFFLOAD_ADD = 0,
17749402b0bSJohn Hurley NFP_TUNNEL_MAC_OFFLOAD_DEL = 1,
17813cf7103SJohn Hurley NFP_TUNNEL_MAC_OFFLOAD_MOD = 2,
17949402b0bSJohn Hurley };
18049402b0bSJohn Hurley
181fd0dd1abSJohn Hurley #define NFP_MAX_MAC_INDEX 0xff
182fd0dd1abSJohn Hurley
183fd0dd1abSJohn Hurley /**
18420cce886SJohn Hurley * struct nfp_tun_offloaded_mac - hashtable entry for an offloaded MAC
18520cce886SJohn Hurley * @ht_node: Hashtable entry
18620cce886SJohn Hurley * @addr: Offloaded MAC address
18720cce886SJohn Hurley * @index: Offloaded index for given MAC address
18820cce886SJohn Hurley * @ref_count: Number of devs using this MAC address
18920cce886SJohn Hurley * @repr_list: List of reprs sharing this MAC address
1902e0bc7f3SJohn Hurley * @bridge_count: Number of bridge/internal devs with MAC
191fd0dd1abSJohn Hurley */
19220cce886SJohn Hurley struct nfp_tun_offloaded_mac {
19320cce886SJohn Hurley struct rhash_head ht_node;
19420cce886SJohn Hurley u8 addr[ETH_ALEN];
19520cce886SJohn Hurley u16 index;
19620cce886SJohn Hurley int ref_count;
19720cce886SJohn Hurley struct list_head repr_list;
1982e0bc7f3SJohn Hurley int bridge_count;
19920cce886SJohn Hurley };
20020cce886SJohn Hurley
20120cce886SJohn Hurley static const struct rhashtable_params offloaded_macs_params = {
20220cce886SJohn Hurley .key_offset = offsetof(struct nfp_tun_offloaded_mac, addr),
20320cce886SJohn Hurley .head_offset = offsetof(struct nfp_tun_offloaded_mac, ht_node),
20420cce886SJohn Hurley .key_len = ETH_ALEN,
20520cce886SJohn Hurley .automatic_shrinking = true,
206fd0dd1abSJohn Hurley };
207fd0dd1abSJohn Hurley
nfp_tunnel_keep_alive(struct nfp_app * app,struct sk_buff * skb)208856f5b13SJohn Hurley void nfp_tunnel_keep_alive(struct nfp_app *app, struct sk_buff *skb)
209856f5b13SJohn Hurley {
210856f5b13SJohn Hurley struct nfp_tun_active_tuns *payload;
211856f5b13SJohn Hurley struct net_device *netdev;
212856f5b13SJohn Hurley int count, i, pay_len;
213856f5b13SJohn Hurley struct neighbour *n;
214856f5b13SJohn Hurley __be32 ipv4_addr;
215856f5b13SJohn Hurley u32 port;
216856f5b13SJohn Hurley
217856f5b13SJohn Hurley payload = nfp_flower_cmsg_get_data(skb);
218856f5b13SJohn Hurley count = be32_to_cpu(payload->count);
219856f5b13SJohn Hurley if (count > NFP_FL_MAX_ROUTES) {
220856f5b13SJohn Hurley nfp_flower_cmsg_warn(app, "Tunnel keep-alive request exceeds max routes.\n");
221856f5b13SJohn Hurley return;
222856f5b13SJohn Hurley }
223856f5b13SJohn Hurley
224856f5b13SJohn Hurley pay_len = nfp_flower_cmsg_get_data_len(skb);
225856e6d9fSGustavo A. R. Silva if (pay_len != struct_size(payload, tun_info, count)) {
226856f5b13SJohn Hurley nfp_flower_cmsg_warn(app, "Corruption in tunnel keep-alive message.\n");
227856f5b13SJohn Hurley return;
228856f5b13SJohn Hurley }
229856f5b13SJohn Hurley
230cb07d915SPieter Jansen van Vuuren rcu_read_lock();
231856f5b13SJohn Hurley for (i = 0; i < count; i++) {
232856f5b13SJohn Hurley ipv4_addr = payload->tun_info[i].ipv4;
233856f5b13SJohn Hurley port = be32_to_cpu(payload->tun_info[i].egress_port);
23427f54b58SJohn Hurley netdev = nfp_app_dev_get(app, port, NULL);
235856f5b13SJohn Hurley if (!netdev)
236856f5b13SJohn Hurley continue;
237856f5b13SJohn Hurley
238856f5b13SJohn Hurley n = neigh_lookup(&arp_tbl, &ipv4_addr, netdev);
239856f5b13SJohn Hurley if (!n)
240856f5b13SJohn Hurley continue;
241856f5b13SJohn Hurley
242856f5b13SJohn Hurley /* Update the used timestamp of neighbour */
243856f5b13SJohn Hurley neigh_event_send(n, NULL);
244856f5b13SJohn Hurley neigh_release(n);
245856f5b13SJohn Hurley }
246cb07d915SPieter Jansen van Vuuren rcu_read_unlock();
247856f5b13SJohn Hurley }
248856f5b13SJohn Hurley
nfp_tunnel_keep_alive_v6(struct nfp_app * app,struct sk_buff * skb)2499ea9bfa1SJohn Hurley void nfp_tunnel_keep_alive_v6(struct nfp_app *app, struct sk_buff *skb)
2509ea9bfa1SJohn Hurley {
2519ea9bfa1SJohn Hurley #if IS_ENABLED(CONFIG_IPV6)
2529ea9bfa1SJohn Hurley struct nfp_tun_active_tuns_v6 *payload;
2539ea9bfa1SJohn Hurley struct net_device *netdev;
2549ea9bfa1SJohn Hurley int count, i, pay_len;
2559ea9bfa1SJohn Hurley struct neighbour *n;
2569ea9bfa1SJohn Hurley void *ipv6_add;
2579ea9bfa1SJohn Hurley u32 port;
2589ea9bfa1SJohn Hurley
2599ea9bfa1SJohn Hurley payload = nfp_flower_cmsg_get_data(skb);
2609ea9bfa1SJohn Hurley count = be32_to_cpu(payload->count);
2619ea9bfa1SJohn Hurley if (count > NFP_FL_IPV6_ADDRS_MAX) {
2629ea9bfa1SJohn Hurley nfp_flower_cmsg_warn(app, "IPv6 tunnel keep-alive request exceeds max routes.\n");
2639ea9bfa1SJohn Hurley return;
2649ea9bfa1SJohn Hurley }
2659ea9bfa1SJohn Hurley
2669ea9bfa1SJohn Hurley pay_len = nfp_flower_cmsg_get_data_len(skb);
2679ea9bfa1SJohn Hurley if (pay_len != struct_size(payload, tun_info, count)) {
2689ea9bfa1SJohn Hurley nfp_flower_cmsg_warn(app, "Corruption in tunnel keep-alive message.\n");
2699ea9bfa1SJohn Hurley return;
2709ea9bfa1SJohn Hurley }
2719ea9bfa1SJohn Hurley
2729ea9bfa1SJohn Hurley rcu_read_lock();
2739ea9bfa1SJohn Hurley for (i = 0; i < count; i++) {
2749ea9bfa1SJohn Hurley ipv6_add = &payload->tun_info[i].ipv6;
2759ea9bfa1SJohn Hurley port = be32_to_cpu(payload->tun_info[i].egress_port);
2769ea9bfa1SJohn Hurley netdev = nfp_app_dev_get(app, port, NULL);
2779ea9bfa1SJohn Hurley if (!netdev)
2789ea9bfa1SJohn Hurley continue;
2799ea9bfa1SJohn Hurley
2809ea9bfa1SJohn Hurley n = neigh_lookup(&nd_tbl, ipv6_add, netdev);
2819ea9bfa1SJohn Hurley if (!n)
2829ea9bfa1SJohn Hurley continue;
2839ea9bfa1SJohn Hurley
2849ea9bfa1SJohn Hurley /* Update the used timestamp of neighbour */
2859ea9bfa1SJohn Hurley neigh_event_send(n, NULL);
2869ea9bfa1SJohn Hurley neigh_release(n);
2879ea9bfa1SJohn Hurley }
2889ea9bfa1SJohn Hurley rcu_read_unlock();
2899ea9bfa1SJohn Hurley #endif
2909ea9bfa1SJohn Hurley }
2919ea9bfa1SJohn Hurley
292fd0dd1abSJohn Hurley static int
nfp_flower_xmit_tun_conf(struct nfp_app * app,u8 mtype,u16 plen,void * pdata,gfp_t flag)293dc4646a9SJohn Hurley nfp_flower_xmit_tun_conf(struct nfp_app *app, u8 mtype, u16 plen, void *pdata,
294dc4646a9SJohn Hurley gfp_t flag)
295fd0dd1abSJohn Hurley {
2969ee7c421SLouis Peens struct nfp_flower_priv *priv = app->priv;
297fd0dd1abSJohn Hurley struct sk_buff *skb;
298fd0dd1abSJohn Hurley unsigned char *msg;
299fd0dd1abSJohn Hurley
3009ee7c421SLouis Peens if (!(priv->flower_ext_feats & NFP_FL_FEATS_DECAP_V2) &&
3019ee7c421SLouis Peens (mtype == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH ||
3029ee7c421SLouis Peens mtype == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6))
3039ee7c421SLouis Peens plen -= sizeof(struct nfp_tun_neigh_ext);
3049ee7c421SLouis Peens
305abc21095SYanguo Li if (!(priv->flower_ext_feats & NFP_FL_FEATS_TUNNEL_NEIGH_LAG) &&
306abc21095SYanguo Li (mtype == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH ||
307abc21095SYanguo Li mtype == NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6))
308abc21095SYanguo Li plen -= sizeof(struct nfp_tun_neigh_lag);
309abc21095SYanguo Li
310dc4646a9SJohn Hurley skb = nfp_flower_cmsg_alloc(app, plen, mtype, flag);
311fd0dd1abSJohn Hurley if (!skb)
312fd0dd1abSJohn Hurley return -ENOMEM;
313fd0dd1abSJohn Hurley
314fd0dd1abSJohn Hurley msg = nfp_flower_cmsg_get_data(skb);
315fd0dd1abSJohn Hurley memcpy(msg, pdata, nfp_flower_cmsg_get_data_len(skb));
316fd0dd1abSJohn Hurley
317fd0dd1abSJohn Hurley nfp_ctrl_tx(app->ctrl, skb);
318fd0dd1abSJohn Hurley return 0;
319fd0dd1abSJohn Hurley }
320fd0dd1abSJohn Hurley
321591c90a1SLouis Peens static void
nfp_tun_mutual_link(struct nfp_predt_entry * predt,struct nfp_neigh_entry * neigh)322591c90a1SLouis Peens nfp_tun_mutual_link(struct nfp_predt_entry *predt,
323591c90a1SLouis Peens struct nfp_neigh_entry *neigh)
324591c90a1SLouis Peens {
325591c90a1SLouis Peens struct nfp_fl_payload *flow_pay = predt->flow_pay;
326591c90a1SLouis Peens struct nfp_tun_neigh_ext *ext;
327591c90a1SLouis Peens struct nfp_tun_neigh *common;
328591c90a1SLouis Peens
329591c90a1SLouis Peens if (flow_pay->pre_tun_rule.is_ipv6 != neigh->is_ipv6)
330591c90a1SLouis Peens return;
331591c90a1SLouis Peens
332591c90a1SLouis Peens /* In the case of bonding it is possible that there might already
333591c90a1SLouis Peens * be a flow linked (as the MAC address gets shared). If a flow
334591c90a1SLouis Peens * is already linked just return.
335591c90a1SLouis Peens */
336591c90a1SLouis Peens if (neigh->flow)
337591c90a1SLouis Peens return;
338591c90a1SLouis Peens
339591c90a1SLouis Peens common = neigh->is_ipv6 ?
340591c90a1SLouis Peens &((struct nfp_tun_neigh_v6 *)neigh->payload)->common :
341591c90a1SLouis Peens &((struct nfp_tun_neigh_v4 *)neigh->payload)->common;
342591c90a1SLouis Peens ext = neigh->is_ipv6 ?
343591c90a1SLouis Peens &((struct nfp_tun_neigh_v6 *)neigh->payload)->ext :
344591c90a1SLouis Peens &((struct nfp_tun_neigh_v4 *)neigh->payload)->ext;
345591c90a1SLouis Peens
346591c90a1SLouis Peens if (memcmp(flow_pay->pre_tun_rule.loc_mac,
347591c90a1SLouis Peens common->src_addr, ETH_ALEN) ||
348591c90a1SLouis Peens memcmp(flow_pay->pre_tun_rule.rem_mac,
349591c90a1SLouis Peens common->dst_addr, ETH_ALEN))
350591c90a1SLouis Peens return;
351591c90a1SLouis Peens
352591c90a1SLouis Peens list_add(&neigh->list_head, &predt->nn_list);
353591c90a1SLouis Peens neigh->flow = predt;
354591c90a1SLouis Peens ext->host_ctx = flow_pay->meta.host_ctx_id;
355591c90a1SLouis Peens ext->vlan_tci = flow_pay->pre_tun_rule.vlan_tci;
356591c90a1SLouis Peens ext->vlan_tpid = flow_pay->pre_tun_rule.vlan_tpid;
357591c90a1SLouis Peens }
358591c90a1SLouis Peens
359591c90a1SLouis Peens static void
nfp_tun_link_predt_entries(struct nfp_app * app,struct nfp_neigh_entry * nn_entry)360591c90a1SLouis Peens nfp_tun_link_predt_entries(struct nfp_app *app,
361591c90a1SLouis Peens struct nfp_neigh_entry *nn_entry)
362591c90a1SLouis Peens {
363591c90a1SLouis Peens struct nfp_flower_priv *priv = app->priv;
364591c90a1SLouis Peens struct nfp_predt_entry *predt, *tmp;
365591c90a1SLouis Peens
366591c90a1SLouis Peens list_for_each_entry_safe(predt, tmp, &priv->predt_list, list_head) {
367591c90a1SLouis Peens nfp_tun_mutual_link(predt, nn_entry);
368591c90a1SLouis Peens }
369591c90a1SLouis Peens }
370591c90a1SLouis Peens
nfp_tun_link_and_update_nn_entries(struct nfp_app * app,struct nfp_predt_entry * predt)371591c90a1SLouis Peens void nfp_tun_link_and_update_nn_entries(struct nfp_app *app,
372591c90a1SLouis Peens struct nfp_predt_entry *predt)
373591c90a1SLouis Peens {
374591c90a1SLouis Peens struct nfp_flower_priv *priv = app->priv;
375591c90a1SLouis Peens struct nfp_neigh_entry *nn_entry;
376591c90a1SLouis Peens struct rhashtable_iter iter;
377591c90a1SLouis Peens size_t neigh_size;
378591c90a1SLouis Peens u8 type;
379591c90a1SLouis Peens
380591c90a1SLouis Peens rhashtable_walk_enter(&priv->neigh_table, &iter);
381591c90a1SLouis Peens rhashtable_walk_start(&iter);
382591c90a1SLouis Peens while ((nn_entry = rhashtable_walk_next(&iter)) != NULL) {
383591c90a1SLouis Peens if (IS_ERR(nn_entry))
384591c90a1SLouis Peens continue;
385591c90a1SLouis Peens nfp_tun_mutual_link(predt, nn_entry);
386591c90a1SLouis Peens neigh_size = nn_entry->is_ipv6 ?
387591c90a1SLouis Peens sizeof(struct nfp_tun_neigh_v6) :
388591c90a1SLouis Peens sizeof(struct nfp_tun_neigh_v4);
389591c90a1SLouis Peens type = nn_entry->is_ipv6 ? NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6 :
390591c90a1SLouis Peens NFP_FLOWER_CMSG_TYPE_TUN_NEIGH;
391591c90a1SLouis Peens nfp_flower_xmit_tun_conf(app, type, neigh_size,
392591c90a1SLouis Peens nn_entry->payload,
393591c90a1SLouis Peens GFP_ATOMIC);
394591c90a1SLouis Peens }
395591c90a1SLouis Peens rhashtable_walk_stop(&iter);
396591c90a1SLouis Peens rhashtable_walk_exit(&iter);
397591c90a1SLouis Peens }
398591c90a1SLouis Peens
nfp_tun_cleanup_nn_entries(struct nfp_app * app)399591c90a1SLouis Peens static void nfp_tun_cleanup_nn_entries(struct nfp_app *app)
400591c90a1SLouis Peens {
401591c90a1SLouis Peens struct nfp_flower_priv *priv = app->priv;
402591c90a1SLouis Peens struct nfp_neigh_entry *neigh;
403591c90a1SLouis Peens struct nfp_tun_neigh_ext *ext;
404591c90a1SLouis Peens struct rhashtable_iter iter;
405591c90a1SLouis Peens size_t neigh_size;
406591c90a1SLouis Peens u8 type;
407591c90a1SLouis Peens
408591c90a1SLouis Peens rhashtable_walk_enter(&priv->neigh_table, &iter);
409591c90a1SLouis Peens rhashtable_walk_start(&iter);
410591c90a1SLouis Peens while ((neigh = rhashtable_walk_next(&iter)) != NULL) {
411591c90a1SLouis Peens if (IS_ERR(neigh))
412591c90a1SLouis Peens continue;
413591c90a1SLouis Peens ext = neigh->is_ipv6 ?
414591c90a1SLouis Peens &((struct nfp_tun_neigh_v6 *)neigh->payload)->ext :
415591c90a1SLouis Peens &((struct nfp_tun_neigh_v4 *)neigh->payload)->ext;
416591c90a1SLouis Peens ext->host_ctx = cpu_to_be32(U32_MAX);
417591c90a1SLouis Peens ext->vlan_tpid = cpu_to_be16(U16_MAX);
418591c90a1SLouis Peens ext->vlan_tci = cpu_to_be16(U16_MAX);
419591c90a1SLouis Peens
420591c90a1SLouis Peens neigh_size = neigh->is_ipv6 ?
421591c90a1SLouis Peens sizeof(struct nfp_tun_neigh_v6) :
422591c90a1SLouis Peens sizeof(struct nfp_tun_neigh_v4);
423591c90a1SLouis Peens type = neigh->is_ipv6 ? NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6 :
424591c90a1SLouis Peens NFP_FLOWER_CMSG_TYPE_TUN_NEIGH;
425591c90a1SLouis Peens nfp_flower_xmit_tun_conf(app, type, neigh_size, neigh->payload,
426591c90a1SLouis Peens GFP_ATOMIC);
427591c90a1SLouis Peens
428591c90a1SLouis Peens rhashtable_remove_fast(&priv->neigh_table, &neigh->ht_node,
429591c90a1SLouis Peens neigh_table_params);
430591c90a1SLouis Peens if (neigh->flow)
431591c90a1SLouis Peens list_del(&neigh->list_head);
432591c90a1SLouis Peens kfree(neigh);
433591c90a1SLouis Peens }
434591c90a1SLouis Peens rhashtable_walk_stop(&iter);
435591c90a1SLouis Peens rhashtable_walk_exit(&iter);
436591c90a1SLouis Peens }
437591c90a1SLouis Peens
nfp_tun_unlink_and_update_nn_entries(struct nfp_app * app,struct nfp_predt_entry * predt)438591c90a1SLouis Peens void nfp_tun_unlink_and_update_nn_entries(struct nfp_app *app,
439591c90a1SLouis Peens struct nfp_predt_entry *predt)
440591c90a1SLouis Peens {
441591c90a1SLouis Peens struct nfp_neigh_entry *neigh, *tmp;
442591c90a1SLouis Peens struct nfp_tun_neigh_ext *ext;
443591c90a1SLouis Peens size_t neigh_size;
444591c90a1SLouis Peens u8 type;
445591c90a1SLouis Peens
446591c90a1SLouis Peens list_for_each_entry_safe(neigh, tmp, &predt->nn_list, list_head) {
447591c90a1SLouis Peens ext = neigh->is_ipv6 ?
448591c90a1SLouis Peens &((struct nfp_tun_neigh_v6 *)neigh->payload)->ext :
449591c90a1SLouis Peens &((struct nfp_tun_neigh_v4 *)neigh->payload)->ext;
450591c90a1SLouis Peens neigh->flow = NULL;
451591c90a1SLouis Peens ext->host_ctx = cpu_to_be32(U32_MAX);
452591c90a1SLouis Peens ext->vlan_tpid = cpu_to_be16(U16_MAX);
453591c90a1SLouis Peens ext->vlan_tci = cpu_to_be16(U16_MAX);
454591c90a1SLouis Peens list_del(&neigh->list_head);
455591c90a1SLouis Peens neigh_size = neigh->is_ipv6 ?
456591c90a1SLouis Peens sizeof(struct nfp_tun_neigh_v6) :
457591c90a1SLouis Peens sizeof(struct nfp_tun_neigh_v4);
458591c90a1SLouis Peens type = neigh->is_ipv6 ? NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6 :
459591c90a1SLouis Peens NFP_FLOWER_CMSG_TYPE_TUN_NEIGH;
460591c90a1SLouis Peens nfp_flower_xmit_tun_conf(app, type, neigh_size, neigh->payload,
461591c90a1SLouis Peens GFP_ATOMIC);
462591c90a1SLouis Peens }
463591c90a1SLouis Peens }
464591c90a1SLouis Peens
4658e6a9046SJohn Hurley static void
nfp_tun_write_neigh(struct net_device * netdev,struct nfp_app * app,void * flow,struct neighbour * neigh,bool is_ipv6,bool override)466f1df7956SLouis Peens nfp_tun_write_neigh(struct net_device *netdev, struct nfp_app *app,
467656bd03aSTianyu Yuan void *flow, struct neighbour *neigh, bool is_ipv6,
468656bd03aSTianyu Yuan bool override)
4698e6a9046SJohn Hurley {
470f1df7956SLouis Peens bool neigh_invalid = !(neigh->nud_state & NUD_VALID) || neigh->dead;
471f1df7956SLouis Peens size_t neigh_size = is_ipv6 ? sizeof(struct nfp_tun_neigh_v6) :
472f1df7956SLouis Peens sizeof(struct nfp_tun_neigh_v4);
473f1df7956SLouis Peens unsigned long cookie = (unsigned long)neigh;
474f1df7956SLouis Peens struct nfp_flower_priv *priv = app->priv;
4759c6b9cbaSYanguo Li struct nfp_tun_neigh_lag lag_info;
476f1df7956SLouis Peens struct nfp_neigh_entry *nn_entry;
47745756dfeSJohn Hurley u32 port_id;
478f1df7956SLouis Peens u8 mtype;
4798e6a9046SJohn Hurley
48045756dfeSJohn Hurley port_id = nfp_flower_get_port_id_from_netdev(app, netdev);
48145756dfeSJohn Hurley if (!port_id)
4828e6a9046SJohn Hurley return;
4838e6a9046SJohn Hurley
4849c6b9cbaSYanguo Li if ((port_id & NFP_FL_LAG_OUT) == NFP_FL_LAG_OUT) {
4859c6b9cbaSYanguo Li memset(&lag_info, 0, sizeof(struct nfp_tun_neigh_lag));
4869c6b9cbaSYanguo Li nfp_flower_lag_get_info_from_netdev(app, netdev, &lag_info);
4879c6b9cbaSYanguo Li }
4889c6b9cbaSYanguo Li
489f1df7956SLouis Peens spin_lock_bh(&priv->predt_lock);
490f1df7956SLouis Peens nn_entry = rhashtable_lookup_fast(&priv->neigh_table, &cookie,
491f1df7956SLouis Peens neigh_table_params);
492f1df7956SLouis Peens if (!nn_entry && !neigh_invalid) {
493f1df7956SLouis Peens struct nfp_tun_neigh_ext *ext;
494abc21095SYanguo Li struct nfp_tun_neigh_lag *lag;
495f1df7956SLouis Peens struct nfp_tun_neigh *common;
4968e6a9046SJohn Hurley
497f1df7956SLouis Peens nn_entry = kzalloc(sizeof(*nn_entry) + neigh_size,
498f1df7956SLouis Peens GFP_ATOMIC);
499f1df7956SLouis Peens if (!nn_entry)
500f1df7956SLouis Peens goto err;
501f1df7956SLouis Peens
502f1df7956SLouis Peens nn_entry->payload = (char *)&nn_entry[1];
503f1df7956SLouis Peens nn_entry->neigh_cookie = cookie;
504f1df7956SLouis Peens nn_entry->is_ipv6 = is_ipv6;
505f1df7956SLouis Peens nn_entry->flow = NULL;
506f1df7956SLouis Peens if (is_ipv6) {
507f1df7956SLouis Peens struct flowi6 *flowi6 = (struct flowi6 *)flow;
508f1df7956SLouis Peens struct nfp_tun_neigh_v6 *payload;
509f1df7956SLouis Peens
510f1df7956SLouis Peens payload = (struct nfp_tun_neigh_v6 *)nn_entry->payload;
511f1df7956SLouis Peens payload->src_ipv6 = flowi6->saddr;
512f1df7956SLouis Peens payload->dst_ipv6 = flowi6->daddr;
513f1df7956SLouis Peens common = &payload->common;
514f1df7956SLouis Peens ext = &payload->ext;
515abc21095SYanguo Li lag = &payload->lag;
516f1df7956SLouis Peens mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6;
517f1df7956SLouis Peens } else {
518f1df7956SLouis Peens struct flowi4 *flowi4 = (struct flowi4 *)flow;
519f1df7956SLouis Peens struct nfp_tun_neigh_v4 *payload;
520f1df7956SLouis Peens
521f1df7956SLouis Peens payload = (struct nfp_tun_neigh_v4 *)nn_entry->payload;
522f1df7956SLouis Peens payload->src_ipv4 = flowi4->saddr;
523f1df7956SLouis Peens payload->dst_ipv4 = flowi4->daddr;
524f1df7956SLouis Peens common = &payload->common;
525f1df7956SLouis Peens ext = &payload->ext;
526abc21095SYanguo Li lag = &payload->lag;
527f1df7956SLouis Peens mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH;
528f1df7956SLouis Peens }
529f1df7956SLouis Peens ext->host_ctx = cpu_to_be32(U32_MAX);
530f1df7956SLouis Peens ext->vlan_tpid = cpu_to_be16(U16_MAX);
531f1df7956SLouis Peens ext->vlan_tci = cpu_to_be16(U16_MAX);
532f1df7956SLouis Peens ether_addr_copy(common->src_addr, netdev->dev_addr);
533f1df7956SLouis Peens neigh_ha_snapshot(common->dst_addr, neigh, netdev);
534abc21095SYanguo Li
535abc21095SYanguo Li if ((port_id & NFP_FL_LAG_OUT) == NFP_FL_LAG_OUT)
5369c6b9cbaSYanguo Li memcpy(lag, &lag_info, sizeof(struct nfp_tun_neigh_lag));
537f1df7956SLouis Peens common->port_id = cpu_to_be32(port_id);
538f1df7956SLouis Peens
539f1df7956SLouis Peens if (rhashtable_insert_fast(&priv->neigh_table,
540f1df7956SLouis Peens &nn_entry->ht_node,
541f1df7956SLouis Peens neigh_table_params))
542f1df7956SLouis Peens goto err;
543f1df7956SLouis Peens
544591c90a1SLouis Peens nfp_tun_link_predt_entries(app, nn_entry);
545f1df7956SLouis Peens nfp_flower_xmit_tun_conf(app, mtype, neigh_size,
546f1df7956SLouis Peens nn_entry->payload,
547f1df7956SLouis Peens GFP_ATOMIC);
548f1df7956SLouis Peens } else if (nn_entry && neigh_invalid) {
549f1df7956SLouis Peens if (is_ipv6) {
550f1df7956SLouis Peens struct flowi6 *flowi6 = (struct flowi6 *)flow;
551f1df7956SLouis Peens struct nfp_tun_neigh_v6 *payload;
552f1df7956SLouis Peens
553f1df7956SLouis Peens payload = (struct nfp_tun_neigh_v6 *)nn_entry->payload;
554f1df7956SLouis Peens memset(payload, 0, sizeof(struct nfp_tun_neigh_v6));
555f1df7956SLouis Peens payload->dst_ipv6 = flowi6->daddr;
556f1df7956SLouis Peens mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6;
557f1df7956SLouis Peens } else {
558f1df7956SLouis Peens struct flowi4 *flowi4 = (struct flowi4 *)flow;
559f1df7956SLouis Peens struct nfp_tun_neigh_v4 *payload;
560f1df7956SLouis Peens
561f1df7956SLouis Peens payload = (struct nfp_tun_neigh_v4 *)nn_entry->payload;
562f1df7956SLouis Peens memset(payload, 0, sizeof(struct nfp_tun_neigh_v4));
563f1df7956SLouis Peens payload->dst_ipv4 = flowi4->daddr;
564f1df7956SLouis Peens mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH;
565f1df7956SLouis Peens }
5668e6a9046SJohn Hurley /* Trigger ARP to verify invalid neighbour state. */
5678e6a9046SJohn Hurley neigh_event_send(neigh, NULL);
568f1df7956SLouis Peens rhashtable_remove_fast(&priv->neigh_table,
569f1df7956SLouis Peens &nn_entry->ht_node,
570f1df7956SLouis Peens neigh_table_params);
571f1df7956SLouis Peens
572f1df7956SLouis Peens nfp_flower_xmit_tun_conf(app, mtype, neigh_size,
573f1df7956SLouis Peens nn_entry->payload,
574f1df7956SLouis Peens GFP_ATOMIC);
575f1df7956SLouis Peens
576f1df7956SLouis Peens if (nn_entry->flow)
577f1df7956SLouis Peens list_del(&nn_entry->list_head);
578f1df7956SLouis Peens kfree(nn_entry);
579abc21095SYanguo Li } else if (nn_entry && !neigh_invalid) {
580abc21095SYanguo Li struct nfp_tun_neigh *common;
581abc21095SYanguo Li u8 dst_addr[ETH_ALEN];
582abc21095SYanguo Li bool is_mac_change;
583abc21095SYanguo Li
584abc21095SYanguo Li if (is_ipv6) {
585abc21095SYanguo Li struct nfp_tun_neigh_v6 *payload;
586abc21095SYanguo Li
587abc21095SYanguo Li payload = (struct nfp_tun_neigh_v6 *)nn_entry->payload;
588abc21095SYanguo Li common = &payload->common;
589abc21095SYanguo Li mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH_V6;
590abc21095SYanguo Li } else {
591abc21095SYanguo Li struct nfp_tun_neigh_v4 *payload;
592abc21095SYanguo Li
593abc21095SYanguo Li payload = (struct nfp_tun_neigh_v4 *)nn_entry->payload;
594abc21095SYanguo Li common = &payload->common;
595abc21095SYanguo Li mtype = NFP_FLOWER_CMSG_TYPE_TUN_NEIGH;
596abc21095SYanguo Li }
597abc21095SYanguo Li
598abc21095SYanguo Li ether_addr_copy(dst_addr, common->dst_addr);
599abc21095SYanguo Li neigh_ha_snapshot(common->dst_addr, neigh, netdev);
600abc21095SYanguo Li is_mac_change = !ether_addr_equal(dst_addr, common->dst_addr);
601abc21095SYanguo Li if (override || is_mac_change) {
602abc21095SYanguo Li if (is_mac_change && nn_entry->flow) {
603abc21095SYanguo Li list_del(&nn_entry->list_head);
604abc21095SYanguo Li nn_entry->flow = NULL;
605abc21095SYanguo Li }
606656bd03aSTianyu Yuan nfp_tun_link_predt_entries(app, nn_entry);
607656bd03aSTianyu Yuan nfp_flower_xmit_tun_conf(app, mtype, neigh_size,
608656bd03aSTianyu Yuan nn_entry->payload,
609656bd03aSTianyu Yuan GFP_ATOMIC);
6108e6a9046SJohn Hurley }
611abc21095SYanguo Li }
6128e6a9046SJohn Hurley
613f1df7956SLouis Peens spin_unlock_bh(&priv->predt_lock);
61450b1c86aSJohn Hurley return;
61550b1c86aSJohn Hurley
616f1df7956SLouis Peens err:
617f1df7956SLouis Peens kfree(nn_entry);
618f1df7956SLouis Peens spin_unlock_bh(&priv->predt_lock);
619f1df7956SLouis Peens nfp_flower_cmsg_warn(app, "Neighbour configuration failed.\n");
62050b1c86aSJohn Hurley }
62150b1c86aSJohn Hurley
6229a89aad0SHui Zhou static void
nfp_tun_release_neigh_update_work(struct nfp_neigh_update_work * update_work)6239a89aad0SHui Zhou nfp_tun_release_neigh_update_work(struct nfp_neigh_update_work *update_work)
6248e6a9046SJohn Hurley {
6259a89aad0SHui Zhou neigh_release(update_work->n);
6269a89aad0SHui Zhou kfree(update_work);
6279a89aad0SHui Zhou }
6289a89aad0SHui Zhou
nfp_tun_neigh_update(struct work_struct * work)6299a89aad0SHui Zhou static void nfp_tun_neigh_update(struct work_struct *work)
6309a89aad0SHui Zhou {
6319a89aad0SHui Zhou struct nfp_neigh_update_work *update_work;
6328e6a9046SJohn Hurley struct nfp_app *app;
6339a89aad0SHui Zhou struct neighbour *n;
6349d5447edSLouis Peens bool neigh_invalid;
6358e6a9046SJohn Hurley int err;
6368e6a9046SJohn Hurley
6379a89aad0SHui Zhou update_work = container_of(work, struct nfp_neigh_update_work, work);
6389a89aad0SHui Zhou app = update_work->app;
6399a89aad0SHui Zhou n = update_work->n;
6408e6a9046SJohn Hurley
641abc21095SYanguo Li if (!nfp_flower_get_port_id_from_netdev(app, n->dev))
6429a89aad0SHui Zhou goto out;
643e8024cb4SJohn Hurley
6448e6a9046SJohn Hurley #if IS_ENABLED(CONFIG_INET)
6459a89aad0SHui Zhou neigh_invalid = !(n->nud_state & NUD_VALID) || n->dead;
64661004d1dSLouis Peens if (n->tbl->family == AF_INET6) {
6476c463a05SJohn Hurley #if IS_ENABLED(CONFIG_IPV6)
64861004d1dSLouis Peens struct flowi6 flow6 = {};
64961004d1dSLouis Peens
65061004d1dSLouis Peens flow6.daddr = *(struct in6_addr *)n->primary_key;
6519d5447edSLouis Peens if (!neigh_invalid) {
6526c463a05SJohn Hurley struct dst_entry *dst;
6539d5447edSLouis Peens /* Use ipv6_dst_lookup_flow to populate flow6->saddr
6549d5447edSLouis Peens * and other fields. This information is only needed
6559d5447edSLouis Peens * for new entries, lookup can be skipped when an entry
6569d5447edSLouis Peens * gets invalidated - as only the daddr is needed for
6579d5447edSLouis Peens * deleting.
6589d5447edSLouis Peens */
6599d5447edSLouis Peens dst = ip6_dst_lookup_flow(dev_net(n->dev), NULL,
6606c463a05SJohn Hurley &flow6, NULL);
6616c463a05SJohn Hurley if (IS_ERR(dst))
6629a89aad0SHui Zhou goto out;
6636c463a05SJohn Hurley
6646c463a05SJohn Hurley dst_release(dst);
6659d5447edSLouis Peens }
666656bd03aSTianyu Yuan nfp_tun_write_neigh(n->dev, app, &flow6, n, true, false);
6676c463a05SJohn Hurley #endif /* CONFIG_IPV6 */
6686c463a05SJohn Hurley } else {
66961004d1dSLouis Peens struct flowi4 flow4 = {};
67061004d1dSLouis Peens
67161004d1dSLouis Peens flow4.daddr = *(__be32 *)n->primary_key;
6729d5447edSLouis Peens if (!neigh_invalid) {
6739d5447edSLouis Peens struct rtable *rt;
6749d5447edSLouis Peens /* Use ip_route_output_key to populate flow4->saddr and
6759d5447edSLouis Peens * other fields. This information is only needed for
6769d5447edSLouis Peens * new entries, lookup can be skipped when an entry
6779d5447edSLouis Peens * gets invalidated - as only the daddr is needed for
6789d5447edSLouis Peens * deleting.
6799d5447edSLouis Peens */
6806c463a05SJohn Hurley rt = ip_route_output_key(dev_net(n->dev), &flow4);
6818e6a9046SJohn Hurley err = PTR_ERR_OR_ZERO(rt);
6828e6a9046SJohn Hurley if (err)
6839a89aad0SHui Zhou goto out;
684e62e51afSPieter Jansen van Vuuren
685e62e51afSPieter Jansen van Vuuren ip_rt_put(rt);
6869d5447edSLouis Peens }
687656bd03aSTianyu Yuan nfp_tun_write_neigh(n->dev, app, &flow4, n, false, false);
6886c463a05SJohn Hurley }
6896c463a05SJohn Hurley #endif /* CONFIG_INET */
6909a89aad0SHui Zhou out:
6919a89aad0SHui Zhou nfp_tun_release_neigh_update_work(update_work);
6929a89aad0SHui Zhou }
6938e6a9046SJohn Hurley
6949a89aad0SHui Zhou static struct nfp_neigh_update_work *
nfp_tun_alloc_neigh_update_work(struct nfp_app * app,struct neighbour * n)6959a89aad0SHui Zhou nfp_tun_alloc_neigh_update_work(struct nfp_app *app, struct neighbour *n)
6969a89aad0SHui Zhou {
6979a89aad0SHui Zhou struct nfp_neigh_update_work *update_work;
6989a89aad0SHui Zhou
6999a89aad0SHui Zhou update_work = kzalloc(sizeof(*update_work), GFP_ATOMIC);
7009a89aad0SHui Zhou if (!update_work)
7019a89aad0SHui Zhou return NULL;
7029a89aad0SHui Zhou
7039a89aad0SHui Zhou INIT_WORK(&update_work->work, nfp_tun_neigh_update);
7049a89aad0SHui Zhou neigh_hold(n);
7059a89aad0SHui Zhou update_work->n = n;
7069a89aad0SHui Zhou update_work->app = app;
7079a89aad0SHui Zhou
7089a89aad0SHui Zhou return update_work;
7099a89aad0SHui Zhou }
7109a89aad0SHui Zhou
7119a89aad0SHui Zhou static int
nfp_tun_neigh_event_handler(struct notifier_block * nb,unsigned long event,void * ptr)7129a89aad0SHui Zhou nfp_tun_neigh_event_handler(struct notifier_block *nb, unsigned long event,
7139a89aad0SHui Zhou void *ptr)
7149a89aad0SHui Zhou {
7159a89aad0SHui Zhou struct nfp_neigh_update_work *update_work;
7169a89aad0SHui Zhou struct nfp_flower_priv *app_priv;
7179a89aad0SHui Zhou struct netevent_redirect *redir;
7189a89aad0SHui Zhou struct neighbour *n;
7199a89aad0SHui Zhou struct nfp_app *app;
7209a89aad0SHui Zhou
7219a89aad0SHui Zhou switch (event) {
7229a89aad0SHui Zhou case NETEVENT_REDIRECT:
7239a89aad0SHui Zhou redir = (struct netevent_redirect *)ptr;
7249a89aad0SHui Zhou n = redir->neigh;
7259a89aad0SHui Zhou break;
7269a89aad0SHui Zhou case NETEVENT_NEIGH_UPDATE:
7279a89aad0SHui Zhou n = (struct neighbour *)ptr;
7289a89aad0SHui Zhou break;
7299a89aad0SHui Zhou default:
7309a89aad0SHui Zhou return NOTIFY_DONE;
7319a89aad0SHui Zhou }
7329a89aad0SHui Zhou #if IS_ENABLED(CONFIG_IPV6)
7339a89aad0SHui Zhou if (n->tbl != ipv6_stub->nd_tbl && n->tbl != &arp_tbl)
7349a89aad0SHui Zhou #else
7359a89aad0SHui Zhou if (n->tbl != &arp_tbl)
7369a89aad0SHui Zhou #endif
7379a89aad0SHui Zhou return NOTIFY_DONE;
7389a89aad0SHui Zhou
7399a89aad0SHui Zhou app_priv = container_of(nb, struct nfp_flower_priv, tun.neigh_nb);
7409a89aad0SHui Zhou app = app_priv->app;
7419a89aad0SHui Zhou update_work = nfp_tun_alloc_neigh_update_work(app, n);
7429a89aad0SHui Zhou if (!update_work)
7439a89aad0SHui Zhou return NOTIFY_DONE;
7449a89aad0SHui Zhou
7459a89aad0SHui Zhou queue_work(system_highpri_wq, &update_work->work);
7469a89aad0SHui Zhou
7479a89aad0SHui Zhou return NOTIFY_DONE;
7488e6a9046SJohn Hurley }
7498e6a9046SJohn Hurley
nfp_tunnel_request_route_v4(struct nfp_app * app,struct sk_buff * skb)75050b1c86aSJohn Hurley void nfp_tunnel_request_route_v4(struct nfp_app *app, struct sk_buff *skb)
7518e6a9046SJohn Hurley {
7528e6a9046SJohn Hurley struct nfp_tun_req_route_ipv4 *payload;
7538e6a9046SJohn Hurley struct net_device *netdev;
7548e6a9046SJohn Hurley struct flowi4 flow = {};
7558e6a9046SJohn Hurley struct neighbour *n;
7568e6a9046SJohn Hurley struct rtable *rt;
7578e6a9046SJohn Hurley int err;
7588e6a9046SJohn Hurley
7598e6a9046SJohn Hurley payload = nfp_flower_cmsg_get_data(skb);
7608e6a9046SJohn Hurley
761cb07d915SPieter Jansen van Vuuren rcu_read_lock();
76227f54b58SJohn Hurley netdev = nfp_app_dev_get(app, be32_to_cpu(payload->ingress_port), NULL);
7638e6a9046SJohn Hurley if (!netdev)
764cb07d915SPieter Jansen van Vuuren goto fail_rcu_unlock;
7659a89aad0SHui Zhou dev_hold(netdev);
7668e6a9046SJohn Hurley
7678e6a9046SJohn Hurley flow.daddr = payload->ipv4_addr;
7688e6a9046SJohn Hurley flow.flowi4_proto = IPPROTO_UDP;
7698e6a9046SJohn Hurley
7708e6a9046SJohn Hurley #if IS_ENABLED(CONFIG_INET)
7718e6a9046SJohn Hurley /* Do a route lookup on same namespace as ingress port. */
7728e6a9046SJohn Hurley rt = ip_route_output_key(dev_net(netdev), &flow);
7738e6a9046SJohn Hurley err = PTR_ERR_OR_ZERO(rt);
7748e6a9046SJohn Hurley if (err)
775cb07d915SPieter Jansen van Vuuren goto fail_rcu_unlock;
7768e6a9046SJohn Hurley #else
777cb07d915SPieter Jansen van Vuuren goto fail_rcu_unlock;
7788e6a9046SJohn Hurley #endif
7798e6a9046SJohn Hurley
7808e6a9046SJohn Hurley /* Get the neighbour entry for the lookup */
7818e6a9046SJohn Hurley n = dst_neigh_lookup(&rt->dst, &flow.daddr);
7828e6a9046SJohn Hurley ip_rt_put(rt);
7838e6a9046SJohn Hurley if (!n)
784cb07d915SPieter Jansen van Vuuren goto fail_rcu_unlock;
7859a89aad0SHui Zhou rcu_read_unlock();
7869a89aad0SHui Zhou
787656bd03aSTianyu Yuan nfp_tun_write_neigh(n->dev, app, &flow, n, false, true);
7888e6a9046SJohn Hurley neigh_release(n);
7899a89aad0SHui Zhou dev_put(netdev);
7908e6a9046SJohn Hurley return;
7918e6a9046SJohn Hurley
792cb07d915SPieter Jansen van Vuuren fail_rcu_unlock:
793cb07d915SPieter Jansen van Vuuren rcu_read_unlock();
7949a89aad0SHui Zhou dev_put(netdev);
7958e6a9046SJohn Hurley nfp_flower_cmsg_warn(app, "Requested route not found.\n");
7968e6a9046SJohn Hurley }
7978e6a9046SJohn Hurley
nfp_tunnel_request_route_v6(struct nfp_app * app,struct sk_buff * skb)79850b1c86aSJohn Hurley void nfp_tunnel_request_route_v6(struct nfp_app *app, struct sk_buff *skb)
79950b1c86aSJohn Hurley {
80050b1c86aSJohn Hurley struct nfp_tun_req_route_ipv6 *payload;
80150b1c86aSJohn Hurley struct net_device *netdev;
80250b1c86aSJohn Hurley struct flowi6 flow = {};
80350b1c86aSJohn Hurley struct dst_entry *dst;
80450b1c86aSJohn Hurley struct neighbour *n;
80550b1c86aSJohn Hurley
80650b1c86aSJohn Hurley payload = nfp_flower_cmsg_get_data(skb);
80750b1c86aSJohn Hurley
80850b1c86aSJohn Hurley rcu_read_lock();
80950b1c86aSJohn Hurley netdev = nfp_app_dev_get(app, be32_to_cpu(payload->ingress_port), NULL);
81050b1c86aSJohn Hurley if (!netdev)
81150b1c86aSJohn Hurley goto fail_rcu_unlock;
8129a89aad0SHui Zhou dev_hold(netdev);
81350b1c86aSJohn Hurley
81450b1c86aSJohn Hurley flow.daddr = payload->ipv6_addr;
81550b1c86aSJohn Hurley flow.flowi6_proto = IPPROTO_UDP;
81650b1c86aSJohn Hurley
81750b1c86aSJohn Hurley #if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
81850b1c86aSJohn Hurley dst = ipv6_stub->ipv6_dst_lookup_flow(dev_net(netdev), NULL, &flow,
81950b1c86aSJohn Hurley NULL);
82050b1c86aSJohn Hurley if (IS_ERR(dst))
82150b1c86aSJohn Hurley goto fail_rcu_unlock;
82250b1c86aSJohn Hurley #else
82350b1c86aSJohn Hurley goto fail_rcu_unlock;
82450b1c86aSJohn Hurley #endif
82550b1c86aSJohn Hurley
82650b1c86aSJohn Hurley n = dst_neigh_lookup(dst, &flow.daddr);
82750b1c86aSJohn Hurley dst_release(dst);
82850b1c86aSJohn Hurley if (!n)
82950b1c86aSJohn Hurley goto fail_rcu_unlock;
8309a89aad0SHui Zhou rcu_read_unlock();
83150b1c86aSJohn Hurley
832656bd03aSTianyu Yuan nfp_tun_write_neigh(n->dev, app, &flow, n, true, true);
83350b1c86aSJohn Hurley neigh_release(n);
8349a89aad0SHui Zhou dev_put(netdev);
83550b1c86aSJohn Hurley return;
83650b1c86aSJohn Hurley
83750b1c86aSJohn Hurley fail_rcu_unlock:
83850b1c86aSJohn Hurley rcu_read_unlock();
8399a89aad0SHui Zhou dev_put(netdev);
84050b1c86aSJohn Hurley nfp_flower_cmsg_warn(app, "Requested IPv6 route not found.\n");
84150b1c86aSJohn Hurley }
84250b1c86aSJohn Hurley
nfp_tun_write_ipv4_list(struct nfp_app * app)8432d9ad71aSJohn Hurley static void nfp_tun_write_ipv4_list(struct nfp_app *app)
8442d9ad71aSJohn Hurley {
8452d9ad71aSJohn Hurley struct nfp_flower_priv *priv = app->priv;
8462d9ad71aSJohn Hurley struct nfp_ipv4_addr_entry *entry;
8472d9ad71aSJohn Hurley struct nfp_tun_ipv4_addr payload;
8482d9ad71aSJohn Hurley struct list_head *ptr, *storage;
8492d9ad71aSJohn Hurley int count;
8502d9ad71aSJohn Hurley
8512d9ad71aSJohn Hurley memset(&payload, 0, sizeof(struct nfp_tun_ipv4_addr));
852f3b97577SJohn Hurley mutex_lock(&priv->tun.ipv4_off_lock);
8532d9ad71aSJohn Hurley count = 0;
854f3b97577SJohn Hurley list_for_each_safe(ptr, storage, &priv->tun.ipv4_off_list) {
8552d9ad71aSJohn Hurley if (count >= NFP_FL_IPV4_ADDRS_MAX) {
856f3b97577SJohn Hurley mutex_unlock(&priv->tun.ipv4_off_lock);
8572d9ad71aSJohn Hurley nfp_flower_cmsg_warn(app, "IPv4 offload exceeds limit.\n");
8582d9ad71aSJohn Hurley return;
8592d9ad71aSJohn Hurley }
8602d9ad71aSJohn Hurley entry = list_entry(ptr, struct nfp_ipv4_addr_entry, list);
8612d9ad71aSJohn Hurley payload.ipv4_addr[count++] = entry->ipv4_addr;
8622d9ad71aSJohn Hurley }
8632d9ad71aSJohn Hurley payload.count = cpu_to_be32(count);
864f3b97577SJohn Hurley mutex_unlock(&priv->tun.ipv4_off_lock);
8652d9ad71aSJohn Hurley
8662d9ad71aSJohn Hurley nfp_flower_xmit_tun_conf(app, NFP_FLOWER_CMSG_TYPE_TUN_IPS,
8672d9ad71aSJohn Hurley sizeof(struct nfp_tun_ipv4_addr),
868dc4646a9SJohn Hurley &payload, GFP_KERNEL);
8692d9ad71aSJohn Hurley }
8702d9ad71aSJohn Hurley
nfp_tunnel_add_ipv4_off(struct nfp_app * app,__be32 ipv4)8712d9ad71aSJohn Hurley void nfp_tunnel_add_ipv4_off(struct nfp_app *app, __be32 ipv4)
8722d9ad71aSJohn Hurley {
8732d9ad71aSJohn Hurley struct nfp_flower_priv *priv = app->priv;
8742d9ad71aSJohn Hurley struct nfp_ipv4_addr_entry *entry;
8752d9ad71aSJohn Hurley struct list_head *ptr, *storage;
8762d9ad71aSJohn Hurley
877f3b97577SJohn Hurley mutex_lock(&priv->tun.ipv4_off_lock);
878f3b97577SJohn Hurley list_for_each_safe(ptr, storage, &priv->tun.ipv4_off_list) {
8792d9ad71aSJohn Hurley entry = list_entry(ptr, struct nfp_ipv4_addr_entry, list);
8802d9ad71aSJohn Hurley if (entry->ipv4_addr == ipv4) {
8812d9ad71aSJohn Hurley entry->ref_count++;
882f3b97577SJohn Hurley mutex_unlock(&priv->tun.ipv4_off_lock);
8832d9ad71aSJohn Hurley return;
8842d9ad71aSJohn Hurley }
8852d9ad71aSJohn Hurley }
8862d9ad71aSJohn Hurley
8872d9ad71aSJohn Hurley entry = kmalloc(sizeof(*entry), GFP_KERNEL);
8882d9ad71aSJohn Hurley if (!entry) {
889f3b97577SJohn Hurley mutex_unlock(&priv->tun.ipv4_off_lock);
8902d9ad71aSJohn Hurley nfp_flower_cmsg_warn(app, "Mem error when offloading IP address.\n");
8912d9ad71aSJohn Hurley return;
8922d9ad71aSJohn Hurley }
8932d9ad71aSJohn Hurley entry->ipv4_addr = ipv4;
8942d9ad71aSJohn Hurley entry->ref_count = 1;
895f3b97577SJohn Hurley list_add_tail(&entry->list, &priv->tun.ipv4_off_list);
896f3b97577SJohn Hurley mutex_unlock(&priv->tun.ipv4_off_lock);
8972d9ad71aSJohn Hurley
8982d9ad71aSJohn Hurley nfp_tun_write_ipv4_list(app);
8992d9ad71aSJohn Hurley }
9002d9ad71aSJohn Hurley
nfp_tunnel_del_ipv4_off(struct nfp_app * app,__be32 ipv4)9012d9ad71aSJohn Hurley void nfp_tunnel_del_ipv4_off(struct nfp_app *app, __be32 ipv4)
9022d9ad71aSJohn Hurley {
9032d9ad71aSJohn Hurley struct nfp_flower_priv *priv = app->priv;
9042d9ad71aSJohn Hurley struct nfp_ipv4_addr_entry *entry;
9052d9ad71aSJohn Hurley struct list_head *ptr, *storage;
9062d9ad71aSJohn Hurley
907f3b97577SJohn Hurley mutex_lock(&priv->tun.ipv4_off_lock);
908f3b97577SJohn Hurley list_for_each_safe(ptr, storage, &priv->tun.ipv4_off_list) {
9092d9ad71aSJohn Hurley entry = list_entry(ptr, struct nfp_ipv4_addr_entry, list);
9102d9ad71aSJohn Hurley if (entry->ipv4_addr == ipv4) {
9112d9ad71aSJohn Hurley entry->ref_count--;
9122d9ad71aSJohn Hurley if (!entry->ref_count) {
9132d9ad71aSJohn Hurley list_del(&entry->list);
9142d9ad71aSJohn Hurley kfree(entry);
9152d9ad71aSJohn Hurley }
9162d9ad71aSJohn Hurley break;
9172d9ad71aSJohn Hurley }
9182d9ad71aSJohn Hurley }
919f3b97577SJohn Hurley mutex_unlock(&priv->tun.ipv4_off_lock);
9202d9ad71aSJohn Hurley
9212d9ad71aSJohn Hurley nfp_tun_write_ipv4_list(app);
9222d9ad71aSJohn Hurley }
9232d9ad71aSJohn Hurley
nfp_tun_write_ipv6_list(struct nfp_app * app)924cfa18993SJohn Hurley static void nfp_tun_write_ipv6_list(struct nfp_app *app)
925cfa18993SJohn Hurley {
926cfa18993SJohn Hurley struct nfp_flower_priv *priv = app->priv;
927cfa18993SJohn Hurley struct nfp_ipv6_addr_entry *entry;
928cfa18993SJohn Hurley struct nfp_tun_ipv6_addr payload;
929cfa18993SJohn Hurley int count = 0;
930cfa18993SJohn Hurley
931cfa18993SJohn Hurley memset(&payload, 0, sizeof(struct nfp_tun_ipv6_addr));
932cfa18993SJohn Hurley mutex_lock(&priv->tun.ipv6_off_lock);
933cfa18993SJohn Hurley list_for_each_entry(entry, &priv->tun.ipv6_off_list, list) {
934cfa18993SJohn Hurley if (count >= NFP_FL_IPV6_ADDRS_MAX) {
935cfa18993SJohn Hurley nfp_flower_cmsg_warn(app, "Too many IPv6 tunnel endpoint addresses, some cannot be offloaded.\n");
936cfa18993SJohn Hurley break;
937cfa18993SJohn Hurley }
938cfa18993SJohn Hurley payload.ipv6_addr[count++] = entry->ipv6_addr;
939cfa18993SJohn Hurley }
940cfa18993SJohn Hurley mutex_unlock(&priv->tun.ipv6_off_lock);
941cfa18993SJohn Hurley payload.count = cpu_to_be32(count);
942cfa18993SJohn Hurley
943cfa18993SJohn Hurley nfp_flower_xmit_tun_conf(app, NFP_FLOWER_CMSG_TYPE_TUN_IPS_V6,
944cfa18993SJohn Hurley sizeof(struct nfp_tun_ipv6_addr),
945cfa18993SJohn Hurley &payload, GFP_KERNEL);
946cfa18993SJohn Hurley }
947cfa18993SJohn Hurley
948cfa18993SJohn Hurley struct nfp_ipv6_addr_entry *
nfp_tunnel_add_ipv6_off(struct nfp_app * app,struct in6_addr * ipv6)949cfa18993SJohn Hurley nfp_tunnel_add_ipv6_off(struct nfp_app *app, struct in6_addr *ipv6)
950cfa18993SJohn Hurley {
951cfa18993SJohn Hurley struct nfp_flower_priv *priv = app->priv;
952cfa18993SJohn Hurley struct nfp_ipv6_addr_entry *entry;
953cfa18993SJohn Hurley
954cfa18993SJohn Hurley mutex_lock(&priv->tun.ipv6_off_lock);
955cfa18993SJohn Hurley list_for_each_entry(entry, &priv->tun.ipv6_off_list, list)
956cfa18993SJohn Hurley if (!memcmp(&entry->ipv6_addr, ipv6, sizeof(*ipv6))) {
957cfa18993SJohn Hurley entry->ref_count++;
958cfa18993SJohn Hurley mutex_unlock(&priv->tun.ipv6_off_lock);
959cfa18993SJohn Hurley return entry;
960cfa18993SJohn Hurley }
961cfa18993SJohn Hurley
962cfa18993SJohn Hurley entry = kmalloc(sizeof(*entry), GFP_KERNEL);
963cfa18993SJohn Hurley if (!entry) {
964cfa18993SJohn Hurley mutex_unlock(&priv->tun.ipv6_off_lock);
965cfa18993SJohn Hurley nfp_flower_cmsg_warn(app, "Mem error when offloading IP address.\n");
966cfa18993SJohn Hurley return NULL;
967cfa18993SJohn Hurley }
968cfa18993SJohn Hurley entry->ipv6_addr = *ipv6;
969cfa18993SJohn Hurley entry->ref_count = 1;
970cfa18993SJohn Hurley list_add_tail(&entry->list, &priv->tun.ipv6_off_list);
971cfa18993SJohn Hurley mutex_unlock(&priv->tun.ipv6_off_lock);
972cfa18993SJohn Hurley
973cfa18993SJohn Hurley nfp_tun_write_ipv6_list(app);
974cfa18993SJohn Hurley
975cfa18993SJohn Hurley return entry;
976cfa18993SJohn Hurley }
977cfa18993SJohn Hurley
978cfa18993SJohn Hurley void
nfp_tunnel_put_ipv6_off(struct nfp_app * app,struct nfp_ipv6_addr_entry * entry)979cfa18993SJohn Hurley nfp_tunnel_put_ipv6_off(struct nfp_app *app, struct nfp_ipv6_addr_entry *entry)
980cfa18993SJohn Hurley {
981cfa18993SJohn Hurley struct nfp_flower_priv *priv = app->priv;
982cfa18993SJohn Hurley bool freed = false;
983cfa18993SJohn Hurley
984cfa18993SJohn Hurley mutex_lock(&priv->tun.ipv6_off_lock);
985cfa18993SJohn Hurley if (!--entry->ref_count) {
986cfa18993SJohn Hurley list_del(&entry->list);
987cfa18993SJohn Hurley kfree(entry);
988cfa18993SJohn Hurley freed = true;
989cfa18993SJohn Hurley }
990cfa18993SJohn Hurley mutex_unlock(&priv->tun.ipv6_off_lock);
991cfa18993SJohn Hurley
992cfa18993SJohn Hurley if (freed)
993cfa18993SJohn Hurley nfp_tun_write_ipv6_list(app);
994cfa18993SJohn Hurley }
995cfa18993SJohn Hurley
99649402b0bSJohn Hurley static int
__nfp_tunnel_offload_mac(struct nfp_app * app,const u8 * mac,u16 idx,bool del)99776660757SJakub Kicinski __nfp_tunnel_offload_mac(struct nfp_app *app, const u8 *mac, u16 idx, bool del)
998fd0dd1abSJohn Hurley {
9990115dcc3SJohn Hurley struct nfp_tun_mac_addr_offload payload;
10000115dcc3SJohn Hurley
10010115dcc3SJohn Hurley memset(&payload, 0, sizeof(payload));
10020115dcc3SJohn Hurley
100349402b0bSJohn Hurley if (del)
100449402b0bSJohn Hurley payload.flags = cpu_to_be16(NFP_TUN_MAC_OFFLOAD_DEL_FLAG);
100549402b0bSJohn Hurley
10060115dcc3SJohn Hurley /* FW supports multiple MACs per cmsg but restrict to single. */
10070115dcc3SJohn Hurley payload.count = cpu_to_be16(1);
10080115dcc3SJohn Hurley payload.index = cpu_to_be16(idx);
10090115dcc3SJohn Hurley ether_addr_copy(payload.addr, mac);
10100115dcc3SJohn Hurley
10110115dcc3SJohn Hurley return nfp_flower_xmit_tun_conf(app, NFP_FLOWER_CMSG_TYPE_TUN_MAC,
10120115dcc3SJohn Hurley sizeof(struct nfp_tun_mac_addr_offload),
10130115dcc3SJohn Hurley &payload, GFP_KERNEL);
10140115dcc3SJohn Hurley }
10150115dcc3SJohn Hurley
nfp_tunnel_port_is_phy_repr(int port)101620cce886SJohn Hurley static bool nfp_tunnel_port_is_phy_repr(int port)
10170115dcc3SJohn Hurley {
1018fd0dd1abSJohn Hurley if (FIELD_GET(NFP_FLOWER_CMSG_PORT_TYPE, port) ==
101920cce886SJohn Hurley NFP_FLOWER_CMSG_PORT_TYPE_PHYS_PORT)
102020cce886SJohn Hurley return true;
1021fd0dd1abSJohn Hurley
102220cce886SJohn Hurley return false;
102349402b0bSJohn Hurley }
102449402b0bSJohn Hurley
nfp_tunnel_get_mac_idx_from_phy_port_id(int port)102520cce886SJohn Hurley static u16 nfp_tunnel_get_mac_idx_from_phy_port_id(int port)
102620cce886SJohn Hurley {
102720cce886SJohn Hurley return port << 8 | NFP_FLOWER_CMSG_PORT_TYPE_PHYS_PORT;
102820cce886SJohn Hurley }
102920cce886SJohn Hurley
nfp_tunnel_get_global_mac_idx_from_ida(int id)103020cce886SJohn Hurley static u16 nfp_tunnel_get_global_mac_idx_from_ida(int id)
103120cce886SJohn Hurley {
103220cce886SJohn Hurley return id << 8 | NFP_FLOWER_CMSG_PORT_TYPE_OTHER_PORT;
103320cce886SJohn Hurley }
103420cce886SJohn Hurley
nfp_tunnel_get_ida_from_global_mac_idx(u16 nfp_mac_idx)103520cce886SJohn Hurley static int nfp_tunnel_get_ida_from_global_mac_idx(u16 nfp_mac_idx)
103620cce886SJohn Hurley {
103720cce886SJohn Hurley return nfp_mac_idx >> 8;
103820cce886SJohn Hurley }
103920cce886SJohn Hurley
nfp_tunnel_is_mac_idx_global(u16 nfp_mac_idx)104020cce886SJohn Hurley static bool nfp_tunnel_is_mac_idx_global(u16 nfp_mac_idx)
104120cce886SJohn Hurley {
104220cce886SJohn Hurley return (nfp_mac_idx & 0xff) == NFP_FLOWER_CMSG_PORT_TYPE_OTHER_PORT;
104320cce886SJohn Hurley }
104420cce886SJohn Hurley
104520cce886SJohn Hurley static struct nfp_tun_offloaded_mac *
nfp_tunnel_lookup_offloaded_macs(struct nfp_app * app,const u8 * mac)104676660757SJakub Kicinski nfp_tunnel_lookup_offloaded_macs(struct nfp_app *app, const u8 *mac)
104720cce886SJohn Hurley {
104820cce886SJohn Hurley struct nfp_flower_priv *priv = app->priv;
104920cce886SJohn Hurley
105020cce886SJohn Hurley return rhashtable_lookup_fast(&priv->tun.offloaded_macs, mac,
105120cce886SJohn Hurley offloaded_macs_params);
105220cce886SJohn Hurley }
105320cce886SJohn Hurley
105420cce886SJohn Hurley static void
nfp_tunnel_offloaded_macs_inc_ref_and_link(struct nfp_tun_offloaded_mac * entry,struct net_device * netdev,bool mod)105520cce886SJohn Hurley nfp_tunnel_offloaded_macs_inc_ref_and_link(struct nfp_tun_offloaded_mac *entry,
105620cce886SJohn Hurley struct net_device *netdev, bool mod)
105720cce886SJohn Hurley {
105820cce886SJohn Hurley if (nfp_netdev_is_nfp_repr(netdev)) {
105920cce886SJohn Hurley struct nfp_flower_repr_priv *repr_priv;
106020cce886SJohn Hurley struct nfp_repr *repr;
106120cce886SJohn Hurley
106220cce886SJohn Hurley repr = netdev_priv(netdev);
106320cce886SJohn Hurley repr_priv = repr->app_priv;
106420cce886SJohn Hurley
106520cce886SJohn Hurley /* If modifing MAC, remove repr from old list first. */
106620cce886SJohn Hurley if (mod)
106720cce886SJohn Hurley list_del(&repr_priv->mac_list);
106820cce886SJohn Hurley
106920cce886SJohn Hurley list_add_tail(&repr_priv->mac_list, &entry->repr_list);
10702e0bc7f3SJohn Hurley } else if (nfp_flower_is_supported_bridge(netdev)) {
10712e0bc7f3SJohn Hurley entry->bridge_count++;
107220cce886SJohn Hurley }
107320cce886SJohn Hurley
107420cce886SJohn Hurley entry->ref_count++;
107520cce886SJohn Hurley }
107620cce886SJohn Hurley
107720cce886SJohn Hurley static int
nfp_tunnel_add_shared_mac(struct nfp_app * app,struct net_device * netdev,int port,bool mod)107820cce886SJohn Hurley nfp_tunnel_add_shared_mac(struct nfp_app *app, struct net_device *netdev,
107920cce886SJohn Hurley int port, bool mod)
108020cce886SJohn Hurley {
108120cce886SJohn Hurley struct nfp_flower_priv *priv = app->priv;
108220cce886SJohn Hurley struct nfp_tun_offloaded_mac *entry;
10833a14d088SChristophe JAILLET int ida_idx = -1, err;
108420cce886SJohn Hurley u16 nfp_mac_idx = 0;
108520cce886SJohn Hurley
108620cce886SJohn Hurley entry = nfp_tunnel_lookup_offloaded_macs(app, netdev->dev_addr);
1087*080da821SDaniel de Villiers if (entry && (nfp_tunnel_is_mac_idx_global(entry->index) || netif_is_lag_port(netdev))) {
10882e0bc7f3SJohn Hurley if (entry->bridge_count ||
10892e0bc7f3SJohn Hurley !nfp_flower_is_supported_bridge(netdev)) {
10902e0bc7f3SJohn Hurley nfp_tunnel_offloaded_macs_inc_ref_and_link(entry,
10912e0bc7f3SJohn Hurley netdev, mod);
109249402b0bSJohn Hurley return 0;
109349402b0bSJohn Hurley }
109449402b0bSJohn Hurley
10952e0bc7f3SJohn Hurley /* MAC is global but matches need to go to pre_tun table. */
10962e0bc7f3SJohn Hurley nfp_mac_idx = entry->index | NFP_TUN_PRE_TUN_IDX_BIT;
10972e0bc7f3SJohn Hurley }
10982e0bc7f3SJohn Hurley
10992e0bc7f3SJohn Hurley if (!nfp_mac_idx) {
11002e0bc7f3SJohn Hurley /* Assign a global index if non-repr or MAC is now shared. */
110120cce886SJohn Hurley if (entry || !port) {
110243250901SChristophe JAILLET ida_idx = ida_alloc_max(&priv->tun.mac_off_ids,
110320cce886SJohn Hurley NFP_MAX_MAC_INDEX, GFP_KERNEL);
110420cce886SJohn Hurley if (ida_idx < 0)
110520cce886SJohn Hurley return ida_idx;
110620cce886SJohn Hurley
11072e0bc7f3SJohn Hurley nfp_mac_idx =
11082e0bc7f3SJohn Hurley nfp_tunnel_get_global_mac_idx_from_ida(ida_idx);
11092e0bc7f3SJohn Hurley
11102e0bc7f3SJohn Hurley if (nfp_flower_is_supported_bridge(netdev))
11112e0bc7f3SJohn Hurley nfp_mac_idx |= NFP_TUN_PRE_TUN_IDX_BIT;
11122e0bc7f3SJohn Hurley
111320cce886SJohn Hurley } else {
11142e0bc7f3SJohn Hurley nfp_mac_idx =
11152e0bc7f3SJohn Hurley nfp_tunnel_get_mac_idx_from_phy_port_id(port);
11162e0bc7f3SJohn Hurley }
111720cce886SJohn Hurley }
111820cce886SJohn Hurley
111920cce886SJohn Hurley if (!entry) {
112020cce886SJohn Hurley entry = kzalloc(sizeof(*entry), GFP_KERNEL);
112120cce886SJohn Hurley if (!entry) {
112220cce886SJohn Hurley err = -ENOMEM;
112320cce886SJohn Hurley goto err_free_ida;
112420cce886SJohn Hurley }
112520cce886SJohn Hurley
112620cce886SJohn Hurley ether_addr_copy(entry->addr, netdev->dev_addr);
112720cce886SJohn Hurley INIT_LIST_HEAD(&entry->repr_list);
112820cce886SJohn Hurley
112920cce886SJohn Hurley if (rhashtable_insert_fast(&priv->tun.offloaded_macs,
113020cce886SJohn Hurley &entry->ht_node,
113120cce886SJohn Hurley offloaded_macs_params)) {
113220cce886SJohn Hurley err = -ENOMEM;
113320cce886SJohn Hurley goto err_free_entry;
113420cce886SJohn Hurley }
113520cce886SJohn Hurley }
113620cce886SJohn Hurley
113720cce886SJohn Hurley err = __nfp_tunnel_offload_mac(app, netdev->dev_addr,
113820cce886SJohn Hurley nfp_mac_idx, false);
113920cce886SJohn Hurley if (err) {
114020cce886SJohn Hurley /* If not shared then free. */
114120cce886SJohn Hurley if (!entry->ref_count)
114220cce886SJohn Hurley goto err_remove_hash;
114320cce886SJohn Hurley goto err_free_ida;
114420cce886SJohn Hurley }
114520cce886SJohn Hurley
114620cce886SJohn Hurley entry->index = nfp_mac_idx;
114720cce886SJohn Hurley nfp_tunnel_offloaded_macs_inc_ref_and_link(entry, netdev, mod);
114820cce886SJohn Hurley
114920cce886SJohn Hurley return 0;
115020cce886SJohn Hurley
115120cce886SJohn Hurley err_remove_hash:
115220cce886SJohn Hurley rhashtable_remove_fast(&priv->tun.offloaded_macs, &entry->ht_node,
115320cce886SJohn Hurley offloaded_macs_params);
115420cce886SJohn Hurley err_free_entry:
115520cce886SJohn Hurley kfree(entry);
115620cce886SJohn Hurley err_free_ida:
11573a14d088SChristophe JAILLET if (ida_idx != -1)
115843250901SChristophe JAILLET ida_free(&priv->tun.mac_off_ids, ida_idx);
115920cce886SJohn Hurley
116020cce886SJohn Hurley return err;
116120cce886SJohn Hurley }
116220cce886SJohn Hurley
116320cce886SJohn Hurley static int
nfp_tunnel_del_shared_mac(struct nfp_app * app,struct net_device * netdev,const u8 * mac,bool mod)116420cce886SJohn Hurley nfp_tunnel_del_shared_mac(struct nfp_app *app, struct net_device *netdev,
116576660757SJakub Kicinski const u8 *mac, bool mod)
116620cce886SJohn Hurley {
116720cce886SJohn Hurley struct nfp_flower_priv *priv = app->priv;
116820cce886SJohn Hurley struct nfp_flower_repr_priv *repr_priv;
116920cce886SJohn Hurley struct nfp_tun_offloaded_mac *entry;
117020cce886SJohn Hurley struct nfp_repr *repr;
11717db788adSLouis Peens u16 nfp_mac_idx;
117220cce886SJohn Hurley int ida_idx;
117320cce886SJohn Hurley
117420cce886SJohn Hurley entry = nfp_tunnel_lookup_offloaded_macs(app, mac);
117520cce886SJohn Hurley if (!entry)
117620cce886SJohn Hurley return 0;
117720cce886SJohn Hurley
117820cce886SJohn Hurley entry->ref_count--;
11799bacb93bSWalter Heymans /* If del is part of a mod then mac_list is still in use elsewhere. */
118020cce886SJohn Hurley if (nfp_netdev_is_nfp_repr(netdev) && !mod) {
118120cce886SJohn Hurley repr = netdev_priv(netdev);
118220cce886SJohn Hurley repr_priv = repr->app_priv;
118320cce886SJohn Hurley list_del(&repr_priv->mac_list);
118420cce886SJohn Hurley }
118520cce886SJohn Hurley
11862e0bc7f3SJohn Hurley if (nfp_flower_is_supported_bridge(netdev)) {
11872e0bc7f3SJohn Hurley entry->bridge_count--;
11882e0bc7f3SJohn Hurley
11892e0bc7f3SJohn Hurley if (!entry->bridge_count && entry->ref_count) {
11902e0bc7f3SJohn Hurley nfp_mac_idx = entry->index & ~NFP_TUN_PRE_TUN_IDX_BIT;
11912e0bc7f3SJohn Hurley if (__nfp_tunnel_offload_mac(app, mac, nfp_mac_idx,
11922e0bc7f3SJohn Hurley false)) {
11932e0bc7f3SJohn Hurley nfp_flower_cmsg_warn(app, "MAC offload index revert failed on %s.\n",
11942e0bc7f3SJohn Hurley netdev_name(netdev));
11952e0bc7f3SJohn Hurley return 0;
11962e0bc7f3SJohn Hurley }
11972e0bc7f3SJohn Hurley
11982e0bc7f3SJohn Hurley entry->index = nfp_mac_idx;
11992e0bc7f3SJohn Hurley return 0;
12002e0bc7f3SJohn Hurley }
12012e0bc7f3SJohn Hurley }
12022e0bc7f3SJohn Hurley
120320cce886SJohn Hurley /* If MAC is now used by 1 repr set the offloaded MAC index to port. */
120420cce886SJohn Hurley if (entry->ref_count == 1 && list_is_singular(&entry->repr_list)) {
120520cce886SJohn Hurley int port, err;
120620cce886SJohn Hurley
120720cce886SJohn Hurley repr_priv = list_first_entry(&entry->repr_list,
120820cce886SJohn Hurley struct nfp_flower_repr_priv,
120920cce886SJohn Hurley mac_list);
121020cce886SJohn Hurley repr = repr_priv->nfp_repr;
121120cce886SJohn Hurley port = nfp_repr_get_port_id(repr->netdev);
121220cce886SJohn Hurley nfp_mac_idx = nfp_tunnel_get_mac_idx_from_phy_port_id(port);
121320cce886SJohn Hurley err = __nfp_tunnel_offload_mac(app, mac, nfp_mac_idx, false);
121420cce886SJohn Hurley if (err) {
121520cce886SJohn Hurley nfp_flower_cmsg_warn(app, "MAC offload index revert failed on %s.\n",
121620cce886SJohn Hurley netdev_name(netdev));
121720cce886SJohn Hurley return 0;
121820cce886SJohn Hurley }
121920cce886SJohn Hurley
122020cce886SJohn Hurley ida_idx = nfp_tunnel_get_ida_from_global_mac_idx(entry->index);
122143250901SChristophe JAILLET ida_free(&priv->tun.mac_off_ids, ida_idx);
122220cce886SJohn Hurley entry->index = nfp_mac_idx;
122320cce886SJohn Hurley return 0;
122420cce886SJohn Hurley }
122520cce886SJohn Hurley
122620cce886SJohn Hurley if (entry->ref_count)
122720cce886SJohn Hurley return 0;
122820cce886SJohn Hurley
122920cce886SJohn Hurley WARN_ON_ONCE(rhashtable_remove_fast(&priv->tun.offloaded_macs,
123020cce886SJohn Hurley &entry->ht_node,
123120cce886SJohn Hurley offloaded_macs_params));
12327db788adSLouis Peens
12337db788adSLouis Peens if (nfp_flower_is_supported_bridge(netdev))
12347db788adSLouis Peens nfp_mac_idx = entry->index & ~NFP_TUN_PRE_TUN_IDX_BIT;
12357db788adSLouis Peens else
12367db788adSLouis Peens nfp_mac_idx = entry->index;
12377db788adSLouis Peens
123820cce886SJohn Hurley /* If MAC has global ID then extract and free the ida entry. */
12397db788adSLouis Peens if (nfp_tunnel_is_mac_idx_global(nfp_mac_idx)) {
124020cce886SJohn Hurley ida_idx = nfp_tunnel_get_ida_from_global_mac_idx(entry->index);
124143250901SChristophe JAILLET ida_free(&priv->tun.mac_off_ids, ida_idx);
124220cce886SJohn Hurley }
124320cce886SJohn Hurley
124420cce886SJohn Hurley kfree(entry);
124520cce886SJohn Hurley
124620cce886SJohn Hurley return __nfp_tunnel_offload_mac(app, mac, 0, true);
124720cce886SJohn Hurley }
124820cce886SJohn Hurley
124949402b0bSJohn Hurley static int
nfp_tunnel_offload_mac(struct nfp_app * app,struct net_device * netdev,enum nfp_flower_mac_offload_cmd cmd)125049402b0bSJohn Hurley nfp_tunnel_offload_mac(struct nfp_app *app, struct net_device *netdev,
125149402b0bSJohn Hurley enum nfp_flower_mac_offload_cmd cmd)
125249402b0bSJohn Hurley {
125313cf7103SJohn Hurley struct nfp_flower_non_repr_priv *nr_priv = NULL;
125413cf7103SJohn Hurley bool non_repr = false, *mac_offloaded;
125513cf7103SJohn Hurley u8 *off_mac = NULL;
125649402b0bSJohn Hurley int err, port = 0;
125749402b0bSJohn Hurley
125849402b0bSJohn Hurley if (nfp_netdev_is_nfp_repr(netdev)) {
125913cf7103SJohn Hurley struct nfp_flower_repr_priv *repr_priv;
126049402b0bSJohn Hurley struct nfp_repr *repr;
126149402b0bSJohn Hurley
126249402b0bSJohn Hurley repr = netdev_priv(netdev);
126349402b0bSJohn Hurley if (repr->app != app)
126449402b0bSJohn Hurley return 0;
126549402b0bSJohn Hurley
126613cf7103SJohn Hurley repr_priv = repr->app_priv;
126709aa811bSJohn Hurley if (repr_priv->on_bridge)
126809aa811bSJohn Hurley return 0;
126909aa811bSJohn Hurley
127013cf7103SJohn Hurley mac_offloaded = &repr_priv->mac_offloaded;
127113cf7103SJohn Hurley off_mac = &repr_priv->offloaded_mac_addr[0];
127249402b0bSJohn Hurley port = nfp_repr_get_port_id(netdev);
127320cce886SJohn Hurley if (!nfp_tunnel_port_is_phy_repr(port))
127420cce886SJohn Hurley return 0;
127549402b0bSJohn Hurley } else if (nfp_fl_is_netdev_to_offload(netdev)) {
127613cf7103SJohn Hurley nr_priv = nfp_flower_non_repr_priv_get(app, netdev);
127713cf7103SJohn Hurley if (!nr_priv)
127813cf7103SJohn Hurley return -ENOMEM;
127913cf7103SJohn Hurley
128013cf7103SJohn Hurley mac_offloaded = &nr_priv->mac_offloaded;
128113cf7103SJohn Hurley off_mac = &nr_priv->offloaded_mac_addr[0];
128249402b0bSJohn Hurley non_repr = true;
128341da0b5eSJohn Hurley } else {
12840115dcc3SJohn Hurley return 0;
1285fd0dd1abSJohn Hurley }
1286fd0dd1abSJohn Hurley
128713cf7103SJohn Hurley if (!is_valid_ether_addr(netdev->dev_addr)) {
128813cf7103SJohn Hurley err = -EINVAL;
128913cf7103SJohn Hurley goto err_put_non_repr_priv;
129013cf7103SJohn Hurley }
129113cf7103SJohn Hurley
129213cf7103SJohn Hurley if (cmd == NFP_TUNNEL_MAC_OFFLOAD_MOD && !*mac_offloaded)
129313cf7103SJohn Hurley cmd = NFP_TUNNEL_MAC_OFFLOAD_ADD;
129449402b0bSJohn Hurley
129549402b0bSJohn Hurley switch (cmd) {
129649402b0bSJohn Hurley case NFP_TUNNEL_MAC_OFFLOAD_ADD:
129720cce886SJohn Hurley err = nfp_tunnel_add_shared_mac(app, netdev, port, false);
129849402b0bSJohn Hurley if (err)
129913cf7103SJohn Hurley goto err_put_non_repr_priv;
130049402b0bSJohn Hurley
130113cf7103SJohn Hurley if (non_repr)
130213cf7103SJohn Hurley __nfp_flower_non_repr_priv_get(nr_priv);
130313cf7103SJohn Hurley
130413cf7103SJohn Hurley *mac_offloaded = true;
130513cf7103SJohn Hurley ether_addr_copy(off_mac, netdev->dev_addr);
130613cf7103SJohn Hurley break;
130713cf7103SJohn Hurley case NFP_TUNNEL_MAC_OFFLOAD_DEL:
130813cf7103SJohn Hurley /* Only attempt delete if add was successful. */
130913cf7103SJohn Hurley if (!*mac_offloaded)
131013cf7103SJohn Hurley break;
131113cf7103SJohn Hurley
131220cce886SJohn Hurley if (non_repr)
131313cf7103SJohn Hurley __nfp_flower_non_repr_priv_put(nr_priv);
131449402b0bSJohn Hurley
131513cf7103SJohn Hurley *mac_offloaded = false;
131613cf7103SJohn Hurley
131720cce886SJohn Hurley err = nfp_tunnel_del_shared_mac(app, netdev, netdev->dev_addr,
131820cce886SJohn Hurley false);
131913cf7103SJohn Hurley if (err)
132013cf7103SJohn Hurley goto err_put_non_repr_priv;
132113cf7103SJohn Hurley
132213cf7103SJohn Hurley break;
132313cf7103SJohn Hurley case NFP_TUNNEL_MAC_OFFLOAD_MOD:
132413cf7103SJohn Hurley /* Ignore if changing to the same address. */
132513cf7103SJohn Hurley if (ether_addr_equal(netdev->dev_addr, off_mac))
132613cf7103SJohn Hurley break;
132713cf7103SJohn Hurley
132820cce886SJohn Hurley err = nfp_tunnel_add_shared_mac(app, netdev, port, true);
132913cf7103SJohn Hurley if (err)
133013cf7103SJohn Hurley goto err_put_non_repr_priv;
133113cf7103SJohn Hurley
133213cf7103SJohn Hurley /* Delete the previous MAC address. */
133320cce886SJohn Hurley err = nfp_tunnel_del_shared_mac(app, netdev, off_mac, true);
133413cf7103SJohn Hurley if (err)
133513cf7103SJohn Hurley nfp_flower_cmsg_warn(app, "Failed to remove offload of replaced MAC addr on %s.\n",
133613cf7103SJohn Hurley netdev_name(netdev));
133713cf7103SJohn Hurley
133813cf7103SJohn Hurley ether_addr_copy(off_mac, netdev->dev_addr);
133913cf7103SJohn Hurley break;
134013cf7103SJohn Hurley default:
134113cf7103SJohn Hurley err = -EINVAL;
134213cf7103SJohn Hurley goto err_put_non_repr_priv;
134313cf7103SJohn Hurley }
134413cf7103SJohn Hurley
134513cf7103SJohn Hurley if (non_repr)
134613cf7103SJohn Hurley __nfp_flower_non_repr_priv_put(nr_priv);
134713cf7103SJohn Hurley
134849402b0bSJohn Hurley return 0;
134913cf7103SJohn Hurley
135013cf7103SJohn Hurley err_put_non_repr_priv:
135113cf7103SJohn Hurley if (non_repr)
135213cf7103SJohn Hurley __nfp_flower_non_repr_priv_put(nr_priv);
135313cf7103SJohn Hurley
135413cf7103SJohn Hurley return err;
1355fd0dd1abSJohn Hurley }
1356fd0dd1abSJohn Hurley
nfp_tunnel_mac_event_handler(struct nfp_app * app,struct net_device * netdev,unsigned long event,void * ptr)13570c665e2bSJakub Kicinski int nfp_tunnel_mac_event_handler(struct nfp_app *app,
13580c665e2bSJakub Kicinski struct net_device *netdev,
1359fd0dd1abSJohn Hurley unsigned long event, void *ptr)
1360fd0dd1abSJohn Hurley {
13610115dcc3SJohn Hurley int err;
13620115dcc3SJohn Hurley
136349402b0bSJohn Hurley if (event == NETDEV_DOWN) {
136449402b0bSJohn Hurley err = nfp_tunnel_offload_mac(app, netdev,
136549402b0bSJohn Hurley NFP_TUNNEL_MAC_OFFLOAD_DEL);
136649402b0bSJohn Hurley if (err)
136749402b0bSJohn Hurley nfp_flower_cmsg_warn(app, "Failed to delete offload MAC on %s.\n",
136849402b0bSJohn Hurley netdev_name(netdev));
136913cf7103SJohn Hurley } else if (event == NETDEV_UP) {
137049402b0bSJohn Hurley err = nfp_tunnel_offload_mac(app, netdev,
137149402b0bSJohn Hurley NFP_TUNNEL_MAC_OFFLOAD_ADD);
13720115dcc3SJohn Hurley if (err)
13730115dcc3SJohn Hurley nfp_flower_cmsg_warn(app, "Failed to offload MAC on %s.\n",
13740115dcc3SJohn Hurley netdev_name(netdev));
137513cf7103SJohn Hurley } else if (event == NETDEV_CHANGEADDR) {
137613cf7103SJohn Hurley /* Only offload addr change if netdev is already up. */
137713cf7103SJohn Hurley if (!(netdev->flags & IFF_UP))
137813cf7103SJohn Hurley return NOTIFY_OK;
137913cf7103SJohn Hurley
138013cf7103SJohn Hurley err = nfp_tunnel_offload_mac(app, netdev,
138113cf7103SJohn Hurley NFP_TUNNEL_MAC_OFFLOAD_MOD);
138213cf7103SJohn Hurley if (err)
138313cf7103SJohn Hurley nfp_flower_cmsg_warn(app, "Failed to offload MAC change on %s.\n",
138413cf7103SJohn Hurley netdev_name(netdev));
138509aa811bSJohn Hurley } else if (event == NETDEV_CHANGEUPPER) {
138609aa811bSJohn Hurley /* If a repr is attached to a bridge then tunnel packets
138709aa811bSJohn Hurley * entering the physical port are directed through the bridge
138809aa811bSJohn Hurley * datapath and cannot be directly detunneled. Therefore,
138909aa811bSJohn Hurley * associated offloaded MACs and indexes should not be used
139009aa811bSJohn Hurley * by fw for detunneling.
139109aa811bSJohn Hurley */
139209aa811bSJohn Hurley struct netdev_notifier_changeupper_info *info = ptr;
139309aa811bSJohn Hurley struct net_device *upper = info->upper_dev;
139409aa811bSJohn Hurley struct nfp_flower_repr_priv *repr_priv;
139509aa811bSJohn Hurley struct nfp_repr *repr;
139609aa811bSJohn Hurley
139709aa811bSJohn Hurley if (!nfp_netdev_is_nfp_repr(netdev) ||
139809aa811bSJohn Hurley !nfp_flower_is_supported_bridge(upper))
139909aa811bSJohn Hurley return NOTIFY_OK;
140009aa811bSJohn Hurley
140109aa811bSJohn Hurley repr = netdev_priv(netdev);
140209aa811bSJohn Hurley if (repr->app != app)
140309aa811bSJohn Hurley return NOTIFY_OK;
140409aa811bSJohn Hurley
140509aa811bSJohn Hurley repr_priv = repr->app_priv;
140609aa811bSJohn Hurley
140709aa811bSJohn Hurley if (info->linking) {
140809aa811bSJohn Hurley if (nfp_tunnel_offload_mac(app, netdev,
140909aa811bSJohn Hurley NFP_TUNNEL_MAC_OFFLOAD_DEL))
141009aa811bSJohn Hurley nfp_flower_cmsg_warn(app, "Failed to delete offloaded MAC on %s.\n",
141109aa811bSJohn Hurley netdev_name(netdev));
141209aa811bSJohn Hurley repr_priv->on_bridge = true;
141309aa811bSJohn Hurley } else {
141409aa811bSJohn Hurley repr_priv->on_bridge = false;
141509aa811bSJohn Hurley
141609aa811bSJohn Hurley if (!(netdev->flags & IFF_UP))
141709aa811bSJohn Hurley return NOTIFY_OK;
141809aa811bSJohn Hurley
141909aa811bSJohn Hurley if (nfp_tunnel_offload_mac(app, netdev,
142009aa811bSJohn Hurley NFP_TUNNEL_MAC_OFFLOAD_ADD))
142109aa811bSJohn Hurley nfp_flower_cmsg_warn(app, "Failed to offload MAC on %s.\n",
142209aa811bSJohn Hurley netdev_name(netdev));
142309aa811bSJohn Hurley }
1424fd0dd1abSJohn Hurley }
1425fd0dd1abSJohn Hurley return NOTIFY_OK;
1426fd0dd1abSJohn Hurley }
1427fd0dd1abSJohn Hurley
nfp_flower_xmit_pre_tun_flow(struct nfp_app * app,struct nfp_fl_payload * flow)1428120ffd84SJohn Hurley int nfp_flower_xmit_pre_tun_flow(struct nfp_app *app,
1429120ffd84SJohn Hurley struct nfp_fl_payload *flow)
1430120ffd84SJohn Hurley {
1431f12725d9SJohn Hurley struct nfp_flower_priv *app_priv = app->priv;
1432f12725d9SJohn Hurley struct nfp_tun_offloaded_mac *mac_entry;
14335c4f5e19SLouis Peens struct nfp_flower_meta_tci *key_meta;
1434f12725d9SJohn Hurley struct nfp_tun_pre_tun_rule payload;
1435f12725d9SJohn Hurley struct net_device *internal_dev;
1436f12725d9SJohn Hurley int err;
1437f12725d9SJohn Hurley
1438f12725d9SJohn Hurley if (app_priv->pre_tun_rule_cnt == NFP_TUN_PRE_TUN_RULE_LIMIT)
1439f12725d9SJohn Hurley return -ENOSPC;
1440f12725d9SJohn Hurley
1441f12725d9SJohn Hurley memset(&payload, 0, sizeof(struct nfp_tun_pre_tun_rule));
1442f12725d9SJohn Hurley
1443f12725d9SJohn Hurley internal_dev = flow->pre_tun_rule.dev;
1444f12725d9SJohn Hurley payload.vlan_tci = flow->pre_tun_rule.vlan_tci;
1445f12725d9SJohn Hurley payload.host_ctx_id = flow->meta.host_ctx_id;
1446f12725d9SJohn Hurley
1447f12725d9SJohn Hurley /* Lookup MAC index for the pre-tunnel rule egress device.
1448f12725d9SJohn Hurley * Note that because the device is always an internal port, it will
1449f12725d9SJohn Hurley * have a constant global index so does not need to be tracked.
1450f12725d9SJohn Hurley */
1451f12725d9SJohn Hurley mac_entry = nfp_tunnel_lookup_offloaded_macs(app,
1452f12725d9SJohn Hurley internal_dev->dev_addr);
1453f12725d9SJohn Hurley if (!mac_entry)
1454f12725d9SJohn Hurley return -ENOENT;
1455f12725d9SJohn Hurley
14565c4f5e19SLouis Peens /* Set/clear IPV6 bit. cpu_to_be16() swap will lead to MSB being
14575c4f5e19SLouis Peens * set/clear for port_idx.
14585c4f5e19SLouis Peens */
14595c4f5e19SLouis Peens key_meta = (struct nfp_flower_meta_tci *)flow->unmasked_data;
14605c4f5e19SLouis Peens if (key_meta->nfp_flow_key_layer & NFP_FLOWER_LAYER_IPV6)
14615c4f5e19SLouis Peens mac_entry->index |= NFP_TUN_PRE_TUN_IPV6_BIT;
14625c4f5e19SLouis Peens else
14635c4f5e19SLouis Peens mac_entry->index &= ~NFP_TUN_PRE_TUN_IPV6_BIT;
14645c4f5e19SLouis Peens
1465f12725d9SJohn Hurley payload.port_idx = cpu_to_be16(mac_entry->index);
1466f12725d9SJohn Hurley
1467f12725d9SJohn Hurley /* Copy mac id and vlan to flow - dev may not exist at delete time. */
1468f12725d9SJohn Hurley flow->pre_tun_rule.vlan_tci = payload.vlan_tci;
1469f12725d9SJohn Hurley flow->pre_tun_rule.port_idx = payload.port_idx;
1470f12725d9SJohn Hurley
1471f12725d9SJohn Hurley err = nfp_flower_xmit_tun_conf(app, NFP_FLOWER_CMSG_TYPE_PRE_TUN_RULE,
1472f12725d9SJohn Hurley sizeof(struct nfp_tun_pre_tun_rule),
1473f12725d9SJohn Hurley (unsigned char *)&payload, GFP_KERNEL);
1474f12725d9SJohn Hurley if (err)
1475f12725d9SJohn Hurley return err;
1476f12725d9SJohn Hurley
1477f12725d9SJohn Hurley app_priv->pre_tun_rule_cnt++;
1478f12725d9SJohn Hurley
1479f12725d9SJohn Hurley return 0;
1480120ffd84SJohn Hurley }
1481120ffd84SJohn Hurley
nfp_flower_xmit_pre_tun_del_flow(struct nfp_app * app,struct nfp_fl_payload * flow)1482120ffd84SJohn Hurley int nfp_flower_xmit_pre_tun_del_flow(struct nfp_app *app,
1483120ffd84SJohn Hurley struct nfp_fl_payload *flow)
1484120ffd84SJohn Hurley {
1485f12725d9SJohn Hurley struct nfp_flower_priv *app_priv = app->priv;
1486f12725d9SJohn Hurley struct nfp_tun_pre_tun_rule payload;
1487f12725d9SJohn Hurley u32 tmp_flags = 0;
1488f12725d9SJohn Hurley int err;
1489f12725d9SJohn Hurley
1490f12725d9SJohn Hurley memset(&payload, 0, sizeof(struct nfp_tun_pre_tun_rule));
1491f12725d9SJohn Hurley
1492f12725d9SJohn Hurley tmp_flags |= NFP_TUN_PRE_TUN_RULE_DEL;
1493f12725d9SJohn Hurley payload.flags = cpu_to_be32(tmp_flags);
1494f12725d9SJohn Hurley payload.vlan_tci = flow->pre_tun_rule.vlan_tci;
1495f12725d9SJohn Hurley payload.port_idx = flow->pre_tun_rule.port_idx;
1496f12725d9SJohn Hurley
1497f12725d9SJohn Hurley err = nfp_flower_xmit_tun_conf(app, NFP_FLOWER_CMSG_TYPE_PRE_TUN_RULE,
1498f12725d9SJohn Hurley sizeof(struct nfp_tun_pre_tun_rule),
1499f12725d9SJohn Hurley (unsigned char *)&payload, GFP_KERNEL);
1500f12725d9SJohn Hurley if (err)
1501f12725d9SJohn Hurley return err;
1502f12725d9SJohn Hurley
1503f12725d9SJohn Hurley app_priv->pre_tun_rule_cnt--;
1504f12725d9SJohn Hurley
1505f12725d9SJohn Hurley return 0;
1506120ffd84SJohn Hurley }
1507120ffd84SJohn Hurley
nfp_tunnel_config_start(struct nfp_app * app)1508fd0dd1abSJohn Hurley int nfp_tunnel_config_start(struct nfp_app *app)
1509fd0dd1abSJohn Hurley {
1510fd0dd1abSJohn Hurley struct nfp_flower_priv *priv = app->priv;
151120cce886SJohn Hurley int err;
1512fd0dd1abSJohn Hurley
151320cce886SJohn Hurley /* Initialise rhash for MAC offload tracking. */
151420cce886SJohn Hurley err = rhashtable_init(&priv->tun.offloaded_macs,
151520cce886SJohn Hurley &offloaded_macs_params);
151620cce886SJohn Hurley if (err)
151720cce886SJohn Hurley return err;
151820cce886SJohn Hurley
1519f3b97577SJohn Hurley ida_init(&priv->tun.mac_off_ids);
1520fd0dd1abSJohn Hurley
1521cfa18993SJohn Hurley /* Initialise priv data for IPv4/v6 offloading. */
1522f3b97577SJohn Hurley mutex_init(&priv->tun.ipv4_off_lock);
1523f3b97577SJohn Hurley INIT_LIST_HEAD(&priv->tun.ipv4_off_list);
1524cfa18993SJohn Hurley mutex_init(&priv->tun.ipv6_off_lock);
1525cfa18993SJohn Hurley INIT_LIST_HEAD(&priv->tun.ipv6_off_list);
15262d9ad71aSJohn Hurley
15278e6a9046SJohn Hurley /* Initialise priv data for neighbour offloading. */
1528f3b97577SJohn Hurley priv->tun.neigh_nb.notifier_call = nfp_tun_neigh_event_handler;
15298e6a9046SJohn Hurley
153020cce886SJohn Hurley err = register_netevent_notifier(&priv->tun.neigh_nb);
153120cce886SJohn Hurley if (err) {
153220cce886SJohn Hurley rhashtable_free_and_destroy(&priv->tun.offloaded_macs,
153320cce886SJohn Hurley nfp_check_rhashtable_empty, NULL);
153420cce886SJohn Hurley return err;
153520cce886SJohn Hurley }
153620cce886SJohn Hurley
153720cce886SJohn Hurley return 0;
1538fd0dd1abSJohn Hurley }
1539fd0dd1abSJohn Hurley
nfp_tunnel_config_stop(struct nfp_app * app)1540fd0dd1abSJohn Hurley void nfp_tunnel_config_stop(struct nfp_app *app)
1541fd0dd1abSJohn Hurley {
1542fd0dd1abSJohn Hurley struct nfp_flower_priv *priv = app->priv;
15432d9ad71aSJohn Hurley struct nfp_ipv4_addr_entry *ip_entry;
1544fd0dd1abSJohn Hurley struct list_head *ptr, *storage;
1545fd0dd1abSJohn Hurley
1546f3b97577SJohn Hurley unregister_netevent_notifier(&priv->tun.neigh_nb);
1547fd0dd1abSJohn Hurley
1548f3b97577SJohn Hurley ida_destroy(&priv->tun.mac_off_ids);
15492d9ad71aSJohn Hurley
15502d9ad71aSJohn Hurley /* Free any memory that may be occupied by ipv4 list. */
1551f3b97577SJohn Hurley list_for_each_safe(ptr, storage, &priv->tun.ipv4_off_list) {
15522d9ad71aSJohn Hurley ip_entry = list_entry(ptr, struct nfp_ipv4_addr_entry, list);
15532d9ad71aSJohn Hurley list_del(&ip_entry->list);
15542d9ad71aSJohn Hurley kfree(ip_entry);
15552d9ad71aSJohn Hurley }
15568e6a9046SJohn Hurley
1557cfa18993SJohn Hurley mutex_destroy(&priv->tun.ipv6_off_lock);
1558cfa18993SJohn Hurley
155920cce886SJohn Hurley /* Destroy rhash. Entries should be cleaned on netdev notifier unreg. */
156020cce886SJohn Hurley rhashtable_free_and_destroy(&priv->tun.offloaded_macs,
156120cce886SJohn Hurley nfp_check_rhashtable_empty, NULL);
1562591c90a1SLouis Peens
1563591c90a1SLouis Peens nfp_tun_cleanup_nn_entries(app);
1564fd0dd1abSJohn Hurley }
1565