111ce2ba3SJiri Pirko /* 211ce2ba3SJiri Pirko * drivers/net/ethernet/rocker/rocker.c - Rocker switch device driver 311ce2ba3SJiri Pirko * Copyright (c) 2014-2016 Jiri Pirko <jiri@mellanox.com> 411ce2ba3SJiri Pirko * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com> 511ce2ba3SJiri Pirko * 611ce2ba3SJiri Pirko * This program is free software; you can redistribute it and/or modify 711ce2ba3SJiri Pirko * it under the terms of the GNU General Public License as published by 811ce2ba3SJiri Pirko * the Free Software Foundation; either version 2 of the License, or 911ce2ba3SJiri Pirko * (at your option) any later version. 1011ce2ba3SJiri Pirko */ 1111ce2ba3SJiri Pirko 1211ce2ba3SJiri Pirko #include <linux/kernel.h> 1311ce2ba3SJiri Pirko #include <linux/module.h> 1411ce2ba3SJiri Pirko #include <linux/pci.h> 1511ce2ba3SJiri Pirko #include <linux/interrupt.h> 1611ce2ba3SJiri Pirko #include <linux/sched.h> 1711ce2ba3SJiri Pirko #include <linux/wait.h> 1811ce2ba3SJiri Pirko #include <linux/spinlock.h> 1911ce2ba3SJiri Pirko #include <linux/hashtable.h> 2011ce2ba3SJiri Pirko #include <linux/crc32.h> 2111ce2ba3SJiri Pirko #include <linux/sort.h> 2211ce2ba3SJiri Pirko #include <linux/random.h> 2311ce2ba3SJiri Pirko #include <linux/netdevice.h> 2411ce2ba3SJiri Pirko #include <linux/inetdevice.h> 2511ce2ba3SJiri Pirko #include <linux/skbuff.h> 2611ce2ba3SJiri Pirko #include <linux/socket.h> 2711ce2ba3SJiri Pirko #include <linux/etherdevice.h> 2811ce2ba3SJiri Pirko #include <linux/ethtool.h> 2911ce2ba3SJiri Pirko #include <linux/if_ether.h> 3011ce2ba3SJiri Pirko #include <linux/if_vlan.h> 3111ce2ba3SJiri Pirko #include <linux/if_bridge.h> 3211ce2ba3SJiri Pirko #include <linux/bitops.h> 3311ce2ba3SJiri Pirko #include <linux/ctype.h> 3411ce2ba3SJiri Pirko #include <net/switchdev.h> 3511ce2ba3SJiri Pirko #include <net/rtnetlink.h> 3611ce2ba3SJiri Pirko #include <net/ip_fib.h> 3711ce2ba3SJiri Pirko #include <net/netevent.h> 3811ce2ba3SJiri Pirko #include <net/arp.h> 3911ce2ba3SJiri Pirko #include <linux/io-64-nonatomic-lo-hi.h> 4011ce2ba3SJiri Pirko #include <generated/utsrelease.h> 4111ce2ba3SJiri Pirko 4211ce2ba3SJiri Pirko #include "rocker_hw.h" 43de152192SJiri Pirko #include "rocker.h" 44de152192SJiri Pirko #include "rocker_tlv.h" 4511ce2ba3SJiri Pirko 4611ce2ba3SJiri Pirko static const char rocker_driver_name[] = "rocker"; 4711ce2ba3SJiri Pirko 4811ce2ba3SJiri Pirko static const struct pci_device_id rocker_pci_id_table[] = { 4911ce2ba3SJiri Pirko {PCI_VDEVICE(REDHAT, PCI_DEVICE_ID_REDHAT_ROCKER), 0}, 5011ce2ba3SJiri Pirko {0, } 5111ce2ba3SJiri Pirko }; 5211ce2ba3SJiri Pirko 5311ce2ba3SJiri Pirko struct rocker_flow_tbl_key { 5411ce2ba3SJiri Pirko u32 priority; 5511ce2ba3SJiri Pirko enum rocker_of_dpa_table_id tbl_id; 5611ce2ba3SJiri Pirko union { 5711ce2ba3SJiri Pirko struct { 5811ce2ba3SJiri Pirko u32 in_pport; 5911ce2ba3SJiri Pirko u32 in_pport_mask; 6011ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl; 6111ce2ba3SJiri Pirko } ig_port; 6211ce2ba3SJiri Pirko struct { 6311ce2ba3SJiri Pirko u32 in_pport; 6411ce2ba3SJiri Pirko __be16 vlan_id; 6511ce2ba3SJiri Pirko __be16 vlan_id_mask; 6611ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl; 6711ce2ba3SJiri Pirko bool untagged; 6811ce2ba3SJiri Pirko __be16 new_vlan_id; 6911ce2ba3SJiri Pirko } vlan; 7011ce2ba3SJiri Pirko struct { 7111ce2ba3SJiri Pirko u32 in_pport; 7211ce2ba3SJiri Pirko u32 in_pport_mask; 7311ce2ba3SJiri Pirko __be16 eth_type; 7411ce2ba3SJiri Pirko u8 eth_dst[ETH_ALEN]; 7511ce2ba3SJiri Pirko u8 eth_dst_mask[ETH_ALEN]; 7611ce2ba3SJiri Pirko __be16 vlan_id; 7711ce2ba3SJiri Pirko __be16 vlan_id_mask; 7811ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl; 7911ce2ba3SJiri Pirko bool copy_to_cpu; 8011ce2ba3SJiri Pirko } term_mac; 8111ce2ba3SJiri Pirko struct { 8211ce2ba3SJiri Pirko __be16 eth_type; 8311ce2ba3SJiri Pirko __be32 dst4; 8411ce2ba3SJiri Pirko __be32 dst4_mask; 8511ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl; 8611ce2ba3SJiri Pirko u32 group_id; 8711ce2ba3SJiri Pirko } ucast_routing; 8811ce2ba3SJiri Pirko struct { 8911ce2ba3SJiri Pirko u8 eth_dst[ETH_ALEN]; 9011ce2ba3SJiri Pirko u8 eth_dst_mask[ETH_ALEN]; 9111ce2ba3SJiri Pirko int has_eth_dst; 9211ce2ba3SJiri Pirko int has_eth_dst_mask; 9311ce2ba3SJiri Pirko __be16 vlan_id; 9411ce2ba3SJiri Pirko u32 tunnel_id; 9511ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl; 9611ce2ba3SJiri Pirko u32 group_id; 9711ce2ba3SJiri Pirko bool copy_to_cpu; 9811ce2ba3SJiri Pirko } bridge; 9911ce2ba3SJiri Pirko struct { 10011ce2ba3SJiri Pirko u32 in_pport; 10111ce2ba3SJiri Pirko u32 in_pport_mask; 10211ce2ba3SJiri Pirko u8 eth_src[ETH_ALEN]; 10311ce2ba3SJiri Pirko u8 eth_src_mask[ETH_ALEN]; 10411ce2ba3SJiri Pirko u8 eth_dst[ETH_ALEN]; 10511ce2ba3SJiri Pirko u8 eth_dst_mask[ETH_ALEN]; 10611ce2ba3SJiri Pirko __be16 eth_type; 10711ce2ba3SJiri Pirko __be16 vlan_id; 10811ce2ba3SJiri Pirko __be16 vlan_id_mask; 10911ce2ba3SJiri Pirko u8 ip_proto; 11011ce2ba3SJiri Pirko u8 ip_proto_mask; 11111ce2ba3SJiri Pirko u8 ip_tos; 11211ce2ba3SJiri Pirko u8 ip_tos_mask; 11311ce2ba3SJiri Pirko u32 group_id; 11411ce2ba3SJiri Pirko } acl; 11511ce2ba3SJiri Pirko }; 11611ce2ba3SJiri Pirko }; 11711ce2ba3SJiri Pirko 11811ce2ba3SJiri Pirko struct rocker_flow_tbl_entry { 11911ce2ba3SJiri Pirko struct hlist_node entry; 12011ce2ba3SJiri Pirko u32 cmd; 12111ce2ba3SJiri Pirko u64 cookie; 12211ce2ba3SJiri Pirko struct rocker_flow_tbl_key key; 12311ce2ba3SJiri Pirko size_t key_len; 12411ce2ba3SJiri Pirko u32 key_crc32; /* key */ 12511ce2ba3SJiri Pirko }; 12611ce2ba3SJiri Pirko 12711ce2ba3SJiri Pirko struct rocker_group_tbl_entry { 12811ce2ba3SJiri Pirko struct hlist_node entry; 12911ce2ba3SJiri Pirko u32 cmd; 13011ce2ba3SJiri Pirko u32 group_id; /* key */ 13111ce2ba3SJiri Pirko u16 group_count; 13211ce2ba3SJiri Pirko u32 *group_ids; 13311ce2ba3SJiri Pirko union { 13411ce2ba3SJiri Pirko struct { 13511ce2ba3SJiri Pirko u8 pop_vlan; 13611ce2ba3SJiri Pirko } l2_interface; 13711ce2ba3SJiri Pirko struct { 13811ce2ba3SJiri Pirko u8 eth_src[ETH_ALEN]; 13911ce2ba3SJiri Pirko u8 eth_dst[ETH_ALEN]; 14011ce2ba3SJiri Pirko __be16 vlan_id; 14111ce2ba3SJiri Pirko u32 group_id; 14211ce2ba3SJiri Pirko } l2_rewrite; 14311ce2ba3SJiri Pirko struct { 14411ce2ba3SJiri Pirko u8 eth_src[ETH_ALEN]; 14511ce2ba3SJiri Pirko u8 eth_dst[ETH_ALEN]; 14611ce2ba3SJiri Pirko __be16 vlan_id; 14711ce2ba3SJiri Pirko bool ttl_check; 14811ce2ba3SJiri Pirko u32 group_id; 14911ce2ba3SJiri Pirko } l3_unicast; 15011ce2ba3SJiri Pirko }; 15111ce2ba3SJiri Pirko }; 15211ce2ba3SJiri Pirko 15311ce2ba3SJiri Pirko struct rocker_fdb_tbl_entry { 15411ce2ba3SJiri Pirko struct hlist_node entry; 15511ce2ba3SJiri Pirko u32 key_crc32; /* key */ 15611ce2ba3SJiri Pirko bool learned; 15711ce2ba3SJiri Pirko unsigned long touched; 15811ce2ba3SJiri Pirko struct rocker_fdb_tbl_key { 15911ce2ba3SJiri Pirko struct rocker_port *rocker_port; 16011ce2ba3SJiri Pirko u8 addr[ETH_ALEN]; 16111ce2ba3SJiri Pirko __be16 vlan_id; 16211ce2ba3SJiri Pirko } key; 16311ce2ba3SJiri Pirko }; 16411ce2ba3SJiri Pirko 16511ce2ba3SJiri Pirko struct rocker_internal_vlan_tbl_entry { 16611ce2ba3SJiri Pirko struct hlist_node entry; 16711ce2ba3SJiri Pirko int ifindex; /* key */ 16811ce2ba3SJiri Pirko u32 ref_count; 16911ce2ba3SJiri Pirko __be16 vlan_id; 17011ce2ba3SJiri Pirko }; 17111ce2ba3SJiri Pirko 17211ce2ba3SJiri Pirko struct rocker_neigh_tbl_entry { 17311ce2ba3SJiri Pirko struct hlist_node entry; 17411ce2ba3SJiri Pirko __be32 ip_addr; /* key */ 17511ce2ba3SJiri Pirko struct net_device *dev; 17611ce2ba3SJiri Pirko u32 ref_count; 17711ce2ba3SJiri Pirko u32 index; 17811ce2ba3SJiri Pirko u8 eth_dst[ETH_ALEN]; 17911ce2ba3SJiri Pirko bool ttl_check; 18011ce2ba3SJiri Pirko }; 18111ce2ba3SJiri Pirko 18211ce2ba3SJiri Pirko static const u8 zero_mac[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 18311ce2ba3SJiri Pirko static const u8 ff_mac[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 18411ce2ba3SJiri Pirko static const u8 ll_mac[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }; 18511ce2ba3SJiri Pirko static const u8 ll_mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0 }; 18611ce2ba3SJiri Pirko static const u8 mcast_mac[ETH_ALEN] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; 18711ce2ba3SJiri Pirko static const u8 ipv4_mcast[ETH_ALEN] = { 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 }; 18811ce2ba3SJiri Pirko static const u8 ipv4_mask[ETH_ALEN] = { 0xff, 0xff, 0xff, 0x80, 0x00, 0x00 }; 18911ce2ba3SJiri Pirko static const u8 ipv6_mcast[ETH_ALEN] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 }; 19011ce2ba3SJiri Pirko static const u8 ipv6_mask[ETH_ALEN] = { 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 }; 19111ce2ba3SJiri Pirko 19211ce2ba3SJiri Pirko /* Rocker priority levels for flow table entries. Higher 19311ce2ba3SJiri Pirko * priority match takes precedence over lower priority match. 19411ce2ba3SJiri Pirko */ 19511ce2ba3SJiri Pirko 19611ce2ba3SJiri Pirko enum { 19711ce2ba3SJiri Pirko ROCKER_PRIORITY_UNKNOWN = 0, 19811ce2ba3SJiri Pirko ROCKER_PRIORITY_IG_PORT = 1, 19911ce2ba3SJiri Pirko ROCKER_PRIORITY_VLAN = 1, 20011ce2ba3SJiri Pirko ROCKER_PRIORITY_TERM_MAC_UCAST = 0, 20111ce2ba3SJiri Pirko ROCKER_PRIORITY_TERM_MAC_MCAST = 1, 20211ce2ba3SJiri Pirko ROCKER_PRIORITY_BRIDGING_VLAN_DFLT_EXACT = 1, 20311ce2ba3SJiri Pirko ROCKER_PRIORITY_BRIDGING_VLAN_DFLT_WILD = 2, 20411ce2ba3SJiri Pirko ROCKER_PRIORITY_BRIDGING_VLAN = 3, 20511ce2ba3SJiri Pirko ROCKER_PRIORITY_BRIDGING_TENANT_DFLT_EXACT = 1, 20611ce2ba3SJiri Pirko ROCKER_PRIORITY_BRIDGING_TENANT_DFLT_WILD = 2, 20711ce2ba3SJiri Pirko ROCKER_PRIORITY_BRIDGING_TENANT = 3, 20811ce2ba3SJiri Pirko ROCKER_PRIORITY_ACL_CTRL = 3, 20911ce2ba3SJiri Pirko ROCKER_PRIORITY_ACL_NORMAL = 2, 21011ce2ba3SJiri Pirko ROCKER_PRIORITY_ACL_DFLT = 1, 21111ce2ba3SJiri Pirko }; 21211ce2ba3SJiri Pirko 21311ce2ba3SJiri Pirko static bool rocker_vlan_id_is_internal(__be16 vlan_id) 21411ce2ba3SJiri Pirko { 21511ce2ba3SJiri Pirko u16 start = ROCKER_INTERNAL_VLAN_ID_BASE; 21611ce2ba3SJiri Pirko u16 end = 0xffe; 21711ce2ba3SJiri Pirko u16 _vlan_id = ntohs(vlan_id); 21811ce2ba3SJiri Pirko 21911ce2ba3SJiri Pirko return (_vlan_id >= start && _vlan_id <= end); 22011ce2ba3SJiri Pirko } 22111ce2ba3SJiri Pirko 22211ce2ba3SJiri Pirko static __be16 rocker_port_vid_to_vlan(const struct rocker_port *rocker_port, 22311ce2ba3SJiri Pirko u16 vid, bool *pop_vlan) 22411ce2ba3SJiri Pirko { 22511ce2ba3SJiri Pirko __be16 vlan_id; 22611ce2ba3SJiri Pirko 22711ce2ba3SJiri Pirko if (pop_vlan) 22811ce2ba3SJiri Pirko *pop_vlan = false; 22911ce2ba3SJiri Pirko vlan_id = htons(vid); 23011ce2ba3SJiri Pirko if (!vlan_id) { 23111ce2ba3SJiri Pirko vlan_id = rocker_port->internal_vlan_id; 23211ce2ba3SJiri Pirko if (pop_vlan) 23311ce2ba3SJiri Pirko *pop_vlan = true; 23411ce2ba3SJiri Pirko } 23511ce2ba3SJiri Pirko 23611ce2ba3SJiri Pirko return vlan_id; 23711ce2ba3SJiri Pirko } 23811ce2ba3SJiri Pirko 23911ce2ba3SJiri Pirko static u16 rocker_port_vlan_to_vid(const struct rocker_port *rocker_port, 24011ce2ba3SJiri Pirko __be16 vlan_id) 24111ce2ba3SJiri Pirko { 24211ce2ba3SJiri Pirko if (rocker_vlan_id_is_internal(vlan_id)) 24311ce2ba3SJiri Pirko return 0; 24411ce2ba3SJiri Pirko 24511ce2ba3SJiri Pirko return ntohs(vlan_id); 24611ce2ba3SJiri Pirko } 24711ce2ba3SJiri Pirko 24811ce2ba3SJiri Pirko static bool rocker_port_is_bridged(const struct rocker_port *rocker_port) 24911ce2ba3SJiri Pirko { 25011ce2ba3SJiri Pirko return rocker_port->bridge_dev && 25111ce2ba3SJiri Pirko netif_is_bridge_master(rocker_port->bridge_dev); 25211ce2ba3SJiri Pirko } 25311ce2ba3SJiri Pirko 25411ce2ba3SJiri Pirko static bool rocker_port_is_ovsed(const struct rocker_port *rocker_port) 25511ce2ba3SJiri Pirko { 25611ce2ba3SJiri Pirko return rocker_port->bridge_dev && 25711ce2ba3SJiri Pirko netif_is_ovs_master(rocker_port->bridge_dev); 25811ce2ba3SJiri Pirko } 25911ce2ba3SJiri Pirko 26011ce2ba3SJiri Pirko #define ROCKER_OP_FLAG_REMOVE BIT(0) 26111ce2ba3SJiri Pirko #define ROCKER_OP_FLAG_NOWAIT BIT(1) 26211ce2ba3SJiri Pirko #define ROCKER_OP_FLAG_LEARNED BIT(2) 26311ce2ba3SJiri Pirko #define ROCKER_OP_FLAG_REFRESH BIT(3) 26411ce2ba3SJiri Pirko 26511ce2ba3SJiri Pirko static void *__rocker_mem_alloc(struct switchdev_trans *trans, int flags, 26611ce2ba3SJiri Pirko size_t size) 26711ce2ba3SJiri Pirko { 26811ce2ba3SJiri Pirko struct switchdev_trans_item *elem = NULL; 26911ce2ba3SJiri Pirko gfp_t gfp_flags = (flags & ROCKER_OP_FLAG_NOWAIT) ? 27011ce2ba3SJiri Pirko GFP_ATOMIC : GFP_KERNEL; 27111ce2ba3SJiri Pirko 27211ce2ba3SJiri Pirko /* If in transaction prepare phase, allocate the memory 27311ce2ba3SJiri Pirko * and enqueue it on a transaction. If in transaction 27411ce2ba3SJiri Pirko * commit phase, dequeue the memory from the transaction 27511ce2ba3SJiri Pirko * rather than re-allocating the memory. The idea is the 27611ce2ba3SJiri Pirko * driver code paths for prepare and commit are identical 27711ce2ba3SJiri Pirko * so the memory allocated in the prepare phase is the 27811ce2ba3SJiri Pirko * memory used in the commit phase. 27911ce2ba3SJiri Pirko */ 28011ce2ba3SJiri Pirko 28111ce2ba3SJiri Pirko if (!trans) { 28211ce2ba3SJiri Pirko elem = kzalloc(size + sizeof(*elem), gfp_flags); 28311ce2ba3SJiri Pirko } else if (switchdev_trans_ph_prepare(trans)) { 28411ce2ba3SJiri Pirko elem = kzalloc(size + sizeof(*elem), gfp_flags); 28511ce2ba3SJiri Pirko if (!elem) 28611ce2ba3SJiri Pirko return NULL; 28711ce2ba3SJiri Pirko switchdev_trans_item_enqueue(trans, elem, kfree, elem); 28811ce2ba3SJiri Pirko } else { 28911ce2ba3SJiri Pirko elem = switchdev_trans_item_dequeue(trans); 29011ce2ba3SJiri Pirko } 29111ce2ba3SJiri Pirko 29211ce2ba3SJiri Pirko return elem ? elem + 1 : NULL; 29311ce2ba3SJiri Pirko } 29411ce2ba3SJiri Pirko 29511ce2ba3SJiri Pirko static void *rocker_kzalloc(struct switchdev_trans *trans, int flags, 29611ce2ba3SJiri Pirko size_t size) 29711ce2ba3SJiri Pirko { 29811ce2ba3SJiri Pirko return __rocker_mem_alloc(trans, flags, size); 29911ce2ba3SJiri Pirko } 30011ce2ba3SJiri Pirko 30111ce2ba3SJiri Pirko static void *rocker_kcalloc(struct switchdev_trans *trans, int flags, 30211ce2ba3SJiri Pirko size_t n, size_t size) 30311ce2ba3SJiri Pirko { 30411ce2ba3SJiri Pirko return __rocker_mem_alloc(trans, flags, n * size); 30511ce2ba3SJiri Pirko } 30611ce2ba3SJiri Pirko 30711ce2ba3SJiri Pirko static void rocker_kfree(struct switchdev_trans *trans, const void *mem) 30811ce2ba3SJiri Pirko { 30911ce2ba3SJiri Pirko struct switchdev_trans_item *elem; 31011ce2ba3SJiri Pirko 31111ce2ba3SJiri Pirko /* Frees are ignored if in transaction prepare phase. The 31211ce2ba3SJiri Pirko * memory remains on the per-port list until freed in the 31311ce2ba3SJiri Pirko * commit phase. 31411ce2ba3SJiri Pirko */ 31511ce2ba3SJiri Pirko 31611ce2ba3SJiri Pirko if (switchdev_trans_ph_prepare(trans)) 31711ce2ba3SJiri Pirko return; 31811ce2ba3SJiri Pirko 31911ce2ba3SJiri Pirko elem = (struct switchdev_trans_item *) mem - 1; 32011ce2ba3SJiri Pirko kfree(elem); 32111ce2ba3SJiri Pirko } 32211ce2ba3SJiri Pirko 32311ce2ba3SJiri Pirko struct rocker_wait { 32411ce2ba3SJiri Pirko wait_queue_head_t wait; 32511ce2ba3SJiri Pirko bool done; 32611ce2ba3SJiri Pirko bool nowait; 32711ce2ba3SJiri Pirko }; 32811ce2ba3SJiri Pirko 32911ce2ba3SJiri Pirko static void rocker_wait_reset(struct rocker_wait *wait) 33011ce2ba3SJiri Pirko { 33111ce2ba3SJiri Pirko wait->done = false; 33211ce2ba3SJiri Pirko wait->nowait = false; 33311ce2ba3SJiri Pirko } 33411ce2ba3SJiri Pirko 33511ce2ba3SJiri Pirko static void rocker_wait_init(struct rocker_wait *wait) 33611ce2ba3SJiri Pirko { 33711ce2ba3SJiri Pirko init_waitqueue_head(&wait->wait); 33811ce2ba3SJiri Pirko rocker_wait_reset(wait); 33911ce2ba3SJiri Pirko } 34011ce2ba3SJiri Pirko 34111ce2ba3SJiri Pirko static struct rocker_wait *rocker_wait_create(struct rocker_port *rocker_port, 34211ce2ba3SJiri Pirko struct switchdev_trans *trans, 34311ce2ba3SJiri Pirko int flags) 34411ce2ba3SJiri Pirko { 34511ce2ba3SJiri Pirko struct rocker_wait *wait; 34611ce2ba3SJiri Pirko 34711ce2ba3SJiri Pirko wait = rocker_kzalloc(trans, flags, sizeof(*wait)); 34811ce2ba3SJiri Pirko if (!wait) 34911ce2ba3SJiri Pirko return NULL; 35011ce2ba3SJiri Pirko rocker_wait_init(wait); 35111ce2ba3SJiri Pirko return wait; 35211ce2ba3SJiri Pirko } 35311ce2ba3SJiri Pirko 35411ce2ba3SJiri Pirko static void rocker_wait_destroy(struct switchdev_trans *trans, 35511ce2ba3SJiri Pirko struct rocker_wait *wait) 35611ce2ba3SJiri Pirko { 35711ce2ba3SJiri Pirko rocker_kfree(trans, wait); 35811ce2ba3SJiri Pirko } 35911ce2ba3SJiri Pirko 36011ce2ba3SJiri Pirko static bool rocker_wait_event_timeout(struct rocker_wait *wait, 36111ce2ba3SJiri Pirko unsigned long timeout) 36211ce2ba3SJiri Pirko { 36311ce2ba3SJiri Pirko wait_event_timeout(wait->wait, wait->done, HZ / 10); 36411ce2ba3SJiri Pirko if (!wait->done) 36511ce2ba3SJiri Pirko return false; 36611ce2ba3SJiri Pirko return true; 36711ce2ba3SJiri Pirko } 36811ce2ba3SJiri Pirko 36911ce2ba3SJiri Pirko static void rocker_wait_wake_up(struct rocker_wait *wait) 37011ce2ba3SJiri Pirko { 37111ce2ba3SJiri Pirko wait->done = true; 37211ce2ba3SJiri Pirko wake_up(&wait->wait); 37311ce2ba3SJiri Pirko } 37411ce2ba3SJiri Pirko 37511ce2ba3SJiri Pirko static u32 rocker_msix_vector(const struct rocker *rocker, unsigned int vector) 37611ce2ba3SJiri Pirko { 37711ce2ba3SJiri Pirko return rocker->msix_entries[vector].vector; 37811ce2ba3SJiri Pirko } 37911ce2ba3SJiri Pirko 38011ce2ba3SJiri Pirko static u32 rocker_msix_tx_vector(const struct rocker_port *rocker_port) 38111ce2ba3SJiri Pirko { 38211ce2ba3SJiri Pirko return rocker_msix_vector(rocker_port->rocker, 38311ce2ba3SJiri Pirko ROCKER_MSIX_VEC_TX(rocker_port->port_number)); 38411ce2ba3SJiri Pirko } 38511ce2ba3SJiri Pirko 38611ce2ba3SJiri Pirko static u32 rocker_msix_rx_vector(const struct rocker_port *rocker_port) 38711ce2ba3SJiri Pirko { 38811ce2ba3SJiri Pirko return rocker_msix_vector(rocker_port->rocker, 38911ce2ba3SJiri Pirko ROCKER_MSIX_VEC_RX(rocker_port->port_number)); 39011ce2ba3SJiri Pirko } 39111ce2ba3SJiri Pirko 39211ce2ba3SJiri Pirko #define rocker_write32(rocker, reg, val) \ 39311ce2ba3SJiri Pirko writel((val), (rocker)->hw_addr + (ROCKER_ ## reg)) 39411ce2ba3SJiri Pirko #define rocker_read32(rocker, reg) \ 39511ce2ba3SJiri Pirko readl((rocker)->hw_addr + (ROCKER_ ## reg)) 39611ce2ba3SJiri Pirko #define rocker_write64(rocker, reg, val) \ 39711ce2ba3SJiri Pirko writeq((val), (rocker)->hw_addr + (ROCKER_ ## reg)) 39811ce2ba3SJiri Pirko #define rocker_read64(rocker, reg) \ 39911ce2ba3SJiri Pirko readq((rocker)->hw_addr + (ROCKER_ ## reg)) 40011ce2ba3SJiri Pirko 40111ce2ba3SJiri Pirko /***************************** 40211ce2ba3SJiri Pirko * HW basic testing functions 40311ce2ba3SJiri Pirko *****************************/ 40411ce2ba3SJiri Pirko 40511ce2ba3SJiri Pirko static int rocker_reg_test(const struct rocker *rocker) 40611ce2ba3SJiri Pirko { 40711ce2ba3SJiri Pirko const struct pci_dev *pdev = rocker->pdev; 40811ce2ba3SJiri Pirko u64 test_reg; 40911ce2ba3SJiri Pirko u64 rnd; 41011ce2ba3SJiri Pirko 41111ce2ba3SJiri Pirko rnd = prandom_u32(); 41211ce2ba3SJiri Pirko rnd >>= 1; 41311ce2ba3SJiri Pirko rocker_write32(rocker, TEST_REG, rnd); 41411ce2ba3SJiri Pirko test_reg = rocker_read32(rocker, TEST_REG); 41511ce2ba3SJiri Pirko if (test_reg != rnd * 2) { 41611ce2ba3SJiri Pirko dev_err(&pdev->dev, "unexpected 32bit register value %08llx, expected %08llx\n", 41711ce2ba3SJiri Pirko test_reg, rnd * 2); 41811ce2ba3SJiri Pirko return -EIO; 41911ce2ba3SJiri Pirko } 42011ce2ba3SJiri Pirko 42111ce2ba3SJiri Pirko rnd = prandom_u32(); 42211ce2ba3SJiri Pirko rnd <<= 31; 42311ce2ba3SJiri Pirko rnd |= prandom_u32(); 42411ce2ba3SJiri Pirko rocker_write64(rocker, TEST_REG64, rnd); 42511ce2ba3SJiri Pirko test_reg = rocker_read64(rocker, TEST_REG64); 42611ce2ba3SJiri Pirko if (test_reg != rnd * 2) { 42711ce2ba3SJiri Pirko dev_err(&pdev->dev, "unexpected 64bit register value %16llx, expected %16llx\n", 42811ce2ba3SJiri Pirko test_reg, rnd * 2); 42911ce2ba3SJiri Pirko return -EIO; 43011ce2ba3SJiri Pirko } 43111ce2ba3SJiri Pirko 43211ce2ba3SJiri Pirko return 0; 43311ce2ba3SJiri Pirko } 43411ce2ba3SJiri Pirko 43511ce2ba3SJiri Pirko static int rocker_dma_test_one(const struct rocker *rocker, 43611ce2ba3SJiri Pirko struct rocker_wait *wait, u32 test_type, 43711ce2ba3SJiri Pirko dma_addr_t dma_handle, const unsigned char *buf, 43811ce2ba3SJiri Pirko const unsigned char *expect, size_t size) 43911ce2ba3SJiri Pirko { 44011ce2ba3SJiri Pirko const struct pci_dev *pdev = rocker->pdev; 44111ce2ba3SJiri Pirko int i; 44211ce2ba3SJiri Pirko 44311ce2ba3SJiri Pirko rocker_wait_reset(wait); 44411ce2ba3SJiri Pirko rocker_write32(rocker, TEST_DMA_CTRL, test_type); 44511ce2ba3SJiri Pirko 44611ce2ba3SJiri Pirko if (!rocker_wait_event_timeout(wait, HZ / 10)) { 44711ce2ba3SJiri Pirko dev_err(&pdev->dev, "no interrupt received within a timeout\n"); 44811ce2ba3SJiri Pirko return -EIO; 44911ce2ba3SJiri Pirko } 45011ce2ba3SJiri Pirko 45111ce2ba3SJiri Pirko for (i = 0; i < size; i++) { 45211ce2ba3SJiri Pirko if (buf[i] != expect[i]) { 45311ce2ba3SJiri Pirko dev_err(&pdev->dev, "unexpected memory content %02x at byte %x\n, %02x expected", 45411ce2ba3SJiri Pirko buf[i], i, expect[i]); 45511ce2ba3SJiri Pirko return -EIO; 45611ce2ba3SJiri Pirko } 45711ce2ba3SJiri Pirko } 45811ce2ba3SJiri Pirko return 0; 45911ce2ba3SJiri Pirko } 46011ce2ba3SJiri Pirko 46111ce2ba3SJiri Pirko #define ROCKER_TEST_DMA_BUF_SIZE (PAGE_SIZE * 4) 46211ce2ba3SJiri Pirko #define ROCKER_TEST_DMA_FILL_PATTERN 0x96 46311ce2ba3SJiri Pirko 46411ce2ba3SJiri Pirko static int rocker_dma_test_offset(const struct rocker *rocker, 46511ce2ba3SJiri Pirko struct rocker_wait *wait, int offset) 46611ce2ba3SJiri Pirko { 46711ce2ba3SJiri Pirko struct pci_dev *pdev = rocker->pdev; 46811ce2ba3SJiri Pirko unsigned char *alloc; 46911ce2ba3SJiri Pirko unsigned char *buf; 47011ce2ba3SJiri Pirko unsigned char *expect; 47111ce2ba3SJiri Pirko dma_addr_t dma_handle; 47211ce2ba3SJiri Pirko int i; 47311ce2ba3SJiri Pirko int err; 47411ce2ba3SJiri Pirko 47511ce2ba3SJiri Pirko alloc = kzalloc(ROCKER_TEST_DMA_BUF_SIZE * 2 + offset, 47611ce2ba3SJiri Pirko GFP_KERNEL | GFP_DMA); 47711ce2ba3SJiri Pirko if (!alloc) 47811ce2ba3SJiri Pirko return -ENOMEM; 47911ce2ba3SJiri Pirko buf = alloc + offset; 48011ce2ba3SJiri Pirko expect = buf + ROCKER_TEST_DMA_BUF_SIZE; 48111ce2ba3SJiri Pirko 48211ce2ba3SJiri Pirko dma_handle = pci_map_single(pdev, buf, ROCKER_TEST_DMA_BUF_SIZE, 48311ce2ba3SJiri Pirko PCI_DMA_BIDIRECTIONAL); 48411ce2ba3SJiri Pirko if (pci_dma_mapping_error(pdev, dma_handle)) { 48511ce2ba3SJiri Pirko err = -EIO; 48611ce2ba3SJiri Pirko goto free_alloc; 48711ce2ba3SJiri Pirko } 48811ce2ba3SJiri Pirko 48911ce2ba3SJiri Pirko rocker_write64(rocker, TEST_DMA_ADDR, dma_handle); 49011ce2ba3SJiri Pirko rocker_write32(rocker, TEST_DMA_SIZE, ROCKER_TEST_DMA_BUF_SIZE); 49111ce2ba3SJiri Pirko 49211ce2ba3SJiri Pirko memset(expect, ROCKER_TEST_DMA_FILL_PATTERN, ROCKER_TEST_DMA_BUF_SIZE); 49311ce2ba3SJiri Pirko err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_FILL, 49411ce2ba3SJiri Pirko dma_handle, buf, expect, 49511ce2ba3SJiri Pirko ROCKER_TEST_DMA_BUF_SIZE); 49611ce2ba3SJiri Pirko if (err) 49711ce2ba3SJiri Pirko goto unmap; 49811ce2ba3SJiri Pirko 49911ce2ba3SJiri Pirko memset(expect, 0, ROCKER_TEST_DMA_BUF_SIZE); 50011ce2ba3SJiri Pirko err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_CLEAR, 50111ce2ba3SJiri Pirko dma_handle, buf, expect, 50211ce2ba3SJiri Pirko ROCKER_TEST_DMA_BUF_SIZE); 50311ce2ba3SJiri Pirko if (err) 50411ce2ba3SJiri Pirko goto unmap; 50511ce2ba3SJiri Pirko 50611ce2ba3SJiri Pirko prandom_bytes(buf, ROCKER_TEST_DMA_BUF_SIZE); 50711ce2ba3SJiri Pirko for (i = 0; i < ROCKER_TEST_DMA_BUF_SIZE; i++) 50811ce2ba3SJiri Pirko expect[i] = ~buf[i]; 50911ce2ba3SJiri Pirko err = rocker_dma_test_one(rocker, wait, ROCKER_TEST_DMA_CTRL_INVERT, 51011ce2ba3SJiri Pirko dma_handle, buf, expect, 51111ce2ba3SJiri Pirko ROCKER_TEST_DMA_BUF_SIZE); 51211ce2ba3SJiri Pirko if (err) 51311ce2ba3SJiri Pirko goto unmap; 51411ce2ba3SJiri Pirko 51511ce2ba3SJiri Pirko unmap: 51611ce2ba3SJiri Pirko pci_unmap_single(pdev, dma_handle, ROCKER_TEST_DMA_BUF_SIZE, 51711ce2ba3SJiri Pirko PCI_DMA_BIDIRECTIONAL); 51811ce2ba3SJiri Pirko free_alloc: 51911ce2ba3SJiri Pirko kfree(alloc); 52011ce2ba3SJiri Pirko 52111ce2ba3SJiri Pirko return err; 52211ce2ba3SJiri Pirko } 52311ce2ba3SJiri Pirko 52411ce2ba3SJiri Pirko static int rocker_dma_test(const struct rocker *rocker, 52511ce2ba3SJiri Pirko struct rocker_wait *wait) 52611ce2ba3SJiri Pirko { 52711ce2ba3SJiri Pirko int i; 52811ce2ba3SJiri Pirko int err; 52911ce2ba3SJiri Pirko 53011ce2ba3SJiri Pirko for (i = 0; i < 8; i++) { 53111ce2ba3SJiri Pirko err = rocker_dma_test_offset(rocker, wait, i); 53211ce2ba3SJiri Pirko if (err) 53311ce2ba3SJiri Pirko return err; 53411ce2ba3SJiri Pirko } 53511ce2ba3SJiri Pirko return 0; 53611ce2ba3SJiri Pirko } 53711ce2ba3SJiri Pirko 53811ce2ba3SJiri Pirko static irqreturn_t rocker_test_irq_handler(int irq, void *dev_id) 53911ce2ba3SJiri Pirko { 54011ce2ba3SJiri Pirko struct rocker_wait *wait = dev_id; 54111ce2ba3SJiri Pirko 54211ce2ba3SJiri Pirko rocker_wait_wake_up(wait); 54311ce2ba3SJiri Pirko 54411ce2ba3SJiri Pirko return IRQ_HANDLED; 54511ce2ba3SJiri Pirko } 54611ce2ba3SJiri Pirko 54711ce2ba3SJiri Pirko static int rocker_basic_hw_test(const struct rocker *rocker) 54811ce2ba3SJiri Pirko { 54911ce2ba3SJiri Pirko const struct pci_dev *pdev = rocker->pdev; 55011ce2ba3SJiri Pirko struct rocker_wait wait; 55111ce2ba3SJiri Pirko int err; 55211ce2ba3SJiri Pirko 55311ce2ba3SJiri Pirko err = rocker_reg_test(rocker); 55411ce2ba3SJiri Pirko if (err) { 55511ce2ba3SJiri Pirko dev_err(&pdev->dev, "reg test failed\n"); 55611ce2ba3SJiri Pirko return err; 55711ce2ba3SJiri Pirko } 55811ce2ba3SJiri Pirko 55911ce2ba3SJiri Pirko err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_TEST), 56011ce2ba3SJiri Pirko rocker_test_irq_handler, 0, 56111ce2ba3SJiri Pirko rocker_driver_name, &wait); 56211ce2ba3SJiri Pirko if (err) { 56311ce2ba3SJiri Pirko dev_err(&pdev->dev, "cannot assign test irq\n"); 56411ce2ba3SJiri Pirko return err; 56511ce2ba3SJiri Pirko } 56611ce2ba3SJiri Pirko 56711ce2ba3SJiri Pirko rocker_wait_init(&wait); 56811ce2ba3SJiri Pirko rocker_write32(rocker, TEST_IRQ, ROCKER_MSIX_VEC_TEST); 56911ce2ba3SJiri Pirko 57011ce2ba3SJiri Pirko if (!rocker_wait_event_timeout(&wait, HZ / 10)) { 57111ce2ba3SJiri Pirko dev_err(&pdev->dev, "no interrupt received within a timeout\n"); 57211ce2ba3SJiri Pirko err = -EIO; 57311ce2ba3SJiri Pirko goto free_irq; 57411ce2ba3SJiri Pirko } 57511ce2ba3SJiri Pirko 57611ce2ba3SJiri Pirko err = rocker_dma_test(rocker, &wait); 57711ce2ba3SJiri Pirko if (err) 57811ce2ba3SJiri Pirko dev_err(&pdev->dev, "dma test failed\n"); 57911ce2ba3SJiri Pirko 58011ce2ba3SJiri Pirko free_irq: 58111ce2ba3SJiri Pirko free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_TEST), &wait); 58211ce2ba3SJiri Pirko return err; 58311ce2ba3SJiri Pirko } 58411ce2ba3SJiri Pirko 58511ce2ba3SJiri Pirko /****************************************** 58611ce2ba3SJiri Pirko * DMA rings and descriptors manipulations 58711ce2ba3SJiri Pirko ******************************************/ 58811ce2ba3SJiri Pirko 58911ce2ba3SJiri Pirko static u32 __pos_inc(u32 pos, size_t limit) 59011ce2ba3SJiri Pirko { 59111ce2ba3SJiri Pirko return ++pos == limit ? 0 : pos; 59211ce2ba3SJiri Pirko } 59311ce2ba3SJiri Pirko 59411ce2ba3SJiri Pirko static int rocker_desc_err(const struct rocker_desc_info *desc_info) 59511ce2ba3SJiri Pirko { 59611ce2ba3SJiri Pirko int err = desc_info->desc->comp_err & ~ROCKER_DMA_DESC_COMP_ERR_GEN; 59711ce2ba3SJiri Pirko 59811ce2ba3SJiri Pirko switch (err) { 59911ce2ba3SJiri Pirko case ROCKER_OK: 60011ce2ba3SJiri Pirko return 0; 60111ce2ba3SJiri Pirko case -ROCKER_ENOENT: 60211ce2ba3SJiri Pirko return -ENOENT; 60311ce2ba3SJiri Pirko case -ROCKER_ENXIO: 60411ce2ba3SJiri Pirko return -ENXIO; 60511ce2ba3SJiri Pirko case -ROCKER_ENOMEM: 60611ce2ba3SJiri Pirko return -ENOMEM; 60711ce2ba3SJiri Pirko case -ROCKER_EEXIST: 60811ce2ba3SJiri Pirko return -EEXIST; 60911ce2ba3SJiri Pirko case -ROCKER_EINVAL: 61011ce2ba3SJiri Pirko return -EINVAL; 61111ce2ba3SJiri Pirko case -ROCKER_EMSGSIZE: 61211ce2ba3SJiri Pirko return -EMSGSIZE; 61311ce2ba3SJiri Pirko case -ROCKER_ENOTSUP: 61411ce2ba3SJiri Pirko return -EOPNOTSUPP; 61511ce2ba3SJiri Pirko case -ROCKER_ENOBUFS: 61611ce2ba3SJiri Pirko return -ENOBUFS; 61711ce2ba3SJiri Pirko } 61811ce2ba3SJiri Pirko 61911ce2ba3SJiri Pirko return -EINVAL; 62011ce2ba3SJiri Pirko } 62111ce2ba3SJiri Pirko 62211ce2ba3SJiri Pirko static void rocker_desc_gen_clear(const struct rocker_desc_info *desc_info) 62311ce2ba3SJiri Pirko { 62411ce2ba3SJiri Pirko desc_info->desc->comp_err &= ~ROCKER_DMA_DESC_COMP_ERR_GEN; 62511ce2ba3SJiri Pirko } 62611ce2ba3SJiri Pirko 62711ce2ba3SJiri Pirko static bool rocker_desc_gen(const struct rocker_desc_info *desc_info) 62811ce2ba3SJiri Pirko { 62911ce2ba3SJiri Pirko u32 comp_err = desc_info->desc->comp_err; 63011ce2ba3SJiri Pirko 63111ce2ba3SJiri Pirko return comp_err & ROCKER_DMA_DESC_COMP_ERR_GEN ? true : false; 63211ce2ba3SJiri Pirko } 63311ce2ba3SJiri Pirko 63411ce2ba3SJiri Pirko static void * 63511ce2ba3SJiri Pirko rocker_desc_cookie_ptr_get(const struct rocker_desc_info *desc_info) 63611ce2ba3SJiri Pirko { 63711ce2ba3SJiri Pirko return (void *)(uintptr_t)desc_info->desc->cookie; 63811ce2ba3SJiri Pirko } 63911ce2ba3SJiri Pirko 64011ce2ba3SJiri Pirko static void rocker_desc_cookie_ptr_set(const struct rocker_desc_info *desc_info, 64111ce2ba3SJiri Pirko void *ptr) 64211ce2ba3SJiri Pirko { 64311ce2ba3SJiri Pirko desc_info->desc->cookie = (uintptr_t) ptr; 64411ce2ba3SJiri Pirko } 64511ce2ba3SJiri Pirko 64611ce2ba3SJiri Pirko static struct rocker_desc_info * 64711ce2ba3SJiri Pirko rocker_desc_head_get(const struct rocker_dma_ring_info *info) 64811ce2ba3SJiri Pirko { 64911ce2ba3SJiri Pirko static struct rocker_desc_info *desc_info; 65011ce2ba3SJiri Pirko u32 head = __pos_inc(info->head, info->size); 65111ce2ba3SJiri Pirko 65211ce2ba3SJiri Pirko desc_info = &info->desc_info[info->head]; 65311ce2ba3SJiri Pirko if (head == info->tail) 65411ce2ba3SJiri Pirko return NULL; /* ring full */ 65511ce2ba3SJiri Pirko desc_info->tlv_size = 0; 65611ce2ba3SJiri Pirko return desc_info; 65711ce2ba3SJiri Pirko } 65811ce2ba3SJiri Pirko 65911ce2ba3SJiri Pirko static void rocker_desc_commit(const struct rocker_desc_info *desc_info) 66011ce2ba3SJiri Pirko { 66111ce2ba3SJiri Pirko desc_info->desc->buf_size = desc_info->data_size; 66211ce2ba3SJiri Pirko desc_info->desc->tlv_size = desc_info->tlv_size; 66311ce2ba3SJiri Pirko } 66411ce2ba3SJiri Pirko 66511ce2ba3SJiri Pirko static void rocker_desc_head_set(const struct rocker *rocker, 66611ce2ba3SJiri Pirko struct rocker_dma_ring_info *info, 66711ce2ba3SJiri Pirko const struct rocker_desc_info *desc_info) 66811ce2ba3SJiri Pirko { 66911ce2ba3SJiri Pirko u32 head = __pos_inc(info->head, info->size); 67011ce2ba3SJiri Pirko 67111ce2ba3SJiri Pirko BUG_ON(head == info->tail); 67211ce2ba3SJiri Pirko rocker_desc_commit(desc_info); 67311ce2ba3SJiri Pirko info->head = head; 67411ce2ba3SJiri Pirko rocker_write32(rocker, DMA_DESC_HEAD(info->type), head); 67511ce2ba3SJiri Pirko } 67611ce2ba3SJiri Pirko 67711ce2ba3SJiri Pirko static struct rocker_desc_info * 67811ce2ba3SJiri Pirko rocker_desc_tail_get(struct rocker_dma_ring_info *info) 67911ce2ba3SJiri Pirko { 68011ce2ba3SJiri Pirko static struct rocker_desc_info *desc_info; 68111ce2ba3SJiri Pirko 68211ce2ba3SJiri Pirko if (info->tail == info->head) 68311ce2ba3SJiri Pirko return NULL; /* nothing to be done between head and tail */ 68411ce2ba3SJiri Pirko desc_info = &info->desc_info[info->tail]; 68511ce2ba3SJiri Pirko if (!rocker_desc_gen(desc_info)) 68611ce2ba3SJiri Pirko return NULL; /* gen bit not set, desc is not ready yet */ 68711ce2ba3SJiri Pirko info->tail = __pos_inc(info->tail, info->size); 68811ce2ba3SJiri Pirko desc_info->tlv_size = desc_info->desc->tlv_size; 68911ce2ba3SJiri Pirko return desc_info; 69011ce2ba3SJiri Pirko } 69111ce2ba3SJiri Pirko 69211ce2ba3SJiri Pirko static void rocker_dma_ring_credits_set(const struct rocker *rocker, 69311ce2ba3SJiri Pirko const struct rocker_dma_ring_info *info, 69411ce2ba3SJiri Pirko u32 credits) 69511ce2ba3SJiri Pirko { 69611ce2ba3SJiri Pirko if (credits) 69711ce2ba3SJiri Pirko rocker_write32(rocker, DMA_DESC_CREDITS(info->type), credits); 69811ce2ba3SJiri Pirko } 69911ce2ba3SJiri Pirko 70011ce2ba3SJiri Pirko static unsigned long rocker_dma_ring_size_fix(size_t size) 70111ce2ba3SJiri Pirko { 70211ce2ba3SJiri Pirko return max(ROCKER_DMA_SIZE_MIN, 70311ce2ba3SJiri Pirko min(roundup_pow_of_two(size), ROCKER_DMA_SIZE_MAX)); 70411ce2ba3SJiri Pirko } 70511ce2ba3SJiri Pirko 70611ce2ba3SJiri Pirko static int rocker_dma_ring_create(const struct rocker *rocker, 70711ce2ba3SJiri Pirko unsigned int type, 70811ce2ba3SJiri Pirko size_t size, 70911ce2ba3SJiri Pirko struct rocker_dma_ring_info *info) 71011ce2ba3SJiri Pirko { 71111ce2ba3SJiri Pirko int i; 71211ce2ba3SJiri Pirko 71311ce2ba3SJiri Pirko BUG_ON(size != rocker_dma_ring_size_fix(size)); 71411ce2ba3SJiri Pirko info->size = size; 71511ce2ba3SJiri Pirko info->type = type; 71611ce2ba3SJiri Pirko info->head = 0; 71711ce2ba3SJiri Pirko info->tail = 0; 71811ce2ba3SJiri Pirko info->desc_info = kcalloc(info->size, sizeof(*info->desc_info), 71911ce2ba3SJiri Pirko GFP_KERNEL); 72011ce2ba3SJiri Pirko if (!info->desc_info) 72111ce2ba3SJiri Pirko return -ENOMEM; 72211ce2ba3SJiri Pirko 72311ce2ba3SJiri Pirko info->desc = pci_alloc_consistent(rocker->pdev, 72411ce2ba3SJiri Pirko info->size * sizeof(*info->desc), 72511ce2ba3SJiri Pirko &info->mapaddr); 72611ce2ba3SJiri Pirko if (!info->desc) { 72711ce2ba3SJiri Pirko kfree(info->desc_info); 72811ce2ba3SJiri Pirko return -ENOMEM; 72911ce2ba3SJiri Pirko } 73011ce2ba3SJiri Pirko 73111ce2ba3SJiri Pirko for (i = 0; i < info->size; i++) 73211ce2ba3SJiri Pirko info->desc_info[i].desc = &info->desc[i]; 73311ce2ba3SJiri Pirko 73411ce2ba3SJiri Pirko rocker_write32(rocker, DMA_DESC_CTRL(info->type), 73511ce2ba3SJiri Pirko ROCKER_DMA_DESC_CTRL_RESET); 73611ce2ba3SJiri Pirko rocker_write64(rocker, DMA_DESC_ADDR(info->type), info->mapaddr); 73711ce2ba3SJiri Pirko rocker_write32(rocker, DMA_DESC_SIZE(info->type), info->size); 73811ce2ba3SJiri Pirko 73911ce2ba3SJiri Pirko return 0; 74011ce2ba3SJiri Pirko } 74111ce2ba3SJiri Pirko 74211ce2ba3SJiri Pirko static void rocker_dma_ring_destroy(const struct rocker *rocker, 74311ce2ba3SJiri Pirko const struct rocker_dma_ring_info *info) 74411ce2ba3SJiri Pirko { 74511ce2ba3SJiri Pirko rocker_write64(rocker, DMA_DESC_ADDR(info->type), 0); 74611ce2ba3SJiri Pirko 74711ce2ba3SJiri Pirko pci_free_consistent(rocker->pdev, 74811ce2ba3SJiri Pirko info->size * sizeof(struct rocker_desc), 74911ce2ba3SJiri Pirko info->desc, info->mapaddr); 75011ce2ba3SJiri Pirko kfree(info->desc_info); 75111ce2ba3SJiri Pirko } 75211ce2ba3SJiri Pirko 75311ce2ba3SJiri Pirko static void rocker_dma_ring_pass_to_producer(const struct rocker *rocker, 75411ce2ba3SJiri Pirko struct rocker_dma_ring_info *info) 75511ce2ba3SJiri Pirko { 75611ce2ba3SJiri Pirko int i; 75711ce2ba3SJiri Pirko 75811ce2ba3SJiri Pirko BUG_ON(info->head || info->tail); 75911ce2ba3SJiri Pirko 76011ce2ba3SJiri Pirko /* When ring is consumer, we need to advance head for each desc. 76111ce2ba3SJiri Pirko * That tells hw that the desc is ready to be used by it. 76211ce2ba3SJiri Pirko */ 76311ce2ba3SJiri Pirko for (i = 0; i < info->size - 1; i++) 76411ce2ba3SJiri Pirko rocker_desc_head_set(rocker, info, &info->desc_info[i]); 76511ce2ba3SJiri Pirko rocker_desc_commit(&info->desc_info[i]); 76611ce2ba3SJiri Pirko } 76711ce2ba3SJiri Pirko 76811ce2ba3SJiri Pirko static int rocker_dma_ring_bufs_alloc(const struct rocker *rocker, 76911ce2ba3SJiri Pirko const struct rocker_dma_ring_info *info, 77011ce2ba3SJiri Pirko int direction, size_t buf_size) 77111ce2ba3SJiri Pirko { 77211ce2ba3SJiri Pirko struct pci_dev *pdev = rocker->pdev; 77311ce2ba3SJiri Pirko int i; 77411ce2ba3SJiri Pirko int err; 77511ce2ba3SJiri Pirko 77611ce2ba3SJiri Pirko for (i = 0; i < info->size; i++) { 77711ce2ba3SJiri Pirko struct rocker_desc_info *desc_info = &info->desc_info[i]; 77811ce2ba3SJiri Pirko struct rocker_desc *desc = &info->desc[i]; 77911ce2ba3SJiri Pirko dma_addr_t dma_handle; 78011ce2ba3SJiri Pirko char *buf; 78111ce2ba3SJiri Pirko 78211ce2ba3SJiri Pirko buf = kzalloc(buf_size, GFP_KERNEL | GFP_DMA); 78311ce2ba3SJiri Pirko if (!buf) { 78411ce2ba3SJiri Pirko err = -ENOMEM; 78511ce2ba3SJiri Pirko goto rollback; 78611ce2ba3SJiri Pirko } 78711ce2ba3SJiri Pirko 78811ce2ba3SJiri Pirko dma_handle = pci_map_single(pdev, buf, buf_size, direction); 78911ce2ba3SJiri Pirko if (pci_dma_mapping_error(pdev, dma_handle)) { 79011ce2ba3SJiri Pirko kfree(buf); 79111ce2ba3SJiri Pirko err = -EIO; 79211ce2ba3SJiri Pirko goto rollback; 79311ce2ba3SJiri Pirko } 79411ce2ba3SJiri Pirko 79511ce2ba3SJiri Pirko desc_info->data = buf; 79611ce2ba3SJiri Pirko desc_info->data_size = buf_size; 79711ce2ba3SJiri Pirko dma_unmap_addr_set(desc_info, mapaddr, dma_handle); 79811ce2ba3SJiri Pirko 79911ce2ba3SJiri Pirko desc->buf_addr = dma_handle; 80011ce2ba3SJiri Pirko desc->buf_size = buf_size; 80111ce2ba3SJiri Pirko } 80211ce2ba3SJiri Pirko return 0; 80311ce2ba3SJiri Pirko 80411ce2ba3SJiri Pirko rollback: 80511ce2ba3SJiri Pirko for (i--; i >= 0; i--) { 80611ce2ba3SJiri Pirko const struct rocker_desc_info *desc_info = &info->desc_info[i]; 80711ce2ba3SJiri Pirko 80811ce2ba3SJiri Pirko pci_unmap_single(pdev, dma_unmap_addr(desc_info, mapaddr), 80911ce2ba3SJiri Pirko desc_info->data_size, direction); 81011ce2ba3SJiri Pirko kfree(desc_info->data); 81111ce2ba3SJiri Pirko } 81211ce2ba3SJiri Pirko return err; 81311ce2ba3SJiri Pirko } 81411ce2ba3SJiri Pirko 81511ce2ba3SJiri Pirko static void rocker_dma_ring_bufs_free(const struct rocker *rocker, 81611ce2ba3SJiri Pirko const struct rocker_dma_ring_info *info, 81711ce2ba3SJiri Pirko int direction) 81811ce2ba3SJiri Pirko { 81911ce2ba3SJiri Pirko struct pci_dev *pdev = rocker->pdev; 82011ce2ba3SJiri Pirko int i; 82111ce2ba3SJiri Pirko 82211ce2ba3SJiri Pirko for (i = 0; i < info->size; i++) { 82311ce2ba3SJiri Pirko const struct rocker_desc_info *desc_info = &info->desc_info[i]; 82411ce2ba3SJiri Pirko struct rocker_desc *desc = &info->desc[i]; 82511ce2ba3SJiri Pirko 82611ce2ba3SJiri Pirko desc->buf_addr = 0; 82711ce2ba3SJiri Pirko desc->buf_size = 0; 82811ce2ba3SJiri Pirko pci_unmap_single(pdev, dma_unmap_addr(desc_info, mapaddr), 82911ce2ba3SJiri Pirko desc_info->data_size, direction); 83011ce2ba3SJiri Pirko kfree(desc_info->data); 83111ce2ba3SJiri Pirko } 83211ce2ba3SJiri Pirko } 83311ce2ba3SJiri Pirko 83411ce2ba3SJiri Pirko static int rocker_dma_rings_init(struct rocker *rocker) 83511ce2ba3SJiri Pirko { 83611ce2ba3SJiri Pirko const struct pci_dev *pdev = rocker->pdev; 83711ce2ba3SJiri Pirko int err; 83811ce2ba3SJiri Pirko 83911ce2ba3SJiri Pirko err = rocker_dma_ring_create(rocker, ROCKER_DMA_CMD, 84011ce2ba3SJiri Pirko ROCKER_DMA_CMD_DEFAULT_SIZE, 84111ce2ba3SJiri Pirko &rocker->cmd_ring); 84211ce2ba3SJiri Pirko if (err) { 84311ce2ba3SJiri Pirko dev_err(&pdev->dev, "failed to create command dma ring\n"); 84411ce2ba3SJiri Pirko return err; 84511ce2ba3SJiri Pirko } 84611ce2ba3SJiri Pirko 84711ce2ba3SJiri Pirko spin_lock_init(&rocker->cmd_ring_lock); 84811ce2ba3SJiri Pirko 84911ce2ba3SJiri Pirko err = rocker_dma_ring_bufs_alloc(rocker, &rocker->cmd_ring, 85011ce2ba3SJiri Pirko PCI_DMA_BIDIRECTIONAL, PAGE_SIZE); 85111ce2ba3SJiri Pirko if (err) { 85211ce2ba3SJiri Pirko dev_err(&pdev->dev, "failed to alloc command dma ring buffers\n"); 85311ce2ba3SJiri Pirko goto err_dma_cmd_ring_bufs_alloc; 85411ce2ba3SJiri Pirko } 85511ce2ba3SJiri Pirko 85611ce2ba3SJiri Pirko err = rocker_dma_ring_create(rocker, ROCKER_DMA_EVENT, 85711ce2ba3SJiri Pirko ROCKER_DMA_EVENT_DEFAULT_SIZE, 85811ce2ba3SJiri Pirko &rocker->event_ring); 85911ce2ba3SJiri Pirko if (err) { 86011ce2ba3SJiri Pirko dev_err(&pdev->dev, "failed to create event dma ring\n"); 86111ce2ba3SJiri Pirko goto err_dma_event_ring_create; 86211ce2ba3SJiri Pirko } 86311ce2ba3SJiri Pirko 86411ce2ba3SJiri Pirko err = rocker_dma_ring_bufs_alloc(rocker, &rocker->event_ring, 86511ce2ba3SJiri Pirko PCI_DMA_FROMDEVICE, PAGE_SIZE); 86611ce2ba3SJiri Pirko if (err) { 86711ce2ba3SJiri Pirko dev_err(&pdev->dev, "failed to alloc event dma ring buffers\n"); 86811ce2ba3SJiri Pirko goto err_dma_event_ring_bufs_alloc; 86911ce2ba3SJiri Pirko } 87011ce2ba3SJiri Pirko rocker_dma_ring_pass_to_producer(rocker, &rocker->event_ring); 87111ce2ba3SJiri Pirko return 0; 87211ce2ba3SJiri Pirko 87311ce2ba3SJiri Pirko err_dma_event_ring_bufs_alloc: 87411ce2ba3SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker->event_ring); 87511ce2ba3SJiri Pirko err_dma_event_ring_create: 87611ce2ba3SJiri Pirko rocker_dma_ring_bufs_free(rocker, &rocker->cmd_ring, 87711ce2ba3SJiri Pirko PCI_DMA_BIDIRECTIONAL); 87811ce2ba3SJiri Pirko err_dma_cmd_ring_bufs_alloc: 87911ce2ba3SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker->cmd_ring); 88011ce2ba3SJiri Pirko return err; 88111ce2ba3SJiri Pirko } 88211ce2ba3SJiri Pirko 88311ce2ba3SJiri Pirko static void rocker_dma_rings_fini(struct rocker *rocker) 88411ce2ba3SJiri Pirko { 88511ce2ba3SJiri Pirko rocker_dma_ring_bufs_free(rocker, &rocker->event_ring, 88611ce2ba3SJiri Pirko PCI_DMA_BIDIRECTIONAL); 88711ce2ba3SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker->event_ring); 88811ce2ba3SJiri Pirko rocker_dma_ring_bufs_free(rocker, &rocker->cmd_ring, 88911ce2ba3SJiri Pirko PCI_DMA_BIDIRECTIONAL); 89011ce2ba3SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker->cmd_ring); 89111ce2ba3SJiri Pirko } 89211ce2ba3SJiri Pirko 89311ce2ba3SJiri Pirko static int rocker_dma_rx_ring_skb_map(const struct rocker_port *rocker_port, 89411ce2ba3SJiri Pirko struct rocker_desc_info *desc_info, 89511ce2ba3SJiri Pirko struct sk_buff *skb, size_t buf_len) 89611ce2ba3SJiri Pirko { 89711ce2ba3SJiri Pirko const struct rocker *rocker = rocker_port->rocker; 89811ce2ba3SJiri Pirko struct pci_dev *pdev = rocker->pdev; 89911ce2ba3SJiri Pirko dma_addr_t dma_handle; 90011ce2ba3SJiri Pirko 90111ce2ba3SJiri Pirko dma_handle = pci_map_single(pdev, skb->data, buf_len, 90211ce2ba3SJiri Pirko PCI_DMA_FROMDEVICE); 90311ce2ba3SJiri Pirko if (pci_dma_mapping_error(pdev, dma_handle)) 90411ce2ba3SJiri Pirko return -EIO; 90511ce2ba3SJiri Pirko if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_RX_FRAG_ADDR, dma_handle)) 90611ce2ba3SJiri Pirko goto tlv_put_failure; 90711ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_RX_FRAG_MAX_LEN, buf_len)) 90811ce2ba3SJiri Pirko goto tlv_put_failure; 90911ce2ba3SJiri Pirko return 0; 91011ce2ba3SJiri Pirko 91111ce2ba3SJiri Pirko tlv_put_failure: 91211ce2ba3SJiri Pirko pci_unmap_single(pdev, dma_handle, buf_len, PCI_DMA_FROMDEVICE); 91311ce2ba3SJiri Pirko desc_info->tlv_size = 0; 91411ce2ba3SJiri Pirko return -EMSGSIZE; 91511ce2ba3SJiri Pirko } 91611ce2ba3SJiri Pirko 91711ce2ba3SJiri Pirko static size_t rocker_port_rx_buf_len(const struct rocker_port *rocker_port) 91811ce2ba3SJiri Pirko { 91911ce2ba3SJiri Pirko return rocker_port->dev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN; 92011ce2ba3SJiri Pirko } 92111ce2ba3SJiri Pirko 92211ce2ba3SJiri Pirko static int rocker_dma_rx_ring_skb_alloc(const struct rocker_port *rocker_port, 92311ce2ba3SJiri Pirko struct rocker_desc_info *desc_info) 92411ce2ba3SJiri Pirko { 92511ce2ba3SJiri Pirko struct net_device *dev = rocker_port->dev; 92611ce2ba3SJiri Pirko struct sk_buff *skb; 92711ce2ba3SJiri Pirko size_t buf_len = rocker_port_rx_buf_len(rocker_port); 92811ce2ba3SJiri Pirko int err; 92911ce2ba3SJiri Pirko 93011ce2ba3SJiri Pirko /* Ensure that hw will see tlv_size zero in case of an error. 93111ce2ba3SJiri Pirko * That tells hw to use another descriptor. 93211ce2ba3SJiri Pirko */ 93311ce2ba3SJiri Pirko rocker_desc_cookie_ptr_set(desc_info, NULL); 93411ce2ba3SJiri Pirko desc_info->tlv_size = 0; 93511ce2ba3SJiri Pirko 93611ce2ba3SJiri Pirko skb = netdev_alloc_skb_ip_align(dev, buf_len); 93711ce2ba3SJiri Pirko if (!skb) 93811ce2ba3SJiri Pirko return -ENOMEM; 93911ce2ba3SJiri Pirko err = rocker_dma_rx_ring_skb_map(rocker_port, desc_info, skb, buf_len); 94011ce2ba3SJiri Pirko if (err) { 94111ce2ba3SJiri Pirko dev_kfree_skb_any(skb); 94211ce2ba3SJiri Pirko return err; 94311ce2ba3SJiri Pirko } 94411ce2ba3SJiri Pirko rocker_desc_cookie_ptr_set(desc_info, skb); 94511ce2ba3SJiri Pirko return 0; 94611ce2ba3SJiri Pirko } 94711ce2ba3SJiri Pirko 94811ce2ba3SJiri Pirko static void rocker_dma_rx_ring_skb_unmap(const struct rocker *rocker, 94911ce2ba3SJiri Pirko const struct rocker_tlv **attrs) 95011ce2ba3SJiri Pirko { 95111ce2ba3SJiri Pirko struct pci_dev *pdev = rocker->pdev; 95211ce2ba3SJiri Pirko dma_addr_t dma_handle; 95311ce2ba3SJiri Pirko size_t len; 95411ce2ba3SJiri Pirko 95511ce2ba3SJiri Pirko if (!attrs[ROCKER_TLV_RX_FRAG_ADDR] || 95611ce2ba3SJiri Pirko !attrs[ROCKER_TLV_RX_FRAG_MAX_LEN]) 95711ce2ba3SJiri Pirko return; 95811ce2ba3SJiri Pirko dma_handle = rocker_tlv_get_u64(attrs[ROCKER_TLV_RX_FRAG_ADDR]); 95911ce2ba3SJiri Pirko len = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FRAG_MAX_LEN]); 96011ce2ba3SJiri Pirko pci_unmap_single(pdev, dma_handle, len, PCI_DMA_FROMDEVICE); 96111ce2ba3SJiri Pirko } 96211ce2ba3SJiri Pirko 96311ce2ba3SJiri Pirko static void rocker_dma_rx_ring_skb_free(const struct rocker *rocker, 96411ce2ba3SJiri Pirko const struct rocker_desc_info *desc_info) 96511ce2ba3SJiri Pirko { 96611ce2ba3SJiri Pirko const struct rocker_tlv *attrs[ROCKER_TLV_RX_MAX + 1]; 96711ce2ba3SJiri Pirko struct sk_buff *skb = rocker_desc_cookie_ptr_get(desc_info); 96811ce2ba3SJiri Pirko 96911ce2ba3SJiri Pirko if (!skb) 97011ce2ba3SJiri Pirko return; 97111ce2ba3SJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_RX_MAX, desc_info); 97211ce2ba3SJiri Pirko rocker_dma_rx_ring_skb_unmap(rocker, attrs); 97311ce2ba3SJiri Pirko dev_kfree_skb_any(skb); 97411ce2ba3SJiri Pirko } 97511ce2ba3SJiri Pirko 97611ce2ba3SJiri Pirko static int rocker_dma_rx_ring_skbs_alloc(const struct rocker_port *rocker_port) 97711ce2ba3SJiri Pirko { 97811ce2ba3SJiri Pirko const struct rocker_dma_ring_info *rx_ring = &rocker_port->rx_ring; 97911ce2ba3SJiri Pirko const struct rocker *rocker = rocker_port->rocker; 98011ce2ba3SJiri Pirko int i; 98111ce2ba3SJiri Pirko int err; 98211ce2ba3SJiri Pirko 98311ce2ba3SJiri Pirko for (i = 0; i < rx_ring->size; i++) { 98411ce2ba3SJiri Pirko err = rocker_dma_rx_ring_skb_alloc(rocker_port, 98511ce2ba3SJiri Pirko &rx_ring->desc_info[i]); 98611ce2ba3SJiri Pirko if (err) 98711ce2ba3SJiri Pirko goto rollback; 98811ce2ba3SJiri Pirko } 98911ce2ba3SJiri Pirko return 0; 99011ce2ba3SJiri Pirko 99111ce2ba3SJiri Pirko rollback: 99211ce2ba3SJiri Pirko for (i--; i >= 0; i--) 99311ce2ba3SJiri Pirko rocker_dma_rx_ring_skb_free(rocker, &rx_ring->desc_info[i]); 99411ce2ba3SJiri Pirko return err; 99511ce2ba3SJiri Pirko } 99611ce2ba3SJiri Pirko 99711ce2ba3SJiri Pirko static void rocker_dma_rx_ring_skbs_free(const struct rocker_port *rocker_port) 99811ce2ba3SJiri Pirko { 99911ce2ba3SJiri Pirko const struct rocker_dma_ring_info *rx_ring = &rocker_port->rx_ring; 100011ce2ba3SJiri Pirko const struct rocker *rocker = rocker_port->rocker; 100111ce2ba3SJiri Pirko int i; 100211ce2ba3SJiri Pirko 100311ce2ba3SJiri Pirko for (i = 0; i < rx_ring->size; i++) 100411ce2ba3SJiri Pirko rocker_dma_rx_ring_skb_free(rocker, &rx_ring->desc_info[i]); 100511ce2ba3SJiri Pirko } 100611ce2ba3SJiri Pirko 100711ce2ba3SJiri Pirko static int rocker_port_dma_rings_init(struct rocker_port *rocker_port) 100811ce2ba3SJiri Pirko { 100911ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 101011ce2ba3SJiri Pirko int err; 101111ce2ba3SJiri Pirko 101211ce2ba3SJiri Pirko err = rocker_dma_ring_create(rocker, 101311ce2ba3SJiri Pirko ROCKER_DMA_TX(rocker_port->port_number), 101411ce2ba3SJiri Pirko ROCKER_DMA_TX_DEFAULT_SIZE, 101511ce2ba3SJiri Pirko &rocker_port->tx_ring); 101611ce2ba3SJiri Pirko if (err) { 101711ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "failed to create tx dma ring\n"); 101811ce2ba3SJiri Pirko return err; 101911ce2ba3SJiri Pirko } 102011ce2ba3SJiri Pirko 102111ce2ba3SJiri Pirko err = rocker_dma_ring_bufs_alloc(rocker, &rocker_port->tx_ring, 102211ce2ba3SJiri Pirko PCI_DMA_TODEVICE, 102311ce2ba3SJiri Pirko ROCKER_DMA_TX_DESC_SIZE); 102411ce2ba3SJiri Pirko if (err) { 102511ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "failed to alloc tx dma ring buffers\n"); 102611ce2ba3SJiri Pirko goto err_dma_tx_ring_bufs_alloc; 102711ce2ba3SJiri Pirko } 102811ce2ba3SJiri Pirko 102911ce2ba3SJiri Pirko err = rocker_dma_ring_create(rocker, 103011ce2ba3SJiri Pirko ROCKER_DMA_RX(rocker_port->port_number), 103111ce2ba3SJiri Pirko ROCKER_DMA_RX_DEFAULT_SIZE, 103211ce2ba3SJiri Pirko &rocker_port->rx_ring); 103311ce2ba3SJiri Pirko if (err) { 103411ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "failed to create rx dma ring\n"); 103511ce2ba3SJiri Pirko goto err_dma_rx_ring_create; 103611ce2ba3SJiri Pirko } 103711ce2ba3SJiri Pirko 103811ce2ba3SJiri Pirko err = rocker_dma_ring_bufs_alloc(rocker, &rocker_port->rx_ring, 103911ce2ba3SJiri Pirko PCI_DMA_BIDIRECTIONAL, 104011ce2ba3SJiri Pirko ROCKER_DMA_RX_DESC_SIZE); 104111ce2ba3SJiri Pirko if (err) { 104211ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "failed to alloc rx dma ring buffers\n"); 104311ce2ba3SJiri Pirko goto err_dma_rx_ring_bufs_alloc; 104411ce2ba3SJiri Pirko } 104511ce2ba3SJiri Pirko 104611ce2ba3SJiri Pirko err = rocker_dma_rx_ring_skbs_alloc(rocker_port); 104711ce2ba3SJiri Pirko if (err) { 104811ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "failed to alloc rx dma ring skbs\n"); 104911ce2ba3SJiri Pirko goto err_dma_rx_ring_skbs_alloc; 105011ce2ba3SJiri Pirko } 105111ce2ba3SJiri Pirko rocker_dma_ring_pass_to_producer(rocker, &rocker_port->rx_ring); 105211ce2ba3SJiri Pirko 105311ce2ba3SJiri Pirko return 0; 105411ce2ba3SJiri Pirko 105511ce2ba3SJiri Pirko err_dma_rx_ring_skbs_alloc: 105611ce2ba3SJiri Pirko rocker_dma_ring_bufs_free(rocker, &rocker_port->rx_ring, 105711ce2ba3SJiri Pirko PCI_DMA_BIDIRECTIONAL); 105811ce2ba3SJiri Pirko err_dma_rx_ring_bufs_alloc: 105911ce2ba3SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker_port->rx_ring); 106011ce2ba3SJiri Pirko err_dma_rx_ring_create: 106111ce2ba3SJiri Pirko rocker_dma_ring_bufs_free(rocker, &rocker_port->tx_ring, 106211ce2ba3SJiri Pirko PCI_DMA_TODEVICE); 106311ce2ba3SJiri Pirko err_dma_tx_ring_bufs_alloc: 106411ce2ba3SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker_port->tx_ring); 106511ce2ba3SJiri Pirko return err; 106611ce2ba3SJiri Pirko } 106711ce2ba3SJiri Pirko 106811ce2ba3SJiri Pirko static void rocker_port_dma_rings_fini(struct rocker_port *rocker_port) 106911ce2ba3SJiri Pirko { 107011ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 107111ce2ba3SJiri Pirko 107211ce2ba3SJiri Pirko rocker_dma_rx_ring_skbs_free(rocker_port); 107311ce2ba3SJiri Pirko rocker_dma_ring_bufs_free(rocker, &rocker_port->rx_ring, 107411ce2ba3SJiri Pirko PCI_DMA_BIDIRECTIONAL); 107511ce2ba3SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker_port->rx_ring); 107611ce2ba3SJiri Pirko rocker_dma_ring_bufs_free(rocker, &rocker_port->tx_ring, 107711ce2ba3SJiri Pirko PCI_DMA_TODEVICE); 107811ce2ba3SJiri Pirko rocker_dma_ring_destroy(rocker, &rocker_port->tx_ring); 107911ce2ba3SJiri Pirko } 108011ce2ba3SJiri Pirko 108111ce2ba3SJiri Pirko static void rocker_port_set_enable(const struct rocker_port *rocker_port, 108211ce2ba3SJiri Pirko bool enable) 108311ce2ba3SJiri Pirko { 108411ce2ba3SJiri Pirko u64 val = rocker_read64(rocker_port->rocker, PORT_PHYS_ENABLE); 108511ce2ba3SJiri Pirko 108611ce2ba3SJiri Pirko if (enable) 108711ce2ba3SJiri Pirko val |= 1ULL << rocker_port->pport; 108811ce2ba3SJiri Pirko else 108911ce2ba3SJiri Pirko val &= ~(1ULL << rocker_port->pport); 109011ce2ba3SJiri Pirko rocker_write64(rocker_port->rocker, PORT_PHYS_ENABLE, val); 109111ce2ba3SJiri Pirko } 109211ce2ba3SJiri Pirko 109311ce2ba3SJiri Pirko /******************************** 109411ce2ba3SJiri Pirko * Interrupt handler and helpers 109511ce2ba3SJiri Pirko ********************************/ 109611ce2ba3SJiri Pirko 109711ce2ba3SJiri Pirko static irqreturn_t rocker_cmd_irq_handler(int irq, void *dev_id) 109811ce2ba3SJiri Pirko { 109911ce2ba3SJiri Pirko struct rocker *rocker = dev_id; 110011ce2ba3SJiri Pirko const struct rocker_desc_info *desc_info; 110111ce2ba3SJiri Pirko struct rocker_wait *wait; 110211ce2ba3SJiri Pirko u32 credits = 0; 110311ce2ba3SJiri Pirko 110411ce2ba3SJiri Pirko spin_lock(&rocker->cmd_ring_lock); 110511ce2ba3SJiri Pirko while ((desc_info = rocker_desc_tail_get(&rocker->cmd_ring))) { 110611ce2ba3SJiri Pirko wait = rocker_desc_cookie_ptr_get(desc_info); 110711ce2ba3SJiri Pirko if (wait->nowait) { 110811ce2ba3SJiri Pirko rocker_desc_gen_clear(desc_info); 110911ce2ba3SJiri Pirko rocker_wait_destroy(NULL, wait); 111011ce2ba3SJiri Pirko } else { 111111ce2ba3SJiri Pirko rocker_wait_wake_up(wait); 111211ce2ba3SJiri Pirko } 111311ce2ba3SJiri Pirko credits++; 111411ce2ba3SJiri Pirko } 111511ce2ba3SJiri Pirko spin_unlock(&rocker->cmd_ring_lock); 111611ce2ba3SJiri Pirko rocker_dma_ring_credits_set(rocker, &rocker->cmd_ring, credits); 111711ce2ba3SJiri Pirko 111811ce2ba3SJiri Pirko return IRQ_HANDLED; 111911ce2ba3SJiri Pirko } 112011ce2ba3SJiri Pirko 112111ce2ba3SJiri Pirko static void rocker_port_link_up(const struct rocker_port *rocker_port) 112211ce2ba3SJiri Pirko { 112311ce2ba3SJiri Pirko netif_carrier_on(rocker_port->dev); 112411ce2ba3SJiri Pirko netdev_info(rocker_port->dev, "Link is up\n"); 112511ce2ba3SJiri Pirko } 112611ce2ba3SJiri Pirko 112711ce2ba3SJiri Pirko static void rocker_port_link_down(const struct rocker_port *rocker_port) 112811ce2ba3SJiri Pirko { 112911ce2ba3SJiri Pirko netif_carrier_off(rocker_port->dev); 113011ce2ba3SJiri Pirko netdev_info(rocker_port->dev, "Link is down\n"); 113111ce2ba3SJiri Pirko } 113211ce2ba3SJiri Pirko 113311ce2ba3SJiri Pirko static int rocker_event_link_change(const struct rocker *rocker, 113411ce2ba3SJiri Pirko const struct rocker_tlv *info) 113511ce2ba3SJiri Pirko { 113611ce2ba3SJiri Pirko const struct rocker_tlv *attrs[ROCKER_TLV_EVENT_LINK_CHANGED_MAX + 1]; 113711ce2ba3SJiri Pirko unsigned int port_number; 113811ce2ba3SJiri Pirko bool link_up; 113911ce2ba3SJiri Pirko struct rocker_port *rocker_port; 114011ce2ba3SJiri Pirko 114111ce2ba3SJiri Pirko rocker_tlv_parse_nested(attrs, ROCKER_TLV_EVENT_LINK_CHANGED_MAX, info); 114211ce2ba3SJiri Pirko if (!attrs[ROCKER_TLV_EVENT_LINK_CHANGED_PPORT] || 114311ce2ba3SJiri Pirko !attrs[ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP]) 114411ce2ba3SJiri Pirko return -EIO; 114511ce2ba3SJiri Pirko port_number = 114611ce2ba3SJiri Pirko rocker_tlv_get_u32(attrs[ROCKER_TLV_EVENT_LINK_CHANGED_PPORT]) - 1; 114711ce2ba3SJiri Pirko link_up = rocker_tlv_get_u8(attrs[ROCKER_TLV_EVENT_LINK_CHANGED_LINKUP]); 114811ce2ba3SJiri Pirko 114911ce2ba3SJiri Pirko if (port_number >= rocker->port_count) 115011ce2ba3SJiri Pirko return -EINVAL; 115111ce2ba3SJiri Pirko 115211ce2ba3SJiri Pirko rocker_port = rocker->ports[port_number]; 115311ce2ba3SJiri Pirko if (netif_carrier_ok(rocker_port->dev) != link_up) { 115411ce2ba3SJiri Pirko if (link_up) 115511ce2ba3SJiri Pirko rocker_port_link_up(rocker_port); 115611ce2ba3SJiri Pirko else 115711ce2ba3SJiri Pirko rocker_port_link_down(rocker_port); 115811ce2ba3SJiri Pirko } 115911ce2ba3SJiri Pirko 116011ce2ba3SJiri Pirko return 0; 116111ce2ba3SJiri Pirko } 116211ce2ba3SJiri Pirko 116311ce2ba3SJiri Pirko static int rocker_port_fdb(struct rocker_port *rocker_port, 116411ce2ba3SJiri Pirko struct switchdev_trans *trans, 116511ce2ba3SJiri Pirko const unsigned char *addr, 116611ce2ba3SJiri Pirko __be16 vlan_id, int flags); 1167e420114eSJiri Pirko static int rocker_world_port_ev_mac_vlan_seen(struct rocker_port *rocker_port, 1168e420114eSJiri Pirko const unsigned char *addr, 1169e420114eSJiri Pirko __be16 vlan_id); 117011ce2ba3SJiri Pirko 117111ce2ba3SJiri Pirko static int rocker_event_mac_vlan_seen(const struct rocker *rocker, 117211ce2ba3SJiri Pirko const struct rocker_tlv *info) 117311ce2ba3SJiri Pirko { 117411ce2ba3SJiri Pirko const struct rocker_tlv *attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAX + 1]; 117511ce2ba3SJiri Pirko unsigned int port_number; 117611ce2ba3SJiri Pirko struct rocker_port *rocker_port; 117711ce2ba3SJiri Pirko const unsigned char *addr; 117811ce2ba3SJiri Pirko int flags = ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_LEARNED; 117911ce2ba3SJiri Pirko __be16 vlan_id; 1180e420114eSJiri Pirko int err; 118111ce2ba3SJiri Pirko 118211ce2ba3SJiri Pirko rocker_tlv_parse_nested(attrs, ROCKER_TLV_EVENT_MAC_VLAN_MAX, info); 118311ce2ba3SJiri Pirko if (!attrs[ROCKER_TLV_EVENT_MAC_VLAN_PPORT] || 118411ce2ba3SJiri Pirko !attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAC] || 118511ce2ba3SJiri Pirko !attrs[ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID]) 118611ce2ba3SJiri Pirko return -EIO; 118711ce2ba3SJiri Pirko port_number = 118811ce2ba3SJiri Pirko rocker_tlv_get_u32(attrs[ROCKER_TLV_EVENT_MAC_VLAN_PPORT]) - 1; 118911ce2ba3SJiri Pirko addr = rocker_tlv_data(attrs[ROCKER_TLV_EVENT_MAC_VLAN_MAC]); 119011ce2ba3SJiri Pirko vlan_id = rocker_tlv_get_be16(attrs[ROCKER_TLV_EVENT_MAC_VLAN_VLAN_ID]); 119111ce2ba3SJiri Pirko 119211ce2ba3SJiri Pirko if (port_number >= rocker->port_count) 119311ce2ba3SJiri Pirko return -EINVAL; 119411ce2ba3SJiri Pirko 119511ce2ba3SJiri Pirko rocker_port = rocker->ports[port_number]; 119611ce2ba3SJiri Pirko 1197e420114eSJiri Pirko err = rocker_world_port_ev_mac_vlan_seen(rocker_port, addr, vlan_id); 1198e420114eSJiri Pirko if (err) 1199e420114eSJiri Pirko return err; 1200e420114eSJiri Pirko 120111ce2ba3SJiri Pirko if (rocker_port->stp_state != BR_STATE_LEARNING && 120211ce2ba3SJiri Pirko rocker_port->stp_state != BR_STATE_FORWARDING) 120311ce2ba3SJiri Pirko return 0; 120411ce2ba3SJiri Pirko 120511ce2ba3SJiri Pirko return rocker_port_fdb(rocker_port, NULL, addr, vlan_id, flags); 120611ce2ba3SJiri Pirko } 120711ce2ba3SJiri Pirko 120811ce2ba3SJiri Pirko static int rocker_event_process(const struct rocker *rocker, 120911ce2ba3SJiri Pirko const struct rocker_desc_info *desc_info) 121011ce2ba3SJiri Pirko { 121111ce2ba3SJiri Pirko const struct rocker_tlv *attrs[ROCKER_TLV_EVENT_MAX + 1]; 121211ce2ba3SJiri Pirko const struct rocker_tlv *info; 121311ce2ba3SJiri Pirko u16 type; 121411ce2ba3SJiri Pirko 121511ce2ba3SJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_EVENT_MAX, desc_info); 121611ce2ba3SJiri Pirko if (!attrs[ROCKER_TLV_EVENT_TYPE] || 121711ce2ba3SJiri Pirko !attrs[ROCKER_TLV_EVENT_INFO]) 121811ce2ba3SJiri Pirko return -EIO; 121911ce2ba3SJiri Pirko 122011ce2ba3SJiri Pirko type = rocker_tlv_get_u16(attrs[ROCKER_TLV_EVENT_TYPE]); 122111ce2ba3SJiri Pirko info = attrs[ROCKER_TLV_EVENT_INFO]; 122211ce2ba3SJiri Pirko 122311ce2ba3SJiri Pirko switch (type) { 122411ce2ba3SJiri Pirko case ROCKER_TLV_EVENT_TYPE_LINK_CHANGED: 122511ce2ba3SJiri Pirko return rocker_event_link_change(rocker, info); 122611ce2ba3SJiri Pirko case ROCKER_TLV_EVENT_TYPE_MAC_VLAN_SEEN: 122711ce2ba3SJiri Pirko return rocker_event_mac_vlan_seen(rocker, info); 122811ce2ba3SJiri Pirko } 122911ce2ba3SJiri Pirko 123011ce2ba3SJiri Pirko return -EOPNOTSUPP; 123111ce2ba3SJiri Pirko } 123211ce2ba3SJiri Pirko 123311ce2ba3SJiri Pirko static irqreturn_t rocker_event_irq_handler(int irq, void *dev_id) 123411ce2ba3SJiri Pirko { 123511ce2ba3SJiri Pirko struct rocker *rocker = dev_id; 123611ce2ba3SJiri Pirko const struct pci_dev *pdev = rocker->pdev; 123711ce2ba3SJiri Pirko const struct rocker_desc_info *desc_info; 123811ce2ba3SJiri Pirko u32 credits = 0; 123911ce2ba3SJiri Pirko int err; 124011ce2ba3SJiri Pirko 124111ce2ba3SJiri Pirko while ((desc_info = rocker_desc_tail_get(&rocker->event_ring))) { 124211ce2ba3SJiri Pirko err = rocker_desc_err(desc_info); 124311ce2ba3SJiri Pirko if (err) { 124411ce2ba3SJiri Pirko dev_err(&pdev->dev, "event desc received with err %d\n", 124511ce2ba3SJiri Pirko err); 124611ce2ba3SJiri Pirko } else { 124711ce2ba3SJiri Pirko err = rocker_event_process(rocker, desc_info); 124811ce2ba3SJiri Pirko if (err) 124911ce2ba3SJiri Pirko dev_err(&pdev->dev, "event processing failed with err %d\n", 125011ce2ba3SJiri Pirko err); 125111ce2ba3SJiri Pirko } 125211ce2ba3SJiri Pirko rocker_desc_gen_clear(desc_info); 125311ce2ba3SJiri Pirko rocker_desc_head_set(rocker, &rocker->event_ring, desc_info); 125411ce2ba3SJiri Pirko credits++; 125511ce2ba3SJiri Pirko } 125611ce2ba3SJiri Pirko rocker_dma_ring_credits_set(rocker, &rocker->event_ring, credits); 125711ce2ba3SJiri Pirko 125811ce2ba3SJiri Pirko return IRQ_HANDLED; 125911ce2ba3SJiri Pirko } 126011ce2ba3SJiri Pirko 126111ce2ba3SJiri Pirko static irqreturn_t rocker_tx_irq_handler(int irq, void *dev_id) 126211ce2ba3SJiri Pirko { 126311ce2ba3SJiri Pirko struct rocker_port *rocker_port = dev_id; 126411ce2ba3SJiri Pirko 126511ce2ba3SJiri Pirko napi_schedule(&rocker_port->napi_tx); 126611ce2ba3SJiri Pirko return IRQ_HANDLED; 126711ce2ba3SJiri Pirko } 126811ce2ba3SJiri Pirko 126911ce2ba3SJiri Pirko static irqreturn_t rocker_rx_irq_handler(int irq, void *dev_id) 127011ce2ba3SJiri Pirko { 127111ce2ba3SJiri Pirko struct rocker_port *rocker_port = dev_id; 127211ce2ba3SJiri Pirko 127311ce2ba3SJiri Pirko napi_schedule(&rocker_port->napi_rx); 127411ce2ba3SJiri Pirko return IRQ_HANDLED; 127511ce2ba3SJiri Pirko } 127611ce2ba3SJiri Pirko 127711ce2ba3SJiri Pirko /******************** 127811ce2ba3SJiri Pirko * Command interface 127911ce2ba3SJiri Pirko ********************/ 128011ce2ba3SJiri Pirko 128111ce2ba3SJiri Pirko typedef int (*rocker_cmd_prep_cb_t)(const struct rocker_port *rocker_port, 128211ce2ba3SJiri Pirko struct rocker_desc_info *desc_info, 128311ce2ba3SJiri Pirko void *priv); 128411ce2ba3SJiri Pirko 128511ce2ba3SJiri Pirko typedef int (*rocker_cmd_proc_cb_t)(const struct rocker_port *rocker_port, 128611ce2ba3SJiri Pirko const struct rocker_desc_info *desc_info, 128711ce2ba3SJiri Pirko void *priv); 128811ce2ba3SJiri Pirko 128911ce2ba3SJiri Pirko static int rocker_cmd_exec(struct rocker_port *rocker_port, 129011ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 129111ce2ba3SJiri Pirko rocker_cmd_prep_cb_t prepare, void *prepare_priv, 129211ce2ba3SJiri Pirko rocker_cmd_proc_cb_t process, void *process_priv) 129311ce2ba3SJiri Pirko { 129411ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 129511ce2ba3SJiri Pirko struct rocker_desc_info *desc_info; 129611ce2ba3SJiri Pirko struct rocker_wait *wait; 129711ce2ba3SJiri Pirko bool nowait = !!(flags & ROCKER_OP_FLAG_NOWAIT); 129811ce2ba3SJiri Pirko unsigned long lock_flags; 129911ce2ba3SJiri Pirko int err; 130011ce2ba3SJiri Pirko 130111ce2ba3SJiri Pirko wait = rocker_wait_create(rocker_port, trans, flags); 130211ce2ba3SJiri Pirko if (!wait) 130311ce2ba3SJiri Pirko return -ENOMEM; 130411ce2ba3SJiri Pirko wait->nowait = nowait; 130511ce2ba3SJiri Pirko 130611ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->cmd_ring_lock, lock_flags); 130711ce2ba3SJiri Pirko 130811ce2ba3SJiri Pirko desc_info = rocker_desc_head_get(&rocker->cmd_ring); 130911ce2ba3SJiri Pirko if (!desc_info) { 131011ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags); 131111ce2ba3SJiri Pirko err = -EAGAIN; 131211ce2ba3SJiri Pirko goto out; 131311ce2ba3SJiri Pirko } 131411ce2ba3SJiri Pirko 131511ce2ba3SJiri Pirko err = prepare(rocker_port, desc_info, prepare_priv); 131611ce2ba3SJiri Pirko if (err) { 131711ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags); 131811ce2ba3SJiri Pirko goto out; 131911ce2ba3SJiri Pirko } 132011ce2ba3SJiri Pirko 132111ce2ba3SJiri Pirko rocker_desc_cookie_ptr_set(desc_info, wait); 132211ce2ba3SJiri Pirko 132311ce2ba3SJiri Pirko if (!switchdev_trans_ph_prepare(trans)) 132411ce2ba3SJiri Pirko rocker_desc_head_set(rocker, &rocker->cmd_ring, desc_info); 132511ce2ba3SJiri Pirko 132611ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->cmd_ring_lock, lock_flags); 132711ce2ba3SJiri Pirko 132811ce2ba3SJiri Pirko if (nowait) 132911ce2ba3SJiri Pirko return 0; 133011ce2ba3SJiri Pirko 133111ce2ba3SJiri Pirko if (!switchdev_trans_ph_prepare(trans)) 133211ce2ba3SJiri Pirko if (!rocker_wait_event_timeout(wait, HZ / 10)) 133311ce2ba3SJiri Pirko return -EIO; 133411ce2ba3SJiri Pirko 133511ce2ba3SJiri Pirko err = rocker_desc_err(desc_info); 133611ce2ba3SJiri Pirko if (err) 133711ce2ba3SJiri Pirko return err; 133811ce2ba3SJiri Pirko 133911ce2ba3SJiri Pirko if (process) 134011ce2ba3SJiri Pirko err = process(rocker_port, desc_info, process_priv); 134111ce2ba3SJiri Pirko 134211ce2ba3SJiri Pirko rocker_desc_gen_clear(desc_info); 134311ce2ba3SJiri Pirko out: 134411ce2ba3SJiri Pirko rocker_wait_destroy(trans, wait); 134511ce2ba3SJiri Pirko return err; 134611ce2ba3SJiri Pirko } 134711ce2ba3SJiri Pirko 134811ce2ba3SJiri Pirko static int 134911ce2ba3SJiri Pirko rocker_cmd_get_port_settings_prep(const struct rocker_port *rocker_port, 135011ce2ba3SJiri Pirko struct rocker_desc_info *desc_info, 135111ce2ba3SJiri Pirko void *priv) 135211ce2ba3SJiri Pirko { 135311ce2ba3SJiri Pirko struct rocker_tlv *cmd_info; 135411ce2ba3SJiri Pirko 135511ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, 135611ce2ba3SJiri Pirko ROCKER_TLV_CMD_TYPE_GET_PORT_SETTINGS)) 135711ce2ba3SJiri Pirko return -EMSGSIZE; 135811ce2ba3SJiri Pirko cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 135911ce2ba3SJiri Pirko if (!cmd_info) 136011ce2ba3SJiri Pirko return -EMSGSIZE; 136111ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, 136211ce2ba3SJiri Pirko rocker_port->pport)) 136311ce2ba3SJiri Pirko return -EMSGSIZE; 136411ce2ba3SJiri Pirko rocker_tlv_nest_end(desc_info, cmd_info); 136511ce2ba3SJiri Pirko return 0; 136611ce2ba3SJiri Pirko } 136711ce2ba3SJiri Pirko 136811ce2ba3SJiri Pirko static int 136911ce2ba3SJiri Pirko rocker_cmd_get_port_settings_ethtool_proc(const struct rocker_port *rocker_port, 137011ce2ba3SJiri Pirko const struct rocker_desc_info *desc_info, 137111ce2ba3SJiri Pirko void *priv) 137211ce2ba3SJiri Pirko { 137311ce2ba3SJiri Pirko struct ethtool_cmd *ecmd = priv; 137411ce2ba3SJiri Pirko const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1]; 137511ce2ba3SJiri Pirko const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1]; 137611ce2ba3SJiri Pirko u32 speed; 137711ce2ba3SJiri Pirko u8 duplex; 137811ce2ba3SJiri Pirko u8 autoneg; 137911ce2ba3SJiri Pirko 138011ce2ba3SJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info); 138111ce2ba3SJiri Pirko if (!attrs[ROCKER_TLV_CMD_INFO]) 138211ce2ba3SJiri Pirko return -EIO; 138311ce2ba3SJiri Pirko 138411ce2ba3SJiri Pirko rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX, 138511ce2ba3SJiri Pirko attrs[ROCKER_TLV_CMD_INFO]); 138611ce2ba3SJiri Pirko if (!info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED] || 138711ce2ba3SJiri Pirko !info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX] || 138811ce2ba3SJiri Pirko !info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]) 138911ce2ba3SJiri Pirko return -EIO; 139011ce2ba3SJiri Pirko 139111ce2ba3SJiri Pirko speed = rocker_tlv_get_u32(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_SPEED]); 139211ce2ba3SJiri Pirko duplex = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX]); 139311ce2ba3SJiri Pirko autoneg = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG]); 139411ce2ba3SJiri Pirko 139511ce2ba3SJiri Pirko ecmd->transceiver = XCVR_INTERNAL; 139611ce2ba3SJiri Pirko ecmd->supported = SUPPORTED_TP; 139711ce2ba3SJiri Pirko ecmd->phy_address = 0xff; 139811ce2ba3SJiri Pirko ecmd->port = PORT_TP; 139911ce2ba3SJiri Pirko ethtool_cmd_speed_set(ecmd, speed); 140011ce2ba3SJiri Pirko ecmd->duplex = duplex ? DUPLEX_FULL : DUPLEX_HALF; 140111ce2ba3SJiri Pirko ecmd->autoneg = autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; 140211ce2ba3SJiri Pirko 140311ce2ba3SJiri Pirko return 0; 140411ce2ba3SJiri Pirko } 140511ce2ba3SJiri Pirko 140611ce2ba3SJiri Pirko static int 140711ce2ba3SJiri Pirko rocker_cmd_get_port_settings_macaddr_proc(const struct rocker_port *rocker_port, 140811ce2ba3SJiri Pirko const struct rocker_desc_info *desc_info, 140911ce2ba3SJiri Pirko void *priv) 141011ce2ba3SJiri Pirko { 141111ce2ba3SJiri Pirko unsigned char *macaddr = priv; 141211ce2ba3SJiri Pirko const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1]; 141311ce2ba3SJiri Pirko const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1]; 141411ce2ba3SJiri Pirko const struct rocker_tlv *attr; 141511ce2ba3SJiri Pirko 141611ce2ba3SJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info); 141711ce2ba3SJiri Pirko if (!attrs[ROCKER_TLV_CMD_INFO]) 141811ce2ba3SJiri Pirko return -EIO; 141911ce2ba3SJiri Pirko 142011ce2ba3SJiri Pirko rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX, 142111ce2ba3SJiri Pirko attrs[ROCKER_TLV_CMD_INFO]); 142211ce2ba3SJiri Pirko attr = info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR]; 142311ce2ba3SJiri Pirko if (!attr) 142411ce2ba3SJiri Pirko return -EIO; 142511ce2ba3SJiri Pirko 142611ce2ba3SJiri Pirko if (rocker_tlv_len(attr) != ETH_ALEN) 142711ce2ba3SJiri Pirko return -EINVAL; 142811ce2ba3SJiri Pirko 142911ce2ba3SJiri Pirko ether_addr_copy(macaddr, rocker_tlv_data(attr)); 143011ce2ba3SJiri Pirko return 0; 143111ce2ba3SJiri Pirko } 143211ce2ba3SJiri Pirko 1433e1ba3deeSJiri Pirko static int 1434e1ba3deeSJiri Pirko rocker_cmd_get_port_settings_mode_proc(const struct rocker_port *rocker_port, 1435e1ba3deeSJiri Pirko const struct rocker_desc_info *desc_info, 1436e1ba3deeSJiri Pirko void *priv) 1437e1ba3deeSJiri Pirko { 1438e1ba3deeSJiri Pirko u8 *p_mode = priv; 1439e1ba3deeSJiri Pirko const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1]; 1440e1ba3deeSJiri Pirko const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1]; 1441e1ba3deeSJiri Pirko const struct rocker_tlv *attr; 1442e1ba3deeSJiri Pirko 1443e1ba3deeSJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info); 1444e1ba3deeSJiri Pirko if (!attrs[ROCKER_TLV_CMD_INFO]) 1445e1ba3deeSJiri Pirko return -EIO; 1446e1ba3deeSJiri Pirko 1447e1ba3deeSJiri Pirko rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX, 1448e1ba3deeSJiri Pirko attrs[ROCKER_TLV_CMD_INFO]); 1449e1ba3deeSJiri Pirko attr = info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]; 1450e1ba3deeSJiri Pirko if (!attr) 1451e1ba3deeSJiri Pirko return -EIO; 1452e1ba3deeSJiri Pirko 1453e1ba3deeSJiri Pirko *p_mode = rocker_tlv_get_u8(info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MODE]); 1454e1ba3deeSJiri Pirko return 0; 1455e1ba3deeSJiri Pirko } 1456e1ba3deeSJiri Pirko 145711ce2ba3SJiri Pirko struct port_name { 145811ce2ba3SJiri Pirko char *buf; 145911ce2ba3SJiri Pirko size_t len; 146011ce2ba3SJiri Pirko }; 146111ce2ba3SJiri Pirko 146211ce2ba3SJiri Pirko static int 146311ce2ba3SJiri Pirko rocker_cmd_get_port_settings_phys_name_proc(const struct rocker_port *rocker_port, 146411ce2ba3SJiri Pirko const struct rocker_desc_info *desc_info, 146511ce2ba3SJiri Pirko void *priv) 146611ce2ba3SJiri Pirko { 146711ce2ba3SJiri Pirko const struct rocker_tlv *info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_MAX + 1]; 146811ce2ba3SJiri Pirko const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1]; 146911ce2ba3SJiri Pirko struct port_name *name = priv; 147011ce2ba3SJiri Pirko const struct rocker_tlv *attr; 147111ce2ba3SJiri Pirko size_t i, j, len; 147211ce2ba3SJiri Pirko const char *str; 147311ce2ba3SJiri Pirko 147411ce2ba3SJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info); 147511ce2ba3SJiri Pirko if (!attrs[ROCKER_TLV_CMD_INFO]) 147611ce2ba3SJiri Pirko return -EIO; 147711ce2ba3SJiri Pirko 147811ce2ba3SJiri Pirko rocker_tlv_parse_nested(info_attrs, ROCKER_TLV_CMD_PORT_SETTINGS_MAX, 147911ce2ba3SJiri Pirko attrs[ROCKER_TLV_CMD_INFO]); 148011ce2ba3SJiri Pirko attr = info_attrs[ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME]; 148111ce2ba3SJiri Pirko if (!attr) 148211ce2ba3SJiri Pirko return -EIO; 148311ce2ba3SJiri Pirko 148411ce2ba3SJiri Pirko len = min_t(size_t, rocker_tlv_len(attr), name->len); 148511ce2ba3SJiri Pirko str = rocker_tlv_data(attr); 148611ce2ba3SJiri Pirko 148711ce2ba3SJiri Pirko /* make sure name only contains alphanumeric characters */ 148811ce2ba3SJiri Pirko for (i = j = 0; i < len; ++i) { 148911ce2ba3SJiri Pirko if (isalnum(str[i])) { 149011ce2ba3SJiri Pirko name->buf[j] = str[i]; 149111ce2ba3SJiri Pirko j++; 149211ce2ba3SJiri Pirko } 149311ce2ba3SJiri Pirko } 149411ce2ba3SJiri Pirko 149511ce2ba3SJiri Pirko if (j == 0) 149611ce2ba3SJiri Pirko return -EIO; 149711ce2ba3SJiri Pirko 149811ce2ba3SJiri Pirko name->buf[j] = '\0'; 149911ce2ba3SJiri Pirko 150011ce2ba3SJiri Pirko return 0; 150111ce2ba3SJiri Pirko } 150211ce2ba3SJiri Pirko 150311ce2ba3SJiri Pirko static int 150411ce2ba3SJiri Pirko rocker_cmd_set_port_settings_ethtool_prep(const struct rocker_port *rocker_port, 150511ce2ba3SJiri Pirko struct rocker_desc_info *desc_info, 150611ce2ba3SJiri Pirko void *priv) 150711ce2ba3SJiri Pirko { 150811ce2ba3SJiri Pirko struct ethtool_cmd *ecmd = priv; 150911ce2ba3SJiri Pirko struct rocker_tlv *cmd_info; 151011ce2ba3SJiri Pirko 151111ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, 151211ce2ba3SJiri Pirko ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS)) 151311ce2ba3SJiri Pirko return -EMSGSIZE; 151411ce2ba3SJiri Pirko cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 151511ce2ba3SJiri Pirko if (!cmd_info) 151611ce2ba3SJiri Pirko return -EMSGSIZE; 151711ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, 151811ce2ba3SJiri Pirko rocker_port->pport)) 151911ce2ba3SJiri Pirko return -EMSGSIZE; 152011ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_SPEED, 152111ce2ba3SJiri Pirko ethtool_cmd_speed(ecmd))) 152211ce2ba3SJiri Pirko return -EMSGSIZE; 152311ce2ba3SJiri Pirko if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_DUPLEX, 152411ce2ba3SJiri Pirko ecmd->duplex)) 152511ce2ba3SJiri Pirko return -EMSGSIZE; 152611ce2ba3SJiri Pirko if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_AUTONEG, 152711ce2ba3SJiri Pirko ecmd->autoneg)) 152811ce2ba3SJiri Pirko return -EMSGSIZE; 152911ce2ba3SJiri Pirko rocker_tlv_nest_end(desc_info, cmd_info); 153011ce2ba3SJiri Pirko return 0; 153111ce2ba3SJiri Pirko } 153211ce2ba3SJiri Pirko 153311ce2ba3SJiri Pirko static int 153411ce2ba3SJiri Pirko rocker_cmd_set_port_settings_macaddr_prep(const struct rocker_port *rocker_port, 153511ce2ba3SJiri Pirko struct rocker_desc_info *desc_info, 153611ce2ba3SJiri Pirko void *priv) 153711ce2ba3SJiri Pirko { 153811ce2ba3SJiri Pirko const unsigned char *macaddr = priv; 153911ce2ba3SJiri Pirko struct rocker_tlv *cmd_info; 154011ce2ba3SJiri Pirko 154111ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, 154211ce2ba3SJiri Pirko ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS)) 154311ce2ba3SJiri Pirko return -EMSGSIZE; 154411ce2ba3SJiri Pirko cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 154511ce2ba3SJiri Pirko if (!cmd_info) 154611ce2ba3SJiri Pirko return -EMSGSIZE; 154711ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, 154811ce2ba3SJiri Pirko rocker_port->pport)) 154911ce2ba3SJiri Pirko return -EMSGSIZE; 155011ce2ba3SJiri Pirko if (rocker_tlv_put(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR, 155111ce2ba3SJiri Pirko ETH_ALEN, macaddr)) 155211ce2ba3SJiri Pirko return -EMSGSIZE; 155311ce2ba3SJiri Pirko rocker_tlv_nest_end(desc_info, cmd_info); 155411ce2ba3SJiri Pirko return 0; 155511ce2ba3SJiri Pirko } 155611ce2ba3SJiri Pirko 155711ce2ba3SJiri Pirko static int 155811ce2ba3SJiri Pirko rocker_cmd_set_port_settings_mtu_prep(const struct rocker_port *rocker_port, 155911ce2ba3SJiri Pirko struct rocker_desc_info *desc_info, 156011ce2ba3SJiri Pirko void *priv) 156111ce2ba3SJiri Pirko { 156211ce2ba3SJiri Pirko int mtu = *(int *)priv; 156311ce2ba3SJiri Pirko struct rocker_tlv *cmd_info; 156411ce2ba3SJiri Pirko 156511ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, 156611ce2ba3SJiri Pirko ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS)) 156711ce2ba3SJiri Pirko return -EMSGSIZE; 156811ce2ba3SJiri Pirko cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 156911ce2ba3SJiri Pirko if (!cmd_info) 157011ce2ba3SJiri Pirko return -EMSGSIZE; 157111ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, 157211ce2ba3SJiri Pirko rocker_port->pport)) 157311ce2ba3SJiri Pirko return -EMSGSIZE; 157411ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_MTU, 157511ce2ba3SJiri Pirko mtu)) 157611ce2ba3SJiri Pirko return -EMSGSIZE; 157711ce2ba3SJiri Pirko rocker_tlv_nest_end(desc_info, cmd_info); 157811ce2ba3SJiri Pirko return 0; 157911ce2ba3SJiri Pirko } 158011ce2ba3SJiri Pirko 158111ce2ba3SJiri Pirko static int 158211ce2ba3SJiri Pirko rocker_cmd_set_port_learning_prep(const struct rocker_port *rocker_port, 158311ce2ba3SJiri Pirko struct rocker_desc_info *desc_info, 158411ce2ba3SJiri Pirko void *priv) 158511ce2ba3SJiri Pirko { 1586c1fe922eSJiri Pirko bool learning = *(bool *)priv; 158711ce2ba3SJiri Pirko struct rocker_tlv *cmd_info; 158811ce2ba3SJiri Pirko 158911ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, 159011ce2ba3SJiri Pirko ROCKER_TLV_CMD_TYPE_SET_PORT_SETTINGS)) 159111ce2ba3SJiri Pirko return -EMSGSIZE; 159211ce2ba3SJiri Pirko cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 159311ce2ba3SJiri Pirko if (!cmd_info) 159411ce2ba3SJiri Pirko return -EMSGSIZE; 159511ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_PPORT, 159611ce2ba3SJiri Pirko rocker_port->pport)) 159711ce2ba3SJiri Pirko return -EMSGSIZE; 159811ce2ba3SJiri Pirko if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING, 1599c1fe922eSJiri Pirko learning)) 160011ce2ba3SJiri Pirko return -EMSGSIZE; 160111ce2ba3SJiri Pirko rocker_tlv_nest_end(desc_info, cmd_info); 160211ce2ba3SJiri Pirko return 0; 160311ce2ba3SJiri Pirko } 160411ce2ba3SJiri Pirko 160511ce2ba3SJiri Pirko static int rocker_cmd_get_port_settings_ethtool(struct rocker_port *rocker_port, 160611ce2ba3SJiri Pirko struct ethtool_cmd *ecmd) 160711ce2ba3SJiri Pirko { 160811ce2ba3SJiri Pirko return rocker_cmd_exec(rocker_port, NULL, 0, 160911ce2ba3SJiri Pirko rocker_cmd_get_port_settings_prep, NULL, 161011ce2ba3SJiri Pirko rocker_cmd_get_port_settings_ethtool_proc, 161111ce2ba3SJiri Pirko ecmd); 161211ce2ba3SJiri Pirko } 161311ce2ba3SJiri Pirko 161411ce2ba3SJiri Pirko static int rocker_cmd_get_port_settings_macaddr(struct rocker_port *rocker_port, 161511ce2ba3SJiri Pirko unsigned char *macaddr) 161611ce2ba3SJiri Pirko { 161711ce2ba3SJiri Pirko return rocker_cmd_exec(rocker_port, NULL, 0, 161811ce2ba3SJiri Pirko rocker_cmd_get_port_settings_prep, NULL, 161911ce2ba3SJiri Pirko rocker_cmd_get_port_settings_macaddr_proc, 162011ce2ba3SJiri Pirko macaddr); 162111ce2ba3SJiri Pirko } 162211ce2ba3SJiri Pirko 1623e1ba3deeSJiri Pirko static int rocker_cmd_get_port_settings_mode(struct rocker_port *rocker_port, 1624e1ba3deeSJiri Pirko u8 *p_mode) 1625e1ba3deeSJiri Pirko { 1626e1ba3deeSJiri Pirko return rocker_cmd_exec(rocker_port, NULL, 0, 1627e1ba3deeSJiri Pirko rocker_cmd_get_port_settings_prep, NULL, 1628e1ba3deeSJiri Pirko rocker_cmd_get_port_settings_mode_proc, p_mode); 1629e1ba3deeSJiri Pirko } 1630e1ba3deeSJiri Pirko 163111ce2ba3SJiri Pirko static int rocker_cmd_set_port_settings_ethtool(struct rocker_port *rocker_port, 163211ce2ba3SJiri Pirko struct ethtool_cmd *ecmd) 163311ce2ba3SJiri Pirko { 163411ce2ba3SJiri Pirko return rocker_cmd_exec(rocker_port, NULL, 0, 163511ce2ba3SJiri Pirko rocker_cmd_set_port_settings_ethtool_prep, 163611ce2ba3SJiri Pirko ecmd, NULL, NULL); 163711ce2ba3SJiri Pirko } 163811ce2ba3SJiri Pirko 163911ce2ba3SJiri Pirko static int rocker_cmd_set_port_settings_macaddr(struct rocker_port *rocker_port, 164011ce2ba3SJiri Pirko unsigned char *macaddr) 164111ce2ba3SJiri Pirko { 164211ce2ba3SJiri Pirko return rocker_cmd_exec(rocker_port, NULL, 0, 164311ce2ba3SJiri Pirko rocker_cmd_set_port_settings_macaddr_prep, 164411ce2ba3SJiri Pirko macaddr, NULL, NULL); 164511ce2ba3SJiri Pirko } 164611ce2ba3SJiri Pirko 164711ce2ba3SJiri Pirko static int rocker_cmd_set_port_settings_mtu(struct rocker_port *rocker_port, 164811ce2ba3SJiri Pirko int mtu) 164911ce2ba3SJiri Pirko { 165011ce2ba3SJiri Pirko return rocker_cmd_exec(rocker_port, NULL, 0, 165111ce2ba3SJiri Pirko rocker_cmd_set_port_settings_mtu_prep, 165211ce2ba3SJiri Pirko &mtu, NULL, NULL); 165311ce2ba3SJiri Pirko } 165411ce2ba3SJiri Pirko 165511ce2ba3SJiri Pirko static int rocker_port_set_learning(struct rocker_port *rocker_port, 1656c1fe922eSJiri Pirko struct switchdev_trans *trans, 1657c1fe922eSJiri Pirko bool learning) 165811ce2ba3SJiri Pirko { 165911ce2ba3SJiri Pirko return rocker_cmd_exec(rocker_port, trans, 0, 166011ce2ba3SJiri Pirko rocker_cmd_set_port_learning_prep, 1661c1fe922eSJiri Pirko &learning, NULL, NULL); 166211ce2ba3SJiri Pirko } 166311ce2ba3SJiri Pirko 1664e420114eSJiri Pirko /********************** 1665e420114eSJiri Pirko * Worlds manipulation 1666e420114eSJiri Pirko **********************/ 1667e420114eSJiri Pirko 1668e420114eSJiri Pirko static struct rocker_world_ops *rocker_world_ops[] = { 1669e420114eSJiri Pirko &rocker_ofdpa_ops, 1670e420114eSJiri Pirko }; 1671e420114eSJiri Pirko 1672e420114eSJiri Pirko #define ROCKER_WORLD_OPS_LEN ARRAY_SIZE(rocker_world_ops) 1673e420114eSJiri Pirko 1674e420114eSJiri Pirko static struct rocker_world_ops *rocker_world_ops_find(u8 mode) 1675e420114eSJiri Pirko { 1676e420114eSJiri Pirko int i; 1677e420114eSJiri Pirko 1678e420114eSJiri Pirko for (i = 0; i < ROCKER_WORLD_OPS_LEN; i++) 1679e420114eSJiri Pirko if (rocker_world_ops[i]->mode == mode) 1680e420114eSJiri Pirko return rocker_world_ops[i]; 1681e420114eSJiri Pirko return NULL; 1682e420114eSJiri Pirko } 1683e420114eSJiri Pirko 1684e420114eSJiri Pirko static int rocker_world_init(struct rocker *rocker, u8 mode) 1685e420114eSJiri Pirko { 1686e420114eSJiri Pirko struct rocker_world_ops *wops; 1687e420114eSJiri Pirko int err; 1688e420114eSJiri Pirko 1689e420114eSJiri Pirko wops = rocker_world_ops_find(mode); 1690e420114eSJiri Pirko if (!wops) { 1691e420114eSJiri Pirko dev_err(&rocker->pdev->dev, "port mode \"%d\" is not supported\n", 1692e420114eSJiri Pirko mode); 1693e420114eSJiri Pirko return -EINVAL; 1694e420114eSJiri Pirko } 1695e420114eSJiri Pirko rocker->wops = wops; 1696e420114eSJiri Pirko rocker->wpriv = kzalloc(wops->priv_size, GFP_KERNEL); 1697e420114eSJiri Pirko if (!rocker->wpriv) 1698e420114eSJiri Pirko return -ENOMEM; 1699e420114eSJiri Pirko if (!wops->init) 1700e420114eSJiri Pirko return 0; 1701e420114eSJiri Pirko err = wops->init(rocker); 1702e420114eSJiri Pirko if (err) 1703e420114eSJiri Pirko kfree(rocker->wpriv); 1704e420114eSJiri Pirko return err; 1705e420114eSJiri Pirko } 1706e420114eSJiri Pirko 1707e420114eSJiri Pirko static void rocker_world_fini(struct rocker *rocker) 1708e420114eSJiri Pirko { 1709e420114eSJiri Pirko struct rocker_world_ops *wops = rocker->wops; 1710e420114eSJiri Pirko 1711e420114eSJiri Pirko if (!wops || !wops->fini) 1712e420114eSJiri Pirko return; 1713e420114eSJiri Pirko wops->fini(rocker); 1714e420114eSJiri Pirko kfree(rocker->wpriv); 1715e420114eSJiri Pirko } 1716e420114eSJiri Pirko 1717e420114eSJiri Pirko static int rocker_world_check_init(struct rocker_port *rocker_port) 1718e420114eSJiri Pirko { 1719e420114eSJiri Pirko struct rocker *rocker = rocker_port->rocker; 1720e420114eSJiri Pirko u8 mode; 1721e420114eSJiri Pirko int err; 1722e420114eSJiri Pirko 1723e420114eSJiri Pirko err = rocker_cmd_get_port_settings_mode(rocker_port, &mode); 1724e420114eSJiri Pirko if (err) { 1725e420114eSJiri Pirko dev_err(&rocker->pdev->dev, "failed to get port mode\n"); 1726e420114eSJiri Pirko return err; 1727e420114eSJiri Pirko } 1728e420114eSJiri Pirko if (rocker->wops) { 1729e420114eSJiri Pirko if (rocker->wops->mode != mode) { 1730e420114eSJiri Pirko dev_err(&rocker->pdev->dev, "hardware has ports in different worlds, which is not supported\n"); 1731e420114eSJiri Pirko return err; 1732e420114eSJiri Pirko } 1733e420114eSJiri Pirko return 0; 1734e420114eSJiri Pirko } 1735e420114eSJiri Pirko return rocker_world_init(rocker, mode); 1736e420114eSJiri Pirko } 1737e420114eSJiri Pirko 1738e420114eSJiri Pirko static int rocker_world_port_pre_init(struct rocker_port *rocker_port) 1739e420114eSJiri Pirko { 1740e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1741e420114eSJiri Pirko int err; 1742e420114eSJiri Pirko 1743e420114eSJiri Pirko rocker_port->wpriv = kzalloc(wops->port_priv_size, GFP_KERNEL); 1744e420114eSJiri Pirko if (!rocker_port->wpriv) 1745e420114eSJiri Pirko return -ENOMEM; 1746e420114eSJiri Pirko if (!wops->port_pre_init) 1747e420114eSJiri Pirko return 0; 1748e420114eSJiri Pirko err = wops->port_pre_init(rocker_port); 1749e420114eSJiri Pirko if (err) 1750e420114eSJiri Pirko kfree(rocker_port->wpriv); 1751e420114eSJiri Pirko return 0; 1752e420114eSJiri Pirko } 1753e420114eSJiri Pirko 1754e420114eSJiri Pirko static int rocker_world_port_init(struct rocker_port *rocker_port) 1755e420114eSJiri Pirko { 1756e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1757e420114eSJiri Pirko 1758e420114eSJiri Pirko if (!wops->port_init) 1759e420114eSJiri Pirko return 0; 1760e420114eSJiri Pirko return wops->port_init(rocker_port); 1761e420114eSJiri Pirko } 1762e420114eSJiri Pirko 1763e420114eSJiri Pirko static void rocker_world_port_fini(struct rocker_port *rocker_port) 1764e420114eSJiri Pirko { 1765e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1766e420114eSJiri Pirko 1767e420114eSJiri Pirko if (!wops->port_fini) 1768e420114eSJiri Pirko return; 1769e420114eSJiri Pirko wops->port_fini(rocker_port); 1770e420114eSJiri Pirko } 1771e420114eSJiri Pirko 1772e420114eSJiri Pirko static void rocker_world_port_post_fini(struct rocker_port *rocker_port) 1773e420114eSJiri Pirko { 1774e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1775e420114eSJiri Pirko 1776e420114eSJiri Pirko if (!wops->port_post_fini) 1777e420114eSJiri Pirko return; 1778e420114eSJiri Pirko wops->port_post_fini(rocker_port); 1779e420114eSJiri Pirko kfree(rocker_port->wpriv); 1780e420114eSJiri Pirko } 1781e420114eSJiri Pirko 1782e420114eSJiri Pirko static int rocker_world_port_open(struct rocker_port *rocker_port) 1783e420114eSJiri Pirko { 1784e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1785e420114eSJiri Pirko 1786e420114eSJiri Pirko if (!wops->port_open) 1787e420114eSJiri Pirko return 0; 1788e420114eSJiri Pirko return wops->port_open(rocker_port); 1789e420114eSJiri Pirko } 1790e420114eSJiri Pirko 1791e420114eSJiri Pirko static void rocker_world_port_stop(struct rocker_port *rocker_port) 1792e420114eSJiri Pirko { 1793e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1794e420114eSJiri Pirko 1795e420114eSJiri Pirko if (!wops->port_stop) 1796e420114eSJiri Pirko return; 1797e420114eSJiri Pirko wops->port_stop(rocker_port); 1798e420114eSJiri Pirko } 1799e420114eSJiri Pirko 1800e420114eSJiri Pirko static int rocker_world_port_attr_stp_state_set(struct rocker_port *rocker_port, 1801e420114eSJiri Pirko u8 state, 1802e420114eSJiri Pirko struct switchdev_trans *trans) 1803e420114eSJiri Pirko { 1804e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1805e420114eSJiri Pirko 1806e420114eSJiri Pirko if (!wops->port_attr_stp_state_set) 1807e420114eSJiri Pirko return 0; 1808e420114eSJiri Pirko return wops->port_attr_stp_state_set(rocker_port, state, trans); 1809e420114eSJiri Pirko } 1810e420114eSJiri Pirko 1811e420114eSJiri Pirko static int 1812e420114eSJiri Pirko rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port, 1813e420114eSJiri Pirko unsigned long brport_flags, 1814e420114eSJiri Pirko struct switchdev_trans *trans) 1815e420114eSJiri Pirko { 1816e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1817e420114eSJiri Pirko 1818e420114eSJiri Pirko if (!wops->port_attr_bridge_flags_set) 1819e420114eSJiri Pirko return 0; 1820e420114eSJiri Pirko return wops->port_attr_bridge_flags_set(rocker_port, brport_flags, 1821e420114eSJiri Pirko trans); 1822e420114eSJiri Pirko } 1823e420114eSJiri Pirko 1824e420114eSJiri Pirko static int 1825e420114eSJiri Pirko rocker_world_port_attr_bridge_flags_get(const struct rocker_port *rocker_port, 1826e420114eSJiri Pirko unsigned long *p_brport_flags) 1827e420114eSJiri Pirko { 1828e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1829e420114eSJiri Pirko 1830e420114eSJiri Pirko if (!wops->port_attr_bridge_flags_get) 1831e420114eSJiri Pirko return 0; 1832e420114eSJiri Pirko return wops->port_attr_bridge_flags_get(rocker_port, p_brport_flags); 1833e420114eSJiri Pirko } 1834e420114eSJiri Pirko 1835e420114eSJiri Pirko static int 1836e420114eSJiri Pirko rocker_world_port_attr_bridge_ageing_time_set(struct rocker_port *rocker_port, 1837e420114eSJiri Pirko u32 ageing_time, 1838e420114eSJiri Pirko struct switchdev_trans *trans) 1839e420114eSJiri Pirko 1840e420114eSJiri Pirko { 1841e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1842e420114eSJiri Pirko 1843e420114eSJiri Pirko if (!wops->port_attr_bridge_ageing_time_set) 1844e420114eSJiri Pirko return 0; 1845e420114eSJiri Pirko return wops->port_attr_bridge_ageing_time_set(rocker_port, ageing_time, 1846e420114eSJiri Pirko trans); 1847e420114eSJiri Pirko } 1848e420114eSJiri Pirko 1849e420114eSJiri Pirko static int 1850e420114eSJiri Pirko rocker_world_port_obj_vlan_add(struct rocker_port *rocker_port, 1851e420114eSJiri Pirko const struct switchdev_obj_port_vlan *vlan, 1852e420114eSJiri Pirko struct switchdev_trans *trans) 1853e420114eSJiri Pirko { 1854e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1855e420114eSJiri Pirko 1856e420114eSJiri Pirko if (!wops->port_obj_vlan_add) 1857e420114eSJiri Pirko return 0; 1858e420114eSJiri Pirko return wops->port_obj_vlan_add(rocker_port, vlan, trans); 1859e420114eSJiri Pirko } 1860e420114eSJiri Pirko 1861e420114eSJiri Pirko static int 1862e420114eSJiri Pirko rocker_world_port_obj_vlan_del(struct rocker_port *rocker_port, 1863e420114eSJiri Pirko const struct switchdev_obj_port_vlan *vlan) 1864e420114eSJiri Pirko { 1865e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1866e420114eSJiri Pirko 1867e420114eSJiri Pirko if (!wops->port_obj_vlan_del) 1868e420114eSJiri Pirko return 0; 1869e420114eSJiri Pirko return wops->port_obj_vlan_del(rocker_port, vlan); 1870e420114eSJiri Pirko } 1871e420114eSJiri Pirko 1872e420114eSJiri Pirko static int 1873e420114eSJiri Pirko rocker_world_port_obj_vlan_dump(const struct rocker_port *rocker_port, 1874e420114eSJiri Pirko struct switchdev_obj_port_vlan *vlan, 1875e420114eSJiri Pirko switchdev_obj_dump_cb_t *cb) 1876e420114eSJiri Pirko { 1877e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1878e420114eSJiri Pirko 1879e420114eSJiri Pirko if (!wops->port_obj_vlan_dump) 1880e420114eSJiri Pirko return 0; 1881e420114eSJiri Pirko return wops->port_obj_vlan_dump(rocker_port, vlan, cb); 1882e420114eSJiri Pirko } 1883e420114eSJiri Pirko 1884e420114eSJiri Pirko static int 1885e420114eSJiri Pirko rocker_world_port_obj_fib4_add(struct rocker_port *rocker_port, 1886e420114eSJiri Pirko const struct switchdev_obj_ipv4_fib *fib4, 1887e420114eSJiri Pirko struct switchdev_trans *trans) 1888e420114eSJiri Pirko { 1889e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1890e420114eSJiri Pirko 1891e420114eSJiri Pirko if (!wops->port_obj_fib4_add) 1892e420114eSJiri Pirko return 0; 1893e420114eSJiri Pirko return wops->port_obj_fib4_add(rocker_port, fib4, trans); 1894e420114eSJiri Pirko } 1895e420114eSJiri Pirko 1896e420114eSJiri Pirko static int 1897e420114eSJiri Pirko rocker_world_port_obj_fib4_del(struct rocker_port *rocker_port, 1898e420114eSJiri Pirko const struct switchdev_obj_ipv4_fib *fib4) 1899e420114eSJiri Pirko { 1900e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1901e420114eSJiri Pirko 1902e420114eSJiri Pirko if (!wops->port_obj_fib4_del) 1903e420114eSJiri Pirko return 0; 1904e420114eSJiri Pirko return wops->port_obj_fib4_del(rocker_port, fib4); 1905e420114eSJiri Pirko } 1906e420114eSJiri Pirko 1907e420114eSJiri Pirko static int 1908e420114eSJiri Pirko rocker_world_port_obj_fdb_add(struct rocker_port *rocker_port, 1909e420114eSJiri Pirko const struct switchdev_obj_port_fdb *fdb, 1910e420114eSJiri Pirko struct switchdev_trans *trans) 1911e420114eSJiri Pirko { 1912e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1913e420114eSJiri Pirko 1914e420114eSJiri Pirko if (!wops->port_obj_fdb_add) 1915e420114eSJiri Pirko return 0; 1916e420114eSJiri Pirko return wops->port_obj_fdb_add(rocker_port, fdb, trans); 1917e420114eSJiri Pirko } 1918e420114eSJiri Pirko 1919e420114eSJiri Pirko static int 1920e420114eSJiri Pirko rocker_world_port_obj_fdb_del(struct rocker_port *rocker_port, 1921e420114eSJiri Pirko const struct switchdev_obj_port_fdb *fdb) 1922e420114eSJiri Pirko { 1923e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1924e420114eSJiri Pirko 1925e420114eSJiri Pirko if (!wops->port_obj_fdb_del) 1926e420114eSJiri Pirko return 0; 1927e420114eSJiri Pirko return wops->port_obj_fdb_del(rocker_port, fdb); 1928e420114eSJiri Pirko } 1929e420114eSJiri Pirko 1930e420114eSJiri Pirko static int 1931e420114eSJiri Pirko rocker_world_port_obj_fdb_dump(const struct rocker_port *rocker_port, 1932e420114eSJiri Pirko struct switchdev_obj_port_fdb *fdb, 1933e420114eSJiri Pirko switchdev_obj_dump_cb_t *cb) 1934e420114eSJiri Pirko { 1935e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1936e420114eSJiri Pirko 1937e420114eSJiri Pirko if (!wops->port_obj_fdb_dump) 1938e420114eSJiri Pirko return 0; 1939e420114eSJiri Pirko return wops->port_obj_fdb_dump(rocker_port, fdb, cb); 1940e420114eSJiri Pirko } 1941e420114eSJiri Pirko 1942e420114eSJiri Pirko static int rocker_world_port_master_linked(struct rocker_port *rocker_port, 1943e420114eSJiri Pirko struct net_device *master) 1944e420114eSJiri Pirko { 1945e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1946e420114eSJiri Pirko 1947e420114eSJiri Pirko if (!wops->port_master_linked) 1948e420114eSJiri Pirko return 0; 1949e420114eSJiri Pirko return wops->port_master_linked(rocker_port, master); 1950e420114eSJiri Pirko } 1951e420114eSJiri Pirko 1952e420114eSJiri Pirko static int rocker_world_port_master_unlinked(struct rocker_port *rocker_port, 1953e420114eSJiri Pirko struct net_device *master) 1954e420114eSJiri Pirko { 1955e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1956e420114eSJiri Pirko 1957e420114eSJiri Pirko if (!wops->port_master_unlinked) 1958e420114eSJiri Pirko return 0; 1959e420114eSJiri Pirko return wops->port_master_unlinked(rocker_port, master); 1960e420114eSJiri Pirko } 1961e420114eSJiri Pirko 1962e420114eSJiri Pirko static int rocker_world_port_neigh_update(struct rocker_port *rocker_port, 1963e420114eSJiri Pirko struct neighbour *n) 1964e420114eSJiri Pirko { 1965e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1966e420114eSJiri Pirko 1967e420114eSJiri Pirko if (!wops->port_neigh_update) 1968e420114eSJiri Pirko return 0; 1969e420114eSJiri Pirko return wops->port_neigh_update(rocker_port, n); 1970e420114eSJiri Pirko } 1971e420114eSJiri Pirko 1972e420114eSJiri Pirko static int rocker_world_port_neigh_destroy(struct rocker_port *rocker_port, 1973e420114eSJiri Pirko struct neighbour *n) 1974e420114eSJiri Pirko { 1975e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1976e420114eSJiri Pirko 1977e420114eSJiri Pirko if (!wops->port_neigh_destroy) 1978e420114eSJiri Pirko return 0; 1979e420114eSJiri Pirko return wops->port_neigh_destroy(rocker_port, n); 1980e420114eSJiri Pirko } 1981e420114eSJiri Pirko 1982e420114eSJiri Pirko static int rocker_world_port_ev_mac_vlan_seen(struct rocker_port *rocker_port, 1983e420114eSJiri Pirko const unsigned char *addr, 1984e420114eSJiri Pirko __be16 vlan_id) 1985e420114eSJiri Pirko { 1986e420114eSJiri Pirko struct rocker_world_ops *wops = rocker_port->rocker->wops; 1987e420114eSJiri Pirko 1988e420114eSJiri Pirko if (!wops->port_ev_mac_vlan_seen) 1989e420114eSJiri Pirko return 0; 1990e420114eSJiri Pirko return wops->port_ev_mac_vlan_seen(rocker_port, addr, vlan_id); 1991e420114eSJiri Pirko } 1992e420114eSJiri Pirko 199311ce2ba3SJiri Pirko static int 199411ce2ba3SJiri Pirko rocker_cmd_flow_tbl_add_ig_port(struct rocker_desc_info *desc_info, 199511ce2ba3SJiri Pirko const struct rocker_flow_tbl_entry *entry) 199611ce2ba3SJiri Pirko { 199711ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT, 199811ce2ba3SJiri Pirko entry->key.ig_port.in_pport)) 199911ce2ba3SJiri Pirko return -EMSGSIZE; 200011ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT_MASK, 200111ce2ba3SJiri Pirko entry->key.ig_port.in_pport_mask)) 200211ce2ba3SJiri Pirko return -EMSGSIZE; 200311ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID, 200411ce2ba3SJiri Pirko entry->key.ig_port.goto_tbl)) 200511ce2ba3SJiri Pirko return -EMSGSIZE; 200611ce2ba3SJiri Pirko 200711ce2ba3SJiri Pirko return 0; 200811ce2ba3SJiri Pirko } 200911ce2ba3SJiri Pirko 201011ce2ba3SJiri Pirko static int 201111ce2ba3SJiri Pirko rocker_cmd_flow_tbl_add_vlan(struct rocker_desc_info *desc_info, 201211ce2ba3SJiri Pirko const struct rocker_flow_tbl_entry *entry) 201311ce2ba3SJiri Pirko { 201411ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT, 201511ce2ba3SJiri Pirko entry->key.vlan.in_pport)) 201611ce2ba3SJiri Pirko return -EMSGSIZE; 201711ce2ba3SJiri Pirko if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID, 201811ce2ba3SJiri Pirko entry->key.vlan.vlan_id)) 201911ce2ba3SJiri Pirko return -EMSGSIZE; 202011ce2ba3SJiri Pirko if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID_MASK, 202111ce2ba3SJiri Pirko entry->key.vlan.vlan_id_mask)) 202211ce2ba3SJiri Pirko return -EMSGSIZE; 202311ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID, 202411ce2ba3SJiri Pirko entry->key.vlan.goto_tbl)) 202511ce2ba3SJiri Pirko return -EMSGSIZE; 202611ce2ba3SJiri Pirko if (entry->key.vlan.untagged && 202711ce2ba3SJiri Pirko rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_NEW_VLAN_ID, 202811ce2ba3SJiri Pirko entry->key.vlan.new_vlan_id)) 202911ce2ba3SJiri Pirko return -EMSGSIZE; 203011ce2ba3SJiri Pirko 203111ce2ba3SJiri Pirko return 0; 203211ce2ba3SJiri Pirko } 203311ce2ba3SJiri Pirko 203411ce2ba3SJiri Pirko static int 203511ce2ba3SJiri Pirko rocker_cmd_flow_tbl_add_term_mac(struct rocker_desc_info *desc_info, 203611ce2ba3SJiri Pirko const struct rocker_flow_tbl_entry *entry) 203711ce2ba3SJiri Pirko { 203811ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT, 203911ce2ba3SJiri Pirko entry->key.term_mac.in_pport)) 204011ce2ba3SJiri Pirko return -EMSGSIZE; 204111ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT_MASK, 204211ce2ba3SJiri Pirko entry->key.term_mac.in_pport_mask)) 204311ce2ba3SJiri Pirko return -EMSGSIZE; 204411ce2ba3SJiri Pirko if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_ETHERTYPE, 204511ce2ba3SJiri Pirko entry->key.term_mac.eth_type)) 204611ce2ba3SJiri Pirko return -EMSGSIZE; 204711ce2ba3SJiri Pirko if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC, 204811ce2ba3SJiri Pirko ETH_ALEN, entry->key.term_mac.eth_dst)) 204911ce2ba3SJiri Pirko return -EMSGSIZE; 205011ce2ba3SJiri Pirko if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC_MASK, 205111ce2ba3SJiri Pirko ETH_ALEN, entry->key.term_mac.eth_dst_mask)) 205211ce2ba3SJiri Pirko return -EMSGSIZE; 205311ce2ba3SJiri Pirko if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID, 205411ce2ba3SJiri Pirko entry->key.term_mac.vlan_id)) 205511ce2ba3SJiri Pirko return -EMSGSIZE; 205611ce2ba3SJiri Pirko if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID_MASK, 205711ce2ba3SJiri Pirko entry->key.term_mac.vlan_id_mask)) 205811ce2ba3SJiri Pirko return -EMSGSIZE; 205911ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID, 206011ce2ba3SJiri Pirko entry->key.term_mac.goto_tbl)) 206111ce2ba3SJiri Pirko return -EMSGSIZE; 206211ce2ba3SJiri Pirko if (entry->key.term_mac.copy_to_cpu && 206311ce2ba3SJiri Pirko rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_COPY_CPU_ACTION, 206411ce2ba3SJiri Pirko entry->key.term_mac.copy_to_cpu)) 206511ce2ba3SJiri Pirko return -EMSGSIZE; 206611ce2ba3SJiri Pirko 206711ce2ba3SJiri Pirko return 0; 206811ce2ba3SJiri Pirko } 206911ce2ba3SJiri Pirko 207011ce2ba3SJiri Pirko static int 207111ce2ba3SJiri Pirko rocker_cmd_flow_tbl_add_ucast_routing(struct rocker_desc_info *desc_info, 207211ce2ba3SJiri Pirko const struct rocker_flow_tbl_entry *entry) 207311ce2ba3SJiri Pirko { 207411ce2ba3SJiri Pirko if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_ETHERTYPE, 207511ce2ba3SJiri Pirko entry->key.ucast_routing.eth_type)) 207611ce2ba3SJiri Pirko return -EMSGSIZE; 207711ce2ba3SJiri Pirko if (rocker_tlv_put_be32(desc_info, ROCKER_TLV_OF_DPA_DST_IP, 207811ce2ba3SJiri Pirko entry->key.ucast_routing.dst4)) 207911ce2ba3SJiri Pirko return -EMSGSIZE; 208011ce2ba3SJiri Pirko if (rocker_tlv_put_be32(desc_info, ROCKER_TLV_OF_DPA_DST_IP_MASK, 208111ce2ba3SJiri Pirko entry->key.ucast_routing.dst4_mask)) 208211ce2ba3SJiri Pirko return -EMSGSIZE; 208311ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID, 208411ce2ba3SJiri Pirko entry->key.ucast_routing.goto_tbl)) 208511ce2ba3SJiri Pirko return -EMSGSIZE; 208611ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID, 208711ce2ba3SJiri Pirko entry->key.ucast_routing.group_id)) 208811ce2ba3SJiri Pirko return -EMSGSIZE; 208911ce2ba3SJiri Pirko 209011ce2ba3SJiri Pirko return 0; 209111ce2ba3SJiri Pirko } 209211ce2ba3SJiri Pirko 209311ce2ba3SJiri Pirko static int 209411ce2ba3SJiri Pirko rocker_cmd_flow_tbl_add_bridge(struct rocker_desc_info *desc_info, 209511ce2ba3SJiri Pirko const struct rocker_flow_tbl_entry *entry) 209611ce2ba3SJiri Pirko { 209711ce2ba3SJiri Pirko if (entry->key.bridge.has_eth_dst && 209811ce2ba3SJiri Pirko rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC, 209911ce2ba3SJiri Pirko ETH_ALEN, entry->key.bridge.eth_dst)) 210011ce2ba3SJiri Pirko return -EMSGSIZE; 210111ce2ba3SJiri Pirko if (entry->key.bridge.has_eth_dst_mask && 210211ce2ba3SJiri Pirko rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC_MASK, 210311ce2ba3SJiri Pirko ETH_ALEN, entry->key.bridge.eth_dst_mask)) 210411ce2ba3SJiri Pirko return -EMSGSIZE; 210511ce2ba3SJiri Pirko if (entry->key.bridge.vlan_id && 210611ce2ba3SJiri Pirko rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID, 210711ce2ba3SJiri Pirko entry->key.bridge.vlan_id)) 210811ce2ba3SJiri Pirko return -EMSGSIZE; 210911ce2ba3SJiri Pirko if (entry->key.bridge.tunnel_id && 211011ce2ba3SJiri Pirko rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_TUNNEL_ID, 211111ce2ba3SJiri Pirko entry->key.bridge.tunnel_id)) 211211ce2ba3SJiri Pirko return -EMSGSIZE; 211311ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GOTO_TABLE_ID, 211411ce2ba3SJiri Pirko entry->key.bridge.goto_tbl)) 211511ce2ba3SJiri Pirko return -EMSGSIZE; 211611ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID, 211711ce2ba3SJiri Pirko entry->key.bridge.group_id)) 211811ce2ba3SJiri Pirko return -EMSGSIZE; 211911ce2ba3SJiri Pirko if (entry->key.bridge.copy_to_cpu && 212011ce2ba3SJiri Pirko rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_COPY_CPU_ACTION, 212111ce2ba3SJiri Pirko entry->key.bridge.copy_to_cpu)) 212211ce2ba3SJiri Pirko return -EMSGSIZE; 212311ce2ba3SJiri Pirko 212411ce2ba3SJiri Pirko return 0; 212511ce2ba3SJiri Pirko } 212611ce2ba3SJiri Pirko 212711ce2ba3SJiri Pirko static int 212811ce2ba3SJiri Pirko rocker_cmd_flow_tbl_add_acl(struct rocker_desc_info *desc_info, 212911ce2ba3SJiri Pirko const struct rocker_flow_tbl_entry *entry) 213011ce2ba3SJiri Pirko { 213111ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT, 213211ce2ba3SJiri Pirko entry->key.acl.in_pport)) 213311ce2ba3SJiri Pirko return -EMSGSIZE; 213411ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_IN_PPORT_MASK, 213511ce2ba3SJiri Pirko entry->key.acl.in_pport_mask)) 213611ce2ba3SJiri Pirko return -EMSGSIZE; 213711ce2ba3SJiri Pirko if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC, 213811ce2ba3SJiri Pirko ETH_ALEN, entry->key.acl.eth_src)) 213911ce2ba3SJiri Pirko return -EMSGSIZE; 214011ce2ba3SJiri Pirko if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC_MASK, 214111ce2ba3SJiri Pirko ETH_ALEN, entry->key.acl.eth_src_mask)) 214211ce2ba3SJiri Pirko return -EMSGSIZE; 214311ce2ba3SJiri Pirko if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC, 214411ce2ba3SJiri Pirko ETH_ALEN, entry->key.acl.eth_dst)) 214511ce2ba3SJiri Pirko return -EMSGSIZE; 214611ce2ba3SJiri Pirko if (rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC_MASK, 214711ce2ba3SJiri Pirko ETH_ALEN, entry->key.acl.eth_dst_mask)) 214811ce2ba3SJiri Pirko return -EMSGSIZE; 214911ce2ba3SJiri Pirko if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_ETHERTYPE, 215011ce2ba3SJiri Pirko entry->key.acl.eth_type)) 215111ce2ba3SJiri Pirko return -EMSGSIZE; 215211ce2ba3SJiri Pirko if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID, 215311ce2ba3SJiri Pirko entry->key.acl.vlan_id)) 215411ce2ba3SJiri Pirko return -EMSGSIZE; 215511ce2ba3SJiri Pirko if (rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID_MASK, 215611ce2ba3SJiri Pirko entry->key.acl.vlan_id_mask)) 215711ce2ba3SJiri Pirko return -EMSGSIZE; 215811ce2ba3SJiri Pirko 215911ce2ba3SJiri Pirko switch (ntohs(entry->key.acl.eth_type)) { 216011ce2ba3SJiri Pirko case ETH_P_IP: 216111ce2ba3SJiri Pirko case ETH_P_IPV6: 216211ce2ba3SJiri Pirko if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_IP_PROTO, 216311ce2ba3SJiri Pirko entry->key.acl.ip_proto)) 216411ce2ba3SJiri Pirko return -EMSGSIZE; 216511ce2ba3SJiri Pirko if (rocker_tlv_put_u8(desc_info, 216611ce2ba3SJiri Pirko ROCKER_TLV_OF_DPA_IP_PROTO_MASK, 216711ce2ba3SJiri Pirko entry->key.acl.ip_proto_mask)) 216811ce2ba3SJiri Pirko return -EMSGSIZE; 216911ce2ba3SJiri Pirko if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_IP_DSCP, 217011ce2ba3SJiri Pirko entry->key.acl.ip_tos & 0x3f)) 217111ce2ba3SJiri Pirko return -EMSGSIZE; 217211ce2ba3SJiri Pirko if (rocker_tlv_put_u8(desc_info, 217311ce2ba3SJiri Pirko ROCKER_TLV_OF_DPA_IP_DSCP_MASK, 217411ce2ba3SJiri Pirko entry->key.acl.ip_tos_mask & 0x3f)) 217511ce2ba3SJiri Pirko return -EMSGSIZE; 217611ce2ba3SJiri Pirko if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_IP_ECN, 217711ce2ba3SJiri Pirko (entry->key.acl.ip_tos & 0xc0) >> 6)) 217811ce2ba3SJiri Pirko return -EMSGSIZE; 217911ce2ba3SJiri Pirko if (rocker_tlv_put_u8(desc_info, 218011ce2ba3SJiri Pirko ROCKER_TLV_OF_DPA_IP_ECN_MASK, 218111ce2ba3SJiri Pirko (entry->key.acl.ip_tos_mask & 0xc0) >> 6)) 218211ce2ba3SJiri Pirko return -EMSGSIZE; 218311ce2ba3SJiri Pirko break; 218411ce2ba3SJiri Pirko } 218511ce2ba3SJiri Pirko 218611ce2ba3SJiri Pirko if (entry->key.acl.group_id != ROCKER_GROUP_NONE && 218711ce2ba3SJiri Pirko rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID, 218811ce2ba3SJiri Pirko entry->key.acl.group_id)) 218911ce2ba3SJiri Pirko return -EMSGSIZE; 219011ce2ba3SJiri Pirko 219111ce2ba3SJiri Pirko return 0; 219211ce2ba3SJiri Pirko } 219311ce2ba3SJiri Pirko 219411ce2ba3SJiri Pirko static int rocker_cmd_flow_tbl_add(const struct rocker_port *rocker_port, 219511ce2ba3SJiri Pirko struct rocker_desc_info *desc_info, 219611ce2ba3SJiri Pirko void *priv) 219711ce2ba3SJiri Pirko { 219811ce2ba3SJiri Pirko const struct rocker_flow_tbl_entry *entry = priv; 219911ce2ba3SJiri Pirko struct rocker_tlv *cmd_info; 220011ce2ba3SJiri Pirko int err = 0; 220111ce2ba3SJiri Pirko 220211ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd)) 220311ce2ba3SJiri Pirko return -EMSGSIZE; 220411ce2ba3SJiri Pirko cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 220511ce2ba3SJiri Pirko if (!cmd_info) 220611ce2ba3SJiri Pirko return -EMSGSIZE; 220711ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_TABLE_ID, 220811ce2ba3SJiri Pirko entry->key.tbl_id)) 220911ce2ba3SJiri Pirko return -EMSGSIZE; 221011ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_PRIORITY, 221111ce2ba3SJiri Pirko entry->key.priority)) 221211ce2ba3SJiri Pirko return -EMSGSIZE; 221311ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_HARDTIME, 0)) 221411ce2ba3SJiri Pirko return -EMSGSIZE; 221511ce2ba3SJiri Pirko if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_OF_DPA_COOKIE, 221611ce2ba3SJiri Pirko entry->cookie)) 221711ce2ba3SJiri Pirko return -EMSGSIZE; 221811ce2ba3SJiri Pirko 221911ce2ba3SJiri Pirko switch (entry->key.tbl_id) { 222011ce2ba3SJiri Pirko case ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT: 222111ce2ba3SJiri Pirko err = rocker_cmd_flow_tbl_add_ig_port(desc_info, entry); 222211ce2ba3SJiri Pirko break; 222311ce2ba3SJiri Pirko case ROCKER_OF_DPA_TABLE_ID_VLAN: 222411ce2ba3SJiri Pirko err = rocker_cmd_flow_tbl_add_vlan(desc_info, entry); 222511ce2ba3SJiri Pirko break; 222611ce2ba3SJiri Pirko case ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC: 222711ce2ba3SJiri Pirko err = rocker_cmd_flow_tbl_add_term_mac(desc_info, entry); 222811ce2ba3SJiri Pirko break; 222911ce2ba3SJiri Pirko case ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING: 223011ce2ba3SJiri Pirko err = rocker_cmd_flow_tbl_add_ucast_routing(desc_info, entry); 223111ce2ba3SJiri Pirko break; 223211ce2ba3SJiri Pirko case ROCKER_OF_DPA_TABLE_ID_BRIDGING: 223311ce2ba3SJiri Pirko err = rocker_cmd_flow_tbl_add_bridge(desc_info, entry); 223411ce2ba3SJiri Pirko break; 223511ce2ba3SJiri Pirko case ROCKER_OF_DPA_TABLE_ID_ACL_POLICY: 223611ce2ba3SJiri Pirko err = rocker_cmd_flow_tbl_add_acl(desc_info, entry); 223711ce2ba3SJiri Pirko break; 223811ce2ba3SJiri Pirko default: 223911ce2ba3SJiri Pirko err = -ENOTSUPP; 224011ce2ba3SJiri Pirko break; 224111ce2ba3SJiri Pirko } 224211ce2ba3SJiri Pirko 224311ce2ba3SJiri Pirko if (err) 224411ce2ba3SJiri Pirko return err; 224511ce2ba3SJiri Pirko 224611ce2ba3SJiri Pirko rocker_tlv_nest_end(desc_info, cmd_info); 224711ce2ba3SJiri Pirko 224811ce2ba3SJiri Pirko return 0; 224911ce2ba3SJiri Pirko } 225011ce2ba3SJiri Pirko 225111ce2ba3SJiri Pirko static int rocker_cmd_flow_tbl_del(const struct rocker_port *rocker_port, 225211ce2ba3SJiri Pirko struct rocker_desc_info *desc_info, 225311ce2ba3SJiri Pirko void *priv) 225411ce2ba3SJiri Pirko { 225511ce2ba3SJiri Pirko const struct rocker_flow_tbl_entry *entry = priv; 225611ce2ba3SJiri Pirko struct rocker_tlv *cmd_info; 225711ce2ba3SJiri Pirko 225811ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd)) 225911ce2ba3SJiri Pirko return -EMSGSIZE; 226011ce2ba3SJiri Pirko cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 226111ce2ba3SJiri Pirko if (!cmd_info) 226211ce2ba3SJiri Pirko return -EMSGSIZE; 226311ce2ba3SJiri Pirko if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_OF_DPA_COOKIE, 226411ce2ba3SJiri Pirko entry->cookie)) 226511ce2ba3SJiri Pirko return -EMSGSIZE; 226611ce2ba3SJiri Pirko rocker_tlv_nest_end(desc_info, cmd_info); 226711ce2ba3SJiri Pirko 226811ce2ba3SJiri Pirko return 0; 226911ce2ba3SJiri Pirko } 227011ce2ba3SJiri Pirko 227111ce2ba3SJiri Pirko static int 227211ce2ba3SJiri Pirko rocker_cmd_group_tbl_add_l2_interface(struct rocker_desc_info *desc_info, 227311ce2ba3SJiri Pirko struct rocker_group_tbl_entry *entry) 227411ce2ba3SJiri Pirko { 227511ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_OUT_PPORT, 227611ce2ba3SJiri Pirko ROCKER_GROUP_PORT_GET(entry->group_id))) 227711ce2ba3SJiri Pirko return -EMSGSIZE; 227811ce2ba3SJiri Pirko if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_POP_VLAN, 227911ce2ba3SJiri Pirko entry->l2_interface.pop_vlan)) 228011ce2ba3SJiri Pirko return -EMSGSIZE; 228111ce2ba3SJiri Pirko 228211ce2ba3SJiri Pirko return 0; 228311ce2ba3SJiri Pirko } 228411ce2ba3SJiri Pirko 228511ce2ba3SJiri Pirko static int 228611ce2ba3SJiri Pirko rocker_cmd_group_tbl_add_l2_rewrite(struct rocker_desc_info *desc_info, 228711ce2ba3SJiri Pirko const struct rocker_group_tbl_entry *entry) 228811ce2ba3SJiri Pirko { 228911ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID_LOWER, 229011ce2ba3SJiri Pirko entry->l2_rewrite.group_id)) 229111ce2ba3SJiri Pirko return -EMSGSIZE; 229211ce2ba3SJiri Pirko if (!is_zero_ether_addr(entry->l2_rewrite.eth_src) && 229311ce2ba3SJiri Pirko rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC, 229411ce2ba3SJiri Pirko ETH_ALEN, entry->l2_rewrite.eth_src)) 229511ce2ba3SJiri Pirko return -EMSGSIZE; 229611ce2ba3SJiri Pirko if (!is_zero_ether_addr(entry->l2_rewrite.eth_dst) && 229711ce2ba3SJiri Pirko rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC, 229811ce2ba3SJiri Pirko ETH_ALEN, entry->l2_rewrite.eth_dst)) 229911ce2ba3SJiri Pirko return -EMSGSIZE; 230011ce2ba3SJiri Pirko if (entry->l2_rewrite.vlan_id && 230111ce2ba3SJiri Pirko rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID, 230211ce2ba3SJiri Pirko entry->l2_rewrite.vlan_id)) 230311ce2ba3SJiri Pirko return -EMSGSIZE; 230411ce2ba3SJiri Pirko 230511ce2ba3SJiri Pirko return 0; 230611ce2ba3SJiri Pirko } 230711ce2ba3SJiri Pirko 230811ce2ba3SJiri Pirko static int 230911ce2ba3SJiri Pirko rocker_cmd_group_tbl_add_group_ids(struct rocker_desc_info *desc_info, 231011ce2ba3SJiri Pirko const struct rocker_group_tbl_entry *entry) 231111ce2ba3SJiri Pirko { 231211ce2ba3SJiri Pirko int i; 231311ce2ba3SJiri Pirko struct rocker_tlv *group_ids; 231411ce2ba3SJiri Pirko 231511ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_OF_DPA_GROUP_COUNT, 231611ce2ba3SJiri Pirko entry->group_count)) 231711ce2ba3SJiri Pirko return -EMSGSIZE; 231811ce2ba3SJiri Pirko 231911ce2ba3SJiri Pirko group_ids = rocker_tlv_nest_start(desc_info, 232011ce2ba3SJiri Pirko ROCKER_TLV_OF_DPA_GROUP_IDS); 232111ce2ba3SJiri Pirko if (!group_ids) 232211ce2ba3SJiri Pirko return -EMSGSIZE; 232311ce2ba3SJiri Pirko 232411ce2ba3SJiri Pirko for (i = 0; i < entry->group_count; i++) 232511ce2ba3SJiri Pirko /* Note TLV array is 1-based */ 232611ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, i + 1, entry->group_ids[i])) 232711ce2ba3SJiri Pirko return -EMSGSIZE; 232811ce2ba3SJiri Pirko 232911ce2ba3SJiri Pirko rocker_tlv_nest_end(desc_info, group_ids); 233011ce2ba3SJiri Pirko 233111ce2ba3SJiri Pirko return 0; 233211ce2ba3SJiri Pirko } 233311ce2ba3SJiri Pirko 233411ce2ba3SJiri Pirko static int 233511ce2ba3SJiri Pirko rocker_cmd_group_tbl_add_l3_unicast(struct rocker_desc_info *desc_info, 233611ce2ba3SJiri Pirko const struct rocker_group_tbl_entry *entry) 233711ce2ba3SJiri Pirko { 233811ce2ba3SJiri Pirko if (!is_zero_ether_addr(entry->l3_unicast.eth_src) && 233911ce2ba3SJiri Pirko rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_SRC_MAC, 234011ce2ba3SJiri Pirko ETH_ALEN, entry->l3_unicast.eth_src)) 234111ce2ba3SJiri Pirko return -EMSGSIZE; 234211ce2ba3SJiri Pirko if (!is_zero_ether_addr(entry->l3_unicast.eth_dst) && 234311ce2ba3SJiri Pirko rocker_tlv_put(desc_info, ROCKER_TLV_OF_DPA_DST_MAC, 234411ce2ba3SJiri Pirko ETH_ALEN, entry->l3_unicast.eth_dst)) 234511ce2ba3SJiri Pirko return -EMSGSIZE; 234611ce2ba3SJiri Pirko if (entry->l3_unicast.vlan_id && 234711ce2ba3SJiri Pirko rocker_tlv_put_be16(desc_info, ROCKER_TLV_OF_DPA_VLAN_ID, 234811ce2ba3SJiri Pirko entry->l3_unicast.vlan_id)) 234911ce2ba3SJiri Pirko return -EMSGSIZE; 235011ce2ba3SJiri Pirko if (rocker_tlv_put_u8(desc_info, ROCKER_TLV_OF_DPA_TTL_CHECK, 235111ce2ba3SJiri Pirko entry->l3_unicast.ttl_check)) 235211ce2ba3SJiri Pirko return -EMSGSIZE; 235311ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID_LOWER, 235411ce2ba3SJiri Pirko entry->l3_unicast.group_id)) 235511ce2ba3SJiri Pirko return -EMSGSIZE; 235611ce2ba3SJiri Pirko 235711ce2ba3SJiri Pirko return 0; 235811ce2ba3SJiri Pirko } 235911ce2ba3SJiri Pirko 236011ce2ba3SJiri Pirko static int rocker_cmd_group_tbl_add(const struct rocker_port *rocker_port, 236111ce2ba3SJiri Pirko struct rocker_desc_info *desc_info, 236211ce2ba3SJiri Pirko void *priv) 236311ce2ba3SJiri Pirko { 236411ce2ba3SJiri Pirko struct rocker_group_tbl_entry *entry = priv; 236511ce2ba3SJiri Pirko struct rocker_tlv *cmd_info; 236611ce2ba3SJiri Pirko int err = 0; 236711ce2ba3SJiri Pirko 236811ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd)) 236911ce2ba3SJiri Pirko return -EMSGSIZE; 237011ce2ba3SJiri Pirko cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 237111ce2ba3SJiri Pirko if (!cmd_info) 237211ce2ba3SJiri Pirko return -EMSGSIZE; 237311ce2ba3SJiri Pirko 237411ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID, 237511ce2ba3SJiri Pirko entry->group_id)) 237611ce2ba3SJiri Pirko return -EMSGSIZE; 237711ce2ba3SJiri Pirko 237811ce2ba3SJiri Pirko switch (ROCKER_GROUP_TYPE_GET(entry->group_id)) { 237911ce2ba3SJiri Pirko case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE: 238011ce2ba3SJiri Pirko err = rocker_cmd_group_tbl_add_l2_interface(desc_info, entry); 238111ce2ba3SJiri Pirko break; 238211ce2ba3SJiri Pirko case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE: 238311ce2ba3SJiri Pirko err = rocker_cmd_group_tbl_add_l2_rewrite(desc_info, entry); 238411ce2ba3SJiri Pirko break; 238511ce2ba3SJiri Pirko case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD: 238611ce2ba3SJiri Pirko case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST: 238711ce2ba3SJiri Pirko err = rocker_cmd_group_tbl_add_group_ids(desc_info, entry); 238811ce2ba3SJiri Pirko break; 238911ce2ba3SJiri Pirko case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST: 239011ce2ba3SJiri Pirko err = rocker_cmd_group_tbl_add_l3_unicast(desc_info, entry); 239111ce2ba3SJiri Pirko break; 239211ce2ba3SJiri Pirko default: 239311ce2ba3SJiri Pirko err = -ENOTSUPP; 239411ce2ba3SJiri Pirko break; 239511ce2ba3SJiri Pirko } 239611ce2ba3SJiri Pirko 239711ce2ba3SJiri Pirko if (err) 239811ce2ba3SJiri Pirko return err; 239911ce2ba3SJiri Pirko 240011ce2ba3SJiri Pirko rocker_tlv_nest_end(desc_info, cmd_info); 240111ce2ba3SJiri Pirko 240211ce2ba3SJiri Pirko return 0; 240311ce2ba3SJiri Pirko } 240411ce2ba3SJiri Pirko 240511ce2ba3SJiri Pirko static int rocker_cmd_group_tbl_del(const struct rocker_port *rocker_port, 240611ce2ba3SJiri Pirko struct rocker_desc_info *desc_info, 240711ce2ba3SJiri Pirko void *priv) 240811ce2ba3SJiri Pirko { 240911ce2ba3SJiri Pirko const struct rocker_group_tbl_entry *entry = priv; 241011ce2ba3SJiri Pirko struct rocker_tlv *cmd_info; 241111ce2ba3SJiri Pirko 241211ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, entry->cmd)) 241311ce2ba3SJiri Pirko return -EMSGSIZE; 241411ce2ba3SJiri Pirko cmd_info = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 241511ce2ba3SJiri Pirko if (!cmd_info) 241611ce2ba3SJiri Pirko return -EMSGSIZE; 241711ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_OF_DPA_GROUP_ID, 241811ce2ba3SJiri Pirko entry->group_id)) 241911ce2ba3SJiri Pirko return -EMSGSIZE; 242011ce2ba3SJiri Pirko rocker_tlv_nest_end(desc_info, cmd_info); 242111ce2ba3SJiri Pirko 242211ce2ba3SJiri Pirko return 0; 242311ce2ba3SJiri Pirko } 242411ce2ba3SJiri Pirko 242511ce2ba3SJiri Pirko /*************************************************** 242611ce2ba3SJiri Pirko * Flow, group, FDB, internal VLAN and neigh tables 242711ce2ba3SJiri Pirko ***************************************************/ 242811ce2ba3SJiri Pirko 242911ce2ba3SJiri Pirko static int rocker_init_tbls(struct rocker *rocker) 243011ce2ba3SJiri Pirko { 243111ce2ba3SJiri Pirko hash_init(rocker->flow_tbl); 243211ce2ba3SJiri Pirko spin_lock_init(&rocker->flow_tbl_lock); 243311ce2ba3SJiri Pirko 243411ce2ba3SJiri Pirko hash_init(rocker->group_tbl); 243511ce2ba3SJiri Pirko spin_lock_init(&rocker->group_tbl_lock); 243611ce2ba3SJiri Pirko 243711ce2ba3SJiri Pirko hash_init(rocker->fdb_tbl); 243811ce2ba3SJiri Pirko spin_lock_init(&rocker->fdb_tbl_lock); 243911ce2ba3SJiri Pirko 244011ce2ba3SJiri Pirko hash_init(rocker->internal_vlan_tbl); 244111ce2ba3SJiri Pirko spin_lock_init(&rocker->internal_vlan_tbl_lock); 244211ce2ba3SJiri Pirko 244311ce2ba3SJiri Pirko hash_init(rocker->neigh_tbl); 244411ce2ba3SJiri Pirko spin_lock_init(&rocker->neigh_tbl_lock); 244511ce2ba3SJiri Pirko 244611ce2ba3SJiri Pirko return 0; 244711ce2ba3SJiri Pirko } 244811ce2ba3SJiri Pirko 244911ce2ba3SJiri Pirko static void rocker_free_tbls(struct rocker *rocker) 245011ce2ba3SJiri Pirko { 245111ce2ba3SJiri Pirko unsigned long flags; 245211ce2ba3SJiri Pirko struct rocker_flow_tbl_entry *flow_entry; 245311ce2ba3SJiri Pirko struct rocker_group_tbl_entry *group_entry; 245411ce2ba3SJiri Pirko struct rocker_fdb_tbl_entry *fdb_entry; 245511ce2ba3SJiri Pirko struct rocker_internal_vlan_tbl_entry *internal_vlan_entry; 245611ce2ba3SJiri Pirko struct rocker_neigh_tbl_entry *neigh_entry; 245711ce2ba3SJiri Pirko struct hlist_node *tmp; 245811ce2ba3SJiri Pirko int bkt; 245911ce2ba3SJiri Pirko 246011ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->flow_tbl_lock, flags); 246111ce2ba3SJiri Pirko hash_for_each_safe(rocker->flow_tbl, bkt, tmp, flow_entry, entry) 246211ce2ba3SJiri Pirko hash_del(&flow_entry->entry); 246311ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->flow_tbl_lock, flags); 246411ce2ba3SJiri Pirko 246511ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->group_tbl_lock, flags); 246611ce2ba3SJiri Pirko hash_for_each_safe(rocker->group_tbl, bkt, tmp, group_entry, entry) 246711ce2ba3SJiri Pirko hash_del(&group_entry->entry); 246811ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->group_tbl_lock, flags); 246911ce2ba3SJiri Pirko 247011ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->fdb_tbl_lock, flags); 247111ce2ba3SJiri Pirko hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, fdb_entry, entry) 247211ce2ba3SJiri Pirko hash_del(&fdb_entry->entry); 247311ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->fdb_tbl_lock, flags); 247411ce2ba3SJiri Pirko 247511ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->internal_vlan_tbl_lock, flags); 247611ce2ba3SJiri Pirko hash_for_each_safe(rocker->internal_vlan_tbl, bkt, 247711ce2ba3SJiri Pirko tmp, internal_vlan_entry, entry) 247811ce2ba3SJiri Pirko hash_del(&internal_vlan_entry->entry); 247911ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->internal_vlan_tbl_lock, flags); 248011ce2ba3SJiri Pirko 248111ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->neigh_tbl_lock, flags); 248211ce2ba3SJiri Pirko hash_for_each_safe(rocker->neigh_tbl, bkt, tmp, neigh_entry, entry) 248311ce2ba3SJiri Pirko hash_del(&neigh_entry->entry); 248411ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->neigh_tbl_lock, flags); 248511ce2ba3SJiri Pirko } 248611ce2ba3SJiri Pirko 248711ce2ba3SJiri Pirko static struct rocker_flow_tbl_entry * 248811ce2ba3SJiri Pirko rocker_flow_tbl_find(const struct rocker *rocker, 248911ce2ba3SJiri Pirko const struct rocker_flow_tbl_entry *match) 249011ce2ba3SJiri Pirko { 249111ce2ba3SJiri Pirko struct rocker_flow_tbl_entry *found; 249211ce2ba3SJiri Pirko size_t key_len = match->key_len ? match->key_len : sizeof(found->key); 249311ce2ba3SJiri Pirko 249411ce2ba3SJiri Pirko hash_for_each_possible(rocker->flow_tbl, found, 249511ce2ba3SJiri Pirko entry, match->key_crc32) { 249611ce2ba3SJiri Pirko if (memcmp(&found->key, &match->key, key_len) == 0) 249711ce2ba3SJiri Pirko return found; 249811ce2ba3SJiri Pirko } 249911ce2ba3SJiri Pirko 250011ce2ba3SJiri Pirko return NULL; 250111ce2ba3SJiri Pirko } 250211ce2ba3SJiri Pirko 250311ce2ba3SJiri Pirko static int rocker_flow_tbl_add(struct rocker_port *rocker_port, 250411ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 250511ce2ba3SJiri Pirko struct rocker_flow_tbl_entry *match) 250611ce2ba3SJiri Pirko { 250711ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 250811ce2ba3SJiri Pirko struct rocker_flow_tbl_entry *found; 250911ce2ba3SJiri Pirko size_t key_len = match->key_len ? match->key_len : sizeof(found->key); 251011ce2ba3SJiri Pirko unsigned long lock_flags; 251111ce2ba3SJiri Pirko 251211ce2ba3SJiri Pirko match->key_crc32 = crc32(~0, &match->key, key_len); 251311ce2ba3SJiri Pirko 251411ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->flow_tbl_lock, lock_flags); 251511ce2ba3SJiri Pirko 251611ce2ba3SJiri Pirko found = rocker_flow_tbl_find(rocker, match); 251711ce2ba3SJiri Pirko 251811ce2ba3SJiri Pirko if (found) { 251911ce2ba3SJiri Pirko match->cookie = found->cookie; 252011ce2ba3SJiri Pirko if (!switchdev_trans_ph_prepare(trans)) 252111ce2ba3SJiri Pirko hash_del(&found->entry); 252211ce2ba3SJiri Pirko rocker_kfree(trans, found); 252311ce2ba3SJiri Pirko found = match; 252411ce2ba3SJiri Pirko found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_MOD; 252511ce2ba3SJiri Pirko } else { 252611ce2ba3SJiri Pirko found = match; 252711ce2ba3SJiri Pirko found->cookie = rocker->flow_tbl_next_cookie++; 252811ce2ba3SJiri Pirko found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_ADD; 252911ce2ba3SJiri Pirko } 253011ce2ba3SJiri Pirko 253111ce2ba3SJiri Pirko if (!switchdev_trans_ph_prepare(trans)) 253211ce2ba3SJiri Pirko hash_add(rocker->flow_tbl, &found->entry, found->key_crc32); 253311ce2ba3SJiri Pirko 253411ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->flow_tbl_lock, lock_flags); 253511ce2ba3SJiri Pirko 253611ce2ba3SJiri Pirko return rocker_cmd_exec(rocker_port, trans, flags, 253711ce2ba3SJiri Pirko rocker_cmd_flow_tbl_add, found, NULL, NULL); 253811ce2ba3SJiri Pirko } 253911ce2ba3SJiri Pirko 254011ce2ba3SJiri Pirko static int rocker_flow_tbl_del(struct rocker_port *rocker_port, 254111ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 254211ce2ba3SJiri Pirko struct rocker_flow_tbl_entry *match) 254311ce2ba3SJiri Pirko { 254411ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 254511ce2ba3SJiri Pirko struct rocker_flow_tbl_entry *found; 254611ce2ba3SJiri Pirko size_t key_len = match->key_len ? match->key_len : sizeof(found->key); 254711ce2ba3SJiri Pirko unsigned long lock_flags; 254811ce2ba3SJiri Pirko int err = 0; 254911ce2ba3SJiri Pirko 255011ce2ba3SJiri Pirko match->key_crc32 = crc32(~0, &match->key, key_len); 255111ce2ba3SJiri Pirko 255211ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->flow_tbl_lock, lock_flags); 255311ce2ba3SJiri Pirko 255411ce2ba3SJiri Pirko found = rocker_flow_tbl_find(rocker, match); 255511ce2ba3SJiri Pirko 255611ce2ba3SJiri Pirko if (found) { 255711ce2ba3SJiri Pirko if (!switchdev_trans_ph_prepare(trans)) 255811ce2ba3SJiri Pirko hash_del(&found->entry); 255911ce2ba3SJiri Pirko found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_FLOW_DEL; 256011ce2ba3SJiri Pirko } 256111ce2ba3SJiri Pirko 256211ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->flow_tbl_lock, lock_flags); 256311ce2ba3SJiri Pirko 256411ce2ba3SJiri Pirko rocker_kfree(trans, match); 256511ce2ba3SJiri Pirko 256611ce2ba3SJiri Pirko if (found) { 256711ce2ba3SJiri Pirko err = rocker_cmd_exec(rocker_port, trans, flags, 256811ce2ba3SJiri Pirko rocker_cmd_flow_tbl_del, 256911ce2ba3SJiri Pirko found, NULL, NULL); 257011ce2ba3SJiri Pirko rocker_kfree(trans, found); 257111ce2ba3SJiri Pirko } 257211ce2ba3SJiri Pirko 257311ce2ba3SJiri Pirko return err; 257411ce2ba3SJiri Pirko } 257511ce2ba3SJiri Pirko 257611ce2ba3SJiri Pirko static int rocker_flow_tbl_do(struct rocker_port *rocker_port, 257711ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 257811ce2ba3SJiri Pirko struct rocker_flow_tbl_entry *entry) 257911ce2ba3SJiri Pirko { 258011ce2ba3SJiri Pirko if (flags & ROCKER_OP_FLAG_REMOVE) 258111ce2ba3SJiri Pirko return rocker_flow_tbl_del(rocker_port, trans, flags, entry); 258211ce2ba3SJiri Pirko else 258311ce2ba3SJiri Pirko return rocker_flow_tbl_add(rocker_port, trans, flags, entry); 258411ce2ba3SJiri Pirko } 258511ce2ba3SJiri Pirko 258611ce2ba3SJiri Pirko static int rocker_flow_tbl_ig_port(struct rocker_port *rocker_port, 258711ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 258811ce2ba3SJiri Pirko u32 in_pport, u32 in_pport_mask, 258911ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl) 259011ce2ba3SJiri Pirko { 259111ce2ba3SJiri Pirko struct rocker_flow_tbl_entry *entry; 259211ce2ba3SJiri Pirko 259311ce2ba3SJiri Pirko entry = rocker_kzalloc(trans, flags, sizeof(*entry)); 259411ce2ba3SJiri Pirko if (!entry) 259511ce2ba3SJiri Pirko return -ENOMEM; 259611ce2ba3SJiri Pirko 259711ce2ba3SJiri Pirko entry->key.priority = ROCKER_PRIORITY_IG_PORT; 259811ce2ba3SJiri Pirko entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_INGRESS_PORT; 259911ce2ba3SJiri Pirko entry->key.ig_port.in_pport = in_pport; 260011ce2ba3SJiri Pirko entry->key.ig_port.in_pport_mask = in_pport_mask; 260111ce2ba3SJiri Pirko entry->key.ig_port.goto_tbl = goto_tbl; 260211ce2ba3SJiri Pirko 260311ce2ba3SJiri Pirko return rocker_flow_tbl_do(rocker_port, trans, flags, entry); 260411ce2ba3SJiri Pirko } 260511ce2ba3SJiri Pirko 260611ce2ba3SJiri Pirko static int rocker_flow_tbl_vlan(struct rocker_port *rocker_port, 260711ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 260811ce2ba3SJiri Pirko u32 in_pport, __be16 vlan_id, 260911ce2ba3SJiri Pirko __be16 vlan_id_mask, 261011ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl, 261111ce2ba3SJiri Pirko bool untagged, __be16 new_vlan_id) 261211ce2ba3SJiri Pirko { 261311ce2ba3SJiri Pirko struct rocker_flow_tbl_entry *entry; 261411ce2ba3SJiri Pirko 261511ce2ba3SJiri Pirko entry = rocker_kzalloc(trans, flags, sizeof(*entry)); 261611ce2ba3SJiri Pirko if (!entry) 261711ce2ba3SJiri Pirko return -ENOMEM; 261811ce2ba3SJiri Pirko 261911ce2ba3SJiri Pirko entry->key.priority = ROCKER_PRIORITY_VLAN; 262011ce2ba3SJiri Pirko entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_VLAN; 262111ce2ba3SJiri Pirko entry->key.vlan.in_pport = in_pport; 262211ce2ba3SJiri Pirko entry->key.vlan.vlan_id = vlan_id; 262311ce2ba3SJiri Pirko entry->key.vlan.vlan_id_mask = vlan_id_mask; 262411ce2ba3SJiri Pirko entry->key.vlan.goto_tbl = goto_tbl; 262511ce2ba3SJiri Pirko 262611ce2ba3SJiri Pirko entry->key.vlan.untagged = untagged; 262711ce2ba3SJiri Pirko entry->key.vlan.new_vlan_id = new_vlan_id; 262811ce2ba3SJiri Pirko 262911ce2ba3SJiri Pirko return rocker_flow_tbl_do(rocker_port, trans, flags, entry); 263011ce2ba3SJiri Pirko } 263111ce2ba3SJiri Pirko 263211ce2ba3SJiri Pirko static int rocker_flow_tbl_term_mac(struct rocker_port *rocker_port, 263311ce2ba3SJiri Pirko struct switchdev_trans *trans, 263411ce2ba3SJiri Pirko u32 in_pport, u32 in_pport_mask, 263511ce2ba3SJiri Pirko __be16 eth_type, const u8 *eth_dst, 263611ce2ba3SJiri Pirko const u8 *eth_dst_mask, __be16 vlan_id, 263711ce2ba3SJiri Pirko __be16 vlan_id_mask, bool copy_to_cpu, 263811ce2ba3SJiri Pirko int flags) 263911ce2ba3SJiri Pirko { 264011ce2ba3SJiri Pirko struct rocker_flow_tbl_entry *entry; 264111ce2ba3SJiri Pirko 264211ce2ba3SJiri Pirko entry = rocker_kzalloc(trans, flags, sizeof(*entry)); 264311ce2ba3SJiri Pirko if (!entry) 264411ce2ba3SJiri Pirko return -ENOMEM; 264511ce2ba3SJiri Pirko 264611ce2ba3SJiri Pirko if (is_multicast_ether_addr(eth_dst)) { 264711ce2ba3SJiri Pirko entry->key.priority = ROCKER_PRIORITY_TERM_MAC_MCAST; 264811ce2ba3SJiri Pirko entry->key.term_mac.goto_tbl = 264911ce2ba3SJiri Pirko ROCKER_OF_DPA_TABLE_ID_MULTICAST_ROUTING; 265011ce2ba3SJiri Pirko } else { 265111ce2ba3SJiri Pirko entry->key.priority = ROCKER_PRIORITY_TERM_MAC_UCAST; 265211ce2ba3SJiri Pirko entry->key.term_mac.goto_tbl = 265311ce2ba3SJiri Pirko ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING; 265411ce2ba3SJiri Pirko } 265511ce2ba3SJiri Pirko 265611ce2ba3SJiri Pirko entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC; 265711ce2ba3SJiri Pirko entry->key.term_mac.in_pport = in_pport; 265811ce2ba3SJiri Pirko entry->key.term_mac.in_pport_mask = in_pport_mask; 265911ce2ba3SJiri Pirko entry->key.term_mac.eth_type = eth_type; 266011ce2ba3SJiri Pirko ether_addr_copy(entry->key.term_mac.eth_dst, eth_dst); 266111ce2ba3SJiri Pirko ether_addr_copy(entry->key.term_mac.eth_dst_mask, eth_dst_mask); 266211ce2ba3SJiri Pirko entry->key.term_mac.vlan_id = vlan_id; 266311ce2ba3SJiri Pirko entry->key.term_mac.vlan_id_mask = vlan_id_mask; 266411ce2ba3SJiri Pirko entry->key.term_mac.copy_to_cpu = copy_to_cpu; 266511ce2ba3SJiri Pirko 266611ce2ba3SJiri Pirko return rocker_flow_tbl_do(rocker_port, trans, flags, entry); 266711ce2ba3SJiri Pirko } 266811ce2ba3SJiri Pirko 266911ce2ba3SJiri Pirko static int rocker_flow_tbl_bridge(struct rocker_port *rocker_port, 267011ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 267111ce2ba3SJiri Pirko const u8 *eth_dst, const u8 *eth_dst_mask, 267211ce2ba3SJiri Pirko __be16 vlan_id, u32 tunnel_id, 267311ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl, 267411ce2ba3SJiri Pirko u32 group_id, bool copy_to_cpu) 267511ce2ba3SJiri Pirko { 267611ce2ba3SJiri Pirko struct rocker_flow_tbl_entry *entry; 267711ce2ba3SJiri Pirko u32 priority; 267811ce2ba3SJiri Pirko bool vlan_bridging = !!vlan_id; 267911ce2ba3SJiri Pirko bool dflt = !eth_dst || (eth_dst && eth_dst_mask); 268011ce2ba3SJiri Pirko bool wild = false; 268111ce2ba3SJiri Pirko 268211ce2ba3SJiri Pirko entry = rocker_kzalloc(trans, flags, sizeof(*entry)); 268311ce2ba3SJiri Pirko if (!entry) 268411ce2ba3SJiri Pirko return -ENOMEM; 268511ce2ba3SJiri Pirko 268611ce2ba3SJiri Pirko entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_BRIDGING; 268711ce2ba3SJiri Pirko 268811ce2ba3SJiri Pirko if (eth_dst) { 268911ce2ba3SJiri Pirko entry->key.bridge.has_eth_dst = 1; 269011ce2ba3SJiri Pirko ether_addr_copy(entry->key.bridge.eth_dst, eth_dst); 269111ce2ba3SJiri Pirko } 269211ce2ba3SJiri Pirko if (eth_dst_mask) { 269311ce2ba3SJiri Pirko entry->key.bridge.has_eth_dst_mask = 1; 269411ce2ba3SJiri Pirko ether_addr_copy(entry->key.bridge.eth_dst_mask, eth_dst_mask); 269511ce2ba3SJiri Pirko if (!ether_addr_equal(eth_dst_mask, ff_mac)) 269611ce2ba3SJiri Pirko wild = true; 269711ce2ba3SJiri Pirko } 269811ce2ba3SJiri Pirko 269911ce2ba3SJiri Pirko priority = ROCKER_PRIORITY_UNKNOWN; 270011ce2ba3SJiri Pirko if (vlan_bridging && dflt && wild) 270111ce2ba3SJiri Pirko priority = ROCKER_PRIORITY_BRIDGING_VLAN_DFLT_WILD; 270211ce2ba3SJiri Pirko else if (vlan_bridging && dflt && !wild) 270311ce2ba3SJiri Pirko priority = ROCKER_PRIORITY_BRIDGING_VLAN_DFLT_EXACT; 270411ce2ba3SJiri Pirko else if (vlan_bridging && !dflt) 270511ce2ba3SJiri Pirko priority = ROCKER_PRIORITY_BRIDGING_VLAN; 270611ce2ba3SJiri Pirko else if (!vlan_bridging && dflt && wild) 270711ce2ba3SJiri Pirko priority = ROCKER_PRIORITY_BRIDGING_TENANT_DFLT_WILD; 270811ce2ba3SJiri Pirko else if (!vlan_bridging && dflt && !wild) 270911ce2ba3SJiri Pirko priority = ROCKER_PRIORITY_BRIDGING_TENANT_DFLT_EXACT; 271011ce2ba3SJiri Pirko else if (!vlan_bridging && !dflt) 271111ce2ba3SJiri Pirko priority = ROCKER_PRIORITY_BRIDGING_TENANT; 271211ce2ba3SJiri Pirko 271311ce2ba3SJiri Pirko entry->key.priority = priority; 271411ce2ba3SJiri Pirko entry->key.bridge.vlan_id = vlan_id; 271511ce2ba3SJiri Pirko entry->key.bridge.tunnel_id = tunnel_id; 271611ce2ba3SJiri Pirko entry->key.bridge.goto_tbl = goto_tbl; 271711ce2ba3SJiri Pirko entry->key.bridge.group_id = group_id; 271811ce2ba3SJiri Pirko entry->key.bridge.copy_to_cpu = copy_to_cpu; 271911ce2ba3SJiri Pirko 272011ce2ba3SJiri Pirko return rocker_flow_tbl_do(rocker_port, trans, flags, entry); 272111ce2ba3SJiri Pirko } 272211ce2ba3SJiri Pirko 272311ce2ba3SJiri Pirko static int rocker_flow_tbl_ucast4_routing(struct rocker_port *rocker_port, 272411ce2ba3SJiri Pirko struct switchdev_trans *trans, 272511ce2ba3SJiri Pirko __be16 eth_type, __be32 dst, 272611ce2ba3SJiri Pirko __be32 dst_mask, u32 priority, 272711ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl, 272811ce2ba3SJiri Pirko u32 group_id, int flags) 272911ce2ba3SJiri Pirko { 273011ce2ba3SJiri Pirko struct rocker_flow_tbl_entry *entry; 273111ce2ba3SJiri Pirko 273211ce2ba3SJiri Pirko entry = rocker_kzalloc(trans, flags, sizeof(*entry)); 273311ce2ba3SJiri Pirko if (!entry) 273411ce2ba3SJiri Pirko return -ENOMEM; 273511ce2ba3SJiri Pirko 273611ce2ba3SJiri Pirko entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_UNICAST_ROUTING; 273711ce2ba3SJiri Pirko entry->key.priority = priority; 273811ce2ba3SJiri Pirko entry->key.ucast_routing.eth_type = eth_type; 273911ce2ba3SJiri Pirko entry->key.ucast_routing.dst4 = dst; 274011ce2ba3SJiri Pirko entry->key.ucast_routing.dst4_mask = dst_mask; 274111ce2ba3SJiri Pirko entry->key.ucast_routing.goto_tbl = goto_tbl; 274211ce2ba3SJiri Pirko entry->key.ucast_routing.group_id = group_id; 274311ce2ba3SJiri Pirko entry->key_len = offsetof(struct rocker_flow_tbl_key, 274411ce2ba3SJiri Pirko ucast_routing.group_id); 274511ce2ba3SJiri Pirko 274611ce2ba3SJiri Pirko return rocker_flow_tbl_do(rocker_port, trans, flags, entry); 274711ce2ba3SJiri Pirko } 274811ce2ba3SJiri Pirko 274911ce2ba3SJiri Pirko static int rocker_flow_tbl_acl(struct rocker_port *rocker_port, 275011ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 275111ce2ba3SJiri Pirko u32 in_pport, u32 in_pport_mask, 275211ce2ba3SJiri Pirko const u8 *eth_src, const u8 *eth_src_mask, 275311ce2ba3SJiri Pirko const u8 *eth_dst, const u8 *eth_dst_mask, 275411ce2ba3SJiri Pirko __be16 eth_type, __be16 vlan_id, 275511ce2ba3SJiri Pirko __be16 vlan_id_mask, u8 ip_proto, 275611ce2ba3SJiri Pirko u8 ip_proto_mask, u8 ip_tos, u8 ip_tos_mask, 275711ce2ba3SJiri Pirko u32 group_id) 275811ce2ba3SJiri Pirko { 275911ce2ba3SJiri Pirko u32 priority; 276011ce2ba3SJiri Pirko struct rocker_flow_tbl_entry *entry; 276111ce2ba3SJiri Pirko 276211ce2ba3SJiri Pirko entry = rocker_kzalloc(trans, flags, sizeof(*entry)); 276311ce2ba3SJiri Pirko if (!entry) 276411ce2ba3SJiri Pirko return -ENOMEM; 276511ce2ba3SJiri Pirko 276611ce2ba3SJiri Pirko priority = ROCKER_PRIORITY_ACL_NORMAL; 276711ce2ba3SJiri Pirko if (eth_dst && eth_dst_mask) { 276811ce2ba3SJiri Pirko if (ether_addr_equal(eth_dst_mask, mcast_mac)) 276911ce2ba3SJiri Pirko priority = ROCKER_PRIORITY_ACL_DFLT; 277011ce2ba3SJiri Pirko else if (is_link_local_ether_addr(eth_dst)) 277111ce2ba3SJiri Pirko priority = ROCKER_PRIORITY_ACL_CTRL; 277211ce2ba3SJiri Pirko } 277311ce2ba3SJiri Pirko 277411ce2ba3SJiri Pirko entry->key.priority = priority; 277511ce2ba3SJiri Pirko entry->key.tbl_id = ROCKER_OF_DPA_TABLE_ID_ACL_POLICY; 277611ce2ba3SJiri Pirko entry->key.acl.in_pport = in_pport; 277711ce2ba3SJiri Pirko entry->key.acl.in_pport_mask = in_pport_mask; 277811ce2ba3SJiri Pirko 277911ce2ba3SJiri Pirko if (eth_src) 278011ce2ba3SJiri Pirko ether_addr_copy(entry->key.acl.eth_src, eth_src); 278111ce2ba3SJiri Pirko if (eth_src_mask) 278211ce2ba3SJiri Pirko ether_addr_copy(entry->key.acl.eth_src_mask, eth_src_mask); 278311ce2ba3SJiri Pirko if (eth_dst) 278411ce2ba3SJiri Pirko ether_addr_copy(entry->key.acl.eth_dst, eth_dst); 278511ce2ba3SJiri Pirko if (eth_dst_mask) 278611ce2ba3SJiri Pirko ether_addr_copy(entry->key.acl.eth_dst_mask, eth_dst_mask); 278711ce2ba3SJiri Pirko 278811ce2ba3SJiri Pirko entry->key.acl.eth_type = eth_type; 278911ce2ba3SJiri Pirko entry->key.acl.vlan_id = vlan_id; 279011ce2ba3SJiri Pirko entry->key.acl.vlan_id_mask = vlan_id_mask; 279111ce2ba3SJiri Pirko entry->key.acl.ip_proto = ip_proto; 279211ce2ba3SJiri Pirko entry->key.acl.ip_proto_mask = ip_proto_mask; 279311ce2ba3SJiri Pirko entry->key.acl.ip_tos = ip_tos; 279411ce2ba3SJiri Pirko entry->key.acl.ip_tos_mask = ip_tos_mask; 279511ce2ba3SJiri Pirko entry->key.acl.group_id = group_id; 279611ce2ba3SJiri Pirko 279711ce2ba3SJiri Pirko return rocker_flow_tbl_do(rocker_port, trans, flags, entry); 279811ce2ba3SJiri Pirko } 279911ce2ba3SJiri Pirko 280011ce2ba3SJiri Pirko static struct rocker_group_tbl_entry * 280111ce2ba3SJiri Pirko rocker_group_tbl_find(const struct rocker *rocker, 280211ce2ba3SJiri Pirko const struct rocker_group_tbl_entry *match) 280311ce2ba3SJiri Pirko { 280411ce2ba3SJiri Pirko struct rocker_group_tbl_entry *found; 280511ce2ba3SJiri Pirko 280611ce2ba3SJiri Pirko hash_for_each_possible(rocker->group_tbl, found, 280711ce2ba3SJiri Pirko entry, match->group_id) { 280811ce2ba3SJiri Pirko if (found->group_id == match->group_id) 280911ce2ba3SJiri Pirko return found; 281011ce2ba3SJiri Pirko } 281111ce2ba3SJiri Pirko 281211ce2ba3SJiri Pirko return NULL; 281311ce2ba3SJiri Pirko } 281411ce2ba3SJiri Pirko 281511ce2ba3SJiri Pirko static void rocker_group_tbl_entry_free(struct switchdev_trans *trans, 281611ce2ba3SJiri Pirko struct rocker_group_tbl_entry *entry) 281711ce2ba3SJiri Pirko { 281811ce2ba3SJiri Pirko switch (ROCKER_GROUP_TYPE_GET(entry->group_id)) { 281911ce2ba3SJiri Pirko case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD: 282011ce2ba3SJiri Pirko case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST: 282111ce2ba3SJiri Pirko rocker_kfree(trans, entry->group_ids); 282211ce2ba3SJiri Pirko break; 282311ce2ba3SJiri Pirko default: 282411ce2ba3SJiri Pirko break; 282511ce2ba3SJiri Pirko } 282611ce2ba3SJiri Pirko rocker_kfree(trans, entry); 282711ce2ba3SJiri Pirko } 282811ce2ba3SJiri Pirko 282911ce2ba3SJiri Pirko static int rocker_group_tbl_add(struct rocker_port *rocker_port, 283011ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 283111ce2ba3SJiri Pirko struct rocker_group_tbl_entry *match) 283211ce2ba3SJiri Pirko { 283311ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 283411ce2ba3SJiri Pirko struct rocker_group_tbl_entry *found; 283511ce2ba3SJiri Pirko unsigned long lock_flags; 283611ce2ba3SJiri Pirko 283711ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->group_tbl_lock, lock_flags); 283811ce2ba3SJiri Pirko 283911ce2ba3SJiri Pirko found = rocker_group_tbl_find(rocker, match); 284011ce2ba3SJiri Pirko 284111ce2ba3SJiri Pirko if (found) { 284211ce2ba3SJiri Pirko if (!switchdev_trans_ph_prepare(trans)) 284311ce2ba3SJiri Pirko hash_del(&found->entry); 284411ce2ba3SJiri Pirko rocker_group_tbl_entry_free(trans, found); 284511ce2ba3SJiri Pirko found = match; 284611ce2ba3SJiri Pirko found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_MOD; 284711ce2ba3SJiri Pirko } else { 284811ce2ba3SJiri Pirko found = match; 284911ce2ba3SJiri Pirko found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_ADD; 285011ce2ba3SJiri Pirko } 285111ce2ba3SJiri Pirko 285211ce2ba3SJiri Pirko if (!switchdev_trans_ph_prepare(trans)) 285311ce2ba3SJiri Pirko hash_add(rocker->group_tbl, &found->entry, found->group_id); 285411ce2ba3SJiri Pirko 285511ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->group_tbl_lock, lock_flags); 285611ce2ba3SJiri Pirko 285711ce2ba3SJiri Pirko return rocker_cmd_exec(rocker_port, trans, flags, 285811ce2ba3SJiri Pirko rocker_cmd_group_tbl_add, found, NULL, NULL); 285911ce2ba3SJiri Pirko } 286011ce2ba3SJiri Pirko 286111ce2ba3SJiri Pirko static int rocker_group_tbl_del(struct rocker_port *rocker_port, 286211ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 286311ce2ba3SJiri Pirko struct rocker_group_tbl_entry *match) 286411ce2ba3SJiri Pirko { 286511ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 286611ce2ba3SJiri Pirko struct rocker_group_tbl_entry *found; 286711ce2ba3SJiri Pirko unsigned long lock_flags; 286811ce2ba3SJiri Pirko int err = 0; 286911ce2ba3SJiri Pirko 287011ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->group_tbl_lock, lock_flags); 287111ce2ba3SJiri Pirko 287211ce2ba3SJiri Pirko found = rocker_group_tbl_find(rocker, match); 287311ce2ba3SJiri Pirko 287411ce2ba3SJiri Pirko if (found) { 287511ce2ba3SJiri Pirko if (!switchdev_trans_ph_prepare(trans)) 287611ce2ba3SJiri Pirko hash_del(&found->entry); 287711ce2ba3SJiri Pirko found->cmd = ROCKER_TLV_CMD_TYPE_OF_DPA_GROUP_DEL; 287811ce2ba3SJiri Pirko } 287911ce2ba3SJiri Pirko 288011ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->group_tbl_lock, lock_flags); 288111ce2ba3SJiri Pirko 288211ce2ba3SJiri Pirko rocker_group_tbl_entry_free(trans, match); 288311ce2ba3SJiri Pirko 288411ce2ba3SJiri Pirko if (found) { 288511ce2ba3SJiri Pirko err = rocker_cmd_exec(rocker_port, trans, flags, 288611ce2ba3SJiri Pirko rocker_cmd_group_tbl_del, 288711ce2ba3SJiri Pirko found, NULL, NULL); 288811ce2ba3SJiri Pirko rocker_group_tbl_entry_free(trans, found); 288911ce2ba3SJiri Pirko } 289011ce2ba3SJiri Pirko 289111ce2ba3SJiri Pirko return err; 289211ce2ba3SJiri Pirko } 289311ce2ba3SJiri Pirko 289411ce2ba3SJiri Pirko static int rocker_group_tbl_do(struct rocker_port *rocker_port, 289511ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 289611ce2ba3SJiri Pirko struct rocker_group_tbl_entry *entry) 289711ce2ba3SJiri Pirko { 289811ce2ba3SJiri Pirko if (flags & ROCKER_OP_FLAG_REMOVE) 289911ce2ba3SJiri Pirko return rocker_group_tbl_del(rocker_port, trans, flags, entry); 290011ce2ba3SJiri Pirko else 290111ce2ba3SJiri Pirko return rocker_group_tbl_add(rocker_port, trans, flags, entry); 290211ce2ba3SJiri Pirko } 290311ce2ba3SJiri Pirko 290411ce2ba3SJiri Pirko static int rocker_group_l2_interface(struct rocker_port *rocker_port, 290511ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 290611ce2ba3SJiri Pirko __be16 vlan_id, u32 out_pport, 290711ce2ba3SJiri Pirko int pop_vlan) 290811ce2ba3SJiri Pirko { 290911ce2ba3SJiri Pirko struct rocker_group_tbl_entry *entry; 291011ce2ba3SJiri Pirko 291111ce2ba3SJiri Pirko entry = rocker_kzalloc(trans, flags, sizeof(*entry)); 291211ce2ba3SJiri Pirko if (!entry) 291311ce2ba3SJiri Pirko return -ENOMEM; 291411ce2ba3SJiri Pirko 291511ce2ba3SJiri Pirko entry->group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport); 291611ce2ba3SJiri Pirko entry->l2_interface.pop_vlan = pop_vlan; 291711ce2ba3SJiri Pirko 291811ce2ba3SJiri Pirko return rocker_group_tbl_do(rocker_port, trans, flags, entry); 291911ce2ba3SJiri Pirko } 292011ce2ba3SJiri Pirko 292111ce2ba3SJiri Pirko static int rocker_group_l2_fan_out(struct rocker_port *rocker_port, 292211ce2ba3SJiri Pirko struct switchdev_trans *trans, 292311ce2ba3SJiri Pirko int flags, u8 group_count, 292411ce2ba3SJiri Pirko const u32 *group_ids, u32 group_id) 292511ce2ba3SJiri Pirko { 292611ce2ba3SJiri Pirko struct rocker_group_tbl_entry *entry; 292711ce2ba3SJiri Pirko 292811ce2ba3SJiri Pirko entry = rocker_kzalloc(trans, flags, sizeof(*entry)); 292911ce2ba3SJiri Pirko if (!entry) 293011ce2ba3SJiri Pirko return -ENOMEM; 293111ce2ba3SJiri Pirko 293211ce2ba3SJiri Pirko entry->group_id = group_id; 293311ce2ba3SJiri Pirko entry->group_count = group_count; 293411ce2ba3SJiri Pirko 293511ce2ba3SJiri Pirko entry->group_ids = rocker_kcalloc(trans, flags, 293611ce2ba3SJiri Pirko group_count, sizeof(u32)); 293711ce2ba3SJiri Pirko if (!entry->group_ids) { 293811ce2ba3SJiri Pirko rocker_kfree(trans, entry); 293911ce2ba3SJiri Pirko return -ENOMEM; 294011ce2ba3SJiri Pirko } 294111ce2ba3SJiri Pirko memcpy(entry->group_ids, group_ids, group_count * sizeof(u32)); 294211ce2ba3SJiri Pirko 294311ce2ba3SJiri Pirko return rocker_group_tbl_do(rocker_port, trans, flags, entry); 294411ce2ba3SJiri Pirko } 294511ce2ba3SJiri Pirko 294611ce2ba3SJiri Pirko static int rocker_group_l2_flood(struct rocker_port *rocker_port, 294711ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 294811ce2ba3SJiri Pirko __be16 vlan_id, u8 group_count, 294911ce2ba3SJiri Pirko const u32 *group_ids, u32 group_id) 295011ce2ba3SJiri Pirko { 295111ce2ba3SJiri Pirko return rocker_group_l2_fan_out(rocker_port, trans, flags, 295211ce2ba3SJiri Pirko group_count, group_ids, 295311ce2ba3SJiri Pirko group_id); 295411ce2ba3SJiri Pirko } 295511ce2ba3SJiri Pirko 295611ce2ba3SJiri Pirko static int rocker_group_l3_unicast(struct rocker_port *rocker_port, 295711ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 295811ce2ba3SJiri Pirko u32 index, const u8 *src_mac, const u8 *dst_mac, 295911ce2ba3SJiri Pirko __be16 vlan_id, bool ttl_check, u32 pport) 296011ce2ba3SJiri Pirko { 296111ce2ba3SJiri Pirko struct rocker_group_tbl_entry *entry; 296211ce2ba3SJiri Pirko 296311ce2ba3SJiri Pirko entry = rocker_kzalloc(trans, flags, sizeof(*entry)); 296411ce2ba3SJiri Pirko if (!entry) 296511ce2ba3SJiri Pirko return -ENOMEM; 296611ce2ba3SJiri Pirko 296711ce2ba3SJiri Pirko entry->group_id = ROCKER_GROUP_L3_UNICAST(index); 296811ce2ba3SJiri Pirko if (src_mac) 296911ce2ba3SJiri Pirko ether_addr_copy(entry->l3_unicast.eth_src, src_mac); 297011ce2ba3SJiri Pirko if (dst_mac) 297111ce2ba3SJiri Pirko ether_addr_copy(entry->l3_unicast.eth_dst, dst_mac); 297211ce2ba3SJiri Pirko entry->l3_unicast.vlan_id = vlan_id; 297311ce2ba3SJiri Pirko entry->l3_unicast.ttl_check = ttl_check; 297411ce2ba3SJiri Pirko entry->l3_unicast.group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, pport); 297511ce2ba3SJiri Pirko 297611ce2ba3SJiri Pirko return rocker_group_tbl_do(rocker_port, trans, flags, entry); 297711ce2ba3SJiri Pirko } 297811ce2ba3SJiri Pirko 297911ce2ba3SJiri Pirko static struct rocker_neigh_tbl_entry * 298011ce2ba3SJiri Pirko rocker_neigh_tbl_find(const struct rocker *rocker, __be32 ip_addr) 298111ce2ba3SJiri Pirko { 298211ce2ba3SJiri Pirko struct rocker_neigh_tbl_entry *found; 298311ce2ba3SJiri Pirko 298411ce2ba3SJiri Pirko hash_for_each_possible(rocker->neigh_tbl, found, 298511ce2ba3SJiri Pirko entry, be32_to_cpu(ip_addr)) 298611ce2ba3SJiri Pirko if (found->ip_addr == ip_addr) 298711ce2ba3SJiri Pirko return found; 298811ce2ba3SJiri Pirko 298911ce2ba3SJiri Pirko return NULL; 299011ce2ba3SJiri Pirko } 299111ce2ba3SJiri Pirko 299211ce2ba3SJiri Pirko static void _rocker_neigh_add(struct rocker *rocker, 299311ce2ba3SJiri Pirko struct switchdev_trans *trans, 299411ce2ba3SJiri Pirko struct rocker_neigh_tbl_entry *entry) 299511ce2ba3SJiri Pirko { 299611ce2ba3SJiri Pirko if (!switchdev_trans_ph_commit(trans)) 299711ce2ba3SJiri Pirko entry->index = rocker->neigh_tbl_next_index++; 299811ce2ba3SJiri Pirko if (switchdev_trans_ph_prepare(trans)) 299911ce2ba3SJiri Pirko return; 300011ce2ba3SJiri Pirko entry->ref_count++; 300111ce2ba3SJiri Pirko hash_add(rocker->neigh_tbl, &entry->entry, 300211ce2ba3SJiri Pirko be32_to_cpu(entry->ip_addr)); 300311ce2ba3SJiri Pirko } 300411ce2ba3SJiri Pirko 300511ce2ba3SJiri Pirko static void _rocker_neigh_del(struct switchdev_trans *trans, 300611ce2ba3SJiri Pirko struct rocker_neigh_tbl_entry *entry) 300711ce2ba3SJiri Pirko { 300811ce2ba3SJiri Pirko if (switchdev_trans_ph_prepare(trans)) 300911ce2ba3SJiri Pirko return; 301011ce2ba3SJiri Pirko if (--entry->ref_count == 0) { 301111ce2ba3SJiri Pirko hash_del(&entry->entry); 301211ce2ba3SJiri Pirko rocker_kfree(trans, entry); 301311ce2ba3SJiri Pirko } 301411ce2ba3SJiri Pirko } 301511ce2ba3SJiri Pirko 301611ce2ba3SJiri Pirko static void _rocker_neigh_update(struct rocker_neigh_tbl_entry *entry, 301711ce2ba3SJiri Pirko struct switchdev_trans *trans, 301811ce2ba3SJiri Pirko const u8 *eth_dst, bool ttl_check) 301911ce2ba3SJiri Pirko { 302011ce2ba3SJiri Pirko if (eth_dst) { 302111ce2ba3SJiri Pirko ether_addr_copy(entry->eth_dst, eth_dst); 302211ce2ba3SJiri Pirko entry->ttl_check = ttl_check; 302311ce2ba3SJiri Pirko } else if (!switchdev_trans_ph_prepare(trans)) { 302411ce2ba3SJiri Pirko entry->ref_count++; 302511ce2ba3SJiri Pirko } 302611ce2ba3SJiri Pirko } 302711ce2ba3SJiri Pirko 302811ce2ba3SJiri Pirko static int rocker_port_ipv4_neigh(struct rocker_port *rocker_port, 302911ce2ba3SJiri Pirko struct switchdev_trans *trans, 303011ce2ba3SJiri Pirko int flags, __be32 ip_addr, const u8 *eth_dst) 303111ce2ba3SJiri Pirko { 303211ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 303311ce2ba3SJiri Pirko struct rocker_neigh_tbl_entry *entry; 303411ce2ba3SJiri Pirko struct rocker_neigh_tbl_entry *found; 303511ce2ba3SJiri Pirko unsigned long lock_flags; 303611ce2ba3SJiri Pirko __be16 eth_type = htons(ETH_P_IP); 303711ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl = 303811ce2ba3SJiri Pirko ROCKER_OF_DPA_TABLE_ID_ACL_POLICY; 303911ce2ba3SJiri Pirko u32 group_id; 304011ce2ba3SJiri Pirko u32 priority = 0; 304111ce2ba3SJiri Pirko bool adding = !(flags & ROCKER_OP_FLAG_REMOVE); 304211ce2ba3SJiri Pirko bool updating; 304311ce2ba3SJiri Pirko bool removing; 304411ce2ba3SJiri Pirko int err = 0; 304511ce2ba3SJiri Pirko 304611ce2ba3SJiri Pirko entry = rocker_kzalloc(trans, flags, sizeof(*entry)); 304711ce2ba3SJiri Pirko if (!entry) 304811ce2ba3SJiri Pirko return -ENOMEM; 304911ce2ba3SJiri Pirko 305011ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->neigh_tbl_lock, lock_flags); 305111ce2ba3SJiri Pirko 305211ce2ba3SJiri Pirko found = rocker_neigh_tbl_find(rocker, ip_addr); 305311ce2ba3SJiri Pirko 305411ce2ba3SJiri Pirko updating = found && adding; 305511ce2ba3SJiri Pirko removing = found && !adding; 305611ce2ba3SJiri Pirko adding = !found && adding; 305711ce2ba3SJiri Pirko 305811ce2ba3SJiri Pirko if (adding) { 305911ce2ba3SJiri Pirko entry->ip_addr = ip_addr; 306011ce2ba3SJiri Pirko entry->dev = rocker_port->dev; 306111ce2ba3SJiri Pirko ether_addr_copy(entry->eth_dst, eth_dst); 306211ce2ba3SJiri Pirko entry->ttl_check = true; 306311ce2ba3SJiri Pirko _rocker_neigh_add(rocker, trans, entry); 306411ce2ba3SJiri Pirko } else if (removing) { 306511ce2ba3SJiri Pirko memcpy(entry, found, sizeof(*entry)); 306611ce2ba3SJiri Pirko _rocker_neigh_del(trans, found); 306711ce2ba3SJiri Pirko } else if (updating) { 306811ce2ba3SJiri Pirko _rocker_neigh_update(found, trans, eth_dst, true); 306911ce2ba3SJiri Pirko memcpy(entry, found, sizeof(*entry)); 307011ce2ba3SJiri Pirko } else { 307111ce2ba3SJiri Pirko err = -ENOENT; 307211ce2ba3SJiri Pirko } 307311ce2ba3SJiri Pirko 307411ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->neigh_tbl_lock, lock_flags); 307511ce2ba3SJiri Pirko 307611ce2ba3SJiri Pirko if (err) 307711ce2ba3SJiri Pirko goto err_out; 307811ce2ba3SJiri Pirko 307911ce2ba3SJiri Pirko /* For each active neighbor, we have an L3 unicast group and 308011ce2ba3SJiri Pirko * a /32 route to the neighbor, which uses the L3 unicast 308111ce2ba3SJiri Pirko * group. The L3 unicast group can also be referred to by 308211ce2ba3SJiri Pirko * other routes' nexthops. 308311ce2ba3SJiri Pirko */ 308411ce2ba3SJiri Pirko 308511ce2ba3SJiri Pirko err = rocker_group_l3_unicast(rocker_port, trans, flags, 308611ce2ba3SJiri Pirko entry->index, 308711ce2ba3SJiri Pirko rocker_port->dev->dev_addr, 308811ce2ba3SJiri Pirko entry->eth_dst, 308911ce2ba3SJiri Pirko rocker_port->internal_vlan_id, 309011ce2ba3SJiri Pirko entry->ttl_check, 309111ce2ba3SJiri Pirko rocker_port->pport); 309211ce2ba3SJiri Pirko if (err) { 309311ce2ba3SJiri Pirko netdev_err(rocker_port->dev, 309411ce2ba3SJiri Pirko "Error (%d) L3 unicast group index %d\n", 309511ce2ba3SJiri Pirko err, entry->index); 309611ce2ba3SJiri Pirko goto err_out; 309711ce2ba3SJiri Pirko } 309811ce2ba3SJiri Pirko 309911ce2ba3SJiri Pirko if (adding || removing) { 310011ce2ba3SJiri Pirko group_id = ROCKER_GROUP_L3_UNICAST(entry->index); 310111ce2ba3SJiri Pirko err = rocker_flow_tbl_ucast4_routing(rocker_port, trans, 310211ce2ba3SJiri Pirko eth_type, ip_addr, 310311ce2ba3SJiri Pirko inet_make_mask(32), 310411ce2ba3SJiri Pirko priority, goto_tbl, 310511ce2ba3SJiri Pirko group_id, flags); 310611ce2ba3SJiri Pirko 310711ce2ba3SJiri Pirko if (err) 310811ce2ba3SJiri Pirko netdev_err(rocker_port->dev, 310911ce2ba3SJiri Pirko "Error (%d) /32 unicast route %pI4 group 0x%08x\n", 311011ce2ba3SJiri Pirko err, &entry->ip_addr, group_id); 311111ce2ba3SJiri Pirko } 311211ce2ba3SJiri Pirko 311311ce2ba3SJiri Pirko err_out: 311411ce2ba3SJiri Pirko if (!adding) 311511ce2ba3SJiri Pirko rocker_kfree(trans, entry); 311611ce2ba3SJiri Pirko 311711ce2ba3SJiri Pirko return err; 311811ce2ba3SJiri Pirko } 311911ce2ba3SJiri Pirko 312011ce2ba3SJiri Pirko static int rocker_port_ipv4_resolve(struct rocker_port *rocker_port, 312111ce2ba3SJiri Pirko struct switchdev_trans *trans, 312211ce2ba3SJiri Pirko __be32 ip_addr) 312311ce2ba3SJiri Pirko { 312411ce2ba3SJiri Pirko struct net_device *dev = rocker_port->dev; 312511ce2ba3SJiri Pirko struct neighbour *n = __ipv4_neigh_lookup(dev, (__force u32)ip_addr); 312611ce2ba3SJiri Pirko int err = 0; 312711ce2ba3SJiri Pirko 312811ce2ba3SJiri Pirko if (!n) { 312911ce2ba3SJiri Pirko n = neigh_create(&arp_tbl, &ip_addr, dev); 313011ce2ba3SJiri Pirko if (IS_ERR(n)) 313111ce2ba3SJiri Pirko return IS_ERR(n); 313211ce2ba3SJiri Pirko } 313311ce2ba3SJiri Pirko 313411ce2ba3SJiri Pirko /* If the neigh is already resolved, then go ahead and 313511ce2ba3SJiri Pirko * install the entry, otherwise start the ARP process to 313611ce2ba3SJiri Pirko * resolve the neigh. 313711ce2ba3SJiri Pirko */ 313811ce2ba3SJiri Pirko 313911ce2ba3SJiri Pirko if (n->nud_state & NUD_VALID) 314011ce2ba3SJiri Pirko err = rocker_port_ipv4_neigh(rocker_port, trans, 0, 314111ce2ba3SJiri Pirko ip_addr, n->ha); 314211ce2ba3SJiri Pirko else 314311ce2ba3SJiri Pirko neigh_event_send(n, NULL); 314411ce2ba3SJiri Pirko 314511ce2ba3SJiri Pirko neigh_release(n); 314611ce2ba3SJiri Pirko return err; 314711ce2ba3SJiri Pirko } 314811ce2ba3SJiri Pirko 314911ce2ba3SJiri Pirko static int rocker_port_ipv4_nh(struct rocker_port *rocker_port, 315011ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 315111ce2ba3SJiri Pirko __be32 ip_addr, u32 *index) 315211ce2ba3SJiri Pirko { 315311ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 315411ce2ba3SJiri Pirko struct rocker_neigh_tbl_entry *entry; 315511ce2ba3SJiri Pirko struct rocker_neigh_tbl_entry *found; 315611ce2ba3SJiri Pirko unsigned long lock_flags; 315711ce2ba3SJiri Pirko bool adding = !(flags & ROCKER_OP_FLAG_REMOVE); 315811ce2ba3SJiri Pirko bool updating; 315911ce2ba3SJiri Pirko bool removing; 316011ce2ba3SJiri Pirko bool resolved = true; 316111ce2ba3SJiri Pirko int err = 0; 316211ce2ba3SJiri Pirko 316311ce2ba3SJiri Pirko entry = rocker_kzalloc(trans, flags, sizeof(*entry)); 316411ce2ba3SJiri Pirko if (!entry) 316511ce2ba3SJiri Pirko return -ENOMEM; 316611ce2ba3SJiri Pirko 316711ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->neigh_tbl_lock, lock_flags); 316811ce2ba3SJiri Pirko 316911ce2ba3SJiri Pirko found = rocker_neigh_tbl_find(rocker, ip_addr); 317011ce2ba3SJiri Pirko if (found) 317111ce2ba3SJiri Pirko *index = found->index; 317211ce2ba3SJiri Pirko 317311ce2ba3SJiri Pirko updating = found && adding; 317411ce2ba3SJiri Pirko removing = found && !adding; 317511ce2ba3SJiri Pirko adding = !found && adding; 317611ce2ba3SJiri Pirko 317711ce2ba3SJiri Pirko if (adding) { 317811ce2ba3SJiri Pirko entry->ip_addr = ip_addr; 317911ce2ba3SJiri Pirko entry->dev = rocker_port->dev; 318011ce2ba3SJiri Pirko _rocker_neigh_add(rocker, trans, entry); 318111ce2ba3SJiri Pirko *index = entry->index; 318211ce2ba3SJiri Pirko resolved = false; 318311ce2ba3SJiri Pirko } else if (removing) { 318411ce2ba3SJiri Pirko _rocker_neigh_del(trans, found); 318511ce2ba3SJiri Pirko } else if (updating) { 318611ce2ba3SJiri Pirko _rocker_neigh_update(found, trans, NULL, false); 318711ce2ba3SJiri Pirko resolved = !is_zero_ether_addr(found->eth_dst); 318811ce2ba3SJiri Pirko } else { 318911ce2ba3SJiri Pirko err = -ENOENT; 319011ce2ba3SJiri Pirko } 319111ce2ba3SJiri Pirko 319211ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->neigh_tbl_lock, lock_flags); 319311ce2ba3SJiri Pirko 319411ce2ba3SJiri Pirko if (!adding) 319511ce2ba3SJiri Pirko rocker_kfree(trans, entry); 319611ce2ba3SJiri Pirko 319711ce2ba3SJiri Pirko if (err) 319811ce2ba3SJiri Pirko return err; 319911ce2ba3SJiri Pirko 320011ce2ba3SJiri Pirko /* Resolved means neigh ip_addr is resolved to neigh mac. */ 320111ce2ba3SJiri Pirko 320211ce2ba3SJiri Pirko if (!resolved) 320311ce2ba3SJiri Pirko err = rocker_port_ipv4_resolve(rocker_port, trans, ip_addr); 320411ce2ba3SJiri Pirko 320511ce2ba3SJiri Pirko return err; 320611ce2ba3SJiri Pirko } 320711ce2ba3SJiri Pirko 320811ce2ba3SJiri Pirko static int rocker_port_vlan_flood_group(struct rocker_port *rocker_port, 320911ce2ba3SJiri Pirko struct switchdev_trans *trans, 321011ce2ba3SJiri Pirko int flags, __be16 vlan_id) 321111ce2ba3SJiri Pirko { 321211ce2ba3SJiri Pirko struct rocker_port *p; 321311ce2ba3SJiri Pirko const struct rocker *rocker = rocker_port->rocker; 321411ce2ba3SJiri Pirko u32 group_id = ROCKER_GROUP_L2_FLOOD(vlan_id, 0); 321511ce2ba3SJiri Pirko u32 *group_ids; 321611ce2ba3SJiri Pirko u8 group_count = 0; 321711ce2ba3SJiri Pirko int err = 0; 321811ce2ba3SJiri Pirko int i; 321911ce2ba3SJiri Pirko 322011ce2ba3SJiri Pirko group_ids = rocker_kcalloc(trans, flags, 322111ce2ba3SJiri Pirko rocker->port_count, sizeof(u32)); 322211ce2ba3SJiri Pirko if (!group_ids) 322311ce2ba3SJiri Pirko return -ENOMEM; 322411ce2ba3SJiri Pirko 322511ce2ba3SJiri Pirko /* Adjust the flood group for this VLAN. The flood group 322611ce2ba3SJiri Pirko * references an L2 interface group for each port in this 322711ce2ba3SJiri Pirko * VLAN. 322811ce2ba3SJiri Pirko */ 322911ce2ba3SJiri Pirko 323011ce2ba3SJiri Pirko for (i = 0; i < rocker->port_count; i++) { 323111ce2ba3SJiri Pirko p = rocker->ports[i]; 323211ce2ba3SJiri Pirko if (!p) 323311ce2ba3SJiri Pirko continue; 323411ce2ba3SJiri Pirko if (!rocker_port_is_bridged(p)) 323511ce2ba3SJiri Pirko continue; 323611ce2ba3SJiri Pirko if (test_bit(ntohs(vlan_id), p->vlan_bitmap)) { 323711ce2ba3SJiri Pirko group_ids[group_count++] = 323811ce2ba3SJiri Pirko ROCKER_GROUP_L2_INTERFACE(vlan_id, p->pport); 323911ce2ba3SJiri Pirko } 324011ce2ba3SJiri Pirko } 324111ce2ba3SJiri Pirko 324211ce2ba3SJiri Pirko /* If there are no bridged ports in this VLAN, we're done */ 324311ce2ba3SJiri Pirko if (group_count == 0) 324411ce2ba3SJiri Pirko goto no_ports_in_vlan; 324511ce2ba3SJiri Pirko 324611ce2ba3SJiri Pirko err = rocker_group_l2_flood(rocker_port, trans, flags, vlan_id, 324711ce2ba3SJiri Pirko group_count, group_ids, group_id); 324811ce2ba3SJiri Pirko if (err) 324911ce2ba3SJiri Pirko netdev_err(rocker_port->dev, 325011ce2ba3SJiri Pirko "Error (%d) port VLAN l2 flood group\n", err); 325111ce2ba3SJiri Pirko 325211ce2ba3SJiri Pirko no_ports_in_vlan: 325311ce2ba3SJiri Pirko rocker_kfree(trans, group_ids); 325411ce2ba3SJiri Pirko return err; 325511ce2ba3SJiri Pirko } 325611ce2ba3SJiri Pirko 325711ce2ba3SJiri Pirko static int rocker_port_vlan_l2_groups(struct rocker_port *rocker_port, 325811ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 325911ce2ba3SJiri Pirko __be16 vlan_id, bool pop_vlan) 326011ce2ba3SJiri Pirko { 326111ce2ba3SJiri Pirko const struct rocker *rocker = rocker_port->rocker; 326211ce2ba3SJiri Pirko struct rocker_port *p; 326311ce2ba3SJiri Pirko bool adding = !(flags & ROCKER_OP_FLAG_REMOVE); 326411ce2ba3SJiri Pirko u32 out_pport; 326511ce2ba3SJiri Pirko int ref = 0; 326611ce2ba3SJiri Pirko int err; 326711ce2ba3SJiri Pirko int i; 326811ce2ba3SJiri Pirko 326911ce2ba3SJiri Pirko /* An L2 interface group for this port in this VLAN, but 327011ce2ba3SJiri Pirko * only when port STP state is LEARNING|FORWARDING. 327111ce2ba3SJiri Pirko */ 327211ce2ba3SJiri Pirko 327311ce2ba3SJiri Pirko if (rocker_port->stp_state == BR_STATE_LEARNING || 327411ce2ba3SJiri Pirko rocker_port->stp_state == BR_STATE_FORWARDING) { 327511ce2ba3SJiri Pirko out_pport = rocker_port->pport; 327611ce2ba3SJiri Pirko err = rocker_group_l2_interface(rocker_port, trans, flags, 327711ce2ba3SJiri Pirko vlan_id, out_pport, pop_vlan); 327811ce2ba3SJiri Pirko if (err) { 327911ce2ba3SJiri Pirko netdev_err(rocker_port->dev, 328011ce2ba3SJiri Pirko "Error (%d) port VLAN l2 group for pport %d\n", 328111ce2ba3SJiri Pirko err, out_pport); 328211ce2ba3SJiri Pirko return err; 328311ce2ba3SJiri Pirko } 328411ce2ba3SJiri Pirko } 328511ce2ba3SJiri Pirko 328611ce2ba3SJiri Pirko /* An L2 interface group for this VLAN to CPU port. 328711ce2ba3SJiri Pirko * Add when first port joins this VLAN and destroy when 328811ce2ba3SJiri Pirko * last port leaves this VLAN. 328911ce2ba3SJiri Pirko */ 329011ce2ba3SJiri Pirko 329111ce2ba3SJiri Pirko for (i = 0; i < rocker->port_count; i++) { 329211ce2ba3SJiri Pirko p = rocker->ports[i]; 329311ce2ba3SJiri Pirko if (p && test_bit(ntohs(vlan_id), p->vlan_bitmap)) 329411ce2ba3SJiri Pirko ref++; 329511ce2ba3SJiri Pirko } 329611ce2ba3SJiri Pirko 329711ce2ba3SJiri Pirko if ((!adding || ref != 1) && (adding || ref != 0)) 329811ce2ba3SJiri Pirko return 0; 329911ce2ba3SJiri Pirko 330011ce2ba3SJiri Pirko out_pport = 0; 330111ce2ba3SJiri Pirko err = rocker_group_l2_interface(rocker_port, trans, flags, 330211ce2ba3SJiri Pirko vlan_id, out_pport, pop_vlan); 330311ce2ba3SJiri Pirko if (err) { 330411ce2ba3SJiri Pirko netdev_err(rocker_port->dev, 330511ce2ba3SJiri Pirko "Error (%d) port VLAN l2 group for CPU port\n", err); 330611ce2ba3SJiri Pirko return err; 330711ce2ba3SJiri Pirko } 330811ce2ba3SJiri Pirko 330911ce2ba3SJiri Pirko return 0; 331011ce2ba3SJiri Pirko } 331111ce2ba3SJiri Pirko 331211ce2ba3SJiri Pirko static struct rocker_ctrl { 331311ce2ba3SJiri Pirko const u8 *eth_dst; 331411ce2ba3SJiri Pirko const u8 *eth_dst_mask; 331511ce2ba3SJiri Pirko __be16 eth_type; 331611ce2ba3SJiri Pirko bool acl; 331711ce2ba3SJiri Pirko bool bridge; 331811ce2ba3SJiri Pirko bool term; 331911ce2ba3SJiri Pirko bool copy_to_cpu; 332011ce2ba3SJiri Pirko } rocker_ctrls[] = { 332111ce2ba3SJiri Pirko [ROCKER_CTRL_LINK_LOCAL_MCAST] = { 332211ce2ba3SJiri Pirko /* pass link local multicast pkts up to CPU for filtering */ 332311ce2ba3SJiri Pirko .eth_dst = ll_mac, 332411ce2ba3SJiri Pirko .eth_dst_mask = ll_mask, 332511ce2ba3SJiri Pirko .acl = true, 332611ce2ba3SJiri Pirko }, 332711ce2ba3SJiri Pirko [ROCKER_CTRL_LOCAL_ARP] = { 332811ce2ba3SJiri Pirko /* pass local ARP pkts up to CPU */ 332911ce2ba3SJiri Pirko .eth_dst = zero_mac, 333011ce2ba3SJiri Pirko .eth_dst_mask = zero_mac, 333111ce2ba3SJiri Pirko .eth_type = htons(ETH_P_ARP), 333211ce2ba3SJiri Pirko .acl = true, 333311ce2ba3SJiri Pirko }, 333411ce2ba3SJiri Pirko [ROCKER_CTRL_IPV4_MCAST] = { 333511ce2ba3SJiri Pirko /* pass IPv4 mcast pkts up to CPU, RFC 1112 */ 333611ce2ba3SJiri Pirko .eth_dst = ipv4_mcast, 333711ce2ba3SJiri Pirko .eth_dst_mask = ipv4_mask, 333811ce2ba3SJiri Pirko .eth_type = htons(ETH_P_IP), 333911ce2ba3SJiri Pirko .term = true, 334011ce2ba3SJiri Pirko .copy_to_cpu = true, 334111ce2ba3SJiri Pirko }, 334211ce2ba3SJiri Pirko [ROCKER_CTRL_IPV6_MCAST] = { 334311ce2ba3SJiri Pirko /* pass IPv6 mcast pkts up to CPU, RFC 2464 */ 334411ce2ba3SJiri Pirko .eth_dst = ipv6_mcast, 334511ce2ba3SJiri Pirko .eth_dst_mask = ipv6_mask, 334611ce2ba3SJiri Pirko .eth_type = htons(ETH_P_IPV6), 334711ce2ba3SJiri Pirko .term = true, 334811ce2ba3SJiri Pirko .copy_to_cpu = true, 334911ce2ba3SJiri Pirko }, 335011ce2ba3SJiri Pirko [ROCKER_CTRL_DFLT_BRIDGING] = { 335111ce2ba3SJiri Pirko /* flood any pkts on vlan */ 335211ce2ba3SJiri Pirko .bridge = true, 335311ce2ba3SJiri Pirko .copy_to_cpu = true, 335411ce2ba3SJiri Pirko }, 335511ce2ba3SJiri Pirko [ROCKER_CTRL_DFLT_OVS] = { 335611ce2ba3SJiri Pirko /* pass all pkts up to CPU */ 335711ce2ba3SJiri Pirko .eth_dst = zero_mac, 335811ce2ba3SJiri Pirko .eth_dst_mask = zero_mac, 335911ce2ba3SJiri Pirko .acl = true, 336011ce2ba3SJiri Pirko }, 336111ce2ba3SJiri Pirko }; 336211ce2ba3SJiri Pirko 336311ce2ba3SJiri Pirko static int rocker_port_ctrl_vlan_acl(struct rocker_port *rocker_port, 336411ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 336511ce2ba3SJiri Pirko const struct rocker_ctrl *ctrl, __be16 vlan_id) 336611ce2ba3SJiri Pirko { 336711ce2ba3SJiri Pirko u32 in_pport = rocker_port->pport; 336811ce2ba3SJiri Pirko u32 in_pport_mask = 0xffffffff; 336911ce2ba3SJiri Pirko u32 out_pport = 0; 337011ce2ba3SJiri Pirko const u8 *eth_src = NULL; 337111ce2ba3SJiri Pirko const u8 *eth_src_mask = NULL; 337211ce2ba3SJiri Pirko __be16 vlan_id_mask = htons(0xffff); 337311ce2ba3SJiri Pirko u8 ip_proto = 0; 337411ce2ba3SJiri Pirko u8 ip_proto_mask = 0; 337511ce2ba3SJiri Pirko u8 ip_tos = 0; 337611ce2ba3SJiri Pirko u8 ip_tos_mask = 0; 337711ce2ba3SJiri Pirko u32 group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport); 337811ce2ba3SJiri Pirko int err; 337911ce2ba3SJiri Pirko 338011ce2ba3SJiri Pirko err = rocker_flow_tbl_acl(rocker_port, trans, flags, 338111ce2ba3SJiri Pirko in_pport, in_pport_mask, 338211ce2ba3SJiri Pirko eth_src, eth_src_mask, 338311ce2ba3SJiri Pirko ctrl->eth_dst, ctrl->eth_dst_mask, 338411ce2ba3SJiri Pirko ctrl->eth_type, 338511ce2ba3SJiri Pirko vlan_id, vlan_id_mask, 338611ce2ba3SJiri Pirko ip_proto, ip_proto_mask, 338711ce2ba3SJiri Pirko ip_tos, ip_tos_mask, 338811ce2ba3SJiri Pirko group_id); 338911ce2ba3SJiri Pirko 339011ce2ba3SJiri Pirko if (err) 339111ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "Error (%d) ctrl ACL\n", err); 339211ce2ba3SJiri Pirko 339311ce2ba3SJiri Pirko return err; 339411ce2ba3SJiri Pirko } 339511ce2ba3SJiri Pirko 339611ce2ba3SJiri Pirko static int rocker_port_ctrl_vlan_bridge(struct rocker_port *rocker_port, 339711ce2ba3SJiri Pirko struct switchdev_trans *trans, 339811ce2ba3SJiri Pirko int flags, 339911ce2ba3SJiri Pirko const struct rocker_ctrl *ctrl, 340011ce2ba3SJiri Pirko __be16 vlan_id) 340111ce2ba3SJiri Pirko { 340211ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl = 340311ce2ba3SJiri Pirko ROCKER_OF_DPA_TABLE_ID_ACL_POLICY; 340411ce2ba3SJiri Pirko u32 group_id = ROCKER_GROUP_L2_FLOOD(vlan_id, 0); 340511ce2ba3SJiri Pirko u32 tunnel_id = 0; 340611ce2ba3SJiri Pirko int err; 340711ce2ba3SJiri Pirko 340811ce2ba3SJiri Pirko if (!rocker_port_is_bridged(rocker_port)) 340911ce2ba3SJiri Pirko return 0; 341011ce2ba3SJiri Pirko 341111ce2ba3SJiri Pirko err = rocker_flow_tbl_bridge(rocker_port, trans, flags, 341211ce2ba3SJiri Pirko ctrl->eth_dst, ctrl->eth_dst_mask, 341311ce2ba3SJiri Pirko vlan_id, tunnel_id, 341411ce2ba3SJiri Pirko goto_tbl, group_id, ctrl->copy_to_cpu); 341511ce2ba3SJiri Pirko 341611ce2ba3SJiri Pirko if (err) 341711ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "Error (%d) ctrl FLOOD\n", err); 341811ce2ba3SJiri Pirko 341911ce2ba3SJiri Pirko return err; 342011ce2ba3SJiri Pirko } 342111ce2ba3SJiri Pirko 342211ce2ba3SJiri Pirko static int rocker_port_ctrl_vlan_term(struct rocker_port *rocker_port, 342311ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 342411ce2ba3SJiri Pirko const struct rocker_ctrl *ctrl, __be16 vlan_id) 342511ce2ba3SJiri Pirko { 342611ce2ba3SJiri Pirko u32 in_pport_mask = 0xffffffff; 342711ce2ba3SJiri Pirko __be16 vlan_id_mask = htons(0xffff); 342811ce2ba3SJiri Pirko int err; 342911ce2ba3SJiri Pirko 343011ce2ba3SJiri Pirko if (ntohs(vlan_id) == 0) 343111ce2ba3SJiri Pirko vlan_id = rocker_port->internal_vlan_id; 343211ce2ba3SJiri Pirko 343311ce2ba3SJiri Pirko err = rocker_flow_tbl_term_mac(rocker_port, trans, 343411ce2ba3SJiri Pirko rocker_port->pport, in_pport_mask, 343511ce2ba3SJiri Pirko ctrl->eth_type, ctrl->eth_dst, 343611ce2ba3SJiri Pirko ctrl->eth_dst_mask, vlan_id, 343711ce2ba3SJiri Pirko vlan_id_mask, ctrl->copy_to_cpu, 343811ce2ba3SJiri Pirko flags); 343911ce2ba3SJiri Pirko 344011ce2ba3SJiri Pirko if (err) 344111ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "Error (%d) ctrl term\n", err); 344211ce2ba3SJiri Pirko 344311ce2ba3SJiri Pirko return err; 344411ce2ba3SJiri Pirko } 344511ce2ba3SJiri Pirko 344611ce2ba3SJiri Pirko static int rocker_port_ctrl_vlan(struct rocker_port *rocker_port, 344711ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 344811ce2ba3SJiri Pirko const struct rocker_ctrl *ctrl, __be16 vlan_id) 344911ce2ba3SJiri Pirko { 345011ce2ba3SJiri Pirko if (ctrl->acl) 345111ce2ba3SJiri Pirko return rocker_port_ctrl_vlan_acl(rocker_port, trans, flags, 345211ce2ba3SJiri Pirko ctrl, vlan_id); 345311ce2ba3SJiri Pirko if (ctrl->bridge) 345411ce2ba3SJiri Pirko return rocker_port_ctrl_vlan_bridge(rocker_port, trans, flags, 345511ce2ba3SJiri Pirko ctrl, vlan_id); 345611ce2ba3SJiri Pirko 345711ce2ba3SJiri Pirko if (ctrl->term) 345811ce2ba3SJiri Pirko return rocker_port_ctrl_vlan_term(rocker_port, trans, flags, 345911ce2ba3SJiri Pirko ctrl, vlan_id); 346011ce2ba3SJiri Pirko 346111ce2ba3SJiri Pirko return -EOPNOTSUPP; 346211ce2ba3SJiri Pirko } 346311ce2ba3SJiri Pirko 346411ce2ba3SJiri Pirko static int rocker_port_ctrl_vlan_add(struct rocker_port *rocker_port, 346511ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 346611ce2ba3SJiri Pirko __be16 vlan_id) 346711ce2ba3SJiri Pirko { 346811ce2ba3SJiri Pirko int err = 0; 346911ce2ba3SJiri Pirko int i; 347011ce2ba3SJiri Pirko 347111ce2ba3SJiri Pirko for (i = 0; i < ROCKER_CTRL_MAX; i++) { 347211ce2ba3SJiri Pirko if (rocker_port->ctrls[i]) { 347311ce2ba3SJiri Pirko err = rocker_port_ctrl_vlan(rocker_port, trans, flags, 347411ce2ba3SJiri Pirko &rocker_ctrls[i], vlan_id); 347511ce2ba3SJiri Pirko if (err) 347611ce2ba3SJiri Pirko return err; 347711ce2ba3SJiri Pirko } 347811ce2ba3SJiri Pirko } 347911ce2ba3SJiri Pirko 348011ce2ba3SJiri Pirko return err; 348111ce2ba3SJiri Pirko } 348211ce2ba3SJiri Pirko 348311ce2ba3SJiri Pirko static int rocker_port_ctrl(struct rocker_port *rocker_port, 348411ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 348511ce2ba3SJiri Pirko const struct rocker_ctrl *ctrl) 348611ce2ba3SJiri Pirko { 348711ce2ba3SJiri Pirko u16 vid; 348811ce2ba3SJiri Pirko int err = 0; 348911ce2ba3SJiri Pirko 349011ce2ba3SJiri Pirko for (vid = 1; vid < VLAN_N_VID; vid++) { 349111ce2ba3SJiri Pirko if (!test_bit(vid, rocker_port->vlan_bitmap)) 349211ce2ba3SJiri Pirko continue; 349311ce2ba3SJiri Pirko err = rocker_port_ctrl_vlan(rocker_port, trans, flags, 349411ce2ba3SJiri Pirko ctrl, htons(vid)); 349511ce2ba3SJiri Pirko if (err) 349611ce2ba3SJiri Pirko break; 349711ce2ba3SJiri Pirko } 349811ce2ba3SJiri Pirko 349911ce2ba3SJiri Pirko return err; 350011ce2ba3SJiri Pirko } 350111ce2ba3SJiri Pirko 350211ce2ba3SJiri Pirko static int rocker_port_vlan(struct rocker_port *rocker_port, 350311ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, u16 vid) 350411ce2ba3SJiri Pirko { 350511ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl = 350611ce2ba3SJiri Pirko ROCKER_OF_DPA_TABLE_ID_TERMINATION_MAC; 350711ce2ba3SJiri Pirko u32 in_pport = rocker_port->pport; 350811ce2ba3SJiri Pirko __be16 vlan_id = htons(vid); 350911ce2ba3SJiri Pirko __be16 vlan_id_mask = htons(0xffff); 351011ce2ba3SJiri Pirko __be16 internal_vlan_id; 351111ce2ba3SJiri Pirko bool untagged; 351211ce2ba3SJiri Pirko bool adding = !(flags & ROCKER_OP_FLAG_REMOVE); 351311ce2ba3SJiri Pirko int err; 351411ce2ba3SJiri Pirko 351511ce2ba3SJiri Pirko internal_vlan_id = rocker_port_vid_to_vlan(rocker_port, vid, &untagged); 351611ce2ba3SJiri Pirko 351711ce2ba3SJiri Pirko if (adding && test_bit(ntohs(internal_vlan_id), 351811ce2ba3SJiri Pirko rocker_port->vlan_bitmap)) 351911ce2ba3SJiri Pirko return 0; /* already added */ 352011ce2ba3SJiri Pirko else if (!adding && !test_bit(ntohs(internal_vlan_id), 352111ce2ba3SJiri Pirko rocker_port->vlan_bitmap)) 352211ce2ba3SJiri Pirko return 0; /* already removed */ 352311ce2ba3SJiri Pirko 352411ce2ba3SJiri Pirko change_bit(ntohs(internal_vlan_id), rocker_port->vlan_bitmap); 352511ce2ba3SJiri Pirko 352611ce2ba3SJiri Pirko if (adding) { 352711ce2ba3SJiri Pirko err = rocker_port_ctrl_vlan_add(rocker_port, trans, flags, 352811ce2ba3SJiri Pirko internal_vlan_id); 352911ce2ba3SJiri Pirko if (err) { 353011ce2ba3SJiri Pirko netdev_err(rocker_port->dev, 353111ce2ba3SJiri Pirko "Error (%d) port ctrl vlan add\n", err); 353211ce2ba3SJiri Pirko goto err_out; 353311ce2ba3SJiri Pirko } 353411ce2ba3SJiri Pirko } 353511ce2ba3SJiri Pirko 353611ce2ba3SJiri Pirko err = rocker_port_vlan_l2_groups(rocker_port, trans, flags, 353711ce2ba3SJiri Pirko internal_vlan_id, untagged); 353811ce2ba3SJiri Pirko if (err) { 353911ce2ba3SJiri Pirko netdev_err(rocker_port->dev, 354011ce2ba3SJiri Pirko "Error (%d) port VLAN l2 groups\n", err); 354111ce2ba3SJiri Pirko goto err_out; 354211ce2ba3SJiri Pirko } 354311ce2ba3SJiri Pirko 354411ce2ba3SJiri Pirko err = rocker_port_vlan_flood_group(rocker_port, trans, flags, 354511ce2ba3SJiri Pirko internal_vlan_id); 354611ce2ba3SJiri Pirko if (err) { 354711ce2ba3SJiri Pirko netdev_err(rocker_port->dev, 354811ce2ba3SJiri Pirko "Error (%d) port VLAN l2 flood group\n", err); 354911ce2ba3SJiri Pirko goto err_out; 355011ce2ba3SJiri Pirko } 355111ce2ba3SJiri Pirko 355211ce2ba3SJiri Pirko err = rocker_flow_tbl_vlan(rocker_port, trans, flags, 355311ce2ba3SJiri Pirko in_pport, vlan_id, vlan_id_mask, 355411ce2ba3SJiri Pirko goto_tbl, untagged, internal_vlan_id); 355511ce2ba3SJiri Pirko if (err) 355611ce2ba3SJiri Pirko netdev_err(rocker_port->dev, 355711ce2ba3SJiri Pirko "Error (%d) port VLAN table\n", err); 355811ce2ba3SJiri Pirko 355911ce2ba3SJiri Pirko err_out: 356011ce2ba3SJiri Pirko if (switchdev_trans_ph_prepare(trans)) 356111ce2ba3SJiri Pirko change_bit(ntohs(internal_vlan_id), rocker_port->vlan_bitmap); 356211ce2ba3SJiri Pirko 356311ce2ba3SJiri Pirko return err; 356411ce2ba3SJiri Pirko } 356511ce2ba3SJiri Pirko 356611ce2ba3SJiri Pirko static int rocker_port_ig_tbl(struct rocker_port *rocker_port, 356711ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags) 356811ce2ba3SJiri Pirko { 356911ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl; 357011ce2ba3SJiri Pirko u32 in_pport; 357111ce2ba3SJiri Pirko u32 in_pport_mask; 357211ce2ba3SJiri Pirko int err; 357311ce2ba3SJiri Pirko 357411ce2ba3SJiri Pirko /* Normal Ethernet Frames. Matches pkts from any local physical 357511ce2ba3SJiri Pirko * ports. Goto VLAN tbl. 357611ce2ba3SJiri Pirko */ 357711ce2ba3SJiri Pirko 357811ce2ba3SJiri Pirko in_pport = 0; 357911ce2ba3SJiri Pirko in_pport_mask = 0xffff0000; 358011ce2ba3SJiri Pirko goto_tbl = ROCKER_OF_DPA_TABLE_ID_VLAN; 358111ce2ba3SJiri Pirko 358211ce2ba3SJiri Pirko err = rocker_flow_tbl_ig_port(rocker_port, trans, flags, 358311ce2ba3SJiri Pirko in_pport, in_pport_mask, 358411ce2ba3SJiri Pirko goto_tbl); 358511ce2ba3SJiri Pirko if (err) 358611ce2ba3SJiri Pirko netdev_err(rocker_port->dev, 358711ce2ba3SJiri Pirko "Error (%d) ingress port table entry\n", err); 358811ce2ba3SJiri Pirko 358911ce2ba3SJiri Pirko return err; 359011ce2ba3SJiri Pirko } 359111ce2ba3SJiri Pirko 359211ce2ba3SJiri Pirko struct rocker_fdb_learn_work { 359311ce2ba3SJiri Pirko struct work_struct work; 359411ce2ba3SJiri Pirko struct rocker_port *rocker_port; 359511ce2ba3SJiri Pirko struct switchdev_trans *trans; 359611ce2ba3SJiri Pirko int flags; 359711ce2ba3SJiri Pirko u8 addr[ETH_ALEN]; 359811ce2ba3SJiri Pirko u16 vid; 359911ce2ba3SJiri Pirko }; 360011ce2ba3SJiri Pirko 360111ce2ba3SJiri Pirko static void rocker_port_fdb_learn_work(struct work_struct *work) 360211ce2ba3SJiri Pirko { 360311ce2ba3SJiri Pirko const struct rocker_fdb_learn_work *lw = 360411ce2ba3SJiri Pirko container_of(work, struct rocker_fdb_learn_work, work); 360511ce2ba3SJiri Pirko bool removing = (lw->flags & ROCKER_OP_FLAG_REMOVE); 360611ce2ba3SJiri Pirko bool learned = (lw->flags & ROCKER_OP_FLAG_LEARNED); 360711ce2ba3SJiri Pirko struct switchdev_notifier_fdb_info info; 360811ce2ba3SJiri Pirko 360911ce2ba3SJiri Pirko info.addr = lw->addr; 361011ce2ba3SJiri Pirko info.vid = lw->vid; 361111ce2ba3SJiri Pirko 361211ce2ba3SJiri Pirko rtnl_lock(); 361311ce2ba3SJiri Pirko if (learned && removing) 361411ce2ba3SJiri Pirko call_switchdev_notifiers(SWITCHDEV_FDB_DEL, 361511ce2ba3SJiri Pirko lw->rocker_port->dev, &info.info); 361611ce2ba3SJiri Pirko else if (learned && !removing) 361711ce2ba3SJiri Pirko call_switchdev_notifiers(SWITCHDEV_FDB_ADD, 361811ce2ba3SJiri Pirko lw->rocker_port->dev, &info.info); 361911ce2ba3SJiri Pirko rtnl_unlock(); 362011ce2ba3SJiri Pirko 362111ce2ba3SJiri Pirko rocker_kfree(lw->trans, work); 362211ce2ba3SJiri Pirko } 362311ce2ba3SJiri Pirko 362411ce2ba3SJiri Pirko static int rocker_port_fdb_learn(struct rocker_port *rocker_port, 362511ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 362611ce2ba3SJiri Pirko const u8 *addr, __be16 vlan_id) 362711ce2ba3SJiri Pirko { 362811ce2ba3SJiri Pirko struct rocker_fdb_learn_work *lw; 362911ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl = 363011ce2ba3SJiri Pirko ROCKER_OF_DPA_TABLE_ID_ACL_POLICY; 363111ce2ba3SJiri Pirko u32 out_pport = rocker_port->pport; 363211ce2ba3SJiri Pirko u32 tunnel_id = 0; 363311ce2ba3SJiri Pirko u32 group_id = ROCKER_GROUP_NONE; 363411ce2ba3SJiri Pirko bool syncing = !!(rocker_port->brport_flags & BR_LEARNING_SYNC); 363511ce2ba3SJiri Pirko bool copy_to_cpu = false; 363611ce2ba3SJiri Pirko int err; 363711ce2ba3SJiri Pirko 363811ce2ba3SJiri Pirko if (rocker_port_is_bridged(rocker_port)) 363911ce2ba3SJiri Pirko group_id = ROCKER_GROUP_L2_INTERFACE(vlan_id, out_pport); 364011ce2ba3SJiri Pirko 364111ce2ba3SJiri Pirko if (!(flags & ROCKER_OP_FLAG_REFRESH)) { 364211ce2ba3SJiri Pirko err = rocker_flow_tbl_bridge(rocker_port, trans, flags, addr, 364311ce2ba3SJiri Pirko NULL, vlan_id, tunnel_id, goto_tbl, 364411ce2ba3SJiri Pirko group_id, copy_to_cpu); 364511ce2ba3SJiri Pirko if (err) 364611ce2ba3SJiri Pirko return err; 364711ce2ba3SJiri Pirko } 364811ce2ba3SJiri Pirko 364911ce2ba3SJiri Pirko if (!syncing) 365011ce2ba3SJiri Pirko return 0; 365111ce2ba3SJiri Pirko 365211ce2ba3SJiri Pirko if (!rocker_port_is_bridged(rocker_port)) 365311ce2ba3SJiri Pirko return 0; 365411ce2ba3SJiri Pirko 365511ce2ba3SJiri Pirko lw = rocker_kzalloc(trans, flags, sizeof(*lw)); 365611ce2ba3SJiri Pirko if (!lw) 365711ce2ba3SJiri Pirko return -ENOMEM; 365811ce2ba3SJiri Pirko 365911ce2ba3SJiri Pirko INIT_WORK(&lw->work, rocker_port_fdb_learn_work); 366011ce2ba3SJiri Pirko 366111ce2ba3SJiri Pirko lw->rocker_port = rocker_port; 366211ce2ba3SJiri Pirko lw->trans = trans; 366311ce2ba3SJiri Pirko lw->flags = flags; 366411ce2ba3SJiri Pirko ether_addr_copy(lw->addr, addr); 366511ce2ba3SJiri Pirko lw->vid = rocker_port_vlan_to_vid(rocker_port, vlan_id); 366611ce2ba3SJiri Pirko 366711ce2ba3SJiri Pirko if (switchdev_trans_ph_prepare(trans)) 366811ce2ba3SJiri Pirko rocker_kfree(trans, lw); 366911ce2ba3SJiri Pirko else 367011ce2ba3SJiri Pirko schedule_work(&lw->work); 367111ce2ba3SJiri Pirko 367211ce2ba3SJiri Pirko return 0; 367311ce2ba3SJiri Pirko } 367411ce2ba3SJiri Pirko 367511ce2ba3SJiri Pirko static struct rocker_fdb_tbl_entry * 367611ce2ba3SJiri Pirko rocker_fdb_tbl_find(const struct rocker *rocker, 367711ce2ba3SJiri Pirko const struct rocker_fdb_tbl_entry *match) 367811ce2ba3SJiri Pirko { 367911ce2ba3SJiri Pirko struct rocker_fdb_tbl_entry *found; 368011ce2ba3SJiri Pirko 368111ce2ba3SJiri Pirko hash_for_each_possible(rocker->fdb_tbl, found, entry, match->key_crc32) 368211ce2ba3SJiri Pirko if (memcmp(&found->key, &match->key, sizeof(found->key)) == 0) 368311ce2ba3SJiri Pirko return found; 368411ce2ba3SJiri Pirko 368511ce2ba3SJiri Pirko return NULL; 368611ce2ba3SJiri Pirko } 368711ce2ba3SJiri Pirko 368811ce2ba3SJiri Pirko static int rocker_port_fdb(struct rocker_port *rocker_port, 368911ce2ba3SJiri Pirko struct switchdev_trans *trans, 369011ce2ba3SJiri Pirko const unsigned char *addr, 369111ce2ba3SJiri Pirko __be16 vlan_id, int flags) 369211ce2ba3SJiri Pirko { 369311ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 369411ce2ba3SJiri Pirko struct rocker_fdb_tbl_entry *fdb; 369511ce2ba3SJiri Pirko struct rocker_fdb_tbl_entry *found; 369611ce2ba3SJiri Pirko bool removing = (flags & ROCKER_OP_FLAG_REMOVE); 369711ce2ba3SJiri Pirko unsigned long lock_flags; 369811ce2ba3SJiri Pirko 369911ce2ba3SJiri Pirko fdb = rocker_kzalloc(trans, flags, sizeof(*fdb)); 370011ce2ba3SJiri Pirko if (!fdb) 370111ce2ba3SJiri Pirko return -ENOMEM; 370211ce2ba3SJiri Pirko 370311ce2ba3SJiri Pirko fdb->learned = (flags & ROCKER_OP_FLAG_LEARNED); 370411ce2ba3SJiri Pirko fdb->touched = jiffies; 370511ce2ba3SJiri Pirko fdb->key.rocker_port = rocker_port; 370611ce2ba3SJiri Pirko ether_addr_copy(fdb->key.addr, addr); 370711ce2ba3SJiri Pirko fdb->key.vlan_id = vlan_id; 370811ce2ba3SJiri Pirko fdb->key_crc32 = crc32(~0, &fdb->key, sizeof(fdb->key)); 370911ce2ba3SJiri Pirko 371011ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags); 371111ce2ba3SJiri Pirko 371211ce2ba3SJiri Pirko found = rocker_fdb_tbl_find(rocker, fdb); 371311ce2ba3SJiri Pirko 371411ce2ba3SJiri Pirko if (found) { 371511ce2ba3SJiri Pirko found->touched = jiffies; 371611ce2ba3SJiri Pirko if (removing) { 371711ce2ba3SJiri Pirko rocker_kfree(trans, fdb); 371811ce2ba3SJiri Pirko if (!switchdev_trans_ph_prepare(trans)) 371911ce2ba3SJiri Pirko hash_del(&found->entry); 372011ce2ba3SJiri Pirko } 372111ce2ba3SJiri Pirko } else if (!removing) { 372211ce2ba3SJiri Pirko if (!switchdev_trans_ph_prepare(trans)) 372311ce2ba3SJiri Pirko hash_add(rocker->fdb_tbl, &fdb->entry, 372411ce2ba3SJiri Pirko fdb->key_crc32); 372511ce2ba3SJiri Pirko } 372611ce2ba3SJiri Pirko 372711ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags); 372811ce2ba3SJiri Pirko 372911ce2ba3SJiri Pirko /* Check if adding and already exists, or removing and can't find */ 373011ce2ba3SJiri Pirko if (!found != !removing) { 373111ce2ba3SJiri Pirko rocker_kfree(trans, fdb); 373211ce2ba3SJiri Pirko if (!found && removing) 373311ce2ba3SJiri Pirko return 0; 373411ce2ba3SJiri Pirko /* Refreshing existing to update aging timers */ 373511ce2ba3SJiri Pirko flags |= ROCKER_OP_FLAG_REFRESH; 373611ce2ba3SJiri Pirko } 373711ce2ba3SJiri Pirko 373811ce2ba3SJiri Pirko return rocker_port_fdb_learn(rocker_port, trans, flags, addr, vlan_id); 373911ce2ba3SJiri Pirko } 374011ce2ba3SJiri Pirko 374111ce2ba3SJiri Pirko static int rocker_port_fdb_flush(struct rocker_port *rocker_port, 374211ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags) 374311ce2ba3SJiri Pirko { 374411ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 374511ce2ba3SJiri Pirko struct rocker_fdb_tbl_entry *found; 374611ce2ba3SJiri Pirko unsigned long lock_flags; 374711ce2ba3SJiri Pirko struct hlist_node *tmp; 374811ce2ba3SJiri Pirko int bkt; 374911ce2ba3SJiri Pirko int err = 0; 375011ce2ba3SJiri Pirko 375111ce2ba3SJiri Pirko if (rocker_port->stp_state == BR_STATE_LEARNING || 375211ce2ba3SJiri Pirko rocker_port->stp_state == BR_STATE_FORWARDING) 375311ce2ba3SJiri Pirko return 0; 375411ce2ba3SJiri Pirko 375511ce2ba3SJiri Pirko flags |= ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE; 375611ce2ba3SJiri Pirko 375711ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags); 375811ce2ba3SJiri Pirko 375911ce2ba3SJiri Pirko hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) { 376011ce2ba3SJiri Pirko if (found->key.rocker_port != rocker_port) 376111ce2ba3SJiri Pirko continue; 376211ce2ba3SJiri Pirko if (!found->learned) 376311ce2ba3SJiri Pirko continue; 376411ce2ba3SJiri Pirko err = rocker_port_fdb_learn(rocker_port, trans, flags, 376511ce2ba3SJiri Pirko found->key.addr, 376611ce2ba3SJiri Pirko found->key.vlan_id); 376711ce2ba3SJiri Pirko if (err) 376811ce2ba3SJiri Pirko goto err_out; 376911ce2ba3SJiri Pirko if (!switchdev_trans_ph_prepare(trans)) 377011ce2ba3SJiri Pirko hash_del(&found->entry); 377111ce2ba3SJiri Pirko } 377211ce2ba3SJiri Pirko 377311ce2ba3SJiri Pirko err_out: 377411ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags); 377511ce2ba3SJiri Pirko 377611ce2ba3SJiri Pirko return err; 377711ce2ba3SJiri Pirko } 377811ce2ba3SJiri Pirko 377911ce2ba3SJiri Pirko static void rocker_fdb_cleanup(unsigned long data) 378011ce2ba3SJiri Pirko { 378111ce2ba3SJiri Pirko struct rocker *rocker = (struct rocker *)data; 378211ce2ba3SJiri Pirko struct rocker_port *rocker_port; 378311ce2ba3SJiri Pirko struct rocker_fdb_tbl_entry *entry; 378411ce2ba3SJiri Pirko struct hlist_node *tmp; 378511ce2ba3SJiri Pirko unsigned long next_timer = jiffies + BR_MIN_AGEING_TIME; 378611ce2ba3SJiri Pirko unsigned long expires; 378711ce2ba3SJiri Pirko unsigned long lock_flags; 378811ce2ba3SJiri Pirko int flags = ROCKER_OP_FLAG_NOWAIT | ROCKER_OP_FLAG_REMOVE | 378911ce2ba3SJiri Pirko ROCKER_OP_FLAG_LEARNED; 379011ce2ba3SJiri Pirko int bkt; 379111ce2ba3SJiri Pirko 379211ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags); 379311ce2ba3SJiri Pirko 379411ce2ba3SJiri Pirko hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, entry, entry) { 379511ce2ba3SJiri Pirko if (!entry->learned) 379611ce2ba3SJiri Pirko continue; 379711ce2ba3SJiri Pirko rocker_port = entry->key.rocker_port; 379811ce2ba3SJiri Pirko expires = entry->touched + rocker_port->ageing_time; 379911ce2ba3SJiri Pirko if (time_before_eq(expires, jiffies)) { 380011ce2ba3SJiri Pirko rocker_port_fdb_learn(rocker_port, NULL, 380111ce2ba3SJiri Pirko flags, entry->key.addr, 380211ce2ba3SJiri Pirko entry->key.vlan_id); 380311ce2ba3SJiri Pirko hash_del(&entry->entry); 380411ce2ba3SJiri Pirko } else if (time_before(expires, next_timer)) { 380511ce2ba3SJiri Pirko next_timer = expires; 380611ce2ba3SJiri Pirko } 380711ce2ba3SJiri Pirko } 380811ce2ba3SJiri Pirko 380911ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags); 381011ce2ba3SJiri Pirko 381111ce2ba3SJiri Pirko mod_timer(&rocker->fdb_cleanup_timer, round_jiffies_up(next_timer)); 381211ce2ba3SJiri Pirko } 381311ce2ba3SJiri Pirko 381411ce2ba3SJiri Pirko static int rocker_port_router_mac(struct rocker_port *rocker_port, 381511ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 381611ce2ba3SJiri Pirko __be16 vlan_id) 381711ce2ba3SJiri Pirko { 381811ce2ba3SJiri Pirko u32 in_pport_mask = 0xffffffff; 381911ce2ba3SJiri Pirko __be16 eth_type; 382011ce2ba3SJiri Pirko const u8 *dst_mac_mask = ff_mac; 382111ce2ba3SJiri Pirko __be16 vlan_id_mask = htons(0xffff); 382211ce2ba3SJiri Pirko bool copy_to_cpu = false; 382311ce2ba3SJiri Pirko int err; 382411ce2ba3SJiri Pirko 382511ce2ba3SJiri Pirko if (ntohs(vlan_id) == 0) 382611ce2ba3SJiri Pirko vlan_id = rocker_port->internal_vlan_id; 382711ce2ba3SJiri Pirko 382811ce2ba3SJiri Pirko eth_type = htons(ETH_P_IP); 382911ce2ba3SJiri Pirko err = rocker_flow_tbl_term_mac(rocker_port, trans, 383011ce2ba3SJiri Pirko rocker_port->pport, in_pport_mask, 383111ce2ba3SJiri Pirko eth_type, rocker_port->dev->dev_addr, 383211ce2ba3SJiri Pirko dst_mac_mask, vlan_id, vlan_id_mask, 383311ce2ba3SJiri Pirko copy_to_cpu, flags); 383411ce2ba3SJiri Pirko if (err) 383511ce2ba3SJiri Pirko return err; 383611ce2ba3SJiri Pirko 383711ce2ba3SJiri Pirko eth_type = htons(ETH_P_IPV6); 383811ce2ba3SJiri Pirko err = rocker_flow_tbl_term_mac(rocker_port, trans, 383911ce2ba3SJiri Pirko rocker_port->pport, in_pport_mask, 384011ce2ba3SJiri Pirko eth_type, rocker_port->dev->dev_addr, 384111ce2ba3SJiri Pirko dst_mac_mask, vlan_id, vlan_id_mask, 384211ce2ba3SJiri Pirko copy_to_cpu, flags); 384311ce2ba3SJiri Pirko 384411ce2ba3SJiri Pirko return err; 384511ce2ba3SJiri Pirko } 384611ce2ba3SJiri Pirko 384711ce2ba3SJiri Pirko static int rocker_port_fwding(struct rocker_port *rocker_port, 384811ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags) 384911ce2ba3SJiri Pirko { 385011ce2ba3SJiri Pirko bool pop_vlan; 385111ce2ba3SJiri Pirko u32 out_pport; 385211ce2ba3SJiri Pirko __be16 vlan_id; 385311ce2ba3SJiri Pirko u16 vid; 385411ce2ba3SJiri Pirko int err; 385511ce2ba3SJiri Pirko 385611ce2ba3SJiri Pirko /* Port will be forwarding-enabled if its STP state is LEARNING 385711ce2ba3SJiri Pirko * or FORWARDING. Traffic from CPU can still egress, regardless of 385811ce2ba3SJiri Pirko * port STP state. Use L2 interface group on port VLANs as a way 385911ce2ba3SJiri Pirko * to toggle port forwarding: if forwarding is disabled, L2 386011ce2ba3SJiri Pirko * interface group will not exist. 386111ce2ba3SJiri Pirko */ 386211ce2ba3SJiri Pirko 386311ce2ba3SJiri Pirko if (rocker_port->stp_state != BR_STATE_LEARNING && 386411ce2ba3SJiri Pirko rocker_port->stp_state != BR_STATE_FORWARDING) 386511ce2ba3SJiri Pirko flags |= ROCKER_OP_FLAG_REMOVE; 386611ce2ba3SJiri Pirko 386711ce2ba3SJiri Pirko out_pport = rocker_port->pport; 386811ce2ba3SJiri Pirko for (vid = 1; vid < VLAN_N_VID; vid++) { 386911ce2ba3SJiri Pirko if (!test_bit(vid, rocker_port->vlan_bitmap)) 387011ce2ba3SJiri Pirko continue; 387111ce2ba3SJiri Pirko vlan_id = htons(vid); 387211ce2ba3SJiri Pirko pop_vlan = rocker_vlan_id_is_internal(vlan_id); 387311ce2ba3SJiri Pirko err = rocker_group_l2_interface(rocker_port, trans, flags, 387411ce2ba3SJiri Pirko vlan_id, out_pport, pop_vlan); 387511ce2ba3SJiri Pirko if (err) { 387611ce2ba3SJiri Pirko netdev_err(rocker_port->dev, 387711ce2ba3SJiri Pirko "Error (%d) port VLAN l2 group for pport %d\n", 387811ce2ba3SJiri Pirko err, out_pport); 387911ce2ba3SJiri Pirko return err; 388011ce2ba3SJiri Pirko } 388111ce2ba3SJiri Pirko } 388211ce2ba3SJiri Pirko 388311ce2ba3SJiri Pirko return 0; 388411ce2ba3SJiri Pirko } 388511ce2ba3SJiri Pirko 388611ce2ba3SJiri Pirko static int rocker_port_stp_update(struct rocker_port *rocker_port, 388711ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags, 388811ce2ba3SJiri Pirko u8 state) 388911ce2ba3SJiri Pirko { 389011ce2ba3SJiri Pirko bool want[ROCKER_CTRL_MAX] = { 0, }; 389111ce2ba3SJiri Pirko bool prev_ctrls[ROCKER_CTRL_MAX]; 389211ce2ba3SJiri Pirko u8 uninitialized_var(prev_state); 389311ce2ba3SJiri Pirko int err; 389411ce2ba3SJiri Pirko int i; 389511ce2ba3SJiri Pirko 389611ce2ba3SJiri Pirko if (switchdev_trans_ph_prepare(trans)) { 389711ce2ba3SJiri Pirko memcpy(prev_ctrls, rocker_port->ctrls, sizeof(prev_ctrls)); 389811ce2ba3SJiri Pirko prev_state = rocker_port->stp_state; 389911ce2ba3SJiri Pirko } 390011ce2ba3SJiri Pirko 390111ce2ba3SJiri Pirko if (rocker_port->stp_state == state) 390211ce2ba3SJiri Pirko return 0; 390311ce2ba3SJiri Pirko 390411ce2ba3SJiri Pirko rocker_port->stp_state = state; 390511ce2ba3SJiri Pirko 390611ce2ba3SJiri Pirko switch (state) { 390711ce2ba3SJiri Pirko case BR_STATE_DISABLED: 390811ce2ba3SJiri Pirko /* port is completely disabled */ 390911ce2ba3SJiri Pirko break; 391011ce2ba3SJiri Pirko case BR_STATE_LISTENING: 391111ce2ba3SJiri Pirko case BR_STATE_BLOCKING: 391211ce2ba3SJiri Pirko want[ROCKER_CTRL_LINK_LOCAL_MCAST] = true; 391311ce2ba3SJiri Pirko break; 391411ce2ba3SJiri Pirko case BR_STATE_LEARNING: 391511ce2ba3SJiri Pirko case BR_STATE_FORWARDING: 391611ce2ba3SJiri Pirko if (!rocker_port_is_ovsed(rocker_port)) 391711ce2ba3SJiri Pirko want[ROCKER_CTRL_LINK_LOCAL_MCAST] = true; 391811ce2ba3SJiri Pirko want[ROCKER_CTRL_IPV4_MCAST] = true; 391911ce2ba3SJiri Pirko want[ROCKER_CTRL_IPV6_MCAST] = true; 392011ce2ba3SJiri Pirko if (rocker_port_is_bridged(rocker_port)) 392111ce2ba3SJiri Pirko want[ROCKER_CTRL_DFLT_BRIDGING] = true; 392211ce2ba3SJiri Pirko else if (rocker_port_is_ovsed(rocker_port)) 392311ce2ba3SJiri Pirko want[ROCKER_CTRL_DFLT_OVS] = true; 392411ce2ba3SJiri Pirko else 392511ce2ba3SJiri Pirko want[ROCKER_CTRL_LOCAL_ARP] = true; 392611ce2ba3SJiri Pirko break; 392711ce2ba3SJiri Pirko } 392811ce2ba3SJiri Pirko 392911ce2ba3SJiri Pirko for (i = 0; i < ROCKER_CTRL_MAX; i++) { 393011ce2ba3SJiri Pirko if (want[i] != rocker_port->ctrls[i]) { 393111ce2ba3SJiri Pirko int ctrl_flags = flags | 393211ce2ba3SJiri Pirko (want[i] ? 0 : ROCKER_OP_FLAG_REMOVE); 393311ce2ba3SJiri Pirko err = rocker_port_ctrl(rocker_port, trans, ctrl_flags, 393411ce2ba3SJiri Pirko &rocker_ctrls[i]); 393511ce2ba3SJiri Pirko if (err) 393611ce2ba3SJiri Pirko goto err_out; 393711ce2ba3SJiri Pirko rocker_port->ctrls[i] = want[i]; 393811ce2ba3SJiri Pirko } 393911ce2ba3SJiri Pirko } 394011ce2ba3SJiri Pirko 394111ce2ba3SJiri Pirko err = rocker_port_fdb_flush(rocker_port, trans, flags); 394211ce2ba3SJiri Pirko if (err) 394311ce2ba3SJiri Pirko goto err_out; 394411ce2ba3SJiri Pirko 394511ce2ba3SJiri Pirko err = rocker_port_fwding(rocker_port, trans, flags); 394611ce2ba3SJiri Pirko 394711ce2ba3SJiri Pirko err_out: 394811ce2ba3SJiri Pirko if (switchdev_trans_ph_prepare(trans)) { 394911ce2ba3SJiri Pirko memcpy(rocker_port->ctrls, prev_ctrls, sizeof(prev_ctrls)); 395011ce2ba3SJiri Pirko rocker_port->stp_state = prev_state; 395111ce2ba3SJiri Pirko } 395211ce2ba3SJiri Pirko 395311ce2ba3SJiri Pirko return err; 395411ce2ba3SJiri Pirko } 395511ce2ba3SJiri Pirko 395611ce2ba3SJiri Pirko static int rocker_port_fwd_enable(struct rocker_port *rocker_port, 395711ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags) 395811ce2ba3SJiri Pirko { 395911ce2ba3SJiri Pirko if (rocker_port_is_bridged(rocker_port)) 396011ce2ba3SJiri Pirko /* bridge STP will enable port */ 396111ce2ba3SJiri Pirko return 0; 396211ce2ba3SJiri Pirko 396311ce2ba3SJiri Pirko /* port is not bridged, so simulate going to FORWARDING state */ 396411ce2ba3SJiri Pirko return rocker_port_stp_update(rocker_port, trans, flags, 396511ce2ba3SJiri Pirko BR_STATE_FORWARDING); 396611ce2ba3SJiri Pirko } 396711ce2ba3SJiri Pirko 396811ce2ba3SJiri Pirko static int rocker_port_fwd_disable(struct rocker_port *rocker_port, 396911ce2ba3SJiri Pirko struct switchdev_trans *trans, int flags) 397011ce2ba3SJiri Pirko { 397111ce2ba3SJiri Pirko if (rocker_port_is_bridged(rocker_port)) 397211ce2ba3SJiri Pirko /* bridge STP will disable port */ 397311ce2ba3SJiri Pirko return 0; 397411ce2ba3SJiri Pirko 397511ce2ba3SJiri Pirko /* port is not bridged, so simulate going to DISABLED state */ 397611ce2ba3SJiri Pirko return rocker_port_stp_update(rocker_port, trans, flags, 397711ce2ba3SJiri Pirko BR_STATE_DISABLED); 397811ce2ba3SJiri Pirko } 397911ce2ba3SJiri Pirko 398011ce2ba3SJiri Pirko static struct rocker_internal_vlan_tbl_entry * 398111ce2ba3SJiri Pirko rocker_internal_vlan_tbl_find(const struct rocker *rocker, int ifindex) 398211ce2ba3SJiri Pirko { 398311ce2ba3SJiri Pirko struct rocker_internal_vlan_tbl_entry *found; 398411ce2ba3SJiri Pirko 398511ce2ba3SJiri Pirko hash_for_each_possible(rocker->internal_vlan_tbl, found, 398611ce2ba3SJiri Pirko entry, ifindex) { 398711ce2ba3SJiri Pirko if (found->ifindex == ifindex) 398811ce2ba3SJiri Pirko return found; 398911ce2ba3SJiri Pirko } 399011ce2ba3SJiri Pirko 399111ce2ba3SJiri Pirko return NULL; 399211ce2ba3SJiri Pirko } 399311ce2ba3SJiri Pirko 399411ce2ba3SJiri Pirko static __be16 rocker_port_internal_vlan_id_get(struct rocker_port *rocker_port, 399511ce2ba3SJiri Pirko int ifindex) 399611ce2ba3SJiri Pirko { 399711ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 399811ce2ba3SJiri Pirko struct rocker_internal_vlan_tbl_entry *entry; 399911ce2ba3SJiri Pirko struct rocker_internal_vlan_tbl_entry *found; 400011ce2ba3SJiri Pirko unsigned long lock_flags; 400111ce2ba3SJiri Pirko int i; 400211ce2ba3SJiri Pirko 400311ce2ba3SJiri Pirko entry = kzalloc(sizeof(*entry), GFP_KERNEL); 400411ce2ba3SJiri Pirko if (!entry) 400511ce2ba3SJiri Pirko return 0; 400611ce2ba3SJiri Pirko 400711ce2ba3SJiri Pirko entry->ifindex = ifindex; 400811ce2ba3SJiri Pirko 400911ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->internal_vlan_tbl_lock, lock_flags); 401011ce2ba3SJiri Pirko 401111ce2ba3SJiri Pirko found = rocker_internal_vlan_tbl_find(rocker, ifindex); 401211ce2ba3SJiri Pirko if (found) { 401311ce2ba3SJiri Pirko kfree(entry); 401411ce2ba3SJiri Pirko goto found; 401511ce2ba3SJiri Pirko } 401611ce2ba3SJiri Pirko 401711ce2ba3SJiri Pirko found = entry; 401811ce2ba3SJiri Pirko hash_add(rocker->internal_vlan_tbl, &found->entry, found->ifindex); 401911ce2ba3SJiri Pirko 402011ce2ba3SJiri Pirko for (i = 0; i < ROCKER_N_INTERNAL_VLANS; i++) { 402111ce2ba3SJiri Pirko if (test_and_set_bit(i, rocker->internal_vlan_bitmap)) 402211ce2ba3SJiri Pirko continue; 402311ce2ba3SJiri Pirko found->vlan_id = htons(ROCKER_INTERNAL_VLAN_ID_BASE + i); 402411ce2ba3SJiri Pirko goto found; 402511ce2ba3SJiri Pirko } 402611ce2ba3SJiri Pirko 402711ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "Out of internal VLAN IDs\n"); 402811ce2ba3SJiri Pirko 402911ce2ba3SJiri Pirko found: 403011ce2ba3SJiri Pirko found->ref_count++; 403111ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->internal_vlan_tbl_lock, lock_flags); 403211ce2ba3SJiri Pirko 403311ce2ba3SJiri Pirko return found->vlan_id; 403411ce2ba3SJiri Pirko } 403511ce2ba3SJiri Pirko 403611ce2ba3SJiri Pirko static void 403711ce2ba3SJiri Pirko rocker_port_internal_vlan_id_put(const struct rocker_port *rocker_port, 403811ce2ba3SJiri Pirko int ifindex) 403911ce2ba3SJiri Pirko { 404011ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 404111ce2ba3SJiri Pirko struct rocker_internal_vlan_tbl_entry *found; 404211ce2ba3SJiri Pirko unsigned long lock_flags; 404311ce2ba3SJiri Pirko unsigned long bit; 404411ce2ba3SJiri Pirko 404511ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->internal_vlan_tbl_lock, lock_flags); 404611ce2ba3SJiri Pirko 404711ce2ba3SJiri Pirko found = rocker_internal_vlan_tbl_find(rocker, ifindex); 404811ce2ba3SJiri Pirko if (!found) { 404911ce2ba3SJiri Pirko netdev_err(rocker_port->dev, 405011ce2ba3SJiri Pirko "ifindex (%d) not found in internal VLAN tbl\n", 405111ce2ba3SJiri Pirko ifindex); 405211ce2ba3SJiri Pirko goto not_found; 405311ce2ba3SJiri Pirko } 405411ce2ba3SJiri Pirko 405511ce2ba3SJiri Pirko if (--found->ref_count <= 0) { 405611ce2ba3SJiri Pirko bit = ntohs(found->vlan_id) - ROCKER_INTERNAL_VLAN_ID_BASE; 405711ce2ba3SJiri Pirko clear_bit(bit, rocker->internal_vlan_bitmap); 405811ce2ba3SJiri Pirko hash_del(&found->entry); 405911ce2ba3SJiri Pirko kfree(found); 406011ce2ba3SJiri Pirko } 406111ce2ba3SJiri Pirko 406211ce2ba3SJiri Pirko not_found: 406311ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->internal_vlan_tbl_lock, lock_flags); 406411ce2ba3SJiri Pirko } 406511ce2ba3SJiri Pirko 406611ce2ba3SJiri Pirko static int rocker_port_fib_ipv4(struct rocker_port *rocker_port, 406711ce2ba3SJiri Pirko struct switchdev_trans *trans, __be32 dst, 406811ce2ba3SJiri Pirko int dst_len, const struct fib_info *fi, 406911ce2ba3SJiri Pirko u32 tb_id, int flags) 407011ce2ba3SJiri Pirko { 407111ce2ba3SJiri Pirko const struct fib_nh *nh; 407211ce2ba3SJiri Pirko __be16 eth_type = htons(ETH_P_IP); 407311ce2ba3SJiri Pirko __be32 dst_mask = inet_make_mask(dst_len); 407411ce2ba3SJiri Pirko __be16 internal_vlan_id = rocker_port->internal_vlan_id; 407511ce2ba3SJiri Pirko u32 priority = fi->fib_priority; 407611ce2ba3SJiri Pirko enum rocker_of_dpa_table_id goto_tbl = 407711ce2ba3SJiri Pirko ROCKER_OF_DPA_TABLE_ID_ACL_POLICY; 407811ce2ba3SJiri Pirko u32 group_id; 407911ce2ba3SJiri Pirko bool nh_on_port; 408011ce2ba3SJiri Pirko bool has_gw; 408111ce2ba3SJiri Pirko u32 index; 408211ce2ba3SJiri Pirko int err; 408311ce2ba3SJiri Pirko 408411ce2ba3SJiri Pirko /* XXX support ECMP */ 408511ce2ba3SJiri Pirko 408611ce2ba3SJiri Pirko nh = fi->fib_nh; 408711ce2ba3SJiri Pirko nh_on_port = (fi->fib_dev == rocker_port->dev); 408811ce2ba3SJiri Pirko has_gw = !!nh->nh_gw; 408911ce2ba3SJiri Pirko 409011ce2ba3SJiri Pirko if (has_gw && nh_on_port) { 409111ce2ba3SJiri Pirko err = rocker_port_ipv4_nh(rocker_port, trans, flags, 409211ce2ba3SJiri Pirko nh->nh_gw, &index); 409311ce2ba3SJiri Pirko if (err) 409411ce2ba3SJiri Pirko return err; 409511ce2ba3SJiri Pirko 409611ce2ba3SJiri Pirko group_id = ROCKER_GROUP_L3_UNICAST(index); 409711ce2ba3SJiri Pirko } else { 409811ce2ba3SJiri Pirko /* Send to CPU for processing */ 409911ce2ba3SJiri Pirko group_id = ROCKER_GROUP_L2_INTERFACE(internal_vlan_id, 0); 410011ce2ba3SJiri Pirko } 410111ce2ba3SJiri Pirko 410211ce2ba3SJiri Pirko err = rocker_flow_tbl_ucast4_routing(rocker_port, trans, eth_type, dst, 410311ce2ba3SJiri Pirko dst_mask, priority, goto_tbl, 410411ce2ba3SJiri Pirko group_id, flags); 410511ce2ba3SJiri Pirko if (err) 410611ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "Error (%d) IPv4 route %pI4\n", 410711ce2ba3SJiri Pirko err, &dst); 410811ce2ba3SJiri Pirko 410911ce2ba3SJiri Pirko return err; 411011ce2ba3SJiri Pirko } 411111ce2ba3SJiri Pirko 411211ce2ba3SJiri Pirko /***************** 411311ce2ba3SJiri Pirko * Net device ops 411411ce2ba3SJiri Pirko *****************/ 411511ce2ba3SJiri Pirko 411611ce2ba3SJiri Pirko static int rocker_port_open(struct net_device *dev) 411711ce2ba3SJiri Pirko { 411811ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 411911ce2ba3SJiri Pirko int err; 412011ce2ba3SJiri Pirko 412111ce2ba3SJiri Pirko err = rocker_port_dma_rings_init(rocker_port); 412211ce2ba3SJiri Pirko if (err) 412311ce2ba3SJiri Pirko return err; 412411ce2ba3SJiri Pirko 412511ce2ba3SJiri Pirko err = request_irq(rocker_msix_tx_vector(rocker_port), 412611ce2ba3SJiri Pirko rocker_tx_irq_handler, 0, 412711ce2ba3SJiri Pirko rocker_driver_name, rocker_port); 412811ce2ba3SJiri Pirko if (err) { 412911ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "cannot assign tx irq\n"); 413011ce2ba3SJiri Pirko goto err_request_tx_irq; 413111ce2ba3SJiri Pirko } 413211ce2ba3SJiri Pirko 413311ce2ba3SJiri Pirko err = request_irq(rocker_msix_rx_vector(rocker_port), 413411ce2ba3SJiri Pirko rocker_rx_irq_handler, 0, 413511ce2ba3SJiri Pirko rocker_driver_name, rocker_port); 413611ce2ba3SJiri Pirko if (err) { 413711ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "cannot assign rx irq\n"); 413811ce2ba3SJiri Pirko goto err_request_rx_irq; 413911ce2ba3SJiri Pirko } 414011ce2ba3SJiri Pirko 4141e420114eSJiri Pirko err = rocker_world_port_open(rocker_port); 4142e420114eSJiri Pirko if (err) { 4143e420114eSJiri Pirko netdev_err(rocker_port->dev, "cannot open port in world\n"); 4144e420114eSJiri Pirko goto err_world_port_open; 4145e420114eSJiri Pirko } 4146e420114eSJiri Pirko 414711ce2ba3SJiri Pirko err = rocker_port_fwd_enable(rocker_port, NULL, 0); 414811ce2ba3SJiri Pirko if (err) 414911ce2ba3SJiri Pirko goto err_fwd_enable; 415011ce2ba3SJiri Pirko 415111ce2ba3SJiri Pirko napi_enable(&rocker_port->napi_tx); 415211ce2ba3SJiri Pirko napi_enable(&rocker_port->napi_rx); 415311ce2ba3SJiri Pirko if (!dev->proto_down) 415411ce2ba3SJiri Pirko rocker_port_set_enable(rocker_port, true); 415511ce2ba3SJiri Pirko netif_start_queue(dev); 415611ce2ba3SJiri Pirko return 0; 415711ce2ba3SJiri Pirko 415811ce2ba3SJiri Pirko err_fwd_enable: 4159e420114eSJiri Pirko err_world_port_open: 416011ce2ba3SJiri Pirko free_irq(rocker_msix_rx_vector(rocker_port), rocker_port); 416111ce2ba3SJiri Pirko err_request_rx_irq: 416211ce2ba3SJiri Pirko free_irq(rocker_msix_tx_vector(rocker_port), rocker_port); 416311ce2ba3SJiri Pirko err_request_tx_irq: 416411ce2ba3SJiri Pirko rocker_port_dma_rings_fini(rocker_port); 416511ce2ba3SJiri Pirko return err; 416611ce2ba3SJiri Pirko } 416711ce2ba3SJiri Pirko 416811ce2ba3SJiri Pirko static int rocker_port_stop(struct net_device *dev) 416911ce2ba3SJiri Pirko { 417011ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 417111ce2ba3SJiri Pirko 417211ce2ba3SJiri Pirko netif_stop_queue(dev); 417311ce2ba3SJiri Pirko rocker_port_set_enable(rocker_port, false); 417411ce2ba3SJiri Pirko napi_disable(&rocker_port->napi_rx); 417511ce2ba3SJiri Pirko napi_disable(&rocker_port->napi_tx); 4176e420114eSJiri Pirko rocker_world_port_stop(rocker_port); 417711ce2ba3SJiri Pirko rocker_port_fwd_disable(rocker_port, NULL, 417811ce2ba3SJiri Pirko ROCKER_OP_FLAG_NOWAIT); 417911ce2ba3SJiri Pirko free_irq(rocker_msix_rx_vector(rocker_port), rocker_port); 418011ce2ba3SJiri Pirko free_irq(rocker_msix_tx_vector(rocker_port), rocker_port); 418111ce2ba3SJiri Pirko rocker_port_dma_rings_fini(rocker_port); 418211ce2ba3SJiri Pirko 418311ce2ba3SJiri Pirko return 0; 418411ce2ba3SJiri Pirko } 418511ce2ba3SJiri Pirko 418611ce2ba3SJiri Pirko static void rocker_tx_desc_frags_unmap(const struct rocker_port *rocker_port, 418711ce2ba3SJiri Pirko const struct rocker_desc_info *desc_info) 418811ce2ba3SJiri Pirko { 418911ce2ba3SJiri Pirko const struct rocker *rocker = rocker_port->rocker; 419011ce2ba3SJiri Pirko struct pci_dev *pdev = rocker->pdev; 419111ce2ba3SJiri Pirko const struct rocker_tlv *attrs[ROCKER_TLV_TX_MAX + 1]; 419211ce2ba3SJiri Pirko struct rocker_tlv *attr; 419311ce2ba3SJiri Pirko int rem; 419411ce2ba3SJiri Pirko 419511ce2ba3SJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_TX_MAX, desc_info); 419611ce2ba3SJiri Pirko if (!attrs[ROCKER_TLV_TX_FRAGS]) 419711ce2ba3SJiri Pirko return; 419811ce2ba3SJiri Pirko rocker_tlv_for_each_nested(attr, attrs[ROCKER_TLV_TX_FRAGS], rem) { 419911ce2ba3SJiri Pirko const struct rocker_tlv *frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_MAX + 1]; 420011ce2ba3SJiri Pirko dma_addr_t dma_handle; 420111ce2ba3SJiri Pirko size_t len; 420211ce2ba3SJiri Pirko 420311ce2ba3SJiri Pirko if (rocker_tlv_type(attr) != ROCKER_TLV_TX_FRAG) 420411ce2ba3SJiri Pirko continue; 420511ce2ba3SJiri Pirko rocker_tlv_parse_nested(frag_attrs, ROCKER_TLV_TX_FRAG_ATTR_MAX, 420611ce2ba3SJiri Pirko attr); 420711ce2ba3SJiri Pirko if (!frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_ADDR] || 420811ce2ba3SJiri Pirko !frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_LEN]) 420911ce2ba3SJiri Pirko continue; 421011ce2ba3SJiri Pirko dma_handle = rocker_tlv_get_u64(frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_ADDR]); 421111ce2ba3SJiri Pirko len = rocker_tlv_get_u16(frag_attrs[ROCKER_TLV_TX_FRAG_ATTR_LEN]); 421211ce2ba3SJiri Pirko pci_unmap_single(pdev, dma_handle, len, DMA_TO_DEVICE); 421311ce2ba3SJiri Pirko } 421411ce2ba3SJiri Pirko } 421511ce2ba3SJiri Pirko 421611ce2ba3SJiri Pirko static int rocker_tx_desc_frag_map_put(const struct rocker_port *rocker_port, 421711ce2ba3SJiri Pirko struct rocker_desc_info *desc_info, 421811ce2ba3SJiri Pirko char *buf, size_t buf_len) 421911ce2ba3SJiri Pirko { 422011ce2ba3SJiri Pirko const struct rocker *rocker = rocker_port->rocker; 422111ce2ba3SJiri Pirko struct pci_dev *pdev = rocker->pdev; 422211ce2ba3SJiri Pirko dma_addr_t dma_handle; 422311ce2ba3SJiri Pirko struct rocker_tlv *frag; 422411ce2ba3SJiri Pirko 422511ce2ba3SJiri Pirko dma_handle = pci_map_single(pdev, buf, buf_len, DMA_TO_DEVICE); 422611ce2ba3SJiri Pirko if (unlikely(pci_dma_mapping_error(pdev, dma_handle))) { 422711ce2ba3SJiri Pirko if (net_ratelimit()) 422811ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "failed to dma map tx frag\n"); 422911ce2ba3SJiri Pirko return -EIO; 423011ce2ba3SJiri Pirko } 423111ce2ba3SJiri Pirko frag = rocker_tlv_nest_start(desc_info, ROCKER_TLV_TX_FRAG); 423211ce2ba3SJiri Pirko if (!frag) 423311ce2ba3SJiri Pirko goto unmap_frag; 423411ce2ba3SJiri Pirko if (rocker_tlv_put_u64(desc_info, ROCKER_TLV_TX_FRAG_ATTR_ADDR, 423511ce2ba3SJiri Pirko dma_handle)) 423611ce2ba3SJiri Pirko goto nest_cancel; 423711ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_TX_FRAG_ATTR_LEN, 423811ce2ba3SJiri Pirko buf_len)) 423911ce2ba3SJiri Pirko goto nest_cancel; 424011ce2ba3SJiri Pirko rocker_tlv_nest_end(desc_info, frag); 424111ce2ba3SJiri Pirko return 0; 424211ce2ba3SJiri Pirko 424311ce2ba3SJiri Pirko nest_cancel: 424411ce2ba3SJiri Pirko rocker_tlv_nest_cancel(desc_info, frag); 424511ce2ba3SJiri Pirko unmap_frag: 424611ce2ba3SJiri Pirko pci_unmap_single(pdev, dma_handle, buf_len, DMA_TO_DEVICE); 424711ce2ba3SJiri Pirko return -EMSGSIZE; 424811ce2ba3SJiri Pirko } 424911ce2ba3SJiri Pirko 425011ce2ba3SJiri Pirko static netdev_tx_t rocker_port_xmit(struct sk_buff *skb, struct net_device *dev) 425111ce2ba3SJiri Pirko { 425211ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 425311ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 425411ce2ba3SJiri Pirko struct rocker_desc_info *desc_info; 425511ce2ba3SJiri Pirko struct rocker_tlv *frags; 425611ce2ba3SJiri Pirko int i; 425711ce2ba3SJiri Pirko int err; 425811ce2ba3SJiri Pirko 425911ce2ba3SJiri Pirko desc_info = rocker_desc_head_get(&rocker_port->tx_ring); 426011ce2ba3SJiri Pirko if (unlikely(!desc_info)) { 426111ce2ba3SJiri Pirko if (net_ratelimit()) 426211ce2ba3SJiri Pirko netdev_err(dev, "tx ring full when queue awake\n"); 426311ce2ba3SJiri Pirko return NETDEV_TX_BUSY; 426411ce2ba3SJiri Pirko } 426511ce2ba3SJiri Pirko 426611ce2ba3SJiri Pirko rocker_desc_cookie_ptr_set(desc_info, skb); 426711ce2ba3SJiri Pirko 426811ce2ba3SJiri Pirko frags = rocker_tlv_nest_start(desc_info, ROCKER_TLV_TX_FRAGS); 426911ce2ba3SJiri Pirko if (!frags) 427011ce2ba3SJiri Pirko goto out; 427111ce2ba3SJiri Pirko err = rocker_tx_desc_frag_map_put(rocker_port, desc_info, 427211ce2ba3SJiri Pirko skb->data, skb_headlen(skb)); 427311ce2ba3SJiri Pirko if (err) 427411ce2ba3SJiri Pirko goto nest_cancel; 427511ce2ba3SJiri Pirko if (skb_shinfo(skb)->nr_frags > ROCKER_TX_FRAGS_MAX) { 427611ce2ba3SJiri Pirko err = skb_linearize(skb); 427711ce2ba3SJiri Pirko if (err) 427811ce2ba3SJiri Pirko goto unmap_frags; 427911ce2ba3SJiri Pirko } 428011ce2ba3SJiri Pirko 428111ce2ba3SJiri Pirko for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 428211ce2ba3SJiri Pirko const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 428311ce2ba3SJiri Pirko 428411ce2ba3SJiri Pirko err = rocker_tx_desc_frag_map_put(rocker_port, desc_info, 428511ce2ba3SJiri Pirko skb_frag_address(frag), 428611ce2ba3SJiri Pirko skb_frag_size(frag)); 428711ce2ba3SJiri Pirko if (err) 428811ce2ba3SJiri Pirko goto unmap_frags; 428911ce2ba3SJiri Pirko } 429011ce2ba3SJiri Pirko rocker_tlv_nest_end(desc_info, frags); 429111ce2ba3SJiri Pirko 429211ce2ba3SJiri Pirko rocker_desc_gen_clear(desc_info); 429311ce2ba3SJiri Pirko rocker_desc_head_set(rocker, &rocker_port->tx_ring, desc_info); 429411ce2ba3SJiri Pirko 429511ce2ba3SJiri Pirko desc_info = rocker_desc_head_get(&rocker_port->tx_ring); 429611ce2ba3SJiri Pirko if (!desc_info) 429711ce2ba3SJiri Pirko netif_stop_queue(dev); 429811ce2ba3SJiri Pirko 429911ce2ba3SJiri Pirko return NETDEV_TX_OK; 430011ce2ba3SJiri Pirko 430111ce2ba3SJiri Pirko unmap_frags: 430211ce2ba3SJiri Pirko rocker_tx_desc_frags_unmap(rocker_port, desc_info); 430311ce2ba3SJiri Pirko nest_cancel: 430411ce2ba3SJiri Pirko rocker_tlv_nest_cancel(desc_info, frags); 430511ce2ba3SJiri Pirko out: 430611ce2ba3SJiri Pirko dev_kfree_skb(skb); 430711ce2ba3SJiri Pirko dev->stats.tx_dropped++; 430811ce2ba3SJiri Pirko 430911ce2ba3SJiri Pirko return NETDEV_TX_OK; 431011ce2ba3SJiri Pirko } 431111ce2ba3SJiri Pirko 431211ce2ba3SJiri Pirko static int rocker_port_set_mac_address(struct net_device *dev, void *p) 431311ce2ba3SJiri Pirko { 431411ce2ba3SJiri Pirko struct sockaddr *addr = p; 431511ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 431611ce2ba3SJiri Pirko int err; 431711ce2ba3SJiri Pirko 431811ce2ba3SJiri Pirko if (!is_valid_ether_addr(addr->sa_data)) 431911ce2ba3SJiri Pirko return -EADDRNOTAVAIL; 432011ce2ba3SJiri Pirko 432111ce2ba3SJiri Pirko err = rocker_cmd_set_port_settings_macaddr(rocker_port, addr->sa_data); 432211ce2ba3SJiri Pirko if (err) 432311ce2ba3SJiri Pirko return err; 432411ce2ba3SJiri Pirko memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); 432511ce2ba3SJiri Pirko return 0; 432611ce2ba3SJiri Pirko } 432711ce2ba3SJiri Pirko 432811ce2ba3SJiri Pirko static int rocker_port_change_mtu(struct net_device *dev, int new_mtu) 432911ce2ba3SJiri Pirko { 433011ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 433111ce2ba3SJiri Pirko int running = netif_running(dev); 433211ce2ba3SJiri Pirko int err; 433311ce2ba3SJiri Pirko 433411ce2ba3SJiri Pirko #define ROCKER_PORT_MIN_MTU 68 433511ce2ba3SJiri Pirko #define ROCKER_PORT_MAX_MTU 9000 433611ce2ba3SJiri Pirko 433711ce2ba3SJiri Pirko if (new_mtu < ROCKER_PORT_MIN_MTU || new_mtu > ROCKER_PORT_MAX_MTU) 433811ce2ba3SJiri Pirko return -EINVAL; 433911ce2ba3SJiri Pirko 434011ce2ba3SJiri Pirko if (running) 434111ce2ba3SJiri Pirko rocker_port_stop(dev); 434211ce2ba3SJiri Pirko 434311ce2ba3SJiri Pirko netdev_info(dev, "MTU change from %d to %d\n", dev->mtu, new_mtu); 434411ce2ba3SJiri Pirko dev->mtu = new_mtu; 434511ce2ba3SJiri Pirko 434611ce2ba3SJiri Pirko err = rocker_cmd_set_port_settings_mtu(rocker_port, new_mtu); 434711ce2ba3SJiri Pirko if (err) 434811ce2ba3SJiri Pirko return err; 434911ce2ba3SJiri Pirko 435011ce2ba3SJiri Pirko if (running) 435111ce2ba3SJiri Pirko err = rocker_port_open(dev); 435211ce2ba3SJiri Pirko 435311ce2ba3SJiri Pirko return err; 435411ce2ba3SJiri Pirko } 435511ce2ba3SJiri Pirko 435611ce2ba3SJiri Pirko static int rocker_port_get_phys_port_name(struct net_device *dev, 435711ce2ba3SJiri Pirko char *buf, size_t len) 435811ce2ba3SJiri Pirko { 435911ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 436011ce2ba3SJiri Pirko struct port_name name = { .buf = buf, .len = len }; 436111ce2ba3SJiri Pirko int err; 436211ce2ba3SJiri Pirko 436311ce2ba3SJiri Pirko err = rocker_cmd_exec(rocker_port, NULL, 0, 436411ce2ba3SJiri Pirko rocker_cmd_get_port_settings_prep, NULL, 436511ce2ba3SJiri Pirko rocker_cmd_get_port_settings_phys_name_proc, 436611ce2ba3SJiri Pirko &name); 436711ce2ba3SJiri Pirko 436811ce2ba3SJiri Pirko return err ? -EOPNOTSUPP : 0; 436911ce2ba3SJiri Pirko } 437011ce2ba3SJiri Pirko 437111ce2ba3SJiri Pirko static int rocker_port_change_proto_down(struct net_device *dev, 437211ce2ba3SJiri Pirko bool proto_down) 437311ce2ba3SJiri Pirko { 437411ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 437511ce2ba3SJiri Pirko 437611ce2ba3SJiri Pirko if (rocker_port->dev->flags & IFF_UP) 437711ce2ba3SJiri Pirko rocker_port_set_enable(rocker_port, !proto_down); 437811ce2ba3SJiri Pirko rocker_port->dev->proto_down = proto_down; 437911ce2ba3SJiri Pirko return 0; 438011ce2ba3SJiri Pirko } 438111ce2ba3SJiri Pirko 438211ce2ba3SJiri Pirko static void rocker_port_neigh_destroy(struct neighbour *n) 438311ce2ba3SJiri Pirko { 438411ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(n->dev); 438511ce2ba3SJiri Pirko int flags = ROCKER_OP_FLAG_REMOVE | ROCKER_OP_FLAG_NOWAIT; 438611ce2ba3SJiri Pirko __be32 ip_addr = *(__be32 *)n->primary_key; 4387e420114eSJiri Pirko int err; 438811ce2ba3SJiri Pirko 438911ce2ba3SJiri Pirko rocker_port_ipv4_neigh(rocker_port, NULL, 439011ce2ba3SJiri Pirko flags, ip_addr, n->ha); 4391e420114eSJiri Pirko err = rocker_world_port_neigh_destroy(rocker_port, n); 4392e420114eSJiri Pirko if (err) 4393e420114eSJiri Pirko netdev_warn(rocker_port->dev, "failed to handle neigh destroy (err %d)\n", 4394e420114eSJiri Pirko err); 439511ce2ba3SJiri Pirko } 439611ce2ba3SJiri Pirko 439711ce2ba3SJiri Pirko static const struct net_device_ops rocker_port_netdev_ops = { 439811ce2ba3SJiri Pirko .ndo_open = rocker_port_open, 439911ce2ba3SJiri Pirko .ndo_stop = rocker_port_stop, 440011ce2ba3SJiri Pirko .ndo_start_xmit = rocker_port_xmit, 440111ce2ba3SJiri Pirko .ndo_set_mac_address = rocker_port_set_mac_address, 440211ce2ba3SJiri Pirko .ndo_change_mtu = rocker_port_change_mtu, 440311ce2ba3SJiri Pirko .ndo_bridge_getlink = switchdev_port_bridge_getlink, 440411ce2ba3SJiri Pirko .ndo_bridge_setlink = switchdev_port_bridge_setlink, 440511ce2ba3SJiri Pirko .ndo_bridge_dellink = switchdev_port_bridge_dellink, 440611ce2ba3SJiri Pirko .ndo_fdb_add = switchdev_port_fdb_add, 440711ce2ba3SJiri Pirko .ndo_fdb_del = switchdev_port_fdb_del, 440811ce2ba3SJiri Pirko .ndo_fdb_dump = switchdev_port_fdb_dump, 440911ce2ba3SJiri Pirko .ndo_get_phys_port_name = rocker_port_get_phys_port_name, 441011ce2ba3SJiri Pirko .ndo_change_proto_down = rocker_port_change_proto_down, 441111ce2ba3SJiri Pirko .ndo_neigh_destroy = rocker_port_neigh_destroy, 441211ce2ba3SJiri Pirko }; 441311ce2ba3SJiri Pirko 441411ce2ba3SJiri Pirko /******************** 441511ce2ba3SJiri Pirko * swdev interface 441611ce2ba3SJiri Pirko ********************/ 441711ce2ba3SJiri Pirko 441811ce2ba3SJiri Pirko static int rocker_port_attr_get(struct net_device *dev, 441911ce2ba3SJiri Pirko struct switchdev_attr *attr) 442011ce2ba3SJiri Pirko { 442111ce2ba3SJiri Pirko const struct rocker_port *rocker_port = netdev_priv(dev); 442211ce2ba3SJiri Pirko const struct rocker *rocker = rocker_port->rocker; 4423e420114eSJiri Pirko int err = 0; 442411ce2ba3SJiri Pirko 442511ce2ba3SJiri Pirko switch (attr->id) { 442611ce2ba3SJiri Pirko case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: 442711ce2ba3SJiri Pirko attr->u.ppid.id_len = sizeof(rocker->hw.id); 442811ce2ba3SJiri Pirko memcpy(&attr->u.ppid.id, &rocker->hw.id, attr->u.ppid.id_len); 442911ce2ba3SJiri Pirko break; 443011ce2ba3SJiri Pirko case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: 443111ce2ba3SJiri Pirko attr->u.brport_flags = rocker_port->brport_flags; 4432e420114eSJiri Pirko err = rocker_world_port_attr_bridge_flags_get(rocker_port, 4433e420114eSJiri Pirko &attr->u.brport_flags); 443411ce2ba3SJiri Pirko break; 443511ce2ba3SJiri Pirko default: 443611ce2ba3SJiri Pirko return -EOPNOTSUPP; 443711ce2ba3SJiri Pirko } 443811ce2ba3SJiri Pirko 4439e420114eSJiri Pirko return err; 444011ce2ba3SJiri Pirko } 444111ce2ba3SJiri Pirko 444211ce2ba3SJiri Pirko static int rocker_port_brport_flags_set(struct rocker_port *rocker_port, 444311ce2ba3SJiri Pirko struct switchdev_trans *trans, 444411ce2ba3SJiri Pirko unsigned long brport_flags) 444511ce2ba3SJiri Pirko { 444611ce2ba3SJiri Pirko unsigned long orig_flags; 444711ce2ba3SJiri Pirko int err = 0; 444811ce2ba3SJiri Pirko 444911ce2ba3SJiri Pirko orig_flags = rocker_port->brport_flags; 445011ce2ba3SJiri Pirko rocker_port->brport_flags = brport_flags; 445111ce2ba3SJiri Pirko if ((orig_flags ^ rocker_port->brport_flags) & BR_LEARNING) 4452c1fe922eSJiri Pirko err = rocker_port_set_learning(rocker_port, trans, 4453c1fe922eSJiri Pirko !!(rocker_port->brport_flags & BR_LEARNING)); 445411ce2ba3SJiri Pirko 445511ce2ba3SJiri Pirko if (switchdev_trans_ph_prepare(trans)) 445611ce2ba3SJiri Pirko rocker_port->brport_flags = orig_flags; 445711ce2ba3SJiri Pirko 445811ce2ba3SJiri Pirko return err; 445911ce2ba3SJiri Pirko } 446011ce2ba3SJiri Pirko 446111ce2ba3SJiri Pirko static int rocker_port_bridge_ageing_time(struct rocker_port *rocker_port, 446211ce2ba3SJiri Pirko struct switchdev_trans *trans, 446311ce2ba3SJiri Pirko u32 ageing_time) 446411ce2ba3SJiri Pirko { 446511ce2ba3SJiri Pirko if (!switchdev_trans_ph_prepare(trans)) { 446611ce2ba3SJiri Pirko rocker_port->ageing_time = clock_t_to_jiffies(ageing_time); 446711ce2ba3SJiri Pirko mod_timer(&rocker_port->rocker->fdb_cleanup_timer, jiffies); 446811ce2ba3SJiri Pirko } 446911ce2ba3SJiri Pirko 447011ce2ba3SJiri Pirko return 0; 447111ce2ba3SJiri Pirko } 447211ce2ba3SJiri Pirko 447311ce2ba3SJiri Pirko static int rocker_port_attr_set(struct net_device *dev, 447411ce2ba3SJiri Pirko const struct switchdev_attr *attr, 447511ce2ba3SJiri Pirko struct switchdev_trans *trans) 447611ce2ba3SJiri Pirko { 447711ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 447811ce2ba3SJiri Pirko int err = 0; 447911ce2ba3SJiri Pirko 448011ce2ba3SJiri Pirko switch (attr->id) { 448111ce2ba3SJiri Pirko case SWITCHDEV_ATTR_ID_PORT_STP_STATE: 448211ce2ba3SJiri Pirko err = rocker_port_stp_update(rocker_port, trans, 0, 448311ce2ba3SJiri Pirko attr->u.stp_state); 4484e420114eSJiri Pirko if (err) 4485e420114eSJiri Pirko break; 4486e420114eSJiri Pirko err = rocker_world_port_attr_stp_state_set(rocker_port, 4487e420114eSJiri Pirko attr->u.stp_state, 4488e420114eSJiri Pirko trans); 448911ce2ba3SJiri Pirko break; 449011ce2ba3SJiri Pirko case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: 449111ce2ba3SJiri Pirko err = rocker_port_brport_flags_set(rocker_port, trans, 449211ce2ba3SJiri Pirko attr->u.brport_flags); 4493e420114eSJiri Pirko if (err) 4494e420114eSJiri Pirko break; 4495e420114eSJiri Pirko err = rocker_world_port_attr_bridge_flags_set(rocker_port, 4496e420114eSJiri Pirko attr->u.brport_flags, 4497e420114eSJiri Pirko trans); 449811ce2ba3SJiri Pirko break; 449911ce2ba3SJiri Pirko case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: 450011ce2ba3SJiri Pirko err = rocker_port_bridge_ageing_time(rocker_port, trans, 450111ce2ba3SJiri Pirko attr->u.ageing_time); 4502e420114eSJiri Pirko if (err) 4503e420114eSJiri Pirko break; 4504e420114eSJiri Pirko err = rocker_world_port_attr_bridge_ageing_time_set(rocker_port, 4505e420114eSJiri Pirko attr->u.ageing_time, 4506e420114eSJiri Pirko trans); 450711ce2ba3SJiri Pirko break; 450811ce2ba3SJiri Pirko default: 450911ce2ba3SJiri Pirko err = -EOPNOTSUPP; 451011ce2ba3SJiri Pirko break; 451111ce2ba3SJiri Pirko } 451211ce2ba3SJiri Pirko 451311ce2ba3SJiri Pirko return err; 451411ce2ba3SJiri Pirko } 451511ce2ba3SJiri Pirko 451611ce2ba3SJiri Pirko static int rocker_port_vlan_add(struct rocker_port *rocker_port, 451711ce2ba3SJiri Pirko struct switchdev_trans *trans, 451811ce2ba3SJiri Pirko u16 vid, u16 flags) 451911ce2ba3SJiri Pirko { 452011ce2ba3SJiri Pirko int err; 452111ce2ba3SJiri Pirko 452211ce2ba3SJiri Pirko /* XXX deal with flags for PVID and untagged */ 452311ce2ba3SJiri Pirko 452411ce2ba3SJiri Pirko err = rocker_port_vlan(rocker_port, trans, 0, vid); 452511ce2ba3SJiri Pirko if (err) 452611ce2ba3SJiri Pirko return err; 452711ce2ba3SJiri Pirko 452811ce2ba3SJiri Pirko err = rocker_port_router_mac(rocker_port, trans, 0, htons(vid)); 452911ce2ba3SJiri Pirko if (err) 453011ce2ba3SJiri Pirko rocker_port_vlan(rocker_port, trans, 453111ce2ba3SJiri Pirko ROCKER_OP_FLAG_REMOVE, vid); 453211ce2ba3SJiri Pirko 453311ce2ba3SJiri Pirko return err; 453411ce2ba3SJiri Pirko } 453511ce2ba3SJiri Pirko 453611ce2ba3SJiri Pirko static int rocker_port_vlans_add(struct rocker_port *rocker_port, 453711ce2ba3SJiri Pirko struct switchdev_trans *trans, 453811ce2ba3SJiri Pirko const struct switchdev_obj_port_vlan *vlan) 453911ce2ba3SJiri Pirko { 454011ce2ba3SJiri Pirko u16 vid; 454111ce2ba3SJiri Pirko int err; 454211ce2ba3SJiri Pirko 454311ce2ba3SJiri Pirko for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 454411ce2ba3SJiri Pirko err = rocker_port_vlan_add(rocker_port, trans, 454511ce2ba3SJiri Pirko vid, vlan->flags); 454611ce2ba3SJiri Pirko if (err) 454711ce2ba3SJiri Pirko return err; 454811ce2ba3SJiri Pirko } 454911ce2ba3SJiri Pirko 455011ce2ba3SJiri Pirko return 0; 455111ce2ba3SJiri Pirko } 455211ce2ba3SJiri Pirko 455311ce2ba3SJiri Pirko static int rocker_port_fdb_add(struct rocker_port *rocker_port, 455411ce2ba3SJiri Pirko struct switchdev_trans *trans, 455511ce2ba3SJiri Pirko const struct switchdev_obj_port_fdb *fdb) 455611ce2ba3SJiri Pirko { 455711ce2ba3SJiri Pirko __be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL); 455811ce2ba3SJiri Pirko int flags = 0; 455911ce2ba3SJiri Pirko 456011ce2ba3SJiri Pirko if (!rocker_port_is_bridged(rocker_port)) 456111ce2ba3SJiri Pirko return -EINVAL; 456211ce2ba3SJiri Pirko 456311ce2ba3SJiri Pirko return rocker_port_fdb(rocker_port, trans, fdb->addr, vlan_id, flags); 456411ce2ba3SJiri Pirko } 456511ce2ba3SJiri Pirko 456611ce2ba3SJiri Pirko static int rocker_port_obj_add(struct net_device *dev, 456711ce2ba3SJiri Pirko const struct switchdev_obj *obj, 456811ce2ba3SJiri Pirko struct switchdev_trans *trans) 456911ce2ba3SJiri Pirko { 457011ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 457111ce2ba3SJiri Pirko const struct switchdev_obj_ipv4_fib *fib4; 457211ce2ba3SJiri Pirko int err = 0; 457311ce2ba3SJiri Pirko 457411ce2ba3SJiri Pirko switch (obj->id) { 457511ce2ba3SJiri Pirko case SWITCHDEV_OBJ_ID_PORT_VLAN: 457611ce2ba3SJiri Pirko err = rocker_port_vlans_add(rocker_port, trans, 457711ce2ba3SJiri Pirko SWITCHDEV_OBJ_PORT_VLAN(obj)); 4578e420114eSJiri Pirko if (err) 4579e420114eSJiri Pirko break; 4580e420114eSJiri Pirko err = rocker_world_port_obj_vlan_add(rocker_port, 4581e420114eSJiri Pirko SWITCHDEV_OBJ_PORT_VLAN(obj), 4582e420114eSJiri Pirko trans); 458311ce2ba3SJiri Pirko break; 458411ce2ba3SJiri Pirko case SWITCHDEV_OBJ_ID_IPV4_FIB: 458511ce2ba3SJiri Pirko fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj); 458611ce2ba3SJiri Pirko err = rocker_port_fib_ipv4(rocker_port, trans, 458711ce2ba3SJiri Pirko htonl(fib4->dst), fib4->dst_len, 458811ce2ba3SJiri Pirko &fib4->fi, fib4->tb_id, 0); 4589e420114eSJiri Pirko if (err) 4590e420114eSJiri Pirko break; 4591e420114eSJiri Pirko err = rocker_world_port_obj_fib4_add(rocker_port, 4592e420114eSJiri Pirko SWITCHDEV_OBJ_IPV4_FIB(obj), 4593e420114eSJiri Pirko trans); 459411ce2ba3SJiri Pirko break; 459511ce2ba3SJiri Pirko case SWITCHDEV_OBJ_ID_PORT_FDB: 459611ce2ba3SJiri Pirko err = rocker_port_fdb_add(rocker_port, trans, 459711ce2ba3SJiri Pirko SWITCHDEV_OBJ_PORT_FDB(obj)); 4598e420114eSJiri Pirko if (err) 4599e420114eSJiri Pirko break; 4600e420114eSJiri Pirko err = rocker_world_port_obj_fdb_add(rocker_port, 4601e420114eSJiri Pirko SWITCHDEV_OBJ_PORT_FDB(obj), 4602e420114eSJiri Pirko trans); 460311ce2ba3SJiri Pirko break; 460411ce2ba3SJiri Pirko default: 460511ce2ba3SJiri Pirko err = -EOPNOTSUPP; 460611ce2ba3SJiri Pirko break; 460711ce2ba3SJiri Pirko } 460811ce2ba3SJiri Pirko 460911ce2ba3SJiri Pirko return err; 461011ce2ba3SJiri Pirko } 461111ce2ba3SJiri Pirko 461211ce2ba3SJiri Pirko static int rocker_port_vlan_del(struct rocker_port *rocker_port, 461311ce2ba3SJiri Pirko u16 vid, u16 flags) 461411ce2ba3SJiri Pirko { 461511ce2ba3SJiri Pirko int err; 461611ce2ba3SJiri Pirko 461711ce2ba3SJiri Pirko err = rocker_port_router_mac(rocker_port, NULL, 461811ce2ba3SJiri Pirko ROCKER_OP_FLAG_REMOVE, htons(vid)); 461911ce2ba3SJiri Pirko if (err) 462011ce2ba3SJiri Pirko return err; 462111ce2ba3SJiri Pirko 462211ce2ba3SJiri Pirko return rocker_port_vlan(rocker_port, NULL, 462311ce2ba3SJiri Pirko ROCKER_OP_FLAG_REMOVE, vid); 462411ce2ba3SJiri Pirko } 462511ce2ba3SJiri Pirko 462611ce2ba3SJiri Pirko static int rocker_port_vlans_del(struct rocker_port *rocker_port, 462711ce2ba3SJiri Pirko const struct switchdev_obj_port_vlan *vlan) 462811ce2ba3SJiri Pirko { 462911ce2ba3SJiri Pirko u16 vid; 463011ce2ba3SJiri Pirko int err; 463111ce2ba3SJiri Pirko 463211ce2ba3SJiri Pirko for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { 463311ce2ba3SJiri Pirko err = rocker_port_vlan_del(rocker_port, vid, vlan->flags); 463411ce2ba3SJiri Pirko if (err) 463511ce2ba3SJiri Pirko return err; 463611ce2ba3SJiri Pirko } 463711ce2ba3SJiri Pirko 463811ce2ba3SJiri Pirko return 0; 463911ce2ba3SJiri Pirko } 464011ce2ba3SJiri Pirko 464111ce2ba3SJiri Pirko static int rocker_port_fdb_del(struct rocker_port *rocker_port, 464211ce2ba3SJiri Pirko struct switchdev_trans *trans, 464311ce2ba3SJiri Pirko const struct switchdev_obj_port_fdb *fdb) 464411ce2ba3SJiri Pirko { 464511ce2ba3SJiri Pirko __be16 vlan_id = rocker_port_vid_to_vlan(rocker_port, fdb->vid, NULL); 464611ce2ba3SJiri Pirko int flags = ROCKER_OP_FLAG_REMOVE; 464711ce2ba3SJiri Pirko 464811ce2ba3SJiri Pirko if (!rocker_port_is_bridged(rocker_port)) 464911ce2ba3SJiri Pirko return -EINVAL; 465011ce2ba3SJiri Pirko 465111ce2ba3SJiri Pirko return rocker_port_fdb(rocker_port, trans, fdb->addr, vlan_id, flags); 465211ce2ba3SJiri Pirko } 465311ce2ba3SJiri Pirko 465411ce2ba3SJiri Pirko static int rocker_port_obj_del(struct net_device *dev, 465511ce2ba3SJiri Pirko const struct switchdev_obj *obj) 465611ce2ba3SJiri Pirko { 465711ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 465811ce2ba3SJiri Pirko const struct switchdev_obj_ipv4_fib *fib4; 465911ce2ba3SJiri Pirko int err = 0; 466011ce2ba3SJiri Pirko 466111ce2ba3SJiri Pirko switch (obj->id) { 466211ce2ba3SJiri Pirko case SWITCHDEV_OBJ_ID_PORT_VLAN: 466311ce2ba3SJiri Pirko err = rocker_port_vlans_del(rocker_port, 466411ce2ba3SJiri Pirko SWITCHDEV_OBJ_PORT_VLAN(obj)); 4665e420114eSJiri Pirko if (err) 4666e420114eSJiri Pirko break; 4667e420114eSJiri Pirko err = rocker_world_port_obj_vlan_del(rocker_port, 4668e420114eSJiri Pirko SWITCHDEV_OBJ_PORT_VLAN(obj)); 466911ce2ba3SJiri Pirko break; 467011ce2ba3SJiri Pirko case SWITCHDEV_OBJ_ID_IPV4_FIB: 467111ce2ba3SJiri Pirko fib4 = SWITCHDEV_OBJ_IPV4_FIB(obj); 467211ce2ba3SJiri Pirko err = rocker_port_fib_ipv4(rocker_port, NULL, 467311ce2ba3SJiri Pirko htonl(fib4->dst), fib4->dst_len, 467411ce2ba3SJiri Pirko &fib4->fi, fib4->tb_id, 467511ce2ba3SJiri Pirko ROCKER_OP_FLAG_REMOVE); 4676e420114eSJiri Pirko if (err) 4677e420114eSJiri Pirko break; 4678e420114eSJiri Pirko err = rocker_world_port_obj_fib4_del(rocker_port, 4679e420114eSJiri Pirko SWITCHDEV_OBJ_IPV4_FIB(obj)); 468011ce2ba3SJiri Pirko break; 468111ce2ba3SJiri Pirko case SWITCHDEV_OBJ_ID_PORT_FDB: 468211ce2ba3SJiri Pirko err = rocker_port_fdb_del(rocker_port, NULL, 468311ce2ba3SJiri Pirko SWITCHDEV_OBJ_PORT_FDB(obj)); 4684e420114eSJiri Pirko if (err) 4685e420114eSJiri Pirko break; 4686e420114eSJiri Pirko err = rocker_world_port_obj_fdb_del(rocker_port, 4687e420114eSJiri Pirko SWITCHDEV_OBJ_PORT_FDB(obj)); 468811ce2ba3SJiri Pirko break; 468911ce2ba3SJiri Pirko default: 469011ce2ba3SJiri Pirko err = -EOPNOTSUPP; 469111ce2ba3SJiri Pirko break; 469211ce2ba3SJiri Pirko } 469311ce2ba3SJiri Pirko 469411ce2ba3SJiri Pirko return err; 469511ce2ba3SJiri Pirko } 469611ce2ba3SJiri Pirko 469711ce2ba3SJiri Pirko static int rocker_port_fdb_dump(const struct rocker_port *rocker_port, 469811ce2ba3SJiri Pirko struct switchdev_obj_port_fdb *fdb, 469911ce2ba3SJiri Pirko switchdev_obj_dump_cb_t *cb) 470011ce2ba3SJiri Pirko { 470111ce2ba3SJiri Pirko struct rocker *rocker = rocker_port->rocker; 470211ce2ba3SJiri Pirko struct rocker_fdb_tbl_entry *found; 470311ce2ba3SJiri Pirko struct hlist_node *tmp; 470411ce2ba3SJiri Pirko unsigned long lock_flags; 470511ce2ba3SJiri Pirko int bkt; 470611ce2ba3SJiri Pirko int err = 0; 470711ce2ba3SJiri Pirko 470811ce2ba3SJiri Pirko spin_lock_irqsave(&rocker->fdb_tbl_lock, lock_flags); 470911ce2ba3SJiri Pirko hash_for_each_safe(rocker->fdb_tbl, bkt, tmp, found, entry) { 471011ce2ba3SJiri Pirko if (found->key.rocker_port != rocker_port) 471111ce2ba3SJiri Pirko continue; 471211ce2ba3SJiri Pirko ether_addr_copy(fdb->addr, found->key.addr); 471311ce2ba3SJiri Pirko fdb->ndm_state = NUD_REACHABLE; 471411ce2ba3SJiri Pirko fdb->vid = rocker_port_vlan_to_vid(rocker_port, 471511ce2ba3SJiri Pirko found->key.vlan_id); 471611ce2ba3SJiri Pirko err = cb(&fdb->obj); 471711ce2ba3SJiri Pirko if (err) 471811ce2ba3SJiri Pirko break; 471911ce2ba3SJiri Pirko } 472011ce2ba3SJiri Pirko spin_unlock_irqrestore(&rocker->fdb_tbl_lock, lock_flags); 472111ce2ba3SJiri Pirko 472211ce2ba3SJiri Pirko return err; 472311ce2ba3SJiri Pirko } 472411ce2ba3SJiri Pirko 472511ce2ba3SJiri Pirko static int rocker_port_vlan_dump(const struct rocker_port *rocker_port, 472611ce2ba3SJiri Pirko struct switchdev_obj_port_vlan *vlan, 472711ce2ba3SJiri Pirko switchdev_obj_dump_cb_t *cb) 472811ce2ba3SJiri Pirko { 472911ce2ba3SJiri Pirko u16 vid; 473011ce2ba3SJiri Pirko int err = 0; 473111ce2ba3SJiri Pirko 473211ce2ba3SJiri Pirko for (vid = 1; vid < VLAN_N_VID; vid++) { 473311ce2ba3SJiri Pirko if (!test_bit(vid, rocker_port->vlan_bitmap)) 473411ce2ba3SJiri Pirko continue; 473511ce2ba3SJiri Pirko vlan->flags = 0; 473611ce2ba3SJiri Pirko if (rocker_vlan_id_is_internal(htons(vid))) 473711ce2ba3SJiri Pirko vlan->flags |= BRIDGE_VLAN_INFO_PVID; 473811ce2ba3SJiri Pirko vlan->vid_begin = vid; 473911ce2ba3SJiri Pirko vlan->vid_end = vid; 474011ce2ba3SJiri Pirko err = cb(&vlan->obj); 474111ce2ba3SJiri Pirko if (err) 474211ce2ba3SJiri Pirko break; 474311ce2ba3SJiri Pirko } 474411ce2ba3SJiri Pirko 474511ce2ba3SJiri Pirko return err; 474611ce2ba3SJiri Pirko } 474711ce2ba3SJiri Pirko 474811ce2ba3SJiri Pirko static int rocker_port_obj_dump(struct net_device *dev, 474911ce2ba3SJiri Pirko struct switchdev_obj *obj, 475011ce2ba3SJiri Pirko switchdev_obj_dump_cb_t *cb) 475111ce2ba3SJiri Pirko { 475211ce2ba3SJiri Pirko const struct rocker_port *rocker_port = netdev_priv(dev); 475311ce2ba3SJiri Pirko int err = 0; 475411ce2ba3SJiri Pirko 475511ce2ba3SJiri Pirko switch (obj->id) { 475611ce2ba3SJiri Pirko case SWITCHDEV_OBJ_ID_PORT_FDB: 475711ce2ba3SJiri Pirko err = rocker_port_fdb_dump(rocker_port, 475811ce2ba3SJiri Pirko SWITCHDEV_OBJ_PORT_FDB(obj), cb); 4759e420114eSJiri Pirko if (err) 4760e420114eSJiri Pirko break; 4761e420114eSJiri Pirko err = rocker_world_port_obj_fdb_dump(rocker_port, 4762e420114eSJiri Pirko SWITCHDEV_OBJ_PORT_FDB(obj), 4763e420114eSJiri Pirko cb); 476411ce2ba3SJiri Pirko break; 476511ce2ba3SJiri Pirko case SWITCHDEV_OBJ_ID_PORT_VLAN: 476611ce2ba3SJiri Pirko err = rocker_port_vlan_dump(rocker_port, 476711ce2ba3SJiri Pirko SWITCHDEV_OBJ_PORT_VLAN(obj), cb); 4768e420114eSJiri Pirko if (err) 4769e420114eSJiri Pirko break; 4770e420114eSJiri Pirko err = rocker_world_port_obj_vlan_dump(rocker_port, 4771e420114eSJiri Pirko SWITCHDEV_OBJ_PORT_VLAN(obj), 4772e420114eSJiri Pirko cb); 477311ce2ba3SJiri Pirko break; 477411ce2ba3SJiri Pirko default: 477511ce2ba3SJiri Pirko err = -EOPNOTSUPP; 477611ce2ba3SJiri Pirko break; 477711ce2ba3SJiri Pirko } 477811ce2ba3SJiri Pirko 477911ce2ba3SJiri Pirko return err; 478011ce2ba3SJiri Pirko } 478111ce2ba3SJiri Pirko 478211ce2ba3SJiri Pirko static const struct switchdev_ops rocker_port_switchdev_ops = { 478311ce2ba3SJiri Pirko .switchdev_port_attr_get = rocker_port_attr_get, 478411ce2ba3SJiri Pirko .switchdev_port_attr_set = rocker_port_attr_set, 478511ce2ba3SJiri Pirko .switchdev_port_obj_add = rocker_port_obj_add, 478611ce2ba3SJiri Pirko .switchdev_port_obj_del = rocker_port_obj_del, 478711ce2ba3SJiri Pirko .switchdev_port_obj_dump = rocker_port_obj_dump, 478811ce2ba3SJiri Pirko }; 478911ce2ba3SJiri Pirko 479011ce2ba3SJiri Pirko /******************** 479111ce2ba3SJiri Pirko * ethtool interface 479211ce2ba3SJiri Pirko ********************/ 479311ce2ba3SJiri Pirko 479411ce2ba3SJiri Pirko static int rocker_port_get_settings(struct net_device *dev, 479511ce2ba3SJiri Pirko struct ethtool_cmd *ecmd) 479611ce2ba3SJiri Pirko { 479711ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 479811ce2ba3SJiri Pirko 479911ce2ba3SJiri Pirko return rocker_cmd_get_port_settings_ethtool(rocker_port, ecmd); 480011ce2ba3SJiri Pirko } 480111ce2ba3SJiri Pirko 480211ce2ba3SJiri Pirko static int rocker_port_set_settings(struct net_device *dev, 480311ce2ba3SJiri Pirko struct ethtool_cmd *ecmd) 480411ce2ba3SJiri Pirko { 480511ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 480611ce2ba3SJiri Pirko 480711ce2ba3SJiri Pirko return rocker_cmd_set_port_settings_ethtool(rocker_port, ecmd); 480811ce2ba3SJiri Pirko } 480911ce2ba3SJiri Pirko 481011ce2ba3SJiri Pirko static void rocker_port_get_drvinfo(struct net_device *dev, 481111ce2ba3SJiri Pirko struct ethtool_drvinfo *drvinfo) 481211ce2ba3SJiri Pirko { 481311ce2ba3SJiri Pirko strlcpy(drvinfo->driver, rocker_driver_name, sizeof(drvinfo->driver)); 481411ce2ba3SJiri Pirko strlcpy(drvinfo->version, UTS_RELEASE, sizeof(drvinfo->version)); 481511ce2ba3SJiri Pirko } 481611ce2ba3SJiri Pirko 481711ce2ba3SJiri Pirko static struct rocker_port_stats { 481811ce2ba3SJiri Pirko char str[ETH_GSTRING_LEN]; 481911ce2ba3SJiri Pirko int type; 482011ce2ba3SJiri Pirko } rocker_port_stats[] = { 482111ce2ba3SJiri Pirko { "rx_packets", ROCKER_TLV_CMD_PORT_STATS_RX_PKTS, }, 482211ce2ba3SJiri Pirko { "rx_bytes", ROCKER_TLV_CMD_PORT_STATS_RX_BYTES, }, 482311ce2ba3SJiri Pirko { "rx_dropped", ROCKER_TLV_CMD_PORT_STATS_RX_DROPPED, }, 482411ce2ba3SJiri Pirko { "rx_errors", ROCKER_TLV_CMD_PORT_STATS_RX_ERRORS, }, 482511ce2ba3SJiri Pirko 482611ce2ba3SJiri Pirko { "tx_packets", ROCKER_TLV_CMD_PORT_STATS_TX_PKTS, }, 482711ce2ba3SJiri Pirko { "tx_bytes", ROCKER_TLV_CMD_PORT_STATS_TX_BYTES, }, 482811ce2ba3SJiri Pirko { "tx_dropped", ROCKER_TLV_CMD_PORT_STATS_TX_DROPPED, }, 482911ce2ba3SJiri Pirko { "tx_errors", ROCKER_TLV_CMD_PORT_STATS_TX_ERRORS, }, 483011ce2ba3SJiri Pirko }; 483111ce2ba3SJiri Pirko 483211ce2ba3SJiri Pirko #define ROCKER_PORT_STATS_LEN ARRAY_SIZE(rocker_port_stats) 483311ce2ba3SJiri Pirko 483411ce2ba3SJiri Pirko static void rocker_port_get_strings(struct net_device *netdev, u32 stringset, 483511ce2ba3SJiri Pirko u8 *data) 483611ce2ba3SJiri Pirko { 483711ce2ba3SJiri Pirko u8 *p = data; 483811ce2ba3SJiri Pirko int i; 483911ce2ba3SJiri Pirko 484011ce2ba3SJiri Pirko switch (stringset) { 484111ce2ba3SJiri Pirko case ETH_SS_STATS: 484211ce2ba3SJiri Pirko for (i = 0; i < ARRAY_SIZE(rocker_port_stats); i++) { 484311ce2ba3SJiri Pirko memcpy(p, rocker_port_stats[i].str, ETH_GSTRING_LEN); 484411ce2ba3SJiri Pirko p += ETH_GSTRING_LEN; 484511ce2ba3SJiri Pirko } 484611ce2ba3SJiri Pirko break; 484711ce2ba3SJiri Pirko } 484811ce2ba3SJiri Pirko } 484911ce2ba3SJiri Pirko 485011ce2ba3SJiri Pirko static int 485111ce2ba3SJiri Pirko rocker_cmd_get_port_stats_prep(const struct rocker_port *rocker_port, 485211ce2ba3SJiri Pirko struct rocker_desc_info *desc_info, 485311ce2ba3SJiri Pirko void *priv) 485411ce2ba3SJiri Pirko { 485511ce2ba3SJiri Pirko struct rocker_tlv *cmd_stats; 485611ce2ba3SJiri Pirko 485711ce2ba3SJiri Pirko if (rocker_tlv_put_u16(desc_info, ROCKER_TLV_CMD_TYPE, 485811ce2ba3SJiri Pirko ROCKER_TLV_CMD_TYPE_GET_PORT_STATS)) 485911ce2ba3SJiri Pirko return -EMSGSIZE; 486011ce2ba3SJiri Pirko 486111ce2ba3SJiri Pirko cmd_stats = rocker_tlv_nest_start(desc_info, ROCKER_TLV_CMD_INFO); 486211ce2ba3SJiri Pirko if (!cmd_stats) 486311ce2ba3SJiri Pirko return -EMSGSIZE; 486411ce2ba3SJiri Pirko 486511ce2ba3SJiri Pirko if (rocker_tlv_put_u32(desc_info, ROCKER_TLV_CMD_PORT_STATS_PPORT, 486611ce2ba3SJiri Pirko rocker_port->pport)) 486711ce2ba3SJiri Pirko return -EMSGSIZE; 486811ce2ba3SJiri Pirko 486911ce2ba3SJiri Pirko rocker_tlv_nest_end(desc_info, cmd_stats); 487011ce2ba3SJiri Pirko 487111ce2ba3SJiri Pirko return 0; 487211ce2ba3SJiri Pirko } 487311ce2ba3SJiri Pirko 487411ce2ba3SJiri Pirko static int 487511ce2ba3SJiri Pirko rocker_cmd_get_port_stats_ethtool_proc(const struct rocker_port *rocker_port, 487611ce2ba3SJiri Pirko const struct rocker_desc_info *desc_info, 487711ce2ba3SJiri Pirko void *priv) 487811ce2ba3SJiri Pirko { 487911ce2ba3SJiri Pirko const struct rocker_tlv *attrs[ROCKER_TLV_CMD_MAX + 1]; 488011ce2ba3SJiri Pirko const struct rocker_tlv *stats_attrs[ROCKER_TLV_CMD_PORT_STATS_MAX + 1]; 488111ce2ba3SJiri Pirko const struct rocker_tlv *pattr; 488211ce2ba3SJiri Pirko u32 pport; 488311ce2ba3SJiri Pirko u64 *data = priv; 488411ce2ba3SJiri Pirko int i; 488511ce2ba3SJiri Pirko 488611ce2ba3SJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_CMD_MAX, desc_info); 488711ce2ba3SJiri Pirko 488811ce2ba3SJiri Pirko if (!attrs[ROCKER_TLV_CMD_INFO]) 488911ce2ba3SJiri Pirko return -EIO; 489011ce2ba3SJiri Pirko 489111ce2ba3SJiri Pirko rocker_tlv_parse_nested(stats_attrs, ROCKER_TLV_CMD_PORT_STATS_MAX, 489211ce2ba3SJiri Pirko attrs[ROCKER_TLV_CMD_INFO]); 489311ce2ba3SJiri Pirko 489411ce2ba3SJiri Pirko if (!stats_attrs[ROCKER_TLV_CMD_PORT_STATS_PPORT]) 489511ce2ba3SJiri Pirko return -EIO; 489611ce2ba3SJiri Pirko 489711ce2ba3SJiri Pirko pport = rocker_tlv_get_u32(stats_attrs[ROCKER_TLV_CMD_PORT_STATS_PPORT]); 489811ce2ba3SJiri Pirko if (pport != rocker_port->pport) 489911ce2ba3SJiri Pirko return -EIO; 490011ce2ba3SJiri Pirko 490111ce2ba3SJiri Pirko for (i = 0; i < ARRAY_SIZE(rocker_port_stats); i++) { 490211ce2ba3SJiri Pirko pattr = stats_attrs[rocker_port_stats[i].type]; 490311ce2ba3SJiri Pirko if (!pattr) 490411ce2ba3SJiri Pirko continue; 490511ce2ba3SJiri Pirko 490611ce2ba3SJiri Pirko data[i] = rocker_tlv_get_u64(pattr); 490711ce2ba3SJiri Pirko } 490811ce2ba3SJiri Pirko 490911ce2ba3SJiri Pirko return 0; 491011ce2ba3SJiri Pirko } 491111ce2ba3SJiri Pirko 491211ce2ba3SJiri Pirko static int rocker_cmd_get_port_stats_ethtool(struct rocker_port *rocker_port, 491311ce2ba3SJiri Pirko void *priv) 491411ce2ba3SJiri Pirko { 491511ce2ba3SJiri Pirko return rocker_cmd_exec(rocker_port, NULL, 0, 491611ce2ba3SJiri Pirko rocker_cmd_get_port_stats_prep, NULL, 491711ce2ba3SJiri Pirko rocker_cmd_get_port_stats_ethtool_proc, 491811ce2ba3SJiri Pirko priv); 491911ce2ba3SJiri Pirko } 492011ce2ba3SJiri Pirko 492111ce2ba3SJiri Pirko static void rocker_port_get_stats(struct net_device *dev, 492211ce2ba3SJiri Pirko struct ethtool_stats *stats, u64 *data) 492311ce2ba3SJiri Pirko { 492411ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 492511ce2ba3SJiri Pirko 492611ce2ba3SJiri Pirko if (rocker_cmd_get_port_stats_ethtool(rocker_port, data) != 0) { 492711ce2ba3SJiri Pirko int i; 492811ce2ba3SJiri Pirko 492911ce2ba3SJiri Pirko for (i = 0; i < ARRAY_SIZE(rocker_port_stats); ++i) 493011ce2ba3SJiri Pirko data[i] = 0; 493111ce2ba3SJiri Pirko } 493211ce2ba3SJiri Pirko } 493311ce2ba3SJiri Pirko 493411ce2ba3SJiri Pirko static int rocker_port_get_sset_count(struct net_device *netdev, int sset) 493511ce2ba3SJiri Pirko { 493611ce2ba3SJiri Pirko switch (sset) { 493711ce2ba3SJiri Pirko case ETH_SS_STATS: 493811ce2ba3SJiri Pirko return ROCKER_PORT_STATS_LEN; 493911ce2ba3SJiri Pirko default: 494011ce2ba3SJiri Pirko return -EOPNOTSUPP; 494111ce2ba3SJiri Pirko } 494211ce2ba3SJiri Pirko } 494311ce2ba3SJiri Pirko 494411ce2ba3SJiri Pirko static const struct ethtool_ops rocker_port_ethtool_ops = { 494511ce2ba3SJiri Pirko .get_settings = rocker_port_get_settings, 494611ce2ba3SJiri Pirko .set_settings = rocker_port_set_settings, 494711ce2ba3SJiri Pirko .get_drvinfo = rocker_port_get_drvinfo, 494811ce2ba3SJiri Pirko .get_link = ethtool_op_get_link, 494911ce2ba3SJiri Pirko .get_strings = rocker_port_get_strings, 495011ce2ba3SJiri Pirko .get_ethtool_stats = rocker_port_get_stats, 495111ce2ba3SJiri Pirko .get_sset_count = rocker_port_get_sset_count, 495211ce2ba3SJiri Pirko }; 495311ce2ba3SJiri Pirko 495411ce2ba3SJiri Pirko /***************** 495511ce2ba3SJiri Pirko * NAPI interface 495611ce2ba3SJiri Pirko *****************/ 495711ce2ba3SJiri Pirko 495811ce2ba3SJiri Pirko static struct rocker_port *rocker_port_napi_tx_get(struct napi_struct *napi) 495911ce2ba3SJiri Pirko { 496011ce2ba3SJiri Pirko return container_of(napi, struct rocker_port, napi_tx); 496111ce2ba3SJiri Pirko } 496211ce2ba3SJiri Pirko 496311ce2ba3SJiri Pirko static int rocker_port_poll_tx(struct napi_struct *napi, int budget) 496411ce2ba3SJiri Pirko { 496511ce2ba3SJiri Pirko struct rocker_port *rocker_port = rocker_port_napi_tx_get(napi); 496611ce2ba3SJiri Pirko const struct rocker *rocker = rocker_port->rocker; 496711ce2ba3SJiri Pirko const struct rocker_desc_info *desc_info; 496811ce2ba3SJiri Pirko u32 credits = 0; 496911ce2ba3SJiri Pirko int err; 497011ce2ba3SJiri Pirko 497111ce2ba3SJiri Pirko /* Cleanup tx descriptors */ 497211ce2ba3SJiri Pirko while ((desc_info = rocker_desc_tail_get(&rocker_port->tx_ring))) { 497311ce2ba3SJiri Pirko struct sk_buff *skb; 497411ce2ba3SJiri Pirko 497511ce2ba3SJiri Pirko err = rocker_desc_err(desc_info); 497611ce2ba3SJiri Pirko if (err && net_ratelimit()) 497711ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "tx desc received with err %d\n", 497811ce2ba3SJiri Pirko err); 497911ce2ba3SJiri Pirko rocker_tx_desc_frags_unmap(rocker_port, desc_info); 498011ce2ba3SJiri Pirko 498111ce2ba3SJiri Pirko skb = rocker_desc_cookie_ptr_get(desc_info); 498211ce2ba3SJiri Pirko if (err == 0) { 498311ce2ba3SJiri Pirko rocker_port->dev->stats.tx_packets++; 498411ce2ba3SJiri Pirko rocker_port->dev->stats.tx_bytes += skb->len; 498511ce2ba3SJiri Pirko } else { 498611ce2ba3SJiri Pirko rocker_port->dev->stats.tx_errors++; 498711ce2ba3SJiri Pirko } 498811ce2ba3SJiri Pirko 498911ce2ba3SJiri Pirko dev_kfree_skb_any(skb); 499011ce2ba3SJiri Pirko credits++; 499111ce2ba3SJiri Pirko } 499211ce2ba3SJiri Pirko 499311ce2ba3SJiri Pirko if (credits && netif_queue_stopped(rocker_port->dev)) 499411ce2ba3SJiri Pirko netif_wake_queue(rocker_port->dev); 499511ce2ba3SJiri Pirko 499611ce2ba3SJiri Pirko napi_complete(napi); 499711ce2ba3SJiri Pirko rocker_dma_ring_credits_set(rocker, &rocker_port->tx_ring, credits); 499811ce2ba3SJiri Pirko 499911ce2ba3SJiri Pirko return 0; 500011ce2ba3SJiri Pirko } 500111ce2ba3SJiri Pirko 500211ce2ba3SJiri Pirko static int rocker_port_rx_proc(const struct rocker *rocker, 500311ce2ba3SJiri Pirko const struct rocker_port *rocker_port, 500411ce2ba3SJiri Pirko struct rocker_desc_info *desc_info) 500511ce2ba3SJiri Pirko { 500611ce2ba3SJiri Pirko const struct rocker_tlv *attrs[ROCKER_TLV_RX_MAX + 1]; 500711ce2ba3SJiri Pirko struct sk_buff *skb = rocker_desc_cookie_ptr_get(desc_info); 500811ce2ba3SJiri Pirko size_t rx_len; 500911ce2ba3SJiri Pirko u16 rx_flags = 0; 501011ce2ba3SJiri Pirko 501111ce2ba3SJiri Pirko if (!skb) 501211ce2ba3SJiri Pirko return -ENOENT; 501311ce2ba3SJiri Pirko 501411ce2ba3SJiri Pirko rocker_tlv_parse_desc(attrs, ROCKER_TLV_RX_MAX, desc_info); 501511ce2ba3SJiri Pirko if (!attrs[ROCKER_TLV_RX_FRAG_LEN]) 501611ce2ba3SJiri Pirko return -EINVAL; 501711ce2ba3SJiri Pirko if (attrs[ROCKER_TLV_RX_FLAGS]) 501811ce2ba3SJiri Pirko rx_flags = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FLAGS]); 501911ce2ba3SJiri Pirko 502011ce2ba3SJiri Pirko rocker_dma_rx_ring_skb_unmap(rocker, attrs); 502111ce2ba3SJiri Pirko 502211ce2ba3SJiri Pirko rx_len = rocker_tlv_get_u16(attrs[ROCKER_TLV_RX_FRAG_LEN]); 502311ce2ba3SJiri Pirko skb_put(skb, rx_len); 502411ce2ba3SJiri Pirko skb->protocol = eth_type_trans(skb, rocker_port->dev); 502511ce2ba3SJiri Pirko 502611ce2ba3SJiri Pirko if (rx_flags & ROCKER_RX_FLAGS_FWD_OFFLOAD) 502711ce2ba3SJiri Pirko skb->offload_fwd_mark = rocker_port->dev->offload_fwd_mark; 502811ce2ba3SJiri Pirko 502911ce2ba3SJiri Pirko rocker_port->dev->stats.rx_packets++; 503011ce2ba3SJiri Pirko rocker_port->dev->stats.rx_bytes += skb->len; 503111ce2ba3SJiri Pirko 503211ce2ba3SJiri Pirko netif_receive_skb(skb); 503311ce2ba3SJiri Pirko 503411ce2ba3SJiri Pirko return rocker_dma_rx_ring_skb_alloc(rocker_port, desc_info); 503511ce2ba3SJiri Pirko } 503611ce2ba3SJiri Pirko 503711ce2ba3SJiri Pirko static struct rocker_port *rocker_port_napi_rx_get(struct napi_struct *napi) 503811ce2ba3SJiri Pirko { 503911ce2ba3SJiri Pirko return container_of(napi, struct rocker_port, napi_rx); 504011ce2ba3SJiri Pirko } 504111ce2ba3SJiri Pirko 504211ce2ba3SJiri Pirko static int rocker_port_poll_rx(struct napi_struct *napi, int budget) 504311ce2ba3SJiri Pirko { 504411ce2ba3SJiri Pirko struct rocker_port *rocker_port = rocker_port_napi_rx_get(napi); 504511ce2ba3SJiri Pirko const struct rocker *rocker = rocker_port->rocker; 504611ce2ba3SJiri Pirko struct rocker_desc_info *desc_info; 504711ce2ba3SJiri Pirko u32 credits = 0; 504811ce2ba3SJiri Pirko int err; 504911ce2ba3SJiri Pirko 505011ce2ba3SJiri Pirko /* Process rx descriptors */ 505111ce2ba3SJiri Pirko while (credits < budget && 505211ce2ba3SJiri Pirko (desc_info = rocker_desc_tail_get(&rocker_port->rx_ring))) { 505311ce2ba3SJiri Pirko err = rocker_desc_err(desc_info); 505411ce2ba3SJiri Pirko if (err) { 505511ce2ba3SJiri Pirko if (net_ratelimit()) 505611ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "rx desc received with err %d\n", 505711ce2ba3SJiri Pirko err); 505811ce2ba3SJiri Pirko } else { 505911ce2ba3SJiri Pirko err = rocker_port_rx_proc(rocker, rocker_port, 506011ce2ba3SJiri Pirko desc_info); 506111ce2ba3SJiri Pirko if (err && net_ratelimit()) 506211ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "rx processing failed with err %d\n", 506311ce2ba3SJiri Pirko err); 506411ce2ba3SJiri Pirko } 506511ce2ba3SJiri Pirko if (err) 506611ce2ba3SJiri Pirko rocker_port->dev->stats.rx_errors++; 506711ce2ba3SJiri Pirko 506811ce2ba3SJiri Pirko rocker_desc_gen_clear(desc_info); 506911ce2ba3SJiri Pirko rocker_desc_head_set(rocker, &rocker_port->rx_ring, desc_info); 507011ce2ba3SJiri Pirko credits++; 507111ce2ba3SJiri Pirko } 507211ce2ba3SJiri Pirko 507311ce2ba3SJiri Pirko if (credits < budget) 507411ce2ba3SJiri Pirko napi_complete(napi); 507511ce2ba3SJiri Pirko 507611ce2ba3SJiri Pirko rocker_dma_ring_credits_set(rocker, &rocker_port->rx_ring, credits); 507711ce2ba3SJiri Pirko 507811ce2ba3SJiri Pirko return credits; 507911ce2ba3SJiri Pirko } 508011ce2ba3SJiri Pirko 508111ce2ba3SJiri Pirko /***************** 508211ce2ba3SJiri Pirko * PCI driver ops 508311ce2ba3SJiri Pirko *****************/ 508411ce2ba3SJiri Pirko 508511ce2ba3SJiri Pirko static void rocker_carrier_init(const struct rocker_port *rocker_port) 508611ce2ba3SJiri Pirko { 508711ce2ba3SJiri Pirko const struct rocker *rocker = rocker_port->rocker; 508811ce2ba3SJiri Pirko u64 link_status = rocker_read64(rocker, PORT_PHYS_LINK_STATUS); 508911ce2ba3SJiri Pirko bool link_up; 509011ce2ba3SJiri Pirko 509111ce2ba3SJiri Pirko link_up = link_status & (1 << rocker_port->pport); 509211ce2ba3SJiri Pirko if (link_up) 509311ce2ba3SJiri Pirko netif_carrier_on(rocker_port->dev); 509411ce2ba3SJiri Pirko else 509511ce2ba3SJiri Pirko netif_carrier_off(rocker_port->dev); 509611ce2ba3SJiri Pirko } 509711ce2ba3SJiri Pirko 5098e420114eSJiri Pirko static void rocker_remove_ports(struct rocker *rocker) 509911ce2ba3SJiri Pirko { 510011ce2ba3SJiri Pirko struct rocker_port *rocker_port; 510111ce2ba3SJiri Pirko int i; 510211ce2ba3SJiri Pirko 510311ce2ba3SJiri Pirko for (i = 0; i < rocker->port_count; i++) { 510411ce2ba3SJiri Pirko rocker_port = rocker->ports[i]; 510511ce2ba3SJiri Pirko if (!rocker_port) 510611ce2ba3SJiri Pirko continue; 510711ce2ba3SJiri Pirko rocker_port_ig_tbl(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE); 5108e420114eSJiri Pirko rocker_world_port_fini(rocker_port); 510911ce2ba3SJiri Pirko unregister_netdev(rocker_port->dev); 5110e420114eSJiri Pirko rocker_world_port_post_fini(rocker_port); 511111ce2ba3SJiri Pirko free_netdev(rocker_port->dev); 511211ce2ba3SJiri Pirko } 5113e420114eSJiri Pirko rocker_world_fini(rocker); 511411ce2ba3SJiri Pirko kfree(rocker->ports); 511511ce2ba3SJiri Pirko } 511611ce2ba3SJiri Pirko 511711ce2ba3SJiri Pirko static void rocker_port_dev_addr_init(struct rocker_port *rocker_port) 511811ce2ba3SJiri Pirko { 511911ce2ba3SJiri Pirko const struct rocker *rocker = rocker_port->rocker; 512011ce2ba3SJiri Pirko const struct pci_dev *pdev = rocker->pdev; 512111ce2ba3SJiri Pirko int err; 512211ce2ba3SJiri Pirko 512311ce2ba3SJiri Pirko err = rocker_cmd_get_port_settings_macaddr(rocker_port, 512411ce2ba3SJiri Pirko rocker_port->dev->dev_addr); 512511ce2ba3SJiri Pirko if (err) { 512611ce2ba3SJiri Pirko dev_warn(&pdev->dev, "failed to get mac address, using random\n"); 512711ce2ba3SJiri Pirko eth_hw_addr_random(rocker_port->dev); 512811ce2ba3SJiri Pirko } 512911ce2ba3SJiri Pirko } 513011ce2ba3SJiri Pirko 513111ce2ba3SJiri Pirko static int rocker_probe_port(struct rocker *rocker, unsigned int port_number) 513211ce2ba3SJiri Pirko { 513311ce2ba3SJiri Pirko const struct pci_dev *pdev = rocker->pdev; 513411ce2ba3SJiri Pirko struct rocker_port *rocker_port; 513511ce2ba3SJiri Pirko struct net_device *dev; 513611ce2ba3SJiri Pirko u16 untagged_vid = 0; 513711ce2ba3SJiri Pirko int err; 513811ce2ba3SJiri Pirko 513911ce2ba3SJiri Pirko dev = alloc_etherdev(sizeof(struct rocker_port)); 514011ce2ba3SJiri Pirko if (!dev) 514111ce2ba3SJiri Pirko return -ENOMEM; 514211ce2ba3SJiri Pirko rocker_port = netdev_priv(dev); 514311ce2ba3SJiri Pirko rocker_port->dev = dev; 514411ce2ba3SJiri Pirko rocker_port->rocker = rocker; 514511ce2ba3SJiri Pirko rocker_port->port_number = port_number; 514611ce2ba3SJiri Pirko rocker_port->pport = port_number + 1; 514711ce2ba3SJiri Pirko rocker_port->brport_flags = BR_LEARNING | BR_LEARNING_SYNC; 514811ce2ba3SJiri Pirko rocker_port->ageing_time = BR_DEFAULT_AGEING_TIME; 514911ce2ba3SJiri Pirko 5150e420114eSJiri Pirko err = rocker_world_check_init(rocker_port); 5151e420114eSJiri Pirko if (err) { 5152e420114eSJiri Pirko dev_err(&pdev->dev, "world init failed\n"); 5153e420114eSJiri Pirko goto err_world_check_init; 5154e420114eSJiri Pirko } 5155e420114eSJiri Pirko 515611ce2ba3SJiri Pirko rocker_port_dev_addr_init(rocker_port); 515711ce2ba3SJiri Pirko dev->netdev_ops = &rocker_port_netdev_ops; 515811ce2ba3SJiri Pirko dev->ethtool_ops = &rocker_port_ethtool_ops; 515911ce2ba3SJiri Pirko dev->switchdev_ops = &rocker_port_switchdev_ops; 516011ce2ba3SJiri Pirko netif_tx_napi_add(dev, &rocker_port->napi_tx, rocker_port_poll_tx, 516111ce2ba3SJiri Pirko NAPI_POLL_WEIGHT); 516211ce2ba3SJiri Pirko netif_napi_add(dev, &rocker_port->napi_rx, rocker_port_poll_rx, 516311ce2ba3SJiri Pirko NAPI_POLL_WEIGHT); 516411ce2ba3SJiri Pirko rocker_carrier_init(rocker_port); 516511ce2ba3SJiri Pirko 516611ce2ba3SJiri Pirko dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_SG; 516711ce2ba3SJiri Pirko 5168e420114eSJiri Pirko err = rocker_world_port_pre_init(rocker_port); 5169e420114eSJiri Pirko if (err) { 5170e420114eSJiri Pirko dev_err(&pdev->dev, "port world pre-init failed\n"); 5171e420114eSJiri Pirko goto err_world_port_pre_init; 5172e420114eSJiri Pirko } 517311ce2ba3SJiri Pirko err = register_netdev(dev); 517411ce2ba3SJiri Pirko if (err) { 517511ce2ba3SJiri Pirko dev_err(&pdev->dev, "register_netdev failed\n"); 517611ce2ba3SJiri Pirko goto err_register_netdev; 517711ce2ba3SJiri Pirko } 517811ce2ba3SJiri Pirko rocker->ports[port_number] = rocker_port; 517911ce2ba3SJiri Pirko 5180e420114eSJiri Pirko err = rocker_world_port_init(rocker_port); 5181e420114eSJiri Pirko if (err) { 5182e420114eSJiri Pirko dev_err(&pdev->dev, "port world init failed\n"); 5183e420114eSJiri Pirko goto err_world_port_init; 5184e420114eSJiri Pirko } 5185e420114eSJiri Pirko 518611ce2ba3SJiri Pirko switchdev_port_fwd_mark_set(rocker_port->dev, NULL, false); 518711ce2ba3SJiri Pirko 5188c1fe922eSJiri Pirko rocker_port_set_learning(rocker_port, NULL, 5189c1fe922eSJiri Pirko !!(rocker_port->brport_flags & BR_LEARNING)); 519011ce2ba3SJiri Pirko 519111ce2ba3SJiri Pirko err = rocker_port_ig_tbl(rocker_port, NULL, 0); 519211ce2ba3SJiri Pirko if (err) { 519311ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "install ig port table failed\n"); 519411ce2ba3SJiri Pirko goto err_port_ig_tbl; 519511ce2ba3SJiri Pirko } 519611ce2ba3SJiri Pirko 519711ce2ba3SJiri Pirko rocker_port->internal_vlan_id = 519811ce2ba3SJiri Pirko rocker_port_internal_vlan_id_get(rocker_port, dev->ifindex); 519911ce2ba3SJiri Pirko 520011ce2ba3SJiri Pirko err = rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0); 520111ce2ba3SJiri Pirko if (err) { 520211ce2ba3SJiri Pirko netdev_err(rocker_port->dev, "install untagged VLAN failed\n"); 520311ce2ba3SJiri Pirko goto err_untagged_vlan; 520411ce2ba3SJiri Pirko } 520511ce2ba3SJiri Pirko 520611ce2ba3SJiri Pirko return 0; 520711ce2ba3SJiri Pirko 520811ce2ba3SJiri Pirko err_untagged_vlan: 520911ce2ba3SJiri Pirko rocker_port_ig_tbl(rocker_port, NULL, ROCKER_OP_FLAG_REMOVE); 521011ce2ba3SJiri Pirko err_port_ig_tbl: 5211e420114eSJiri Pirko rocker_world_port_fini(rocker_port); 5212e420114eSJiri Pirko err_world_port_init: 521311ce2ba3SJiri Pirko rocker->ports[port_number] = NULL; 521411ce2ba3SJiri Pirko unregister_netdev(dev); 521511ce2ba3SJiri Pirko err_register_netdev: 5216e420114eSJiri Pirko rocker_world_port_post_fini(rocker_port); 5217e420114eSJiri Pirko err_world_port_pre_init: 5218e420114eSJiri Pirko err_world_check_init: 521911ce2ba3SJiri Pirko free_netdev(dev); 522011ce2ba3SJiri Pirko return err; 522111ce2ba3SJiri Pirko } 522211ce2ba3SJiri Pirko 522311ce2ba3SJiri Pirko static int rocker_probe_ports(struct rocker *rocker) 522411ce2ba3SJiri Pirko { 522511ce2ba3SJiri Pirko int i; 522611ce2ba3SJiri Pirko size_t alloc_size; 522711ce2ba3SJiri Pirko int err; 522811ce2ba3SJiri Pirko 522911ce2ba3SJiri Pirko alloc_size = sizeof(struct rocker_port *) * rocker->port_count; 523011ce2ba3SJiri Pirko rocker->ports = kzalloc(alloc_size, GFP_KERNEL); 523111ce2ba3SJiri Pirko if (!rocker->ports) 523211ce2ba3SJiri Pirko return -ENOMEM; 523311ce2ba3SJiri Pirko for (i = 0; i < rocker->port_count; i++) { 523411ce2ba3SJiri Pirko err = rocker_probe_port(rocker, i); 523511ce2ba3SJiri Pirko if (err) 523611ce2ba3SJiri Pirko goto remove_ports; 523711ce2ba3SJiri Pirko } 523811ce2ba3SJiri Pirko return 0; 523911ce2ba3SJiri Pirko 524011ce2ba3SJiri Pirko remove_ports: 524111ce2ba3SJiri Pirko rocker_remove_ports(rocker); 524211ce2ba3SJiri Pirko return err; 524311ce2ba3SJiri Pirko } 524411ce2ba3SJiri Pirko 524511ce2ba3SJiri Pirko static int rocker_msix_init(struct rocker *rocker) 524611ce2ba3SJiri Pirko { 524711ce2ba3SJiri Pirko struct pci_dev *pdev = rocker->pdev; 524811ce2ba3SJiri Pirko int msix_entries; 524911ce2ba3SJiri Pirko int i; 525011ce2ba3SJiri Pirko int err; 525111ce2ba3SJiri Pirko 525211ce2ba3SJiri Pirko msix_entries = pci_msix_vec_count(pdev); 525311ce2ba3SJiri Pirko if (msix_entries < 0) 525411ce2ba3SJiri Pirko return msix_entries; 525511ce2ba3SJiri Pirko 525611ce2ba3SJiri Pirko if (msix_entries != ROCKER_MSIX_VEC_COUNT(rocker->port_count)) 525711ce2ba3SJiri Pirko return -EINVAL; 525811ce2ba3SJiri Pirko 525911ce2ba3SJiri Pirko rocker->msix_entries = kmalloc_array(msix_entries, 526011ce2ba3SJiri Pirko sizeof(struct msix_entry), 526111ce2ba3SJiri Pirko GFP_KERNEL); 526211ce2ba3SJiri Pirko if (!rocker->msix_entries) 526311ce2ba3SJiri Pirko return -ENOMEM; 526411ce2ba3SJiri Pirko 526511ce2ba3SJiri Pirko for (i = 0; i < msix_entries; i++) 526611ce2ba3SJiri Pirko rocker->msix_entries[i].entry = i; 526711ce2ba3SJiri Pirko 526811ce2ba3SJiri Pirko err = pci_enable_msix_exact(pdev, rocker->msix_entries, msix_entries); 526911ce2ba3SJiri Pirko if (err < 0) 527011ce2ba3SJiri Pirko goto err_enable_msix; 527111ce2ba3SJiri Pirko 527211ce2ba3SJiri Pirko return 0; 527311ce2ba3SJiri Pirko 527411ce2ba3SJiri Pirko err_enable_msix: 527511ce2ba3SJiri Pirko kfree(rocker->msix_entries); 527611ce2ba3SJiri Pirko return err; 527711ce2ba3SJiri Pirko } 527811ce2ba3SJiri Pirko 527911ce2ba3SJiri Pirko static void rocker_msix_fini(const struct rocker *rocker) 528011ce2ba3SJiri Pirko { 528111ce2ba3SJiri Pirko pci_disable_msix(rocker->pdev); 528211ce2ba3SJiri Pirko kfree(rocker->msix_entries); 528311ce2ba3SJiri Pirko } 528411ce2ba3SJiri Pirko 528511ce2ba3SJiri Pirko static int rocker_probe(struct pci_dev *pdev, const struct pci_device_id *id) 528611ce2ba3SJiri Pirko { 528711ce2ba3SJiri Pirko struct rocker *rocker; 528811ce2ba3SJiri Pirko int err; 528911ce2ba3SJiri Pirko 529011ce2ba3SJiri Pirko rocker = kzalloc(sizeof(*rocker), GFP_KERNEL); 529111ce2ba3SJiri Pirko if (!rocker) 529211ce2ba3SJiri Pirko return -ENOMEM; 529311ce2ba3SJiri Pirko 529411ce2ba3SJiri Pirko err = pci_enable_device(pdev); 529511ce2ba3SJiri Pirko if (err) { 529611ce2ba3SJiri Pirko dev_err(&pdev->dev, "pci_enable_device failed\n"); 529711ce2ba3SJiri Pirko goto err_pci_enable_device; 529811ce2ba3SJiri Pirko } 529911ce2ba3SJiri Pirko 530011ce2ba3SJiri Pirko err = pci_request_regions(pdev, rocker_driver_name); 530111ce2ba3SJiri Pirko if (err) { 530211ce2ba3SJiri Pirko dev_err(&pdev->dev, "pci_request_regions failed\n"); 530311ce2ba3SJiri Pirko goto err_pci_request_regions; 530411ce2ba3SJiri Pirko } 530511ce2ba3SJiri Pirko 530611ce2ba3SJiri Pirko err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); 530711ce2ba3SJiri Pirko if (!err) { 530811ce2ba3SJiri Pirko err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); 530911ce2ba3SJiri Pirko if (err) { 531011ce2ba3SJiri Pirko dev_err(&pdev->dev, "pci_set_consistent_dma_mask failed\n"); 531111ce2ba3SJiri Pirko goto err_pci_set_dma_mask; 531211ce2ba3SJiri Pirko } 531311ce2ba3SJiri Pirko } else { 531411ce2ba3SJiri Pirko err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); 531511ce2ba3SJiri Pirko if (err) { 531611ce2ba3SJiri Pirko dev_err(&pdev->dev, "pci_set_dma_mask failed\n"); 531711ce2ba3SJiri Pirko goto err_pci_set_dma_mask; 531811ce2ba3SJiri Pirko } 531911ce2ba3SJiri Pirko } 532011ce2ba3SJiri Pirko 532111ce2ba3SJiri Pirko if (pci_resource_len(pdev, 0) < ROCKER_PCI_BAR0_SIZE) { 532211ce2ba3SJiri Pirko dev_err(&pdev->dev, "invalid PCI region size\n"); 532311ce2ba3SJiri Pirko err = -EINVAL; 532411ce2ba3SJiri Pirko goto err_pci_resource_len_check; 532511ce2ba3SJiri Pirko } 532611ce2ba3SJiri Pirko 532711ce2ba3SJiri Pirko rocker->hw_addr = ioremap(pci_resource_start(pdev, 0), 532811ce2ba3SJiri Pirko pci_resource_len(pdev, 0)); 532911ce2ba3SJiri Pirko if (!rocker->hw_addr) { 533011ce2ba3SJiri Pirko dev_err(&pdev->dev, "ioremap failed\n"); 533111ce2ba3SJiri Pirko err = -EIO; 533211ce2ba3SJiri Pirko goto err_ioremap; 533311ce2ba3SJiri Pirko } 533411ce2ba3SJiri Pirko pci_set_master(pdev); 533511ce2ba3SJiri Pirko 533611ce2ba3SJiri Pirko rocker->pdev = pdev; 533711ce2ba3SJiri Pirko pci_set_drvdata(pdev, rocker); 533811ce2ba3SJiri Pirko 533911ce2ba3SJiri Pirko rocker->port_count = rocker_read32(rocker, PORT_PHYS_COUNT); 534011ce2ba3SJiri Pirko 534111ce2ba3SJiri Pirko err = rocker_msix_init(rocker); 534211ce2ba3SJiri Pirko if (err) { 534311ce2ba3SJiri Pirko dev_err(&pdev->dev, "MSI-X init failed\n"); 534411ce2ba3SJiri Pirko goto err_msix_init; 534511ce2ba3SJiri Pirko } 534611ce2ba3SJiri Pirko 534711ce2ba3SJiri Pirko err = rocker_basic_hw_test(rocker); 534811ce2ba3SJiri Pirko if (err) { 534911ce2ba3SJiri Pirko dev_err(&pdev->dev, "basic hw test failed\n"); 535011ce2ba3SJiri Pirko goto err_basic_hw_test; 535111ce2ba3SJiri Pirko } 535211ce2ba3SJiri Pirko 535311ce2ba3SJiri Pirko rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET); 535411ce2ba3SJiri Pirko 535511ce2ba3SJiri Pirko err = rocker_dma_rings_init(rocker); 535611ce2ba3SJiri Pirko if (err) 535711ce2ba3SJiri Pirko goto err_dma_rings_init; 535811ce2ba3SJiri Pirko 535911ce2ba3SJiri Pirko err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), 536011ce2ba3SJiri Pirko rocker_cmd_irq_handler, 0, 536111ce2ba3SJiri Pirko rocker_driver_name, rocker); 536211ce2ba3SJiri Pirko if (err) { 536311ce2ba3SJiri Pirko dev_err(&pdev->dev, "cannot assign cmd irq\n"); 536411ce2ba3SJiri Pirko goto err_request_cmd_irq; 536511ce2ba3SJiri Pirko } 536611ce2ba3SJiri Pirko 536711ce2ba3SJiri Pirko err = request_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), 536811ce2ba3SJiri Pirko rocker_event_irq_handler, 0, 536911ce2ba3SJiri Pirko rocker_driver_name, rocker); 537011ce2ba3SJiri Pirko if (err) { 537111ce2ba3SJiri Pirko dev_err(&pdev->dev, "cannot assign event irq\n"); 537211ce2ba3SJiri Pirko goto err_request_event_irq; 537311ce2ba3SJiri Pirko } 537411ce2ba3SJiri Pirko 537511ce2ba3SJiri Pirko rocker->hw.id = rocker_read64(rocker, SWITCH_ID); 537611ce2ba3SJiri Pirko 537711ce2ba3SJiri Pirko err = rocker_init_tbls(rocker); 537811ce2ba3SJiri Pirko if (err) { 537911ce2ba3SJiri Pirko dev_err(&pdev->dev, "cannot init rocker tables\n"); 538011ce2ba3SJiri Pirko goto err_init_tbls; 538111ce2ba3SJiri Pirko } 538211ce2ba3SJiri Pirko 538311ce2ba3SJiri Pirko setup_timer(&rocker->fdb_cleanup_timer, rocker_fdb_cleanup, 538411ce2ba3SJiri Pirko (unsigned long) rocker); 538511ce2ba3SJiri Pirko mod_timer(&rocker->fdb_cleanup_timer, jiffies); 538611ce2ba3SJiri Pirko 538711ce2ba3SJiri Pirko err = rocker_probe_ports(rocker); 538811ce2ba3SJiri Pirko if (err) { 538911ce2ba3SJiri Pirko dev_err(&pdev->dev, "failed to probe ports\n"); 539011ce2ba3SJiri Pirko goto err_probe_ports; 539111ce2ba3SJiri Pirko } 539211ce2ba3SJiri Pirko 539311ce2ba3SJiri Pirko dev_info(&pdev->dev, "Rocker switch with id %*phN\n", 539411ce2ba3SJiri Pirko (int)sizeof(rocker->hw.id), &rocker->hw.id); 539511ce2ba3SJiri Pirko 539611ce2ba3SJiri Pirko return 0; 539711ce2ba3SJiri Pirko 539811ce2ba3SJiri Pirko err_probe_ports: 539911ce2ba3SJiri Pirko del_timer_sync(&rocker->fdb_cleanup_timer); 540011ce2ba3SJiri Pirko rocker_free_tbls(rocker); 540111ce2ba3SJiri Pirko err_init_tbls: 540211ce2ba3SJiri Pirko free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker); 540311ce2ba3SJiri Pirko err_request_event_irq: 540411ce2ba3SJiri Pirko free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), rocker); 540511ce2ba3SJiri Pirko err_request_cmd_irq: 540611ce2ba3SJiri Pirko rocker_dma_rings_fini(rocker); 540711ce2ba3SJiri Pirko err_dma_rings_init: 540811ce2ba3SJiri Pirko err_basic_hw_test: 540911ce2ba3SJiri Pirko rocker_msix_fini(rocker); 541011ce2ba3SJiri Pirko err_msix_init: 541111ce2ba3SJiri Pirko iounmap(rocker->hw_addr); 541211ce2ba3SJiri Pirko err_ioremap: 541311ce2ba3SJiri Pirko err_pci_resource_len_check: 541411ce2ba3SJiri Pirko err_pci_set_dma_mask: 541511ce2ba3SJiri Pirko pci_release_regions(pdev); 541611ce2ba3SJiri Pirko err_pci_request_regions: 541711ce2ba3SJiri Pirko pci_disable_device(pdev); 541811ce2ba3SJiri Pirko err_pci_enable_device: 541911ce2ba3SJiri Pirko kfree(rocker); 542011ce2ba3SJiri Pirko return err; 542111ce2ba3SJiri Pirko } 542211ce2ba3SJiri Pirko 542311ce2ba3SJiri Pirko static void rocker_remove(struct pci_dev *pdev) 542411ce2ba3SJiri Pirko { 542511ce2ba3SJiri Pirko struct rocker *rocker = pci_get_drvdata(pdev); 542611ce2ba3SJiri Pirko 542711ce2ba3SJiri Pirko del_timer_sync(&rocker->fdb_cleanup_timer); 542811ce2ba3SJiri Pirko rocker_free_tbls(rocker); 542911ce2ba3SJiri Pirko rocker_write32(rocker, CONTROL, ROCKER_CONTROL_RESET); 543011ce2ba3SJiri Pirko rocker_remove_ports(rocker); 543111ce2ba3SJiri Pirko free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_EVENT), rocker); 543211ce2ba3SJiri Pirko free_irq(rocker_msix_vector(rocker, ROCKER_MSIX_VEC_CMD), rocker); 543311ce2ba3SJiri Pirko rocker_dma_rings_fini(rocker); 543411ce2ba3SJiri Pirko rocker_msix_fini(rocker); 543511ce2ba3SJiri Pirko iounmap(rocker->hw_addr); 543611ce2ba3SJiri Pirko pci_release_regions(rocker->pdev); 543711ce2ba3SJiri Pirko pci_disable_device(rocker->pdev); 543811ce2ba3SJiri Pirko kfree(rocker); 543911ce2ba3SJiri Pirko } 544011ce2ba3SJiri Pirko 544111ce2ba3SJiri Pirko static struct pci_driver rocker_pci_driver = { 544211ce2ba3SJiri Pirko .name = rocker_driver_name, 544311ce2ba3SJiri Pirko .id_table = rocker_pci_id_table, 544411ce2ba3SJiri Pirko .probe = rocker_probe, 544511ce2ba3SJiri Pirko .remove = rocker_remove, 544611ce2ba3SJiri Pirko }; 544711ce2ba3SJiri Pirko 544811ce2ba3SJiri Pirko /************************************ 544911ce2ba3SJiri Pirko * Net device notifier event handler 545011ce2ba3SJiri Pirko ************************************/ 545111ce2ba3SJiri Pirko 545211ce2ba3SJiri Pirko static bool rocker_port_dev_check(const struct net_device *dev) 545311ce2ba3SJiri Pirko { 545411ce2ba3SJiri Pirko return dev->netdev_ops == &rocker_port_netdev_ops; 545511ce2ba3SJiri Pirko } 545611ce2ba3SJiri Pirko 545711ce2ba3SJiri Pirko static int rocker_port_bridge_join(struct rocker_port *rocker_port, 545811ce2ba3SJiri Pirko struct net_device *bridge) 545911ce2ba3SJiri Pirko { 546011ce2ba3SJiri Pirko u16 untagged_vid = 0; 546111ce2ba3SJiri Pirko int err; 546211ce2ba3SJiri Pirko 546311ce2ba3SJiri Pirko /* Port is joining bridge, so the internal VLAN for the 546411ce2ba3SJiri Pirko * port is going to change to the bridge internal VLAN. 546511ce2ba3SJiri Pirko * Let's remove untagged VLAN (vid=0) from port and 546611ce2ba3SJiri Pirko * re-add once internal VLAN has changed. 546711ce2ba3SJiri Pirko */ 546811ce2ba3SJiri Pirko 546911ce2ba3SJiri Pirko err = rocker_port_vlan_del(rocker_port, untagged_vid, 0); 547011ce2ba3SJiri Pirko if (err) 547111ce2ba3SJiri Pirko return err; 547211ce2ba3SJiri Pirko 547311ce2ba3SJiri Pirko rocker_port_internal_vlan_id_put(rocker_port, 547411ce2ba3SJiri Pirko rocker_port->dev->ifindex); 547511ce2ba3SJiri Pirko rocker_port->internal_vlan_id = 547611ce2ba3SJiri Pirko rocker_port_internal_vlan_id_get(rocker_port, bridge->ifindex); 547711ce2ba3SJiri Pirko 547811ce2ba3SJiri Pirko rocker_port->bridge_dev = bridge; 547911ce2ba3SJiri Pirko switchdev_port_fwd_mark_set(rocker_port->dev, bridge, true); 548011ce2ba3SJiri Pirko 548111ce2ba3SJiri Pirko return rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0); 548211ce2ba3SJiri Pirko } 548311ce2ba3SJiri Pirko 548411ce2ba3SJiri Pirko static int rocker_port_bridge_leave(struct rocker_port *rocker_port) 548511ce2ba3SJiri Pirko { 548611ce2ba3SJiri Pirko u16 untagged_vid = 0; 548711ce2ba3SJiri Pirko int err; 548811ce2ba3SJiri Pirko 548911ce2ba3SJiri Pirko err = rocker_port_vlan_del(rocker_port, untagged_vid, 0); 549011ce2ba3SJiri Pirko if (err) 549111ce2ba3SJiri Pirko return err; 549211ce2ba3SJiri Pirko 549311ce2ba3SJiri Pirko rocker_port_internal_vlan_id_put(rocker_port, 549411ce2ba3SJiri Pirko rocker_port->bridge_dev->ifindex); 549511ce2ba3SJiri Pirko rocker_port->internal_vlan_id = 549611ce2ba3SJiri Pirko rocker_port_internal_vlan_id_get(rocker_port, 549711ce2ba3SJiri Pirko rocker_port->dev->ifindex); 549811ce2ba3SJiri Pirko 549911ce2ba3SJiri Pirko switchdev_port_fwd_mark_set(rocker_port->dev, rocker_port->bridge_dev, 550011ce2ba3SJiri Pirko false); 550111ce2ba3SJiri Pirko rocker_port->bridge_dev = NULL; 550211ce2ba3SJiri Pirko 550311ce2ba3SJiri Pirko err = rocker_port_vlan_add(rocker_port, NULL, untagged_vid, 0); 550411ce2ba3SJiri Pirko if (err) 550511ce2ba3SJiri Pirko return err; 550611ce2ba3SJiri Pirko 550711ce2ba3SJiri Pirko if (rocker_port->dev->flags & IFF_UP) 550811ce2ba3SJiri Pirko err = rocker_port_fwd_enable(rocker_port, NULL, 0); 550911ce2ba3SJiri Pirko 551011ce2ba3SJiri Pirko return err; 551111ce2ba3SJiri Pirko } 551211ce2ba3SJiri Pirko 551311ce2ba3SJiri Pirko static int rocker_port_ovs_changed(struct rocker_port *rocker_port, 551411ce2ba3SJiri Pirko struct net_device *master) 551511ce2ba3SJiri Pirko { 551611ce2ba3SJiri Pirko int err; 551711ce2ba3SJiri Pirko 551811ce2ba3SJiri Pirko rocker_port->bridge_dev = master; 551911ce2ba3SJiri Pirko 552011ce2ba3SJiri Pirko err = rocker_port_fwd_disable(rocker_port, NULL, 0); 552111ce2ba3SJiri Pirko if (err) 552211ce2ba3SJiri Pirko return err; 552311ce2ba3SJiri Pirko err = rocker_port_fwd_enable(rocker_port, NULL, 0); 552411ce2ba3SJiri Pirko 552511ce2ba3SJiri Pirko return err; 552611ce2ba3SJiri Pirko } 552711ce2ba3SJiri Pirko 552811ce2ba3SJiri Pirko static int rocker_port_master_linked(struct rocker_port *rocker_port, 552911ce2ba3SJiri Pirko struct net_device *master) 553011ce2ba3SJiri Pirko { 553111ce2ba3SJiri Pirko int err = 0; 553211ce2ba3SJiri Pirko 553311ce2ba3SJiri Pirko if (netif_is_bridge_master(master)) 553411ce2ba3SJiri Pirko err = rocker_port_bridge_join(rocker_port, master); 553511ce2ba3SJiri Pirko else if (netif_is_ovs_master(master)) 553611ce2ba3SJiri Pirko err = rocker_port_ovs_changed(rocker_port, master); 553711ce2ba3SJiri Pirko return err; 553811ce2ba3SJiri Pirko } 553911ce2ba3SJiri Pirko 554011ce2ba3SJiri Pirko static int rocker_port_master_unlinked(struct rocker_port *rocker_port) 554111ce2ba3SJiri Pirko { 554211ce2ba3SJiri Pirko int err = 0; 554311ce2ba3SJiri Pirko 554411ce2ba3SJiri Pirko if (rocker_port_is_bridged(rocker_port)) 554511ce2ba3SJiri Pirko err = rocker_port_bridge_leave(rocker_port); 554611ce2ba3SJiri Pirko else if (rocker_port_is_ovsed(rocker_port)) 554711ce2ba3SJiri Pirko err = rocker_port_ovs_changed(rocker_port, NULL); 554811ce2ba3SJiri Pirko return err; 554911ce2ba3SJiri Pirko } 555011ce2ba3SJiri Pirko 555111ce2ba3SJiri Pirko static int rocker_netdevice_event(struct notifier_block *unused, 555211ce2ba3SJiri Pirko unsigned long event, void *ptr) 555311ce2ba3SJiri Pirko { 555411ce2ba3SJiri Pirko struct net_device *dev = netdev_notifier_info_to_dev(ptr); 555511ce2ba3SJiri Pirko struct netdev_notifier_changeupper_info *info; 555611ce2ba3SJiri Pirko struct rocker_port *rocker_port; 555711ce2ba3SJiri Pirko int err; 555811ce2ba3SJiri Pirko 555911ce2ba3SJiri Pirko if (!rocker_port_dev_check(dev)) 556011ce2ba3SJiri Pirko return NOTIFY_DONE; 556111ce2ba3SJiri Pirko 556211ce2ba3SJiri Pirko switch (event) { 556311ce2ba3SJiri Pirko case NETDEV_CHANGEUPPER: 556411ce2ba3SJiri Pirko info = ptr; 556511ce2ba3SJiri Pirko if (!info->master) 556611ce2ba3SJiri Pirko goto out; 556711ce2ba3SJiri Pirko rocker_port = netdev_priv(dev); 556811ce2ba3SJiri Pirko if (info->linking) { 5569e420114eSJiri Pirko err = rocker_world_port_master_linked(rocker_port, 5570e420114eSJiri Pirko info->upper_dev); 5571e420114eSJiri Pirko if (err) 5572e420114eSJiri Pirko netdev_warn(dev, "failed to reflect master linked (err %d)\n", 5573e420114eSJiri Pirko err); 557411ce2ba3SJiri Pirko err = rocker_port_master_linked(rocker_port, 557511ce2ba3SJiri Pirko info->upper_dev); 557611ce2ba3SJiri Pirko if (err) 557711ce2ba3SJiri Pirko netdev_warn(dev, "failed to reflect master linked (err %d)\n", 557811ce2ba3SJiri Pirko err); 557911ce2ba3SJiri Pirko } else { 5580e420114eSJiri Pirko err = rocker_world_port_master_unlinked(rocker_port, 5581e420114eSJiri Pirko info->upper_dev); 5582e420114eSJiri Pirko if (err) 5583e420114eSJiri Pirko netdev_warn(dev, "failed to reflect master unlinked (err %d)\n", 5584e420114eSJiri Pirko err); 558511ce2ba3SJiri Pirko err = rocker_port_master_unlinked(rocker_port); 558611ce2ba3SJiri Pirko if (err) 558711ce2ba3SJiri Pirko netdev_warn(dev, "failed to reflect master unlinked (err %d)\n", 558811ce2ba3SJiri Pirko err); 558911ce2ba3SJiri Pirko } 559011ce2ba3SJiri Pirko break; 559111ce2ba3SJiri Pirko } 559211ce2ba3SJiri Pirko out: 559311ce2ba3SJiri Pirko return NOTIFY_DONE; 559411ce2ba3SJiri Pirko } 559511ce2ba3SJiri Pirko 559611ce2ba3SJiri Pirko static struct notifier_block rocker_netdevice_nb __read_mostly = { 559711ce2ba3SJiri Pirko .notifier_call = rocker_netdevice_event, 559811ce2ba3SJiri Pirko }; 559911ce2ba3SJiri Pirko 560011ce2ba3SJiri Pirko /************************************ 560111ce2ba3SJiri Pirko * Net event notifier event handler 560211ce2ba3SJiri Pirko ************************************/ 560311ce2ba3SJiri Pirko 560411ce2ba3SJiri Pirko static int rocker_neigh_update(struct net_device *dev, struct neighbour *n) 560511ce2ba3SJiri Pirko { 560611ce2ba3SJiri Pirko struct rocker_port *rocker_port = netdev_priv(dev); 560711ce2ba3SJiri Pirko int flags = (n->nud_state & NUD_VALID ? 0 : ROCKER_OP_FLAG_REMOVE) | 560811ce2ba3SJiri Pirko ROCKER_OP_FLAG_NOWAIT; 560911ce2ba3SJiri Pirko __be32 ip_addr = *(__be32 *)n->primary_key; 561011ce2ba3SJiri Pirko 561111ce2ba3SJiri Pirko return rocker_port_ipv4_neigh(rocker_port, NULL, flags, ip_addr, n->ha); 561211ce2ba3SJiri Pirko } 561311ce2ba3SJiri Pirko 561411ce2ba3SJiri Pirko static int rocker_netevent_event(struct notifier_block *unused, 561511ce2ba3SJiri Pirko unsigned long event, void *ptr) 561611ce2ba3SJiri Pirko { 5617e420114eSJiri Pirko struct rocker_port *rocker_port; 561811ce2ba3SJiri Pirko struct net_device *dev; 561911ce2ba3SJiri Pirko struct neighbour *n = ptr; 562011ce2ba3SJiri Pirko int err; 562111ce2ba3SJiri Pirko 562211ce2ba3SJiri Pirko switch (event) { 562311ce2ba3SJiri Pirko case NETEVENT_NEIGH_UPDATE: 562411ce2ba3SJiri Pirko if (n->tbl != &arp_tbl) 562511ce2ba3SJiri Pirko return NOTIFY_DONE; 562611ce2ba3SJiri Pirko dev = n->dev; 562711ce2ba3SJiri Pirko if (!rocker_port_dev_check(dev)) 562811ce2ba3SJiri Pirko return NOTIFY_DONE; 5629e420114eSJiri Pirko rocker_port = netdev_priv(dev); 5630e420114eSJiri Pirko err = rocker_world_port_neigh_update(rocker_port, n); 5631e420114eSJiri Pirko if (err) 5632e420114eSJiri Pirko netdev_warn(dev, "failed to handle neigh update (err %d)\n", 5633e420114eSJiri Pirko err); 563411ce2ba3SJiri Pirko err = rocker_neigh_update(dev, n); 563511ce2ba3SJiri Pirko if (err) 563611ce2ba3SJiri Pirko netdev_warn(dev, 563711ce2ba3SJiri Pirko "failed to handle neigh update (err %d)\n", 563811ce2ba3SJiri Pirko err); 563911ce2ba3SJiri Pirko break; 564011ce2ba3SJiri Pirko } 564111ce2ba3SJiri Pirko 564211ce2ba3SJiri Pirko return NOTIFY_DONE; 564311ce2ba3SJiri Pirko } 564411ce2ba3SJiri Pirko 564511ce2ba3SJiri Pirko static struct notifier_block rocker_netevent_nb __read_mostly = { 564611ce2ba3SJiri Pirko .notifier_call = rocker_netevent_event, 564711ce2ba3SJiri Pirko }; 564811ce2ba3SJiri Pirko 564911ce2ba3SJiri Pirko /*********************** 565011ce2ba3SJiri Pirko * Module init and exit 565111ce2ba3SJiri Pirko ***********************/ 565211ce2ba3SJiri Pirko 565311ce2ba3SJiri Pirko static int __init rocker_module_init(void) 565411ce2ba3SJiri Pirko { 565511ce2ba3SJiri Pirko int err; 565611ce2ba3SJiri Pirko 565711ce2ba3SJiri Pirko register_netdevice_notifier(&rocker_netdevice_nb); 565811ce2ba3SJiri Pirko register_netevent_notifier(&rocker_netevent_nb); 565911ce2ba3SJiri Pirko err = pci_register_driver(&rocker_pci_driver); 566011ce2ba3SJiri Pirko if (err) 566111ce2ba3SJiri Pirko goto err_pci_register_driver; 566211ce2ba3SJiri Pirko return 0; 566311ce2ba3SJiri Pirko 566411ce2ba3SJiri Pirko err_pci_register_driver: 566511ce2ba3SJiri Pirko unregister_netevent_notifier(&rocker_netevent_nb); 566611ce2ba3SJiri Pirko unregister_netdevice_notifier(&rocker_netdevice_nb); 566711ce2ba3SJiri Pirko return err; 566811ce2ba3SJiri Pirko } 566911ce2ba3SJiri Pirko 567011ce2ba3SJiri Pirko static void __exit rocker_module_exit(void) 567111ce2ba3SJiri Pirko { 567211ce2ba3SJiri Pirko unregister_netevent_notifier(&rocker_netevent_nb); 567311ce2ba3SJiri Pirko unregister_netdevice_notifier(&rocker_netdevice_nb); 567411ce2ba3SJiri Pirko pci_unregister_driver(&rocker_pci_driver); 567511ce2ba3SJiri Pirko } 567611ce2ba3SJiri Pirko 567711ce2ba3SJiri Pirko module_init(rocker_module_init); 567811ce2ba3SJiri Pirko module_exit(rocker_module_exit); 567911ce2ba3SJiri Pirko 568011ce2ba3SJiri Pirko MODULE_LICENSE("GPL v2"); 568111ce2ba3SJiri Pirko MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>"); 568211ce2ba3SJiri Pirko MODULE_AUTHOR("Scott Feldman <sfeldma@gmail.com>"); 568311ce2ba3SJiri Pirko MODULE_DESCRIPTION("Rocker switch device driver"); 568411ce2ba3SJiri Pirko MODULE_DEVICE_TABLE(pci, rocker_pci_id_table); 5685