18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/net/ethernet/rocker/rocker_ofdpa.c - Rocker switch OF-DPA-like 48c2ecf20Sopenharmony_ci * implementation 58c2ecf20Sopenharmony_ci * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com> 68c2ecf20Sopenharmony_ci * Copyright (c) 2014-2016 Jiri Pirko <jiri@mellanox.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 128c2ecf20Sopenharmony_ci#include <linux/hashtable.h> 138c2ecf20Sopenharmony_ci#include <linux/crc32.h> 148c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 158c2ecf20Sopenharmony_ci#include <linux/inetdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 178c2ecf20Sopenharmony_ci#include <linux/if_bridge.h> 188c2ecf20Sopenharmony_ci#include <net/neighbour.h> 198c2ecf20Sopenharmony_ci#include <net/switchdev.h> 208c2ecf20Sopenharmony_ci#include <net/ip_fib.h> 218c2ecf20Sopenharmony_ci#include <net/nexthop.h> 228c2ecf20Sopenharmony_ci#include <net/arp.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "rocker.h" 258c2ecf20Sopenharmony_ci#include "rocker_tlv.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct ofdpa_flow_tbl_key { 288c2ecf20Sopenharmony_ci u32 priority; 298c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id tbl_id; 308c2ecf20Sopenharmony_ci union { 318c2ecf20Sopenharmony_ci struct { 328c2ecf20Sopenharmony_ci u32 in_pport; 338c2ecf20Sopenharmony_ci u32 in_pport_mask; 348c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl; 358c2ecf20Sopenharmony_ci } ig_port; 368c2ecf20Sopenharmony_ci struct { 378c2ecf20Sopenharmony_ci u32 in_pport; 388c2ecf20Sopenharmony_ci __be16 vlan_id; 398c2ecf20Sopenharmony_ci __be16 vlan_id_mask; 408c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl; 418c2ecf20Sopenharmony_ci bool untagged; 428c2ecf20Sopenharmony_ci __be16 new_vlan_id; 438c2ecf20Sopenharmony_ci } vlan; 448c2ecf20Sopenharmony_ci struct { 458c2ecf20Sopenharmony_ci u32 in_pport; 468c2ecf20Sopenharmony_ci u32 in_pport_mask; 478c2ecf20Sopenharmony_ci __be16 eth_type; 488c2ecf20Sopenharmony_ci u8 eth_dst[ETH_ALEN]; 498c2ecf20Sopenharmony_ci u8 eth_dst_mask[ETH_ALEN]; 508c2ecf20Sopenharmony_ci __be16 vlan_id; 518c2ecf20Sopenharmony_ci __be16 vlan_id_mask; 528c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl; 538c2ecf20Sopenharmony_ci bool copy_to_cpu; 548c2ecf20Sopenharmony_ci } term_mac; 558c2ecf20Sopenharmony_ci struct { 568c2ecf20Sopenharmony_ci __be16 eth_type; 578c2ecf20Sopenharmony_ci __be32 dst4; 588c2ecf20Sopenharmony_ci __be32 dst4_mask; 598c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl; 608c2ecf20Sopenharmony_ci u32 group_id; 618c2ecf20Sopenharmony_ci } ucast_routing; 628c2ecf20Sopenharmony_ci struct { 638c2ecf20Sopenharmony_ci u8 eth_dst[ETH_ALEN]; 648c2ecf20Sopenharmony_ci u8 eth_dst_mask[ETH_ALEN]; 658c2ecf20Sopenharmony_ci int has_eth_dst; 668c2ecf20Sopenharmony_ci int has_eth_dst_mask; 678c2ecf20Sopenharmony_ci __be16 vlan_id; 688c2ecf20Sopenharmony_ci u32 tunnel_id; 698c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl; 708c2ecf20Sopenharmony_ci u32 group_id; 718c2ecf20Sopenharmony_ci bool copy_to_cpu; 728c2ecf20Sopenharmony_ci } bridge; 738c2ecf20Sopenharmony_ci struct { 748c2ecf20Sopenharmony_ci u32 in_pport; 758c2ecf20Sopenharmony_ci u32 in_pport_mask; 768c2ecf20Sopenharmony_ci u8 eth_src[ETH_ALEN]; 778c2ecf20Sopenharmony_ci u8 eth_src_mask[ETH_ALEN]; 788c2ecf20Sopenharmony_ci u8 eth_dst[ETH_ALEN]; 798c2ecf20Sopenharmony_ci u8 eth_dst_mask[ETH_ALEN]; 808c2ecf20Sopenharmony_ci __be16 eth_type; 818c2ecf20Sopenharmony_ci __be16 vlan_id; 828c2ecf20Sopenharmony_ci __be16 vlan_id_mask; 838c2ecf20Sopenharmony_ci u8 ip_proto; 848c2ecf20Sopenharmony_ci u8 ip_proto_mask; 858c2ecf20Sopenharmony_ci u8 ip_tos; 868c2ecf20Sopenharmony_ci u8 ip_tos_mask; 878c2ecf20Sopenharmony_ci u32 group_id; 888c2ecf20Sopenharmony_ci } acl; 898c2ecf20Sopenharmony_ci }; 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistruct ofdpa_flow_tbl_entry { 938c2ecf20Sopenharmony_ci struct hlist_node entry; 948c2ecf20Sopenharmony_ci u32 cmd; 958c2ecf20Sopenharmony_ci u64 cookie; 968c2ecf20Sopenharmony_ci struct ofdpa_flow_tbl_key key; 978c2ecf20Sopenharmony_ci size_t key_len; 988c2ecf20Sopenharmony_ci u32 key_crc32; /* key */ 998c2ecf20Sopenharmony_ci struct fib_info *fi; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistruct ofdpa_group_tbl_entry { 1038c2ecf20Sopenharmony_ci struct hlist_node entry; 1048c2ecf20Sopenharmony_ci u32 cmd; 1058c2ecf20Sopenharmony_ci u32 group_id; /* key */ 1068c2ecf20Sopenharmony_ci u16 group_count; 1078c2ecf20Sopenharmony_ci u32 *group_ids; 1088c2ecf20Sopenharmony_ci union { 1098c2ecf20Sopenharmony_ci struct { 1108c2ecf20Sopenharmony_ci u8 pop_vlan; 1118c2ecf20Sopenharmony_ci } l2_interface; 1128c2ecf20Sopenharmony_ci struct { 1138c2ecf20Sopenharmony_ci u8 eth_src[ETH_ALEN]; 1148c2ecf20Sopenharmony_ci u8 eth_dst[ETH_ALEN]; 1158c2ecf20Sopenharmony_ci __be16 vlan_id; 1168c2ecf20Sopenharmony_ci u32 group_id; 1178c2ecf20Sopenharmony_ci } l2_rewrite; 1188c2ecf20Sopenharmony_ci struct { 1198c2ecf20Sopenharmony_ci u8 eth_src[ETH_ALEN]; 1208c2ecf20Sopenharmony_ci u8 eth_dst[ETH_ALEN]; 1218c2ecf20Sopenharmony_ci __be16 vlan_id; 1228c2ecf20Sopenharmony_ci bool ttl_check; 1238c2ecf20Sopenharmony_ci u32 group_id; 1248c2ecf20Sopenharmony_ci } l3_unicast; 1258c2ecf20Sopenharmony_ci }; 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistruct ofdpa_fdb_tbl_entry { 1298c2ecf20Sopenharmony_ci struct hlist_node entry; 1308c2ecf20Sopenharmony_ci u32 key_crc32; /* key */ 1318c2ecf20Sopenharmony_ci bool learned; 1328c2ecf20Sopenharmony_ci unsigned long touched; 1338c2ecf20Sopenharmony_ci struct ofdpa_fdb_tbl_key { 1348c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port; 1358c2ecf20Sopenharmony_ci u8 addr[ETH_ALEN]; 1368c2ecf20Sopenharmony_ci __be16 vlan_id; 1378c2ecf20Sopenharmony_ci } key; 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_cistruct ofdpa_internal_vlan_tbl_entry { 1418c2ecf20Sopenharmony_ci struct hlist_node entry; 1428c2ecf20Sopenharmony_ci int ifindex; /* key */ 1438c2ecf20Sopenharmony_ci u32 ref_count; 1448c2ecf20Sopenharmony_ci __be16 vlan_id; 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistruct ofdpa_neigh_tbl_entry { 1488c2ecf20Sopenharmony_ci struct hlist_node entry; 1498c2ecf20Sopenharmony_ci __be32 ip_addr; /* key */ 1508c2ecf20Sopenharmony_ci struct net_device *dev; 1518c2ecf20Sopenharmony_ci u32 ref_count; 1528c2ecf20Sopenharmony_ci u32 index; 1538c2ecf20Sopenharmony_ci u8 eth_dst[ETH_ALEN]; 1548c2ecf20Sopenharmony_ci bool ttl_check; 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cienum { 1588c2ecf20Sopenharmony_ci OFDPA_CTRL_LINK_LOCAL_MCAST, 1598c2ecf20Sopenharmony_ci OFDPA_CTRL_LOCAL_ARP, 1608c2ecf20Sopenharmony_ci OFDPA_CTRL_IPV4_MCAST, 1618c2ecf20Sopenharmony_ci OFDPA_CTRL_IPV6_MCAST, 1628c2ecf20Sopenharmony_ci OFDPA_CTRL_DFLT_BRIDGING, 1638c2ecf20Sopenharmony_ci OFDPA_CTRL_DFLT_OVS, 1648c2ecf20Sopenharmony_ci OFDPA_CTRL_MAX, 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci#define OFDPA_INTERNAL_VLAN_ID_BASE 0x0f00 1688c2ecf20Sopenharmony_ci#define OFDPA_N_INTERNAL_VLANS 255 1698c2ecf20Sopenharmony_ci#define OFDPA_VLAN_BITMAP_LEN BITS_TO_LONGS(VLAN_N_VID) 1708c2ecf20Sopenharmony_ci#define OFDPA_INTERNAL_VLAN_BITMAP_LEN BITS_TO_LONGS(OFDPA_N_INTERNAL_VLANS) 1718c2ecf20Sopenharmony_ci#define OFDPA_UNTAGGED_VID 0 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistruct ofdpa { 1748c2ecf20Sopenharmony_ci struct rocker *rocker; 1758c2ecf20Sopenharmony_ci DECLARE_HASHTABLE(flow_tbl, 16); 1768c2ecf20Sopenharmony_ci spinlock_t flow_tbl_lock; /* for flow tbl accesses */ 1778c2ecf20Sopenharmony_ci u64 flow_tbl_next_cookie; 1788c2ecf20Sopenharmony_ci DECLARE_HASHTABLE(group_tbl, 16); 1798c2ecf20Sopenharmony_ci spinlock_t group_tbl_lock; /* for group tbl accesses */ 1808c2ecf20Sopenharmony_ci struct timer_list fdb_cleanup_timer; 1818c2ecf20Sopenharmony_ci DECLARE_HASHTABLE(fdb_tbl, 16); 1828c2ecf20Sopenharmony_ci spinlock_t fdb_tbl_lock; /* for fdb tbl accesses */ 1838c2ecf20Sopenharmony_ci unsigned long internal_vlan_bitmap[OFDPA_INTERNAL_VLAN_BITMAP_LEN]; 1848c2ecf20Sopenharmony_ci DECLARE_HASHTABLE(internal_vlan_tbl, 8); 1858c2ecf20Sopenharmony_ci spinlock_t internal_vlan_tbl_lock; /* for vlan tbl accesses */ 1868c2ecf20Sopenharmony_ci DECLARE_HASHTABLE(neigh_tbl, 16); 1878c2ecf20Sopenharmony_ci spinlock_t neigh_tbl_lock; /* for neigh tbl accesses */ 1888c2ecf20Sopenharmony_ci u32 neigh_tbl_next_index; 1898c2ecf20Sopenharmony_ci unsigned long ageing_time; 1908c2ecf20Sopenharmony_ci bool fib_aborted; 1918c2ecf20Sopenharmony_ci}; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cistruct ofdpa_port { 1948c2ecf20Sopenharmony_ci struct ofdpa *ofdpa; 1958c2ecf20Sopenharmony_ci struct rocker_port *rocker_port; 1968c2ecf20Sopenharmony_ci struct net_device *dev; 1978c2ecf20Sopenharmony_ci u32 pport; 1988c2ecf20Sopenharmony_ci struct net_device *bridge_dev; 1998c2ecf20Sopenharmony_ci __be16 internal_vlan_id; 2008c2ecf20Sopenharmony_ci int stp_state; 2018c2ecf20Sopenharmony_ci u32 brport_flags; 2028c2ecf20Sopenharmony_ci unsigned long ageing_time; 2038c2ecf20Sopenharmony_ci bool ctrls[OFDPA_CTRL_MAX]; 2048c2ecf20Sopenharmony_ci unsigned long vlan_bitmap[OFDPA_VLAN_BITMAP_LEN]; 2058c2ecf20Sopenharmony_ci}; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic const u8 zero_mac[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 2088c2ecf20Sopenharmony_cistatic const u8 ff_mac[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 2098c2ecf20Sopenharmony_cistatic const u8 ll_mac[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; 2108c2ecf20Sopenharmony_cistatic const u8 ll_mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0 }; 2118c2ecf20Sopenharmony_cistatic const u8 mcast_mac[ETH_ALEN] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; 2128c2ecf20Sopenharmony_cistatic const u8 ipv4_mcast[ETH_ALEN] = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 }; 2138c2ecf20Sopenharmony_cistatic const u8 ipv4_mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0x80, 0x00, 0x00 }; 2148c2ecf20Sopenharmony_cistatic const u8 ipv6_mcast[ETH_ALEN] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 }; 2158c2ecf20Sopenharmony_cistatic const u8 ipv6_mask[ETH_ALEN] = { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci/* Rocker priority levels for flow table entries. Higher 2188c2ecf20Sopenharmony_ci * priority match takes precedence over lower priority match. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cienum { 2228c2ecf20Sopenharmony_ci OFDPA_PRIORITY_UNKNOWN = 0, 2238c2ecf20Sopenharmony_ci OFDPA_PRIORITY_IG_PORT = 1, 2248c2ecf20Sopenharmony_ci OFDPA_PRIORITY_VLAN = 1, 2258c2ecf20Sopenharmony_ci OFDPA_PRIORITY_TERM_MAC_UCAST = 0, 2268c2ecf20Sopenharmony_ci OFDPA_PRIORITY_TERM_MAC_MCAST = 1, 2278c2ecf20Sopenharmony_ci OFDPA_PRIORITY_BRIDGING_VLAN_DFLT_EXACT = 1, 2288c2ecf20Sopenharmony_ci OFDPA_PRIORITY_BRIDGING_VLAN_DFLT_WILD = 2, 2298c2ecf20Sopenharmony_ci OFDPA_PRIORITY_BRIDGING_VLAN = 3, 2308c2ecf20Sopenharmony_ci OFDPA_PRIORITY_BRIDGING_TENANT_DFLT_EXACT = 1, 2318c2ecf20Sopenharmony_ci OFDPA_PRIORITY_BRIDGING_TENANT_DFLT_WILD = 2, 2328c2ecf20Sopenharmony_ci OFDPA_PRIORITY_BRIDGING_TENANT = 3, 2338c2ecf20Sopenharmony_ci OFDPA_PRIORITY_ACL_CTRL = 3, 2348c2ecf20Sopenharmony_ci OFDPA_PRIORITY_ACL_NORMAL = 2, 2358c2ecf20Sopenharmony_ci OFDPA_PRIORITY_ACL_DFLT = 1, 2368c2ecf20Sopenharmony_ci}; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic bool ofdpa_vlan_id_is_internal(__be16 vlan_id) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci u16 start = OFDPA_INTERNAL_VLAN_ID_BASE; 2418c2ecf20Sopenharmony_ci u16 end = 0xffe; 2428c2ecf20Sopenharmony_ci u16 _vlan_id = ntohs(vlan_id); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return (_vlan_id >= start && _vlan_id <= end); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic __be16 ofdpa_port_vid_to_vlan(const struct ofdpa_port *ofdpa_port, 2488c2ecf20Sopenharmony_ci u16 vid, bool *pop_vlan) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci __be16 vlan_id; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (pop_vlan) 2538c2ecf20Sopenharmony_ci *pop_vlan = false; 2548c2ecf20Sopenharmony_ci vlan_id = htons(vid); 2558c2ecf20Sopenharmony_ci if (!vlan_id) { 2568c2ecf20Sopenharmony_ci vlan_id = ofdpa_port->internal_vlan_id; 2578c2ecf20Sopenharmony_ci if (pop_vlan) 2588c2ecf20Sopenharmony_ci *pop_vlan = true; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci return vlan_id; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic u16 ofdpa_port_vlan_to_vid(const struct ofdpa_port *ofdpa_port, 2658c2ecf20Sopenharmony_ci __be16 vlan_id) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci if (ofdpa_vlan_id_is_internal(vlan_id)) 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return ntohs(vlan_id); 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic bool ofdpa_port_is_slave(const struct ofdpa_port *ofdpa_port, 2748c2ecf20Sopenharmony_ci const char *kind) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci return ofdpa_port->bridge_dev && 2778c2ecf20Sopenharmony_ci !strcmp(ofdpa_port->bridge_dev->rtnl_link_ops->kind, kind); 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic bool ofdpa_port_is_bridged(const struct ofdpa_port *ofdpa_port) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci return ofdpa_port_is_slave(ofdpa_port, "bridge"); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic bool ofdpa_port_is_ovsed(const struct ofdpa_port *ofdpa_port) 2868c2ecf20Sopenharmony_ci{ 2878c2ecf20Sopenharmony_ci return ofdpa_port_is_slave(ofdpa_port, "openvswitch"); 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci#define OFDPA_OP_FLAG_REMOVE BIT(0) 2918c2ecf20Sopenharmony_ci#define OFDPA_OP_FLAG_NOWAIT BIT(1) 2928c2ecf20Sopenharmony_ci#define OFDPA_OP_FLAG_LEARNED BIT(2) 2938c2ecf20Sopenharmony_ci#define OFDPA_OP_FLAG_REFRESH BIT(3) 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic bool ofdpa_flags_nowait(int flags) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci return flags & OFDPA_OP_FLAG_NOWAIT; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/************************************************************* 3018c2ecf20Sopenharmony_ci * Flow, group, FDB, internal VLAN and neigh command prepares 3028c2ecf20Sopenharmony_ci *************************************************************/ 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int 3058c2ecf20Sopenharmony_ciofdpa_cmd_flow_tbl_add_ig_port(struct rocker_desc_info *desc_info, 3068c2ecf20Sopenharmony_ci const struct ofdpa_flow_tbl_entry *entry) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT, 3098c2ecf20Sopenharmony_ci entry->key.ig_port.in_pport)) 3108c2ecf20Sopenharmony_ci return -EMSGSIZE; 3118c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT_MASK, 3128c2ecf20Sopenharmony_ci entry->key.ig_port.in_pport_mask)) 3138c2ecf20Sopenharmony_ci return -EMSGSIZE; 3148c2ecf20Sopenharmony_ci if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID, 3158c2ecf20Sopenharmony_ci entry->key.ig_port.goto_tbl)) 3168c2ecf20Sopenharmony_ci return -EMSGSIZE; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return 0; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic int 3228c2ecf20Sopenharmony_ciofdpa_cmd_flow_tbl_add_vlan(struct rocker_desc_info *desc_info, 3238c2ecf20Sopenharmony_ci const struct ofdpa_flow_tbl_entry *entry) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT, 3268c2ecf20Sopenharmony_ci entry->key.vlan.in_pport)) 3278c2ecf20Sopenharmony_ci return -EMSGSIZE; 3288c2ecf20Sopenharmony_ci if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID, 3298c2ecf20Sopenharmony_ci entry->key.vlan.vlan_id)) 3308c2ecf20Sopenharmony_ci return -EMSGSIZE; 3318c2ecf20Sopenharmony_ci if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID_MASK, 3328c2ecf20Sopenharmony_ci entry->key.vlan.vlan_id_mask)) 3338c2ecf20Sopenharmony_ci return -EMSGSIZE; 3348c2ecf20Sopenharmony_ci if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID, 3358c2ecf20Sopenharmony_ci entry->key.vlan.goto_tbl)) 3368c2ecf20Sopenharmony_ci return -EMSGSIZE; 3378c2ecf20Sopenharmony_ci if (entry->key.vlan.untagged && 3388c2ecf20Sopenharmony_ci rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_NEW_VLAN_ID, 3398c2ecf20Sopenharmony_ci entry->key.vlan.new_vlan_id)) 3408c2ecf20Sopenharmony_ci return -EMSGSIZE; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic int 3468c2ecf20Sopenharmony_ciofdpa_cmd_flow_tbl_add_term_mac(struct rocker_desc_info *desc_info, 3478c2ecf20Sopenharmony_ci const struct ofdpa_flow_tbl_entry *entry) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT, 3508c2ecf20Sopenharmony_ci entry->key.term_mac.in_pport)) 3518c2ecf20Sopenharmony_ci return -EMSGSIZE; 3528c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT_MASK, 3538c2ecf20Sopenharmony_ci entry->key.term_mac.in_pport_mask)) 3548c2ecf20Sopenharmony_ci return -EMSGSIZE; 3558c2ecf20Sopenharmony_ci if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_ETHERTYPE, 3568c2ecf20Sopenharmony_ci entry->key.term_mac.eth_type)) 3578c2ecf20Sopenharmony_ci return -EMSGSIZE; 3588c2ecf20Sopenharmony_ci if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC, 3598c2ecf20Sopenharmony_ci ETH_ALEN, entry->key.term_mac.eth_dst)) 3608c2ecf20Sopenharmony_ci return -EMSGSIZE; 3618c2ecf20Sopenharmony_ci if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC_MASK, 3628c2ecf20Sopenharmony_ci ETH_ALEN, entry->key.term_mac.eth_dst_mask)) 3638c2ecf20Sopenharmony_ci return -EMSGSIZE; 3648c2ecf20Sopenharmony_ci if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID, 3658c2ecf20Sopenharmony_ci entry->key.term_mac.vlan_id)) 3668c2ecf20Sopenharmony_ci return -EMSGSIZE; 3678c2ecf20Sopenharmony_ci if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID_MASK, 3688c2ecf20Sopenharmony_ci entry->key.term_mac.vlan_id_mask)) 3698c2ecf20Sopenharmony_ci return -EMSGSIZE; 3708c2ecf20Sopenharmony_ci if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID, 3718c2ecf20Sopenharmony_ci entry->key.term_mac.goto_tbl)) 3728c2ecf20Sopenharmony_ci return -EMSGSIZE; 3738c2ecf20Sopenharmony_ci if (entry->key.term_mac.copy_to_cpu && 3748c2ecf20Sopenharmony_ci rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_COPY_CPU_ACTION, 3758c2ecf20Sopenharmony_ci entry->key.term_mac.copy_to_cpu)) 3768c2ecf20Sopenharmony_ci return -EMSGSIZE; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci return 0; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int 3828c2ecf20Sopenharmony_ciofdpa_cmd_flow_tbl_add_ucast_routing(struct rocker_desc_info *desc_info, 3838c2ecf20Sopenharmony_ci const struct ofdpa_flow_tbl_entry *entry) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_ETHERTYPE, 3868c2ecf20Sopenharmony_ci entry->key.ucast_routing.eth_type)) 3878c2ecf20Sopenharmony_ci return -EMSGSIZE; 3888c2ecf20Sopenharmony_ci if (rocker_tlv_put_be32(desc_info, ROCKER_TLV_OF_DPA_DST_IP, 3898c2ecf20Sopenharmony_ci entry->key.ucast_routing.dst4)) 3908c2ecf20Sopenharmony_ci return -EMSGSIZE; 3918c2ecf20Sopenharmony_ci if (rocker_tlv_put_be32(desc_info, ROCKER_TLV_OF_DPA_DST_IP_MASK, 3928c2ecf20Sopenharmony_ci entry->key.ucast_routing.dst4_mask)) 3938c2ecf20Sopenharmony_ci return -EMSGSIZE; 3948c2ecf20Sopenharmony_ci if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID, 3958c2ecf20Sopenharmony_ci entry->key.ucast_routing.goto_tbl)) 3968c2ecf20Sopenharmony_ci return -EMSGSIZE; 3978c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID, 3988c2ecf20Sopenharmony_ci entry->key.ucast_routing.group_id)) 3998c2ecf20Sopenharmony_ci return -EMSGSIZE; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic int 4058c2ecf20Sopenharmony_ciofdpa_cmd_flow_tbl_add_bridge(struct rocker_desc_info *desc_info, 4068c2ecf20Sopenharmony_ci const struct ofdpa_flow_tbl_entry *entry) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci if (entry->key.bridge.has_eth_dst && 4098c2ecf20Sopenharmony_ci rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC, 4108c2ecf20Sopenharmony_ci ETH_ALEN, entry->key.bridge.eth_dst)) 4118c2ecf20Sopenharmony_ci return -EMSGSIZE; 4128c2ecf20Sopenharmony_ci if (entry->key.bridge.has_eth_dst_mask && 4138c2ecf20Sopenharmony_ci rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC_MASK, 4148c2ecf20Sopenharmony_ci ETH_ALEN, entry->key.bridge.eth_dst_mask)) 4158c2ecf20Sopenharmony_ci return -EMSGSIZE; 4168c2ecf20Sopenharmony_ci if (entry->key.bridge.vlan_id && 4178c2ecf20Sopenharmony_ci rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID, 4188c2ecf20Sopenharmony_ci entry->key.bridge.vlan_id)) 4198c2ecf20Sopenharmony_ci return -EMSGSIZE; 4208c2ecf20Sopenharmony_ci if (entry->key.bridge.tunnel_id && 4218c2ecf20Sopenharmony_ci rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_TUNNEL_ID, 4228c2ecf20Sopenharmony_ci entry->key.bridge.tunnel_id)) 4238c2ecf20Sopenharmony_ci return -EMSGSIZE; 4248c2ecf20Sopenharmony_ci if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID, 4258c2ecf20Sopenharmony_ci entry->key.bridge.goto_tbl)) 4268c2ecf20Sopenharmony_ci return -EMSGSIZE; 4278c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID, 4288c2ecf20Sopenharmony_ci entry->key.bridge.group_id)) 4298c2ecf20Sopenharmony_ci return -EMSGSIZE; 4308c2ecf20Sopenharmony_ci if (entry->key.bridge.copy_to_cpu && 4318c2ecf20Sopenharmony_ci rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_COPY_CPU_ACTION, 4328c2ecf20Sopenharmony_ci entry->key.bridge.copy_to_cpu)) 4338c2ecf20Sopenharmony_ci return -EMSGSIZE; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int 4398c2ecf20Sopenharmony_ciofdpa_cmd_flow_tbl_add_acl(struct rocker_desc_info *desc_info, 4408c2ecf20Sopenharmony_ci const struct ofdpa_flow_tbl_entry *entry) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT, 4438c2ecf20Sopenharmony_ci entry->key.acl.in_pport)) 4448c2ecf20Sopenharmony_ci return -EMSGSIZE; 4458c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT_MASK, 4468c2ecf20Sopenharmony_ci entry->key.acl.in_pport_mask)) 4478c2ecf20Sopenharmony_ci return -EMSGSIZE; 4488c2ecf20Sopenharmony_ci if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC, 4498c2ecf20Sopenharmony_ci ETH_ALEN, entry->key.acl.eth_src)) 4508c2ecf20Sopenharmony_ci return -EMSGSIZE; 4518c2ecf20Sopenharmony_ci if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC_MASK, 4528c2ecf20Sopenharmony_ci ETH_ALEN, entry->key.acl.eth_src_mask)) 4538c2ecf20Sopenharmony_ci return -EMSGSIZE; 4548c2ecf20Sopenharmony_ci if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC, 4558c2ecf20Sopenharmony_ci ETH_ALEN, entry->key.acl.eth_dst)) 4568c2ecf20Sopenharmony_ci return -EMSGSIZE; 4578c2ecf20Sopenharmony_ci if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC_MASK, 4588c2ecf20Sopenharmony_ci ETH_ALEN, entry->key.acl.eth_dst_mask)) 4598c2ecf20Sopenharmony_ci return -EMSGSIZE; 4608c2ecf20Sopenharmony_ci if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_ETHERTYPE, 4618c2ecf20Sopenharmony_ci entry->key.acl.eth_type)) 4628c2ecf20Sopenharmony_ci return -EMSGSIZE; 4638c2ecf20Sopenharmony_ci if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID, 4648c2ecf20Sopenharmony_ci entry->key.acl.vlan_id)) 4658c2ecf20Sopenharmony_ci return -EMSGSIZE; 4668c2ecf20Sopenharmony_ci if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID_MASK, 4678c2ecf20Sopenharmony_ci entry->key.acl.vlan_id_mask)) 4688c2ecf20Sopenharmony_ci return -EMSGSIZE; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci switch (ntohs(entry->key.acl.eth_type)) { 4718c2ecf20Sopenharmony_ci case ETH_P_IP: 4728c2ecf20Sopenharmony_ci case ETH_P_IPV6: 4738c2ecf20Sopenharmony_ci if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_IP_PROTO, 4748c2ecf20Sopenharmony_ci entry->key.acl.ip_proto)) 4758c2ecf20Sopenharmony_ci return -EMSGSIZE; 4768c2ecf20Sopenharmony_ci if (rocker_tlv_put_u8(desc_info, 4778c2ecf20Sopenharmony_ci ROCKER_TLV_OF_DPA_IP_PROTO_MASK, 4788c2ecf20Sopenharmony_ci entry->key.acl.ip_proto_mask)) 4798c2ecf20Sopenharmony_ci return -EMSGSIZE; 4808c2ecf20Sopenharmony_ci if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_IP_DSCP, 4818c2ecf20Sopenharmony_ci entry->key.acl.ip_tos & 0x3f)) 4828c2ecf20Sopenharmony_ci return -EMSGSIZE; 4838c2ecf20Sopenharmony_ci if (rocker_tlv_put_u8(desc_info, 4848c2ecf20Sopenharmony_ci ROCKER_TLV_OF_DPA_IP_DSCP_MASK, 4858c2ecf20Sopenharmony_ci entry->key.acl.ip_tos_mask & 0x3f)) 4868c2ecf20Sopenharmony_ci return -EMSGSIZE; 4878c2ecf20Sopenharmony_ci if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_IP_ECN, 4888c2ecf20Sopenharmony_ci (entry->key.acl.ip_tos & 0xc0) >> 6)) 4898c2ecf20Sopenharmony_ci return -EMSGSIZE; 4908c2ecf20Sopenharmony_ci if (rocker_tlv_put_u8(desc_info, 4918c2ecf20Sopenharmony_ci ROCKER_TLV_OF_DPA_IP_ECN_MASK, 4928c2ecf20Sopenharmony_ci (entry->key.acl.ip_tos_mask & 0xc0) >> 6)) 4938c2ecf20Sopenharmony_ci return -EMSGSIZE; 4948c2ecf20Sopenharmony_ci break; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci if (entry->key.acl.group_id != ROCKER_GROUP_NONE && 4988c2ecf20Sopenharmony_ci rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID, 4998c2ecf20Sopenharmony_ci entry->key.acl.group_id)) 5008c2ecf20Sopenharmony_ci return -EMSGSIZE; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci return 0; 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic int ofdpa_cmd_flow_tbl_add(const struct rocker_port *rocker_port, 5068c2ecf20Sopenharmony_ci struct rocker_desc_info *desc_info, 5078c2ecf20Sopenharmony_ci void *priv) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci const struct ofdpa_flow_tbl_entry *entry = priv; 5108c2ecf20Sopenharmony_ci struct rocker_tlv *cmd_info; 5118c2ecf20Sopenharmony_ci int err = 0; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd)) 5148c2ecf20Sopenharmony_ci return -EMSGSIZE; 5158c2ecf20Sopenharmony_ci cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 5168c2ecf20Sopenharmony_ci if (!cmd_info) 5178c2ecf20Sopenharmony_ci return -EMSGSIZE; 5188c2ecf20Sopenharmony_ci if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_TABLE_ID, 5198c2ecf20Sopenharmony_ci entry->key.tbl_id)) 5208c2ecf20Sopenharmony_ci return -EMSGSIZE; 5218c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_PRIORITY, 5228c2ecf20Sopenharmony_ci entry->key.priority)) 5238c2ecf20Sopenharmony_ci return -EMSGSIZE; 5248c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_HARDTIME, 0)) 5258c2ecf20Sopenharmony_ci return -EMSGSIZE; 5268c2ecf20Sopenharmony_ci if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_OF_DPA_COOKIE, 5278c2ecf20Sopenharmony_ci entry->cookie)) 5288c2ecf20Sopenharmony_ci return -EMSGSIZE; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci switch (entry->key.tbl_id) { 5318c2ecf20Sopenharmony_ci case ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT: 5328c2ecf20Sopenharmony_ci err = ofdpa_cmd_flow_tbl_add_ig_port(desc_info, entry); 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci case ROCKER_OF_DPA_TABLE_ID_VLAN: 5358c2ecf20Sopenharmony_ci err = ofdpa_cmd_flow_tbl_add_vlan(desc_info, entry); 5368c2ecf20Sopenharmony_ci break; 5378c2ecf20Sopenharmony_ci case ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC: 5388c2ecf20Sopenharmony_ci err = ofdpa_cmd_flow_tbl_add_term_mac(desc_info, entry); 5398c2ecf20Sopenharmony_ci break; 5408c2ecf20Sopenharmony_ci case ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING: 5418c2ecf20Sopenharmony_ci err = ofdpa_cmd_flow_tbl_add_ucast_routing(desc_info, entry); 5428c2ecf20Sopenharmony_ci break; 5438c2ecf20Sopenharmony_ci case ROCKER_OF_DPA_TABLE_ID_BRIDGING: 5448c2ecf20Sopenharmony_ci err = ofdpa_cmd_flow_tbl_add_bridge(desc_info, entry); 5458c2ecf20Sopenharmony_ci break; 5468c2ecf20Sopenharmony_ci case ROCKER_OF_DPA_TABLE_ID_ACL_POLICY: 5478c2ecf20Sopenharmony_ci err = ofdpa_cmd_flow_tbl_add_acl(desc_info, entry); 5488c2ecf20Sopenharmony_ci break; 5498c2ecf20Sopenharmony_ci default: 5508c2ecf20Sopenharmony_ci err = -ENOTSUPP; 5518c2ecf20Sopenharmony_ci break; 5528c2ecf20Sopenharmony_ci } 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci if (err) 5558c2ecf20Sopenharmony_ci return err; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci rocker_tlv_nest_end(desc_info, cmd_info); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci return 0; 5608c2ecf20Sopenharmony_ci} 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistatic int ofdpa_cmd_flow_tbl_del(const struct rocker_port *rocker_port, 5638c2ecf20Sopenharmony_ci struct rocker_desc_info *desc_info, 5648c2ecf20Sopenharmony_ci void *priv) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci const struct ofdpa_flow_tbl_entry *entry = priv; 5678c2ecf20Sopenharmony_ci struct rocker_tlv *cmd_info; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd)) 5708c2ecf20Sopenharmony_ci return -EMSGSIZE; 5718c2ecf20Sopenharmony_ci cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 5728c2ecf20Sopenharmony_ci if (!cmd_info) 5738c2ecf20Sopenharmony_ci return -EMSGSIZE; 5748c2ecf20Sopenharmony_ci if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_OF_DPA_COOKIE, 5758c2ecf20Sopenharmony_ci entry->cookie)) 5768c2ecf20Sopenharmony_ci return -EMSGSIZE; 5778c2ecf20Sopenharmony_ci rocker_tlv_nest_end(desc_info, cmd_info); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci return 0; 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistatic int 5838c2ecf20Sopenharmony_ciofdpa_cmd_group_tbl_add_l2_interface(struct rocker_desc_info *desc_info, 5848c2ecf20Sopenharmony_ci struct ofdpa_group_tbl_entry *entry) 5858c2ecf20Sopenharmony_ci{ 5868c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_OUT_PPORT, 5878c2ecf20Sopenharmony_ci ROCKER_GROUP_PORT_GET(entry->group_id))) 5888c2ecf20Sopenharmony_ci return -EMSGSIZE; 5898c2ecf20Sopenharmony_ci if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_POP_VLAN, 5908c2ecf20Sopenharmony_ci entry->l2_interface.pop_vlan)) 5918c2ecf20Sopenharmony_ci return -EMSGSIZE; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci return 0; 5948c2ecf20Sopenharmony_ci} 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_cistatic int 5978c2ecf20Sopenharmony_ciofdpa_cmd_group_tbl_add_l2_rewrite(struct rocker_desc_info *desc_info, 5988c2ecf20Sopenharmony_ci const struct ofdpa_group_tbl_entry *entry) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID_LOWER, 6018c2ecf20Sopenharmony_ci entry->l2_rewrite.group_id)) 6028c2ecf20Sopenharmony_ci return -EMSGSIZE; 6038c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(entry->l2_rewrite.eth_src) && 6048c2ecf20Sopenharmony_ci rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC, 6058c2ecf20Sopenharmony_ci ETH_ALEN, entry->l2_rewrite.eth_src)) 6068c2ecf20Sopenharmony_ci return -EMSGSIZE; 6078c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(entry->l2_rewrite.eth_dst) && 6088c2ecf20Sopenharmony_ci rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC, 6098c2ecf20Sopenharmony_ci ETH_ALEN, entry->l2_rewrite.eth_dst)) 6108c2ecf20Sopenharmony_ci return -EMSGSIZE; 6118c2ecf20Sopenharmony_ci if (entry->l2_rewrite.vlan_id && 6128c2ecf20Sopenharmony_ci rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID, 6138c2ecf20Sopenharmony_ci entry->l2_rewrite.vlan_id)) 6148c2ecf20Sopenharmony_ci return -EMSGSIZE; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci return 0; 6178c2ecf20Sopenharmony_ci} 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_cistatic int 6208c2ecf20Sopenharmony_ciofdpa_cmd_group_tbl_add_group_ids(struct rocker_desc_info *desc_info, 6218c2ecf20Sopenharmony_ci const struct ofdpa_group_tbl_entry *entry) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci int i; 6248c2ecf20Sopenharmony_ci struct rocker_tlv *group_ids; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GROUP_COUNT, 6278c2ecf20Sopenharmony_ci entry->group_count)) 6288c2ecf20Sopenharmony_ci return -EMSGSIZE; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci group_ids = rocker_tlv_nest_start(desc_info, 6318c2ecf20Sopenharmony_ci ROCKER_TLV_OF_DPA_GROUP_IDS); 6328c2ecf20Sopenharmony_ci if (!group_ids) 6338c2ecf20Sopenharmony_ci return -EMSGSIZE; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci for (i = 0; i < entry->group_count; i++) 6368c2ecf20Sopenharmony_ci /* Note TLV array is 1-based */ 6378c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, i + 1, entry->group_ids[i])) 6388c2ecf20Sopenharmony_ci return -EMSGSIZE; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci rocker_tlv_nest_end(desc_info, group_ids); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci return 0; 6438c2ecf20Sopenharmony_ci} 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cistatic int 6468c2ecf20Sopenharmony_ciofdpa_cmd_group_tbl_add_l3_unicast(struct rocker_desc_info *desc_info, 6478c2ecf20Sopenharmony_ci const struct ofdpa_group_tbl_entry *entry) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(entry->l3_unicast.eth_src) && 6508c2ecf20Sopenharmony_ci rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC, 6518c2ecf20Sopenharmony_ci ETH_ALEN, entry->l3_unicast.eth_src)) 6528c2ecf20Sopenharmony_ci return -EMSGSIZE; 6538c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(entry->l3_unicast.eth_dst) && 6548c2ecf20Sopenharmony_ci rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC, 6558c2ecf20Sopenharmony_ci ETH_ALEN, entry->l3_unicast.eth_dst)) 6568c2ecf20Sopenharmony_ci return -EMSGSIZE; 6578c2ecf20Sopenharmony_ci if (entry->l3_unicast.vlan_id && 6588c2ecf20Sopenharmony_ci rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID, 6598c2ecf20Sopenharmony_ci entry->l3_unicast.vlan_id)) 6608c2ecf20Sopenharmony_ci return -EMSGSIZE; 6618c2ecf20Sopenharmony_ci if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_TTL_CHECK, 6628c2ecf20Sopenharmony_ci entry->l3_unicast.ttl_check)) 6638c2ecf20Sopenharmony_ci return -EMSGSIZE; 6648c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID_LOWER, 6658c2ecf20Sopenharmony_ci entry->l3_unicast.group_id)) 6668c2ecf20Sopenharmony_ci return -EMSGSIZE; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci return 0; 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic int ofdpa_cmd_group_tbl_add(const struct rocker_port *rocker_port, 6728c2ecf20Sopenharmony_ci struct rocker_desc_info *desc_info, 6738c2ecf20Sopenharmony_ci void *priv) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct ofdpa_group_tbl_entry *entry = priv; 6768c2ecf20Sopenharmony_ci struct rocker_tlv *cmd_info; 6778c2ecf20Sopenharmony_ci int err = 0; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd)) 6808c2ecf20Sopenharmony_ci return -EMSGSIZE; 6818c2ecf20Sopenharmony_ci cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 6828c2ecf20Sopenharmony_ci if (!cmd_info) 6838c2ecf20Sopenharmony_ci return -EMSGSIZE; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID, 6868c2ecf20Sopenharmony_ci entry->group_id)) 6878c2ecf20Sopenharmony_ci return -EMSGSIZE; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci switch (ROCKER_GROUP_TYPE_GET(entry->group_id)) { 6908c2ecf20Sopenharmony_ci case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE: 6918c2ecf20Sopenharmony_ci err = ofdpa_cmd_group_tbl_add_l2_interface(desc_info, entry); 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE: 6948c2ecf20Sopenharmony_ci err = ofdpa_cmd_group_tbl_add_l2_rewrite(desc_info, entry); 6958c2ecf20Sopenharmony_ci break; 6968c2ecf20Sopenharmony_ci case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD: 6978c2ecf20Sopenharmony_ci case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST: 6988c2ecf20Sopenharmony_ci err = ofdpa_cmd_group_tbl_add_group_ids(desc_info, entry); 6998c2ecf20Sopenharmony_ci break; 7008c2ecf20Sopenharmony_ci case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST: 7018c2ecf20Sopenharmony_ci err = ofdpa_cmd_group_tbl_add_l3_unicast(desc_info, entry); 7028c2ecf20Sopenharmony_ci break; 7038c2ecf20Sopenharmony_ci default: 7048c2ecf20Sopenharmony_ci err = -ENOTSUPP; 7058c2ecf20Sopenharmony_ci break; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci if (err) 7098c2ecf20Sopenharmony_ci return err; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci rocker_tlv_nest_end(desc_info, cmd_info); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci return 0; 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic int ofdpa_cmd_group_tbl_del(const struct rocker_port *rocker_port, 7178c2ecf20Sopenharmony_ci struct rocker_desc_info *desc_info, 7188c2ecf20Sopenharmony_ci void *priv) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci const struct ofdpa_group_tbl_entry *entry = priv; 7218c2ecf20Sopenharmony_ci struct rocker_tlv *cmd_info; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd)) 7248c2ecf20Sopenharmony_ci return -EMSGSIZE; 7258c2ecf20Sopenharmony_ci cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 7268c2ecf20Sopenharmony_ci if (!cmd_info) 7278c2ecf20Sopenharmony_ci return -EMSGSIZE; 7288c2ecf20Sopenharmony_ci if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID, 7298c2ecf20Sopenharmony_ci entry->group_id)) 7308c2ecf20Sopenharmony_ci return -EMSGSIZE; 7318c2ecf20Sopenharmony_ci rocker_tlv_nest_end(desc_info, cmd_info); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci return 0; 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci/*************************************************** 7378c2ecf20Sopenharmony_ci * Flow, group, FDB, internal VLAN and neigh tables 7388c2ecf20Sopenharmony_ci ***************************************************/ 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_cistatic struct ofdpa_flow_tbl_entry * 7418c2ecf20Sopenharmony_ciofdpa_flow_tbl_find(const struct ofdpa *ofdpa, 7428c2ecf20Sopenharmony_ci const struct ofdpa_flow_tbl_entry *match) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci struct ofdpa_flow_tbl_entry *found; 7458c2ecf20Sopenharmony_ci size_t key_len = match->key_len ? match->key_len : sizeof(found->key); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci hash_for_each_possible(ofdpa->flow_tbl, found, 7488c2ecf20Sopenharmony_ci entry, match->key_crc32) { 7498c2ecf20Sopenharmony_ci if (memcmp(&found->key, &match->key, key_len) == 0) 7508c2ecf20Sopenharmony_ci return found; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci return NULL; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic int ofdpa_flow_tbl_add(struct ofdpa_port *ofdpa_port, 7578c2ecf20Sopenharmony_ci int flags, struct ofdpa_flow_tbl_entry *match) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = ofdpa_port->ofdpa; 7608c2ecf20Sopenharmony_ci struct ofdpa_flow_tbl_entry *found; 7618c2ecf20Sopenharmony_ci size_t key_len = match->key_len ? match->key_len : sizeof(found->key); 7628c2ecf20Sopenharmony_ci unsigned long lock_flags; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci match->key_crc32 = crc32(~0, &match->key, key_len); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->flow_tbl_lock, lock_flags); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci found = ofdpa_flow_tbl_find(ofdpa, match); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci if (found) { 7718c2ecf20Sopenharmony_ci match->cookie = found->cookie; 7728c2ecf20Sopenharmony_ci hash_del(&found->entry); 7738c2ecf20Sopenharmony_ci kfree(found); 7748c2ecf20Sopenharmony_ci found = match; 7758c2ecf20Sopenharmony_ci found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD; 7768c2ecf20Sopenharmony_ci } else { 7778c2ecf20Sopenharmony_ci found = match; 7788c2ecf20Sopenharmony_ci found->cookie = ofdpa->flow_tbl_next_cookie++; 7798c2ecf20Sopenharmony_ci found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci hash_add(ofdpa->flow_tbl, &found->entry, found->key_crc32); 7838c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->flow_tbl_lock, lock_flags); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci return rocker_cmd_exec(ofdpa_port->rocker_port, 7868c2ecf20Sopenharmony_ci ofdpa_flags_nowait(flags), 7878c2ecf20Sopenharmony_ci ofdpa_cmd_flow_tbl_add, 7888c2ecf20Sopenharmony_ci found, NULL, NULL); 7898c2ecf20Sopenharmony_ci} 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic int ofdpa_flow_tbl_del(struct ofdpa_port *ofdpa_port, 7928c2ecf20Sopenharmony_ci int flags, struct ofdpa_flow_tbl_entry *match) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = ofdpa_port->ofdpa; 7958c2ecf20Sopenharmony_ci struct ofdpa_flow_tbl_entry *found; 7968c2ecf20Sopenharmony_ci size_t key_len = match->key_len ? match->key_len : sizeof(found->key); 7978c2ecf20Sopenharmony_ci unsigned long lock_flags; 7988c2ecf20Sopenharmony_ci int err = 0; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci match->key_crc32 = crc32(~0, &match->key, key_len); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->flow_tbl_lock, lock_flags); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci found = ofdpa_flow_tbl_find(ofdpa, match); 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci if (found) { 8078c2ecf20Sopenharmony_ci hash_del(&found->entry); 8088c2ecf20Sopenharmony_ci found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL; 8098c2ecf20Sopenharmony_ci } 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->flow_tbl_lock, lock_flags); 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci kfree(match); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci if (found) { 8168c2ecf20Sopenharmony_ci err = rocker_cmd_exec(ofdpa_port->rocker_port, 8178c2ecf20Sopenharmony_ci ofdpa_flags_nowait(flags), 8188c2ecf20Sopenharmony_ci ofdpa_cmd_flow_tbl_del, 8198c2ecf20Sopenharmony_ci found, NULL, NULL); 8208c2ecf20Sopenharmony_ci kfree(found); 8218c2ecf20Sopenharmony_ci } 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci return err; 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic int ofdpa_flow_tbl_do(struct ofdpa_port *ofdpa_port, int flags, 8278c2ecf20Sopenharmony_ci struct ofdpa_flow_tbl_entry *entry) 8288c2ecf20Sopenharmony_ci{ 8298c2ecf20Sopenharmony_ci if (flags & OFDPA_OP_FLAG_REMOVE) 8308c2ecf20Sopenharmony_ci return ofdpa_flow_tbl_del(ofdpa_port, flags, entry); 8318c2ecf20Sopenharmony_ci else 8328c2ecf20Sopenharmony_ci return ofdpa_flow_tbl_add(ofdpa_port, flags, entry); 8338c2ecf20Sopenharmony_ci} 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic int ofdpa_flow_tbl_ig_port(struct ofdpa_port *ofdpa_port, int flags, 8368c2ecf20Sopenharmony_ci u32 in_pport, u32 in_pport_mask, 8378c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl) 8388c2ecf20Sopenharmony_ci{ 8398c2ecf20Sopenharmony_ci struct ofdpa_flow_tbl_entry *entry; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 8428c2ecf20Sopenharmony_ci if (!entry) 8438c2ecf20Sopenharmony_ci return -ENOMEM; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci entry->key.priority = OFDPA_PRIORITY_IG_PORT; 8468c2ecf20Sopenharmony_ci entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT; 8478c2ecf20Sopenharmony_ci entry->key.ig_port.in_pport = in_pport; 8488c2ecf20Sopenharmony_ci entry->key.ig_port.in_pport_mask = in_pport_mask; 8498c2ecf20Sopenharmony_ci entry->key.ig_port.goto_tbl = goto_tbl; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci return ofdpa_flow_tbl_do(ofdpa_port, flags, entry); 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cistatic int ofdpa_flow_tbl_vlan(struct ofdpa_port *ofdpa_port, 8558c2ecf20Sopenharmony_ci int flags, 8568c2ecf20Sopenharmony_ci u32 in_pport, __be16 vlan_id, 8578c2ecf20Sopenharmony_ci __be16 vlan_id_mask, 8588c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl, 8598c2ecf20Sopenharmony_ci bool untagged, __be16 new_vlan_id) 8608c2ecf20Sopenharmony_ci{ 8618c2ecf20Sopenharmony_ci struct ofdpa_flow_tbl_entry *entry; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 8648c2ecf20Sopenharmony_ci if (!entry) 8658c2ecf20Sopenharmony_ci return -ENOMEM; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci entry->key.priority = OFDPA_PRIORITY_VLAN; 8688c2ecf20Sopenharmony_ci entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_VLAN; 8698c2ecf20Sopenharmony_ci entry->key.vlan.in_pport = in_pport; 8708c2ecf20Sopenharmony_ci entry->key.vlan.vlan_id = vlan_id; 8718c2ecf20Sopenharmony_ci entry->key.vlan.vlan_id_mask = vlan_id_mask; 8728c2ecf20Sopenharmony_ci entry->key.vlan.goto_tbl = goto_tbl; 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci entry->key.vlan.untagged = untagged; 8758c2ecf20Sopenharmony_ci entry->key.vlan.new_vlan_id = new_vlan_id; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci return ofdpa_flow_tbl_do(ofdpa_port, flags, entry); 8788c2ecf20Sopenharmony_ci} 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_cistatic int ofdpa_flow_tbl_term_mac(struct ofdpa_port *ofdpa_port, 8818c2ecf20Sopenharmony_ci u32 in_pport, u32 in_pport_mask, 8828c2ecf20Sopenharmony_ci __be16 eth_type, const u8 *eth_dst, 8838c2ecf20Sopenharmony_ci const u8 *eth_dst_mask, __be16 vlan_id, 8848c2ecf20Sopenharmony_ci __be16 vlan_id_mask, bool copy_to_cpu, 8858c2ecf20Sopenharmony_ci int flags) 8868c2ecf20Sopenharmony_ci{ 8878c2ecf20Sopenharmony_ci struct ofdpa_flow_tbl_entry *entry; 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 8908c2ecf20Sopenharmony_ci if (!entry) 8918c2ecf20Sopenharmony_ci return -ENOMEM; 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci if (is_multicast_ether_addr(eth_dst)) { 8948c2ecf20Sopenharmony_ci entry->key.priority = OFDPA_PRIORITY_TERM_MAC_MCAST; 8958c2ecf20Sopenharmony_ci entry->key.term_mac.goto_tbl = 8968c2ecf20Sopenharmony_ci ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING; 8978c2ecf20Sopenharmony_ci } else { 8988c2ecf20Sopenharmony_ci entry->key.priority = OFDPA_PRIORITY_TERM_MAC_UCAST; 8998c2ecf20Sopenharmony_ci entry->key.term_mac.goto_tbl = 9008c2ecf20Sopenharmony_ci ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING; 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC; 9048c2ecf20Sopenharmony_ci entry->key.term_mac.in_pport = in_pport; 9058c2ecf20Sopenharmony_ci entry->key.term_mac.in_pport_mask = in_pport_mask; 9068c2ecf20Sopenharmony_ci entry->key.term_mac.eth_type = eth_type; 9078c2ecf20Sopenharmony_ci ether_addr_copy(entry->key.term_mac.eth_dst, eth_dst); 9088c2ecf20Sopenharmony_ci ether_addr_copy(entry->key.term_mac.eth_dst_mask, eth_dst_mask); 9098c2ecf20Sopenharmony_ci entry->key.term_mac.vlan_id = vlan_id; 9108c2ecf20Sopenharmony_ci entry->key.term_mac.vlan_id_mask = vlan_id_mask; 9118c2ecf20Sopenharmony_ci entry->key.term_mac.copy_to_cpu = copy_to_cpu; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci return ofdpa_flow_tbl_do(ofdpa_port, flags, entry); 9148c2ecf20Sopenharmony_ci} 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_cistatic int ofdpa_flow_tbl_bridge(struct ofdpa_port *ofdpa_port, 9178c2ecf20Sopenharmony_ci int flags, const u8 *eth_dst, 9188c2ecf20Sopenharmony_ci const u8 *eth_dst_mask, __be16 vlan_id, 9198c2ecf20Sopenharmony_ci u32 tunnel_id, 9208c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl, 9218c2ecf20Sopenharmony_ci u32 group_id, bool copy_to_cpu) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct ofdpa_flow_tbl_entry *entry; 9248c2ecf20Sopenharmony_ci u32 priority; 9258c2ecf20Sopenharmony_ci bool vlan_bridging = !!vlan_id; 9268c2ecf20Sopenharmony_ci bool dflt = !eth_dst || (eth_dst && eth_dst_mask); 9278c2ecf20Sopenharmony_ci bool wild = false; 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_ATOMIC); 9308c2ecf20Sopenharmony_ci if (!entry) 9318c2ecf20Sopenharmony_ci return -ENOMEM; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_BRIDGING; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci if (eth_dst) { 9368c2ecf20Sopenharmony_ci entry->key.bridge.has_eth_dst = 1; 9378c2ecf20Sopenharmony_ci ether_addr_copy(entry->key.bridge.eth_dst, eth_dst); 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci if (eth_dst_mask) { 9408c2ecf20Sopenharmony_ci entry->key.bridge.has_eth_dst_mask = 1; 9418c2ecf20Sopenharmony_ci ether_addr_copy(entry->key.bridge.eth_dst_mask, eth_dst_mask); 9428c2ecf20Sopenharmony_ci if (!ether_addr_equal(eth_dst_mask, ff_mac)) 9438c2ecf20Sopenharmony_ci wild = true; 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci priority = OFDPA_PRIORITY_UNKNOWN; 9478c2ecf20Sopenharmony_ci if (vlan_bridging && dflt && wild) 9488c2ecf20Sopenharmony_ci priority = OFDPA_PRIORITY_BRIDGING_VLAN_DFLT_WILD; 9498c2ecf20Sopenharmony_ci else if (vlan_bridging && dflt && !wild) 9508c2ecf20Sopenharmony_ci priority = OFDPA_PRIORITY_BRIDGING_VLAN_DFLT_EXACT; 9518c2ecf20Sopenharmony_ci else if (vlan_bridging && !dflt) 9528c2ecf20Sopenharmony_ci priority = OFDPA_PRIORITY_BRIDGING_VLAN; 9538c2ecf20Sopenharmony_ci else if (!vlan_bridging && dflt && wild) 9548c2ecf20Sopenharmony_ci priority = OFDPA_PRIORITY_BRIDGING_TENANT_DFLT_WILD; 9558c2ecf20Sopenharmony_ci else if (!vlan_bridging && dflt && !wild) 9568c2ecf20Sopenharmony_ci priority = OFDPA_PRIORITY_BRIDGING_TENANT_DFLT_EXACT; 9578c2ecf20Sopenharmony_ci else if (!vlan_bridging && !dflt) 9588c2ecf20Sopenharmony_ci priority = OFDPA_PRIORITY_BRIDGING_TENANT; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci entry->key.priority = priority; 9618c2ecf20Sopenharmony_ci entry->key.bridge.vlan_id = vlan_id; 9628c2ecf20Sopenharmony_ci entry->key.bridge.tunnel_id = tunnel_id; 9638c2ecf20Sopenharmony_ci entry->key.bridge.goto_tbl = goto_tbl; 9648c2ecf20Sopenharmony_ci entry->key.bridge.group_id = group_id; 9658c2ecf20Sopenharmony_ci entry->key.bridge.copy_to_cpu = copy_to_cpu; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci return ofdpa_flow_tbl_do(ofdpa_port, flags, entry); 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_cistatic int ofdpa_flow_tbl_ucast4_routing(struct ofdpa_port *ofdpa_port, 9718c2ecf20Sopenharmony_ci __be16 eth_type, __be32 dst, 9728c2ecf20Sopenharmony_ci __be32 dst_mask, u32 priority, 9738c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl, 9748c2ecf20Sopenharmony_ci u32 group_id, struct fib_info *fi, 9758c2ecf20Sopenharmony_ci int flags) 9768c2ecf20Sopenharmony_ci{ 9778c2ecf20Sopenharmony_ci struct ofdpa_flow_tbl_entry *entry; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 9808c2ecf20Sopenharmony_ci if (!entry) 9818c2ecf20Sopenharmony_ci return -ENOMEM; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING; 9848c2ecf20Sopenharmony_ci entry->key.priority = priority; 9858c2ecf20Sopenharmony_ci entry->key.ucast_routing.eth_type = eth_type; 9868c2ecf20Sopenharmony_ci entry->key.ucast_routing.dst4 = dst; 9878c2ecf20Sopenharmony_ci entry->key.ucast_routing.dst4_mask = dst_mask; 9888c2ecf20Sopenharmony_ci entry->key.ucast_routing.goto_tbl = goto_tbl; 9898c2ecf20Sopenharmony_ci entry->key.ucast_routing.group_id = group_id; 9908c2ecf20Sopenharmony_ci entry->key_len = offsetof(struct ofdpa_flow_tbl_key, 9918c2ecf20Sopenharmony_ci ucast_routing.group_id); 9928c2ecf20Sopenharmony_ci entry->fi = fi; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci return ofdpa_flow_tbl_do(ofdpa_port, flags, entry); 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic int ofdpa_flow_tbl_acl(struct ofdpa_port *ofdpa_port, int flags, 9988c2ecf20Sopenharmony_ci u32 in_pport, u32 in_pport_mask, 9998c2ecf20Sopenharmony_ci const u8 *eth_src, const u8 *eth_src_mask, 10008c2ecf20Sopenharmony_ci const u8 *eth_dst, const u8 *eth_dst_mask, 10018c2ecf20Sopenharmony_ci __be16 eth_type, __be16 vlan_id, 10028c2ecf20Sopenharmony_ci __be16 vlan_id_mask, u8 ip_proto, 10038c2ecf20Sopenharmony_ci u8 ip_proto_mask, u8 ip_tos, u8 ip_tos_mask, 10048c2ecf20Sopenharmony_ci u32 group_id) 10058c2ecf20Sopenharmony_ci{ 10068c2ecf20Sopenharmony_ci u32 priority; 10078c2ecf20Sopenharmony_ci struct ofdpa_flow_tbl_entry *entry; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 10108c2ecf20Sopenharmony_ci if (!entry) 10118c2ecf20Sopenharmony_ci return -ENOMEM; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci priority = OFDPA_PRIORITY_ACL_NORMAL; 10148c2ecf20Sopenharmony_ci if (eth_dst && eth_dst_mask) { 10158c2ecf20Sopenharmony_ci if (ether_addr_equal(eth_dst_mask, mcast_mac)) 10168c2ecf20Sopenharmony_ci priority = OFDPA_PRIORITY_ACL_DFLT; 10178c2ecf20Sopenharmony_ci else if (is_link_local_ether_addr(eth_dst)) 10188c2ecf20Sopenharmony_ci priority = OFDPA_PRIORITY_ACL_CTRL; 10198c2ecf20Sopenharmony_ci } 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci entry->key.priority = priority; 10228c2ecf20Sopenharmony_ci entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_ACL_POLICY; 10238c2ecf20Sopenharmony_ci entry->key.acl.in_pport = in_pport; 10248c2ecf20Sopenharmony_ci entry->key.acl.in_pport_mask = in_pport_mask; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci if (eth_src) 10278c2ecf20Sopenharmony_ci ether_addr_copy(entry->key.acl.eth_src, eth_src); 10288c2ecf20Sopenharmony_ci if (eth_src_mask) 10298c2ecf20Sopenharmony_ci ether_addr_copy(entry->key.acl.eth_src_mask, eth_src_mask); 10308c2ecf20Sopenharmony_ci if (eth_dst) 10318c2ecf20Sopenharmony_ci ether_addr_copy(entry->key.acl.eth_dst, eth_dst); 10328c2ecf20Sopenharmony_ci if (eth_dst_mask) 10338c2ecf20Sopenharmony_ci ether_addr_copy(entry->key.acl.eth_dst_mask, eth_dst_mask); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci entry->key.acl.eth_type = eth_type; 10368c2ecf20Sopenharmony_ci entry->key.acl.vlan_id = vlan_id; 10378c2ecf20Sopenharmony_ci entry->key.acl.vlan_id_mask = vlan_id_mask; 10388c2ecf20Sopenharmony_ci entry->key.acl.ip_proto = ip_proto; 10398c2ecf20Sopenharmony_ci entry->key.acl.ip_proto_mask = ip_proto_mask; 10408c2ecf20Sopenharmony_ci entry->key.acl.ip_tos = ip_tos; 10418c2ecf20Sopenharmony_ci entry->key.acl.ip_tos_mask = ip_tos_mask; 10428c2ecf20Sopenharmony_ci entry->key.acl.group_id = group_id; 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci return ofdpa_flow_tbl_do(ofdpa_port, flags, entry); 10458c2ecf20Sopenharmony_ci} 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_cistatic struct ofdpa_group_tbl_entry * 10488c2ecf20Sopenharmony_ciofdpa_group_tbl_find(const struct ofdpa *ofdpa, 10498c2ecf20Sopenharmony_ci const struct ofdpa_group_tbl_entry *match) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci struct ofdpa_group_tbl_entry *found; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci hash_for_each_possible(ofdpa->group_tbl, found, 10548c2ecf20Sopenharmony_ci entry, match->group_id) { 10558c2ecf20Sopenharmony_ci if (found->group_id == match->group_id) 10568c2ecf20Sopenharmony_ci return found; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci return NULL; 10608c2ecf20Sopenharmony_ci} 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_cistatic void ofdpa_group_tbl_entry_free(struct ofdpa_group_tbl_entry *entry) 10638c2ecf20Sopenharmony_ci{ 10648c2ecf20Sopenharmony_ci switch (ROCKER_GROUP_TYPE_GET(entry->group_id)) { 10658c2ecf20Sopenharmony_ci case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD: 10668c2ecf20Sopenharmony_ci case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST: 10678c2ecf20Sopenharmony_ci kfree(entry->group_ids); 10688c2ecf20Sopenharmony_ci break; 10698c2ecf20Sopenharmony_ci default: 10708c2ecf20Sopenharmony_ci break; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci kfree(entry); 10738c2ecf20Sopenharmony_ci} 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic int ofdpa_group_tbl_add(struct ofdpa_port *ofdpa_port, int flags, 10768c2ecf20Sopenharmony_ci struct ofdpa_group_tbl_entry *match) 10778c2ecf20Sopenharmony_ci{ 10788c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = ofdpa_port->ofdpa; 10798c2ecf20Sopenharmony_ci struct ofdpa_group_tbl_entry *found; 10808c2ecf20Sopenharmony_ci unsigned long lock_flags; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->group_tbl_lock, lock_flags); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci found = ofdpa_group_tbl_find(ofdpa, match); 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci if (found) { 10878c2ecf20Sopenharmony_ci hash_del(&found->entry); 10888c2ecf20Sopenharmony_ci ofdpa_group_tbl_entry_free(found); 10898c2ecf20Sopenharmony_ci found = match; 10908c2ecf20Sopenharmony_ci found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD; 10918c2ecf20Sopenharmony_ci } else { 10928c2ecf20Sopenharmony_ci found = match; 10938c2ecf20Sopenharmony_ci found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci hash_add(ofdpa->group_tbl, &found->entry, found->group_id); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->group_tbl_lock, lock_flags); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci return rocker_cmd_exec(ofdpa_port->rocker_port, 11018c2ecf20Sopenharmony_ci ofdpa_flags_nowait(flags), 11028c2ecf20Sopenharmony_ci ofdpa_cmd_group_tbl_add, 11038c2ecf20Sopenharmony_ci found, NULL, NULL); 11048c2ecf20Sopenharmony_ci} 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_cistatic int ofdpa_group_tbl_del(struct ofdpa_port *ofdpa_port, int flags, 11078c2ecf20Sopenharmony_ci struct ofdpa_group_tbl_entry *match) 11088c2ecf20Sopenharmony_ci{ 11098c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = ofdpa_port->ofdpa; 11108c2ecf20Sopenharmony_ci struct ofdpa_group_tbl_entry *found; 11118c2ecf20Sopenharmony_ci unsigned long lock_flags; 11128c2ecf20Sopenharmony_ci int err = 0; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->group_tbl_lock, lock_flags); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci found = ofdpa_group_tbl_find(ofdpa, match); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci if (found) { 11198c2ecf20Sopenharmony_ci hash_del(&found->entry); 11208c2ecf20Sopenharmony_ci found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL; 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->group_tbl_lock, lock_flags); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci ofdpa_group_tbl_entry_free(match); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci if (found) { 11288c2ecf20Sopenharmony_ci err = rocker_cmd_exec(ofdpa_port->rocker_port, 11298c2ecf20Sopenharmony_ci ofdpa_flags_nowait(flags), 11308c2ecf20Sopenharmony_ci ofdpa_cmd_group_tbl_del, 11318c2ecf20Sopenharmony_ci found, NULL, NULL); 11328c2ecf20Sopenharmony_ci ofdpa_group_tbl_entry_free(found); 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci return err; 11368c2ecf20Sopenharmony_ci} 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_cistatic int ofdpa_group_tbl_do(struct ofdpa_port *ofdpa_port, int flags, 11398c2ecf20Sopenharmony_ci struct ofdpa_group_tbl_entry *entry) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci if (flags & OFDPA_OP_FLAG_REMOVE) 11428c2ecf20Sopenharmony_ci return ofdpa_group_tbl_del(ofdpa_port, flags, entry); 11438c2ecf20Sopenharmony_ci else 11448c2ecf20Sopenharmony_ci return ofdpa_group_tbl_add(ofdpa_port, flags, entry); 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic int ofdpa_group_l2_interface(struct ofdpa_port *ofdpa_port, 11488c2ecf20Sopenharmony_ci int flags, __be16 vlan_id, 11498c2ecf20Sopenharmony_ci u32 out_pport, int pop_vlan) 11508c2ecf20Sopenharmony_ci{ 11518c2ecf20Sopenharmony_ci struct ofdpa_group_tbl_entry *entry; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 11548c2ecf20Sopenharmony_ci if (!entry) 11558c2ecf20Sopenharmony_ci return -ENOMEM; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci entry->group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport); 11588c2ecf20Sopenharmony_ci entry->l2_interface.pop_vlan = pop_vlan; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci return ofdpa_group_tbl_do(ofdpa_port, flags, entry); 11618c2ecf20Sopenharmony_ci} 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cistatic int ofdpa_group_l2_fan_out(struct ofdpa_port *ofdpa_port, 11648c2ecf20Sopenharmony_ci int flags, u8 group_count, 11658c2ecf20Sopenharmony_ci const u32 *group_ids, u32 group_id) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci struct ofdpa_group_tbl_entry *entry; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 11708c2ecf20Sopenharmony_ci if (!entry) 11718c2ecf20Sopenharmony_ci return -ENOMEM; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci entry->group_id = group_id; 11748c2ecf20Sopenharmony_ci entry->group_count = group_count; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci entry->group_ids = kcalloc(group_count, sizeof(u32), GFP_KERNEL); 11778c2ecf20Sopenharmony_ci if (!entry->group_ids) { 11788c2ecf20Sopenharmony_ci kfree(entry); 11798c2ecf20Sopenharmony_ci return -ENOMEM; 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci memcpy(entry->group_ids, group_ids, group_count * sizeof(u32)); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci return ofdpa_group_tbl_do(ofdpa_port, flags, entry); 11848c2ecf20Sopenharmony_ci} 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_cistatic int ofdpa_group_l2_flood(struct ofdpa_port *ofdpa_port, 11878c2ecf20Sopenharmony_ci int flags, __be16 vlan_id, 11888c2ecf20Sopenharmony_ci u8 group_count, const u32 *group_ids, 11898c2ecf20Sopenharmony_ci u32 group_id) 11908c2ecf20Sopenharmony_ci{ 11918c2ecf20Sopenharmony_ci return ofdpa_group_l2_fan_out(ofdpa_port, flags, 11928c2ecf20Sopenharmony_ci group_count, group_ids, 11938c2ecf20Sopenharmony_ci group_id); 11948c2ecf20Sopenharmony_ci} 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_cistatic int ofdpa_group_l3_unicast(struct ofdpa_port *ofdpa_port, int flags, 11978c2ecf20Sopenharmony_ci u32 index, const u8 *src_mac, const u8 *dst_mac, 11988c2ecf20Sopenharmony_ci __be16 vlan_id, bool ttl_check, u32 pport) 11998c2ecf20Sopenharmony_ci{ 12008c2ecf20Sopenharmony_ci struct ofdpa_group_tbl_entry *entry; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 12038c2ecf20Sopenharmony_ci if (!entry) 12048c2ecf20Sopenharmony_ci return -ENOMEM; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci entry->group_id = ROCKER_GROUP_L3_UNICAST(index); 12078c2ecf20Sopenharmony_ci if (src_mac) 12088c2ecf20Sopenharmony_ci ether_addr_copy(entry->l3_unicast.eth_src, src_mac); 12098c2ecf20Sopenharmony_ci if (dst_mac) 12108c2ecf20Sopenharmony_ci ether_addr_copy(entry->l3_unicast.eth_dst, dst_mac); 12118c2ecf20Sopenharmony_ci entry->l3_unicast.vlan_id = vlan_id; 12128c2ecf20Sopenharmony_ci entry->l3_unicast.ttl_check = ttl_check; 12138c2ecf20Sopenharmony_ci entry->l3_unicast.group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, pport); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci return ofdpa_group_tbl_do(ofdpa_port, flags, entry); 12168c2ecf20Sopenharmony_ci} 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_cistatic struct ofdpa_neigh_tbl_entry * 12198c2ecf20Sopenharmony_ciofdpa_neigh_tbl_find(const struct ofdpa *ofdpa, __be32 ip_addr) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci struct ofdpa_neigh_tbl_entry *found; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci hash_for_each_possible(ofdpa->neigh_tbl, found, 12248c2ecf20Sopenharmony_ci entry, be32_to_cpu(ip_addr)) 12258c2ecf20Sopenharmony_ci if (found->ip_addr == ip_addr) 12268c2ecf20Sopenharmony_ci return found; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci return NULL; 12298c2ecf20Sopenharmony_ci} 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_cistatic void ofdpa_neigh_add(struct ofdpa *ofdpa, 12328c2ecf20Sopenharmony_ci struct ofdpa_neigh_tbl_entry *entry) 12338c2ecf20Sopenharmony_ci{ 12348c2ecf20Sopenharmony_ci entry->index = ofdpa->neigh_tbl_next_index++; 12358c2ecf20Sopenharmony_ci entry->ref_count++; 12368c2ecf20Sopenharmony_ci hash_add(ofdpa->neigh_tbl, &entry->entry, 12378c2ecf20Sopenharmony_ci be32_to_cpu(entry->ip_addr)); 12388c2ecf20Sopenharmony_ci} 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_cistatic void ofdpa_neigh_del(struct ofdpa_neigh_tbl_entry *entry) 12418c2ecf20Sopenharmony_ci{ 12428c2ecf20Sopenharmony_ci if (--entry->ref_count == 0) { 12438c2ecf20Sopenharmony_ci hash_del(&entry->entry); 12448c2ecf20Sopenharmony_ci kfree(entry); 12458c2ecf20Sopenharmony_ci } 12468c2ecf20Sopenharmony_ci} 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_cistatic void ofdpa_neigh_update(struct ofdpa_neigh_tbl_entry *entry, 12498c2ecf20Sopenharmony_ci const u8 *eth_dst, bool ttl_check) 12508c2ecf20Sopenharmony_ci{ 12518c2ecf20Sopenharmony_ci if (eth_dst) { 12528c2ecf20Sopenharmony_ci ether_addr_copy(entry->eth_dst, eth_dst); 12538c2ecf20Sopenharmony_ci entry->ttl_check = ttl_check; 12548c2ecf20Sopenharmony_ci } else { 12558c2ecf20Sopenharmony_ci entry->ref_count++; 12568c2ecf20Sopenharmony_ci } 12578c2ecf20Sopenharmony_ci} 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_cistatic int ofdpa_port_ipv4_neigh(struct ofdpa_port *ofdpa_port, 12608c2ecf20Sopenharmony_ci int flags, __be32 ip_addr, const u8 *eth_dst) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = ofdpa_port->ofdpa; 12638c2ecf20Sopenharmony_ci struct ofdpa_neigh_tbl_entry *entry; 12648c2ecf20Sopenharmony_ci struct ofdpa_neigh_tbl_entry *found; 12658c2ecf20Sopenharmony_ci unsigned long lock_flags; 12668c2ecf20Sopenharmony_ci __be16 eth_type = htons(ETH_P_IP); 12678c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl = 12688c2ecf20Sopenharmony_ci ROCKER_OF_DPA_TABLE_ID_ACL_POLICY; 12698c2ecf20Sopenharmony_ci u32 group_id; 12708c2ecf20Sopenharmony_ci u32 priority = 0; 12718c2ecf20Sopenharmony_ci bool adding = !(flags & OFDPA_OP_FLAG_REMOVE); 12728c2ecf20Sopenharmony_ci bool updating; 12738c2ecf20Sopenharmony_ci bool removing; 12748c2ecf20Sopenharmony_ci int err = 0; 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_ATOMIC); 12778c2ecf20Sopenharmony_ci if (!entry) 12788c2ecf20Sopenharmony_ci return -ENOMEM; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->neigh_tbl_lock, lock_flags); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci found = ofdpa_neigh_tbl_find(ofdpa, ip_addr); 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci updating = found && adding; 12858c2ecf20Sopenharmony_ci removing = found && !adding; 12868c2ecf20Sopenharmony_ci adding = !found && adding; 12878c2ecf20Sopenharmony_ci 12888c2ecf20Sopenharmony_ci if (adding) { 12898c2ecf20Sopenharmony_ci entry->ip_addr = ip_addr; 12908c2ecf20Sopenharmony_ci entry->dev = ofdpa_port->dev; 12918c2ecf20Sopenharmony_ci ether_addr_copy(entry->eth_dst, eth_dst); 12928c2ecf20Sopenharmony_ci entry->ttl_check = true; 12938c2ecf20Sopenharmony_ci ofdpa_neigh_add(ofdpa, entry); 12948c2ecf20Sopenharmony_ci } else if (removing) { 12958c2ecf20Sopenharmony_ci memcpy(entry, found, sizeof(*entry)); 12968c2ecf20Sopenharmony_ci ofdpa_neigh_del(found); 12978c2ecf20Sopenharmony_ci } else if (updating) { 12988c2ecf20Sopenharmony_ci ofdpa_neigh_update(found, eth_dst, true); 12998c2ecf20Sopenharmony_ci memcpy(entry, found, sizeof(*entry)); 13008c2ecf20Sopenharmony_ci } else { 13018c2ecf20Sopenharmony_ci err = -ENOENT; 13028c2ecf20Sopenharmony_ci } 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->neigh_tbl_lock, lock_flags); 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci if (err) 13078c2ecf20Sopenharmony_ci goto err_out; 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci /* For each active neighbor, we have an L3 unicast group and 13108c2ecf20Sopenharmony_ci * a /32 route to the neighbor, which uses the L3 unicast 13118c2ecf20Sopenharmony_ci * group. The L3 unicast group can also be referred to by 13128c2ecf20Sopenharmony_ci * other routes' nexthops. 13138c2ecf20Sopenharmony_ci */ 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci err = ofdpa_group_l3_unicast(ofdpa_port, flags, 13168c2ecf20Sopenharmony_ci entry->index, 13178c2ecf20Sopenharmony_ci ofdpa_port->dev->dev_addr, 13188c2ecf20Sopenharmony_ci entry->eth_dst, 13198c2ecf20Sopenharmony_ci ofdpa_port->internal_vlan_id, 13208c2ecf20Sopenharmony_ci entry->ttl_check, 13218c2ecf20Sopenharmony_ci ofdpa_port->pport); 13228c2ecf20Sopenharmony_ci if (err) { 13238c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) L3 unicast group index %d\n", 13248c2ecf20Sopenharmony_ci err, entry->index); 13258c2ecf20Sopenharmony_ci goto err_out; 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci if (adding || removing) { 13298c2ecf20Sopenharmony_ci group_id = ROCKER_GROUP_L3_UNICAST(entry->index); 13308c2ecf20Sopenharmony_ci err = ofdpa_flow_tbl_ucast4_routing(ofdpa_port, 13318c2ecf20Sopenharmony_ci eth_type, ip_addr, 13328c2ecf20Sopenharmony_ci inet_make_mask(32), 13338c2ecf20Sopenharmony_ci priority, goto_tbl, 13348c2ecf20Sopenharmony_ci group_id, NULL, flags); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci if (err) 13378c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) /32 unicast route %pI4 group 0x%08x\n", 13388c2ecf20Sopenharmony_ci err, &entry->ip_addr, group_id); 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_cierr_out: 13428c2ecf20Sopenharmony_ci if (!adding) 13438c2ecf20Sopenharmony_ci kfree(entry); 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci return err; 13468c2ecf20Sopenharmony_ci} 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_cistatic int ofdpa_port_ipv4_resolve(struct ofdpa_port *ofdpa_port, 13498c2ecf20Sopenharmony_ci __be32 ip_addr) 13508c2ecf20Sopenharmony_ci{ 13518c2ecf20Sopenharmony_ci struct net_device *dev = ofdpa_port->dev; 13528c2ecf20Sopenharmony_ci struct neighbour *n = __ipv4_neigh_lookup(dev, (__force u32)ip_addr); 13538c2ecf20Sopenharmony_ci int err = 0; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci if (!n) { 13568c2ecf20Sopenharmony_ci n = neigh_create(&arp_tbl, &ip_addr, dev); 13578c2ecf20Sopenharmony_ci if (IS_ERR(n)) 13588c2ecf20Sopenharmony_ci return PTR_ERR(n); 13598c2ecf20Sopenharmony_ci } 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci /* If the neigh is already resolved, then go ahead and 13628c2ecf20Sopenharmony_ci * install the entry, otherwise start the ARP process to 13638c2ecf20Sopenharmony_ci * resolve the neigh. 13648c2ecf20Sopenharmony_ci */ 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci if (n->nud_state & NUD_VALID) 13678c2ecf20Sopenharmony_ci err = ofdpa_port_ipv4_neigh(ofdpa_port, 0, 13688c2ecf20Sopenharmony_ci ip_addr, n->ha); 13698c2ecf20Sopenharmony_ci else 13708c2ecf20Sopenharmony_ci neigh_event_send(n, NULL); 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci neigh_release(n); 13738c2ecf20Sopenharmony_ci return err; 13748c2ecf20Sopenharmony_ci} 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_cistatic int ofdpa_port_ipv4_nh(struct ofdpa_port *ofdpa_port, 13778c2ecf20Sopenharmony_ci int flags, __be32 ip_addr, u32 *index) 13788c2ecf20Sopenharmony_ci{ 13798c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = ofdpa_port->ofdpa; 13808c2ecf20Sopenharmony_ci struct ofdpa_neigh_tbl_entry *entry; 13818c2ecf20Sopenharmony_ci struct ofdpa_neigh_tbl_entry *found; 13828c2ecf20Sopenharmony_ci unsigned long lock_flags; 13838c2ecf20Sopenharmony_ci bool adding = !(flags & OFDPA_OP_FLAG_REMOVE); 13848c2ecf20Sopenharmony_ci bool updating; 13858c2ecf20Sopenharmony_ci bool removing; 13868c2ecf20Sopenharmony_ci bool resolved = true; 13878c2ecf20Sopenharmony_ci int err = 0; 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 13908c2ecf20Sopenharmony_ci if (!entry) 13918c2ecf20Sopenharmony_ci return -ENOMEM; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->neigh_tbl_lock, lock_flags); 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci found = ofdpa_neigh_tbl_find(ofdpa, ip_addr); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci updating = found && adding; 13988c2ecf20Sopenharmony_ci removing = found && !adding; 13998c2ecf20Sopenharmony_ci adding = !found && adding; 14008c2ecf20Sopenharmony_ci 14018c2ecf20Sopenharmony_ci if (adding) { 14028c2ecf20Sopenharmony_ci entry->ip_addr = ip_addr; 14038c2ecf20Sopenharmony_ci entry->dev = ofdpa_port->dev; 14048c2ecf20Sopenharmony_ci ofdpa_neigh_add(ofdpa, entry); 14058c2ecf20Sopenharmony_ci *index = entry->index; 14068c2ecf20Sopenharmony_ci resolved = false; 14078c2ecf20Sopenharmony_ci } else if (removing) { 14088c2ecf20Sopenharmony_ci *index = found->index; 14098c2ecf20Sopenharmony_ci ofdpa_neigh_del(found); 14108c2ecf20Sopenharmony_ci } else if (updating) { 14118c2ecf20Sopenharmony_ci ofdpa_neigh_update(found, NULL, false); 14128c2ecf20Sopenharmony_ci resolved = !is_zero_ether_addr(found->eth_dst); 14138c2ecf20Sopenharmony_ci *index = found->index; 14148c2ecf20Sopenharmony_ci } else { 14158c2ecf20Sopenharmony_ci err = -ENOENT; 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->neigh_tbl_lock, lock_flags); 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci if (!adding) 14218c2ecf20Sopenharmony_ci kfree(entry); 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci if (err) 14248c2ecf20Sopenharmony_ci return err; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci /* Resolved means neigh ip_addr is resolved to neigh mac. */ 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci if (!resolved) 14298c2ecf20Sopenharmony_ci err = ofdpa_port_ipv4_resolve(ofdpa_port, ip_addr); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci return err; 14328c2ecf20Sopenharmony_ci} 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_cistatic struct ofdpa_port *ofdpa_port_get(const struct ofdpa *ofdpa, 14358c2ecf20Sopenharmony_ci int port_index) 14368c2ecf20Sopenharmony_ci{ 14378c2ecf20Sopenharmony_ci struct rocker_port *rocker_port; 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci rocker_port = ofdpa->rocker->ports[port_index]; 14408c2ecf20Sopenharmony_ci return rocker_port ? rocker_port->wpriv : NULL; 14418c2ecf20Sopenharmony_ci} 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_cistatic int ofdpa_port_vlan_flood_group(struct ofdpa_port *ofdpa_port, 14448c2ecf20Sopenharmony_ci int flags, __be16 vlan_id) 14458c2ecf20Sopenharmony_ci{ 14468c2ecf20Sopenharmony_ci struct ofdpa_port *p; 14478c2ecf20Sopenharmony_ci const struct ofdpa *ofdpa = ofdpa_port->ofdpa; 14488c2ecf20Sopenharmony_ci unsigned int port_count = ofdpa->rocker->port_count; 14498c2ecf20Sopenharmony_ci u32 group_id = ROCKER_GROUP_L2_FLOOD(vlan_id, 0); 14508c2ecf20Sopenharmony_ci u32 *group_ids; 14518c2ecf20Sopenharmony_ci u8 group_count = 0; 14528c2ecf20Sopenharmony_ci int err = 0; 14538c2ecf20Sopenharmony_ci int i; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci group_ids = kcalloc(port_count, sizeof(u32), GFP_KERNEL); 14568c2ecf20Sopenharmony_ci if (!group_ids) 14578c2ecf20Sopenharmony_ci return -ENOMEM; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci /* Adjust the flood group for this VLAN. The flood group 14608c2ecf20Sopenharmony_ci * references an L2 interface group for each port in this 14618c2ecf20Sopenharmony_ci * VLAN. 14628c2ecf20Sopenharmony_ci */ 14638c2ecf20Sopenharmony_ci 14648c2ecf20Sopenharmony_ci for (i = 0; i < port_count; i++) { 14658c2ecf20Sopenharmony_ci p = ofdpa_port_get(ofdpa, i); 14668c2ecf20Sopenharmony_ci if (!p) 14678c2ecf20Sopenharmony_ci continue; 14688c2ecf20Sopenharmony_ci if (!ofdpa_port_is_bridged(p)) 14698c2ecf20Sopenharmony_ci continue; 14708c2ecf20Sopenharmony_ci if (test_bit(ntohs(vlan_id), p->vlan_bitmap)) { 14718c2ecf20Sopenharmony_ci group_ids[group_count++] = 14728c2ecf20Sopenharmony_ci ROCKER_GROUP_L2_INTERFACE(vlan_id, p->pport); 14738c2ecf20Sopenharmony_ci } 14748c2ecf20Sopenharmony_ci } 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci /* If there are no bridged ports in this VLAN, we're done */ 14778c2ecf20Sopenharmony_ci if (group_count == 0) 14788c2ecf20Sopenharmony_ci goto no_ports_in_vlan; 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci err = ofdpa_group_l2_flood(ofdpa_port, flags, vlan_id, 14818c2ecf20Sopenharmony_ci group_count, group_ids, group_id); 14828c2ecf20Sopenharmony_ci if (err) 14838c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 flood group\n", err); 14848c2ecf20Sopenharmony_ci 14858c2ecf20Sopenharmony_cino_ports_in_vlan: 14868c2ecf20Sopenharmony_ci kfree(group_ids); 14878c2ecf20Sopenharmony_ci return err; 14888c2ecf20Sopenharmony_ci} 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_cistatic int ofdpa_port_vlan_l2_groups(struct ofdpa_port *ofdpa_port, int flags, 14918c2ecf20Sopenharmony_ci __be16 vlan_id, bool pop_vlan) 14928c2ecf20Sopenharmony_ci{ 14938c2ecf20Sopenharmony_ci const struct ofdpa *ofdpa = ofdpa_port->ofdpa; 14948c2ecf20Sopenharmony_ci unsigned int port_count = ofdpa->rocker->port_count; 14958c2ecf20Sopenharmony_ci struct ofdpa_port *p; 14968c2ecf20Sopenharmony_ci bool adding = !(flags & OFDPA_OP_FLAG_REMOVE); 14978c2ecf20Sopenharmony_ci u32 out_pport; 14988c2ecf20Sopenharmony_ci int ref = 0; 14998c2ecf20Sopenharmony_ci int err; 15008c2ecf20Sopenharmony_ci int i; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci /* An L2 interface group for this port in this VLAN, but 15038c2ecf20Sopenharmony_ci * only when port STP state is LEARNING|FORWARDING. 15048c2ecf20Sopenharmony_ci */ 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci if (ofdpa_port->stp_state == BR_STATE_LEARNING || 15078c2ecf20Sopenharmony_ci ofdpa_port->stp_state == BR_STATE_FORWARDING) { 15088c2ecf20Sopenharmony_ci out_pport = ofdpa_port->pport; 15098c2ecf20Sopenharmony_ci err = ofdpa_group_l2_interface(ofdpa_port, flags, 15108c2ecf20Sopenharmony_ci vlan_id, out_pport, pop_vlan); 15118c2ecf20Sopenharmony_ci if (err) { 15128c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 group for pport %d\n", 15138c2ecf20Sopenharmony_ci err, out_pport); 15148c2ecf20Sopenharmony_ci return err; 15158c2ecf20Sopenharmony_ci } 15168c2ecf20Sopenharmony_ci } 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci /* An L2 interface group for this VLAN to CPU port. 15198c2ecf20Sopenharmony_ci * Add when first port joins this VLAN and destroy when 15208c2ecf20Sopenharmony_ci * last port leaves this VLAN. 15218c2ecf20Sopenharmony_ci */ 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci for (i = 0; i < port_count; i++) { 15248c2ecf20Sopenharmony_ci p = ofdpa_port_get(ofdpa, i); 15258c2ecf20Sopenharmony_ci if (p && test_bit(ntohs(vlan_id), p->vlan_bitmap)) 15268c2ecf20Sopenharmony_ci ref++; 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci if ((!adding || ref != 1) && (adding || ref != 0)) 15308c2ecf20Sopenharmony_ci return 0; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci out_pport = 0; 15338c2ecf20Sopenharmony_ci err = ofdpa_group_l2_interface(ofdpa_port, flags, 15348c2ecf20Sopenharmony_ci vlan_id, out_pport, pop_vlan); 15358c2ecf20Sopenharmony_ci if (err) { 15368c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 group for CPU port\n", err); 15378c2ecf20Sopenharmony_ci return err; 15388c2ecf20Sopenharmony_ci } 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci return 0; 15418c2ecf20Sopenharmony_ci} 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_cistatic struct ofdpa_ctrl { 15448c2ecf20Sopenharmony_ci const u8 *eth_dst; 15458c2ecf20Sopenharmony_ci const u8 *eth_dst_mask; 15468c2ecf20Sopenharmony_ci __be16 eth_type; 15478c2ecf20Sopenharmony_ci bool acl; 15488c2ecf20Sopenharmony_ci bool bridge; 15498c2ecf20Sopenharmony_ci bool term; 15508c2ecf20Sopenharmony_ci bool copy_to_cpu; 15518c2ecf20Sopenharmony_ci} ofdpa_ctrls[] = { 15528c2ecf20Sopenharmony_ci [OFDPA_CTRL_LINK_LOCAL_MCAST] = { 15538c2ecf20Sopenharmony_ci /* pass link local multicast pkts up to CPU for filtering */ 15548c2ecf20Sopenharmony_ci .eth_dst = ll_mac, 15558c2ecf20Sopenharmony_ci .eth_dst_mask = ll_mask, 15568c2ecf20Sopenharmony_ci .acl = true, 15578c2ecf20Sopenharmony_ci }, 15588c2ecf20Sopenharmony_ci [OFDPA_CTRL_LOCAL_ARP] = { 15598c2ecf20Sopenharmony_ci /* pass local ARP pkts up to CPU */ 15608c2ecf20Sopenharmony_ci .eth_dst = zero_mac, 15618c2ecf20Sopenharmony_ci .eth_dst_mask = zero_mac, 15628c2ecf20Sopenharmony_ci .eth_type = htons(ETH_P_ARP), 15638c2ecf20Sopenharmony_ci .acl = true, 15648c2ecf20Sopenharmony_ci }, 15658c2ecf20Sopenharmony_ci [OFDPA_CTRL_IPV4_MCAST] = { 15668c2ecf20Sopenharmony_ci /* pass IPv4 mcast pkts up to CPU, RFC 1112 */ 15678c2ecf20Sopenharmony_ci .eth_dst = ipv4_mcast, 15688c2ecf20Sopenharmony_ci .eth_dst_mask = ipv4_mask, 15698c2ecf20Sopenharmony_ci .eth_type = htons(ETH_P_IP), 15708c2ecf20Sopenharmony_ci .term = true, 15718c2ecf20Sopenharmony_ci .copy_to_cpu = true, 15728c2ecf20Sopenharmony_ci }, 15738c2ecf20Sopenharmony_ci [OFDPA_CTRL_IPV6_MCAST] = { 15748c2ecf20Sopenharmony_ci /* pass IPv6 mcast pkts up to CPU, RFC 2464 */ 15758c2ecf20Sopenharmony_ci .eth_dst = ipv6_mcast, 15768c2ecf20Sopenharmony_ci .eth_dst_mask = ipv6_mask, 15778c2ecf20Sopenharmony_ci .eth_type = htons(ETH_P_IPV6), 15788c2ecf20Sopenharmony_ci .term = true, 15798c2ecf20Sopenharmony_ci .copy_to_cpu = true, 15808c2ecf20Sopenharmony_ci }, 15818c2ecf20Sopenharmony_ci [OFDPA_CTRL_DFLT_BRIDGING] = { 15828c2ecf20Sopenharmony_ci /* flood any pkts on vlan */ 15838c2ecf20Sopenharmony_ci .bridge = true, 15848c2ecf20Sopenharmony_ci .copy_to_cpu = true, 15858c2ecf20Sopenharmony_ci }, 15868c2ecf20Sopenharmony_ci [OFDPA_CTRL_DFLT_OVS] = { 15878c2ecf20Sopenharmony_ci /* pass all pkts up to CPU */ 15888c2ecf20Sopenharmony_ci .eth_dst = zero_mac, 15898c2ecf20Sopenharmony_ci .eth_dst_mask = zero_mac, 15908c2ecf20Sopenharmony_ci .acl = true, 15918c2ecf20Sopenharmony_ci }, 15928c2ecf20Sopenharmony_ci}; 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_cistatic int ofdpa_port_ctrl_vlan_acl(struct ofdpa_port *ofdpa_port, int flags, 15958c2ecf20Sopenharmony_ci const struct ofdpa_ctrl *ctrl, __be16 vlan_id) 15968c2ecf20Sopenharmony_ci{ 15978c2ecf20Sopenharmony_ci u32 in_pport = ofdpa_port->pport; 15988c2ecf20Sopenharmony_ci u32 in_pport_mask = 0xffffffff; 15998c2ecf20Sopenharmony_ci u32 out_pport = 0; 16008c2ecf20Sopenharmony_ci const u8 *eth_src = NULL; 16018c2ecf20Sopenharmony_ci const u8 *eth_src_mask = NULL; 16028c2ecf20Sopenharmony_ci __be16 vlan_id_mask = htons(0xffff); 16038c2ecf20Sopenharmony_ci u8 ip_proto = 0; 16048c2ecf20Sopenharmony_ci u8 ip_proto_mask = 0; 16058c2ecf20Sopenharmony_ci u8 ip_tos = 0; 16068c2ecf20Sopenharmony_ci u8 ip_tos_mask = 0; 16078c2ecf20Sopenharmony_ci u32 group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport); 16088c2ecf20Sopenharmony_ci int err; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci err = ofdpa_flow_tbl_acl(ofdpa_port, flags, 16118c2ecf20Sopenharmony_ci in_pport, in_pport_mask, 16128c2ecf20Sopenharmony_ci eth_src, eth_src_mask, 16138c2ecf20Sopenharmony_ci ctrl->eth_dst, ctrl->eth_dst_mask, 16148c2ecf20Sopenharmony_ci ctrl->eth_type, 16158c2ecf20Sopenharmony_ci vlan_id, vlan_id_mask, 16168c2ecf20Sopenharmony_ci ip_proto, ip_proto_mask, 16178c2ecf20Sopenharmony_ci ip_tos, ip_tos_mask, 16188c2ecf20Sopenharmony_ci group_id); 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci if (err) 16218c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) ctrl ACL\n", err); 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci return err; 16248c2ecf20Sopenharmony_ci} 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_cistatic int ofdpa_port_ctrl_vlan_bridge(struct ofdpa_port *ofdpa_port, 16278c2ecf20Sopenharmony_ci int flags, const struct ofdpa_ctrl *ctrl, 16288c2ecf20Sopenharmony_ci __be16 vlan_id) 16298c2ecf20Sopenharmony_ci{ 16308c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl = 16318c2ecf20Sopenharmony_ci ROCKER_OF_DPA_TABLE_ID_ACL_POLICY; 16328c2ecf20Sopenharmony_ci u32 group_id = ROCKER_GROUP_L2_FLOOD(vlan_id, 0); 16338c2ecf20Sopenharmony_ci u32 tunnel_id = 0; 16348c2ecf20Sopenharmony_ci int err; 16358c2ecf20Sopenharmony_ci 16368c2ecf20Sopenharmony_ci if (!ofdpa_port_is_bridged(ofdpa_port)) 16378c2ecf20Sopenharmony_ci return 0; 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci err = ofdpa_flow_tbl_bridge(ofdpa_port, flags, 16408c2ecf20Sopenharmony_ci ctrl->eth_dst, ctrl->eth_dst_mask, 16418c2ecf20Sopenharmony_ci vlan_id, tunnel_id, 16428c2ecf20Sopenharmony_ci goto_tbl, group_id, ctrl->copy_to_cpu); 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci if (err) 16458c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) ctrl FLOOD\n", err); 16468c2ecf20Sopenharmony_ci 16478c2ecf20Sopenharmony_ci return err; 16488c2ecf20Sopenharmony_ci} 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_cistatic int ofdpa_port_ctrl_vlan_term(struct ofdpa_port *ofdpa_port, int flags, 16518c2ecf20Sopenharmony_ci const struct ofdpa_ctrl *ctrl, __be16 vlan_id) 16528c2ecf20Sopenharmony_ci{ 16538c2ecf20Sopenharmony_ci u32 in_pport_mask = 0xffffffff; 16548c2ecf20Sopenharmony_ci __be16 vlan_id_mask = htons(0xffff); 16558c2ecf20Sopenharmony_ci int err; 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci if (ntohs(vlan_id) == 0) 16588c2ecf20Sopenharmony_ci vlan_id = ofdpa_port->internal_vlan_id; 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ci err = ofdpa_flow_tbl_term_mac(ofdpa_port, ofdpa_port->pport, in_pport_mask, 16618c2ecf20Sopenharmony_ci ctrl->eth_type, ctrl->eth_dst, 16628c2ecf20Sopenharmony_ci ctrl->eth_dst_mask, vlan_id, 16638c2ecf20Sopenharmony_ci vlan_id_mask, ctrl->copy_to_cpu, 16648c2ecf20Sopenharmony_ci flags); 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci if (err) 16678c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) ctrl term\n", err); 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci return err; 16708c2ecf20Sopenharmony_ci} 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_cistatic int ofdpa_port_ctrl_vlan(struct ofdpa_port *ofdpa_port, int flags, 16738c2ecf20Sopenharmony_ci const struct ofdpa_ctrl *ctrl, __be16 vlan_id) 16748c2ecf20Sopenharmony_ci{ 16758c2ecf20Sopenharmony_ci if (ctrl->acl) 16768c2ecf20Sopenharmony_ci return ofdpa_port_ctrl_vlan_acl(ofdpa_port, flags, 16778c2ecf20Sopenharmony_ci ctrl, vlan_id); 16788c2ecf20Sopenharmony_ci if (ctrl->bridge) 16798c2ecf20Sopenharmony_ci return ofdpa_port_ctrl_vlan_bridge(ofdpa_port, flags, 16808c2ecf20Sopenharmony_ci ctrl, vlan_id); 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci if (ctrl->term) 16838c2ecf20Sopenharmony_ci return ofdpa_port_ctrl_vlan_term(ofdpa_port, flags, 16848c2ecf20Sopenharmony_ci ctrl, vlan_id); 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 16878c2ecf20Sopenharmony_ci} 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_cistatic int ofdpa_port_ctrl_vlan_add(struct ofdpa_port *ofdpa_port, int flags, 16908c2ecf20Sopenharmony_ci __be16 vlan_id) 16918c2ecf20Sopenharmony_ci{ 16928c2ecf20Sopenharmony_ci int err = 0; 16938c2ecf20Sopenharmony_ci int i; 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci for (i = 0; i < OFDPA_CTRL_MAX; i++) { 16968c2ecf20Sopenharmony_ci if (ofdpa_port->ctrls[i]) { 16978c2ecf20Sopenharmony_ci err = ofdpa_port_ctrl_vlan(ofdpa_port, flags, 16988c2ecf20Sopenharmony_ci &ofdpa_ctrls[i], vlan_id); 16998c2ecf20Sopenharmony_ci if (err) 17008c2ecf20Sopenharmony_ci return err; 17018c2ecf20Sopenharmony_ci } 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci return err; 17058c2ecf20Sopenharmony_ci} 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_cistatic int ofdpa_port_ctrl(struct ofdpa_port *ofdpa_port, int flags, 17088c2ecf20Sopenharmony_ci const struct ofdpa_ctrl *ctrl) 17098c2ecf20Sopenharmony_ci{ 17108c2ecf20Sopenharmony_ci u16 vid; 17118c2ecf20Sopenharmony_ci int err = 0; 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci for (vid = 1; vid < VLAN_N_VID; vid++) { 17148c2ecf20Sopenharmony_ci if (!test_bit(vid, ofdpa_port->vlan_bitmap)) 17158c2ecf20Sopenharmony_ci continue; 17168c2ecf20Sopenharmony_ci err = ofdpa_port_ctrl_vlan(ofdpa_port, flags, 17178c2ecf20Sopenharmony_ci ctrl, htons(vid)); 17188c2ecf20Sopenharmony_ci if (err) 17198c2ecf20Sopenharmony_ci break; 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci return err; 17238c2ecf20Sopenharmony_ci} 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_cistatic int ofdpa_port_vlan(struct ofdpa_port *ofdpa_port, int flags, 17268c2ecf20Sopenharmony_ci u16 vid) 17278c2ecf20Sopenharmony_ci{ 17288c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl = 17298c2ecf20Sopenharmony_ci ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC; 17308c2ecf20Sopenharmony_ci u32 in_pport = ofdpa_port->pport; 17318c2ecf20Sopenharmony_ci __be16 vlan_id = htons(vid); 17328c2ecf20Sopenharmony_ci __be16 vlan_id_mask = htons(0xffff); 17338c2ecf20Sopenharmony_ci __be16 internal_vlan_id; 17348c2ecf20Sopenharmony_ci bool untagged; 17358c2ecf20Sopenharmony_ci bool adding = !(flags & OFDPA_OP_FLAG_REMOVE); 17368c2ecf20Sopenharmony_ci int err; 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci internal_vlan_id = ofdpa_port_vid_to_vlan(ofdpa_port, vid, &untagged); 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci if (adding && 17418c2ecf20Sopenharmony_ci test_bit(ntohs(internal_vlan_id), ofdpa_port->vlan_bitmap)) 17428c2ecf20Sopenharmony_ci return 0; /* already added */ 17438c2ecf20Sopenharmony_ci else if (!adding && 17448c2ecf20Sopenharmony_ci !test_bit(ntohs(internal_vlan_id), ofdpa_port->vlan_bitmap)) 17458c2ecf20Sopenharmony_ci return 0; /* already removed */ 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci change_bit(ntohs(internal_vlan_id), ofdpa_port->vlan_bitmap); 17488c2ecf20Sopenharmony_ci 17498c2ecf20Sopenharmony_ci if (adding) { 17508c2ecf20Sopenharmony_ci err = ofdpa_port_ctrl_vlan_add(ofdpa_port, flags, 17518c2ecf20Sopenharmony_ci internal_vlan_id); 17528c2ecf20Sopenharmony_ci if (err) { 17538c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) port ctrl vlan add\n", err); 17548c2ecf20Sopenharmony_ci goto err_vlan_add; 17558c2ecf20Sopenharmony_ci } 17568c2ecf20Sopenharmony_ci } 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci err = ofdpa_port_vlan_l2_groups(ofdpa_port, flags, 17598c2ecf20Sopenharmony_ci internal_vlan_id, untagged); 17608c2ecf20Sopenharmony_ci if (err) { 17618c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 groups\n", err); 17628c2ecf20Sopenharmony_ci goto err_vlan_l2_groups; 17638c2ecf20Sopenharmony_ci } 17648c2ecf20Sopenharmony_ci 17658c2ecf20Sopenharmony_ci err = ofdpa_port_vlan_flood_group(ofdpa_port, flags, 17668c2ecf20Sopenharmony_ci internal_vlan_id); 17678c2ecf20Sopenharmony_ci if (err) { 17688c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 flood group\n", err); 17698c2ecf20Sopenharmony_ci goto err_flood_group; 17708c2ecf20Sopenharmony_ci } 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci err = ofdpa_flow_tbl_vlan(ofdpa_port, flags, 17738c2ecf20Sopenharmony_ci in_pport, vlan_id, vlan_id_mask, 17748c2ecf20Sopenharmony_ci goto_tbl, untagged, internal_vlan_id); 17758c2ecf20Sopenharmony_ci if (err) 17768c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) port VLAN table\n", err); 17778c2ecf20Sopenharmony_ci 17788c2ecf20Sopenharmony_ci return 0; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_cierr_vlan_add: 17818c2ecf20Sopenharmony_cierr_vlan_l2_groups: 17828c2ecf20Sopenharmony_cierr_flood_group: 17838c2ecf20Sopenharmony_ci change_bit(ntohs(internal_vlan_id), ofdpa_port->vlan_bitmap); 17848c2ecf20Sopenharmony_ci return err; 17858c2ecf20Sopenharmony_ci} 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_cistatic int ofdpa_port_ig_tbl(struct ofdpa_port *ofdpa_port, int flags) 17888c2ecf20Sopenharmony_ci{ 17898c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl; 17908c2ecf20Sopenharmony_ci u32 in_pport; 17918c2ecf20Sopenharmony_ci u32 in_pport_mask; 17928c2ecf20Sopenharmony_ci int err; 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci /* Normal Ethernet Frames. Matches pkts from any local physical 17958c2ecf20Sopenharmony_ci * ports. Goto VLAN tbl. 17968c2ecf20Sopenharmony_ci */ 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci in_pport = 0; 17998c2ecf20Sopenharmony_ci in_pport_mask = 0xffff0000; 18008c2ecf20Sopenharmony_ci goto_tbl = ROCKER_OF_DPA_TABLE_ID_VLAN; 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci err = ofdpa_flow_tbl_ig_port(ofdpa_port, flags, 18038c2ecf20Sopenharmony_ci in_pport, in_pport_mask, 18048c2ecf20Sopenharmony_ci goto_tbl); 18058c2ecf20Sopenharmony_ci if (err) 18068c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) ingress port table entry\n", err); 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci return err; 18098c2ecf20Sopenharmony_ci} 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_cistruct ofdpa_fdb_learn_work { 18128c2ecf20Sopenharmony_ci struct work_struct work; 18138c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port; 18148c2ecf20Sopenharmony_ci int flags; 18158c2ecf20Sopenharmony_ci u8 addr[ETH_ALEN]; 18168c2ecf20Sopenharmony_ci u16 vid; 18178c2ecf20Sopenharmony_ci}; 18188c2ecf20Sopenharmony_ci 18198c2ecf20Sopenharmony_cistatic void ofdpa_port_fdb_learn_work(struct work_struct *work) 18208c2ecf20Sopenharmony_ci{ 18218c2ecf20Sopenharmony_ci const struct ofdpa_fdb_learn_work *lw = 18228c2ecf20Sopenharmony_ci container_of(work, struct ofdpa_fdb_learn_work, work); 18238c2ecf20Sopenharmony_ci bool removing = (lw->flags & OFDPA_OP_FLAG_REMOVE); 18248c2ecf20Sopenharmony_ci bool learned = (lw->flags & OFDPA_OP_FLAG_LEARNED); 18258c2ecf20Sopenharmony_ci struct switchdev_notifier_fdb_info info; 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci info.addr = lw->addr; 18288c2ecf20Sopenharmony_ci info.vid = lw->vid; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci rtnl_lock(); 18318c2ecf20Sopenharmony_ci if (learned && removing) 18328c2ecf20Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, 18338c2ecf20Sopenharmony_ci lw->ofdpa_port->dev, &info.info, NULL); 18348c2ecf20Sopenharmony_ci else if (learned && !removing) 18358c2ecf20Sopenharmony_ci call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, 18368c2ecf20Sopenharmony_ci lw->ofdpa_port->dev, &info.info, NULL); 18378c2ecf20Sopenharmony_ci rtnl_unlock(); 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci kfree(work); 18408c2ecf20Sopenharmony_ci} 18418c2ecf20Sopenharmony_ci 18428c2ecf20Sopenharmony_cistatic int ofdpa_port_fdb_learn(struct ofdpa_port *ofdpa_port, 18438c2ecf20Sopenharmony_ci int flags, const u8 *addr, __be16 vlan_id) 18448c2ecf20Sopenharmony_ci{ 18458c2ecf20Sopenharmony_ci struct ofdpa_fdb_learn_work *lw; 18468c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl = 18478c2ecf20Sopenharmony_ci ROCKER_OF_DPA_TABLE_ID_ACL_POLICY; 18488c2ecf20Sopenharmony_ci u32 out_pport = ofdpa_port->pport; 18498c2ecf20Sopenharmony_ci u32 tunnel_id = 0; 18508c2ecf20Sopenharmony_ci u32 group_id = ROCKER_GROUP_NONE; 18518c2ecf20Sopenharmony_ci bool copy_to_cpu = false; 18528c2ecf20Sopenharmony_ci int err; 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci if (ofdpa_port_is_bridged(ofdpa_port)) 18558c2ecf20Sopenharmony_ci group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport); 18568c2ecf20Sopenharmony_ci 18578c2ecf20Sopenharmony_ci if (!(flags & OFDPA_OP_FLAG_REFRESH)) { 18588c2ecf20Sopenharmony_ci err = ofdpa_flow_tbl_bridge(ofdpa_port, flags, addr, 18598c2ecf20Sopenharmony_ci NULL, vlan_id, tunnel_id, goto_tbl, 18608c2ecf20Sopenharmony_ci group_id, copy_to_cpu); 18618c2ecf20Sopenharmony_ci if (err) 18628c2ecf20Sopenharmony_ci return err; 18638c2ecf20Sopenharmony_ci } 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci if (!ofdpa_port_is_bridged(ofdpa_port)) 18668c2ecf20Sopenharmony_ci return 0; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci lw = kzalloc(sizeof(*lw), GFP_ATOMIC); 18698c2ecf20Sopenharmony_ci if (!lw) 18708c2ecf20Sopenharmony_ci return -ENOMEM; 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_ci INIT_WORK(&lw->work, ofdpa_port_fdb_learn_work); 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci lw->ofdpa_port = ofdpa_port; 18758c2ecf20Sopenharmony_ci lw->flags = flags; 18768c2ecf20Sopenharmony_ci ether_addr_copy(lw->addr, addr); 18778c2ecf20Sopenharmony_ci lw->vid = ofdpa_port_vlan_to_vid(ofdpa_port, vlan_id); 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci schedule_work(&lw->work); 18808c2ecf20Sopenharmony_ci return 0; 18818c2ecf20Sopenharmony_ci} 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_cistatic struct ofdpa_fdb_tbl_entry * 18848c2ecf20Sopenharmony_ciofdpa_fdb_tbl_find(const struct ofdpa *ofdpa, 18858c2ecf20Sopenharmony_ci const struct ofdpa_fdb_tbl_entry *match) 18868c2ecf20Sopenharmony_ci{ 18878c2ecf20Sopenharmony_ci struct ofdpa_fdb_tbl_entry *found; 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci hash_for_each_possible(ofdpa->fdb_tbl, found, entry, match->key_crc32) 18908c2ecf20Sopenharmony_ci if (memcmp(&found->key, &match->key, sizeof(found->key)) == 0) 18918c2ecf20Sopenharmony_ci return found; 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci return NULL; 18948c2ecf20Sopenharmony_ci} 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_cistatic int ofdpa_port_fdb(struct ofdpa_port *ofdpa_port, 18978c2ecf20Sopenharmony_ci const unsigned char *addr, 18988c2ecf20Sopenharmony_ci __be16 vlan_id, int flags) 18998c2ecf20Sopenharmony_ci{ 19008c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = ofdpa_port->ofdpa; 19018c2ecf20Sopenharmony_ci struct ofdpa_fdb_tbl_entry *fdb; 19028c2ecf20Sopenharmony_ci struct ofdpa_fdb_tbl_entry *found; 19038c2ecf20Sopenharmony_ci bool removing = (flags & OFDPA_OP_FLAG_REMOVE); 19048c2ecf20Sopenharmony_ci unsigned long lock_flags; 19058c2ecf20Sopenharmony_ci 19068c2ecf20Sopenharmony_ci fdb = kzalloc(sizeof(*fdb), GFP_KERNEL); 19078c2ecf20Sopenharmony_ci if (!fdb) 19088c2ecf20Sopenharmony_ci return -ENOMEM; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ci fdb->learned = (flags & OFDPA_OP_FLAG_LEARNED); 19118c2ecf20Sopenharmony_ci fdb->touched = jiffies; 19128c2ecf20Sopenharmony_ci fdb->key.ofdpa_port = ofdpa_port; 19138c2ecf20Sopenharmony_ci ether_addr_copy(fdb->key.addr, addr); 19148c2ecf20Sopenharmony_ci fdb->key.vlan_id = vlan_id; 19158c2ecf20Sopenharmony_ci fdb->key_crc32 = crc32(~0, &fdb->key, sizeof(fdb->key)); 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->fdb_tbl_lock, lock_flags); 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci found = ofdpa_fdb_tbl_find(ofdpa, fdb); 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci if (found) { 19228c2ecf20Sopenharmony_ci found->touched = jiffies; 19238c2ecf20Sopenharmony_ci if (removing) { 19248c2ecf20Sopenharmony_ci kfree(fdb); 19258c2ecf20Sopenharmony_ci hash_del(&found->entry); 19268c2ecf20Sopenharmony_ci } 19278c2ecf20Sopenharmony_ci } else if (!removing) { 19288c2ecf20Sopenharmony_ci hash_add(ofdpa->fdb_tbl, &fdb->entry, 19298c2ecf20Sopenharmony_ci fdb->key_crc32); 19308c2ecf20Sopenharmony_ci } 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->fdb_tbl_lock, lock_flags); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci /* Check if adding and already exists, or removing and can't find */ 19358c2ecf20Sopenharmony_ci if (!found != !removing) { 19368c2ecf20Sopenharmony_ci kfree(fdb); 19378c2ecf20Sopenharmony_ci if (!found && removing) 19388c2ecf20Sopenharmony_ci return 0; 19398c2ecf20Sopenharmony_ci /* Refreshing existing to update aging timers */ 19408c2ecf20Sopenharmony_ci flags |= OFDPA_OP_FLAG_REFRESH; 19418c2ecf20Sopenharmony_ci } 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci return ofdpa_port_fdb_learn(ofdpa_port, flags, addr, vlan_id); 19448c2ecf20Sopenharmony_ci} 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_cistatic int ofdpa_port_fdb_flush(struct ofdpa_port *ofdpa_port, int flags) 19478c2ecf20Sopenharmony_ci{ 19488c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = ofdpa_port->ofdpa; 19498c2ecf20Sopenharmony_ci struct ofdpa_fdb_tbl_entry *found; 19508c2ecf20Sopenharmony_ci unsigned long lock_flags; 19518c2ecf20Sopenharmony_ci struct hlist_node *tmp; 19528c2ecf20Sopenharmony_ci int bkt; 19538c2ecf20Sopenharmony_ci int err = 0; 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci if (ofdpa_port->stp_state == BR_STATE_LEARNING || 19568c2ecf20Sopenharmony_ci ofdpa_port->stp_state == BR_STATE_FORWARDING) 19578c2ecf20Sopenharmony_ci return 0; 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci flags |= OFDPA_OP_FLAG_NOWAIT | OFDPA_OP_FLAG_REMOVE; 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->fdb_tbl_lock, lock_flags); 19628c2ecf20Sopenharmony_ci 19638c2ecf20Sopenharmony_ci hash_for_each_safe(ofdpa->fdb_tbl, bkt, tmp, found, entry) { 19648c2ecf20Sopenharmony_ci if (found->key.ofdpa_port != ofdpa_port) 19658c2ecf20Sopenharmony_ci continue; 19668c2ecf20Sopenharmony_ci if (!found->learned) 19678c2ecf20Sopenharmony_ci continue; 19688c2ecf20Sopenharmony_ci err = ofdpa_port_fdb_learn(ofdpa_port, flags, 19698c2ecf20Sopenharmony_ci found->key.addr, 19708c2ecf20Sopenharmony_ci found->key.vlan_id); 19718c2ecf20Sopenharmony_ci if (err) 19728c2ecf20Sopenharmony_ci goto err_out; 19738c2ecf20Sopenharmony_ci hash_del(&found->entry); 19748c2ecf20Sopenharmony_ci } 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_cierr_out: 19778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->fdb_tbl_lock, lock_flags); 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci return err; 19808c2ecf20Sopenharmony_ci} 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_cistatic void ofdpa_fdb_cleanup(struct timer_list *t) 19838c2ecf20Sopenharmony_ci{ 19848c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = from_timer(ofdpa, t, fdb_cleanup_timer); 19858c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port; 19868c2ecf20Sopenharmony_ci struct ofdpa_fdb_tbl_entry *entry; 19878c2ecf20Sopenharmony_ci struct hlist_node *tmp; 19888c2ecf20Sopenharmony_ci unsigned long next_timer = jiffies + ofdpa->ageing_time; 19898c2ecf20Sopenharmony_ci unsigned long expires; 19908c2ecf20Sopenharmony_ci unsigned long lock_flags; 19918c2ecf20Sopenharmony_ci int flags = OFDPA_OP_FLAG_NOWAIT | OFDPA_OP_FLAG_REMOVE | 19928c2ecf20Sopenharmony_ci OFDPA_OP_FLAG_LEARNED; 19938c2ecf20Sopenharmony_ci int bkt; 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->fdb_tbl_lock, lock_flags); 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci hash_for_each_safe(ofdpa->fdb_tbl, bkt, tmp, entry, entry) { 19988c2ecf20Sopenharmony_ci if (!entry->learned) 19998c2ecf20Sopenharmony_ci continue; 20008c2ecf20Sopenharmony_ci ofdpa_port = entry->key.ofdpa_port; 20018c2ecf20Sopenharmony_ci expires = entry->touched + ofdpa_port->ageing_time; 20028c2ecf20Sopenharmony_ci if (time_before_eq(expires, jiffies)) { 20038c2ecf20Sopenharmony_ci ofdpa_port_fdb_learn(ofdpa_port, flags, 20048c2ecf20Sopenharmony_ci entry->key.addr, 20058c2ecf20Sopenharmony_ci entry->key.vlan_id); 20068c2ecf20Sopenharmony_ci hash_del(&entry->entry); 20078c2ecf20Sopenharmony_ci } else if (time_before(expires, next_timer)) { 20088c2ecf20Sopenharmony_ci next_timer = expires; 20098c2ecf20Sopenharmony_ci } 20108c2ecf20Sopenharmony_ci } 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->fdb_tbl_lock, lock_flags); 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci mod_timer(&ofdpa->fdb_cleanup_timer, round_jiffies_up(next_timer)); 20158c2ecf20Sopenharmony_ci} 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_cistatic int ofdpa_port_router_mac(struct ofdpa_port *ofdpa_port, 20188c2ecf20Sopenharmony_ci int flags, __be16 vlan_id) 20198c2ecf20Sopenharmony_ci{ 20208c2ecf20Sopenharmony_ci u32 in_pport_mask = 0xffffffff; 20218c2ecf20Sopenharmony_ci __be16 eth_type; 20228c2ecf20Sopenharmony_ci const u8 *dst_mac_mask = ff_mac; 20238c2ecf20Sopenharmony_ci __be16 vlan_id_mask = htons(0xffff); 20248c2ecf20Sopenharmony_ci bool copy_to_cpu = false; 20258c2ecf20Sopenharmony_ci int err; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci if (ntohs(vlan_id) == 0) 20288c2ecf20Sopenharmony_ci vlan_id = ofdpa_port->internal_vlan_id; 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci eth_type = htons(ETH_P_IP); 20318c2ecf20Sopenharmony_ci err = ofdpa_flow_tbl_term_mac(ofdpa_port, ofdpa_port->pport, 20328c2ecf20Sopenharmony_ci in_pport_mask, eth_type, 20338c2ecf20Sopenharmony_ci ofdpa_port->dev->dev_addr, 20348c2ecf20Sopenharmony_ci dst_mac_mask, vlan_id, vlan_id_mask, 20358c2ecf20Sopenharmony_ci copy_to_cpu, flags); 20368c2ecf20Sopenharmony_ci if (err) 20378c2ecf20Sopenharmony_ci return err; 20388c2ecf20Sopenharmony_ci 20398c2ecf20Sopenharmony_ci eth_type = htons(ETH_P_IPV6); 20408c2ecf20Sopenharmony_ci err = ofdpa_flow_tbl_term_mac(ofdpa_port, ofdpa_port->pport, 20418c2ecf20Sopenharmony_ci in_pport_mask, eth_type, 20428c2ecf20Sopenharmony_ci ofdpa_port->dev->dev_addr, 20438c2ecf20Sopenharmony_ci dst_mac_mask, vlan_id, vlan_id_mask, 20448c2ecf20Sopenharmony_ci copy_to_cpu, flags); 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci return err; 20478c2ecf20Sopenharmony_ci} 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_cistatic int ofdpa_port_fwding(struct ofdpa_port *ofdpa_port, int flags) 20508c2ecf20Sopenharmony_ci{ 20518c2ecf20Sopenharmony_ci bool pop_vlan; 20528c2ecf20Sopenharmony_ci u32 out_pport; 20538c2ecf20Sopenharmony_ci __be16 vlan_id; 20548c2ecf20Sopenharmony_ci u16 vid; 20558c2ecf20Sopenharmony_ci int err; 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci /* Port will be forwarding-enabled if its STP state is LEARNING 20588c2ecf20Sopenharmony_ci * or FORWARDING. Traffic from CPU can still egress, regardless of 20598c2ecf20Sopenharmony_ci * port STP state. Use L2 interface group on port VLANs as a way 20608c2ecf20Sopenharmony_ci * to toggle port forwarding: if forwarding is disabled, L2 20618c2ecf20Sopenharmony_ci * interface group will not exist. 20628c2ecf20Sopenharmony_ci */ 20638c2ecf20Sopenharmony_ci 20648c2ecf20Sopenharmony_ci if (ofdpa_port->stp_state != BR_STATE_LEARNING && 20658c2ecf20Sopenharmony_ci ofdpa_port->stp_state != BR_STATE_FORWARDING) 20668c2ecf20Sopenharmony_ci flags |= OFDPA_OP_FLAG_REMOVE; 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci out_pport = ofdpa_port->pport; 20698c2ecf20Sopenharmony_ci for (vid = 1; vid < VLAN_N_VID; vid++) { 20708c2ecf20Sopenharmony_ci if (!test_bit(vid, ofdpa_port->vlan_bitmap)) 20718c2ecf20Sopenharmony_ci continue; 20728c2ecf20Sopenharmony_ci vlan_id = htons(vid); 20738c2ecf20Sopenharmony_ci pop_vlan = ofdpa_vlan_id_is_internal(vlan_id); 20748c2ecf20Sopenharmony_ci err = ofdpa_group_l2_interface(ofdpa_port, flags, 20758c2ecf20Sopenharmony_ci vlan_id, out_pport, pop_vlan); 20768c2ecf20Sopenharmony_ci if (err) { 20778c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) port VLAN l2 group for pport %d\n", 20788c2ecf20Sopenharmony_ci err, out_pport); 20798c2ecf20Sopenharmony_ci return err; 20808c2ecf20Sopenharmony_ci } 20818c2ecf20Sopenharmony_ci } 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci return 0; 20848c2ecf20Sopenharmony_ci} 20858c2ecf20Sopenharmony_ci 20868c2ecf20Sopenharmony_cistatic int ofdpa_port_stp_update(struct ofdpa_port *ofdpa_port, 20878c2ecf20Sopenharmony_ci int flags, u8 state) 20888c2ecf20Sopenharmony_ci{ 20898c2ecf20Sopenharmony_ci bool want[OFDPA_CTRL_MAX] = { 0, }; 20908c2ecf20Sopenharmony_ci bool prev_ctrls[OFDPA_CTRL_MAX]; 20918c2ecf20Sopenharmony_ci u8 prev_state; 20928c2ecf20Sopenharmony_ci int err; 20938c2ecf20Sopenharmony_ci int i; 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci memcpy(prev_ctrls, ofdpa_port->ctrls, sizeof(prev_ctrls)); 20968c2ecf20Sopenharmony_ci prev_state = ofdpa_port->stp_state; 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_ci if (ofdpa_port->stp_state == state) 20998c2ecf20Sopenharmony_ci return 0; 21008c2ecf20Sopenharmony_ci 21018c2ecf20Sopenharmony_ci ofdpa_port->stp_state = state; 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci switch (state) { 21048c2ecf20Sopenharmony_ci case BR_STATE_DISABLED: 21058c2ecf20Sopenharmony_ci /* port is completely disabled */ 21068c2ecf20Sopenharmony_ci break; 21078c2ecf20Sopenharmony_ci case BR_STATE_LISTENING: 21088c2ecf20Sopenharmony_ci case BR_STATE_BLOCKING: 21098c2ecf20Sopenharmony_ci want[OFDPA_CTRL_LINK_LOCAL_MCAST] = true; 21108c2ecf20Sopenharmony_ci break; 21118c2ecf20Sopenharmony_ci case BR_STATE_LEARNING: 21128c2ecf20Sopenharmony_ci case BR_STATE_FORWARDING: 21138c2ecf20Sopenharmony_ci if (!ofdpa_port_is_ovsed(ofdpa_port)) 21148c2ecf20Sopenharmony_ci want[OFDPA_CTRL_LINK_LOCAL_MCAST] = true; 21158c2ecf20Sopenharmony_ci want[OFDPA_CTRL_IPV4_MCAST] = true; 21168c2ecf20Sopenharmony_ci want[OFDPA_CTRL_IPV6_MCAST] = true; 21178c2ecf20Sopenharmony_ci if (ofdpa_port_is_bridged(ofdpa_port)) 21188c2ecf20Sopenharmony_ci want[OFDPA_CTRL_DFLT_BRIDGING] = true; 21198c2ecf20Sopenharmony_ci else if (ofdpa_port_is_ovsed(ofdpa_port)) 21208c2ecf20Sopenharmony_ci want[OFDPA_CTRL_DFLT_OVS] = true; 21218c2ecf20Sopenharmony_ci else 21228c2ecf20Sopenharmony_ci want[OFDPA_CTRL_LOCAL_ARP] = true; 21238c2ecf20Sopenharmony_ci break; 21248c2ecf20Sopenharmony_ci } 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci for (i = 0; i < OFDPA_CTRL_MAX; i++) { 21278c2ecf20Sopenharmony_ci if (want[i] != ofdpa_port->ctrls[i]) { 21288c2ecf20Sopenharmony_ci int ctrl_flags = flags | 21298c2ecf20Sopenharmony_ci (want[i] ? 0 : OFDPA_OP_FLAG_REMOVE); 21308c2ecf20Sopenharmony_ci err = ofdpa_port_ctrl(ofdpa_port, ctrl_flags, 21318c2ecf20Sopenharmony_ci &ofdpa_ctrls[i]); 21328c2ecf20Sopenharmony_ci if (err) 21338c2ecf20Sopenharmony_ci goto err_port_ctrl; 21348c2ecf20Sopenharmony_ci ofdpa_port->ctrls[i] = want[i]; 21358c2ecf20Sopenharmony_ci } 21368c2ecf20Sopenharmony_ci } 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci err = ofdpa_port_fdb_flush(ofdpa_port, flags); 21398c2ecf20Sopenharmony_ci if (err) 21408c2ecf20Sopenharmony_ci goto err_fdb_flush; 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_ci err = ofdpa_port_fwding(ofdpa_port, flags); 21438c2ecf20Sopenharmony_ci if (err) 21448c2ecf20Sopenharmony_ci goto err_port_fwding; 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci return 0; 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_cierr_port_ctrl: 21498c2ecf20Sopenharmony_cierr_fdb_flush: 21508c2ecf20Sopenharmony_cierr_port_fwding: 21518c2ecf20Sopenharmony_ci memcpy(ofdpa_port->ctrls, prev_ctrls, sizeof(prev_ctrls)); 21528c2ecf20Sopenharmony_ci ofdpa_port->stp_state = prev_state; 21538c2ecf20Sopenharmony_ci return err; 21548c2ecf20Sopenharmony_ci} 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_cistatic int ofdpa_port_fwd_enable(struct ofdpa_port *ofdpa_port, int flags) 21578c2ecf20Sopenharmony_ci{ 21588c2ecf20Sopenharmony_ci if (ofdpa_port_is_bridged(ofdpa_port)) 21598c2ecf20Sopenharmony_ci /* bridge STP will enable port */ 21608c2ecf20Sopenharmony_ci return 0; 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_ci /* port is not bridged, so simulate going to FORWARDING state */ 21638c2ecf20Sopenharmony_ci return ofdpa_port_stp_update(ofdpa_port, flags, 21648c2ecf20Sopenharmony_ci BR_STATE_FORWARDING); 21658c2ecf20Sopenharmony_ci} 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_cistatic int ofdpa_port_fwd_disable(struct ofdpa_port *ofdpa_port, int flags) 21688c2ecf20Sopenharmony_ci{ 21698c2ecf20Sopenharmony_ci if (ofdpa_port_is_bridged(ofdpa_port)) 21708c2ecf20Sopenharmony_ci /* bridge STP will disable port */ 21718c2ecf20Sopenharmony_ci return 0; 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci /* port is not bridged, so simulate going to DISABLED state */ 21748c2ecf20Sopenharmony_ci return ofdpa_port_stp_update(ofdpa_port, flags, 21758c2ecf20Sopenharmony_ci BR_STATE_DISABLED); 21768c2ecf20Sopenharmony_ci} 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_cistatic int ofdpa_port_vlan_add(struct ofdpa_port *ofdpa_port, 21798c2ecf20Sopenharmony_ci u16 vid, u16 flags) 21808c2ecf20Sopenharmony_ci{ 21818c2ecf20Sopenharmony_ci int err; 21828c2ecf20Sopenharmony_ci 21838c2ecf20Sopenharmony_ci /* XXX deal with flags for PVID and untagged */ 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci err = ofdpa_port_vlan(ofdpa_port, 0, vid); 21868c2ecf20Sopenharmony_ci if (err) 21878c2ecf20Sopenharmony_ci return err; 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci err = ofdpa_port_router_mac(ofdpa_port, 0, htons(vid)); 21908c2ecf20Sopenharmony_ci if (err) 21918c2ecf20Sopenharmony_ci ofdpa_port_vlan(ofdpa_port, 21928c2ecf20Sopenharmony_ci OFDPA_OP_FLAG_REMOVE, vid); 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_ci return err; 21958c2ecf20Sopenharmony_ci} 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_cistatic int ofdpa_port_vlan_del(struct ofdpa_port *ofdpa_port, 21988c2ecf20Sopenharmony_ci u16 vid, u16 flags) 21998c2ecf20Sopenharmony_ci{ 22008c2ecf20Sopenharmony_ci int err; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci err = ofdpa_port_router_mac(ofdpa_port, OFDPA_OP_FLAG_REMOVE, 22038c2ecf20Sopenharmony_ci htons(vid)); 22048c2ecf20Sopenharmony_ci if (err) 22058c2ecf20Sopenharmony_ci return err; 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci return ofdpa_port_vlan(ofdpa_port, OFDPA_OP_FLAG_REMOVE, 22088c2ecf20Sopenharmony_ci vid); 22098c2ecf20Sopenharmony_ci} 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_cistatic struct ofdpa_internal_vlan_tbl_entry * 22128c2ecf20Sopenharmony_ciofdpa_internal_vlan_tbl_find(const struct ofdpa *ofdpa, int ifindex) 22138c2ecf20Sopenharmony_ci{ 22148c2ecf20Sopenharmony_ci struct ofdpa_internal_vlan_tbl_entry *found; 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci hash_for_each_possible(ofdpa->internal_vlan_tbl, found, 22178c2ecf20Sopenharmony_ci entry, ifindex) { 22188c2ecf20Sopenharmony_ci if (found->ifindex == ifindex) 22198c2ecf20Sopenharmony_ci return found; 22208c2ecf20Sopenharmony_ci } 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci return NULL; 22238c2ecf20Sopenharmony_ci} 22248c2ecf20Sopenharmony_ci 22258c2ecf20Sopenharmony_cistatic __be16 ofdpa_port_internal_vlan_id_get(struct ofdpa_port *ofdpa_port, 22268c2ecf20Sopenharmony_ci int ifindex) 22278c2ecf20Sopenharmony_ci{ 22288c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = ofdpa_port->ofdpa; 22298c2ecf20Sopenharmony_ci struct ofdpa_internal_vlan_tbl_entry *entry; 22308c2ecf20Sopenharmony_ci struct ofdpa_internal_vlan_tbl_entry *found; 22318c2ecf20Sopenharmony_ci unsigned long lock_flags; 22328c2ecf20Sopenharmony_ci int i; 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci entry = kzalloc(sizeof(*entry), GFP_KERNEL); 22358c2ecf20Sopenharmony_ci if (!entry) 22368c2ecf20Sopenharmony_ci return 0; 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci entry->ifindex = ifindex; 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->internal_vlan_tbl_lock, lock_flags); 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci found = ofdpa_internal_vlan_tbl_find(ofdpa, ifindex); 22438c2ecf20Sopenharmony_ci if (found) { 22448c2ecf20Sopenharmony_ci kfree(entry); 22458c2ecf20Sopenharmony_ci goto found; 22468c2ecf20Sopenharmony_ci } 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci found = entry; 22498c2ecf20Sopenharmony_ci hash_add(ofdpa->internal_vlan_tbl, &found->entry, found->ifindex); 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ci for (i = 0; i < OFDPA_N_INTERNAL_VLANS; i++) { 22528c2ecf20Sopenharmony_ci if (test_and_set_bit(i, ofdpa->internal_vlan_bitmap)) 22538c2ecf20Sopenharmony_ci continue; 22548c2ecf20Sopenharmony_ci found->vlan_id = htons(OFDPA_INTERNAL_VLAN_ID_BASE + i); 22558c2ecf20Sopenharmony_ci goto found; 22568c2ecf20Sopenharmony_ci } 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Out of internal VLAN IDs\n"); 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_cifound: 22618c2ecf20Sopenharmony_ci found->ref_count++; 22628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->internal_vlan_tbl_lock, lock_flags); 22638c2ecf20Sopenharmony_ci 22648c2ecf20Sopenharmony_ci return found->vlan_id; 22658c2ecf20Sopenharmony_ci} 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_cistatic int ofdpa_port_fib_ipv4(struct ofdpa_port *ofdpa_port, __be32 dst, 22688c2ecf20Sopenharmony_ci int dst_len, struct fib_info *fi, u32 tb_id, 22698c2ecf20Sopenharmony_ci int flags) 22708c2ecf20Sopenharmony_ci{ 22718c2ecf20Sopenharmony_ci const struct fib_nh *nh; 22728c2ecf20Sopenharmony_ci __be16 eth_type = htons(ETH_P_IP); 22738c2ecf20Sopenharmony_ci __be32 dst_mask = inet_make_mask(dst_len); 22748c2ecf20Sopenharmony_ci __be16 internal_vlan_id = ofdpa_port->internal_vlan_id; 22758c2ecf20Sopenharmony_ci u32 priority = fi->fib_priority; 22768c2ecf20Sopenharmony_ci enum rocker_of_dpa_table_id goto_tbl = 22778c2ecf20Sopenharmony_ci ROCKER_OF_DPA_TABLE_ID_ACL_POLICY; 22788c2ecf20Sopenharmony_ci u32 group_id; 22798c2ecf20Sopenharmony_ci bool nh_on_port; 22808c2ecf20Sopenharmony_ci bool has_gw; 22818c2ecf20Sopenharmony_ci u32 index; 22828c2ecf20Sopenharmony_ci int err; 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci /* XXX support ECMP */ 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci nh = fib_info_nh(fi, 0); 22878c2ecf20Sopenharmony_ci nh_on_port = (nh->fib_nh_dev == ofdpa_port->dev); 22888c2ecf20Sopenharmony_ci has_gw = !!nh->fib_nh_gw4; 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci if (has_gw && nh_on_port) { 22918c2ecf20Sopenharmony_ci err = ofdpa_port_ipv4_nh(ofdpa_port, flags, 22928c2ecf20Sopenharmony_ci nh->fib_nh_gw4, &index); 22938c2ecf20Sopenharmony_ci if (err) 22948c2ecf20Sopenharmony_ci return err; 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci group_id = ROCKER_GROUP_L3_UNICAST(index); 22978c2ecf20Sopenharmony_ci } else { 22988c2ecf20Sopenharmony_ci /* Send to CPU for processing */ 22998c2ecf20Sopenharmony_ci group_id = ROCKER_GROUP_L2_INTERFACE(internal_vlan_id, 0); 23008c2ecf20Sopenharmony_ci } 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci err = ofdpa_flow_tbl_ucast4_routing(ofdpa_port, eth_type, dst, 23038c2ecf20Sopenharmony_ci dst_mask, priority, goto_tbl, 23048c2ecf20Sopenharmony_ci group_id, fi, flags); 23058c2ecf20Sopenharmony_ci if (err) 23068c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "Error (%d) IPv4 route %pI4\n", 23078c2ecf20Sopenharmony_ci err, &dst); 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci return err; 23108c2ecf20Sopenharmony_ci} 23118c2ecf20Sopenharmony_ci 23128c2ecf20Sopenharmony_cistatic void 23138c2ecf20Sopenharmony_ciofdpa_port_internal_vlan_id_put(const struct ofdpa_port *ofdpa_port, 23148c2ecf20Sopenharmony_ci int ifindex) 23158c2ecf20Sopenharmony_ci{ 23168c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = ofdpa_port->ofdpa; 23178c2ecf20Sopenharmony_ci struct ofdpa_internal_vlan_tbl_entry *found; 23188c2ecf20Sopenharmony_ci unsigned long lock_flags; 23198c2ecf20Sopenharmony_ci unsigned long bit; 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->internal_vlan_tbl_lock, lock_flags); 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci found = ofdpa_internal_vlan_tbl_find(ofdpa, ifindex); 23248c2ecf20Sopenharmony_ci if (!found) { 23258c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, 23268c2ecf20Sopenharmony_ci "ifindex (%d) not found in internal VLAN tbl\n", 23278c2ecf20Sopenharmony_ci ifindex); 23288c2ecf20Sopenharmony_ci goto not_found; 23298c2ecf20Sopenharmony_ci } 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci if (--found->ref_count <= 0) { 23328c2ecf20Sopenharmony_ci bit = ntohs(found->vlan_id) - OFDPA_INTERNAL_VLAN_ID_BASE; 23338c2ecf20Sopenharmony_ci clear_bit(bit, ofdpa->internal_vlan_bitmap); 23348c2ecf20Sopenharmony_ci hash_del(&found->entry); 23358c2ecf20Sopenharmony_ci kfree(found); 23368c2ecf20Sopenharmony_ci } 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_cinot_found: 23398c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->internal_vlan_tbl_lock, lock_flags); 23408c2ecf20Sopenharmony_ci} 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci/********************************** 23438c2ecf20Sopenharmony_ci * Rocker world ops implementation 23448c2ecf20Sopenharmony_ci **********************************/ 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_cistatic int ofdpa_init(struct rocker *rocker) 23478c2ecf20Sopenharmony_ci{ 23488c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = rocker->wpriv; 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_ci ofdpa->rocker = rocker; 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci hash_init(ofdpa->flow_tbl); 23538c2ecf20Sopenharmony_ci spin_lock_init(&ofdpa->flow_tbl_lock); 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci hash_init(ofdpa->group_tbl); 23568c2ecf20Sopenharmony_ci spin_lock_init(&ofdpa->group_tbl_lock); 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci hash_init(ofdpa->fdb_tbl); 23598c2ecf20Sopenharmony_ci spin_lock_init(&ofdpa->fdb_tbl_lock); 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci hash_init(ofdpa->internal_vlan_tbl); 23628c2ecf20Sopenharmony_ci spin_lock_init(&ofdpa->internal_vlan_tbl_lock); 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci hash_init(ofdpa->neigh_tbl); 23658c2ecf20Sopenharmony_ci spin_lock_init(&ofdpa->neigh_tbl_lock); 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_ci timer_setup(&ofdpa->fdb_cleanup_timer, ofdpa_fdb_cleanup, 0); 23688c2ecf20Sopenharmony_ci mod_timer(&ofdpa->fdb_cleanup_timer, jiffies); 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci ofdpa->ageing_time = BR_DEFAULT_AGEING_TIME; 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci return 0; 23738c2ecf20Sopenharmony_ci} 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_cistatic void ofdpa_fini(struct rocker *rocker) 23768c2ecf20Sopenharmony_ci{ 23778c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = rocker->wpriv; 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_ci unsigned long flags; 23808c2ecf20Sopenharmony_ci struct ofdpa_flow_tbl_entry *flow_entry; 23818c2ecf20Sopenharmony_ci struct ofdpa_group_tbl_entry *group_entry; 23828c2ecf20Sopenharmony_ci struct ofdpa_fdb_tbl_entry *fdb_entry; 23838c2ecf20Sopenharmony_ci struct ofdpa_internal_vlan_tbl_entry *internal_vlan_entry; 23848c2ecf20Sopenharmony_ci struct ofdpa_neigh_tbl_entry *neigh_entry; 23858c2ecf20Sopenharmony_ci struct hlist_node *tmp; 23868c2ecf20Sopenharmony_ci int bkt; 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_ci del_timer_sync(&ofdpa->fdb_cleanup_timer); 23898c2ecf20Sopenharmony_ci flush_workqueue(rocker->rocker_owq); 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->flow_tbl_lock, flags); 23928c2ecf20Sopenharmony_ci hash_for_each_safe(ofdpa->flow_tbl, bkt, tmp, flow_entry, entry) 23938c2ecf20Sopenharmony_ci hash_del(&flow_entry->entry); 23948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->flow_tbl_lock, flags); 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->group_tbl_lock, flags); 23978c2ecf20Sopenharmony_ci hash_for_each_safe(ofdpa->group_tbl, bkt, tmp, group_entry, entry) 23988c2ecf20Sopenharmony_ci hash_del(&group_entry->entry); 23998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->group_tbl_lock, flags); 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->fdb_tbl_lock, flags); 24028c2ecf20Sopenharmony_ci hash_for_each_safe(ofdpa->fdb_tbl, bkt, tmp, fdb_entry, entry) 24038c2ecf20Sopenharmony_ci hash_del(&fdb_entry->entry); 24048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->fdb_tbl_lock, flags); 24058c2ecf20Sopenharmony_ci 24068c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->internal_vlan_tbl_lock, flags); 24078c2ecf20Sopenharmony_ci hash_for_each_safe(ofdpa->internal_vlan_tbl, bkt, 24088c2ecf20Sopenharmony_ci tmp, internal_vlan_entry, entry) 24098c2ecf20Sopenharmony_ci hash_del(&internal_vlan_entry->entry); 24108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->internal_vlan_tbl_lock, flags); 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->neigh_tbl_lock, flags); 24138c2ecf20Sopenharmony_ci hash_for_each_safe(ofdpa->neigh_tbl, bkt, tmp, neigh_entry, entry) 24148c2ecf20Sopenharmony_ci hash_del(&neigh_entry->entry); 24158c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->neigh_tbl_lock, flags); 24168c2ecf20Sopenharmony_ci} 24178c2ecf20Sopenharmony_ci 24188c2ecf20Sopenharmony_cistatic int ofdpa_port_pre_init(struct rocker_port *rocker_port) 24198c2ecf20Sopenharmony_ci{ 24208c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 24218c2ecf20Sopenharmony_ci 24228c2ecf20Sopenharmony_ci ofdpa_port->ofdpa = rocker_port->rocker->wpriv; 24238c2ecf20Sopenharmony_ci ofdpa_port->rocker_port = rocker_port; 24248c2ecf20Sopenharmony_ci ofdpa_port->dev = rocker_port->dev; 24258c2ecf20Sopenharmony_ci ofdpa_port->pport = rocker_port->pport; 24268c2ecf20Sopenharmony_ci ofdpa_port->brport_flags = BR_LEARNING; 24278c2ecf20Sopenharmony_ci ofdpa_port->ageing_time = BR_DEFAULT_AGEING_TIME; 24288c2ecf20Sopenharmony_ci return 0; 24298c2ecf20Sopenharmony_ci} 24308c2ecf20Sopenharmony_ci 24318c2ecf20Sopenharmony_cistatic int ofdpa_port_init(struct rocker_port *rocker_port) 24328c2ecf20Sopenharmony_ci{ 24338c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 24348c2ecf20Sopenharmony_ci int err; 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci rocker_port_set_learning(rocker_port, 24378c2ecf20Sopenharmony_ci !!(ofdpa_port->brport_flags & BR_LEARNING)); 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci err = ofdpa_port_ig_tbl(ofdpa_port, 0); 24408c2ecf20Sopenharmony_ci if (err) { 24418c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "install ig port table failed\n"); 24428c2ecf20Sopenharmony_ci return err; 24438c2ecf20Sopenharmony_ci } 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_ci ofdpa_port->internal_vlan_id = 24468c2ecf20Sopenharmony_ci ofdpa_port_internal_vlan_id_get(ofdpa_port, 24478c2ecf20Sopenharmony_ci ofdpa_port->dev->ifindex); 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ci err = ofdpa_port_vlan_add(ofdpa_port, OFDPA_UNTAGGED_VID, 0); 24508c2ecf20Sopenharmony_ci if (err) { 24518c2ecf20Sopenharmony_ci netdev_err(ofdpa_port->dev, "install untagged VLAN failed\n"); 24528c2ecf20Sopenharmony_ci goto err_untagged_vlan; 24538c2ecf20Sopenharmony_ci } 24548c2ecf20Sopenharmony_ci return 0; 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_cierr_untagged_vlan: 24578c2ecf20Sopenharmony_ci ofdpa_port_ig_tbl(ofdpa_port, OFDPA_OP_FLAG_REMOVE); 24588c2ecf20Sopenharmony_ci return err; 24598c2ecf20Sopenharmony_ci} 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_cistatic void ofdpa_port_fini(struct rocker_port *rocker_port) 24628c2ecf20Sopenharmony_ci{ 24638c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci ofdpa_port_ig_tbl(ofdpa_port, OFDPA_OP_FLAG_REMOVE); 24668c2ecf20Sopenharmony_ci} 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_cistatic int ofdpa_port_open(struct rocker_port *rocker_port) 24698c2ecf20Sopenharmony_ci{ 24708c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 24718c2ecf20Sopenharmony_ci 24728c2ecf20Sopenharmony_ci return ofdpa_port_fwd_enable(ofdpa_port, 0); 24738c2ecf20Sopenharmony_ci} 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_cistatic void ofdpa_port_stop(struct rocker_port *rocker_port) 24768c2ecf20Sopenharmony_ci{ 24778c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci ofdpa_port_fwd_disable(ofdpa_port, OFDPA_OP_FLAG_NOWAIT); 24808c2ecf20Sopenharmony_ci} 24818c2ecf20Sopenharmony_ci 24828c2ecf20Sopenharmony_cistatic int ofdpa_port_attr_stp_state_set(struct rocker_port *rocker_port, 24838c2ecf20Sopenharmony_ci u8 state) 24848c2ecf20Sopenharmony_ci{ 24858c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 24868c2ecf20Sopenharmony_ci 24878c2ecf20Sopenharmony_ci return ofdpa_port_stp_update(ofdpa_port, 0, state); 24888c2ecf20Sopenharmony_ci} 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_cistatic int ofdpa_port_attr_bridge_flags_set(struct rocker_port *rocker_port, 24918c2ecf20Sopenharmony_ci unsigned long brport_flags, 24928c2ecf20Sopenharmony_ci struct switchdev_trans *trans) 24938c2ecf20Sopenharmony_ci{ 24948c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 24958c2ecf20Sopenharmony_ci unsigned long orig_flags; 24968c2ecf20Sopenharmony_ci int err = 0; 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci orig_flags = ofdpa_port->brport_flags; 24998c2ecf20Sopenharmony_ci ofdpa_port->brport_flags = brport_flags; 25008c2ecf20Sopenharmony_ci if ((orig_flags ^ ofdpa_port->brport_flags) & BR_LEARNING && 25018c2ecf20Sopenharmony_ci !switchdev_trans_ph_prepare(trans)) 25028c2ecf20Sopenharmony_ci err = rocker_port_set_learning(ofdpa_port->rocker_port, 25038c2ecf20Sopenharmony_ci !!(ofdpa_port->brport_flags & BR_LEARNING)); 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci if (switchdev_trans_ph_prepare(trans)) 25068c2ecf20Sopenharmony_ci ofdpa_port->brport_flags = orig_flags; 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci return err; 25098c2ecf20Sopenharmony_ci} 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_cistatic int 25128c2ecf20Sopenharmony_ciofdpa_port_attr_bridge_flags_support_get(const struct rocker_port * 25138c2ecf20Sopenharmony_ci rocker_port, 25148c2ecf20Sopenharmony_ci unsigned long * 25158c2ecf20Sopenharmony_ci p_brport_flags_support) 25168c2ecf20Sopenharmony_ci{ 25178c2ecf20Sopenharmony_ci *p_brport_flags_support = BR_LEARNING; 25188c2ecf20Sopenharmony_ci return 0; 25198c2ecf20Sopenharmony_ci} 25208c2ecf20Sopenharmony_ci 25218c2ecf20Sopenharmony_cistatic int 25228c2ecf20Sopenharmony_ciofdpa_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port, 25238c2ecf20Sopenharmony_ci u32 ageing_time, 25248c2ecf20Sopenharmony_ci struct switchdev_trans *trans) 25258c2ecf20Sopenharmony_ci{ 25268c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 25278c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = ofdpa_port->ofdpa; 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci if (!switchdev_trans_ph_prepare(trans)) { 25308c2ecf20Sopenharmony_ci ofdpa_port->ageing_time = clock_t_to_jiffies(ageing_time); 25318c2ecf20Sopenharmony_ci if (ofdpa_port->ageing_time < ofdpa->ageing_time) 25328c2ecf20Sopenharmony_ci ofdpa->ageing_time = ofdpa_port->ageing_time; 25338c2ecf20Sopenharmony_ci mod_timer(&ofdpa_port->ofdpa->fdb_cleanup_timer, jiffies); 25348c2ecf20Sopenharmony_ci } 25358c2ecf20Sopenharmony_ci 25368c2ecf20Sopenharmony_ci return 0; 25378c2ecf20Sopenharmony_ci} 25388c2ecf20Sopenharmony_ci 25398c2ecf20Sopenharmony_cistatic int ofdpa_port_obj_vlan_add(struct rocker_port *rocker_port, 25408c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 25418c2ecf20Sopenharmony_ci{ 25428c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 25438c2ecf20Sopenharmony_ci u16 vid; 25448c2ecf20Sopenharmony_ci int err; 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 25478c2ecf20Sopenharmony_ci err = ofdpa_port_vlan_add(ofdpa_port, vid, vlan->flags); 25488c2ecf20Sopenharmony_ci if (err) 25498c2ecf20Sopenharmony_ci return err; 25508c2ecf20Sopenharmony_ci } 25518c2ecf20Sopenharmony_ci 25528c2ecf20Sopenharmony_ci return 0; 25538c2ecf20Sopenharmony_ci} 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_cistatic int ofdpa_port_obj_vlan_del(struct rocker_port *rocker_port, 25568c2ecf20Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 25578c2ecf20Sopenharmony_ci{ 25588c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 25598c2ecf20Sopenharmony_ci u16 vid; 25608c2ecf20Sopenharmony_ci int err; 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 25638c2ecf20Sopenharmony_ci err = ofdpa_port_vlan_del(ofdpa_port, vid, vlan->flags); 25648c2ecf20Sopenharmony_ci if (err) 25658c2ecf20Sopenharmony_ci return err; 25668c2ecf20Sopenharmony_ci } 25678c2ecf20Sopenharmony_ci 25688c2ecf20Sopenharmony_ci return 0; 25698c2ecf20Sopenharmony_ci} 25708c2ecf20Sopenharmony_ci 25718c2ecf20Sopenharmony_cistatic int ofdpa_port_obj_fdb_add(struct rocker_port *rocker_port, 25728c2ecf20Sopenharmony_ci u16 vid, const unsigned char *addr) 25738c2ecf20Sopenharmony_ci{ 25748c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 25758c2ecf20Sopenharmony_ci __be16 vlan_id = ofdpa_port_vid_to_vlan(ofdpa_port, vid, NULL); 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci if (!ofdpa_port_is_bridged(ofdpa_port)) 25788c2ecf20Sopenharmony_ci return -EINVAL; 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci return ofdpa_port_fdb(ofdpa_port, addr, vlan_id, 0); 25818c2ecf20Sopenharmony_ci} 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_cistatic int ofdpa_port_obj_fdb_del(struct rocker_port *rocker_port, 25848c2ecf20Sopenharmony_ci u16 vid, const unsigned char *addr) 25858c2ecf20Sopenharmony_ci{ 25868c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 25878c2ecf20Sopenharmony_ci __be16 vlan_id = ofdpa_port_vid_to_vlan(ofdpa_port, vid, NULL); 25888c2ecf20Sopenharmony_ci int flags = OFDPA_OP_FLAG_REMOVE; 25898c2ecf20Sopenharmony_ci 25908c2ecf20Sopenharmony_ci if (!ofdpa_port_is_bridged(ofdpa_port)) 25918c2ecf20Sopenharmony_ci return -EINVAL; 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci return ofdpa_port_fdb(ofdpa_port, addr, vlan_id, flags); 25948c2ecf20Sopenharmony_ci} 25958c2ecf20Sopenharmony_ci 25968c2ecf20Sopenharmony_cistatic int ofdpa_port_bridge_join(struct ofdpa_port *ofdpa_port, 25978c2ecf20Sopenharmony_ci struct net_device *bridge) 25988c2ecf20Sopenharmony_ci{ 25998c2ecf20Sopenharmony_ci int err; 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci /* Port is joining bridge, so the internal VLAN for the 26028c2ecf20Sopenharmony_ci * port is going to change to the bridge internal VLAN. 26038c2ecf20Sopenharmony_ci * Let's remove untagged VLAN (vid=0) from port and 26048c2ecf20Sopenharmony_ci * re-add once internal VLAN has changed. 26058c2ecf20Sopenharmony_ci */ 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_ci err = ofdpa_port_vlan_del(ofdpa_port, OFDPA_UNTAGGED_VID, 0); 26088c2ecf20Sopenharmony_ci if (err) 26098c2ecf20Sopenharmony_ci return err; 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_ci ofdpa_port_internal_vlan_id_put(ofdpa_port, 26128c2ecf20Sopenharmony_ci ofdpa_port->dev->ifindex); 26138c2ecf20Sopenharmony_ci ofdpa_port->internal_vlan_id = 26148c2ecf20Sopenharmony_ci ofdpa_port_internal_vlan_id_get(ofdpa_port, bridge->ifindex); 26158c2ecf20Sopenharmony_ci 26168c2ecf20Sopenharmony_ci ofdpa_port->bridge_dev = bridge; 26178c2ecf20Sopenharmony_ci 26188c2ecf20Sopenharmony_ci return ofdpa_port_vlan_add(ofdpa_port, OFDPA_UNTAGGED_VID, 0); 26198c2ecf20Sopenharmony_ci} 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_cistatic int ofdpa_port_bridge_leave(struct ofdpa_port *ofdpa_port) 26228c2ecf20Sopenharmony_ci{ 26238c2ecf20Sopenharmony_ci int err; 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci err = ofdpa_port_vlan_del(ofdpa_port, OFDPA_UNTAGGED_VID, 0); 26268c2ecf20Sopenharmony_ci if (err) 26278c2ecf20Sopenharmony_ci return err; 26288c2ecf20Sopenharmony_ci 26298c2ecf20Sopenharmony_ci ofdpa_port_internal_vlan_id_put(ofdpa_port, 26308c2ecf20Sopenharmony_ci ofdpa_port->bridge_dev->ifindex); 26318c2ecf20Sopenharmony_ci ofdpa_port->internal_vlan_id = 26328c2ecf20Sopenharmony_ci ofdpa_port_internal_vlan_id_get(ofdpa_port, 26338c2ecf20Sopenharmony_ci ofdpa_port->dev->ifindex); 26348c2ecf20Sopenharmony_ci 26358c2ecf20Sopenharmony_ci ofdpa_port->bridge_dev = NULL; 26368c2ecf20Sopenharmony_ci 26378c2ecf20Sopenharmony_ci err = ofdpa_port_vlan_add(ofdpa_port, OFDPA_UNTAGGED_VID, 0); 26388c2ecf20Sopenharmony_ci if (err) 26398c2ecf20Sopenharmony_ci return err; 26408c2ecf20Sopenharmony_ci 26418c2ecf20Sopenharmony_ci if (ofdpa_port->dev->flags & IFF_UP) 26428c2ecf20Sopenharmony_ci err = ofdpa_port_fwd_enable(ofdpa_port, 0); 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci return err; 26458c2ecf20Sopenharmony_ci} 26468c2ecf20Sopenharmony_ci 26478c2ecf20Sopenharmony_cistatic int ofdpa_port_ovs_changed(struct ofdpa_port *ofdpa_port, 26488c2ecf20Sopenharmony_ci struct net_device *master) 26498c2ecf20Sopenharmony_ci{ 26508c2ecf20Sopenharmony_ci int err; 26518c2ecf20Sopenharmony_ci 26528c2ecf20Sopenharmony_ci ofdpa_port->bridge_dev = master; 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci err = ofdpa_port_fwd_disable(ofdpa_port, 0); 26558c2ecf20Sopenharmony_ci if (err) 26568c2ecf20Sopenharmony_ci return err; 26578c2ecf20Sopenharmony_ci err = ofdpa_port_fwd_enable(ofdpa_port, 0); 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_ci return err; 26608c2ecf20Sopenharmony_ci} 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_cistatic int ofdpa_port_master_linked(struct rocker_port *rocker_port, 26638c2ecf20Sopenharmony_ci struct net_device *master) 26648c2ecf20Sopenharmony_ci{ 26658c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 26668c2ecf20Sopenharmony_ci int err = 0; 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_ci if (netif_is_bridge_master(master)) 26698c2ecf20Sopenharmony_ci err = ofdpa_port_bridge_join(ofdpa_port, master); 26708c2ecf20Sopenharmony_ci else if (netif_is_ovs_master(master)) 26718c2ecf20Sopenharmony_ci err = ofdpa_port_ovs_changed(ofdpa_port, master); 26728c2ecf20Sopenharmony_ci return err; 26738c2ecf20Sopenharmony_ci} 26748c2ecf20Sopenharmony_ci 26758c2ecf20Sopenharmony_cistatic int ofdpa_port_master_unlinked(struct rocker_port *rocker_port, 26768c2ecf20Sopenharmony_ci struct net_device *master) 26778c2ecf20Sopenharmony_ci{ 26788c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 26798c2ecf20Sopenharmony_ci int err = 0; 26808c2ecf20Sopenharmony_ci 26818c2ecf20Sopenharmony_ci if (ofdpa_port_is_bridged(ofdpa_port)) 26828c2ecf20Sopenharmony_ci err = ofdpa_port_bridge_leave(ofdpa_port); 26838c2ecf20Sopenharmony_ci else if (ofdpa_port_is_ovsed(ofdpa_port)) 26848c2ecf20Sopenharmony_ci err = ofdpa_port_ovs_changed(ofdpa_port, NULL); 26858c2ecf20Sopenharmony_ci return err; 26868c2ecf20Sopenharmony_ci} 26878c2ecf20Sopenharmony_ci 26888c2ecf20Sopenharmony_cistatic int ofdpa_port_neigh_update(struct rocker_port *rocker_port, 26898c2ecf20Sopenharmony_ci struct neighbour *n) 26908c2ecf20Sopenharmony_ci{ 26918c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 26928c2ecf20Sopenharmony_ci int flags = (n->nud_state & NUD_VALID ? 0 : OFDPA_OP_FLAG_REMOVE) | 26938c2ecf20Sopenharmony_ci OFDPA_OP_FLAG_NOWAIT; 26948c2ecf20Sopenharmony_ci __be32 ip_addr = *(__be32 *) n->primary_key; 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_ci return ofdpa_port_ipv4_neigh(ofdpa_port, flags, ip_addr, n->ha); 26978c2ecf20Sopenharmony_ci} 26988c2ecf20Sopenharmony_ci 26998c2ecf20Sopenharmony_cistatic int ofdpa_port_neigh_destroy(struct rocker_port *rocker_port, 27008c2ecf20Sopenharmony_ci struct neighbour *n) 27018c2ecf20Sopenharmony_ci{ 27028c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 27038c2ecf20Sopenharmony_ci int flags = OFDPA_OP_FLAG_REMOVE | OFDPA_OP_FLAG_NOWAIT; 27048c2ecf20Sopenharmony_ci __be32 ip_addr = *(__be32 *) n->primary_key; 27058c2ecf20Sopenharmony_ci 27068c2ecf20Sopenharmony_ci return ofdpa_port_ipv4_neigh(ofdpa_port, flags, ip_addr, n->ha); 27078c2ecf20Sopenharmony_ci} 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_cistatic int ofdpa_port_ev_mac_vlan_seen(struct rocker_port *rocker_port, 27108c2ecf20Sopenharmony_ci const unsigned char *addr, 27118c2ecf20Sopenharmony_ci __be16 vlan_id) 27128c2ecf20Sopenharmony_ci{ 27138c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port = rocker_port->wpriv; 27148c2ecf20Sopenharmony_ci int flags = OFDPA_OP_FLAG_NOWAIT | OFDPA_OP_FLAG_LEARNED; 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci if (ofdpa_port->stp_state != BR_STATE_LEARNING && 27178c2ecf20Sopenharmony_ci ofdpa_port->stp_state != BR_STATE_FORWARDING) 27188c2ecf20Sopenharmony_ci return 0; 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci return ofdpa_port_fdb(ofdpa_port, addr, vlan_id, flags); 27218c2ecf20Sopenharmony_ci} 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_cistatic struct ofdpa_port *ofdpa_port_dev_lower_find(struct net_device *dev, 27248c2ecf20Sopenharmony_ci struct rocker *rocker) 27258c2ecf20Sopenharmony_ci{ 27268c2ecf20Sopenharmony_ci struct rocker_port *rocker_port; 27278c2ecf20Sopenharmony_ci 27288c2ecf20Sopenharmony_ci rocker_port = rocker_port_dev_lower_find(dev, rocker); 27298c2ecf20Sopenharmony_ci return rocker_port ? rocker_port->wpriv : NULL; 27308c2ecf20Sopenharmony_ci} 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_cistatic int ofdpa_fib4_add(struct rocker *rocker, 27338c2ecf20Sopenharmony_ci const struct fib_entry_notifier_info *fen_info) 27348c2ecf20Sopenharmony_ci{ 27358c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = rocker->wpriv; 27368c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port; 27378c2ecf20Sopenharmony_ci struct fib_nh *nh; 27388c2ecf20Sopenharmony_ci int err; 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci if (ofdpa->fib_aborted) 27418c2ecf20Sopenharmony_ci return 0; 27428c2ecf20Sopenharmony_ci nh = fib_info_nh(fen_info->fi, 0); 27438c2ecf20Sopenharmony_ci ofdpa_port = ofdpa_port_dev_lower_find(nh->fib_nh_dev, rocker); 27448c2ecf20Sopenharmony_ci if (!ofdpa_port) 27458c2ecf20Sopenharmony_ci return 0; 27468c2ecf20Sopenharmony_ci err = ofdpa_port_fib_ipv4(ofdpa_port, htonl(fen_info->dst), 27478c2ecf20Sopenharmony_ci fen_info->dst_len, fen_info->fi, 27488c2ecf20Sopenharmony_ci fen_info->tb_id, 0); 27498c2ecf20Sopenharmony_ci if (err) 27508c2ecf20Sopenharmony_ci return err; 27518c2ecf20Sopenharmony_ci nh->fib_nh_flags |= RTNH_F_OFFLOAD; 27528c2ecf20Sopenharmony_ci return 0; 27538c2ecf20Sopenharmony_ci} 27548c2ecf20Sopenharmony_ci 27558c2ecf20Sopenharmony_cistatic int ofdpa_fib4_del(struct rocker *rocker, 27568c2ecf20Sopenharmony_ci const struct fib_entry_notifier_info *fen_info) 27578c2ecf20Sopenharmony_ci{ 27588c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = rocker->wpriv; 27598c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port; 27608c2ecf20Sopenharmony_ci struct fib_nh *nh; 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_ci if (ofdpa->fib_aborted) 27638c2ecf20Sopenharmony_ci return 0; 27648c2ecf20Sopenharmony_ci nh = fib_info_nh(fen_info->fi, 0); 27658c2ecf20Sopenharmony_ci ofdpa_port = ofdpa_port_dev_lower_find(nh->fib_nh_dev, rocker); 27668c2ecf20Sopenharmony_ci if (!ofdpa_port) 27678c2ecf20Sopenharmony_ci return 0; 27688c2ecf20Sopenharmony_ci nh->fib_nh_flags &= ~RTNH_F_OFFLOAD; 27698c2ecf20Sopenharmony_ci return ofdpa_port_fib_ipv4(ofdpa_port, htonl(fen_info->dst), 27708c2ecf20Sopenharmony_ci fen_info->dst_len, fen_info->fi, 27718c2ecf20Sopenharmony_ci fen_info->tb_id, OFDPA_OP_FLAG_REMOVE); 27728c2ecf20Sopenharmony_ci} 27738c2ecf20Sopenharmony_ci 27748c2ecf20Sopenharmony_cistatic void ofdpa_fib4_abort(struct rocker *rocker) 27758c2ecf20Sopenharmony_ci{ 27768c2ecf20Sopenharmony_ci struct ofdpa *ofdpa = rocker->wpriv; 27778c2ecf20Sopenharmony_ci struct ofdpa_port *ofdpa_port; 27788c2ecf20Sopenharmony_ci struct ofdpa_flow_tbl_entry *flow_entry; 27798c2ecf20Sopenharmony_ci struct hlist_node *tmp; 27808c2ecf20Sopenharmony_ci unsigned long flags; 27818c2ecf20Sopenharmony_ci int bkt; 27828c2ecf20Sopenharmony_ci 27838c2ecf20Sopenharmony_ci if (ofdpa->fib_aborted) 27848c2ecf20Sopenharmony_ci return; 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci spin_lock_irqsave(&ofdpa->flow_tbl_lock, flags); 27878c2ecf20Sopenharmony_ci hash_for_each_safe(ofdpa->flow_tbl, bkt, tmp, flow_entry, entry) { 27888c2ecf20Sopenharmony_ci struct fib_nh *nh; 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci if (flow_entry->key.tbl_id != 27918c2ecf20Sopenharmony_ci ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING) 27928c2ecf20Sopenharmony_ci continue; 27938c2ecf20Sopenharmony_ci nh = fib_info_nh(flow_entry->fi, 0); 27948c2ecf20Sopenharmony_ci ofdpa_port = ofdpa_port_dev_lower_find(nh->fib_nh_dev, rocker); 27958c2ecf20Sopenharmony_ci if (!ofdpa_port) 27968c2ecf20Sopenharmony_ci continue; 27978c2ecf20Sopenharmony_ci nh->fib_nh_flags &= ~RTNH_F_OFFLOAD; 27988c2ecf20Sopenharmony_ci ofdpa_flow_tbl_del(ofdpa_port, 27998c2ecf20Sopenharmony_ci OFDPA_OP_FLAG_REMOVE | OFDPA_OP_FLAG_NOWAIT, 28008c2ecf20Sopenharmony_ci flow_entry); 28018c2ecf20Sopenharmony_ci } 28028c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ofdpa->flow_tbl_lock, flags); 28038c2ecf20Sopenharmony_ci ofdpa->fib_aborted = true; 28048c2ecf20Sopenharmony_ci} 28058c2ecf20Sopenharmony_ci 28068c2ecf20Sopenharmony_cistruct rocker_world_ops rocker_ofdpa_ops = { 28078c2ecf20Sopenharmony_ci .kind = "ofdpa", 28088c2ecf20Sopenharmony_ci .priv_size = sizeof(struct ofdpa), 28098c2ecf20Sopenharmony_ci .port_priv_size = sizeof(struct ofdpa_port), 28108c2ecf20Sopenharmony_ci .mode = ROCKER_PORT_MODE_OF_DPA, 28118c2ecf20Sopenharmony_ci .init = ofdpa_init, 28128c2ecf20Sopenharmony_ci .fini = ofdpa_fini, 28138c2ecf20Sopenharmony_ci .port_pre_init = ofdpa_port_pre_init, 28148c2ecf20Sopenharmony_ci .port_init = ofdpa_port_init, 28158c2ecf20Sopenharmony_ci .port_fini = ofdpa_port_fini, 28168c2ecf20Sopenharmony_ci .port_open = ofdpa_port_open, 28178c2ecf20Sopenharmony_ci .port_stop = ofdpa_port_stop, 28188c2ecf20Sopenharmony_ci .port_attr_stp_state_set = ofdpa_port_attr_stp_state_set, 28198c2ecf20Sopenharmony_ci .port_attr_bridge_flags_set = ofdpa_port_attr_bridge_flags_set, 28208c2ecf20Sopenharmony_ci .port_attr_bridge_flags_support_get = ofdpa_port_attr_bridge_flags_support_get, 28218c2ecf20Sopenharmony_ci .port_attr_bridge_ageing_time_set = ofdpa_port_attr_bridge_ageing_time_set, 28228c2ecf20Sopenharmony_ci .port_obj_vlan_add = ofdpa_port_obj_vlan_add, 28238c2ecf20Sopenharmony_ci .port_obj_vlan_del = ofdpa_port_obj_vlan_del, 28248c2ecf20Sopenharmony_ci .port_obj_fdb_add = ofdpa_port_obj_fdb_add, 28258c2ecf20Sopenharmony_ci .port_obj_fdb_del = ofdpa_port_obj_fdb_del, 28268c2ecf20Sopenharmony_ci .port_master_linked = ofdpa_port_master_linked, 28278c2ecf20Sopenharmony_ci .port_master_unlinked = ofdpa_port_master_unlinked, 28288c2ecf20Sopenharmony_ci .port_neigh_update = ofdpa_port_neigh_update, 28298c2ecf20Sopenharmony_ci .port_neigh_destroy = ofdpa_port_neigh_destroy, 28308c2ecf20Sopenharmony_ci .port_ev_mac_vlan_seen = ofdpa_port_ev_mac_vlan_seen, 28318c2ecf20Sopenharmony_ci .fib4_add = ofdpa_fib4_add, 28328c2ecf20Sopenharmony_ci .fib4_del = ofdpa_fib4_del, 28338c2ecf20Sopenharmony_ci .fib4_abort = ofdpa_fib4_abort, 28348c2ecf20Sopenharmony_ci}; 2835