599 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			599 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
 | |
| /* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */
 | |
| 
 | |
| #include <net/devlink.h>
 | |
| 
 | |
| #include "prestera_devlink.h"
 | |
| #include "prestera_hw.h"
 | |
| 
 | |
| /* All driver-specific traps must be documented in
 | |
|  * Documentation/networking/devlink/prestera.rst
 | |
|  */
 | |
| enum {
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_ARP_BC,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_IS_IS,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_OSPF,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_IP_BC_MAC,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_ROUTER_MC,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_VRRP,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_DHCP,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_MAC_TO_ME,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_IPV4_OPTIONS,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_IP_DEFAULT_ROUTE,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_IP_TO_ME,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_IPV4_ICMP_REDIRECT,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_0,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_1,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_2,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_3,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_4,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_5,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_6,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_ACL_CODE_7,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_BGP,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_SSH,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_TELNET,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_ICMP,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_MET_RED,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_IP_SIP_IS_ZERO,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_IP_UC_DIP_DA_MISMATCH,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_ILLEGAL_IPV4_HDR,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_ILLEGAL_IP_ADDR,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_INVALID_SA,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_LOCAL_PORT,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_PORT_NO_VLAN,
 | |
| 	DEVLINK_PRESTERA_TRAP_ID_RXDMA_DROP,
 | |
| };
 | |
| 
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_ARP_BC \
 | |
| 	"arp_bc"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_IS_IS \
 | |
| 	"is_is"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_OSPF \
 | |
| 	"ospf"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_IP_BC_MAC \
 | |
| 	"ip_bc_mac"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_ROUTER_MC \
 | |
| 	"router_mc"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_VRRP \
 | |
| 	"vrrp"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_DHCP \
 | |
| 	"dhcp"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_MAC_TO_ME \
 | |
| 	"mac_to_me"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_IPV4_OPTIONS \
 | |
| 	"ipv4_options"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_IP_DEFAULT_ROUTE \
 | |
| 	"ip_default_route"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_IP_TO_ME \
 | |
| 	"ip_to_me"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_IPV4_ICMP_REDIRECT \
 | |
| 	"ipv4_icmp_redirect"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_0 \
 | |
| 	"acl_code_0"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_1 \
 | |
| 	"acl_code_1"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_2 \
 | |
| 	"acl_code_2"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_3 \
 | |
| 	"acl_code_3"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_4 \
 | |
| 	"acl_code_4"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_5 \
 | |
| 	"acl_code_5"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_6 \
 | |
| 	"acl_code_6"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_ACL_CODE_7 \
 | |
| 	"acl_code_7"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_BGP \
 | |
| 	"bgp"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_SSH \
 | |
| 	"ssh"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_TELNET \
 | |
| 	"telnet"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_ICMP \
 | |
| 	"icmp"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_RXDMA_DROP \
 | |
| 	"rxdma_drop"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_PORT_NO_VLAN \
 | |
| 	"port_no_vlan"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_LOCAL_PORT \
 | |
| 	"local_port"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_INVALID_SA \
 | |
| 	"invalid_sa"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_ILLEGAL_IP_ADDR \
 | |
| 	"illegal_ip_addr"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_ILLEGAL_IPV4_HDR \
 | |
| 	"illegal_ipv4_hdr"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_IP_UC_DIP_DA_MISMATCH \
 | |
| 	"ip_uc_dip_da_mismatch"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_IP_SIP_IS_ZERO \
 | |
| 	"ip_sip_is_zero"
 | |
| #define DEVLINK_PRESTERA_TRAP_NAME_MET_RED \
 | |
| 	"met_red"
 | |
| 
 | |
| struct prestera_trap {
 | |
| 	struct devlink_trap trap;
 | |
| 	u8 cpu_code;
 | |
| };
 | |
| 
 | |
| struct prestera_trap_item {
 | |
| 	enum devlink_trap_action action;
 | |
| 	void *trap_ctx;
 | |
| };
 | |
| 
 | |
| struct prestera_trap_data {
 | |
| 	struct prestera_switch *sw;
 | |
| 	struct prestera_trap_item *trap_items_arr;
 | |
| 	u32 traps_count;
 | |
| };
 | |
| 
 | |
| #define PRESTERA_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT
 | |
| 
 | |
| #define PRESTERA_TRAP_CONTROL(_id, _group_id, _action)			      \
 | |
| 	DEVLINK_TRAP_GENERIC(CONTROL, _action, _id,			      \
 | |
| 			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
 | |
| 			     PRESTERA_TRAP_METADATA)
 | |
| 
 | |
| #define PRESTERA_TRAP_DRIVER_CONTROL(_id, _group_id)			      \
 | |
| 	DEVLINK_TRAP_DRIVER(CONTROL, TRAP, DEVLINK_PRESTERA_TRAP_ID_##_id,    \
 | |
| 			    DEVLINK_PRESTERA_TRAP_NAME_##_id,		      \
 | |
| 			    DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
 | |
| 			    PRESTERA_TRAP_METADATA)
 | |
| 
 | |
| #define PRESTERA_TRAP_EXCEPTION(_id, _group_id)				      \
 | |
| 	DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id,			      \
 | |
| 			     DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
 | |
| 			     PRESTERA_TRAP_METADATA)
 | |
| 
 | |
| #define PRESTERA_TRAP_DRIVER_EXCEPTION(_id, _group_id)			      \
 | |
| 	DEVLINK_TRAP_DRIVER(EXCEPTION, TRAP, DEVLINK_PRESTERA_TRAP_ID_##_id,  \
 | |
| 			    DEVLINK_PRESTERA_TRAP_NAME_##_id,		      \
 | |
| 			    DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
 | |
| 			    PRESTERA_TRAP_METADATA)
 | |
| 
 | |
| #define PRESTERA_TRAP_DRIVER_DROP(_id, _group_id)			      \
 | |
| 	DEVLINK_TRAP_DRIVER(DROP, DROP, DEVLINK_PRESTERA_TRAP_ID_##_id,	      \
 | |
| 			    DEVLINK_PRESTERA_TRAP_NAME_##_id,		      \
 | |
| 			    DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id,	      \
 | |
| 			    PRESTERA_TRAP_METADATA)
 | |
| 
 | |
| static const struct devlink_trap_group prestera_trap_groups_arr[] = {
 | |
| 	/* No policer is associated with following groups (policerid == 0)*/
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(L3_EXCEPTIONS, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(NEIGH_DISCOVERY, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(ACL_TRAP, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(ACL_SAMPLE, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(OSPF, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(STP, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(LACP, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(LLDP, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(VRRP, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(DHCP, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(BGP, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(LOCAL_DELIVERY, 0),
 | |
| 	DEVLINK_TRAP_GROUP_GENERIC(BUFFER_DROPS, 0),
 | |
| };
 | |
| 
 | |
| /* Initialize trap list, as well as associate CPU code with them. */
 | |
| static struct prestera_trap prestera_trap_items_arr[] = {
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(ARP_BC, NEIGH_DISCOVERY),
 | |
| 		.cpu_code = 5,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(IS_IS, LOCAL_DELIVERY),
 | |
| 		.cpu_code = 13,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(OSPF, OSPF),
 | |
| 		.cpu_code = 16,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(IP_BC_MAC, LOCAL_DELIVERY),
 | |
| 		.cpu_code = 19,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_CONTROL(STP, STP, TRAP),
 | |
| 		.cpu_code = 26,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_CONTROL(LACP, LACP, TRAP),
 | |
| 		.cpu_code = 27,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_CONTROL(LLDP, LLDP, TRAP),
 | |
| 		.cpu_code = 28,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(ROUTER_MC, LOCAL_DELIVERY),
 | |
| 		.cpu_code = 29,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(VRRP, VRRP),
 | |
| 		.cpu_code = 30,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(DHCP, DHCP),
 | |
| 		.cpu_code = 33,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_EXCEPTION(MTU_ERROR, L3_EXCEPTIONS),
 | |
| 		.cpu_code = 63,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(MAC_TO_ME, LOCAL_DELIVERY),
 | |
| 		.cpu_code = 65,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_EXCEPTION(TTL_ERROR, L3_EXCEPTIONS),
 | |
| 		.cpu_code = 133,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_EXCEPTION(IPV4_OPTIONS,
 | |
| 						       L3_EXCEPTIONS),
 | |
| 		.cpu_code = 141,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(IP_DEFAULT_ROUTE,
 | |
| 						     LOCAL_DELIVERY),
 | |
| 		.cpu_code = 160,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_CONTROL(LOCAL_ROUTE, LOCAL_DELIVERY,
 | |
| 					      TRAP),
 | |
| 		.cpu_code = 161,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_EXCEPTION(IPV4_ICMP_REDIRECT,
 | |
| 						       L3_EXCEPTIONS),
 | |
| 		.cpu_code = 180,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_CONTROL(ARP_RESPONSE, NEIGH_DISCOVERY,
 | |
| 					      TRAP),
 | |
| 		.cpu_code = 188,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_0, ACL_TRAP),
 | |
| 		.cpu_code = 192,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_1, ACL_TRAP),
 | |
| 		.cpu_code = 193,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_2, ACL_TRAP),
 | |
| 		.cpu_code = 194,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_3, ACL_TRAP),
 | |
| 		.cpu_code = 195,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_4, ACL_TRAP),
 | |
| 		.cpu_code = 196,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_5, ACL_TRAP),
 | |
| 		.cpu_code = 197,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_6, ACL_TRAP),
 | |
| 		.cpu_code = 198,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(ACL_CODE_7, ACL_TRAP),
 | |
| 		.cpu_code = 199,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(BGP, BGP),
 | |
| 		.cpu_code = 206,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(SSH, LOCAL_DELIVERY),
 | |
| 		.cpu_code = 207,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(TELNET, LOCAL_DELIVERY),
 | |
| 		.cpu_code = 208,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_CONTROL(ICMP, LOCAL_DELIVERY),
 | |
| 		.cpu_code = 209,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_DROP(RXDMA_DROP, BUFFER_DROPS),
 | |
| 		.cpu_code = 37,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_DROP(PORT_NO_VLAN, L2_DROPS),
 | |
| 		.cpu_code = 39,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_DROP(LOCAL_PORT, L2_DROPS),
 | |
| 		.cpu_code = 56,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_DROP(INVALID_SA, L2_DROPS),
 | |
| 		.cpu_code = 60,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_DROP(ILLEGAL_IP_ADDR, L3_DROPS),
 | |
| 		.cpu_code = 136,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_DROP(ILLEGAL_IPV4_HDR, L3_DROPS),
 | |
| 		.cpu_code = 137,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_DROP(IP_UC_DIP_DA_MISMATCH,
 | |
| 						  L3_DROPS),
 | |
| 		.cpu_code = 138,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_DROP(IP_SIP_IS_ZERO, L3_DROPS),
 | |
| 		.cpu_code = 145,
 | |
| 	},
 | |
| 	{
 | |
| 		.trap = PRESTERA_TRAP_DRIVER_DROP(MET_RED, BUFFER_DROPS),
 | |
| 		.cpu_code = 185,
 | |
| 	},
 | |
| };
 | |
| 
 | |
| static int prestera_drop_counter_get(struct devlink *devlink,
 | |
| 				     const struct devlink_trap *trap,
 | |
| 				     u64 *p_drops);
 | |
| 
 | |
| static int prestera_dl_info_get(struct devlink *dl,
 | |
| 				struct devlink_info_req *req,
 | |
| 				struct netlink_ext_ack *extack)
 | |
| {
 | |
| 	struct prestera_switch *sw = devlink_priv(dl);
 | |
| 	char buf[16];
 | |
| 
 | |
| 	snprintf(buf, sizeof(buf), "%d.%d.%d",
 | |
| 		 sw->dev->fw_rev.maj,
 | |
| 		 sw->dev->fw_rev.min,
 | |
| 		 sw->dev->fw_rev.sub);
 | |
| 
 | |
| 	return devlink_info_version_running_put(req,
 | |
| 					       DEVLINK_INFO_VERSION_GENERIC_FW,
 | |
| 					       buf);
 | |
| }
 | |
| 
 | |
| static int prestera_trap_init(struct devlink *devlink,
 | |
| 			      const struct devlink_trap *trap, void *trap_ctx);
 | |
| 
 | |
| static int prestera_trap_action_set(struct devlink *devlink,
 | |
| 				    const struct devlink_trap *trap,
 | |
| 				    enum devlink_trap_action action,
 | |
| 				    struct netlink_ext_ack *extack);
 | |
| 
 | |
| static const struct devlink_ops prestera_dl_ops = {
 | |
| 	.info_get = prestera_dl_info_get,
 | |
| 	.trap_init = prestera_trap_init,
 | |
| 	.trap_action_set = prestera_trap_action_set,
 | |
| 	.trap_drop_counter_get = prestera_drop_counter_get,
 | |
| };
 | |
| 
 | |
| struct prestera_switch *prestera_devlink_alloc(struct prestera_device *dev)
 | |
| {
 | |
| 	struct devlink *dl;
 | |
| 
 | |
| 	dl = devlink_alloc(&prestera_dl_ops, sizeof(struct prestera_switch),
 | |
| 			   dev->dev);
 | |
| 
 | |
| 	return devlink_priv(dl);
 | |
| }
 | |
| 
 | |
| void prestera_devlink_free(struct prestera_switch *sw)
 | |
| {
 | |
| 	struct devlink *dl = priv_to_devlink(sw);
 | |
| 
 | |
| 	devlink_free(dl);
 | |
| }
 | |
| 
 | |
| void prestera_devlink_register(struct prestera_switch *sw)
 | |
| {
 | |
| 	struct devlink *dl = priv_to_devlink(sw);
 | |
| 
 | |
| 	devlink_register(dl);
 | |
| }
 | |
| 
 | |
| void prestera_devlink_unregister(struct prestera_switch *sw)
 | |
| {
 | |
| 	struct devlink *dl = priv_to_devlink(sw);
 | |
| 
 | |
| 	devlink_unregister(dl);
 | |
| }
 | |
| 
 | |
| int prestera_devlink_port_register(struct prestera_port *port)
 | |
| {
 | |
| 	struct prestera_switch *sw = port->sw;
 | |
| 	struct devlink *dl = priv_to_devlink(sw);
 | |
| 	struct devlink_port_attrs attrs = {};
 | |
| 	int err;
 | |
| 
 | |
| 	attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
 | |
| 	attrs.phys.port_number = port->fp_id;
 | |
| 	attrs.switch_id.id_len = sizeof(sw->id);
 | |
| 	memcpy(attrs.switch_id.id, &sw->id, attrs.switch_id.id_len);
 | |
| 
 | |
| 	devlink_port_attrs_set(&port->dl_port, &attrs);
 | |
| 
 | |
| 	err = devlink_port_register(dl, &port->dl_port, port->fp_id);
 | |
| 	if (err) {
 | |
| 		dev_err(prestera_dev(sw), "devlink_port_register failed: %d\n", err);
 | |
| 		return err;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void prestera_devlink_port_unregister(struct prestera_port *port)
 | |
| {
 | |
| 	devlink_port_unregister(&port->dl_port);
 | |
| }
 | |
| 
 | |
| int prestera_devlink_traps_register(struct prestera_switch *sw)
 | |
| {
 | |
| 	const u32 groups_count = ARRAY_SIZE(prestera_trap_groups_arr);
 | |
| 	const u32 traps_count = ARRAY_SIZE(prestera_trap_items_arr);
 | |
| 	struct devlink *devlink = priv_to_devlink(sw);
 | |
| 	struct prestera_trap_data *trap_data;
 | |
| 	struct prestera_trap *prestera_trap;
 | |
| 	int err, i;
 | |
| 
 | |
| 	trap_data = kzalloc(sizeof(*trap_data), GFP_KERNEL);
 | |
| 	if (!trap_data)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	trap_data->trap_items_arr = kcalloc(traps_count,
 | |
| 					    sizeof(struct prestera_trap_item),
 | |
| 					    GFP_KERNEL);
 | |
| 	if (!trap_data->trap_items_arr) {
 | |
| 		err = -ENOMEM;
 | |
| 		goto err_trap_items_alloc;
 | |
| 	}
 | |
| 
 | |
| 	trap_data->sw = sw;
 | |
| 	trap_data->traps_count = traps_count;
 | |
| 	sw->trap_data = trap_data;
 | |
| 
 | |
| 	err = devlink_trap_groups_register(devlink, prestera_trap_groups_arr,
 | |
| 					   groups_count);
 | |
| 	if (err)
 | |
| 		goto err_groups_register;
 | |
| 
 | |
| 	for (i = 0; i < traps_count; i++) {
 | |
| 		prestera_trap = &prestera_trap_items_arr[i];
 | |
| 		err = devlink_traps_register(devlink, &prestera_trap->trap, 1,
 | |
| 					     sw);
 | |
| 		if (err)
 | |
| 			goto err_trap_register;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| err_trap_register:
 | |
| 	for (i--; i >= 0; i--) {
 | |
| 		prestera_trap = &prestera_trap_items_arr[i];
 | |
| 		devlink_traps_unregister(devlink, &prestera_trap->trap, 1);
 | |
| 	}
 | |
| 	devlink_trap_groups_unregister(devlink, prestera_trap_groups_arr,
 | |
| 				       groups_count);
 | |
| err_groups_register:
 | |
| 	kfree(trap_data->trap_items_arr);
 | |
| err_trap_items_alloc:
 | |
| 	kfree(trap_data);
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| static struct prestera_trap_item *
 | |
| prestera_get_trap_item_by_cpu_code(struct prestera_switch *sw, u8 cpu_code)
 | |
| {
 | |
| 	struct prestera_trap_data *trap_data = sw->trap_data;
 | |
| 	struct prestera_trap *prestera_trap;
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < trap_data->traps_count; i++) {
 | |
| 		prestera_trap = &prestera_trap_items_arr[i];
 | |
| 		if (cpu_code == prestera_trap->cpu_code)
 | |
| 			return &trap_data->trap_items_arr[i];
 | |
| 	}
 | |
| 
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| void prestera_devlink_trap_report(struct prestera_port *port,
 | |
| 				  struct sk_buff *skb, u8 cpu_code)
 | |
| {
 | |
| 	struct prestera_trap_item *trap_item;
 | |
| 	struct devlink *devlink;
 | |
| 
 | |
| 	devlink = port->dl_port.devlink;
 | |
| 
 | |
| 	trap_item = prestera_get_trap_item_by_cpu_code(port->sw, cpu_code);
 | |
| 	if (unlikely(!trap_item))
 | |
| 		return;
 | |
| 
 | |
| 	devlink_trap_report(devlink, skb, trap_item->trap_ctx,
 | |
| 			    &port->dl_port, NULL);
 | |
| }
 | |
| 
 | |
| static struct prestera_trap_item *
 | |
| prestera_devlink_trap_item_lookup(struct prestera_switch *sw, u16 trap_id)
 | |
| {
 | |
| 	struct prestera_trap_data *trap_data = sw->trap_data;
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < ARRAY_SIZE(prestera_trap_items_arr); i++) {
 | |
| 		if (prestera_trap_items_arr[i].trap.id == trap_id)
 | |
| 			return &trap_data->trap_items_arr[i];
 | |
| 	}
 | |
| 
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static int prestera_trap_init(struct devlink *devlink,
 | |
| 			      const struct devlink_trap *trap, void *trap_ctx)
 | |
| {
 | |
| 	struct prestera_switch *sw = devlink_priv(devlink);
 | |
| 	struct prestera_trap_item *trap_item;
 | |
| 
 | |
| 	trap_item = prestera_devlink_trap_item_lookup(sw, trap->id);
 | |
| 	if (WARN_ON(!trap_item))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	trap_item->trap_ctx = trap_ctx;
 | |
| 	trap_item->action = trap->init_action;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int prestera_trap_action_set(struct devlink *devlink,
 | |
| 				    const struct devlink_trap *trap,
 | |
| 				    enum devlink_trap_action action,
 | |
| 				    struct netlink_ext_ack *extack)
 | |
| {
 | |
| 	/* Currently, driver does not support trap action altering */
 | |
| 	return -EOPNOTSUPP;
 | |
| }
 | |
| 
 | |
| static int prestera_drop_counter_get(struct devlink *devlink,
 | |
| 				     const struct devlink_trap *trap,
 | |
| 				     u64 *p_drops)
 | |
| {
 | |
| 	struct prestera_switch *sw = devlink_priv(devlink);
 | |
| 	enum prestera_hw_cpu_code_cnt_t cpu_code_type =
 | |
| 		PRESTERA_HW_CPU_CODE_CNT_TYPE_DROP;
 | |
| 	struct prestera_trap *prestera_trap =
 | |
| 		container_of(trap, struct prestera_trap, trap);
 | |
| 
 | |
| 	return prestera_hw_cpu_code_counters_get(sw, prestera_trap->cpu_code,
 | |
| 						 cpu_code_type, p_drops);
 | |
| }
 | |
| 
 | |
| void prestera_devlink_traps_unregister(struct prestera_switch *sw)
 | |
| {
 | |
| 	struct prestera_trap_data *trap_data = sw->trap_data;
 | |
| 	struct devlink *dl = priv_to_devlink(sw);
 | |
| 	const struct devlink_trap *trap;
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < ARRAY_SIZE(prestera_trap_items_arr); ++i) {
 | |
| 		trap = &prestera_trap_items_arr[i].trap;
 | |
| 		devlink_traps_unregister(dl, trap, 1);
 | |
| 	}
 | |
| 
 | |
| 	devlink_trap_groups_unregister(dl, prestera_trap_groups_arr,
 | |
| 				       ARRAY_SIZE(prestera_trap_groups_arr));
 | |
| 	kfree(trap_data->trap_items_arr);
 | |
| 	kfree(trap_data);
 | |
| }
 |