diff options
Diffstat (limited to 'drivers/net/ethernet/microchip/lan966x')
7 files changed, 283 insertions, 52 deletions
diff --git a/drivers/net/ethernet/microchip/lan966x/Makefile b/drivers/net/ethernet/microchip/lan966x/Makefile index 56afd694f3c7..7b0cda4ffa6b 100644 --- a/drivers/net/ethernet/microchip/lan966x/Makefile +++ b/drivers/net/ethernet/microchip/lan966x/Makefile @@ -15,5 +15,7 @@ lan966x-switch-objs := lan966x_main.o lan966x_phylink.o lan966x_port.o \ lan966x_xdp.o lan966x_vcap_impl.o lan966x_vcap_ag_api.o \ lan966x_tc_flower.o lan966x_goto.o +lan966x-switch-$(CONFIG_DEBUG_FS) += lan966x_vcap_debugfs.o + # Provide include files ccflags-y += -I$(srctree)/drivers/net/ethernet/microchip/vcap diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c index 580c91d24a52..47b37ab9d7d5 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.c @@ -1035,6 +1035,8 @@ static int lan966x_probe(struct platform_device *pdev) platform_set_drvdata(pdev, lan966x); lan966x->dev = &pdev->dev; + lan966x->debugfs_root = debugfs_create_dir("lan966x", NULL); + if (!device_get_mac_address(&pdev->dev, mac_addr)) { ether_addr_copy(lan966x->base_mac, mac_addr); } else { @@ -1223,6 +1225,8 @@ static int lan966x_remove(struct platform_device *pdev) lan966x_fdb_deinit(lan966x); lan966x_ptp_deinit(lan966x); + debugfs_remove_recursive(lan966x->debugfs_root); + return 0; } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h index 0106f9487cbe..49f5159afbf3 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_main.h +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_main.h @@ -3,6 +3,7 @@ #ifndef __LAN966X_MAIN_H__ #define __LAN966X_MAIN_H__ +#include <linux/debugfs.h> #include <linux/etherdevice.h> #include <linux/if_vlan.h> #include <linux/jiffies.h> @@ -14,6 +15,9 @@ #include <net/pkt_sched.h> #include <net/switchdev.h> +#include <vcap_api.h> +#include <vcap_api_client.h> + #include "lan966x_regs.h" #include "lan966x_ifh.h" @@ -128,6 +132,13 @@ enum LAN966X_PORT_MASK_MODE { LAN966X_PMM_REDIRECT, }; +enum vcap_is2_port_sel_ipv6 { + VCAP_IS2_PS_IPV6_TCPUDP_OTHER, + VCAP_IS2_PS_IPV6_STD, + VCAP_IS2_PS_IPV6_IP4_TCPUDP_IP4_OTHER, + VCAP_IS2_PS_IPV6_MAC_ETYPE, +}; + struct lan966x_port; struct lan966x_db { @@ -315,6 +326,9 @@ struct lan966x { /* vcap */ struct vcap_control *vcap_ctrl; + + /* debugfs */ + struct dentry *debugfs_root; }; struct lan966x_port_config { @@ -601,9 +615,22 @@ static inline bool lan966x_xdp_port_present(struct lan966x_port *port) int lan966x_vcap_init(struct lan966x *lan966x); void lan966x_vcap_deinit(struct lan966x *lan966x); +#if defined(CONFIG_DEBUG_FS) +int lan966x_vcap_port_info(struct net_device *dev, + struct vcap_admin *admin, + struct vcap_output_print *out); +#else +static inline int lan966x_vcap_port_info(struct net_device *dev, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + return 0; +} +#endif int lan966x_tc_flower(struct lan966x_port *port, - struct flow_cls_offload *f); + struct flow_cls_offload *f, + bool ingress); int lan966x_goto_port_add(struct lan966x_port *port, int from_cid, int to_cid, diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c index 01072121c999..cf0cc7562d04 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ #include <net/pkt_cls.h> +#include <net/pkt_sched.h> #include "lan966x_main.h" @@ -70,7 +71,7 @@ static int lan966x_tc_block_cb(enum tc_setup_type type, void *type_data, case TC_SETUP_CLSMATCHALL: return lan966x_tc_matchall(port, type_data, ingress); case TC_SETUP_CLSFLOWER: - return lan966x_tc_flower(port, type_data); + return lan966x_tc_flower(port, type_data, ingress); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c index b66a8725a071..bd10a7189741 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_tc_flower.c @@ -3,53 +3,135 @@ #include "lan966x_main.h" #include "vcap_api.h" #include "vcap_api_client.h" +#include "vcap_tc.h" -struct lan966x_tc_flower_parse_usage { - struct flow_cls_offload *f; - struct flow_rule *frule; - struct vcap_rule *vrule; - unsigned int used_keys; - u16 l3_proto; -}; +static bool lan966x_tc_is_known_etype(u16 etype) +{ + switch (etype) { + case ETH_P_ALL: + case ETH_P_ARP: + case ETH_P_IP: + case ETH_P_IPV6: + return true; + } + + return false; +} -static int lan966x_tc_flower_handler_ethaddr_usage(struct lan966x_tc_flower_parse_usage *st) +static int +lan966x_tc_flower_handler_control_usage(struct vcap_tc_flower_parse_usage *st) { - enum vcap_key_field smac_key = VCAP_KF_L2_SMAC; - enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC; - struct flow_match_eth_addrs match; - struct vcap_u48_key smac, dmac; + struct flow_match_control match; int err = 0; - flow_rule_match_eth_addrs(st->frule, &match); - - if (!is_zero_ether_addr(match.mask->src)) { - vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN); - vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN); - err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac); + flow_rule_match_control(st->frule, &match); + if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) { + if (match.key->flags & FLOW_DIS_IS_FRAGMENT) + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_L3_FRAGMENT, + VCAP_BIT_1); + else + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_L3_FRAGMENT, + VCAP_BIT_0); if (err) goto out; } - if (!is_zero_ether_addr(match.mask->dst)) { - vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN); - vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN); - err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac); + if (match.mask->flags & FLOW_DIS_FIRST_FRAG) { + if (match.key->flags & FLOW_DIS_FIRST_FRAG) + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_L3_FRAG_OFS_GT0, + VCAP_BIT_0); + else + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_L3_FRAG_OFS_GT0, + VCAP_BIT_1); if (err) goto out; } - st->used_keys |= BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS); + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_CONTROL); return err; out: - NL_SET_ERR_MSG_MOD(st->f->common.extack, "eth_addr parse error"); + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_frag parse error"); + return err; +} + +static int +lan966x_tc_flower_handler_basic_usage(struct vcap_tc_flower_parse_usage *st) +{ + struct flow_match_basic match; + int err = 0; + + flow_rule_match_basic(st->frule, &match); + if (match.mask->n_proto) { + st->l3_proto = be16_to_cpu(match.key->n_proto); + if (!lan966x_tc_is_known_etype(st->l3_proto)) { + err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ETYPE, + st->l3_proto, ~0); + if (err) + goto out; + } else if (st->l3_proto == ETH_P_IP) { + err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_IP4_IS, + VCAP_BIT_1); + if (err) + goto out; + } + } + if (match.mask->ip_proto) { + st->l4_proto = match.key->ip_proto; + + if (st->l4_proto == IPPROTO_TCP) { + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_TCP_IS, + VCAP_BIT_1); + if (err) + goto out; + } else if (st->l4_proto == IPPROTO_UDP) { + err = vcap_rule_add_key_bit(st->vrule, + VCAP_KF_TCP_IS, + VCAP_BIT_0); + if (err) + goto out; + } else { + err = vcap_rule_add_key_u32(st->vrule, + VCAP_KF_L3_IP_PROTO, + st->l4_proto, ~0); + if (err) + goto out; + } + } + + st->used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC); + return err; +out: + NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_proto parse error"); return err; } static int -(*lan966x_tc_flower_handlers_usage[])(struct lan966x_tc_flower_parse_usage *st) = { - [FLOW_DISSECTOR_KEY_ETH_ADDRS] = lan966x_tc_flower_handler_ethaddr_usage, +lan966x_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st) +{ + return vcap_tc_flower_handler_vlan_usage(st, + VCAP_KF_8021Q_VID_CLS, + VCAP_KF_8021Q_PCP_CLS); +} + +static int +(*lan966x_tc_flower_handlers_usage[])(struct vcap_tc_flower_parse_usage *st) = { + [FLOW_DISSECTOR_KEY_ETH_ADDRS] = vcap_tc_flower_handler_ethaddr_usage, + [FLOW_DISSECTOR_KEY_IPV4_ADDRS] = vcap_tc_flower_handler_ipv4_usage, + [FLOW_DISSECTOR_KEY_IPV6_ADDRS] = vcap_tc_flower_handler_ipv6_usage, + [FLOW_DISSECTOR_KEY_CONTROL] = lan966x_tc_flower_handler_control_usage, + [FLOW_DISSECTOR_KEY_PORTS] = vcap_tc_flower_handler_portnum_usage, + [FLOW_DISSECTOR_KEY_BASIC] = lan966x_tc_flower_handler_basic_usage, + [FLOW_DISSECTOR_KEY_VLAN] = lan966x_tc_flower_handler_vlan_usage, + [FLOW_DISSECTOR_KEY_TCP] = vcap_tc_flower_handler_tcp_usage, + [FLOW_DISSECTOR_KEY_ARP] = vcap_tc_flower_handler_arp_usage, + [FLOW_DISSECTOR_KEY_IP] = vcap_tc_flower_handler_ip_usage, }; static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f, @@ -57,8 +139,8 @@ static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f, struct vcap_rule *vrule, u16 *l3_proto) { - struct lan966x_tc_flower_parse_usage state = { - .f = f, + struct vcap_tc_flower_parse_usage state = { + .fco = f, .vrule = vrule, .l3_proto = ETH_P_ALL, }; @@ -83,7 +165,8 @@ static int lan966x_tc_flower_use_dissectors(struct flow_cls_offload *f, static int lan966x_tc_flower_action_check(struct vcap_control *vctrl, struct net_device *dev, - struct flow_cls_offload *fco) + struct flow_cls_offload *fco, + bool ingress) { struct flow_rule *rule = flow_cls_offload_flow_rule(fco); struct flow_action_entry *actent, *last_actent = NULL; @@ -120,7 +203,8 @@ static int lan966x_tc_flower_action_check(struct vcap_control *vctrl, "Invalid goto chain"); return -EINVAL; } - } else if (!vcap_is_last_chain(vctrl, fco->common.chain_index)) { + } else if (!vcap_is_last_chain(vctrl, fco->common.chain_index, + ingress)) { NL_SET_ERR_MSG_MOD(fco->common.extack, "Last action must be 'goto'"); return -EINVAL; @@ -139,7 +223,8 @@ static int lan966x_tc_flower_action_check(struct vcap_control *vctrl, static int lan966x_tc_flower_add(struct lan966x_port *port, struct flow_cls_offload *f, - struct vcap_admin *admin) + struct vcap_admin *admin, + bool ingress) { struct flow_action_entry *act; u16 l3_proto = ETH_P_ALL; @@ -148,7 +233,7 @@ static int lan966x_tc_flower_add(struct lan966x_port *port, int err, idx; err = lan966x_tc_flower_action_check(port->lan966x->vcap_ctrl, - port->dev, f); + port->dev, f, ingress); if (err) return err; @@ -231,8 +316,27 @@ static int lan966x_tc_flower_del(struct lan966x_port *port, return err; } +static int lan966x_tc_flower_stats(struct lan966x_port *port, + struct flow_cls_offload *f, + struct vcap_admin *admin) +{ + struct vcap_counter count = {}; + int err; + + err = vcap_get_rule_count_by_cookie(port->lan966x->vcap_ctrl, + &count, f->cookie); + if (err) + return err; + + flow_stats_update(&f->stats, 0x0, count.value, 0, 0, + FLOW_ACTION_HW_STATS_IMMEDIATE); + + return err; +} + int lan966x_tc_flower(struct lan966x_port *port, - struct flow_cls_offload *f) + struct flow_cls_offload *f, + bool ingress) { struct vcap_admin *admin; @@ -245,9 +349,11 @@ int lan966x_tc_flower(struct lan966x_port *port, switch (f->command) { case FLOW_CLS_REPLACE: - return lan966x_tc_flower_add(port, f, admin); + return lan966x_tc_flower_add(port, f, admin, ingress); case FLOW_CLS_DESTROY: return lan966x_tc_flower_del(port, f, admin); + case FLOW_CLS_STATS: + return lan966x_tc_flower_stats(port, f, admin); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c new file mode 100644 index 000000000000..7a0db58f5513 --- /dev/null +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_debugfs.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include "lan966x_main.h" +#include "lan966x_vcap_ag_api.h" +#include "vcap_api.h" +#include "vcap_api_client.h" + +static void lan966x_vcap_port_keys(struct lan966x_port *port, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + struct lan966x *lan966x = port->lan966x; + u32 val; + + out->prf(out->dst, " port[%d] (%s): ", port->chip_port, + netdev_name(port->dev)); + + val = lan_rd(lan966x, ANA_VCAP_S2_CFG(port->chip_port)); + out->prf(out->dst, "\n state: "); + if (ANA_VCAP_S2_CFG_ENA_GET(val)) + out->prf(out->dst, "on"); + else + out->prf(out->dst, "off"); + + for (int l = 0; l < admin->lookups; ++l) { + out->prf(out->dst, "\n Lookup %d: ", l); + + out->prf(out->dst, "\n snap: "); + if (ANA_VCAP_S2_CFG_SNAP_DIS_GET(val) & (BIT(0) << l)) + out->prf(out->dst, "mac_llc"); + else + out->prf(out->dst, "mac_snap"); + + out->prf(out->dst, "\n oam: "); + if (ANA_VCAP_S2_CFG_OAM_DIS_GET(val) & (BIT(0) << l)) + out->prf(out->dst, "mac_etype"); + else + out->prf(out->dst, "mac_oam"); + + out->prf(out->dst, "\n arp: "); + if (ANA_VCAP_S2_CFG_ARP_DIS_GET(val) & (BIT(0) << l)) + out->prf(out->dst, "mac_etype"); + else + out->prf(out->dst, "mac_arp"); + + out->prf(out->dst, "\n ipv4_other: "); + if (ANA_VCAP_S2_CFG_IP_OTHER_DIS_GET(val) & (BIT(0) << l)) + out->prf(out->dst, "mac_etype"); + else + out->prf(out->dst, "ip4_other"); + + out->prf(out->dst, "\n ipv4_tcp_udp: "); + if (ANA_VCAP_S2_CFG_IP_TCPUDP_DIS_GET(val) & (BIT(0) << l)) + out->prf(out->dst, "mac_etype"); + else + out->prf(out->dst, "ipv4_tcp_udp"); + + out->prf(out->dst, "\n ipv6: "); + switch (ANA_VCAP_S2_CFG_IP6_CFG_GET(val) & (0x3 << l)) { + case VCAP_IS2_PS_IPV6_TCPUDP_OTHER: + out->prf(out->dst, "ipv6_tcp_udp ipv6_tcp_udp"); + break; + case VCAP_IS2_PS_IPV6_STD: + out->prf(out->dst, "ipv6_std"); + break; + case VCAP_IS2_PS_IPV6_IP4_TCPUDP_IP4_OTHER: + out->prf(out->dst, "ipv4_tcp_udp ipv4_tcp_udp"); + break; + case VCAP_IS2_PS_IPV6_MAC_ETYPE: + out->prf(out->dst, "mac_etype"); + break; + } + } + + out->prf(out->dst, "\n"); +} + +int lan966x_vcap_port_info(struct net_device *dev, + struct vcap_admin *admin, + struct vcap_output_print *out) +{ + struct lan966x_port *port = netdev_priv(dev); + struct lan966x *lan966x = port->lan966x; + const struct vcap_info *vcap; + struct vcap_control *vctrl; + + vctrl = lan966x->vcap_ctrl; + vcap = &vctrl->vcaps[admin->vtype]; + + out->prf(out->dst, "%s:\n", vcap->name); + lan966x_vcap_port_keys(port, admin, out); + + return 0; +} diff --git a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c index 76a9fb113f50..68f9d69fd37b 100644 --- a/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c +++ b/drivers/net/ethernet/microchip/lan966x/lan966x_vcap_impl.c @@ -4,18 +4,12 @@ #include "lan966x_vcap_ag_api.h" #include "vcap_api.h" #include "vcap_api_client.h" +#include "vcap_api_debugfs.h" #define STREAMSIZE (64 * 4) #define LAN966X_IS2_LOOKUPS 2 -enum vcap_is2_port_sel_ipv6 { - VCAP_IS2_PS_IPV6_TCPUDP_OTHER, - VCAP_IS2_PS_IPV6_STD, - VCAP_IS2_PS_IPV6_IP4_TCPUDP_IP4_OTHER, - VCAP_IS2_PS_IPV6_MAC_ETYPE, -}; - static struct lan966x_vcap_inst { enum vcap_type vtype; /* type of vcap */ int tgt_inst; /* hardware instance number */ @@ -23,6 +17,7 @@ static struct lan966x_vcap_inst { int first_cid; /* first chain id in this vcap */ int last_cid; /* last chain id in this vcap */ int count; /* number of available addresses */ + bool ingress; /* is vcap in the ingress path */ } lan966x_vcap_inst_cfg[] = { { .vtype = VCAP_TYPE_IS2, /* IS2-0 */ @@ -31,6 +26,7 @@ static struct lan966x_vcap_inst { .first_cid = LAN966X_VCAP_CID_IS2_L0, .last_cid = LAN966X_VCAP_CID_IS2_MAX, .count = 256, + .ingress = true, }, }; @@ -383,13 +379,6 @@ static void lan966x_vcap_move(struct net_device *dev, lan966x_vcap_wait_update(lan966x, admin->tgt_inst); } -static int lan966x_vcap_port_info(struct net_device *dev, - struct vcap_admin *admin, - struct vcap_output_print *out) -{ - return 0; -} - static struct vcap_operations lan966x_vcap_ops = { .validate_keyset = lan966x_vcap_validate_keyset, .add_default_fields = lan966x_vcap_add_default_fields, @@ -431,6 +420,7 @@ lan966x_vcap_admin_alloc(struct lan966x *lan966x, struct vcap_control *ctrl, admin->vtype = cfg->vtype; admin->vinst = 0; + admin->ingress = cfg->ingress; admin->w32be = true; admin->tgt_inst = cfg->tgt_inst; @@ -483,6 +473,7 @@ int lan966x_vcap_init(struct lan966x *lan966x) struct lan966x_vcap_inst *cfg; struct vcap_control *ctrl; struct vcap_admin *admin; + struct dentry *dir; ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); if (!ctrl) @@ -506,11 +497,17 @@ int lan966x_vcap_init(struct lan966x *lan966x) list_add_tail(&admin->list, &ctrl->list); } - for (int p = 0; p < lan966x->num_phys_ports; ++p) - if (lan966x->ports[p]) + dir = vcap_debugfs(lan966x->dev, lan966x->debugfs_root, ctrl); + for (int p = 0; p < lan966x->num_phys_ports; ++p) { + if (lan966x->ports[p]) { + vcap_port_debugfs(lan966x->dev, dir, ctrl, + lan966x->ports[p]->dev); + lan_rmw(ANA_VCAP_S2_CFG_ENA_SET(true), ANA_VCAP_S2_CFG_ENA, lan966x, ANA_VCAP_S2_CFG(lan966x->ports[p]->chip_port)); + } + } lan966x->vcap_ctrl = ctrl; |