diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-08-03 16:29:08 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-08-03 16:29:08 -0700 |
commit | f86d1fbbe7858884d6754534a0afbb74fc30bc26 (patch) | |
tree | f61796870edefbe77d495e9d719c68af1d14275b /drivers/net/ethernet/marvell | |
parent | 526942b8134cc34d25d27f95dfff98b8ce2f6fcd (diff) | |
parent | 7c6327c77d509e78bff76f2a4551fcfee851682e (diff) |
Merge tag 'net-next-6.0' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
Pull networking changes from Paolo Abeni:
"Core:
- Refactor the forward memory allocation to better cope with memory
pressure with many open sockets, moving from a per socket cache to
a per-CPU one
- Replace rwlocks with RCU for better fairness in ping, raw sockets
and IP multicast router.
- Network-side support for IO uring zero-copy send.
- A few skb drop reason improvements, including codegen the source
file with string mapping instead of using macro magic.
- Rename reference tracking helpers to a more consistent netdev_*
schema.
- Adapt u64_stats_t type to address load/store tearing issues.
- Refine debug helper usage to reduce the log noise caused by bots.
BPF:
- Improve socket map performance, avoiding skb cloning on read
operation.
- Add support for 64 bits enum, to match types exposed by kernel.
- Introduce support for sleepable uprobes program.
- Introduce support for enum textual representation in libbpf.
- New helpers to implement synproxy with eBPF/XDP.
- Improve loop performances, inlining indirect calls when possible.
- Removed all the deprecated libbpf APIs.
- Implement new eBPF-based LSM flavor.
- Add type match support, which allow accurate queries to the eBPF
used types.
- A few TCP congetsion control framework usability improvements.
- Add new infrastructure to manipulate CT entries via eBPF programs.
- Allow for livepatch (KLP) and BPF trampolines to attach to the same
kernel function.
Protocols:
- Introduce per network namespace lookup tables for unix sockets,
increasing scalability and reducing contention.
- Preparation work for Wi-Fi 7 Multi-Link Operation (MLO) support.
- Add support to forciby close TIME_WAIT TCP sockets via user-space
tools.
- Significant performance improvement for the TLS 1.3 receive path,
both for zero-copy and not-zero-copy.
- Support for changing the initial MTPCP subflow priority/backup
status
- Introduce virtually contingus buffers for sockets over RDMA, to
cope better with memory pressure.
- Extend CAN ethtool support with timestamping capabilities
- Refactor CAN build infrastructure to allow building only the needed
features.
Driver API:
- Remove devlink mutex to allow parallel commands on multiple links.
- Add support for pause stats in distributed switch.
- Implement devlink helpers to query and flash line cards.
- New helper for phy mode to register conversion.
New hardware / drivers:
- Ethernet DSA driver for the rockchip mt7531 on BPI-R2 Pro.
- Ethernet DSA driver for the Renesas RZ/N1 A5PSW switch.
- Ethernet DSA driver for the Microchip LAN937x switch.
- Ethernet PHY driver for the Aquantia AQR113C EPHY.
- CAN driver for the OBD-II ELM327 interface.
- CAN driver for RZ/N1 SJA1000 CAN controller.
- Bluetooth: Infineon CYW55572 Wi-Fi plus Bluetooth combo device.
Drivers:
- Intel Ethernet NICs:
- i40e: add support for vlan pruning
- i40e: add support for XDP framented packets
- ice: improved vlan offload support
- ice: add support for PPPoE offload
- Mellanox Ethernet (mlx5)
- refactor packet steering offload for performance and scalability
- extend support for TC offload
- refactor devlink code to clean-up the locking schema
- support stacked vlans for bridge offloads
- use TLS objects pool to improve connection rate
- Netronome Ethernet NICs (nfp):
- extend support for IPv6 fields mangling offload
- add support for vepa mode in HW bridge
- better support for virtio data path acceleration (VDPA)
- enable TSO by default
- Microsoft vNIC driver (mana)
- add support for XDP redirect
- Others Ethernet drivers:
- bonding: add per-port priority support
- microchip lan743x: extend phy support
- Fungible funeth: support UDP segmentation offload and XDP xmit
- Solarflare EF100: add support for virtual function representors
- MediaTek SoC: add XDP support
- Mellanox Ethernet/IB switch (mlxsw):
- dropped support for unreleased H/W (XM router).
- improved stats accuracy
- unified bridge model coversion improving scalability (parts 1-6)
- support for PTP in Spectrum-2 asics
- Broadcom PHYs
- add PTP support for BCM54210E
- add support for the BCM53128 internal PHY
- Marvell Ethernet switches (prestera):
- implement support for multicast forwarding offload
- Embedded Ethernet switches:
- refactor OcteonTx MAC filter for better scalability
- improve TC H/W offload for the Felix driver
- refactor the Microchip ksz8 and ksz9477 drivers to share the
probe code (parts 1, 2), add support for phylink mac
configuration
- Other WiFi:
- Microchip wilc1000: diable WEP support and enable WPA3
- Atheros ath10k: encapsulation offload support
Old code removal:
- Neterion vxge ethernet driver: this is untouched since more than 10 years"
* tag 'net-next-6.0' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1890 commits)
doc: sfp-phylink: Fix a broken reference
wireguard: selftests: support UML
wireguard: allowedips: don't corrupt stack when detecting overflow
wireguard: selftests: update config fragments
wireguard: ratelimiter: use hrtimer in selftest
net/mlx5e: xsk: Discard unaligned XSK frames on striding RQ
net: usb: ax88179_178a: Bind only to vendor-specific interface
selftests: net: fix IOAM test skip return code
net: usb: make USB_RTL8153_ECM non user configurable
net: marvell: prestera: remove reduntant code
octeontx2-pf: Reduce minimum mtu size to 60
net: devlink: Fix missing mutex_unlock() call
net/tls: Remove redundant workqueue flush before destroy
net: txgbe: Fix an error handling path in txgbe_probe()
net: dsa: Fix spelling mistakes and cleanup code
Documentation: devlink: add add devlink-selftests to the table of contents
dccp: put dccp_qpolicy_full() and dccp_qpolicy_push() in the same lock
net: ionic: fix error check for vlan flags in ionic_set_nic_features()
net: ice: fix error NETIF_F_HW_VLAN_CTAG_FILTER check in ice_vsi_sync_fltr()
nfp: flower: add support for tunnel offload without key ID
...
Diffstat (limited to 'drivers/net/ethernet/marvell')
46 files changed, 4679 insertions, 304 deletions
diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 57eff4e9e6de..b6be0552a6c1 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -775,7 +775,7 @@ txq_put_hdr_tso(struct sk_buff *skb, struct tx_queue *txq, int length, u32 *first_cmd_sts, bool first_desc) { struct mv643xx_eth_private *mp = txq_to_mp(txq); - int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + int hdr_len = skb_tcp_all_headers(skb); int tx_index; struct tx_desc *desc; int ret; diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 384f5a16753d..0caa2df87c04 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -2664,8 +2664,8 @@ err_drop_frame: static inline void mvneta_tso_put_hdr(struct sk_buff *skb, struct mvneta_tx_queue *txq) { - int hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); struct mvneta_tx_buf *buf = &txq->buf[txq->txq_put_index]; + int hdr_len = skb_tcp_all_headers(skb); struct mvneta_tx_desc *tx_desc; tx_desc = mvneta_txq_next_desc_get(txq); @@ -2727,7 +2727,7 @@ static int mvneta_tx_tso(struct sk_buff *skb, struct net_device *dev, if ((txq->count + tso_count_descs(skb)) >= txq->size) return 0; - if (skb_headlen(skb) < (skb_transport_offset(skb) + tcp_hdrlen(skb))) { + if (skb_headlen(skb) < skb_tcp_all_headers(skb)) { pr_info("*** Is this even possible?\n"); return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/Makefile b/drivers/net/ethernet/marvell/octeontx2/af/Makefile index 7f4a4ca9af78..40203560b291 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/Makefile +++ b/drivers/net/ethernet/marvell/octeontx2/af/Makefile @@ -11,4 +11,4 @@ rvu_mbox-y := mbox.o rvu_trace.o rvu_af-y := cgx.o rvu.o rvu_cgx.o rvu_npa.o rvu_nix.o \ rvu_reg.o rvu_npc.o rvu_debugfs.o ptp.o rvu_npc_fs.o \ rvu_cpt.o rvu_devlink.o rpm.o rvu_cn10k.o rvu_switch.o \ - rvu_sdp.o + rvu_sdp.o rvu_npc_hash.o diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c index 25491edc35ce..c8724bfa86b0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c @@ -498,6 +498,32 @@ static u8 cgx_get_lmac_type(void *cgxd, int lmac_id) return (cfg >> CGX_LMAC_TYPE_SHIFT) & CGX_LMAC_TYPE_MASK; } +static u32 cgx_get_lmac_fifo_len(void *cgxd, int lmac_id) +{ + struct cgx *cgx = cgxd; + u8 num_lmacs; + u32 fifo_len; + + fifo_len = cgx->mac_ops->fifo_len; + num_lmacs = cgx->mac_ops->get_nr_lmacs(cgx); + + switch (num_lmacs) { + case 1: + return fifo_len; + case 2: + return fifo_len / 2; + case 3: + /* LMAC0 gets half of the FIFO, reset 1/4th */ + if (lmac_id == 0) + return fifo_len / 2; + return fifo_len / 4; + case 4: + default: + return fifo_len / 4; + } + return 0; +} + /* Configure CGX LMAC in internal loopback mode */ int cgx_lmac_internal_loopback(void *cgxd, int lmac_id, bool enable) { @@ -847,6 +873,11 @@ static void cgx_lmac_pause_frm_config(void *cgxd, int lmac_id, bool enable) cfg |= CGX_CMR_RX_OVR_BP_EN(lmac_id); cfg &= ~CGX_CMR_RX_OVR_BP_BP(lmac_id); cgx_write(cgx, 0, CGXX_CMR_RX_OVR_BP, cfg); + + /* Disable all PFC classes by default */ + cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL); + cfg = FIELD_SET(CGX_PFC_CLASS_MASK, 0, cfg); + cgx_write(cgx, lmac_id, CGXX_SMUX_CBFC_CTL, cfg); } int verify_lmac_fc_cfg(void *cgxd, int lmac_id, u8 tx_pause, u8 rx_pause, @@ -899,6 +930,7 @@ int cgx_lmac_pfc_config(void *cgxd, int lmac_id, u8 tx_pause, return 0; cfg = cgx_read(cgx, lmac_id, CGXX_SMUX_CBFC_CTL); + pfc_en |= FIELD_GET(CGX_PFC_CLASS_MASK, cfg); if (rx_pause) { cfg |= (CGXX_SMUX_CBFC_CTL_RX_EN | @@ -910,12 +942,13 @@ int cgx_lmac_pfc_config(void *cgxd, int lmac_id, u8 tx_pause, CGXX_SMUX_CBFC_CTL_DRP_EN); } - if (tx_pause) + if (tx_pause) { cfg |= CGXX_SMUX_CBFC_CTL_TX_EN; - else + cfg = FIELD_SET(CGX_PFC_CLASS_MASK, pfc_en, cfg); + } else { cfg &= ~CGXX_SMUX_CBFC_CTL_TX_EN; - - cfg = FIELD_SET(CGX_PFC_CLASS_MASK, pfc_en, cfg); + cfg = FIELD_SET(CGX_PFC_CLASS_MASK, 0, cfg); + } cgx_write(cgx, lmac_id, CGXX_SMUX_CBFC_CTL, cfg); @@ -1005,9 +1038,9 @@ int cgx_fwi_cmd_send(u64 req, u64 *resp, struct lmac *lmac) if (!wait_event_timeout(lmac->wq_cmd_cmplt, !lmac->cmd_pend, msecs_to_jiffies(CGX_CMD_TIMEOUT))) { dev = &cgx->pdev->dev; - dev_err(dev, "cgx port %d:%d cmd timeout\n", - cgx->cgx_id, lmac->lmac_id); - err = -EIO; + dev_err(dev, "cgx port %d:%d cmd %lld timeout\n", + cgx->cgx_id, lmac->lmac_id, FIELD_GET(CMDREG_ID, req)); + err = LMAC_AF_ERR_CMD_TIMEOUT; goto unlock; } @@ -1433,11 +1466,19 @@ static int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool enable) u64 req = 0; u64 resp; - if (enable) + if (enable) { req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_UP, req); - else - req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_DOWN, req); + /* On CN10K firmware offloads link bring up/down operations to ECP + * On Octeontx2 link operations are handled by firmware itself + * which can cause mbox errors so configure maximum time firmware + * poll for Link as 1000 ms + */ + if (!is_dev_rpm(cgx)) + req = FIELD_SET(LINKCFG_TIMEOUT, 1000, req); + } else { + req = FIELD_SET(CMDREG_ID, CGX_CMD_LINK_BRING_DOWN, req); + } return cgx_fwi_cmd_generic(req, &resp, cgx, lmac_id); } @@ -1689,6 +1730,7 @@ static struct mac_ops cgx_mac_ops = { .tx_stats_cnt = 18, .get_nr_lmacs = cgx_get_nr_lmacs, .get_lmac_type = cgx_get_lmac_type, + .lmac_fifo_len = cgx_get_lmac_fifo_len, .mac_lmac_intl_lbk = cgx_lmac_internal_loopback, .mac_get_rx_stats = cgx_get_rx_stats, .mac_get_tx_stats = cgx_get_tx_stats, @@ -1743,6 +1785,13 @@ static int cgx_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto err_release_regions; } + cgx->lmac_count = cgx->mac_ops->get_nr_lmacs(cgx); + if (!cgx->lmac_count) { + dev_notice(dev, "CGX %d LMAC count is zero, skipping probe\n", cgx->cgx_id); + err = -EOPNOTSUPP; + goto err_release_regions; + } + nvec = pci_msix_vec_count(cgx->pdev); err = pci_alloc_irq_vectors(pdev, nvec, nvec, PCI_IRQ_MSIX); if (err < 0 || err != nvec) { diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h index bd2f33a26eee..0b06788b8d80 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h @@ -92,7 +92,7 @@ #define CGX_COMMAND_REG CGXX_SCRATCH1_REG #define CGX_EVENT_REG CGXX_SCRATCH0_REG -#define CGX_CMD_TIMEOUT 2200 /* msecs */ +#define CGX_CMD_TIMEOUT 5000 /* msecs */ #define DEFAULT_PAUSE_TIME 0x7FF #define CGX_LMAC_FWI 0 diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx_fw_if.h b/drivers/net/ethernet/marvell/octeontx2/af/cgx_fw_if.h index f72ec0e2506f..d4a27c882a5b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/cgx_fw_if.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx_fw_if.h @@ -261,4 +261,6 @@ struct cgx_lnk_sts { #define CMDMODECHANGE_PORT GENMASK_ULL(21, 14) #define CMDMODECHANGE_FLAGS GENMASK_ULL(63, 22) +/* LINK_BRING_UP command timeout */ +#define LINKCFG_TIMEOUT GENMASK_ULL(21, 8) #endif /* __CGX_FW_INTF_H__ */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h index f30581bf0688..52b6016789fa 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h @@ -80,6 +80,7 @@ struct mac_ops { */ int (*get_nr_lmacs)(void *cgx); u8 (*get_lmac_type)(void *cgx, int lmac_id); + u32 (*lmac_fifo_len)(void *cgx, int lmac_id); int (*mac_lmac_intl_lbk)(void *cgx, int lmac_id, bool enable); /* Register Stats related functions */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h index 550cb11197bf..d7762577e285 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/mbox.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/mbox.h @@ -33,7 +33,7 @@ #define INTR_MASK(pfvfs) ((pfvfs < 64) ? (BIT_ULL(pfvfs) - 1) : (~0ull)) -#define MBOX_RSP_TIMEOUT 3000 /* Time(ms) to wait for mbox response */ +#define MBOX_RSP_TIMEOUT 6000 /* Time(ms) to wait for mbox response */ #define MBOX_MSG_ALIGN 16 /* Align mbox msg start to 16bytes */ @@ -169,9 +169,10 @@ M(CGX_GET_PHY_FEC_STATS, 0x219, cgx_get_phy_fec_stats, msg_req, msg_rsp) \ M(CGX_FEATURES_GET, 0x21B, cgx_features_get, msg_req, \ cgx_features_info_msg) \ M(RPM_STATS, 0x21C, rpm_stats, msg_req, rpm_stats_rsp) \ -M(CGX_MAC_ADDR_RESET, 0x21D, cgx_mac_addr_reset, msg_req, msg_rsp) \ +M(CGX_MAC_ADDR_RESET, 0x21D, cgx_mac_addr_reset, cgx_mac_addr_reset_req, \ + msg_rsp) \ M(CGX_MAC_ADDR_UPDATE, 0x21E, cgx_mac_addr_update, cgx_mac_addr_update_req, \ - msg_rsp) \ + cgx_mac_addr_update_rsp) \ M(CGX_PRIO_FLOW_CTRL_CFG, 0x21F, cgx_prio_flow_ctrl_cfg, cgx_pfc_cfg, \ cgx_pfc_rsp) \ /* NPA mbox IDs (range 0x400 - 0x5FF) */ \ @@ -241,6 +242,9 @@ M(NPC_MCAM_READ_BASE_RULE, 0x6011, npc_read_base_steer_rule, \ M(NPC_MCAM_GET_STATS, 0x6012, npc_mcam_entry_stats, \ npc_mcam_get_stats_req, \ npc_mcam_get_stats_rsp) \ +M(NPC_GET_SECRET_KEY, 0x6013, npc_get_secret_key, \ + npc_get_secret_key_req, \ + npc_get_secret_key_rsp) \ /* NIX mbox IDs (range 0x8000 - 0xFFFF) */ \ M(NIX_LF_ALLOC, 0x8000, nix_lf_alloc, \ nix_lf_alloc_req, nix_lf_alloc_rsp) \ @@ -428,6 +432,7 @@ struct get_hw_cap_rsp { struct mbox_msghdr hdr; u8 nix_fixed_txschq_mapping; /* Schq mapping fixed or flexible */ u8 nix_shaping; /* Is shaping and coloring supported */ + u8 npc_hash_extract; /* Is hash extract supported */ }; /* CGX mbox message formats */ @@ -451,6 +456,7 @@ struct cgx_fec_stats_rsp { struct cgx_mac_addr_set_or_get { struct mbox_msghdr hdr; u8 mac_addr[ETH_ALEN]; + u32 index; }; /* Structure for requesting the operation to @@ -466,7 +472,7 @@ struct cgx_mac_addr_add_req { */ struct cgx_mac_addr_add_rsp { struct mbox_msghdr hdr; - u8 index; + u32 index; }; /* Structure for requesting the operation to @@ -474,7 +480,7 @@ struct cgx_mac_addr_add_rsp { */ struct cgx_mac_addr_del_req { struct mbox_msghdr hdr; - u8 index; + u32 index; }; /* Structure for response against the operation to @@ -482,7 +488,7 @@ struct cgx_mac_addr_del_req { */ struct cgx_max_dmac_entries_get_rsp { struct mbox_msghdr hdr; - u8 max_dmac_filters; + u32 max_dmac_filters; }; struct cgx_link_user_info { @@ -583,10 +589,20 @@ struct cgx_set_link_mode_rsp { int status; }; +struct cgx_mac_addr_reset_req { + struct mbox_msghdr hdr; + u32 index; +}; + struct cgx_mac_addr_update_req { struct mbox_msghdr hdr; u8 mac_addr[ETH_ALEN]; - u8 index; + u32 index; +}; + +struct cgx_mac_addr_update_rsp { + struct mbox_msghdr hdr; + u32 index; }; #define RVU_LMAC_FEAT_FC BIT_ULL(0) /* pause frames */ @@ -1440,6 +1456,16 @@ struct npc_mcam_get_stats_rsp { u8 stat_ena; /* enabled */ }; +struct npc_get_secret_key_req { + struct mbox_msghdr hdr; + u8 intf; +}; + +struct npc_get_secret_key_rsp { + struct mbox_msghdr hdr; + u64 secret_key[3]; +}; + enum ptp_op { PTP_OP_ADJFINE = 0, PTP_OP_GET_CLOCK = 1, @@ -1622,6 +1648,11 @@ enum cgx_af_status { LMAC_AF_ERR_PERM_DENIED = -1103, LMAC_AF_ERR_PFC_ENADIS_PERM_DENIED = -1104, LMAC_AF_ERR_8023PAUSE_ENADIS_PERM_DENIED = -1105, + LMAC_AF_ERR_CMD_TIMEOUT = -1106, + LMAC_AF_ERR_FIRMWARE_DATA_NOT_MAPPED = -1107, + LMAC_AF_ERR_EXACT_MATCH_TBL_ADD_FAILED = -1108, + LMAC_AF_ERR_EXACT_MATCH_TBL_DEL_FAILED = -1109, + LMAC_AF_ERR_EXACT_MATCH_TBL_LOOK_UP_FAILED = -1110, }; #endif /* MBOX_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/npc.h index 9b6e587e78b4..f187293e3e08 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/npc.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/npc.h @@ -10,6 +10,14 @@ #define NPC_KEX_CHAN_MASK 0xFFFULL +#define SET_KEX_LD(intf, lid, ltype, ld, cfg) \ + rvu_write64(rvu, blkaddr, \ + NPC_AF_INTFX_LIDX_LTX_LDX_CFG(intf, lid, ltype, ld), cfg) + +#define SET_KEX_LDFLAGS(intf, ld, flags, cfg) \ + rvu_write64(rvu, blkaddr, \ + NPC_AF_INTFX_LDATAX_FLAGSX_CFG(intf, ld, flags), cfg) + enum NPC_LID_E { NPC_LID_LA = 0, NPC_LID_LB, @@ -200,6 +208,7 @@ enum key_fields { NPC_ERRLEV, NPC_ERRCODE, NPC_LXMB, + NPC_EXACT_RESULT, NPC_LA, NPC_LB, NPC_LC, @@ -381,6 +390,22 @@ struct nix_rx_action { }; /* NPC_AF_INTFX_KEX_CFG field masks */ +#define NPC_EXACT_NIBBLE_START 40 +#define NPC_EXACT_NIBBLE_END 43 +#define NPC_EXACT_NIBBLE GENMASK_ULL(43, 40) + +/* NPC_EXACT_KEX_S nibble definitions for each field */ +#define NPC_EXACT_NIBBLE_HIT BIT_ULL(40) +#define NPC_EXACT_NIBBLE_OPC BIT_ULL(40) +#define NPC_EXACT_NIBBLE_WAY BIT_ULL(40) +#define NPC_EXACT_NIBBLE_INDEX GENMASK_ULL(43, 41) + +#define NPC_EXACT_RESULT_HIT BIT_ULL(0) +#define NPC_EXACT_RESULT_OPC GENMASK_ULL(2, 1) +#define NPC_EXACT_RESULT_WAY GENMASK_ULL(4, 3) +#define NPC_EXACT_RESULT_IDX GENMASK_ULL(15, 5) + +/* NPC_AF_INTFX_KEX_CFG field masks */ #define NPC_PARSE_NIBBLE GENMASK_ULL(30, 0) /* NPC_PARSE_KEX_S nibble definitions for each field */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h b/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h index 4180376fa676..a820bad3abb2 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/npc_profile.h @@ -155,7 +155,7 @@ /* Rx parse key extract nibble enable */ #define NPC_PARSE_NIBBLE_INTF_RX (NPC_PARSE_NIBBLE_CHAN | \ - NPC_PARSE_NIBBLE_ERRCODE | \ + NPC_PARSE_NIBBLE_L2L3_BCAST | \ NPC_PARSE_NIBBLE_LA_LTYPE | \ NPC_PARSE_NIBBLE_LB_LTYPE | \ NPC_PARSE_NIBBLE_LC_LTYPE | \ @@ -15123,7 +15123,8 @@ static struct npc_mcam_kex npc_mkex_default = { .kpu_version = NPC_KPU_PROFILE_VER, .keyx_cfg = { /* nibble: LA..LE (ltype only) + Error code + Channel */ - [NIX_INTF_RX] = ((u64)NPC_MCAM_KEY_X2 << 32) | NPC_PARSE_NIBBLE_INTF_RX, + [NIX_INTF_RX] = ((u64)NPC_MCAM_KEY_X2 << 32) | NPC_PARSE_NIBBLE_INTF_RX | + (u64)NPC_EXACT_NIBBLE_HIT, /* nibble: LA..LE (ltype only) */ [NIX_INTF_TX] = ((u64)NPC_MCAM_KEY_X2 << 32) | NPC_PARSE_NIBBLE_INTF_TX, }, diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c index 47e83d7a5804..ef59de43b11e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c @@ -22,6 +22,7 @@ static struct mac_ops rpm_mac_ops = { .tx_stats_cnt = 34, .get_nr_lmacs = rpm_get_nr_lmacs, .get_lmac_type = rpm_get_lmac_type, + .lmac_fifo_len = rpm_get_lmac_fifo_len, .mac_lmac_intl_lbk = rpm_lmac_internal_loopback, .mac_get_rx_stats = rpm_get_rx_stats, .mac_get_tx_stats = rpm_get_tx_stats, @@ -276,6 +277,14 @@ void rpm_lmac_pause_frm_config(void *rpmd, int lmac_id, bool enable) cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); cfg |= RPMX_MTI_MAC100X_COMMAND_CONFIG_TX_P_DISABLE; rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); + + /* Disable all PFC classes */ + cfg = rpm_read(rpm, lmac_id, RPMX_CMRX_PRT_CBFC_CTL); + cfg = FIELD_SET(RPM_PFC_CLASS_MASK, 0, cfg); + rpm_write(rpm, lmac_id, RPMX_CMRX_PRT_CBFC_CTL, cfg); + + /* Enable channel mask for all LMACS */ + rpm_write(rpm, 0, RPMX_CMR_CHAN_MSK_OR, ~0ULL); } int rpm_get_rx_stats(void *rpmd, int lmac_id, int idx, u64 *rx_stat) @@ -342,6 +351,35 @@ u8 rpm_get_lmac_type(void *rpmd, int lmac_id) return err; } +u32 rpm_get_lmac_fifo_len(void *rpmd, int lmac_id) +{ + rpm_t *rpm = rpmd; + u64 hi_perf_lmac; + u8 num_lmacs; + u32 fifo_len; + + fifo_len = rpm->mac_ops->fifo_len; + num_lmacs = rpm->mac_ops->get_nr_lmacs(rpm); + + switch (num_lmacs) { + case 1: + return fifo_len; + case 2: + return fifo_len / 2; + case 3: + /* LMAC marked as hi_perf gets half of the FIFO and rest 1/4th */ + hi_perf_lmac = rpm_read(rpm, 0, CGXX_CMRX_RX_LMACS); + hi_perf_lmac = (hi_perf_lmac >> 4) & 0x3ULL; + if (lmac_id == hi_perf_lmac) + return fifo_len / 2; + return fifo_len / 4; + case 4: + default: + return fifo_len / 4; + } + return 0; +} + int rpm_lmac_internal_loopback(void *rpmd, int lmac_id, bool enable) { rpm_t *rpm = rpmd; @@ -387,15 +425,14 @@ void rpm_lmac_ptp_config(void *rpmd, int lmac_id, bool enable) int rpm_lmac_pfc_config(void *rpmd, int lmac_id, u8 tx_pause, u8 rx_pause, u16 pfc_en) { rpm_t *rpm = rpmd; - u64 cfg; + u64 cfg, class_en; if (!is_lmac_valid(rpm, lmac_id)) return -ENODEV; - /* reset PFC class quanta and threshold */ - rpm_cfg_pfc_quanta_thresh(rpm, lmac_id, 0xffff, false); - cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG); + class_en = rpm_read(rpm, lmac_id, RPMX_CMRX_PRT_CBFC_CTL); + pfc_en |= FIELD_GET(RPM_PFC_CLASS_MASK, class_en); if (rx_pause) { cfg &= ~(RPMX_MTI_MAC100X_COMMAND_CONFIG_RX_P_DISABLE | @@ -410,9 +447,11 @@ int rpm_lmac_pfc_config(void *rpmd, int lmac_id, u8 tx_pause, u8 rx_pause, u16 p if (tx_pause) { rpm_cfg_pfc_quanta_thresh(rpm, lmac_id, pfc_en, true); cfg &= ~RPMX_MTI_MAC100X_COMMAND_CONFIG_TX_P_DISABLE; + class_en = FIELD_SET(RPM_PFC_CLASS_MASK, pfc_en, class_en); } else { rpm_cfg_pfc_quanta_thresh(rpm, lmac_id, 0xfff, false); cfg |= RPMX_MTI_MAC100X_COMMAND_CONFIG_TX_P_DISABLE; + class_en = FIELD_SET(RPM_PFC_CLASS_MASK, 0, class_en); } if (!rx_pause && !tx_pause) @@ -422,9 +461,7 @@ int rpm_lmac_pfc_config(void *rpmd, int lmac_id, u8 tx_pause, u8 rx_pause, u16 p rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg); - cfg = rpm_read(rpm, lmac_id, RPMX_CMRX_PRT_CBFC_CTL); - cfg = FIELD_SET(RPM_PFC_CLASS_MASK, pfc_en, cfg); - rpm_write(rpm, lmac_id, RPMX_CMRX_PRT_CBFC_CTL, cfg); + rpm_write(rpm, lmac_id, RPMX_CMRX_PRT_CBFC_CTL, class_en); return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.h b/drivers/net/ethernet/marvell/octeontx2/af/rpm.h index 9ab8d49dd180..c2bd6e54ea51 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.h @@ -48,10 +48,10 @@ #define RPMX_MTI_MAC100X_CL1011_QUANTA_THRESH 0x8130 #define RPMX_MTI_MAC100X_CL1213_QUANTA_THRESH 0x8138 #define RPMX_MTI_MAC100X_CL1415_QUANTA_THRESH 0x8140 -#define RPM_DEFAULT_PAUSE_TIME 0xFFFF #define RPMX_CMR_RX_OVR_BP 0x4120 #define RPMX_CMR_RX_OVR_BP_EN(x) BIT_ULL((x) + 8) #define RPMX_CMR_RX_OVR_BP_BP(x) BIT_ULL((x) + 4) +#define RPMX_CMR_CHAN_MSK_OR 0x4118 #define RPMX_MTI_STAT_RX_STAT_PAGES_COUNTERX 0x12000 #define RPMX_MTI_STAT_TX_STAT_PAGES_COUNTERX 0x13000 #define RPMX_MTI_STAT_DATA_HI_CDC 0x10038 @@ -70,11 +70,12 @@ #define RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_FWD BIT_ULL(7) #define RPMX_MTI_MAC100X_CL01_PAUSE_QUANTA 0x80A8 #define RPMX_MTI_MAC100X_CL89_PAUSE_QUANTA 0x8108 -#define RPM_DEFAULT_PAUSE_TIME 0xFFFF +#define RPM_DEFAULT_PAUSE_TIME 0x7FF /* Function Declarations */ int rpm_get_nr_lmacs(void *rpmd); u8 rpm_get_lmac_type(void *rpmd, int lmac_id); +u32 rpm_get_lmac_fifo_len(void *rpmd, int lmac_id); int rpm_lmac_internal_loopback(void *rpmd, int lmac_id, bool enable); void rpm_lmac_enadis_rx_pause_fwding(void *rpmd, int lmac_id, bool enable); int rpm_lmac_get_pause_frm_status(void *cgxd, int lmac_id, u8 *tx_pause, diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index 54e1b27a7dfe..6809b8b4c556 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -18,6 +18,7 @@ #include "ptp.h" #include "rvu_trace.h" +#include "rvu_npc_hash.h" #define DRV_NAME "rvu_af" #define DRV_STRING "Marvell OcteonTX2 RVU Admin Function Driver" @@ -68,6 +69,8 @@ static void rvu_setup_hw_capabilities(struct rvu *rvu) hw->cap.nix_tx_link_bp = true; hw->cap.nix_rx_multicast = true; hw->cap.nix_shaper_toggle_wait = false; + hw->cap.npc_hash_extract = false; + hw->cap.npc_exact_match_enabled = false; hw->rvu = rvu; if (is_rvu_pre_96xx_C0(rvu)) { @@ -85,6 +88,9 @@ static void rvu_setup_hw_capabilities(struct rvu *rvu) if (!is_rvu_otx2(rvu)) hw->cap.per_pf_mbox_regs = true; + + if (is_rvu_npc_hash_extract_en(rvu)) + hw->cap.npc_hash_extract = true; } /* Poll a RVU block's register 'offset', for a 'zero' @@ -1122,6 +1128,12 @@ cpt: goto cgx_err; } + err = rvu_npc_exact_init(rvu); + if (err) { + dev_err(rvu->dev, "failed to initialize exact match table\n"); + return err; + } + /* Assign MACs for CGX mapped functions */ rvu_setup_pfvf_macaddress(rvu); @@ -1991,6 +2003,7 @@ int rvu_mbox_handler_get_hw_cap(struct rvu *rvu, struct msg_req *req, rsp->nix_fixed_txschq_mapping = hw->cap.nix_fixed_txschq_mapping; rsp->nix_shaping = hw->cap.nix_shaping; + rsp->npc_hash_extract = hw->cap.npc_hash_extract; return 0; } @@ -2548,6 +2561,9 @@ static void rvu_blklf_teardown(struct rvu *rvu, u16 pcifunc, u8 blkaddr) static void __rvu_flr_handler(struct rvu *rvu, u16 pcifunc) { + if (rvu_npc_exact_has_match_table(rvu)) + rvu_npc_exact_reset(rvu, pcifunc); + mutex_lock(&rvu->flr_lock); /* Reset order should reflect inter-block dependencies: * 1. Reset any packet/work sources (NIX, CPT, TIM) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h index 513b43ecd5be..d15bc443335d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h @@ -338,6 +338,8 @@ struct hw_cap { bool per_pf_mbox_regs; /* PF mbox specified in per PF registers ? */ bool programmable_chans; /* Channels programmable ? */ bool ipolicer; + bool npc_hash_extract; /* Hash extract enabled ? */ + bool npc_exact_match_enabled; /* Exact match supported ? */ }; struct rvu_hwinfo { @@ -369,6 +371,7 @@ struct rvu_hwinfo { struct rvu *rvu; struct npc_pkind pkind; struct npc_mcam mcam; + struct npc_exact_table *table; }; struct mbox_wq_info { @@ -419,6 +422,7 @@ struct npc_kpu_profile_adapter { const struct npc_kpu_profile_action *ikpu; /* array[pkinds] */ const struct npc_kpu_profile *kpu; /* array[kpus] */ struct npc_mcam_kex *mkex; + struct npc_mcam_kex_hash *mkex_hash; bool custom; size_t pkinds; size_t kpus; @@ -575,6 +579,17 @@ static inline bool is_rvu_otx2(struct rvu *rvu) midr == PCI_REVISION_ID_95XXMM || midr == PCI_REVISION_ID_95XXO); } +static inline bool is_rvu_npc_hash_extract_en(struct rvu *rvu) +{ + u64 npc_const3; + + npc_const3 = rvu_read64(rvu, BLKADDR_NPC, NPC_AF_CONST3); + if (!(npc_const3 & BIT_ULL(62))) + return false; + + return true; +} + static inline u16 rvu_nix_chan_cgx(struct rvu *rvu, u8 cgxid, u8 lmacid, u8 chan) { @@ -754,7 +769,6 @@ u32 convert_dwrr_mtu_to_bytes(u8 dwrr_mtu); u32 convert_bytes_to_dwrr_mtu(u32 bytes); /* NPC APIs */ -int rvu_npc_init(struct rvu *rvu); void rvu_npc_freemem(struct rvu *rvu); int rvu_npc_get_pkind(struct rvu *rvu, u16 pf); void rvu_npc_set_pkind(struct rvu *rvu, int pkind, struct rvu_pfvf *pfvf); @@ -773,14 +787,17 @@ void rvu_npc_install_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf, u64 chan); void rvu_npc_enable_allmulti_entry(struct rvu *rvu, u16 pcifunc, int nixlf, bool enable); + void npc_enadis_default_mce_entry(struct rvu *rvu, u16 pcifunc, int nixlf, int type, bool enable); void rvu_npc_disable_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf); +bool rvu_npc_enable_mcam_by_entry_index(struct rvu *rvu, int entry, int intf, bool enable); void rvu_npc_free_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf); void rvu_npc_disable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf); void rvu_npc_enable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf); void rvu_npc_update_flowkey_alg_idx(struct rvu *rvu, u16 pcifunc, int nixlf, int group, int alg_idx, int mcam_index); + void rvu_npc_get_mcam_entry_alloc_info(struct rvu *rvu, u16 pcifunc, int blkaddr, int *alloc_cnt, int *enable_cnt); @@ -810,11 +827,16 @@ int rvu_cgx_config_tx(void *cgxd, int lmac_id, bool enable); int rvu_cgx_prio_flow_ctrl_cfg(struct rvu *rvu, u16 pcifunc, u8 tx_pause, u8 rx_pause, u16 pfc_en); int rvu_cgx_cfg_pause_frm(struct rvu *rvu, u16 pcifunc, u8 tx_pause, u8 rx_pause); - +u32 rvu_cgx_get_lmac_fifolen(struct rvu *rvu, int cgx, int lmac); int npc_get_nixlf_mcam_index(struct npc_mcam *mcam, u16 pcifunc, int nixlf, int type); bool is_mcam_entry_enabled(struct rvu *rvu, struct npc_mcam *mcam, int blkaddr, int index); +int rvu_npc_init(struct rvu *rvu); +int npc_install_mcam_drop_rule(struct rvu *rvu, int mcam_idx, u16 *counter_idx, + u64 chan_val, u64 chan_mask, u64 exact_val, u64 exact_mask, + u64 bcast_mcast_val, u64 bcast_mcast_mask); +void npc_mcam_rsrcs_reserve(struct rvu *rvu, int blkaddr, int entry_idx); /* CPT APIs */ int rvu_cpt_register_interrupts(struct rvu *rvu); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c index 9ffe99830e34..addc69f4b65c 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c @@ -14,6 +14,7 @@ #include "lmac_common.h" #include "rvu_reg.h" #include "rvu_trace.h" +#include "rvu_npc_hash.h" struct cgx_evq_entry { struct list_head evq_node; @@ -474,6 +475,11 @@ void rvu_cgx_disable_dmac_entries(struct rvu *rvu, u16 pcifunc) if (!is_cgx_config_permitted(rvu, pcifunc)) return; + if (rvu_npc_exact_has_match_table(rvu)) { + rvu_npc_exact_reset(rvu, pcifunc); + return; + } + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); cgx_dev = cgx_get_pdata(cgx_id); lmac_count = cgx_get_lmac_cnt(cgx_dev); @@ -584,6 +590,9 @@ int rvu_mbox_handler_cgx_mac_addr_set(struct rvu *rvu, if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) return -EPERM; + if (rvu_npc_exact_has_match_table(rvu)) + return rvu_npc_exact_mac_addr_set(rvu, req, rsp); + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); cgx_lmac_addr_set(cgx_id, lmac_id, req->mac_addr); @@ -602,6 +611,9 @@ int rvu_mbox_handler_cgx_mac_addr_add(struct rvu *rvu, if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) return -EPERM; + if (rvu_npc_exact_has_match_table(rvu)) + return rvu_npc_exact_mac_addr_add(rvu, req, rsp); + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); rc = cgx_lmac_addr_add(cgx_id, lmac_id, req->mac_addr); if (rc >= 0) { @@ -622,6 +634,9 @@ int rvu_mbox_handler_cgx_mac_addr_del(struct rvu *rvu, if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) return -EPERM; + if (rvu_npc_exact_has_match_table(rvu)) + return rvu_npc_exact_mac_addr_del(rvu, req, rsp); + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); return cgx_lmac_addr_del(cgx_id, lmac_id, req->index); } @@ -643,6 +658,11 @@ int rvu_mbox_handler_cgx_mac_max_entries_get(struct rvu *rvu, return 0; } + if (rvu_npc_exact_has_match_table(rvu)) { + rsp->max_dmac_filters = rvu_npc_exact_get_max_entries(rvu); + return 0; + } + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); rsp->max_dmac_filters = cgx_lmac_addr_max_entries_get(cgx_id, lmac_id); return 0; @@ -680,6 +700,10 @@ int rvu_mbox_handler_cgx_promisc_enable(struct rvu *rvu, struct msg_req *req, if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) return -EPERM; + /* Disable drop on non hit rule */ + if (rvu_npc_exact_has_match_table(rvu)) + return rvu_npc_exact_promisc_enable(rvu, req->hdr.pcifunc); + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); cgx_lmac_promisc_config(cgx_id, lmac_id, true); @@ -695,6 +719,10 @@ int rvu_mbox_handler_cgx_promisc_disable(struct rvu *rvu, struct msg_req *req, if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) return -EPERM; + /* Disable drop on non hit rule */ + if (rvu_npc_exact_has_match_table(rvu)) + return rvu_npc_exact_promisc_disable(rvu, req->hdr.pcifunc); + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); cgx_lmac_promisc_config(cgx_id, lmac_id, false); @@ -833,6 +861,22 @@ u32 rvu_cgx_get_fifolen(struct rvu *rvu) return fifo_len; } +u32 rvu_cgx_get_lmac_fifolen(struct rvu *rvu, int cgx, int lmac) +{ + struct mac_ops *mac_ops; + void *cgxd; + + cgxd = rvu_cgx_pdata(cgx, rvu); + if (!cgxd) + return 0; + + mac_ops = get_mac_ops(cgxd); + if (!mac_ops->lmac_fifo_len) + return 0; + + return mac_ops->lmac_fifo_len(cgxd, lmac); +} + static int rvu_cgx_config_intlbk(struct rvu *rvu, u16 pcifunc, bool en) { int pf = rvu_get_pf(pcifunc); @@ -1059,7 +1103,7 @@ int rvu_mbox_handler_cgx_get_aux_link_info(struct rvu *rvu, struct msg_req *req, u8 cgx_id, lmac_id; if (!rvu->fwdata) - return -ENXIO; + return LMAC_AF_ERR_FIRMWARE_DATA_NOT_MAPPED; if (!is_pf_cgxmapped(rvu, pf)) return -EPERM; @@ -1088,7 +1132,7 @@ int rvu_mbox_handler_cgx_set_link_mode(struct rvu *rvu, return 0; } -int rvu_mbox_handler_cgx_mac_addr_reset(struct rvu *rvu, struct msg_req *req, +int rvu_mbox_handler_cgx_mac_addr_reset(struct rvu *rvu, struct cgx_mac_addr_reset_req *req, struct msg_rsp *rsp) { int pf = rvu_get_pf(req->hdr.pcifunc); @@ -1098,12 +1142,16 @@ int rvu_mbox_handler_cgx_mac_addr_reset(struct rvu *rvu, struct msg_req *req, return LMAC_AF_ERR_PERM_DENIED; rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + + if (rvu_npc_exact_has_match_table(rvu)) + return rvu_npc_exact_mac_addr_reset(rvu, req, rsp); + return cgx_lmac_addr_reset(cgx_id, lmac_id); } int rvu_mbox_handler_cgx_mac_addr_update(struct rvu *rvu, struct cgx_mac_addr_update_req *req, - struct msg_rsp *rsp) + struct cgx_mac_addr_update_rsp *rsp) { int pf = rvu_get_pf(req->hdr.pcifunc); u8 cgx_id, lmac_id; @@ -1111,6 +1159,9 @@ int rvu_mbox_handler_cgx_mac_addr_update(struct rvu *rvu, if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) return LMAC_AF_ERR_PERM_DENIED; + if (rvu_npc_exact_has_match_table(rvu)) + return rvu_npc_exact_mac_addr_update(rvu, req, rsp); + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); return cgx_lmac_addr_update(cgx_id, lmac_id, req->mac_addr, req->index); } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cpt.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cpt.c index a9da85e418a4..38bbae5d9ae0 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cpt.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cpt.c @@ -17,7 +17,7 @@ #define PCI_DEVID_OTX2_CPT10K_PF 0xA0F2 /* Length of initial context fetch in 128 byte words */ -#define CPT_CTX_ILEN 2 +#define CPT_CTX_ILEN 2ULL #define cpt_get_eng_sts(e_min, e_max, rsp, etype) \ ({ \ @@ -480,7 +480,7 @@ static int cpt_inline_ipsec_cfg_inbound(struct rvu *rvu, int blkaddr, u8 cptlf, */ if (!is_rvu_otx2(rvu)) { val = (ilog2(NIX_CHAN_CPT_X2P_MASK + 1) << 16); - val |= rvu->hw->cpt_chan_base; + val |= (u64)rvu->hw->cpt_chan_base; rvu_write64(rvu, blkaddr, CPT_AF_X2PX_LINK_CFG(0), val); rvu_write64(rvu, blkaddr, CPT_AF_X2PX_LINK_CFG(1), val); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c index 2ad73b180276..f42a09f04b25 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c @@ -18,6 +18,7 @@ #include "cgx.h" #include "lmac_common.h" #include "npc.h" +#include "rvu_npc_hash.h" #define DEBUGFS_DIR_NAME "octeontx2" @@ -2600,6 +2601,170 @@ static int rvu_dbg_npc_mcam_show_rules(struct seq_file *s, void *unused) RVU_DEBUG_SEQ_FOPS(npc_mcam_rules, npc_mcam_show_rules, NULL); +static int rvu_dbg_npc_exact_show_entries(struct seq_file *s, void *unused) +{ + struct npc_exact_table_entry *mem_entry[NPC_EXACT_TBL_MAX_WAYS] = { 0 }; + struct npc_exact_table_entry *cam_entry; + struct npc_exact_table *table; + struct rvu *rvu = s->private; + int i, j; + + u8 bitmap = 0; + + table = rvu->hw->table; + + mutex_lock(&table->lock); + + /* Check if there is at least one entry in mem table */ + if (!table->mem_tbl_entry_cnt) + goto dump_cam_table; + + /* Print table headers */ + seq_puts(s, "\n\tExact Match MEM Table\n"); + seq_puts(s, "Index\t"); + + for (i = 0; i < table->mem_table.ways; i++) { + mem_entry[i] = list_first_entry_or_null(&table->lhead_mem_tbl_entry[i], + struct npc_exact_table_entry, list); + + seq_printf(s, "Way-%d\t\t\t\t\t", i); + } + + seq_puts(s, "\n"); + for (i = 0; i < table->mem_table.ways; i++) + seq_puts(s, "\tChan MAC \t"); + + seq_puts(s, "\n\n"); + + /* Print mem table entries */ + for (i = 0; i < table->mem_table.depth; i++) { + bitmap = 0; + for (j = 0; j < table->mem_table.ways; j++) { + if (!mem_entry[j]) + continue; + + if (mem_entry[j]->index != i) + continue; + + bitmap |= BIT(j); + } + + /* No valid entries */ + if (!bitmap) + continue; + + seq_printf(s, "%d\t", i); + for (j = 0; j < table->mem_table.ways; j++) { + if (!(bitmap & BIT(j))) { + seq_puts(s, "nil\t\t\t\t\t"); + continue; + } + + seq_printf(s, "0x%x %pM\t\t\t", mem_entry[j]->chan, + mem_entry[j]->mac); + mem_entry[j] = list_next_entry(mem_entry[j], list); + } + seq_puts(s, "\n"); + } + +dump_cam_table: + + if (!table->cam_tbl_entry_cnt) + goto done; + + seq_puts(s, "\n\tExact Match CAM Table\n"); + seq_puts(s, "index\tchan\tMAC\n"); + + /* Traverse cam table entries */ + list_for_each_entry(cam_entry, &table->lhead_cam_tbl_entry, list) { + seq_printf(s, "%d\t0x%x\t%pM\n", cam_entry->index, cam_entry->chan, + cam_entry->mac); + } + +done: + mutex_unlock(&table->lock); + return 0; +} + +RVU_DEBUG_SEQ_FOPS(npc_exact_entries, npc_exact_show_entries, NULL); + +static int rvu_dbg_npc_exact_show_info(struct seq_file *s, void *unused) +{ + struct npc_exact_table *table; + struct rvu *rvu = s->private; + int i; + + table = rvu->hw->table; + + seq_puts(s, "\n\tExact Table Info\n"); + seq_printf(s, "Exact Match Feature : %s\n", + rvu->hw->cap.npc_exact_match_enabled ? "enabled" : "disable"); + if (!rvu->hw->cap.npc_exact_match_enabled) + return 0; + + seq_puts(s, "\nMCAM Index\tMAC Filter Rules Count\n"); + for (i = 0; i < table->num_drop_rules; i++) + seq_printf(s, "%d\t\t%d\n", i, table->cnt_cmd_rules[i]); + + seq_puts(s, "\nMcam Index\tPromisc Mode Status\n"); + for (i = 0; i < table->num_drop_rules; i++) + seq_printf(s, "%d\t\t%s\n", i, table->promisc_mode[i] ? "on" : "off"); + + seq_puts(s, "\n\tMEM Table Info\n"); + seq_printf(s, "Ways : %d\n", table->mem_table.ways); + seq_printf(s, "Depth : %d\n", table->mem_table.depth); + seq_printf(s, "Mask : 0x%llx\n", table->mem_table.mask); + seq_printf(s, "Hash Mask : 0x%x\n", table->mem_table.hash_mask); + seq_printf(s, "Hash Offset : 0x%x\n", table->mem_table.hash_offset); + + seq_puts(s, "\n\tCAM Table Info\n"); + seq_printf(s, "Depth : %d\n", table->cam_table.depth); + + return 0; +} + +RVU_DEBUG_SEQ_FOPS(npc_exact_info, npc_exact_show_info, NULL); + +static int rvu_dbg_npc_exact_drop_cnt(struct seq_file *s, void *unused) +{ + struct npc_exact_table *table; + struct rvu *rvu = s->private; + struct npc_key_field *field; + u16 chan, pcifunc; + int blkaddr, i; + u64 cfg, cam1; + char *str; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + table = rvu->hw->table; + + field = &rvu->hw->mcam.rx_key_fields[NPC_CHAN]; + + seq_puts(s, "\n\t Exact Hit on drop status\n"); + seq_puts(s, "\npcifunc\tmcam_idx\tHits\tchan\tstatus\n"); + + for (i = 0; i < table->num_drop_rules; i++) { + pcifunc = rvu_npc_exact_drop_rule_to_pcifunc(rvu, i); + cfg = rvu_read64(rvu, blkaddr, NPC_AF_MCAMEX_BANKX_CFG(i, 0)); + + /* channel will be always in keyword 0 */ + cam1 = rvu_read64(rvu, blkaddr, + NPC_AF_MCAMEX_BANKX_CAMX_W0(i, 0, 1)); + chan = field->kw_mask[0] & cam1; + + str = (cfg & 1) ? "enabled" : "disabled"; + + seq_printf(s, "0x%x\t%d\t\t%llu\t0x%x\t%s\n", pcifunc, i, + rvu_read64(rvu, blkaddr, + NPC_AF_MATCH_STATX(table->counter_idx[i])), + chan, str); + } + + return 0; +} + +RVU_DEBUG_SEQ_FOPS(npc_exact_drop_cnt, npc_exact_drop_cnt, NULL); + static void rvu_dbg_npc_init(struct rvu *rvu) { rvu->rvu_dbg.npc = debugfs_create_dir("npc", rvu->rvu_dbg.root); @@ -2608,8 +2773,22 @@ static void rvu_dbg_npc_init(struct rvu *rvu) &rvu_dbg_npc_mcam_info_fops); debugfs_create_file("mcam_rules", 0444, rvu->rvu_dbg.npc, rvu, &rvu_dbg_npc_mcam_rules_fops); + debugfs_create_file("rx_miss_act_stats", 0444, rvu->rvu_dbg.npc, rvu, &rvu_dbg_npc_rx_miss_act_fops); + + if (!rvu->hw->cap.npc_exact_match_enabled) + return; + + debugfs_create_file("exact_entries", 0444, rvu->rvu_dbg.npc, rvu, + &rvu_dbg_npc_exact_entries_fops); + + debugfs_create_file("exact_info", 0444, rvu->rvu_dbg.npc, rvu, + &rvu_dbg_npc_exact_info_fops); + + debugfs_create_file("exact_drop_cnt", 0444, rvu->rvu_dbg.npc, rvu, + &rvu_dbg_npc_exact_drop_cnt_fops); + } static int cpt_eng_sts_display(struct seq_file *filp, u8 eng_type) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c index d0ab8f233a02..88dee589cb21 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_devlink.c @@ -10,6 +10,7 @@ #include "rvu.h" #include "rvu_reg.h" #include "rvu_struct.h" +#include "rvu_npc_hash.h" #define DRV_NAME "octeontx2-af" @@ -1436,14 +1437,75 @@ static int rvu_af_dl_dwrr_mtu_get(struct devlink *devlink, u32 id, enum rvu_af_dl_param_id { RVU_AF_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, RVU_AF_DEVLINK_PARAM_ID_DWRR_MTU, + RVU_AF_DEVLINK_PARAM_ID_NPC_EXACT_FEATURE_DISABLE, }; +static int rvu_af_npc_exact_feature_get(struct devlink *devlink, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + struct rvu_devlink *rvu_dl = devlink_priv(devlink); + struct rvu *rvu = rvu_dl->rvu; + bool enabled; + + enabled = rvu_npc_exact_has_match_table(rvu); + + snprintf(ctx->val.vstr, sizeof(ctx->val.vstr), "%s", + enabled ? "enabled" : "disabled"); + + return 0; +} + +static int rvu_af_npc_exact_feature_disable(struct devlink *devlink, u32 id, + struct devlink_param_gset_ctx *ctx) +{ + struct rvu_devlink *rvu_dl = devlink_priv(devlink); + struct rvu *rvu = rvu_dl->rvu; + + rvu_npc_exact_disable_feature(rvu); + + return 0; +} + +static int rvu_af_npc_exact_feature_validate(struct devlink *devlink, u32 id, + union devlink_param_value val, + struct netlink_ext_ack *extack) +{ + struct rvu_devlink *rvu_dl = devlink_priv(devlink); + struct rvu *rvu = rvu_dl->rvu; + u64 enable; + + if (kstrtoull(val.vstr, 10, &enable)) { + NL_SET_ERR_MSG_MOD(extack, + "Only 1 value is supported"); + return -EINVAL; + } + + if (enable != 1) { + NL_SET_ERR_MSG_MOD(extack, + "Only disabling exact match feature is supported"); + return -EINVAL; + } + + if (rvu_npc_exact_can_disable_feature(rvu)) + return 0; + + NL_SET_ERR_MSG_MOD(extack, + "Can't disable exact match feature; Please try before any configuration"); + return -EFAULT; +} + static const struct devlink_param rvu_af_dl_params[] = { DEVLINK_PARAM_DRIVER(RVU_AF_DEVLINK_PARAM_ID_DWRR_MTU, "dwrr_mtu", DEVLINK_PARAM_TYPE_U32, BIT(DEVLINK_PARAM_CMODE_RUNTIME), rvu_af_dl_dwrr_mtu_get, rvu_af_dl_dwrr_mtu_set, rvu_af_dl_dwrr_mtu_validate), + DEVLINK_PARAM_DRIVER(RVU_AF_DEVLINK_PARAM_ID_NPC_EXACT_FEATURE_DISABLE, + "npc_exact_feature_disable", DEVLINK_PARAM_TYPE_STRING, + BIT(DEVLINK_PARAM_CMODE_RUNTIME), + rvu_af_npc_exact_feature_get, + rvu_af_npc_exact_feature_disable, + rvu_af_npc_exact_feature_validate), }; /* Devlink switch mode */ @@ -1501,6 +1563,7 @@ int rvu_register_dl(struct rvu *rvu) { struct rvu_devlink *rvu_dl; struct devlink *dl; + size_t size; int err; dl = devlink_alloc(&rvu_devlink_ops, sizeof(struct rvu_devlink), @@ -1522,8 +1585,12 @@ int rvu_register_dl(struct rvu *rvu) goto err_dl_health; } - err = devlink_params_register(dl, rvu_af_dl_params, - ARRAY_SIZE(rvu_af_dl_params)); + /* Register exact match devlink only for CN10K-B */ + size = ARRAY_SIZE(rvu_af_dl_params); + if (!rvu_npc_exact_has_match_table(rvu)) + size -= 1; + + err = devlink_params_register(dl, rvu_af_dl_params, size); if (err) { dev_err(rvu->dev, "devlink params register failed with error %d", err); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 0fa625e2528e..0879a48411f3 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -14,6 +14,7 @@ #include "npc.h" #include "cgx.h" #include "lmac_common.h" +#include "rvu_npc_hash.h" static void nix_free_tx_vtag_entries(struct rvu *rvu, u16 pcifunc); static int rvu_nix_get_bpid(struct rvu *rvu, struct nix_bp_cfg_req *req, @@ -3792,9 +3793,15 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, rvu_npc_install_promisc_entry(rvu, pcifunc, nixlf, pfvf->rx_chan_base, pfvf->rx_chan_cnt); + + if (rvu_npc_exact_has_match_table(rvu)) + rvu_npc_exact_promisc_enable(rvu, pcifunc); } else { if (!nix_rx_multicast) rvu_npc_enable_promisc_entry(rvu, pcifunc, nixlf, false); + + if (rvu_npc_exact_has_match_table(rvu)) + rvu_npc_exact_promisc_disable(rvu, pcifunc); } return 0; @@ -4003,9 +4010,13 @@ linkcfg: return 0; /* Update transmit credits for CGX links */ - lmac_fifo_len = - rvu_cgx_get_fifolen(rvu) / - cgx_get_lmac_cnt(rvu_cgx_pdata(cgx, rvu)); + lmac_fifo_len = rvu_cgx_get_lmac_fifolen(rvu, cgx, lmac); + if (!lmac_fifo_len) { + dev_err(rvu->dev, + "%s: Failed to get CGX/RPM%d:LMAC%d FIFO size\n", + __func__, cgx, lmac); + return 0; + } return nix_config_link_credits(rvu, blkaddr, link, pcifunc, (lmac_fifo_len - req->maxlen) / 16); } @@ -4057,7 +4068,10 @@ static void nix_link_config(struct rvu *rvu, int blkaddr, struct rvu_hwinfo *hw = rvu->hw; int cgx, lmac_cnt, slink, link; u16 lbk_max_frs, lmac_max_frs; + unsigned long lmac_bmap; u64 tx_credits, cfg; + u64 lmac_fifo_len; + int iter; rvu_get_lbk_link_max_frs(rvu, &lbk_max_frs); rvu_get_lmac_link_max_frs(rvu, &lmac_max_frs); @@ -4091,12 +4105,23 @@ static void nix_link_config(struct rvu *rvu, int blkaddr, /* Skip when cgx is not available or lmac cnt is zero */ if (lmac_cnt <= 0) continue; - tx_credits = ((rvu_cgx_get_fifolen(rvu) / lmac_cnt) - - lmac_max_frs) / 16; - /* Enable credits and set credit pkt count to max allowed */ - cfg = (tx_credits << 12) | (0x1FF << 2) | BIT_ULL(1); slink = cgx * hw->lmac_per_cgx; - for (link = slink; link < (slink + lmac_cnt); link++) { + + /* Get LMAC id's from bitmap */ + lmac_bmap = cgx_get_lmac_bmap(rvu_cgx_pdata(cgx, rvu)); + for_each_set_bit(iter, &lmac_bmap, MAX_LMAC_PER_CGX) { + lmac_fifo_len = rvu_cgx_get_lmac_fifolen(rvu, cgx, iter); + if (!lmac_fifo_len) { + dev_err(rvu->dev, + "%s: Failed to get CGX/RPM%d:LMAC%d FIFO size\n", + __func__, cgx, iter); + continue; + } + tx_credits = (lmac_fifo_len - lmac_max_frs) / 16; + /* Enable credits and set credit pkt count to max allowed */ + cfg = (tx_credits << 12) | (0x1FF << 2) | BIT_ULL(1); + + link = iter + slink; nix_hw->tx_credits[link] = tx_credits; rvu_write64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link), cfg); diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c index 3a31fb8cc155..583ead4dd246 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c @@ -15,6 +15,7 @@ #include "npc.h" #include "cgx.h" #include "npc_profile.h" +#include "rvu_npc_hash.h" #define RSVD_MCAM_ENTRIES_PER_PF 3 /* Broadcast, Promisc and AllMulticast */ #define RSVD_MCAM_ENTRIES_PER_NIXLF 1 /* Ucast for LFs */ @@ -1105,6 +1106,34 @@ void rvu_npc_disable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf) NIXLF_PROMISC_ENTRY, false); } +bool rvu_npc_enable_mcam_by_entry_index(struct rvu *rvu, int entry, int intf, bool enable) +{ + int blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + struct npc_mcam *mcam = &rvu->hw->mcam; + struct rvu_npc_mcam_rule *rule, *tmp; + + mutex_lock(&mcam->lock); + + list_for_each_entry_safe(rule, tmp, &mcam->mcam_rules, list) { + if (rule->intf != intf) + continue; + + if (rule->entry != entry) + continue; + + rule->enable = enable; + mutex_unlock(&mcam->lock); + + npc_enable_mcam_entry(rvu, mcam, blkaddr, + entry, enable); + + return true; + } + + mutex_unlock(&mcam->lock); + return false; +} + void rvu_npc_enable_default_entries(struct rvu *rvu, u16 pcifunc, int nixlf) { /* Enables only broadcast match entry. Promisc/Allmulti are enabled @@ -1181,14 +1210,6 @@ void rvu_npc_free_mcam_entries(struct rvu *rvu, u16 pcifunc, int nixlf) rvu_npc_disable_default_entries(rvu, pcifunc, nixlf); } -#define SET_KEX_LD(intf, lid, ltype, ld, cfg) \ - rvu_write64(rvu, blkaddr, \ - NPC_AF_INTFX_LIDX_LTX_LDX_CFG(intf, lid, ltype, ld), cfg) - -#define SET_KEX_LDFLAGS(intf, ld, flags, cfg) \ - rvu_write64(rvu, blkaddr, \ - NPC_AF_INTFX_LDATAX_FLAGSX_CFG(intf, ld, flags), cfg) - static void npc_program_mkex_rx(struct rvu *rvu, int blkaddr, struct npc_mcam_kex *mkex, u8 intf) { @@ -1262,6 +1283,9 @@ static void npc_program_mkex_profile(struct rvu *rvu, int blkaddr, npc_program_mkex_rx(rvu, blkaddr, mkex, intf); npc_program_mkex_tx(rvu, blkaddr, mkex, intf); } + + /* Programme mkex hash profile */ + npc_program_mkex_hash(rvu, blkaddr); } static int npc_fwdb_prfl_img_map(struct rvu *rvu, void __iomem **prfl_img_addr, @@ -1463,6 +1487,7 @@ static int npc_prepare_default_kpu(struct npc_kpu_profile_adapter *profile) profile->kpus = ARRAY_SIZE(npc_kpu_profiles); profile->lt_def = &npc_lt_defaults; profile->mkex = &npc_mkex_default; + profile->mkex_hash = &npc_mkex_hash_default; return 0; } @@ -1819,7 +1844,6 @@ static int npc_mcam_rsrcs_init(struct rvu *rvu, int blkaddr) mcam->hprio_count = mcam->lprio_count; mcam->hprio_end = mcam->hprio_count; - /* Allocate bitmap for managing MCAM counters and memory * for saving counter to RVU PFFUNC allocation mapping. */ @@ -2047,6 +2071,7 @@ int rvu_npc_init(struct rvu *rvu) rvu_npc_setup_interfaces(rvu, blkaddr); + npc_config_secret_key(rvu, blkaddr); /* Configure MKEX profile */ npc_load_mkex_profile(rvu, blkaddr, rvu->mkex_pfl_name); @@ -2534,7 +2559,7 @@ alloc: /* Copy MCAM entry indices into mbox response entry_list. * Requester always expects indices in ascending order, so - * so reverse the list if reverse bitmap is used for allocation. + * reverse the list if reverse bitmap is used for allocation. */ if (!req->contig && rsp->count) { index = 0; @@ -2562,6 +2587,14 @@ alloc: return 0; } +/* Marks bitmaps to reserved the mcam slot */ +void npc_mcam_rsrcs_reserve(struct rvu *rvu, int blkaddr, int entry_idx) +{ + struct npc_mcam *mcam = &rvu->hw->mcam; + + npc_mcam_set_bit(mcam, entry_idx); +} + int rvu_mbox_handler_npc_mcam_alloc_entry(struct rvu *rvu, struct npc_mcam_alloc_entry_req *req, struct npc_mcam_alloc_entry_rsp *rsp) diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c index 19c53e591d0d..a400aa22da79 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.c @@ -10,6 +10,8 @@ #include "rvu_reg.h" #include "rvu.h" #include "npc.h" +#include "rvu_npc_fs.h" +#include "rvu_npc_hash.h" #define NPC_BYTESM GENMASK_ULL(19, 16) #define NPC_HDR_OFFSET GENMASK_ULL(15, 8) @@ -227,6 +229,25 @@ static bool npc_check_field(struct rvu *rvu, int blkaddr, enum key_fields type, return true; } +static void npc_scan_exact_result(struct npc_mcam *mcam, u8 bit_number, + u8 key_nibble, u8 intf) +{ + u8 offset = (key_nibble * 4) % 64; /* offset within key word */ + u8 kwi = (key_nibble * 4) / 64; /* which word in key */ + u8 nr_bits = 4; /* bits in a nibble */ + u8 type; + + switch (bit_number) { + case 40 ... 43: + type = NPC_EXACT_RESULT; + break; + + default: + return; + } + npc_set_kw_masks(mcam, type, nr_bits, kwi, offset, intf); +} + static void npc_scan_parse_result(struct npc_mcam *mcam, u8 bit_number, u8 key_nibble, u8 intf) { @@ -276,6 +297,7 @@ static void npc_scan_parse_result(struct npc_mcam *mcam, u8 bit_number, default: return; } + npc_set_kw_masks(mcam, type, nr_bits, kwi, offset, intf); } @@ -509,8 +531,8 @@ static int npc_scan_kex(struct rvu *rvu, int blkaddr, u8 intf) { struct npc_mcam *mcam = &rvu->hw->mcam; u8 lid, lt, ld, bitnr; + u64 cfg, masked_cfg; u8 key_nibble = 0; - u64 cfg; /* Scan and note how parse result is going to be in key. * A bit set in PARSE_NIBBLE_ENA corresponds to a nibble from @@ -518,12 +540,24 @@ static int npc_scan_kex(struct rvu *rvu, int blkaddr, u8 intf) * will be concatenated in key. */ cfg = rvu_read64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(intf)); - cfg &= NPC_PARSE_NIBBLE; - for_each_set_bit(bitnr, (unsigned long *)&cfg, 31) { + masked_cfg = cfg & NPC_PARSE_NIBBLE; + for_each_set_bit(bitnr, (unsigned long *)&masked_cfg, 31) { npc_scan_parse_result(mcam, bitnr, key_nibble, intf); key_nibble++; } + /* Ignore exact match bits for mcam entries except the first rule + * which is drop on hit. This first rule is configured explitcitly by + * exact match code. + */ + masked_cfg = cfg & NPC_EXACT_NIBBLE; + bitnr = NPC_EXACT_NIBBLE_START; + for_each_set_bit_from(bitnr, (unsigned long *)&masked_cfg, + NPC_EXACT_NIBBLE_START) { + npc_scan_exact_result(mcam, bitnr, key_nibble, intf); + key_nibble++; + } + /* Scan and note how layer data is going to be in key */ for (lid = 0; lid < NPC_MAX_LID; lid++) { for (lt = 0; lt < NPC_MAX_LT; lt++) { @@ -624,9 +658,9 @@ static int npc_check_unsupported_flows(struct rvu *rvu, u64 features, u8 intf) * If any bits in mask are 0 then corresponding bits in value are * dont care. */ -static void npc_update_entry(struct rvu *rvu, enum key_fields type, - struct mcam_entry *entry, u64 val_lo, - u64 val_hi, u64 mask_lo, u64 mask_hi, u8 intf) +void npc_update_entry(struct rvu *rvu, enum key_fields type, + struct mcam_entry *entry, u64 val_lo, + u64 val_hi, u64 mask_lo, u64 mask_hi, u8 intf) { struct npc_mcam *mcam = &rvu->hw->mcam; struct mcam_entry dummy = { {0} }; @@ -705,8 +739,6 @@ static void npc_update_entry(struct rvu *rvu, enum key_fields type, } } -#define IPV6_WORDS 4 - static void npc_update_ipv6_flow(struct rvu *rvu, struct mcam_entry *entry, u64 features, struct flow_msg *pkt, struct flow_msg *mask, @@ -779,7 +811,8 @@ static void npc_update_vlan_features(struct rvu *rvu, struct mcam_entry *entry, static void npc_update_flow(struct rvu *rvu, struct mcam_entry *entry, u64 features, struct flow_msg *pkt, struct flow_msg *mask, - struct rvu_npc_mcam_rule *output, u8 intf) + struct rvu_npc_mcam_rule *output, u8 intf, + int blkaddr) { u64 dmac_mask = ether_addr_to_u64(mask->dmac); u64 smac_mask = ether_addr_to_u64(mask->smac); @@ -828,6 +861,7 @@ do { \ } while (0) NPC_WRITE_FLOW(NPC_DMAC, dmac, dmac_val, 0, dmac_mask, 0); + NPC_WRITE_FLOW(NPC_SMAC, smac, smac_val, 0, smac_mask, 0); NPC_WRITE_FLOW(NPC_ETYPE, etype, ntohs(pkt->etype), 0, ntohs(mask->etype), 0); @@ -854,10 +888,12 @@ do { \ npc_update_ipv6_flow(rvu, entry, features, pkt, mask, output, intf); npc_update_vlan_features(rvu, entry, features, intf); + + npc_update_field_hash(rvu, intf, entry, blkaddr, features, + pkt, mask, opkt, omask); } -static struct rvu_npc_mcam_rule *rvu_mcam_find_rule(struct npc_mcam *mcam, - u16 entry) +static struct rvu_npc_mcam_rule *rvu_mcam_find_rule(struct npc_mcam *mcam, u16 entry) { struct rvu_npc_mcam_rule *iter; @@ -1023,8 +1059,9 @@ static int npc_install_flow(struct rvu *rvu, int blkaddr, u16 target, u16 owner = req->hdr.pcifunc; struct msg_rsp write_rsp; struct mcam_entry *entry; - int entry_index, err; bool new = false; + u16 entry_index; + int err; installed_features = req->features; features = req->features; @@ -1032,7 +1069,7 @@ static int npc_install_flow(struct rvu *rvu, int blkaddr, u16 target, entry_index = req->entry; npc_update_flow(rvu, entry, features, &req->packet, &req->mask, &dummy, - req->intf); + req->intf, blkaddr); if (is_npc_intf_rx(req->intf)) npc_update_rx_entry(rvu, pfvf, entry, req, target, pf_set_vfs_mac); @@ -1057,7 +1094,8 @@ static int npc_install_flow(struct rvu *rvu, int blkaddr, u16 target, npc_update_flow(rvu, entry, missing_features, &def_ucast_rule->packet, &def_ucast_rule->mask, - &dummy, req->intf); + &dummy, req->intf, + blkaddr); installed_features = req->features | missing_features; } @@ -1424,3 +1462,98 @@ void npc_mcam_disable_flows(struct rvu *rvu, u16 target) } mutex_unlock(&mcam->lock); } + +/* single drop on non hit rule starting from 0th index. This an extension + * to RPM mac filter to support more rules. + */ +int npc_install_mcam_drop_rule(struct rvu *rvu, int mcam_idx, u16 *counter_idx, + u64 chan_val, u64 chan_mask, u64 exact_val, u64 exact_mask, + u64 bcast_mcast_val, u64 bcast_mcast_mask) +{ + struct npc_mcam_alloc_counter_req cntr_req = { 0 }; + struct npc_mcam_alloc_counter_rsp cntr_rsp = { 0 }; + struct npc_mcam_write_entry_req req = { 0 }; + struct npc_mcam *mcam = &rvu->hw->mcam; + struct rvu_npc_mcam_rule *rule; + struct msg_rsp rsp; + bool enabled; + int blkaddr; + int err; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) { + dev_err(rvu->dev, "%s: NPC block not implemented\n", __func__); + return -ENODEV; + } + + /* Bail out if no exact match support */ + if (!rvu_npc_exact_has_match_table(rvu)) { + dev_info(rvu->dev, "%s: No support for exact match feature\n", __func__); + return -EINVAL; + } + + /* If 0th entry is already used, return err */ + enabled = is_mcam_entry_enabled(rvu, mcam, blkaddr, mcam_idx); + if (enabled) { + dev_err(rvu->dev, "%s: failed to add single drop on non hit rule at %d th index\n", + __func__, mcam_idx); + return -EINVAL; + } + + /* Add this entry to mcam rules list */ + rule = kzalloc(sizeof(*rule), GFP_KERNEL); + if (!rule) + return -ENOMEM; + + /* Disable rule by default. Enable rule when first dmac filter is + * installed + */ + rule->enable = false; + rule->chan = chan_val; + rule->chan_mask = chan_mask; + rule->entry = mcam_idx; + rvu_mcam_add_rule(mcam, rule); + + /* Reserve slot 0 */ + npc_mcam_rsrcs_reserve(rvu, blkaddr, mcam_idx); + + /* Allocate counter for this single drop on non hit rule */ + cntr_req.hdr.pcifunc = 0; /* AF request */ + cntr_req.contig = true; + cntr_req.count = 1; + err = rvu_mbox_handler_npc_mcam_alloc_counter(rvu, &cntr_req, &cntr_rsp); + if (err) { + dev_err(rvu->dev, "%s: Err to allocate cntr for drop rule (err=%d)\n", + __func__, err); + return -EFAULT; + } + *counter_idx = cntr_rsp.cntr; + + /* Fill in fields for this mcam entry */ + npc_update_entry(rvu, NPC_EXACT_RESULT, &req.entry_data, exact_val, 0, + exact_mask, 0, NIX_INTF_RX); + npc_update_entry(rvu, NPC_CHAN, &req.entry_data, chan_val, 0, + chan_mask, 0, NIX_INTF_RX); + npc_update_entry(rvu, NPC_LXMB, &req.entry_data, bcast_mcast_val, 0, + bcast_mcast_mask, 0, NIX_INTF_RX); + + req.intf = NIX_INTF_RX; + req.set_cntr = true; + req.cntr = cntr_rsp.cntr; + req.entry = mcam_idx; + + err = rvu_mbox_handler_npc_mcam_write_entry(rvu, &req, &rsp); + if (err) { + dev_err(rvu->dev, "%s: Installation of single drop on non hit rule at %d failed\n", + __func__, mcam_idx); + return err; + } + + dev_err(rvu->dev, "%s: Installed single drop on non hit rule at %d, cntr=%d\n", + __func__, mcam_idx, req.cntr); + + /* disable entry at Bank 0, index 0 */ + npc_enable_mcam_entry(rvu, mcam, blkaddr, mcam_idx, false); + + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.h new file mode 100644 index 000000000000..bdd65ce56a32 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_fs.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell RVU Admin Function driver + * + * Copyright (C) 2022 Marvell. + * + */ + +#ifndef __RVU_NPC_FS_H +#define __RVU_NPC_FS_H + +#define IPV6_WORDS 4 + +void npc_update_entry(struct rvu *rvu, enum key_fields type, + struct mcam_entry *entry, u64 val_lo, + u64 val_hi, u64 mask_lo, u64 mask_hi, u8 intf); + +#endif /* RVU_NPC_FS_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c new file mode 100644 index 000000000000..594029007f85 --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c @@ -0,0 +1,2009 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Marvell RVU Admin Function driver + * + * Copyright (C) 2022 Marvell. + * + */ + +#include <linux/bitfield.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/firmware.h> +#include <linux/stddef.h> +#include <linux/debugfs.h> + +#include "rvu_struct.h" +#include "rvu_reg.h" +#include "rvu.h" +#include "npc.h" +#include "cgx.h" +#include "rvu_npc_fs.h" +#include "rvu_npc_hash.h" + +static u64 rvu_npc_wide_extract(const u64 input[], size_t start_bit, + size_t width_bits) +{ + const u64 mask = ~(u64)((~(__uint128_t)0) << width_bits); + const size_t msb = start_bit + width_bits - 1; + const size_t lword = start_bit >> 6; + const size_t uword = msb >> 6; + size_t lbits; + u64 hi, lo; + + if (lword == uword) + return (input[lword] >> (start_bit & 63)) & mask; + + lbits = 64 - (start_bit & 63); + hi = input[uword]; + lo = (input[lword] >> (start_bit & 63)); + return ((hi << lbits) | lo) & mask; +} + +static void rvu_npc_lshift_key(u64 *key, size_t key_bit_len) +{ + u64 prev_orig_word = 0; + u64 cur_orig_word = 0; + size_t extra = key_bit_len % 64; + size_t max_idx = key_bit_len / 64; + size_t i; + + if (extra) + max_idx++; + + for (i = 0; i < max_idx; i++) { + cur_orig_word = key[i]; + key[i] = key[i] << 1; + key[i] |= ((prev_orig_word >> 63) & 0x1); + prev_orig_word = cur_orig_word; + } +} + +static u32 rvu_npc_toeplitz_hash(const u64 *data, u64 *key, size_t data_bit_len, + size_t key_bit_len) +{ + u32 hash_out = 0; + u64 temp_data = 0; + int i; + + for (i = data_bit_len - 1; i >= 0; i--) { + temp_data = (data[i / 64]); + temp_data = temp_data >> (i % 64); + temp_data &= 0x1; + if (temp_data) + hash_out ^= (u32)(rvu_npc_wide_extract(key, key_bit_len - 32, 32)); + + rvu_npc_lshift_key(key, key_bit_len); + } + + return hash_out; +} + +u32 npc_field_hash_calc(u64 *ldata, struct npc_mcam_kex_hash *mkex_hash, + u64 *secret_key, u8 intf, u8 hash_idx) +{ + u64 hash_key[3]; + u64 data_padded[2]; + u32 field_hash; + + hash_key[0] = secret_key[1] << 31; + hash_key[0] |= secret_key[2]; + hash_key[1] = secret_key[1] >> 33; + hash_key[1] |= secret_key[0] << 31; + hash_key[2] = secret_key[0] >> 33; + + data_padded[0] = mkex_hash->hash_mask[intf][hash_idx][0] & ldata[0]; + data_padded[1] = mkex_hash->hash_mask[intf][hash_idx][1] & ldata[1]; + field_hash = rvu_npc_toeplitz_hash(data_padded, hash_key, 128, 159); + + field_hash &= mkex_hash->hash_ctrl[intf][hash_idx] >> 32; + field_hash |= mkex_hash->hash_ctrl[intf][hash_idx]; + return field_hash; +} + +static u64 npc_update_use_hash(int lt, int ld) +{ + u64 cfg = 0; + + switch (lt) { + case NPC_LT_LC_IP6: + /* Update use_hash(bit-20) and bytesm1 (bit-16:19) + * in KEX_LD_CFG + */ + cfg = KEX_LD_CFG_USE_HASH(0x1, 0x03, + ld ? 0x8 : 0x18, + 0x1, 0x0, 0x10); + break; + } + + return cfg; +} + +static void npc_program_mkex_hash_rx(struct rvu *rvu, int blkaddr, + u8 intf) +{ + struct npc_mcam_kex_hash *mkex_hash = rvu->kpu.mkex_hash; + int lid, lt, ld, hash_cnt = 0; + + if (is_npc_intf_tx(intf)) + return; + + /* Program HASH_CFG */ + for (lid = 0; lid < NPC_MAX_LID; lid++) { + for (lt = 0; lt < NPC_MAX_LT; lt++) { + for (ld = 0; ld < NPC_MAX_LD; ld++) { + if (mkex_hash->lid_lt_ld_hash_en[intf][lid][lt][ld]) { + u64 cfg = npc_update_use_hash(lt, ld); + + hash_cnt++; + if (hash_cnt == NPC_MAX_HASH) + return; + + /* Set updated KEX configuration */ + SET_KEX_LD(intf, lid, lt, ld, cfg); + /* Set HASH configuration */ + SET_KEX_LD_HASH(intf, ld, + mkex_hash->hash[intf][ld]); + SET_KEX_LD_HASH_MASK(intf, ld, 0, + mkex_hash->hash_mask[intf][ld][0]); + SET_KEX_LD_HASH_MASK(intf, ld, 1, + mkex_hash->hash_mask[intf][ld][1]); + SET_KEX_LD_HASH_CTRL(intf, ld, + mkex_hash->hash_ctrl[intf][ld]); + } + } + } + } +} + +static void npc_program_mkex_hash_tx(struct rvu *rvu, int blkaddr, + u8 intf) +{ + struct npc_mcam_kex_hash *mkex_hash = rvu->kpu.mkex_hash; + int lid, lt, ld, hash_cnt = 0; + + if (is_npc_intf_rx(intf)) + return; + + /* Program HASH_CFG */ + for (lid = 0; lid < NPC_MAX_LID; lid++) { + for (lt = 0; lt < NPC_MAX_LT; lt++) { + for (ld = 0; ld < NPC_MAX_LD; ld++) + if (mkex_hash->lid_lt_ld_hash_en[intf][lid][lt][ld]) { + u64 cfg = npc_update_use_hash(lt, ld); + + hash_cnt++; + if (hash_cnt == NPC_MAX_HASH) + return; + + /* Set updated KEX configuration */ + SET_KEX_LD(intf, lid, lt, ld, cfg); + /* Set HASH configuration */ + SET_KEX_LD_HASH(intf, ld, + mkex_hash->hash[intf][ld]); + SET_KEX_LD_HASH_MASK(intf, ld, 0, + mkex_hash->hash_mask[intf][ld][0]); + SET_KEX_LD_HASH_MASK(intf, ld, 1, + mkex_hash->hash_mask[intf][ld][1]); + SET_KEX_LD_HASH_CTRL(intf, ld, + mkex_hash->hash_ctrl[intf][ld]); + hash_cnt++; + if (hash_cnt == NPC_MAX_HASH) + return; + } + } + } +} + +void npc_config_secret_key(struct rvu *rvu, int blkaddr) +{ + struct hw_cap *hwcap = &rvu->hw->cap; + struct rvu_hwinfo *hw = rvu->hw; + u8 intf; + + if (!hwcap->npc_hash_extract) { + dev_info(rvu->dev, "HW does not support secret key configuration\n"); + return; + } + + for (intf = 0; intf < hw->npc_intfs; intf++) { + rvu_write64(rvu, blkaddr, NPC_AF_INTFX_SECRET_KEY0(intf), + RVU_NPC_HASH_SECRET_KEY0); + rvu_write64(rvu, blkaddr, NPC_AF_INTFX_SECRET_KEY1(intf), + RVU_NPC_HASH_SECRET_KEY1); + rvu_write64(rvu, blkaddr, NPC_AF_INTFX_SECRET_KEY2(intf), + RVU_NPC_HASH_SECRET_KEY2); + } +} + +void npc_program_mkex_hash(struct rvu *rvu, int blkaddr) +{ + struct hw_cap *hwcap = &rvu->hw->cap; + struct rvu_hwinfo *hw = rvu->hw; + u8 intf; + + if (!hwcap->npc_hash_extract) { + dev_dbg(rvu->dev, "Field hash extract feature is not supported\n"); + return; + } + + for (intf = 0; intf < hw->npc_intfs; intf++) { + npc_program_mkex_hash_rx(rvu, blkaddr, intf); + npc_program_mkex_hash_tx(rvu, blkaddr, intf); + } +} + +void npc_update_field_hash(struct rvu *rvu, u8 intf, + struct mcam_entry *entry, + int blkaddr, + u64 features, + struct flow_msg *pkt, + struct flow_msg *mask, + struct flow_msg *opkt, + struct flow_msg *omask) +{ + struct npc_mcam_kex_hash *mkex_hash = rvu->kpu.mkex_hash; + struct npc_get_secret_key_req req; + struct npc_get_secret_key_rsp rsp; + u64 ldata[2], cfg; + u32 field_hash; + u8 hash_idx; + + if (!rvu->hw->cap.npc_hash_extract) { + dev_dbg(rvu->dev, "%s: Field hash extract feature is not supported\n", __func__); + return; + } + + req.intf = intf; + rvu_mbox_handler_npc_get_secret_key(rvu, &req, &rsp); + + for (hash_idx = 0; hash_idx < NPC_MAX_HASH; hash_idx++) { + cfg = rvu_read64(rvu, blkaddr, NPC_AF_INTFX_HASHX_CFG(intf, hash_idx)); + if ((cfg & BIT_ULL(11)) && (cfg & BIT_ULL(12))) { + u8 lid = (cfg & GENMASK_ULL(10, 8)) >> 8; + u8 ltype = (cfg & GENMASK_ULL(7, 4)) >> 4; + u8 ltype_mask = cfg & GENMASK_ULL(3, 0); + + if (mkex_hash->lid_lt_ld_hash_en[intf][lid][ltype][hash_idx]) { + switch (ltype & ltype_mask) { + /* If hash extract enabled is supported for IPv6 then + * 128 bit IPv6 source and destination addressed + * is hashed to 32 bit value. + */ + case NPC_LT_LC_IP6: + if (features & BIT_ULL(NPC_SIP_IPV6)) { + u32 src_ip[IPV6_WORDS]; + + be32_to_cpu_array(src_ip, pkt->ip6src, IPV6_WORDS); + ldata[0] = (u64)src_ip[0] << 32 | src_ip[1]; + ldata[1] = (u64)src_ip[2] << 32 | src_ip[3]; + field_hash = npc_field_hash_calc(ldata, + mkex_hash, + rsp.secret_key, + intf, + hash_idx); + npc_update_entry(rvu, NPC_SIP_IPV6, entry, + field_hash, 0, 32, 0, intf); + memcpy(&opkt->ip6src, &pkt->ip6src, + sizeof(pkt->ip6src)); + memcpy(&omask->ip6src, &mask->ip6src, + sizeof(mask->ip6src)); + break; + } + + if (features & BIT_ULL(NPC_DIP_IPV6)) { + u32 dst_ip[IPV6_WORDS]; + + be32_to_cpu_array(dst_ip, pkt->ip6dst, IPV6_WORDS); + ldata[0] = (u64)dst_ip[0] << 32 | dst_ip[1]; + ldata[1] = (u64)dst_ip[2] << 32 | dst_ip[3]; + field_hash = npc_field_hash_calc(ldata, + mkex_hash, + rsp.secret_key, + intf, + hash_idx); + npc_update_entry(rvu, NPC_DIP_IPV6, entry, + field_hash, 0, 32, 0, intf); + memcpy(&opkt->ip6dst, &pkt->ip6dst, + sizeof(pkt->ip6dst)); + memcpy(&omask->ip6dst, &mask->ip6dst, + sizeof(mask->ip6dst)); + } + break; + } + } + } + } +} + +int rvu_mbox_handler_npc_get_secret_key(struct rvu *rvu, + struct npc_get_secret_key_req *req, + struct npc_get_secret_key_rsp *rsp) +{ + u64 *secret_key = rsp->secret_key; + u8 intf = req->intf; + int blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) { + dev_err(rvu->dev, "%s: NPC block not implemented\n", __func__); + return -EINVAL; + } + + secret_key[0] = rvu_read64(rvu, blkaddr, NPC_AF_INTFX_SECRET_KEY0(intf)); + secret_key[1] = rvu_read64(rvu, blkaddr, NPC_AF_INTFX_SECRET_KEY1(intf)); + secret_key[2] = rvu_read64(rvu, blkaddr, NPC_AF_INTFX_SECRET_KEY2(intf)); + + return 0; +} + +/** + * rvu_npc_exact_mac2u64 - utility function to convert mac address to u64. + * @mac_addr: MAC address. + * Return: mdata for exact match table. + */ +static u64 rvu_npc_exact_mac2u64(u8 *mac_addr) +{ + u64 mac = 0; + int index; + + for (index = ETH_ALEN - 1; index >= 0; index--) + mac |= ((u64)*mac_addr++) << (8 * index); + + return mac; +} + +/** + * rvu_exact_prepare_mdata - Make mdata for mcam entry + * @mac: MAC address + * @chan: Channel number. + * @ctype: Channel Type. + * @mask: LDATA mask. + * Return: Meta data + */ +static u64 rvu_exact_prepare_mdata(u8 *mac, u16 chan, u16 ctype, u64 mask) +{ + u64 ldata = rvu_npc_exact_mac2u64(mac); + + /* Please note that mask is 48bit which excludes chan and ctype. + * Increase mask bits if we need to include them as well. + */ + ldata |= ((u64)chan << 48); + ldata |= ((u64)ctype << 60); + ldata &= mask; + ldata = ldata << 2; + + return ldata; +} + +/** + * rvu_exact_calculate_hash - calculate hash index to mem table. + * @rvu: resource virtualization unit. + * @chan: Channel number + * @ctype: Channel type. + * @mac: MAC address + * @mask: HASH mask. + * @table_depth: Depth of table. + * Return: Hash value + */ +static u32 rvu_exact_calculate_hash(struct rvu *rvu, u16 chan, u16 ctype, u8 *mac, + u64 mask, u32 table_depth) +{ + struct npc_exact_table *table = rvu->hw->table; + u64 hash_key[2]; + u64 key_in[2]; + u64 ldata; + u32 hash; + + key_in[0] = RVU_NPC_HASH_SECRET_KEY0; + key_in[1] = RVU_NPC_HASH_SECRET_KEY2; + + hash_key[0] = key_in[0] << 31; + hash_key[0] |= key_in[1]; + hash_key[1] = key_in[0] >> 33; + + ldata = rvu_exact_prepare_mdata(mac, chan, ctype, mask); + + dev_dbg(rvu->dev, "%s: ldata=0x%llx hash_key0=0x%llx hash_key2=0x%llx\n", __func__, + ldata, hash_key[1], hash_key[0]); + hash = rvu_npc_toeplitz_hash(&ldata, (u64 *)hash_key, 64, 95); + + hash &= table->mem_table.hash_mask; + hash += table->mem_table.hash_offset; + dev_dbg(rvu->dev, "%s: hash=%x\n", __func__, hash); + + return hash; +} + +/** + * rvu_npc_exact_alloc_mem_table_entry - find free entry in 4 way table. + * @rvu: resource virtualization unit. + * @way: Indicate way to table. + * @index: Hash index to 4 way table. + * @hash: Hash value. + * + * Searches 4 way table using hash index. Returns 0 on success. + * Return: 0 upon success. + */ +static int rvu_npc_exact_alloc_mem_table_entry(struct rvu *rvu, u8 *way, + u32 *index, unsigned int hash) +{ + struct npc_exact_table *table; + int depth, i; + + table = rvu->hw->table; + depth = table->mem_table.depth; + + /* Check all the 4 ways for a free slot. */ + mutex_lock(&table->lock); + for (i = 0; i < table->mem_table.ways; i++) { + if (test_bit(hash + i * depth, table->mem_table.bmap)) + continue; + + set_bit(hash + i * depth, table->mem_table.bmap); + mutex_unlock(&table->lock); + + dev_dbg(rvu->dev, "%s: mem table entry alloc success (way=%d index=%d)\n", + __func__, i, hash); + + *way = i; + *index = hash; + return 0; + } + mutex_unlock(&table->lock); + + dev_dbg(rvu->dev, "%s: No space in 4 way exact way, weight=%u\n", __func__, + bitmap_weight(table->mem_table.bmap, table->mem_table.depth)); + return -ENOSPC; +} + +/** + * rvu_npc_exact_free_id - Free seq id from bitmat. + * @rvu: Resource virtualization unit. + * @seq_id: Sequence identifier to be freed. + */ +static void rvu_npc_exact_free_id(struct rvu *rvu, u32 seq_id) +{ + struct npc_exact_table *table; + + table = rvu->hw->table; + mutex_lock(&table->lock); + clear_bit(seq_id, table->id_bmap); + mutex_unlock(&table->lock); + dev_dbg(rvu->dev, "%s: freed id %d\n", __func__, seq_id); +} + +/** + * rvu_npc_exact_alloc_id - Alloc seq id from bitmap. + * @rvu: Resource virtualization unit. + * @seq_id: Sequence identifier. + * Return: True or false. + */ +static bool rvu_npc_exact_alloc_id(struct rvu *rvu, u32 *seq_id) +{ + struct npc_exact_table *table; + u32 idx; + + table = rvu->hw->table; + + mutex_lock(&table->lock); + idx = find_first_zero_bit(table->id_bmap, table->tot_ids); + if (idx == table->tot_ids) { + mutex_unlock(&table->lock); + dev_err(rvu->dev, "%s: No space in id bitmap (%d)\n", + __func__, bitmap_weight(table->id_bmap, table->tot_ids)); + + return false; + } + + /* Mark bit map to indicate that slot is used.*/ + set_bit(idx, table->id_bmap); + mutex_unlock(&table->lock); + + *seq_id = idx; + dev_dbg(rvu->dev, "%s: Allocated id (%d)\n", __func__, *seq_id); + + return true; +} + +/** + * rvu_npc_exact_alloc_cam_table_entry - find free slot in fully associative table. + * @rvu: resource virtualization unit. + * @index: Index to exact CAM table. + * Return: 0 upon success; else error number. + */ +static int rvu_npc_exact_alloc_cam_table_entry(struct rvu *rvu, int *index) +{ + struct npc_exact_table *table; + u32 idx; + + table = rvu->hw->table; + + mutex_lock(&table->lock); + idx = find_first_zero_bit(table->cam_table.bmap, table->cam_table.depth); + if (idx == table->cam_table.depth) { + mutex_unlock(&table->lock); + dev_info(rvu->dev, "%s: No space in exact cam table, weight=%u\n", __func__, + bitmap_weight(table->cam_table.bmap, table->cam_table.depth)); + return -ENOSPC; + } + + /* Mark bit map to indicate that slot is used.*/ + set_bit(idx, table->cam_table.bmap); + mutex_unlock(&table->lock); + + *index = idx; + dev_dbg(rvu->dev, "%s: cam table entry alloc success (index=%d)\n", + __func__, idx); + return 0; +} + +/** + * rvu_exact_prepare_table_entry - Data for exact match table entry. + * @rvu: Resource virtualization unit. + * @enable: Enable/Disable entry + * @ctype: Software defined channel type. Currently set as 0. + * @chan: Channel number. + * @mac_addr: Destination mac address. + * Return: mdata for exact match table. + */ +static u64 rvu_exact_prepare_table_entry(struct rvu *rvu, bool enable, + u8 ctype, u16 chan, u8 *mac_addr) + +{ + u64 ldata = rvu_npc_exact_mac2u64(mac_addr); + + /* Enable or disable */ + u64 mdata = FIELD_PREP(GENMASK_ULL(63, 63), enable ? 1 : 0); + + /* Set Ctype */ + mdata |= FIELD_PREP(GENMASK_ULL(61, 60), ctype); + + /* Set chan */ + mdata |= FIELD_PREP(GENMASK_ULL(59, 48), chan); + + /* MAC address */ + mdata |= FIELD_PREP(GENMASK_ULL(47, 0), ldata); + + return mdata; +} + +/** + * rvu_exact_config_secret_key - Configure secret key. + * @rvu: Resource virtualization unit. + */ +static void rvu_exact_config_secret_key(struct rvu *rvu) +{ + int blkaddr; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + rvu_write64(rvu, blkaddr, NPC_AF_INTFX_EXACT_SECRET0(NIX_INTF_RX), + RVU_NPC_HASH_SECRET_KEY0); + + rvu_write64(rvu, blkaddr, NPC_AF_INTFX_EXACT_SECRET1(NIX_INTF_RX), + RVU_NPC_HASH_SECRET_KEY1); + + rvu_write64(rvu, blkaddr, NPC_AF_INTFX_EXACT_SECRET2(NIX_INTF_RX), + RVU_NPC_HASH_SECRET_KEY2); +} + +/** + * rvu_exact_config_search_key - Configure search key + * @rvu: Resource virtualization unit. + */ +static void rvu_exact_config_search_key(struct rvu *rvu) +{ + int blkaddr; + u64 reg_val; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + + /* HDR offset */ + reg_val = FIELD_PREP(GENMASK_ULL(39, 32), 0); + + /* BYTESM1, number of bytes - 1 */ + reg_val |= FIELD_PREP(GENMASK_ULL(18, 16), ETH_ALEN - 1); + + /* Enable LID and set LID to NPC_LID_LA */ + reg_val |= FIELD_PREP(GENMASK_ULL(11, 11), 1); + reg_val |= FIELD_PREP(GENMASK_ULL(10, 8), NPC_LID_LA); + + /* Clear layer type based extraction */ + + /* Disable LT_EN */ + reg_val |= FIELD_PREP(GENMASK_ULL(12, 12), 0); + + /* Set LTYPE_MATCH to 0 */ + reg_val |= FIELD_PREP(GENMASK_ULL(7, 4), 0); + + /* Set LTYPE_MASK to 0 */ + reg_val |= FIELD_PREP(GENMASK_ULL(3, 0), 0); + + rvu_write64(rvu, blkaddr, NPC_AF_INTFX_EXACT_CFG(NIX_INTF_RX), reg_val); +} + +/** + * rvu_exact_config_result_ctrl - Set exact table hash control + * @rvu: Resource virtualization unit. + * @depth: Depth of Exact match table. + * + * Sets mask and offset for hash for mem table. + */ +static void rvu_exact_config_result_ctrl(struct rvu *rvu, uint32_t depth) +{ + int blkaddr; + u64 reg = 0; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + + /* Set mask. Note that depth is a power of 2 */ + rvu->hw->table->mem_table.hash_mask = (depth - 1); + reg |= FIELD_PREP(GENMASK_ULL(42, 32), (depth - 1)); + + /* Set offset as 0 */ + rvu->hw->table->mem_table.hash_offset = 0; + reg |= FIELD_PREP(GENMASK_ULL(10, 0), 0); + + /* Set reg for RX */ + rvu_write64(rvu, blkaddr, NPC_AF_INTFX_EXACT_RESULT_CTL(NIX_INTF_RX), reg); + /* Store hash mask and offset for s/w algorithm */ +} + +/** + * rvu_exact_config_table_mask - Set exact table mask. + * @rvu: Resource virtualization unit. + */ +static void rvu_exact_config_table_mask(struct rvu *rvu) +{ + int blkaddr; + u64 mask = 0; + + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + + /* Don't use Ctype */ + mask |= FIELD_PREP(GENMASK_ULL(61, 60), 0); + + /* Set chan */ + mask |= GENMASK_ULL(59, 48); + + /* Full ldata */ + mask |= GENMASK_ULL(47, 0); + + /* Store mask for s/w hash calcualtion */ + rvu->hw->table->mem_table.mask = mask; + + /* Set mask for RX.*/ + rvu_write64(rvu, blkaddr, NPC_AF_INTFX_EXACT_MASK(NIX_INTF_RX), mask); +} + +/** + * rvu_npc_exact_get_max_entries - Get total number of entries in table. + * @rvu: resource virtualization unit. + * Return: Maximum table entries possible. + */ +u32 rvu_npc_exact_get_max_entries(struct rvu *rvu) +{ + struct npc_exact_table *table; + + table = rvu->hw->table; + return table->tot_ids; +} + +/** + * rvu_npc_exact_has_match_table - Checks support for exact match. + * @rvu: resource virtualization unit. + * Return: True if exact match table is supported/enabled. + */ +bool rvu_npc_exact_has_match_table(struct rvu *rvu) +{ + return rvu->hw->cap.npc_exact_match_enabled; +} + +/** + * __rvu_npc_exact_find_entry_by_seq_id - find entry by id + * @rvu: resource virtualization unit. + * @seq_id: Sequence identifier. + * + * Caller should acquire the lock. + * Return: Pointer to table entry. + */ +static struct npc_exact_table_entry * +__rvu_npc_exact_find_entry_by_seq_id(struct rvu *rvu, u32 seq_id) +{ + struct npc_exact_table *table = rvu->hw->table; + struct npc_exact_table_entry *entry = NULL; + struct list_head *lhead; + + lhead = &table->lhead_gbl; + + /* traverse to find the matching entry */ + list_for_each_entry(entry, lhead, glist) { + if (entry->seq_id != seq_id) + continue; + + return entry; + } + + return NULL; +} + +/** + * rvu_npc_exact_add_to_list - Add entry to list + * @rvu: resource virtualization unit. + * @opc_type: OPCODE to select MEM/CAM table. + * @ways: MEM table ways. + * @index: Index in MEM/CAM table. + * @cgx_id: CGX identifier. + * @lmac_id: LMAC identifier. + * @mac_addr: MAC address. + * @chan: Channel number. + * @ctype: Channel Type. + * @seq_id: Sequence identifier + * @cmd: True if function is called by ethtool cmd + * @mcam_idx: NPC mcam index of DMAC entry in NPC mcam. + * @pcifunc: pci function + * Return: 0 upon success. + */ +static int rvu_npc_exact_add_to_list(struct rvu *rvu, enum npc_exact_opc_type opc_type, u8 ways, + u32 index, u8 cgx_id, u8 lmac_id, u8 *mac_addr, u16 chan, + u8 ctype, u32 *seq_id, bool cmd, u32 mcam_idx, u16 pcifunc) +{ + struct npc_exact_table_entry *entry, *tmp, *iter; + struct npc_exact_table *table = rvu->hw->table; + struct list_head *lhead, *pprev; + + WARN_ON(ways >= NPC_EXACT_TBL_MAX_WAYS); + + if (!rvu_npc_exact_alloc_id(rvu, seq_id)) { + dev_err(rvu->dev, "%s: Generate seq id failed\n", __func__); + return -EFAULT; + } + + entry = kmalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + rvu_npc_exact_free_id(rvu, *seq_id); + dev_err(rvu->dev, "%s: Memory allocation failed\n", __func__); + return -ENOMEM; + } + + mutex_lock(&table->lock); + switch (opc_type) { + case NPC_EXACT_OPC_CAM: + lhead = &table->lhead_cam_tbl_entry; + table->cam_tbl_entry_cnt++; + break; + + case NPC_EXACT_OPC_MEM: + lhead = &table->lhead_mem_tbl_entry[ways]; + table->mem_tbl_entry_cnt++; + break; + + default: + mutex_unlock(&table->lock); + kfree(entry); + rvu_npc_exact_free_id(rvu, *seq_id); + + dev_err(rvu->dev, "%s: Unknown opc type%d\n", __func__, opc_type); + return -EINVAL; + } + + /* Add to global list */ + INIT_LIST_HEAD(&entry->glist); + list_add_tail(&entry->glist, &table->lhead_gbl); + INIT_LIST_HEAD(&entry->list); + entry->index = index; + entry->ways = ways; + entry->opc_type = opc_type; + + entry->pcifunc = pcifunc; + + ether_addr_copy(entry->mac, mac_addr); + entry->chan = chan; + entry->ctype = ctype; + entry->cgx_id = cgx_id; + entry->lmac_id = lmac_id; + + entry->seq_id = *seq_id; + + entry->mcam_idx = mcam_idx; + entry->cmd = cmd; + + pprev = lhead; + + /* Insert entry in ascending order of index */ + list_for_each_entry_safe(iter, tmp, lhead, list) { + if (index < iter->index) + break; + + pprev = &iter->list; + } + + /* Add to each table list */ + list_add(&entry->list, pprev); + mutex_unlock(&table->lock); + return 0; +} + +/** + * rvu_npc_exact_mem_table_write - Wrapper for register write + * @rvu: resource virtualization unit. + * @blkaddr: Block address + * @ways: ways for MEM table. + * @index: Index in MEM + * @mdata: Meta data to be written to register. + */ +static void rvu_npc_exact_mem_table_write(struct rvu *rvu, int blkaddr, u8 ways, + u32 index, u64 mdata) +{ + rvu_write64(rvu, blkaddr, NPC_AF_EXACT_MEM_ENTRY(ways, index), mdata); +} + +/** + * rvu_npc_exact_cam_table_write - Wrapper for register write + * @rvu: resource virtualization unit. + * @blkaddr: Block address + * @index: Index in MEM + * @mdata: Meta data to be written to register. + */ +static void rvu_npc_exact_cam_table_write(struct rvu *rvu, int blkaddr, + u32 index, u64 mdata) +{ + rvu_write64(rvu, blkaddr, NPC_AF_EXACT_CAM_ENTRY(index), mdata); +} + +/** + * rvu_npc_exact_dealloc_table_entry - dealloc table entry + * @rvu: resource virtualization unit. + * @opc_type: OPCODE for selection of table(MEM or CAM) + * @ways: ways if opc_type is MEM table. + * @index: Index of MEM or CAM table. + * Return: 0 upon success. + */ +static int rvu_npc_exact_dealloc_table_entry(struct rvu *rvu, enum npc_exact_opc_type opc_type, + u8 ways, u32 index) +{ + int blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + struct npc_exact_table *table; + u8 null_dmac[6] = { 0 }; + int depth; + + /* Prepare entry with all fields set to zero */ + u64 null_mdata = rvu_exact_prepare_table_entry(rvu, false, 0, 0, null_dmac); + + table = rvu->hw->table; + depth = table->mem_table.depth; + + mutex_lock(&table->lock); + + switch (opc_type) { + case NPC_EXACT_OPC_CAM: + + /* Check whether entry is used already */ + if (!test_bit(index, table->cam_table.bmap)) { + mutex_unlock(&table->lock); + dev_err(rvu->dev, "%s: Trying to free an unused entry ways=%d index=%d\n", + __func__, ways, index); + return -EINVAL; + } + + rvu_npc_exact_cam_table_write(rvu, blkaddr, index, null_mdata); + clear_bit(index, table->cam_table.bmap); + break; + + case NPC_EXACT_OPC_MEM: + + /* Check whether entry is used already */ + if (!test_bit(index + ways * depth, table->mem_table.bmap)) { + mutex_unlock(&table->lock); + dev_err(rvu->dev, "%s: Trying to free an unused entry index=%d\n", + __func__, index); + return -EINVAL; + } + + rvu_npc_exact_mem_table_write(rvu, blkaddr, ways, index, null_mdata); + clear_bit(index + ways * depth, table->mem_table.bmap); + break; + + default: + mutex_unlock(&table->lock); + dev_err(rvu->dev, "%s: invalid opc type %d", __func__, opc_type); + return -ENOSPC; + } + + mutex_unlock(&table->lock); + + dev_dbg(rvu->dev, "%s: Successfully deleted entry (index=%d, ways=%d opc_type=%d\n", + __func__, index, ways, opc_type); + + return 0; +} + +/** + * rvu_npc_exact_alloc_table_entry - Allociate an entry + * @rvu: resource virtualization unit. + * @mac: MAC address. + * @chan: Channel number. + * @ctype: Channel Type. + * @index: Index of MEM table or CAM table. + * @ways: Ways. Only valid for MEM table. + * @opc_type: OPCODE to select table (MEM or CAM) + * + * Try allocating a slot from MEM table. If all 4 ways + * slot are full for a hash index, check availability in + * 32-entry CAM table for allocation. + * Return: 0 upon success. + */ +static int rvu_npc_exact_alloc_table_entry(struct rvu *rvu, char *mac, u16 chan, u8 ctype, + u32 *index, u8 *ways, enum npc_exact_opc_type *opc_type) +{ + struct npc_exact_table *table; + unsigned int hash; + int err; + + table = rvu->hw->table; + + /* Check in 4-ways mem entry for free slote */ + hash = rvu_exact_calculate_hash(rvu, chan, ctype, mac, table->mem_table.mask, + table->mem_table.depth); + err = rvu_npc_exact_alloc_mem_table_entry(rvu, ways, index, hash); + if (!err) { + *opc_type = NPC_EXACT_OPC_MEM; + dev_dbg(rvu->dev, "%s: inserted in 4 ways hash table ways=%d, index=%d\n", + __func__, *ways, *index); + return 0; + } + + dev_dbg(rvu->dev, "%s: failed to insert in 4 ways hash table\n", __func__); + + /* wayss is 0 for cam table */ + *ways = 0; + err = rvu_npc_exact_alloc_cam_table_entry(rvu, index); + if (!err) { + *opc_type = NPC_EXACT_OPC_CAM; + dev_dbg(rvu->dev, "%s: inserted in fully associative hash table index=%u\n", + __func__, *index); + return 0; + } + + dev_err(rvu->dev, "%s: failed to insert in fully associative hash table\n", __func__); + return -ENOSPC; +} + +/** + * rvu_npc_exact_save_drop_rule_chan_and_mask - Save drop rules info in data base. + * @rvu: resource virtualization unit. + * @drop_mcam_idx: Drop rule index in NPC mcam. + * @chan_val: Channel value. + * @chan_mask: Channel Mask. + * @pcifunc: pcifunc of interface. + * Return: True upon success. + */ +static bool rvu_npc_exact_save_drop_rule_chan_and_mask(struct rvu *rvu, int drop_mcam_idx, + u64 chan_val, u64 chan_mask, u16 pcifunc) +{ + struct npc_exact_table *table; + int i; + + table = rvu->hw->table; + + for (i = 0; i < NPC_MCAM_DROP_RULE_MAX; i++) { + if (!table->drop_rule_map[i].valid) + break; + + if (table->drop_rule_map[i].chan_val != (u16)chan_val) + continue; + + if (table->drop_rule_map[i].chan_mask != (u16)chan_mask) + continue; + + return false; + } + + if (i == NPC_MCAM_DROP_RULE_MAX) + return false; + + table->drop_rule_map[i].drop_rule_idx = drop_mcam_idx; + table->drop_rule_map[i].chan_val = (u16)chan_val; + table->drop_rule_map[i].chan_mask = (u16)chan_mask; + table->drop_rule_map[i].pcifunc = pcifunc; + table->drop_rule_map[i].valid = true; + return true; +} + +/** + * rvu_npc_exact_calc_drop_rule_chan_and_mask - Calculate Channel number and mask. + * @rvu: resource virtualization unit. + * @intf_type: Interface type (SDK, LBK or CGX) + * @cgx_id: CGX identifier. + * @lmac_id: LAMC identifier. + * @val: Channel number. + * @mask: Channel mask. + * Return: True upon success. + */ +static bool rvu_npc_exact_calc_drop_rule_chan_and_mask(struct rvu *rvu, u8 intf_type, + u8 cgx_id, u8 lmac_id, + u64 *val, u64 *mask) +{ + u16 chan_val, chan_mask; + + /* No support for SDP and LBK */ + if (intf_type != NIX_INTF_TYPE_CGX) + return false; + + chan_val = rvu_nix_chan_cgx(rvu, cgx_id, lmac_id, 0); + chan_mask = 0xfff; + + if (val) + *val = chan_val; + + if (mask) + *mask = chan_mask; + + return true; +} + +/** + * rvu_npc_exact_drop_rule_to_pcifunc - Retrieve pcifunc + * @rvu: resource virtualization unit. + * @drop_rule_idx: Drop rule index in NPC mcam. + * + * Debugfs (exact_drop_cnt) entry displays pcifunc for interface + * by retrieving the pcifunc value from data base. + * Return: Drop rule index. + */ +u16 rvu_npc_exact_drop_rule_to_pcifunc(struct rvu *rvu, u32 drop_rule_idx) +{ + struct npc_exact_table *table; + int i; + + table = rvu->hw->table; + + for (i = 0; i < NPC_MCAM_DROP_RULE_MAX; i++) { + if (!table->drop_rule_map[i].valid) + break; + + if (table->drop_rule_map[i].drop_rule_idx != drop_rule_idx) + continue; + + return table->drop_rule_map[i].pcifunc; + } + + dev_err(rvu->dev, "%s: drop mcam rule index (%d) >= NPC_MCAM_DROP_RULE_MAX\n", + __func__, drop_rule_idx); + return -1; +} + +/** + * rvu_npc_exact_get_drop_rule_info - Get drop rule information. + * @rvu: resource virtualization unit. + * @intf_type: Interface type (CGX, SDP or LBK) + * @cgx_id: CGX identifier. + * @lmac_id: LMAC identifier. + * @drop_mcam_idx: NPC mcam drop rule index. + * @val: Channel value. + * @mask: Channel mask. + * @pcifunc: pcifunc of interface corresponding to the drop rule. + * Return: True upon success. + */ +static bool rvu_npc_exact_get_drop_rule_info(struct rvu *rvu, u8 intf_type, u8 cgx_id, + u8 lmac_id, u32 *drop_mcam_idx, u64 *val, + u64 *mask, u16 *pcifunc) +{ + struct npc_exact_table *table; + u64 chan_val, chan_mask; + bool rc; + int i; + + table = rvu->hw->table; + + if (intf_type != NIX_INTF_TYPE_CGX) { + dev_err(rvu->dev, "%s: No drop rule for LBK/SDP mode\n", __func__); + return false; + } + + rc = rvu_npc_exact_calc_drop_rule_chan_and_mask(rvu, intf_type, cgx_id, + lmac_id, &chan_val, &chan_mask); + if (!rc) + return false; + + for (i = 0; i < NPC_MCAM_DROP_RULE_MAX; i++) { + if (!table->drop_rule_map[i].valid) + break; + + if (table->drop_rule_map[i].chan_val != (u16)chan_val) + continue; + + if (val) + *val = table->drop_rule_map[i].chan_val; + if (mask) + *mask = table->drop_rule_map[i].chan_mask; + if (pcifunc) + *pcifunc = table->drop_rule_map[i].pcifunc; + + *drop_mcam_idx = i; + return true; + } + + if (i == NPC_MCAM_DROP_RULE_MAX) { + dev_err(rvu->dev, "%s: drop mcam rule index (%d) >= NPC_MCAM_DROP_RULE_MAX\n", + __func__, *drop_mcam_idx); + return false; + } + + dev_err(rvu->dev, "%s: Could not retrieve for cgx=%d, lmac=%d\n", + __func__, cgx_id, lmac_id); + return false; +} + +/** + * __rvu_npc_exact_cmd_rules_cnt_update - Update number dmac rules against a drop rule. + * @rvu: resource virtualization unit. + * @drop_mcam_idx: NPC mcam drop rule index. + * @val: +1 or -1. + * @enable_or_disable_cam: If no exact match rules against a drop rule, disable it. + * + * when first exact match entry against a drop rule is added, enable_or_disable_cam + * is set to true. When last exact match entry against a drop rule is deleted, + * enable_or_disable_cam is set to true. + * Return: Number of rules + */ +static u16 __rvu_npc_exact_cmd_rules_cnt_update(struct rvu *rvu, int drop_mcam_idx, + int val, bool *enable_or_disable_cam) +{ + struct npc_exact_table *table; + u16 *cnt, old_cnt; + bool promisc; + + table = rvu->hw->table; + promisc = table->promisc_mode[drop_mcam_idx]; + + cnt = &table->cnt_cmd_rules[drop_mcam_idx]; + old_cnt = *cnt; + + *cnt += val; + + if (!enable_or_disable_cam) + goto done; + + *enable_or_disable_cam = false; + + if (promisc) + goto done; + + /* If all rules are deleted and not already in promisc mode; disable cam */ + if (!*cnt && val < 0) { + *enable_or_disable_cam = true; + goto done; + } + + /* If rule got added and not already in promisc mode; enable cam */ + if (!old_cnt && val > 0) { + *enable_or_disable_cam = true; + goto done; + } + +done: + return *cnt; +} + +/** + * rvu_npc_exact_del_table_entry_by_id - Delete and free table entry. + * @rvu: resource virtualization unit. + * @seq_id: Sequence identifier of the entry. + * + * Deletes entry from linked lists and free up slot in HW MEM or CAM + * table. + * Return: 0 upon success. + */ +static int rvu_npc_exact_del_table_entry_by_id(struct rvu *rvu, u32 seq_id) +{ + struct npc_exact_table_entry *entry = NULL; + struct npc_exact_table *table; + bool disable_cam = false; + u32 drop_mcam_idx = -1; + int *cnt; + bool rc; + + table = rvu->hw->table; + + mutex_lock(&table->lock); + + /* Lookup for entry which needs to be updated */ + entry = __rvu_npc_exact_find_entry_by_seq_id(rvu, seq_id); + if (!entry) { + dev_dbg(rvu->dev, "%s: failed to find entry for id=%d\n", __func__, seq_id); + mutex_unlock(&table->lock); + return -ENODATA; + } + + cnt = (entry->opc_type == NPC_EXACT_OPC_CAM) ? &table->cam_tbl_entry_cnt : + &table->mem_tbl_entry_cnt; + + /* delete from lists */ + list_del_init(&entry->list); + list_del_init(&entry->glist); + + (*cnt)--; + + rc = rvu_npc_exact_get_drop_rule_info(rvu, NIX_INTF_TYPE_CGX, entry->cgx_id, + entry->lmac_id, &drop_mcam_idx, NULL, NULL, NULL); + if (!rc) { + dev_dbg(rvu->dev, "%s: failed to retrieve drop info for id=0x%x\n", + __func__, seq_id); + mutex_unlock(&table->lock); + return -ENODATA; + } + + if (entry->cmd) + __rvu_npc_exact_cmd_rules_cnt_update(rvu, drop_mcam_idx, -1, &disable_cam); + + /* No dmac filter rules; disable drop on hit rule */ + if (disable_cam) { + rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, false); + dev_dbg(rvu->dev, "%s: Disabling mcam idx %d\n", + __func__, drop_mcam_idx); + } + + mutex_unlock(&table->lock); + + rvu_npc_exact_dealloc_table_entry(rvu, entry->opc_type, entry->ways, entry->index); + + rvu_npc_exact_free_id(rvu, seq_id); + + dev_dbg(rvu->dev, "%s: delete entry success for id=0x%x, mca=%pM\n", + __func__, seq_id, entry->mac); + kfree(entry); + + return 0; +} + +/** + * rvu_npc_exact_add_table_entry - Adds a table entry + * @rvu: resource virtualization unit. + * @cgx_id: cgx identifier. + * @lmac_id: lmac identifier. + * @mac: MAC address. + * @chan: Channel number. + * @ctype: Channel Type. + * @seq_id: Sequence number. + * @cmd: Whether it is invoked by ethtool cmd. + * @mcam_idx: NPC mcam index corresponding to MAC + * @pcifunc: PCI func. + * + * Creates a new exact match table entry in either CAM or + * MEM table. + * Return: 0 upon success. + */ +static int rvu_npc_exact_add_table_entry(struct rvu *rvu, u8 cgx_id, u8 lmac_id, u8 *mac, + u16 chan, u8 ctype, u32 *seq_id, bool cmd, + u32 mcam_idx, u16 pcifunc) +{ + int blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + enum npc_exact_opc_type opc_type; + bool enable_cam = false; + u32 drop_mcam_idx; + u32 index; + u64 mdata; + bool rc; + int err; + u8 ways; + + ctype = 0; + + err = rvu_npc_exact_alloc_table_entry(rvu, mac, chan, ctype, &index, &ways, &opc_type); + if (err) { + dev_err(rvu->dev, "%s: Could not alloc in exact match table\n", __func__); + return err; + } + + /* Write mdata to table */ + mdata = rvu_exact_prepare_table_entry(rvu, true, ctype, chan, mac); + + if (opc_type == NPC_EXACT_OPC_CAM) + rvu_npc_exact_cam_table_write(rvu, blkaddr, index, mdata); + else + rvu_npc_exact_mem_table_write(rvu, blkaddr, ways, index, mdata); + + /* Insert entry to linked list */ + err = rvu_npc_exact_add_to_list(rvu, opc_type, ways, index, cgx_id, lmac_id, + mac, chan, ctype, seq_id, cmd, mcam_idx, pcifunc); + if (err) { + rvu_npc_exact_dealloc_table_entry(rvu, opc_type, ways, index); + dev_err(rvu->dev, "%s: could not add to exact match table\n", __func__); + return err; + } + + rc = rvu_npc_exact_get_drop_rule_info(rvu, NIX_INTF_TYPE_CGX, cgx_id, lmac_id, + &drop_mcam_idx, NULL, NULL, NULL); + if (!rc) { + rvu_npc_exact_dealloc_table_entry(rvu, opc_type, ways, index); + dev_dbg(rvu->dev, "%s: failed to get drop rule info cgx=%d lmac=%d\n", + __func__, cgx_id, lmac_id); + return -EINVAL; + } + + if (cmd) + __rvu_npc_exact_cmd_rules_cnt_update(rvu, drop_mcam_idx, 1, &enable_cam); + + /* First command rule; enable drop on hit rule */ + if (enable_cam) { + rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, true); + dev_dbg(rvu->dev, "%s: Enabling mcam idx %d\n", + __func__, drop_mcam_idx); + } + + dev_dbg(rvu->dev, + "%s: Successfully added entry (index=%d, dmac=%pM, ways=%d opc_type=%d\n", + __func__, index, mac, ways, opc_type); + + return 0; +} + +/** + * rvu_npc_exact_update_table_entry - Update exact match table. + * @rvu: resource virtualization unit. + * @cgx_id: CGX identifier. + * @lmac_id: LMAC identifier. + * @old_mac: Existing MAC address entry. + * @new_mac: New MAC address entry. + * @seq_id: Sequence identifier of the entry. + * + * Updates MAC address of an entry. If entry is in MEM table, new + * hash value may not match with old one. + * Return: 0 upon success. + */ +static int rvu_npc_exact_update_table_entry(struct rvu *rvu, u8 cgx_id, u8 lmac_id, + u8 *old_mac, u8 *new_mac, u32 *seq_id) +{ + int blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + struct npc_exact_table_entry *entry; + struct npc_exact_table *table; + u32 hash_index; + u64 mdata; + + table = rvu->hw->table; + + mutex_lock(&table->lock); + + /* Lookup for entry which needs to be updated */ + entry = __rvu_npc_exact_find_entry_by_seq_id(rvu, *seq_id); + if (!entry) { + mutex_unlock(&table->lock); + dev_dbg(rvu->dev, + "%s: failed to find entry for cgx_id=%d lmac_id=%d old_mac=%pM\n", + __func__, cgx_id, lmac_id, old_mac); + return -ENODATA; + } + + /* If entry is in mem table and new hash index is different than old + * hash index, we cannot update the entry. Fail in these scenarios. + */ + if (entry->opc_type == NPC_EXACT_OPC_MEM) { + hash_index = rvu_exact_calculate_hash(rvu, entry->chan, entry->ctype, + new_mac, table->mem_table.mask, + table->mem_table.depth); + if (hash_index != entry->index) { + dev_dbg(rvu->dev, + "%s: Update failed due to index mismatch(new=0x%x, old=%x)\n", + __func__, hash_index, entry->index); + mutex_unlock(&table->lock); + return -EINVAL; + } + } + + mdata = rvu_exact_prepare_table_entry(rvu, true, entry->ctype, entry->chan, new_mac); + + if (entry->opc_type == NPC_EXACT_OPC_MEM) + rvu_npc_exact_mem_table_write(rvu, blkaddr, entry->ways, entry->index, mdata); + else + rvu_npc_exact_cam_table_write(rvu, blkaddr, entry->index, mdata); + + /* Update entry fields */ + ether_addr_copy(entry->mac, new_mac); + *seq_id = entry->seq_id; + + dev_dbg(rvu->dev, + "%s: Successfully updated entry (index=%d, dmac=%pM, ways=%d opc_type=%d\n", + __func__, entry->index, entry->mac, entry->ways, entry->opc_type); + + dev_dbg(rvu->dev, "%s: Successfully updated entry (old mac=%pM new_mac=%pM\n", + __func__, old_mac, new_mac); + + mutex_unlock(&table->lock); + return 0; +} + +/** + * rvu_npc_exact_promisc_disable - Disable promiscuous mode. + * @rvu: resource virtualization unit. + * @pcifunc: pcifunc + * + * Drop rule is against each PF. We dont support DMAC filter for + * VF. + * Return: 0 upon success + */ + +int rvu_npc_exact_promisc_disable(struct rvu *rvu, u16 pcifunc) +{ + struct npc_exact_table *table; + int pf = rvu_get_pf(pcifunc); + u8 cgx_id, lmac_id; + u32 drop_mcam_idx; + bool *promisc; + bool rc; + u32 cnt; + + table = rvu->hw->table; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + rc = rvu_npc_exact_get_drop_rule_info(rvu, NIX_INTF_TYPE_CGX, cgx_id, lmac_id, + &drop_mcam_idx, NULL, NULL, NULL); + if (!rc) { + dev_dbg(rvu->dev, "%s: failed to get drop rule info cgx=%d lmac=%d\n", + __func__, cgx_id, lmac_id); + return -EINVAL; + } + + mutex_lock(&table->lock); + promisc = &table->promisc_mode[drop_mcam_idx]; + + if (!*promisc) { + mutex_unlock(&table->lock); + dev_dbg(rvu->dev, "%s: Err Already promisc mode disabled (cgx=%d lmac=%d)\n", + __func__, cgx_id, lmac_id); + return LMAC_AF_ERR_INVALID_PARAM; + } + *promisc = false; + cnt = __rvu_npc_exact_cmd_rules_cnt_update(rvu, drop_mcam_idx, 0, NULL); + mutex_unlock(&table->lock); + + /* If no dmac filter entries configured, disable drop rule */ + if (!cnt) + rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, false); + else + rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, !*promisc); + + dev_dbg(rvu->dev, "%s: disabled promisc mode (cgx=%d lmac=%d, cnt=%d)\n", + __func__, cgx_id, lmac_id, cnt); + return 0; +} + +/** + * rvu_npc_exact_promisc_enable - Enable promiscuous mode. + * @rvu: resource virtualization unit. + * @pcifunc: pcifunc. + * Return: 0 upon success + */ +int rvu_npc_exact_promisc_enable(struct rvu *rvu, u16 pcifunc) +{ + struct npc_exact_table *table; + int pf = rvu_get_pf(pcifunc); + u8 cgx_id, lmac_id; + u32 drop_mcam_idx; + bool *promisc; + bool rc; + u32 cnt; + + table = rvu->hw->table; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + rc = rvu_npc_exact_get_drop_rule_info(rvu, NIX_INTF_TYPE_CGX, cgx_id, lmac_id, + &drop_mcam_idx, NULL, NULL, NULL); + if (!rc) { + dev_dbg(rvu->dev, "%s: failed to get drop rule info cgx=%d lmac=%d\n", + __func__, cgx_id, lmac_id); + return -EINVAL; + } + + mutex_lock(&table->lock); + promisc = &table->promisc_mode[drop_mcam_idx]; + + if (*promisc) { + mutex_unlock(&table->lock); + dev_dbg(rvu->dev, "%s: Already in promisc mode (cgx=%d lmac=%d)\n", + __func__, cgx_id, lmac_id); + return LMAC_AF_ERR_INVALID_PARAM; + } + *promisc = true; + cnt = __rvu_npc_exact_cmd_rules_cnt_update(rvu, drop_mcam_idx, 0, NULL); + mutex_unlock(&table->lock); + + /* If no dmac filter entries configured, disable drop rule */ + if (!cnt) + rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, false); + else + rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, !*promisc); + + dev_dbg(rvu->dev, "%s: Enabled promisc mode (cgx=%d lmac=%d cnt=%d)\n", + __func__, cgx_id, lmac_id, cnt); + return 0; +} + +/** + * rvu_npc_exact_mac_addr_reset - Delete PF mac address. + * @rvu: resource virtualization unit. + * @req: Reset request + * @rsp: Reset response. + * Return: 0 upon success + */ +int rvu_npc_exact_mac_addr_reset(struct rvu *rvu, struct cgx_mac_addr_reset_req *req, + struct msg_rsp *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + u32 seq_id = req->index; + struct rvu_pfvf *pfvf; + u8 cgx_id, lmac_id; + int rc; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + + pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc); + + rc = rvu_npc_exact_del_table_entry_by_id(rvu, seq_id); + if (rc) { + /* TODO: how to handle this error case ? */ + dev_err(rvu->dev, "%s MAC (%pM) del PF=%d failed\n", __func__, pfvf->mac_addr, pf); + return 0; + } + + dev_dbg(rvu->dev, "%s MAC (%pM) del PF=%d success (seq_id=%u)\n", + __func__, pfvf->mac_addr, pf, seq_id); + return 0; +} + +/** + * rvu_npc_exact_mac_addr_update - Update mac address field with new value. + * @rvu: resource virtualization unit. + * @req: Update request. + * @rsp: Update response. + * Return: 0 upon success + */ +int rvu_npc_exact_mac_addr_update(struct rvu *rvu, + struct cgx_mac_addr_update_req *req, + struct cgx_mac_addr_update_rsp *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + struct npc_exact_table_entry *entry; + struct npc_exact_table *table; + struct rvu_pfvf *pfvf; + u32 seq_id, mcam_idx; + u8 old_mac[ETH_ALEN]; + u8 cgx_id, lmac_id; + int rc; + + if (!is_cgx_config_permitted(rvu, req->hdr.pcifunc)) + return LMAC_AF_ERR_PERM_DENIED; + + dev_dbg(rvu->dev, "%s: Update request for seq_id=%d, mac=%pM\n", + __func__, req->index, req->mac_addr); + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + + pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc); + + table = rvu->hw->table; + + mutex_lock(&table->lock); + + /* Lookup for entry which needs to be updated */ + entry = __rvu_npc_exact_find_entry_by_seq_id(rvu, req->index); + if (!entry) { + dev_err(rvu->dev, "%s: failed to find entry for id=0x%x\n", __func__, req->index); + mutex_unlock(&table->lock); + return LMAC_AF_ERR_EXACT_MATCH_TBL_LOOK_UP_FAILED; + } + ether_addr_copy(old_mac, entry->mac); + seq_id = entry->seq_id; + mcam_idx = entry->mcam_idx; + mutex_unlock(&table->lock); + + rc = rvu_npc_exact_update_table_entry(rvu, cgx_id, lmac_id, old_mac, + req->mac_addr, &seq_id); + if (!rc) { + rsp->index = seq_id; + dev_dbg(rvu->dev, "%s mac:%pM (pfvf:%pM default:%pM) update to PF=%d success\n", + __func__, req->mac_addr, pfvf->mac_addr, pfvf->default_mac, pf); + ether_addr_copy(pfvf->mac_addr, req->mac_addr); + return 0; + } + + /* Try deleting and adding it again */ + rc = rvu_npc_exact_del_table_entry_by_id(rvu, req->index); + if (rc) { + /* This could be a new entry */ + dev_dbg(rvu->dev, "%s MAC (%pM) del PF=%d failed\n", __func__, + pfvf->mac_addr, pf); + } + + rc = rvu_npc_exact_add_table_entry(rvu, cgx_id, lmac_id, req->mac_addr, + pfvf->rx_chan_base, 0, &seq_id, true, + mcam_idx, req->hdr.pcifunc); + if (rc) { + dev_err(rvu->dev, "%s MAC (%pM) add PF=%d failed\n", __func__, + req->mac_addr, pf); + return LMAC_AF_ERR_EXACT_MATCH_TBL_ADD_FAILED; + } + + rsp->index = seq_id; + dev_dbg(rvu->dev, + "%s MAC (new:%pM, old=%pM default:%pM) del and add to PF=%d success (seq_id=%u)\n", + __func__, req->mac_addr, pfvf->mac_addr, pfvf->default_mac, pf, seq_id); + + ether_addr_copy(pfvf->mac_addr, req->mac_addr); + return 0; +} + +/** + * rvu_npc_exact_mac_addr_add - Adds MAC address to exact match table. + * @rvu: resource virtualization unit. + * @req: Add request. + * @rsp: Add response. + * Return: 0 upon success + */ +int rvu_npc_exact_mac_addr_add(struct rvu *rvu, + struct cgx_mac_addr_add_req *req, + struct cgx_mac_addr_add_rsp *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + struct rvu_pfvf *pfvf; + u8 cgx_id, lmac_id; + int rc = 0; + u32 seq_id; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + pfvf = rvu_get_pfvf(rvu, req->hdr.pcifunc); + + rc = rvu_npc_exact_add_table_entry(rvu, cgx_id, lmac_id, req->mac_addr, + pfvf->rx_chan_base, 0, &seq_id, + true, -1, req->hdr.pcifunc); + + if (!rc) { + rsp->index = seq_id; + dev_dbg(rvu->dev, "%s MAC (%pM) add to PF=%d success (seq_id=%u)\n", + __func__, req->mac_addr, pf, seq_id); + return 0; + } + + dev_err(rvu->dev, "%s MAC (%pM) add to PF=%d failed\n", __func__, + req->mac_addr, pf); + return LMAC_AF_ERR_EXACT_MATCH_TBL_ADD_FAILED; +} + +/** + * rvu_npc_exact_mac_addr_del - Delete DMAC filter + * @rvu: resource virtualization unit. + * @req: Delete request. + * @rsp: Delete response. + * Return: 0 upon success + */ +int rvu_npc_exact_mac_addr_del(struct rvu *rvu, + struct cgx_mac_addr_del_req *req, + struct msg_rsp *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + int rc; + + rc = rvu_npc_exact_del_table_entry_by_id(rvu, req->index); + if (!rc) { + dev_dbg(rvu->dev, "%s del to PF=%d success (seq_id=%u)\n", + __func__, pf, req->index); + return 0; + } + + dev_err(rvu->dev, "%s del to PF=%d failed (seq_id=%u)\n", + __func__, pf, req->index); + return LMAC_AF_ERR_EXACT_MATCH_TBL_DEL_FAILED; +} + +/** + * rvu_npc_exact_mac_addr_set - Add PF mac address to dmac filter. + * @rvu: resource virtualization unit. + * @req: Set request. + * @rsp: Set response. + * Return: 0 upon success + */ +int rvu_npc_exact_mac_addr_set(struct rvu *rvu, struct cgx_mac_addr_set_or_get *req, + struct cgx_mac_addr_set_or_get *rsp) +{ + int pf = rvu_get_pf(req->hdr.pcifunc); + u32 seq_id = req->index; + struct rvu_pfvf *pfvf; + u8 cgx_id, lmac_id; + u32 mcam_idx = -1; + int rc, nixlf; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id); + + pfvf = &rvu->pf[pf]; + + /* If table does not have an entry; both update entry and del table entry API + * below fails. Those are not failure conditions. + */ + rc = rvu_npc_exact_update_table_entry(rvu, cgx_id, lmac_id, pfvf->mac_addr, + req->mac_addr, &seq_id); + if (!rc) { + rsp->index = seq_id; + ether_addr_copy(pfvf->mac_addr, req->mac_addr); + ether_addr_copy(rsp->mac_addr, req->mac_addr); + dev_dbg(rvu->dev, "%s MAC (%pM) update to PF=%d success\n", + __func__, req->mac_addr, pf); + return 0; + } + + /* Try deleting and adding it again */ + rc = rvu_npc_exact_del_table_entry_by_id(rvu, req->index); + if (rc) { + dev_dbg(rvu->dev, "%s MAC (%pM) del PF=%d failed\n", + __func__, pfvf->mac_addr, pf); + } + + /* find mcam entry if exist */ + rc = nix_get_nixlf(rvu, req->hdr.pcifunc, &nixlf, NULL); + if (!rc) { + mcam_idx = npc_get_nixlf_mcam_index(&rvu->hw->mcam, req->hdr.pcifunc, + nixlf, NIXLF_UCAST_ENTRY); + } + + rc = rvu_npc_exact_add_table_entry(rvu, cgx_id, lmac_id, req->mac_addr, + pfvf->rx_chan_base, 0, &seq_id, + true, mcam_idx, req->hdr.pcifunc); + if (rc) { + dev_err(rvu->dev, "%s MAC (%pM) add PF=%d failed\n", + __func__, req->mac_addr, pf); + return LMAC_AF_ERR_EXACT_MATCH_TBL_ADD_FAILED; + } + + rsp->index = seq_id; + ether_addr_copy(rsp->mac_addr, req->mac_addr); + ether_addr_copy(pfvf->mac_addr, req->mac_addr); + dev_dbg(rvu->dev, + "%s MAC (%pM) del and add to PF=%d success (seq_id=%u)\n", + __func__, req->mac_addr, pf, seq_id); + return 0; +} + +/** + * rvu_npc_exact_can_disable_feature - Check if feature can be disabled. + * @rvu: resource virtualization unit. + * Return: True if exact match feature is supported. + */ +bool rvu_npc_exact_can_disable_feature(struct rvu *rvu) +{ + struct npc_exact_table *table = rvu->hw->table; + bool empty; + + if (!rvu->hw->cap.npc_exact_match_enabled) + return false; + + mutex_lock(&table->lock); + empty = list_empty(&table->lhead_gbl); + mutex_unlock(&table->lock); + + return empty; +} + +/** + * rvu_npc_exact_disable_feature - Disable feature. + * @rvu: resource virtualization unit. + */ +void rvu_npc_exact_disable_feature(struct rvu *rvu) +{ + rvu->hw->cap.npc_exact_match_enabled = false; +} + +/** + * rvu_npc_exact_reset - Delete and free all entry which match pcifunc. + * @rvu: resource virtualization unit. + * @pcifunc: PCI func to match. + */ +void rvu_npc_exact_reset(struct rvu *rvu, u16 pcifunc) +{ + struct npc_exact_table *table = rvu->hw->table; + struct npc_exact_table_entry *tmp, *iter; + u32 seq_id; + + mutex_lock(&table->lock); + list_for_each_entry_safe(iter, tmp, &table->lhead_gbl, glist) { + if (pcifunc != iter->pcifunc) + continue; + + seq_id = iter->seq_id; + dev_dbg(rvu->dev, "%s: resetting pcifun=%d seq_id=%u\n", __func__, + pcifunc, seq_id); + + mutex_unlock(&table->lock); + rvu_npc_exact_del_table_entry_by_id(rvu, seq_id); + mutex_lock(&table->lock); + } + mutex_unlock(&table->lock); +} + +/** + * rvu_npc_exact_init - initialize exact match table + * @rvu: resource virtualization unit. + * + * Initialize HW and SW resources to manage 4way-2K table and fully + * associative 32-entry mcam table. + * Return: 0 upon success. + */ +int rvu_npc_exact_init(struct rvu *rvu) +{ + u64 bcast_mcast_val, bcast_mcast_mask; + struct npc_exact_table *table; + u64 exact_val, exact_mask; + u64 chan_val, chan_mask; + u8 cgx_id, lmac_id; + u32 *drop_mcam_idx; + u16 max_lmac_cnt; + u64 npc_const3; + int table_size; + int blkaddr; + u16 pcifunc; + int err, i; + u64 cfg; + bool rc; + + /* Read NPC_AF_CONST3 and check for have exact + * match functionality is present + */ + blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NPC, 0); + if (blkaddr < 0) { + dev_err(rvu->dev, "%s: NPC block not implemented\n", __func__); + return -EINVAL; + } + + /* Check exact match feature is supported */ + npc_const3 = rvu_read64(rvu, blkaddr, NPC_AF_CONST3); + if (!(npc_const3 & BIT_ULL(62))) { + dev_info(rvu->dev, "%s: No support for exact match support\n", + __func__); + return 0; + } + + /* Check if kex profile has enabled EXACT match nibble */ + cfg = rvu_read64(rvu, blkaddr, NPC_AF_INTFX_KEX_CFG(NIX_INTF_RX)); + if (!(cfg & NPC_EXACT_NIBBLE_HIT)) { + dev_info(rvu->dev, "%s: NPC exact match nibble not enabled in KEX profile\n", + __func__); + return 0; + } + + /* Set capability to true */ + rvu->hw->cap.npc_exact_match_enabled = true; + + table = kmalloc(sizeof(*table), GFP_KERNEL); + if (!table) + return -ENOMEM; + + dev_dbg(rvu->dev, "%s: Memory allocation for table success\n", __func__); + memset(table, 0, sizeof(*table)); + rvu->hw->table = table; + + /* Read table size, ways and depth */ + table->mem_table.depth = FIELD_GET(GENMASK_ULL(31, 24), npc_const3); + table->mem_table.ways = FIELD_GET(GENMASK_ULL(19, 16), npc_const3); + table->cam_table.depth = FIELD_GET(GENMASK_ULL(15, 0), npc_const3); + + dev_dbg(rvu->dev, "%s: NPC exact match 4way_2k table(ways=%d, depth=%d)\n", + __func__, table->mem_table.ways, table->cam_table.depth); + + /* Check if depth of table is not a sequre of 2 + * TODO: why _builtin_popcount() is not working ? + */ + if ((table->mem_table.depth & (table->mem_table.depth - 1)) != 0) { + dev_err(rvu->dev, + "%s: NPC exact match 4way_2k table depth(%d) is not square of 2\n", + __func__, table->mem_table.depth); + return -EINVAL; + } + + table_size = table->mem_table.depth * table->mem_table.ways; + + /* Allocate bitmap for 4way 2K table */ + table->mem_table.bmap = devm_kcalloc(rvu->dev, BITS_TO_LONGS(table_size), + sizeof(long), GFP_KERNEL); + if (!table->mem_table.bmap) + return -ENOMEM; + + dev_dbg(rvu->dev, "%s: Allocated bitmap for 4way 2K entry table\n", __func__); + + /* Allocate bitmap for 32 entry mcam */ + table->cam_table.bmap = devm_kcalloc(rvu->dev, 1, sizeof(long), GFP_KERNEL); + + if (!table->cam_table.bmap) + return -ENOMEM; + + dev_dbg(rvu->dev, "%s: Allocated bitmap for 32 entry cam\n", __func__); + + table->tot_ids = (table->mem_table.depth * table->mem_table.ways) + table->cam_table.depth; + table->id_bmap = devm_kcalloc(rvu->dev, BITS_TO_LONGS(table->tot_ids), + table->tot_ids, GFP_KERNEL); + + if (!table->id_bmap) + return -ENOMEM; + + dev_dbg(rvu->dev, "%s: Allocated bitmap for id map (total=%d)\n", + __func__, table->tot_ids); + + /* Initialize list heads for npc_exact_table entries. + * This entry is used by debugfs to show entries in + * exact match table. + */ + for (i = 0; i < NPC_EXACT_TBL_MAX_WAYS; i++) + INIT_LIST_HEAD(&table->lhead_mem_tbl_entry[i]); + + INIT_LIST_HEAD(&table->lhead_cam_tbl_entry); + INIT_LIST_HEAD(&table->lhead_gbl); + + mutex_init(&table->lock); + + rvu_exact_config_secret_key(rvu); + rvu_exact_config_search_key(rvu); + + rvu_exact_config_table_mask(rvu); + rvu_exact_config_result_ctrl(rvu, table->mem_table.depth); + + /* - No drop rule for LBK + * - Drop rules for SDP and each LMAC. + */ + exact_val = !NPC_EXACT_RESULT_HIT; + exact_mask = NPC_EXACT_RESULT_HIT; + + /* nibble - 3 2 1 0 + * L3B L3M L2B L2M + */ + bcast_mcast_val = 0b0000; + bcast_mcast_mask = 0b0011; + + /* Install SDP drop rule */ + drop_mcam_idx = &table->num_drop_rules; + + max_lmac_cnt = rvu->cgx_cnt_max * MAX_LMAC_PER_CGX + PF_CGXMAP_BASE; + for (i = PF_CGXMAP_BASE; i < max_lmac_cnt; i++) { + if (rvu->pf2cgxlmac_map[i] == 0xFF) + continue; + + rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[i], &cgx_id, &lmac_id); + + rc = rvu_npc_exact_calc_drop_rule_chan_and_mask(rvu, NIX_INTF_TYPE_CGX, cgx_id, + lmac_id, &chan_val, &chan_mask); + if (!rc) { + dev_err(rvu->dev, + "%s: failed, info chan_val=0x%llx chan_mask=0x%llx rule_id=%d\n", + __func__, chan_val, chan_mask, *drop_mcam_idx); + return -EINVAL; + } + + /* Filter rules are only for PF */ + pcifunc = RVU_PFFUNC(i, 0); + + dev_dbg(rvu->dev, + "%s:Drop rule cgx=%d lmac=%d chan(val=0x%llx, mask=0x%llx\n", + __func__, cgx_id, lmac_id, chan_val, chan_mask); + + rc = rvu_npc_exact_save_drop_rule_chan_and_mask(rvu, table->num_drop_rules, + chan_val, chan_mask, pcifunc); + if (!rc) { + dev_err(rvu->dev, + "%s: failed to set drop info for cgx=%d, lmac=%d, chan=%llx\n", + __func__, cgx_id, lmac_id, chan_val); + return -EINVAL; + } + + err = npc_install_mcam_drop_rule(rvu, *drop_mcam_idx, + &table->counter_idx[*drop_mcam_idx], + chan_val, chan_mask, + exact_val, exact_mask, + bcast_mcast_val, bcast_mcast_mask); + if (err) { + dev_err(rvu->dev, + "failed to configure drop rule (cgx=%d lmac=%d)\n", + cgx_id, lmac_id); + return err; + } + + (*drop_mcam_idx)++; + } + + dev_info(rvu->dev, "initialized exact match table successfully\n"); + return 0; +} diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.h new file mode 100644 index 000000000000..3efeb09c58de --- /dev/null +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.h @@ -0,0 +1,233 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Marvell RVU Admin Function driver + * + * Copyright (C) 2022 Marvell. + * + */ + +#ifndef __RVU_NPC_HASH_H +#define __RVU_NPC_HASH_H + +#define RVU_NPC_HASH_SECRET_KEY0 0xa9d5af4c9fbc76b1 +#define RVU_NPC_HASH_SECRET_KEY1 0xa9d5af4c9fbc87b4 +#define RVU_NPC_HASH_SECRET_KEY2 0x5954c9e7 + +#define NPC_MAX_HASH 2 +#define NPC_MAX_HASH_MASK 2 + +#define KEX_LD_CFG_USE_HASH(use_hash, bytesm1, hdr_ofs, ena, flags_ena, key_ofs) \ + ((use_hash) << 20 | ((bytesm1) << 16) | ((hdr_ofs) << 8) | \ + ((ena) << 7) | ((flags_ena) << 6) | ((key_ofs) & 0x3F)) +#define KEX_LD_CFG_HASH(hdr_ofs, bytesm1, lt_en, lid_en, lid, ltype_match, ltype_mask) \ + (((hdr_ofs) << 32) | ((bytesm1) << 16) | \ + ((lt_en) << 12) | ((lid_en) << 11) | ((lid) << 8) | \ + ((ltype_match) << 4) | ((ltype_mask) & 0xF)) + +#define SET_KEX_LD_HASH(intf, ld, cfg) \ + rvu_write64(rvu, blkaddr, \ + NPC_AF_INTFX_HASHX_CFG(intf, ld), cfg) + +#define SET_KEX_LD_HASH_MASK(intf, ld, mask_idx, cfg) \ + rvu_write64(rvu, blkaddr, \ + NPC_AF_INTFX_HASHX_MASKX(intf, ld, mask_idx), cfg) + +#define SET_KEX_LD_HASH_CTRL(intf, ld, cfg) \ + rvu_write64(rvu, blkaddr, \ + NPC_AF_INTFX_HASHX_RESULT_CTRL(intf, ld), cfg) + +struct npc_mcam_kex_hash { + /* NPC_AF_INTF(0..1)_LID(0..7)_LT(0..15)_LD(0..1)_CFG */ + bool lid_lt_ld_hash_en[NPC_MAX_INTF][NPC_MAX_LID][NPC_MAX_LT][NPC_MAX_LD]; + /* NPC_AF_INTF(0..1)_HASH(0..1)_CFG */ + u64 hash[NPC_MAX_INTF][NPC_MAX_HASH]; + /* NPC_AF_INTF(0..1)_HASH(0..1)_MASK(0..1) */ + u64 hash_mask[NPC_MAX_INTF][NPC_MAX_HASH][NPC_MAX_HASH_MASK]; + /* NPC_AF_INTF(0..1)_HASH(0..1)_RESULT_CTRL */ + u64 hash_ctrl[NPC_MAX_INTF][NPC_MAX_HASH]; +} __packed; + +void npc_update_field_hash(struct rvu *rvu, u8 intf, + struct mcam_entry *entry, + int blkaddr, + u64 features, + struct flow_msg *pkt, + struct flow_msg *mask, + struct flow_msg *opkt, + struct flow_msg *omask); +void npc_config_secret_key(struct rvu *rvu, int blkaddr); +void npc_program_mkex_hash(struct rvu *rvu, int blkaddr); +u32 npc_field_hash_calc(u64 *ldata, struct npc_mcam_kex_hash *mkex_hash, + u64 *secret_key, u8 intf, u8 hash_idx); + +static struct npc_mcam_kex_hash npc_mkex_hash_default __maybe_unused = { + .lid_lt_ld_hash_en = { + [NIX_INTF_RX] = { + [NPC_LID_LC] = { + [NPC_LT_LC_IP6] = { + true, + true, + }, + }, + }, + + [NIX_INTF_TX] = { + [NPC_LID_LC] = { + [NPC_LT_LC_IP6] = { + true, + true, + }, + }, + }, + }, + + .hash = { + [NIX_INTF_RX] = { + KEX_LD_CFG_HASH(0x8ULL, 0xf, 0x1, 0x1, NPC_LID_LC, NPC_LT_LC_IP6, 0xf), + KEX_LD_CFG_HASH(0x18ULL, 0xf, 0x1, 0x1, NPC_LID_LC, NPC_LT_LC_IP6, 0xf), + }, + + [NIX_INTF_TX] = { + KEX_LD_CFG_HASH(0x8ULL, 0xf, 0x1, 0x1, NPC_LID_LC, NPC_LT_LC_IP6, 0xf), + KEX_LD_CFG_HASH(0x18ULL, 0xf, 0x1, 0x1, NPC_LID_LC, NPC_LT_LC_IP6, 0xf), + }, + }, + + .hash_mask = { + [NIX_INTF_RX] = { + [0] = { + GENMASK_ULL(63, 0), + GENMASK_ULL(63, 0), + }, + [1] = { + GENMASK_ULL(63, 0), + GENMASK_ULL(63, 0), + }, + }, + + [NIX_INTF_TX] = { + [0] = { + GENMASK_ULL(63, 0), + GENMASK_ULL(63, 0), + }, + [1] = { + GENMASK_ULL(63, 0), + GENMASK_ULL(63, 0), + }, + }, + }, + + .hash_ctrl = { + [NIX_INTF_RX] = { + [0] = GENMASK_ULL(63, 32), /* MSB 32 bit is mask and LSB 32 bit is offset. */ + [1] = GENMASK_ULL(63, 32), /* MSB 32 bit is mask and LSB 32 bit is offset. */ + }, + + [NIX_INTF_TX] = { + [0] = GENMASK_ULL(63, 32), /* MSB 32 bit is mask and LSB 32 bit is offset. */ + [1] = GENMASK_ULL(63, 32), /* MSB 32 bit is mask and LSB 32 bit is offset. */ + }, + }, +}; + +/* If exact match table support is enabled, enable drop rules */ +#define NPC_MCAM_DROP_RULE_MAX 30 +#define NPC_MCAM_SDP_DROP_RULE_IDX 0 + +#define RVU_PFFUNC(pf, func) \ + ((((pf) & RVU_PFVF_PF_MASK) << RVU_PFVF_PF_SHIFT) | \ + (((func) & RVU_PFVF_FUNC_MASK) << RVU_PFVF_FUNC_SHIFT)) + +enum npc_exact_opc_type { + NPC_EXACT_OPC_MEM, + NPC_EXACT_OPC_CAM, +}; + +struct npc_exact_table_entry { + struct list_head list; + struct list_head glist; + u32 seq_id; /* Sequence number of entry */ + u32 index; /* Mem table or cam table index */ + u32 mcam_idx; + /* Mcam index. This is valid only if "cmd" field is false */ + enum npc_exact_opc_type opc_type; + u16 chan; + u16 pcifunc; + u8 ways; + u8 mac[ETH_ALEN]; + u8 ctype; + u8 cgx_id; + u8 lmac_id; + bool cmd; /* Is added by ethtool command ? */ +}; + +struct npc_exact_table { + struct mutex lock; /* entries update lock */ + unsigned long *id_bmap; + int num_drop_rules; + u32 tot_ids; + u16 cnt_cmd_rules[NPC_MCAM_DROP_RULE_MAX]; + u16 counter_idx[NPC_MCAM_DROP_RULE_MAX]; + bool promisc_mode[NPC_MCAM_DROP_RULE_MAX]; + struct { + int ways; + int depth; + unsigned long *bmap; + u64 mask; // Masks before hash calculation. + u16 hash_mask; // 11 bits for hash mask + u16 hash_offset; // 11 bits offset + } mem_table; + + struct { + int depth; + unsigned long *bmap; + } cam_table; + + struct { + bool valid; + u16 chan_val; + u16 chan_mask; + u16 pcifunc; + u8 drop_rule_idx; + } drop_rule_map[NPC_MCAM_DROP_RULE_MAX]; + +#define NPC_EXACT_TBL_MAX_WAYS 4 + + struct list_head lhead_mem_tbl_entry[NPC_EXACT_TBL_MAX_WAYS]; + int mem_tbl_entry_cnt; + + struct list_head lhead_cam_tbl_entry; + int cam_tbl_entry_cnt; + + struct list_head lhead_gbl; +}; + +bool rvu_npc_exact_has_match_table(struct rvu *rvu); +u32 rvu_npc_exact_get_max_entries(struct rvu *rvu); +int rvu_npc_exact_init(struct rvu *rvu); +int rvu_npc_exact_mac_addr_reset(struct rvu *rvu, struct cgx_mac_addr_reset_req *req, + struct msg_rsp *rsp); + +int rvu_npc_exact_mac_addr_update(struct rvu *rvu, + struct cgx_mac_addr_update_req *req, + struct cgx_mac_addr_update_rsp *rsp); + +int rvu_npc_exact_mac_addr_add(struct rvu *rvu, + struct cgx_mac_addr_add_req *req, + struct cgx_mac_addr_add_rsp *rsp); + +int rvu_npc_exact_mac_addr_del(struct rvu *rvu, + struct cgx_mac_addr_del_req *req, + struct msg_rsp *rsp); + +int rvu_npc_exact_mac_addr_set(struct rvu *rvu, struct cgx_mac_addr_set_or_get *req, + struct cgx_mac_addr_set_or_get *rsp); + +void rvu_npc_exact_reset(struct rvu *rvu, u16 pcifunc); + +bool rvu_npc_exact_can_disable_feature(struct rvu *rvu); +void rvu_npc_exact_disable_feature(struct rvu *rvu); +void rvu_npc_exact_reset(struct rvu *rvu, u16 pcifunc); +u16 rvu_npc_exact_drop_rule_to_pcifunc(struct rvu *rvu, u32 drop_rule_idx); +int rvu_npc_exact_promisc_disable(struct rvu *rvu, u16 pcifunc); +int rvu_npc_exact_promisc_enable(struct rvu *rvu, u16 pcifunc); +#endif /* RVU_NPC_HASH_H */ diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h index 22cd751613cd..77a9ade91f3e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h @@ -565,7 +565,13 @@ #define NPC_AF_PCK_DEF_OIP4 (0x00620) #define NPC_AF_PCK_DEF_OIP6 (0x00630) #define NPC_AF_PCK_DEF_IIP4 (0x00640) +#define NPC_AF_INTFX_HASHX_RESULT_CTRL(a, b) (0x006c0 | (a) << 4 | (b) << 3) +#define NPC_AF_INTFX_HASHX_MASKX(a, b, c) (0x00700 | (a) << 5 | (b) << 4 | (c) << 3) #define NPC_AF_KEX_LDATAX_FLAGS_CFG(a) (0x00800 | (a) << 3) +#define NPC_AF_INTFX_HASHX_CFG(a, b) (0x00b00 | (a) << 6 | (b) << 4) +#define NPC_AF_INTFX_SECRET_KEY0(a) (0x00e00 | (a) << 3) +#define NPC_AF_INTFX_SECRET_KEY1(a) (0x00e20 | (a) << 3) +#define NPC_AF_INTFX_SECRET_KEY2(a) (0x00e40 | (a) << 3) #define NPC_AF_INTFX_KEX_CFG(a) (0x01010 | (a) << 8) #define NPC_AF_PKINDX_ACTION0(a) (0x80000ull | (a) << 6) #define NPC_AF_PKINDX_ACTION1(a) (0x80008ull | (a) << 6) @@ -599,6 +605,15 @@ #define NPC_AF_DBG_DATAX(a) (0x3001400 | (a) << 4) #define NPC_AF_DBG_RESULTX(a) (0x3001800 | (a) << 4) +#define NPC_AF_EXACT_MEM_ENTRY(a, b) (0x300000 | (a) << 15 | (b) << 3) +#define NPC_AF_EXACT_CAM_ENTRY(a) (0xC00 | (a) << 3) +#define NPC_AF_INTFX_EXACT_MASK(a) (0x660 | (a) << 3) +#define NPC_AF_INTFX_EXACT_RESULT_CTL(a)(0x680 | (a) << 3) +#define NPC_AF_INTFX_EXACT_CFG(a) (0xA00 | (a) << 3) +#define NPC_AF_INTFX_EXACT_SECRET0(a) (0xE00 | (a) << 3) +#define NPC_AF_INTFX_EXACT_SECRET1(a) (0xE20 | (a) << 3) +#define NPC_AF_INTFX_EXACT_SECRET2(a) (0xE40 | (a) << 3) + #define NPC_AF_MCAMEX_BANKX_CAMX_INTF(a, b, c) ({ \ u64 offset; \ \ diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index ce2766317c0b..e795f9ee76dd 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -314,8 +314,8 @@ struct otx2_flow_config { #define OTX2_VF_VLAN_TX_INDEX 1 u16 max_flows; u8 dmacflt_max_flows; - u8 *bmap_to_dmacindex; - unsigned long dmacflt_bmap; + u32 *bmap_to_dmacindex; + unsigned long *dmacflt_bmap; struct list_head flow_list; }; @@ -895,9 +895,9 @@ int otx2_setup_tc(struct net_device *netdev, enum tc_setup_type type, int otx2_tc_alloc_ent_bitmap(struct otx2_nic *nic); /* CGX/RPM DMAC filters support */ int otx2_dmacflt_get_max_cnt(struct otx2_nic *pf); -int otx2_dmacflt_add(struct otx2_nic *pf, const u8 *mac, u8 bit_pos); -int otx2_dmacflt_remove(struct otx2_nic *pf, const u8 *mac, u8 bit_pos); -int otx2_dmacflt_update(struct otx2_nic *pf, u8 *mac, u8 bit_pos); +int otx2_dmacflt_add(struct otx2_nic *pf, const u8 *mac, u32 bit_pos); +int otx2_dmacflt_remove(struct otx2_nic *pf, const u8 *mac, u32 bit_pos); +int otx2_dmacflt_update(struct otx2_nic *pf, u8 *mac, u32 bit_pos); void otx2_dmacflt_reinstall_flows(struct otx2_nic *pf); void otx2_dmacflt_update_pfmac_flow(struct otx2_nic *pfvf); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c index 2ec800f741d8..80d853b343f9 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_dmac_flt.c @@ -8,7 +8,7 @@ #include "otx2_common.h" static int otx2_dmacflt_do_add(struct otx2_nic *pf, const u8 *mac, - u8 *dmac_index) + u32 *dmac_index) { struct cgx_mac_addr_add_req *req; struct cgx_mac_addr_add_rsp *rsp; @@ -35,9 +35,10 @@ static int otx2_dmacflt_do_add(struct otx2_nic *pf, const u8 *mac, return err; } -static int otx2_dmacflt_add_pfmac(struct otx2_nic *pf) +static int otx2_dmacflt_add_pfmac(struct otx2_nic *pf, u32 *dmac_index) { struct cgx_mac_addr_set_or_get *req; + struct cgx_mac_addr_set_or_get *rsp; int err; mutex_lock(&pf->mbox.lock); @@ -48,16 +49,31 @@ static int otx2_dmacflt_add_pfmac(struct otx2_nic *pf) return -ENOMEM; } + req->index = *dmac_index; + ether_addr_copy(req->mac_addr, pf->netdev->dev_addr); err = otx2_sync_mbox_msg(&pf->mbox); + if (err) + goto out; + + rsp = (struct cgx_mac_addr_set_or_get *) + otx2_mbox_get_rsp(&pf->mbox.mbox, 0, &req->hdr); + + if (IS_ERR_OR_NULL(rsp)) { + err = -EINVAL; + goto out; + } + + *dmac_index = rsp->index; +out: mutex_unlock(&pf->mbox.lock); return err; } -int otx2_dmacflt_add(struct otx2_nic *pf, const u8 *mac, u8 bit_pos) +int otx2_dmacflt_add(struct otx2_nic *pf, const u8 *mac, u32 bit_pos) { - u8 *dmacindex; + u32 *dmacindex; /* Store dmacindex returned by CGX/RPM driver which will * be used for macaddr update/remove @@ -65,13 +81,13 @@ int otx2_dmacflt_add(struct otx2_nic *pf, const u8 *mac, u8 bit_pos) dmacindex = &pf->flow_cfg->bmap_to_dmacindex[bit_pos]; if (ether_addr_equal(mac, pf->netdev->dev_addr)) - return otx2_dmacflt_add_pfmac(pf); + return otx2_dmacflt_add_pfmac(pf, dmacindex); else return otx2_dmacflt_do_add(pf, mac, dmacindex); } static int otx2_dmacflt_do_remove(struct otx2_nic *pfvf, const u8 *mac, - u8 dmac_index) + u32 dmac_index) { struct cgx_mac_addr_del_req *req; int err; @@ -91,9 +107,9 @@ static int otx2_dmacflt_do_remove(struct otx2_nic *pfvf, const u8 *mac, return err; } -static int otx2_dmacflt_remove_pfmac(struct otx2_nic *pf) +static int otx2_dmacflt_remove_pfmac(struct otx2_nic *pf, u32 dmac_index) { - struct msg_req *req; + struct cgx_mac_addr_reset_req *req; int err; mutex_lock(&pf->mbox.lock); @@ -102,6 +118,7 @@ static int otx2_dmacflt_remove_pfmac(struct otx2_nic *pf) mutex_unlock(&pf->mbox.lock); return -ENOMEM; } + req->index = dmac_index; err = otx2_sync_mbox_msg(&pf->mbox); @@ -110,12 +127,12 @@ static int otx2_dmacflt_remove_pfmac(struct otx2_nic *pf) } int otx2_dmacflt_remove(struct otx2_nic *pf, const u8 *mac, - u8 bit_pos) + u32 bit_pos) { - u8 dmacindex = pf->flow_cfg->bmap_to_dmacindex[bit_pos]; + u32 dmacindex = pf->flow_cfg->bmap_to_dmacindex[bit_pos]; if (ether_addr_equal(mac, pf->netdev->dev_addr)) - return otx2_dmacflt_remove_pfmac(pf); + return otx2_dmacflt_remove_pfmac(pf, dmacindex); else return otx2_dmacflt_do_remove(pf, mac, dmacindex); } @@ -144,6 +161,12 @@ int otx2_dmacflt_get_max_cnt(struct otx2_nic *pf) rsp = (struct cgx_max_dmac_entries_get_rsp *) otx2_mbox_get_rsp(&pf->mbox.mbox, 0, &msg->hdr); + + if (IS_ERR_OR_NULL(rsp)) { + err = -EINVAL; + goto out; + } + pf->flow_cfg->dmacflt_max_flows = rsp->max_dmac_filters; out: @@ -151,9 +174,10 @@ out: return err; } -int otx2_dmacflt_update(struct otx2_nic *pf, u8 *mac, u8 bit_pos) +int otx2_dmacflt_update(struct otx2_nic *pf, u8 *mac, u32 bit_pos) { struct cgx_mac_addr_update_req *req; + struct cgx_mac_addr_update_rsp *rsp; int rc; mutex_lock(&pf->mbox.lock); @@ -167,8 +191,19 @@ int otx2_dmacflt_update(struct otx2_nic *pf, u8 *mac, u8 bit_pos) ether_addr_copy(req->mac_addr, mac); req->index = pf->flow_cfg->bmap_to_dmacindex[bit_pos]; + + /* check the response and change index */ + rc = otx2_sync_mbox_msg(&pf->mbox); + if (rc) + goto out; + + rsp = (struct cgx_mac_addr_update_rsp *) + otx2_mbox_get_rsp(&pf->mbox.mbox, 0, &req->hdr); + pf->flow_cfg->bmap_to_dmacindex[bit_pos] = rsp->index; + +out: mutex_unlock(&pf->mbox.lock); return rc; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c index 2dd192b5e4e0..709fc0114fbd 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c @@ -18,7 +18,7 @@ struct otx2_flow { struct ethtool_rx_flow_spec flow_spec; struct list_head list; u32 location; - u16 entry; + u32 entry; bool is_vf; u8 rss_ctx_id; #define DMAC_FILTER_RULE BIT(0) @@ -232,6 +232,9 @@ static int otx2_mcam_entry_init(struct otx2_nic *pfvf) return 0; } +/* TODO : revisit on size */ +#define OTX2_DMAC_FLTR_BITMAP_SZ (4 * 2048 + 32) + int otx2vf_mcam_flow_init(struct otx2_nic *pfvf) { struct otx2_flow_config *flow_cfg; @@ -242,6 +245,12 @@ int otx2vf_mcam_flow_init(struct otx2_nic *pfvf) if (!pfvf->flow_cfg) return -ENOMEM; + pfvf->flow_cfg->dmacflt_bmap = devm_kcalloc(pfvf->dev, + BITS_TO_LONGS(OTX2_DMAC_FLTR_BITMAP_SZ), + sizeof(long), GFP_KERNEL); + if (!pfvf->flow_cfg->dmacflt_bmap) + return -ENOMEM; + flow_cfg = pfvf->flow_cfg; INIT_LIST_HEAD(&flow_cfg->flow_list); flow_cfg->max_flows = 0; @@ -259,6 +268,12 @@ int otx2_mcam_flow_init(struct otx2_nic *pf) if (!pf->flow_cfg) return -ENOMEM; + pf->flow_cfg->dmacflt_bmap = devm_kcalloc(pf->dev, + BITS_TO_LONGS(OTX2_DMAC_FLTR_BITMAP_SZ), + sizeof(long), GFP_KERNEL); + if (!pf->flow_cfg->dmacflt_bmap) + return -ENOMEM; + INIT_LIST_HEAD(&pf->flow_cfg->flow_list); /* Allocate bare minimum number of MCAM entries needed for @@ -284,7 +299,7 @@ int otx2_mcam_flow_init(struct otx2_nic *pf) return 0; pf->flow_cfg->bmap_to_dmacindex = - devm_kzalloc(pf->dev, sizeof(u8) * + devm_kzalloc(pf->dev, sizeof(u32) * pf->flow_cfg->dmacflt_max_flows, GFP_KERNEL); @@ -355,7 +370,7 @@ int otx2_add_macfilter(struct net_device *netdev, const u8 *mac) { struct otx2_nic *pf = netdev_priv(netdev); - if (!bitmap_empty(&pf->flow_cfg->dmacflt_bmap, + if (!bitmap_empty(pf->flow_cfg->dmacflt_bmap, pf->flow_cfg->dmacflt_max_flows)) netdev_warn(netdev, "Add %pM to CGX/RPM DMAC filters list as well\n", @@ -438,7 +453,7 @@ int otx2_get_maxflows(struct otx2_flow_config *flow_cfg) return 0; if (flow_cfg->nr_flows == flow_cfg->max_flows || - !bitmap_empty(&flow_cfg->dmacflt_bmap, + !bitmap_empty(flow_cfg->dmacflt_bmap, flow_cfg->dmacflt_max_flows)) return flow_cfg->max_flows + flow_cfg->dmacflt_max_flows; else @@ -1010,7 +1025,7 @@ static int otx2_add_flow_with_pfmac(struct otx2_nic *pfvf, otx2_add_flow_to_list(pfvf, pf_mac); pfvf->flow_cfg->nr_flows++; - set_bit(0, &pfvf->flow_cfg->dmacflt_bmap); + set_bit(0, pfvf->flow_cfg->dmacflt_bmap); return 0; } @@ -1064,7 +1079,7 @@ int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc) return otx2_dmacflt_update(pfvf, eth_hdr->h_dest, flow->entry); - if (bitmap_full(&flow_cfg->dmacflt_bmap, + if (bitmap_full(flow_cfg->dmacflt_bmap, flow_cfg->dmacflt_max_flows)) { netdev_warn(pfvf->netdev, "Can't insert the rule %d as max allowed dmac filters are %d\n", @@ -1078,17 +1093,17 @@ int otx2_add_flow(struct otx2_nic *pfvf, struct ethtool_rxnfc *nfc) } /* Install PF mac address to DMAC filter list */ - if (!test_bit(0, &flow_cfg->dmacflt_bmap)) + if (!test_bit(0, flow_cfg->dmacflt_bmap)) otx2_add_flow_with_pfmac(pfvf, flow); flow->rule_type |= DMAC_FILTER_RULE; - flow->entry = find_first_zero_bit(&flow_cfg->dmacflt_bmap, + flow->entry = find_first_zero_bit(flow_cfg->dmacflt_bmap, flow_cfg->dmacflt_max_flows); fsp->location = flow_cfg->max_flows + flow->entry; flow->flow_spec.location = fsp->location; flow->location = fsp->location; - set_bit(flow->entry, &flow_cfg->dmacflt_bmap); + set_bit(flow->entry, flow_cfg->dmacflt_bmap); otx2_dmacflt_add(pfvf, eth_hdr->h_dest, flow->entry); } else { @@ -1154,11 +1169,12 @@ static void otx2_update_rem_pfmac(struct otx2_nic *pfvf, int req) if (req == DMAC_ADDR_DEL) { otx2_dmacflt_remove(pfvf, eth_hdr->h_dest, 0); - clear_bit(0, &pfvf->flow_cfg->dmacflt_bmap); + clear_bit(0, pfvf->flow_cfg->dmacflt_bmap); found = true; } else { ether_addr_copy(eth_hdr->h_dest, pfvf->netdev->dev_addr); + otx2_dmacflt_update(pfvf, eth_hdr->h_dest, 0); } break; @@ -1194,12 +1210,12 @@ int otx2_remove_flow(struct otx2_nic *pfvf, u32 location) err = otx2_dmacflt_remove(pfvf, eth_hdr->h_dest, flow->entry); - clear_bit(flow->entry, &flow_cfg->dmacflt_bmap); + clear_bit(flow->entry, flow_cfg->dmacflt_bmap); /* If all dmac filters are removed delete macfilter with * interface mac address and configure CGX/RPM block in * promiscuous mode */ - if (bitmap_weight(&flow_cfg->dmacflt_bmap, + if (bitmap_weight(flow_cfg->dmacflt_bmap, flow_cfg->dmacflt_max_flows) == 1) otx2_update_rem_pfmac(pfvf, DMAC_ADDR_DEL); } else { diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 9106c359e64c..9376d0e62914 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1120,7 +1120,7 @@ static int otx2_cgx_config_loopback(struct otx2_nic *pf, bool enable) struct msg_req *msg; int err; - if (enable && !bitmap_empty(&pf->flow_cfg->dmacflt_bmap, + if (enable && !bitmap_empty(pf->flow_cfg->dmacflt_bmap, pf->flow_cfg->dmacflt_max_flows)) netdev_warn(pf->netdev, "CGX/RPM internal loopback might not work as DMAC filters are active\n"); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index 3baeafc40807..a18e8efd0f1e 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -624,7 +624,7 @@ static void otx2_sqe_add_ext(struct otx2_nic *pfvf, struct otx2_snd_queue *sq, ext->subdc = NIX_SUBDC_EXT; if (skb_shinfo(skb)->gso_size) { ext->lso = 1; - ext->lso_sb = skb_transport_offset(skb) + tcp_hdrlen(skb); + ext->lso_sb = skb_tcp_all_headers(skb); ext->lso_mps = skb_shinfo(skb)->gso_size; /* Only TSOv4 and TSOv6 GSO offloads are supported */ @@ -931,7 +931,7 @@ static bool is_hw_tso_supported(struct otx2_nic *pfvf, * be correctly modified, hence don't offload such TSO segments. */ - payload_len = skb->len - (skb_transport_offset(skb) + tcp_hdrlen(skb)); + payload_len = skb->len - skb_tcp_all_headers(skb); last_seg_size = payload_len % skb_shinfo(skb)->gso_size; if (last_seg_size && last_seg_size < 16) return false; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h index c88e8a436029..fbe62bbfb789 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h @@ -21,7 +21,7 @@ #define OTX2_HEAD_ROOM OTX2_ALIGN #define OTX2_ETH_HLEN (VLAN_ETH_HLEN + VLAN_HLEN) -#define OTX2_MIN_MTU 64 +#define OTX2_MIN_MTU 60 #define OTX2_MAX_GSO_SEGS 255 #define OTX2_MAX_FRAGS_IN_SQE 9 diff --git a/drivers/net/ethernet/marvell/prestera/Kconfig b/drivers/net/ethernet/marvell/prestera/Kconfig index b6f20e2034c6..f2f7663c3d10 100644 --- a/drivers/net/ethernet/marvell/prestera/Kconfig +++ b/drivers/net/ethernet/marvell/prestera/Kconfig @@ -8,6 +8,7 @@ config PRESTERA depends on NET_SWITCHDEV && VLAN_8021Q depends on BRIDGE || BRIDGE=n select NET_DEVLINK + select PHYLINK help This driver supports Marvell Prestera Switch ASICs family. diff --git a/drivers/net/ethernet/marvell/prestera/prestera.h b/drivers/net/ethernet/marvell/prestera/prestera.h index 6f754ae2a584..2f84d0fb4094 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera.h +++ b/drivers/net/ethernet/marvell/prestera/prestera.h @@ -7,6 +7,7 @@ #include <linux/notifier.h> #include <linux/skbuff.h> #include <linux/workqueue.h> +#include <linux/phylink.h> #include <net/devlink.h> #include <uapi/linux/if_ether.h> @@ -20,6 +21,26 @@ struct prestera_fw_rev { u16 sub; }; +struct prestera_flood_domain { + struct prestera_switch *sw; + struct list_head flood_domain_port_list; + u32 idx; +}; + +struct prestera_mdb_entry { + struct prestera_switch *sw; + struct prestera_flood_domain *flood_domain; + unsigned char addr[ETH_ALEN]; + u16 vid; +}; + +struct prestera_flood_domain_port { + struct prestera_flood_domain *flood_domain; + struct net_device *dev; + struct list_head flood_domain_port_node; + u16 vid; +}; + struct prestera_port_stats { u64 good_octets_received; u64 bad_octets_received; @@ -72,6 +93,7 @@ struct prestera_lag { struct prestera_flow_block; struct prestera_port_mac_state { + bool valid; u32 mode; u32 speed; bool oper; @@ -107,7 +129,8 @@ struct prestera_port_phy_config { struct prestera_port { struct net_device *dev; struct prestera_switch *sw; - struct prestera_flow_block *flow_block; + struct prestera_flow_block *ingress_flow_block; + struct prestera_flow_block *egress_flow_block; struct devlink_port dl_port; struct list_head lag_member; struct prestera_lag *lag; @@ -130,6 +153,13 @@ struct prestera_port { struct prestera_port_phy_config cfg_phy; struct prestera_port_mac_state state_mac; struct prestera_port_phy_state state_phy; + + struct phylink_config phy_config; + struct phylink *phy_link; + struct phylink_pcs phylink_pcs; + + /* protects state_mac */ + spinlock_t state_mac_lock; }; struct prestera_device { @@ -270,6 +300,7 @@ struct prestera_switch { u32 mtu_min; u32 mtu_max; u8 id; + struct device_node *np; struct prestera_router *router; struct prestera_lag *lags; struct prestera_counter *counter; @@ -320,6 +351,8 @@ void prestera_router_fini(struct prestera_switch *sw); struct prestera_port *prestera_find_port(struct prestera_switch *sw, u32 id); +struct prestera_switch *prestera_switch_get(struct net_device *dev); + int prestera_port_cfg_mac_read(struct prestera_port *port, struct prestera_port_mac_config *cfg); @@ -330,6 +363,10 @@ struct prestera_port *prestera_port_dev_lower_find(struct net_device *dev); void prestera_queue_work(struct work_struct *work); +int prestera_port_learning_set(struct prestera_port *port, bool learn_enable); +int prestera_port_uc_flood_set(struct prestera_port *port, bool flood); +int prestera_port_mc_flood_set(struct prestera_port *port, bool flood); + int prestera_port_pvid_set(struct prestera_port *port, u16 vid); bool prestera_netdev_check(const struct net_device *dev); @@ -337,9 +374,30 @@ bool prestera_netdev_check(const struct net_device *dev); int prestera_is_valid_mac_addr(struct prestera_port *port, const u8 *addr); bool prestera_port_is_lag_member(const struct prestera_port *port); +int prestera_lag_id(struct prestera_switch *sw, + struct net_device *lag_dev, u16 *lag_id); struct prestera_lag *prestera_lag_by_id(struct prestera_switch *sw, u16 id); u16 prestera_port_lag_id(const struct prestera_port *port); +struct prestera_mdb_entry * +prestera_mdb_entry_create(struct prestera_switch *sw, + const unsigned char *addr, u16 vid); +void prestera_mdb_entry_destroy(struct prestera_mdb_entry *mdb_entry); + +struct prestera_flood_domain * +prestera_flood_domain_create(struct prestera_switch *sw); +void prestera_flood_domain_destroy(struct prestera_flood_domain *flood_domain); + +int +prestera_flood_domain_port_create(struct prestera_flood_domain *flood_domain, + struct net_device *dev, + u16 vid); +void +prestera_flood_domain_port_destroy(struct prestera_flood_domain_port *port); +struct prestera_flood_domain_port * +prestera_flood_domain_port_find(struct prestera_flood_domain *flood_domain, + struct net_device *dev, u16 vid); + #endif /* _PRESTERA_H_ */ diff --git a/drivers/net/ethernet/marvell/prestera/prestera_acl.c b/drivers/net/ethernet/marvell/prestera/prestera_acl.c index 3a141f2db812..3d4b85f2d541 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_acl.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_acl.c @@ -61,6 +61,7 @@ struct prestera_acl_ruleset { u32 index; u16 pcl_id; bool offload; + bool ingress; }; struct prestera_acl_vtcam { @@ -70,6 +71,7 @@ struct prestera_acl_vtcam { u32 id; bool is_keymask_set; u8 lookup; + u8 direction; }; static const struct rhashtable_params prestera_acl_ruleset_ht_params = { @@ -93,23 +95,36 @@ static const struct rhashtable_params __prestera_acl_rule_entry_ht_params = { .automatic_shrinking = true, }; -int prestera_acl_chain_to_client(u32 chain_index, u32 *client) +int prestera_acl_chain_to_client(u32 chain_index, bool ingress, u32 *client) { - static const u32 client_map[] = { - PRESTERA_HW_COUNTER_CLIENT_LOOKUP_0, - PRESTERA_HW_COUNTER_CLIENT_LOOKUP_1, - PRESTERA_HW_COUNTER_CLIENT_LOOKUP_2 + static const u32 ingress_client_map[] = { + PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_0, + PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_1, + PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_2 }; - if (chain_index >= ARRAY_SIZE(client_map)) + if (!ingress) { + /* prestera supports only one chain on egress */ + if (chain_index > 0) + return -EINVAL; + + *client = PRESTERA_HW_COUNTER_CLIENT_EGRESS_LOOKUP; + return 0; + } + + if (chain_index >= ARRAY_SIZE(ingress_client_map)) return -EINVAL; - *client = client_map[chain_index]; + *client = ingress_client_map[chain_index]; return 0; } -static bool prestera_acl_chain_is_supported(u32 chain_index) +static bool prestera_acl_chain_is_supported(u32 chain_index, bool ingress) { + if (!ingress) + /* prestera supports only one chain on egress */ + return chain_index == 0; + return (chain_index & ~PRESTERA_ACL_CHAIN_MASK) == 0; } @@ -122,7 +137,7 @@ prestera_acl_ruleset_create(struct prestera_acl *acl, u32 uid = 0; int err; - if (!prestera_acl_chain_is_supported(chain_index)) + if (!prestera_acl_chain_is_supported(chain_index, block->ingress)) return ERR_PTR(-EINVAL); ruleset = kzalloc(sizeof(*ruleset), GFP_KERNEL); @@ -130,6 +145,7 @@ prestera_acl_ruleset_create(struct prestera_acl *acl, return ERR_PTR(-ENOMEM); ruleset->acl = acl; + ruleset->ingress = block->ingress; ruleset->ht_key.block = block; ruleset->ht_key.chain_index = chain_index; refcount_set(&ruleset->refcount, 1); @@ -172,13 +188,18 @@ int prestera_acl_ruleset_offload(struct prestera_acl_ruleset *ruleset) { struct prestera_acl_iface iface; u32 vtcam_id; + int dir; int err; + dir = ruleset->ingress ? + PRESTERA_HW_VTCAM_DIR_INGRESS : PRESTERA_HW_VTCAM_DIR_EGRESS; + if (ruleset->offload) return -EEXIST; err = prestera_acl_vtcam_id_get(ruleset->acl, ruleset->ht_key.chain_index, + dir, ruleset->keymask, &vtcam_id); if (err) goto err_vtcam_create; @@ -719,7 +740,7 @@ vtcam_found: return 0; } -int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, +int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, u8 dir, void *keymask, u32 *vtcam_id) { struct prestera_acl_vtcam *vtcam; @@ -731,7 +752,8 @@ int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, * fine for now */ list_for_each_entry(vtcam, &acl->vtcam_list, list) { - if (lookup != vtcam->lookup) + if (lookup != vtcam->lookup || + dir != vtcam->direction) continue; if (!keymask && !vtcam->is_keymask_set) { @@ -752,7 +774,7 @@ int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, return -ENOMEM; err = prestera_hw_vtcam_create(acl->sw, lookup, keymask, &new_vtcam_id, - PRESTERA_HW_VTCAM_DIR_INGRESS); + dir); if (err) { kfree(vtcam); @@ -765,6 +787,7 @@ int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, return 0; } + vtcam->direction = dir; vtcam->id = new_vtcam_id; vtcam->lookup = lookup; if (keymask) { diff --git a/drivers/net/ethernet/marvell/prestera/prestera_acl.h b/drivers/net/ethernet/marvell/prestera/prestera_acl.h index f963e1e0c0f0..03fc5b9dc925 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_acl.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_acl.h @@ -199,9 +199,9 @@ void prestera_acl_rule_keymask_pcl_id_set(struct prestera_acl_rule *rule, u16 pcl_id); -int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, +int prestera_acl_vtcam_id_get(struct prestera_acl *acl, u8 lookup, u8 dir, void *keymask, u32 *vtcam_id); int prestera_acl_vtcam_id_put(struct prestera_acl *acl, u32 vtcam_id); -int prestera_acl_chain_to_client(u32 chain_index, u32 *client); +int prestera_acl_chain_to_client(u32 chain_index, bool ingress, u32 *client); #endif /* _PRESTERA_ACL_H_ */ diff --git a/drivers/net/ethernet/marvell/prestera/prestera_ethtool.c b/drivers/net/ethernet/marvell/prestera/prestera_ethtool.c index 40d5b89573bb..1da7ff889417 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_ethtool.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_ethtool.c @@ -521,6 +521,9 @@ prestera_ethtool_get_link_ksettings(struct net_device *dev, ecmd->base.speed = SPEED_UNKNOWN; ecmd->base.duplex = DUPLEX_UNKNOWN; + if (port->phy_link) + return phylink_ethtool_ksettings_get(port->phy_link, ecmd); + ecmd->base.autoneg = port->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE; if (port->caps.type == PRESTERA_PORT_TYPE_TP) { @@ -648,6 +651,9 @@ prestera_ethtool_set_link_ksettings(struct net_device *dev, u8 adver_fec; int err; + if (port->phy_link) + return phylink_ethtool_ksettings_set(port->phy_link, ecmd); + err = prestera_port_type_set(ecmd, port); if (err) return err; @@ -782,28 +788,6 @@ static int prestera_ethtool_nway_reset(struct net_device *dev) return -EINVAL; } -void prestera_ethtool_port_state_changed(struct prestera_port *port, - struct prestera_port_event *evt) -{ - struct prestera_port_mac_state *smac = &port->state_mac; - - smac->oper = evt->data.mac.oper; - - if (smac->oper) { - smac->mode = evt->data.mac.mode; - smac->speed = evt->data.mac.speed; - smac->duplex = evt->data.mac.duplex; - smac->fc = evt->data.mac.fc; - smac->fec = evt->data.mac.fec; - } else { - smac->mode = PRESTERA_MAC_MODE_MAX; - smac->speed = SPEED_UNKNOWN; - smac->duplex = DUPLEX_UNKNOWN; - smac->fc = 0; - smac->fec = 0; - } -} - const struct ethtool_ops prestera_ethtool_ops = { .get_drvinfo = prestera_ethtool_get_drvinfo, .get_link_ksettings = prestera_ethtool_get_link_ksettings, diff --git a/drivers/net/ethernet/marvell/prestera/prestera_ethtool.h b/drivers/net/ethernet/marvell/prestera/prestera_ethtool.h index 9eb18e99dea6..bd5600886bc6 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_ethtool.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_ethtool.h @@ -11,7 +11,4 @@ struct prestera_port; extern const struct ethtool_ops prestera_ethtool_ops; -void prestera_ethtool_port_state_changed(struct prestera_port *port, - struct prestera_port_event *evt); - #endif /* _PRESTERA_ETHTOOL_H_ */ diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flow.c b/drivers/net/ethernet/marvell/prestera/prestera_flow.c index 05c3ad98eba9..2262693bd5cf 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flow.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_flow.c @@ -75,7 +75,9 @@ static void prestera_flow_block_destroy(void *cb_priv) } static struct prestera_flow_block * -prestera_flow_block_create(struct prestera_switch *sw, struct net *net) +prestera_flow_block_create(struct prestera_switch *sw, + struct net *net, + bool ingress) { struct prestera_flow_block *block; @@ -87,6 +89,7 @@ prestera_flow_block_create(struct prestera_switch *sw, struct net *net) INIT_LIST_HEAD(&block->template_list); block->net = net; block->sw = sw; + block->ingress = ingress; return block; } @@ -165,7 +168,8 @@ static int prestera_flow_block_unbind(struct prestera_flow_block *block, static struct prestera_flow_block * prestera_flow_block_get(struct prestera_switch *sw, struct flow_block_offload *f, - bool *register_block) + bool *register_block, + bool ingress) { struct prestera_flow_block *block; struct flow_block_cb *block_cb; @@ -173,7 +177,7 @@ prestera_flow_block_get(struct prestera_switch *sw, block_cb = flow_block_cb_lookup(f->block, prestera_flow_block_cb, sw); if (!block_cb) { - block = prestera_flow_block_create(sw, f->net); + block = prestera_flow_block_create(sw, f->net, ingress); if (!block) return ERR_PTR(-ENOMEM); @@ -209,7 +213,7 @@ static void prestera_flow_block_put(struct prestera_flow_block *block) } static int prestera_setup_flow_block_bind(struct prestera_port *port, - struct flow_block_offload *f) + struct flow_block_offload *f, bool ingress) { struct prestera_switch *sw = port->sw; struct prestera_flow_block *block; @@ -217,7 +221,7 @@ static int prestera_setup_flow_block_bind(struct prestera_port *port, bool register_block; int err; - block = prestera_flow_block_get(sw, f, ®ister_block); + block = prestera_flow_block_get(sw, f, ®ister_block, ingress); if (IS_ERR(block)) return PTR_ERR(block); @@ -232,7 +236,11 @@ static int prestera_setup_flow_block_bind(struct prestera_port *port, list_add_tail(&block_cb->driver_list, &prestera_block_cb_list); } - port->flow_block = block; + if (ingress) + port->ingress_flow_block = block; + else + port->egress_flow_block = block; + return 0; err_block_bind: @@ -242,7 +250,7 @@ err_block_bind: } static void prestera_setup_flow_block_unbind(struct prestera_port *port, - struct flow_block_offload *f) + struct flow_block_offload *f, bool ingress) { struct prestera_switch *sw = port->sw; struct prestera_flow_block *block; @@ -266,24 +274,38 @@ static void prestera_setup_flow_block_unbind(struct prestera_port *port, list_del(&block_cb->driver_list); } error: - port->flow_block = NULL; + if (ingress) + port->ingress_flow_block = NULL; + else + port->egress_flow_block = NULL; } -int prestera_flow_block_setup(struct prestera_port *port, - struct flow_block_offload *f) +static int prestera_setup_flow_block_clsact(struct prestera_port *port, + struct flow_block_offload *f, + bool ingress) { - if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) - return -EOPNOTSUPP; - f->driver_block_list = &prestera_block_cb_list; switch (f->command) { case FLOW_BLOCK_BIND: - return prestera_setup_flow_block_bind(port, f); + return prestera_setup_flow_block_bind(port, f, ingress); case FLOW_BLOCK_UNBIND: - prestera_setup_flow_block_unbind(port, f); + prestera_setup_flow_block_unbind(port, f, ingress); return 0; default: return -EOPNOTSUPP; } } + +int prestera_flow_block_setup(struct prestera_port *port, + struct flow_block_offload *f) +{ + switch (f->binder_type) { + case FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS: + return prestera_setup_flow_block_clsact(port, f, true); + case FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS: + return prestera_setup_flow_block_clsact(port, f, false); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flow.h b/drivers/net/ethernet/marvell/prestera/prestera_flow.h index 6550278b166a..0c9e13263261 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flow.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_flow.h @@ -23,6 +23,7 @@ struct prestera_flow_block { struct flow_block_cb *block_cb; struct list_head template_list; unsigned int rule_count; + bool ingress; }; int prestera_flow_block_setup(struct prestera_port *port, diff --git a/drivers/net/ethernet/marvell/prestera/prestera_flower.c b/drivers/net/ethernet/marvell/prestera/prestera_flower.c index 4d93ad6a284c..19d3b55c578e 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_flower.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_flower.c @@ -79,7 +79,7 @@ static int prestera_flower_parse_actions(struct prestera_flow_block *block, } else if (act->hw_stats & FLOW_ACTION_HW_STATS_DELAYED) { /* setup counter first */ rule->re_arg.count.valid = true; - err = prestera_acl_chain_to_client(chain_index, + err = prestera_acl_chain_to_client(chain_index, block->ingress, &rule->re_arg.count.client); if (err) return err; @@ -116,7 +116,7 @@ static int prestera_flower_parse_actions(struct prestera_flow_block *block, rule->re_arg.police.rate = act->police.rate_bytes_ps; rule->re_arg.police.burst = act->police.burst; - rule->re_arg.police.ingress = true; + rule->re_arg.police.ingress = block->ingress; break; case FLOW_ACTION_GOTO: err = prestera_flower_parse_goto_action(block, rule, @@ -138,7 +138,8 @@ static int prestera_flower_parse_actions(struct prestera_flow_block *block, static int prestera_flower_parse_meta(struct prestera_acl_rule *rule, struct flow_cls_offload *f, struct prestera_flow_block *block) -{ struct flow_rule *f_rule = flow_cls_offload_flow_rule(f); +{ + struct flow_rule *f_rule = flow_cls_offload_flow_rule(f); struct prestera_acl_match *r_match = &rule->re_key.match; struct prestera_port *port; struct net_device *ingress_dev; @@ -178,13 +179,13 @@ static int prestera_flower_parse_meta(struct prestera_acl_rule *rule, rule_match_set(r_match->mask, SYS_DEV, mask); return 0; - } static int prestera_flower_parse(struct prestera_flow_block *block, struct prestera_acl_rule *rule, struct flow_cls_offload *f) -{ struct flow_rule *f_rule = flow_cls_offload_flow_rule(f); +{ + struct flow_rule *f_rule = flow_cls_offload_flow_rule(f); struct flow_dissector *dissector = f_rule->match.dissector; struct prestera_acl_match *r_match = &rule->re_key.match; __be16 n_proto_mask = 0; @@ -202,6 +203,7 @@ static int prestera_flower_parse(struct prestera_flow_block *block, BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | BIT(FLOW_DISSECTOR_KEY_ICMP) | BIT(FLOW_DISSECTOR_KEY_PORTS) | + BIT(FLOW_DISSECTOR_KEY_PORTS_RANGE) | BIT(FLOW_DISSECTOR_KEY_VLAN))) { NL_SET_ERR_MSG_MOD(f->common.extack, "Unsupported key"); return -EOPNOTSUPP; @@ -301,6 +303,29 @@ static int prestera_flower_parse(struct prestera_flow_block *block, rule_match_set(r_match->mask, L4_PORT_DST, match.mask->dst); } + if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_PORTS_RANGE)) { + struct flow_match_ports_range match; + __be32 tp_key, tp_mask; + + flow_rule_match_ports_range(f_rule, &match); + + /* src port range (min, max) */ + tp_key = htonl(ntohs(match.key->tp_min.src) | + (ntohs(match.key->tp_max.src) << 16)); + tp_mask = htonl(ntohs(match.mask->tp_min.src) | + (ntohs(match.mask->tp_max.src) << 16)); + rule_match_set(r_match->key, L4_PORT_RANGE_SRC, tp_key); + rule_match_set(r_match->mask, L4_PORT_RANGE_SRC, tp_mask); + + /* dst port range (min, max) */ + tp_key = htonl(ntohs(match.key->tp_min.dst) | + (ntohs(match.key->tp_max.dst) << 16)); + tp_mask = htonl(ntohs(match.mask->tp_min.dst) | + (ntohs(match.mask->tp_max.dst) << 16)); + rule_match_set(r_match->key, L4_PORT_RANGE_DST, tp_key); + rule_match_set(r_match->mask, L4_PORT_RANGE_DST, tp_mask); + } + if (flow_rule_match_key(f_rule, FLOW_DISSECTOR_KEY_VLAN)) { struct flow_match_vlan match; @@ -397,7 +422,6 @@ void prestera_flower_destroy(struct prestera_flow_block *block, prestera_acl_rule_destroy(rule); } prestera_acl_ruleset_put(ruleset); - } int prestera_flower_tmplt_create(struct prestera_flow_block *block, diff --git a/drivers/net/ethernet/marvell/prestera/prestera_hw.c b/drivers/net/ethernet/marvell/prestera/prestera_hw.c index 79fd3cac539d..962d7e0c0cb5 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_hw.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_hw.c @@ -60,6 +60,14 @@ enum prestera_cmd_type_t { PRESTERA_CMD_TYPE_ROUTER_VR_CREATE = 0x630, PRESTERA_CMD_TYPE_ROUTER_VR_DELETE = 0x631, + PRESTERA_CMD_TYPE_FLOOD_DOMAIN_CREATE = 0x700, + PRESTERA_CMD_TYPE_FLOOD_DOMAIN_DESTROY = 0x701, + PRESTERA_CMD_TYPE_FLOOD_DOMAIN_PORTS_SET = 0x702, + PRESTERA_CMD_TYPE_FLOOD_DOMAIN_PORTS_RESET = 0x703, + + PRESTERA_CMD_TYPE_MDB_CREATE = 0x704, + PRESTERA_CMD_TYPE_MDB_DESTROY = 0x705, + PRESTERA_CMD_TYPE_RXTX_INIT = 0x800, PRESTERA_CMD_TYPE_LAG_MEMBER_ADD = 0x900, @@ -185,6 +193,12 @@ struct prestera_fw_event_handler { void *arg; }; +enum { + PRESTERA_HW_FLOOD_DOMAIN_PORT_TYPE_REG_PORT = 0, + PRESTERA_HW_FLOOD_DOMAIN_PORT_TYPE_LAG = 1, + PRESTERA_HW_FLOOD_DOMAIN_PORT_TYPE_MAX = 2, +}; + struct prestera_msg_cmd { __le32 type; }; @@ -627,6 +641,57 @@ struct prestera_msg_event_fdb { u8 dest_type; }; +struct prestera_msg_flood_domain_create_req { + struct prestera_msg_cmd cmd; +}; + +struct prestera_msg_flood_domain_create_resp { + struct prestera_msg_ret ret; + __le32 flood_domain_idx; +}; + +struct prestera_msg_flood_domain_destroy_req { + struct prestera_msg_cmd cmd; + __le32 flood_domain_idx; +}; + +struct prestera_msg_flood_domain_ports_set_req { + struct prestera_msg_cmd cmd; + __le32 flood_domain_idx; + __le32 ports_num; +}; + +struct prestera_msg_flood_domain_ports_reset_req { + struct prestera_msg_cmd cmd; + __le32 flood_domain_idx; +}; + +struct prestera_msg_flood_domain_port { + union { + struct { + __le32 port_num; + __le32 dev_num; + }; + __le16 lag_id; + }; + __le16 vid; + __le16 port_type; +}; + +struct prestera_msg_mdb_create_req { + struct prestera_msg_cmd cmd; + __le32 flood_domain_idx; + __le16 vid; + u8 mac[ETH_ALEN]; +}; + +struct prestera_msg_mdb_destroy_req { + struct prestera_msg_cmd cmd; + __le32 flood_domain_idx; + __le16 vid; + u8 mac[ETH_ALEN]; +}; + static void prestera_hw_build_tests(void) { /* check requests */ @@ -654,10 +719,17 @@ static void prestera_hw_build_tests(void) BUILD_BUG_ON(sizeof(struct prestera_msg_vr_req) != 8); BUILD_BUG_ON(sizeof(struct prestera_msg_lpm_req) != 36); BUILD_BUG_ON(sizeof(struct prestera_msg_policer_req) != 36); + BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_create_req) != 4); + BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_destroy_req) != 8); + BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_ports_set_req) != 12); + BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_ports_reset_req) != 8); + BUILD_BUG_ON(sizeof(struct prestera_msg_mdb_create_req) != 16); + BUILD_BUG_ON(sizeof(struct prestera_msg_mdb_destroy_req) != 16); /* structure that are part of req/resp fw messages */ BUILD_BUG_ON(sizeof(struct prestera_msg_iface) != 16); BUILD_BUG_ON(sizeof(struct prestera_msg_ip_addr) != 20); + BUILD_BUG_ON(sizeof(struct prestera_msg_flood_domain_port) != 12); /* check responses */ BUILD_BUG_ON(sizeof(struct prestera_msg_common_resp) != 8); @@ -1531,7 +1603,7 @@ int prestera_hw_port_learning_set(struct prestera_port *port, bool enable) &req.cmd, sizeof(req)); } -static int prestera_hw_port_uc_flood_set(struct prestera_port *port, bool flood) +int prestera_hw_port_uc_flood_set(const struct prestera_port *port, bool flood) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_FLOOD), @@ -1549,7 +1621,7 @@ static int prestera_hw_port_uc_flood_set(struct prestera_port *port, bool flood) &req.cmd, sizeof(req)); } -static int prestera_hw_port_mc_flood_set(struct prestera_port *port, bool flood) +int prestera_hw_port_mc_flood_set(const struct prestera_port *port, bool flood) { struct prestera_msg_port_attr_req req = { .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_FLOOD), @@ -1567,56 +1639,6 @@ static int prestera_hw_port_mc_flood_set(struct prestera_port *port, bool flood) &req.cmd, sizeof(req)); } -static int prestera_hw_port_flood_set_v2(struct prestera_port *port, bool flood) -{ - struct prestera_msg_port_attr_req req = { - .attr = __cpu_to_le32(PRESTERA_CMD_PORT_ATTR_FLOOD), - .port = __cpu_to_le32(port->hw_id), - .dev = __cpu_to_le32(port->dev_id), - .param = { - .flood = flood, - } - }; - - return prestera_cmd(port->sw, PRESTERA_CMD_TYPE_PORT_ATTR_SET, - &req.cmd, sizeof(req)); -} - -int prestera_hw_port_flood_set(struct prestera_port *port, unsigned long mask, - unsigned long val) -{ - int err; - - if (port->sw->dev->fw_rev.maj <= 2) { - if (!(mask & BR_FLOOD)) - return 0; - - return prestera_hw_port_flood_set_v2(port, val & BR_FLOOD); - } - - if (mask & BR_FLOOD) { - err = prestera_hw_port_uc_flood_set(port, val & BR_FLOOD); - if (err) - goto err_uc_flood; - } - - if (mask & BR_MCAST_FLOOD) { - err = prestera_hw_port_mc_flood_set(port, val & BR_MCAST_FLOOD); - if (err) - goto err_mc_flood; - } - - return 0; - -err_mc_flood: - prestera_hw_port_mc_flood_set(port, 0); -err_uc_flood: - if (mask & BR_FLOOD) - prestera_hw_port_uc_flood_set(port, 0); - - return err; -} - int prestera_hw_vlan_create(struct prestera_switch *sw, u16 vid) { struct prestera_msg_vlan_req req = { @@ -2244,3 +2266,133 @@ int prestera_hw_policer_sr_tcm_set(struct prestera_switch *sw, return prestera_cmd(sw, PRESTERA_CMD_TYPE_POLICER_SET, &req.cmd, sizeof(req)); } + +int prestera_hw_flood_domain_create(struct prestera_flood_domain *domain) +{ + struct prestera_msg_flood_domain_create_resp resp; + struct prestera_msg_flood_domain_create_req req; + int err; + + err = prestera_cmd_ret(domain->sw, + PRESTERA_CMD_TYPE_FLOOD_DOMAIN_CREATE, &req.cmd, + sizeof(req), &resp.ret, sizeof(resp)); + if (err) + return err; + + domain->idx = __le32_to_cpu(resp.flood_domain_idx); + + return 0; +} + +int prestera_hw_flood_domain_destroy(struct prestera_flood_domain *domain) +{ + struct prestera_msg_flood_domain_destroy_req req = { + .flood_domain_idx = __cpu_to_le32(domain->idx), + }; + + return prestera_cmd(domain->sw, PRESTERA_CMD_TYPE_FLOOD_DOMAIN_DESTROY, + &req.cmd, sizeof(req)); +} + +int prestera_hw_flood_domain_ports_set(struct prestera_flood_domain *domain) +{ + struct prestera_flood_domain_port *flood_domain_port; + struct prestera_msg_flood_domain_ports_set_req *req; + struct prestera_msg_flood_domain_port *ports; + struct prestera_switch *sw = domain->sw; + struct prestera_port *port; + u32 ports_num = 0; + int buf_size; + void *buff; + u16 lag_id; + int err; + + list_for_each_entry(flood_domain_port, &domain->flood_domain_port_list, + flood_domain_port_node) + ports_num++; + + if (!ports_num) + return -EINVAL; + + buf_size = sizeof(*req) + sizeof(*ports) * ports_num; + + buff = kmalloc(buf_size, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + req = buff; + ports = buff + sizeof(*req); + + req->flood_domain_idx = __cpu_to_le32(domain->idx); + req->ports_num = __cpu_to_le32(ports_num); + + list_for_each_entry(flood_domain_port, &domain->flood_domain_port_list, + flood_domain_port_node) { + if (netif_is_lag_master(flood_domain_port->dev)) { + if (prestera_lag_id(sw, flood_domain_port->dev, + &lag_id)) { + kfree(buff); + return -EINVAL; + } + + ports->port_type = + __cpu_to_le16(PRESTERA_HW_FLOOD_DOMAIN_PORT_TYPE_LAG); + ports->lag_id = __cpu_to_le16(lag_id); + } else { + port = prestera_port_dev_lower_find(flood_domain_port->dev); + + ports->port_type = + __cpu_to_le16(PRESTERA_HW_FDB_ENTRY_TYPE_REG_PORT); + ports->dev_num = __cpu_to_le32(port->dev_id); + ports->port_num = __cpu_to_le32(port->hw_id); + } + + ports->vid = __cpu_to_le16(flood_domain_port->vid); + + ports++; + } + + err = prestera_cmd(sw, PRESTERA_CMD_TYPE_FLOOD_DOMAIN_PORTS_SET, + &req->cmd, buf_size); + + kfree(buff); + + return err; +} + +int prestera_hw_flood_domain_ports_reset(struct prestera_flood_domain *domain) +{ + struct prestera_msg_flood_domain_ports_reset_req req = { + .flood_domain_idx = __cpu_to_le32(domain->idx), + }; + + return prestera_cmd(domain->sw, + PRESTERA_CMD_TYPE_FLOOD_DOMAIN_PORTS_RESET, &req.cmd, + sizeof(req)); +} + +int prestera_hw_mdb_create(struct prestera_mdb_entry *mdb) +{ + struct prestera_msg_mdb_create_req req = { + .flood_domain_idx = __cpu_to_le32(mdb->flood_domain->idx), + .vid = __cpu_to_le16(mdb->vid), + }; + + memcpy(req.mac, mdb->addr, ETH_ALEN); + + return prestera_cmd(mdb->sw, PRESTERA_CMD_TYPE_MDB_CREATE, &req.cmd, + sizeof(req)); +} + +int prestera_hw_mdb_destroy(struct prestera_mdb_entry *mdb) +{ + struct prestera_msg_mdb_destroy_req req = { + .flood_domain_idx = __cpu_to_le32(mdb->flood_domain->idx), + .vid = __cpu_to_le16(mdb->vid), + }; + + memcpy(req.mac, mdb->addr, ETH_ALEN); + + return prestera_cmd(mdb->sw, PRESTERA_CMD_TYPE_MDB_DESTROY, &req.cmd, + sizeof(req)); +} diff --git a/drivers/net/ethernet/marvell/prestera/prestera_hw.h b/drivers/net/ethernet/marvell/prestera/prestera_hw.h index 579d9ba23ffc..56e043146dd2 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_hw.h +++ b/drivers/net/ethernet/marvell/prestera/prestera_hw.h @@ -123,9 +123,10 @@ enum prestera_hw_vtcam_direction_t { }; enum { - PRESTERA_HW_COUNTER_CLIENT_LOOKUP_0 = 0, - PRESTERA_HW_COUNTER_CLIENT_LOOKUP_1 = 1, - PRESTERA_HW_COUNTER_CLIENT_LOOKUP_2 = 2, + PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_0 = 0, + PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_1 = 1, + PRESTERA_HW_COUNTER_CLIENT_INGRESS_LOOKUP_2 = 2, + PRESTERA_HW_COUNTER_CLIENT_EGRESS_LOOKUP = 3, }; struct prestera_switch; @@ -143,6 +144,8 @@ struct prestera_acl_hw_action_info; struct prestera_acl_iface; struct prestera_counter_stats; struct prestera_iface; +struct prestera_flood_domain; +struct prestera_mdb_entry; /* Switch API */ int prestera_hw_switch_init(struct prestera_switch *sw); @@ -178,8 +181,8 @@ int prestera_hw_port_stats_get(const struct prestera_port *port, struct prestera_port_stats *stats); int prestera_hw_port_speed_get(const struct prestera_port *port, u32 *speed); int prestera_hw_port_learning_set(struct prestera_port *port, bool enable); -int prestera_hw_port_flood_set(struct prestera_port *port, unsigned long mask, - unsigned long val); +int prestera_hw_port_uc_flood_set(const struct prestera_port *port, bool flood); +int prestera_hw_port_mc_flood_set(const struct prestera_port *port, bool flood); int prestera_hw_port_accept_frm_type(struct prestera_port *port, enum prestera_accept_frm_type type); /* Vlan API */ @@ -301,4 +304,13 @@ int prestera_hw_policer_release(struct prestera_switch *sw, int prestera_hw_policer_sr_tcm_set(struct prestera_switch *sw, u32 policer_id, u64 cir, u32 cbs); +/* Flood domain / MDB API */ +int prestera_hw_flood_domain_create(struct prestera_flood_domain *domain); +int prestera_hw_flood_domain_destroy(struct prestera_flood_domain *domain); +int prestera_hw_flood_domain_ports_set(struct prestera_flood_domain *domain); +int prestera_hw_flood_domain_ports_reset(struct prestera_flood_domain *domain); + +int prestera_hw_mdb_create(struct prestera_mdb_entry *mdb); +int prestera_hw_mdb_destroy(struct prestera_mdb_entry *mdb); + #endif /* _PRESTERA_HW_H_ */ diff --git a/drivers/net/ethernet/marvell/prestera/prestera_main.c b/drivers/net/ethernet/marvell/prestera/prestera_main.c index 3952fdcc9240..ede3e53b9790 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_main.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_main.c @@ -9,6 +9,7 @@ #include <linux/of.h> #include <linux/of_net.h> #include <linux/if_vlan.h> +#include <linux/phylink.h> #include "prestera.h" #include "prestera_hw.h" @@ -35,6 +36,21 @@ void prestera_queue_work(struct work_struct *work) queue_work(prestera_owq, work); } +int prestera_port_learning_set(struct prestera_port *port, bool learn) +{ + return prestera_hw_port_learning_set(port, learn); +} + +int prestera_port_uc_flood_set(struct prestera_port *port, bool flood) +{ + return prestera_hw_port_uc_flood_set(port, flood); +} + +int prestera_port_mc_flood_set(struct prestera_port *port, bool flood) +{ + return prestera_hw_port_mc_flood_set(port, flood); +} + int prestera_port_pvid_set(struct prestera_port *port, u16 vid) { enum prestera_accept_frm_type frm_type; @@ -91,6 +107,14 @@ struct prestera_port *prestera_find_port(struct prestera_switch *sw, u32 id) return port; } +struct prestera_switch *prestera_switch_get(struct net_device *dev) +{ + struct prestera_port *port; + + port = prestera_port_dev_lower_find(dev); + return port ? port->sw : NULL; +} + int prestera_port_cfg_mac_read(struct prestera_port *port, struct prestera_port_mac_config *cfg) { @@ -119,18 +143,24 @@ static int prestera_port_open(struct net_device *dev) struct prestera_port_mac_config cfg_mac; int err = 0; - if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) { - err = prestera_port_cfg_mac_read(port, &cfg_mac); - if (!err) { - cfg_mac.admin = true; - err = prestera_port_cfg_mac_write(port, &cfg_mac); - } + if (port->phy_link) { + phylink_start(port->phy_link); } else { - port->cfg_phy.admin = true; - err = prestera_hw_port_phy_mode_set(port, true, port->autoneg, - port->cfg_phy.mode, - port->adver_link_modes, - port->cfg_phy.mdix); + if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) { + err = prestera_port_cfg_mac_read(port, &cfg_mac); + if (!err) { + cfg_mac.admin = true; + err = prestera_port_cfg_mac_write(port, + &cfg_mac); + } + } else { + port->cfg_phy.admin = true; + err = prestera_hw_port_phy_mode_set(port, true, + port->autoneg, + port->cfg_phy.mode, + port->adver_link_modes, + port->cfg_phy.mdix); + } } netif_start_queue(dev); @@ -146,23 +176,259 @@ static int prestera_port_close(struct net_device *dev) netif_stop_queue(dev); - if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) { + if (port->phy_link) { + phylink_stop(port->phy_link); + phylink_disconnect_phy(port->phy_link); err = prestera_port_cfg_mac_read(port, &cfg_mac); if (!err) { cfg_mac.admin = false; prestera_port_cfg_mac_write(port, &cfg_mac); } } else { - port->cfg_phy.admin = false; - err = prestera_hw_port_phy_mode_set(port, false, port->autoneg, - port->cfg_phy.mode, - port->adver_link_modes, - port->cfg_phy.mdix); + if (port->caps.transceiver == PRESTERA_PORT_TCVR_SFP) { + err = prestera_port_cfg_mac_read(port, &cfg_mac); + if (!err) { + cfg_mac.admin = false; + prestera_port_cfg_mac_write(port, &cfg_mac); + } + } else { + port->cfg_phy.admin = false; + err = prestera_hw_port_phy_mode_set(port, false, port->autoneg, + port->cfg_phy.mode, + port->adver_link_modes, + port->cfg_phy.mdix); + } } return err; } +static void +prestera_port_mac_state_cache_read(struct prestera_port *port, + struct prestera_port_mac_state *state) +{ + spin_lock(&port->state_mac_lock); + *state = port->state_mac; + spin_unlock(&port->state_mac_lock); +} + +static void +prestera_port_mac_state_cache_write(struct prestera_port *port, + struct prestera_port_mac_state *state) +{ + spin_lock(&port->state_mac_lock); + port->state_mac = *state; + spin_unlock(&port->state_mac_lock); +} + +static struct prestera_port *prestera_pcs_to_port(struct phylink_pcs *pcs) +{ + return container_of(pcs, struct prestera_port, phylink_pcs); +} + +static void prestera_mac_config(struct phylink_config *config, + unsigned int an_mode, + const struct phylink_link_state *state) +{ +} + +static void prestera_mac_link_down(struct phylink_config *config, + unsigned int mode, phy_interface_t interface) +{ + struct net_device *ndev = to_net_dev(config->dev); + struct prestera_port *port = netdev_priv(ndev); + struct prestera_port_mac_state state_mac; + + /* Invalidate. Parameters will update on next link event. */ + memset(&state_mac, 0, sizeof(state_mac)); + state_mac.valid = false; + prestera_port_mac_state_cache_write(port, &state_mac); +} + +static void prestera_mac_link_up(struct phylink_config *config, + struct phy_device *phy, + unsigned int mode, phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ +} + +static struct phylink_pcs * +prestera_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct net_device *dev = to_net_dev(config->dev); + struct prestera_port *port = netdev_priv(dev); + + return &port->phylink_pcs; +} + +static void prestera_pcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) +{ + struct prestera_port *port = container_of(pcs, struct prestera_port, + phylink_pcs); + struct prestera_port_mac_state smac; + + prestera_port_mac_state_cache_read(port, &smac); + + if (smac.valid) { + state->link = smac.oper ? 1 : 0; + /* AN is completed, when port is up */ + state->an_complete = (smac.oper && port->autoneg) ? 1 : 0; + state->speed = smac.speed; + state->duplex = smac.duplex; + } else { + state->link = 0; + state->an_complete = 0; + } +} + +static int prestera_pcs_config(struct phylink_pcs *pcs, + unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct prestera_port *port = prestera_pcs_to_port(pcs); + struct prestera_port_mac_config cfg_mac; + int err; + + err = prestera_port_cfg_mac_read(port, &cfg_mac); + if (err) + return err; + + cfg_mac.admin = true; + cfg_mac.fec = PRESTERA_PORT_FEC_OFF; + + switch (interface) { + case PHY_INTERFACE_MODE_10GBASER: + cfg_mac.speed = SPEED_10000; + cfg_mac.inband = 0; + cfg_mac.mode = PRESTERA_MAC_MODE_SR_LR; + break; + case PHY_INTERFACE_MODE_2500BASEX: + cfg_mac.speed = SPEED_2500; + cfg_mac.duplex = DUPLEX_FULL; + cfg_mac.inband = test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + advertising); + cfg_mac.mode = PRESTERA_MAC_MODE_SGMII; + break; + case PHY_INTERFACE_MODE_SGMII: + cfg_mac.inband = 1; + cfg_mac.mode = PRESTERA_MAC_MODE_SGMII; + break; + case PHY_INTERFACE_MODE_1000BASEX: + default: + cfg_mac.speed = SPEED_1000; + cfg_mac.duplex = DUPLEX_FULL; + cfg_mac.inband = test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, + advertising); + cfg_mac.mode = PRESTERA_MAC_MODE_1000BASE_X; + break; + } + + err = prestera_port_cfg_mac_write(port, &cfg_mac); + if (err) + return err; + + return 0; +} + +static void prestera_pcs_an_restart(struct phylink_pcs *pcs) +{ + /* TODO: add 1000basex AN restart support + * (Currently FW has no support for 1000baseX AN restart, but it will in the future, + * so as for now the function would stay empty.) + */ +} + +static const struct phylink_mac_ops prestera_mac_ops = { + .validate = phylink_generic_validate, + .mac_select_pcs = prestera_mac_select_pcs, + .mac_config = prestera_mac_config, + .mac_link_down = prestera_mac_link_down, + .mac_link_up = prestera_mac_link_up, +}; + +static const struct phylink_pcs_ops prestera_pcs_ops = { + .pcs_get_state = prestera_pcs_get_state, + .pcs_config = prestera_pcs_config, + .pcs_an_restart = prestera_pcs_an_restart, +}; + +static int prestera_port_sfp_bind(struct prestera_port *port) +{ + struct prestera_switch *sw = port->sw; + struct device_node *ports, *node; + struct fwnode_handle *fwnode; + struct phylink *phy_link; + int err; + + if (!sw->np) + return 0; + + ports = of_find_node_by_name(sw->np, "ports"); + + for_each_child_of_node(ports, node) { + int num; + + err = of_property_read_u32(node, "prestera,port-num", &num); + if (err) { + dev_err(sw->dev->dev, + "device node %pOF has no valid reg property: %d\n", + node, err); + goto out; + } + + if (port->fp_id != num) + continue; + + port->phylink_pcs.ops = &prestera_pcs_ops; + + port->phy_config.dev = &port->dev->dev; + port->phy_config.type = PHYLINK_NETDEV; + + fwnode = of_fwnode_handle(node); + + __set_bit(PHY_INTERFACE_MODE_10GBASER, + port->phy_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + port->phy_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_SGMII, + port->phy_config.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + port->phy_config.supported_interfaces); + + port->phy_config.mac_capabilities = + MAC_1000 | MAC_2500FD | MAC_10000FD; + + phy_link = phylink_create(&port->phy_config, fwnode, + PHY_INTERFACE_MODE_INTERNAL, + &prestera_mac_ops); + if (IS_ERR(phy_link)) { + netdev_err(port->dev, "failed to create phylink\n"); + err = PTR_ERR(phy_link); + goto out; + } + + port->phy_link = phy_link; + break; + } + +out: + of_node_put(ports); + return err; +} + +static int prestera_port_sfp_unbind(struct prestera_port *port) +{ + if (port->phy_link) + phylink_destroy(port->phy_link); + + return 0; +} + static netdev_tx_t prestera_port_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -343,6 +609,8 @@ static int prestera_port_create(struct prestera_switch *sw, u32 id) port->id = id; port->sw = sw; + spin_lock_init(&port->state_mac_lock); + err = prestera_hw_port_info_get(port, &port->dev_id, &port->hw_id, &port->fp_id); if (err) { @@ -357,8 +625,10 @@ static int prestera_port_create(struct prestera_switch *sw, u32 id) dev->features |= NETIF_F_NETNS_LOCAL | NETIF_F_HW_TC; dev->netdev_ops = &prestera_netdev_ops; dev->ethtool_ops = &prestera_ethtool_ops; + SET_NETDEV_DEV(dev, sw->dev->dev); - netif_carrier_off(dev); + if (port->caps.transceiver != PRESTERA_PORT_TCVR_SFP) + netif_carrier_off(dev); dev->mtu = min_t(unsigned int, sw->mtu_max, PRESTERA_MTU_DEFAULT); dev->min_mtu = sw->mtu_min; @@ -409,7 +679,7 @@ static int prestera_port_create(struct prestera_switch *sw, u32 id) cfg_mac.admin = false; cfg_mac.mode = PRESTERA_MAC_MODE_MAX; } - cfg_mac.inband = false; + cfg_mac.inband = 0; cfg_mac.speed = 0; cfg_mac.duplex = DUPLEX_UNKNOWN; cfg_mac.fec = PRESTERA_PORT_FEC_OFF; @@ -451,8 +721,13 @@ static int prestera_port_create(struct prestera_switch *sw, u32 id) prestera_devlink_port_set(port); + err = prestera_port_sfp_bind(port); + if (err) + goto err_sfp_bind; + return 0; +err_sfp_bind: err_register_netdev: prestera_port_list_del(port); err_port_init: @@ -498,8 +773,10 @@ static int prestera_create_ports(struct prestera_switch *sw) return 0; err_port_create: - list_for_each_entry_safe(port, tmp, &sw->port_list, list) + list_for_each_entry_safe(port, tmp, &sw->port_list, list) { + prestera_port_sfp_unbind(port); prestera_port_destroy(port); + } return err; } @@ -507,25 +784,47 @@ err_port_create: static void prestera_port_handle_event(struct prestera_switch *sw, struct prestera_event *evt, void *arg) { + struct prestera_port_mac_state smac; + struct prestera_port_event *pevt; struct delayed_work *caching_dw; struct prestera_port *port; - port = prestera_find_port(sw, evt->port_evt.port_id); - if (!port || !port->dev) - return; - - caching_dw = &port->cached_hw_stats.caching_dw; - - prestera_ethtool_port_state_changed(port, &evt->port_evt); - if (evt->id == PRESTERA_PORT_EVENT_MAC_STATE_CHANGED) { + pevt = &evt->port_evt; + port = prestera_find_port(sw, pevt->port_id); + if (!port || !port->dev) + return; + + caching_dw = &port->cached_hw_stats.caching_dw; + + if (port->phy_link) { + memset(&smac, 0, sizeof(smac)); + smac.valid = true; + smac.oper = pevt->data.mac.oper; + if (smac.oper) { + smac.mode = pevt->data.mac.mode; + smac.speed = pevt->data.mac.speed; + smac.duplex = pevt->data.mac.duplex; + smac.fc = pevt->data.mac.fc; + smac.fec = pevt->data.mac.fec; + phylink_mac_change(port->phy_link, true); + } else { + phylink_mac_change(port->phy_link, false); + } + prestera_port_mac_state_cache_write(port, &smac); + } + if (port->state_mac.oper) { - netif_carrier_on(port->dev); + if (!port->phy_link) + netif_carrier_on(port->dev); + if (!delayed_work_pending(caching_dw)) queue_delayed_work(prestera_wq, caching_dw, 0); } else if (netif_running(port->dev) && netif_carrier_ok(port->dev)) { - netif_carrier_off(port->dev); + if (!port->phy_link) + netif_carrier_off(port->dev); + if (delayed_work_pending(caching_dw)) cancel_delayed_work(caching_dw); } @@ -548,19 +847,20 @@ static void prestera_event_handlers_unregister(struct prestera_switch *sw) static int prestera_switch_set_base_mac_addr(struct prestera_switch *sw) { struct device_node *base_mac_np; - struct device_node *np; - int ret; + int ret = 0; - np = of_find_compatible_node(NULL, NULL, "marvell,prestera"); - base_mac_np = of_parse_phandle(np, "base-mac-provider", 0); + if (sw->np) { + base_mac_np = of_parse_phandle(sw->np, "base-mac-provider", 0); + if (base_mac_np) { + ret = of_get_mac_address(base_mac_np, sw->base_mac); + of_node_put(base_mac_np); + } + } - ret = of_get_mac_address(base_mac_np, sw->base_mac); - if (ret) { + if (!is_valid_ether_addr(sw->base_mac) || ret) { eth_random_addr(sw->base_mac); dev_info(prestera_dev(sw), "using random base mac address\n"); } - of_node_put(base_mac_np); - of_node_put(np); return prestera_hw_switch_mac_set(sw, sw->base_mac); } @@ -585,6 +885,30 @@ static struct prestera_lag *prestera_lag_by_dev(struct prestera_switch *sw, return NULL; } +int prestera_lag_id(struct prestera_switch *sw, + struct net_device *lag_dev, u16 *lag_id) +{ + struct prestera_lag *lag; + int free_id = -1; + int id; + + for (id = 0; id < sw->lag_max; id++) { + lag = prestera_lag_by_id(sw, id); + if (lag->member_count) { + if (lag->dev == lag_dev) { + *lag_id = id; + return 0; + } + } else if (free_id < 0) { + free_id = id; + } + } + if (free_id < 0) + return -ENOSPC; + *lag_id = free_id; + return 0; +} + static struct prestera_lag *prestera_lag_create(struct prestera_switch *sw, struct net_device *lag_dev) { @@ -876,6 +1200,150 @@ static int prestera_netdev_event_handler(struct notifier_block *nb, return notifier_from_errno(err); } +struct prestera_mdb_entry * +prestera_mdb_entry_create(struct prestera_switch *sw, + const unsigned char *addr, u16 vid) +{ + struct prestera_flood_domain *flood_domain; + struct prestera_mdb_entry *mdb_entry; + + mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL); + if (!mdb_entry) + goto err_mdb_alloc; + + flood_domain = prestera_flood_domain_create(sw); + if (!flood_domain) + goto err_flood_domain_create; + + mdb_entry->sw = sw; + mdb_entry->vid = vid; + mdb_entry->flood_domain = flood_domain; + ether_addr_copy(mdb_entry->addr, addr); + + if (prestera_hw_mdb_create(mdb_entry)) + goto err_mdb_hw_create; + + return mdb_entry; + +err_mdb_hw_create: + prestera_flood_domain_destroy(flood_domain); +err_flood_domain_create: + kfree(mdb_entry); +err_mdb_alloc: + return NULL; +} + +void prestera_mdb_entry_destroy(struct prestera_mdb_entry *mdb_entry) +{ + prestera_hw_mdb_destroy(mdb_entry); + prestera_flood_domain_destroy(mdb_entry->flood_domain); + kfree(mdb_entry); +} + +struct prestera_flood_domain * +prestera_flood_domain_create(struct prestera_switch *sw) +{ + struct prestera_flood_domain *domain; + + domain = kzalloc(sizeof(*domain), GFP_KERNEL); + if (!domain) + return NULL; + + domain->sw = sw; + + if (prestera_hw_flood_domain_create(domain)) { + kfree(domain); + return NULL; + } + + INIT_LIST_HEAD(&domain->flood_domain_port_list); + + return domain; +} + +void prestera_flood_domain_destroy(struct prestera_flood_domain *flood_domain) +{ + WARN_ON(!list_empty(&flood_domain->flood_domain_port_list)); + WARN_ON_ONCE(prestera_hw_flood_domain_destroy(flood_domain)); + kfree(flood_domain); +} + +int +prestera_flood_domain_port_create(struct prestera_flood_domain *flood_domain, + struct net_device *dev, + u16 vid) +{ + struct prestera_flood_domain_port *flood_domain_port; + bool is_first_port_in_list = false; + int err; + + flood_domain_port = kzalloc(sizeof(*flood_domain_port), GFP_KERNEL); + if (!flood_domain_port) { + err = -ENOMEM; + goto err_port_alloc; + } + + flood_domain_port->vid = vid; + + if (list_empty(&flood_domain->flood_domain_port_list)) + is_first_port_in_list = true; + + list_add(&flood_domain_port->flood_domain_port_node, + &flood_domain->flood_domain_port_list); + + flood_domain_port->flood_domain = flood_domain; + flood_domain_port->dev = dev; + + if (!is_first_port_in_list) { + err = prestera_hw_flood_domain_ports_reset(flood_domain); + if (err) + goto err_prestera_mdb_port_create_hw; + } + + err = prestera_hw_flood_domain_ports_set(flood_domain); + if (err) + goto err_prestera_mdb_port_create_hw; + + return 0; + +err_prestera_mdb_port_create_hw: + list_del(&flood_domain_port->flood_domain_port_node); + kfree(flood_domain_port); +err_port_alloc: + return err; +} + +void +prestera_flood_domain_port_destroy(struct prestera_flood_domain_port *port) +{ + struct prestera_flood_domain *flood_domain = port->flood_domain; + + list_del(&port->flood_domain_port_node); + + WARN_ON_ONCE(prestera_hw_flood_domain_ports_reset(flood_domain)); + + if (!list_empty(&flood_domain->flood_domain_port_list)) + WARN_ON_ONCE(prestera_hw_flood_domain_ports_set(flood_domain)); + + kfree(port); +} + +struct prestera_flood_domain_port * +prestera_flood_domain_port_find(struct prestera_flood_domain *flood_domain, + struct net_device *dev, u16 vid) +{ + struct prestera_flood_domain_port *flood_domain_port; + + list_for_each_entry(flood_domain_port, + &flood_domain->flood_domain_port_list, + flood_domain_port_node) + if (flood_domain_port->dev == dev && + vid == flood_domain_port->vid) + return flood_domain_port; + + return NULL; +} + static int prestera_netdev_event_handler_register(struct prestera_switch *sw) { sw->netdev_nb.notifier_call = prestera_netdev_event_handler; @@ -892,6 +1360,8 @@ static int prestera_switch_init(struct prestera_switch *sw) { int err; + sw->np = of_find_compatible_node(NULL, NULL, "marvell,prestera"); + err = prestera_hw_switch_init(sw); if (err) { dev_err(prestera_dev(sw), "Failed to init Switch device\n"); @@ -992,6 +1462,7 @@ static void prestera_switch_fini(struct prestera_switch *sw) prestera_router_fini(sw); prestera_netdev_event_handler_unregister(sw); prestera_hw_switch_fini(sw); + of_node_put(sw->np); } int prestera_device_register(struct prestera_device *dev) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_router.c b/drivers/net/ethernet/marvell/prestera/prestera_router.c index 3c8116f16b4d..58f4e44d5ad7 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_router.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_router.c @@ -389,8 +389,8 @@ static int __prestera_inetaddr_event(struct prestera_switch *sw, unsigned long event, struct netlink_ext_ack *extack) { - if (!prestera_netdev_check(dev) || netif_is_bridge_port(dev) || - netif_is_lag_port(dev) || netif_is_ovs_port(dev)) + if (!prestera_netdev_check(dev) || netif_is_any_bridge_port(dev) || + netif_is_lag_port(dev)) return 0; return __prestera_inetaddr_port_event(dev, event, extack); diff --git a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c index b4599fe4ca8d..71cde97d85c8 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c @@ -39,7 +39,10 @@ struct prestera_bridge { struct net_device *dev; struct prestera_switchdev *swdev; struct list_head port_list; + struct list_head br_mdb_entry_list; + bool mrouter_exist; bool vlan_enabled; + bool multicast_enabled; u16 bridge_id; }; @@ -48,8 +51,10 @@ struct prestera_bridge_port { struct net_device *dev; struct prestera_bridge *bridge; struct list_head vlan_list; + struct list_head br_mdb_port_list; refcount_t ref_count; unsigned long flags; + bool mrouter; u8 stp_state; }; @@ -67,6 +72,20 @@ struct prestera_port_vlan { u16 vid; }; +struct prestera_br_mdb_port { + struct prestera_bridge_port *br_port; + struct list_head br_mdb_port_node; +}; + +/* Software representation of MDB table. */ +struct prestera_br_mdb_entry { + struct prestera_bridge *bridge; + struct prestera_mdb_entry *mdb; + struct list_head br_mdb_port_list; + struct list_head br_mdb_entry_node; + bool enabled; +}; + static struct workqueue_struct *swdev_wq; static void prestera_bridge_port_put(struct prestera_bridge_port *br_port); @@ -74,6 +93,82 @@ static void prestera_bridge_port_put(struct prestera_bridge_port *br_port); static int prestera_port_vid_stp_set(struct prestera_port *port, u16 vid, u8 state); +static struct prestera_bridge * +prestera_bridge_find(const struct prestera_switch *sw, + const struct net_device *br_dev) +{ + struct prestera_bridge *bridge; + + list_for_each_entry(bridge, &sw->swdev->bridge_list, head) + if (bridge->dev == br_dev) + return bridge; + + return NULL; +} + +static struct prestera_bridge_port * +__prestera_bridge_port_find(const struct prestera_bridge *bridge, + const struct net_device *brport_dev) +{ + struct prestera_bridge_port *br_port; + + list_for_each_entry(br_port, &bridge->port_list, head) + if (br_port->dev == brport_dev) + return br_port; + + return NULL; +} + +static struct prestera_bridge_port * +prestera_bridge_port_find(struct prestera_switch *sw, + struct net_device *brport_dev) +{ + struct net_device *br_dev = netdev_master_upper_dev_get(brport_dev); + struct prestera_bridge *bridge; + + if (!br_dev) + return NULL; + + bridge = prestera_bridge_find(sw, br_dev); + if (!bridge) + return NULL; + + return __prestera_bridge_port_find(bridge, brport_dev); +} + +static void +prestera_br_port_flags_reset(struct prestera_bridge_port *br_port, + struct prestera_port *port) +{ + prestera_port_uc_flood_set(port, false); + prestera_port_mc_flood_set(port, false); + prestera_port_learning_set(port, false); +} + +static int prestera_br_port_flags_set(struct prestera_bridge_port *br_port, + struct prestera_port *port) +{ + int err; + + err = prestera_port_uc_flood_set(port, br_port->flags & BR_FLOOD); + if (err) + goto err_out; + + err = prestera_port_mc_flood_set(port, br_port->flags & BR_MCAST_FLOOD); + if (err) + goto err_out; + + err = prestera_port_learning_set(port, br_port->flags & BR_LEARNING); + if (err) + goto err_out; + + return 0; + +err_out: + prestera_br_port_flags_reset(br_port, port); + return err; +} + static struct prestera_bridge_vlan * prestera_bridge_vlan_create(struct prestera_bridge_port *br_port, u16 vid) { @@ -220,6 +315,70 @@ static int prestera_fdb_flush_port(struct prestera_port *port, u32 mode) } static void +prestera_mdb_port_del(struct prestera_mdb_entry *mdb, + struct net_device *orig_dev) +{ + struct prestera_flood_domain *fl_domain = mdb->flood_domain; + struct prestera_flood_domain_port *flood_domain_port; + + flood_domain_port = prestera_flood_domain_port_find(fl_domain, + orig_dev, + mdb->vid); + if (flood_domain_port) + prestera_flood_domain_port_destroy(flood_domain_port); +} + +static void +prestera_br_mdb_entry_put(struct prestera_br_mdb_entry *br_mdb) +{ + struct prestera_bridge_port *br_port; + + if (list_empty(&br_mdb->br_mdb_port_list)) { + list_for_each_entry(br_port, &br_mdb->bridge->port_list, head) + prestera_mdb_port_del(br_mdb->mdb, br_port->dev); + + prestera_mdb_entry_destroy(br_mdb->mdb); + list_del(&br_mdb->br_mdb_entry_node); + kfree(br_mdb); + } +} + +static void +prestera_br_mdb_port_del(struct prestera_br_mdb_entry *br_mdb, + struct prestera_bridge_port *br_port) +{ + struct prestera_br_mdb_port *br_mdb_port, *tmp; + + list_for_each_entry_safe(br_mdb_port, tmp, &br_mdb->br_mdb_port_list, + br_mdb_port_node) { + if (br_mdb_port->br_port == br_port) { + list_del(&br_mdb_port->br_mdb_port_node); + kfree(br_mdb_port); + } + } +} + +static void +prestera_mdb_flush_bridge_port(struct prestera_bridge_port *br_port) +{ + struct prestera_br_mdb_port *br_mdb_port, *tmp_port; + struct prestera_br_mdb_entry *br_mdb, *br_mdb_tmp; + struct prestera_bridge *br_dev = br_port->bridge; + + list_for_each_entry_safe(br_mdb, br_mdb_tmp, &br_dev->br_mdb_entry_list, + br_mdb_entry_node) { + list_for_each_entry_safe(br_mdb_port, tmp_port, + &br_mdb->br_mdb_port_list, + br_mdb_port_node) { + prestera_mdb_port_del(br_mdb->mdb, + br_mdb_port->br_port->dev); + prestera_br_mdb_port_del(br_mdb, br_mdb_port->br_port); + } + prestera_br_mdb_entry_put(br_mdb); + } +} + +static void prestera_port_vlan_bridge_leave(struct prestera_port_vlan *port_vlan) { u32 fdb_flush_mode = PRESTERA_FDB_FLUSH_MODE_DYNAMIC; @@ -244,6 +403,8 @@ prestera_port_vlan_bridge_leave(struct prestera_port_vlan *port_vlan) else prestera_fdb_flush_port_vlan(port, vid, fdb_flush_mode); + prestera_mdb_flush_bridge_port(br_port); + list_del(&port_vlan->br_vlan_head); prestera_bridge_vlan_put(br_vlan); prestera_bridge_port_put(br_port); @@ -295,8 +456,10 @@ prestera_bridge_create(struct prestera_switchdev *swdev, struct net_device *dev) bridge->vlan_enabled = vlan_enabled; bridge->swdev = swdev; bridge->dev = dev; + bridge->multicast_enabled = br_multicast_enabled(dev); INIT_LIST_HEAD(&bridge->port_list); + INIT_LIST_HEAD(&bridge->br_mdb_entry_list); list_add(&bridge->head, &swdev->bridge_list); @@ -314,6 +477,7 @@ static void prestera_bridge_destroy(struct prestera_bridge *bridge) else prestera_hw_bridge_delete(swdev->sw, bridge->bridge_id); + WARN_ON(!list_empty(&bridge->br_mdb_entry_list)); WARN_ON(!list_empty(&bridge->port_list)); kfree(bridge); } @@ -405,6 +569,7 @@ prestera_bridge_port_create(struct prestera_bridge *bridge, INIT_LIST_HEAD(&br_port->vlan_list); list_add(&br_port->head, &bridge->port_list); + INIT_LIST_HEAD(&br_port->br_mdb_port_list); return br_port; } @@ -414,6 +579,7 @@ prestera_bridge_port_destroy(struct prestera_bridge_port *br_port) { list_del(&br_port->head); WARN_ON(!list_empty(&br_port->vlan_list)); + WARN_ON(!list_empty(&br_port->br_mdb_port_list)); kfree(br_port); } @@ -461,19 +627,13 @@ prestera_bridge_1d_port_join(struct prestera_bridge_port *br_port) if (err) return err; - err = prestera_hw_port_flood_set(port, BR_FLOOD | BR_MCAST_FLOOD, - br_port->flags); + err = prestera_br_port_flags_set(br_port, port); if (err) - goto err_port_flood_set; - - err = prestera_hw_port_learning_set(port, br_port->flags & BR_LEARNING); - if (err) - goto err_port_learning_set; + goto err_flags2port_set; return 0; -err_port_learning_set: -err_port_flood_set: +err_flags2port_set: prestera_hw_bridge_port_delete(port, bridge->bridge_id); return err; @@ -592,8 +752,9 @@ void prestera_bridge_port_leave(struct net_device *br_dev, switchdev_bridge_port_unoffload(br_port->dev, NULL, NULL, NULL); - prestera_hw_port_learning_set(port, false); - prestera_hw_port_flood_set(port, BR_FLOOD | BR_MCAST_FLOOD, 0); + prestera_mdb_flush_bridge_port(br_port); + + prestera_br_port_flags_reset(br_port, port); prestera_port_vid_stp_set(port, PRESTERA_VID_ALL, BR_STATE_FORWARDING); prestera_bridge_port_put(br_port); } @@ -603,26 +764,14 @@ static int prestera_port_attr_br_flags_set(struct prestera_port *port, struct switchdev_brport_flags flags) { struct prestera_bridge_port *br_port; - int err; br_port = prestera_bridge_port_by_dev(port->sw->swdev, dev); if (!br_port) return 0; - err = prestera_hw_port_flood_set(port, flags.mask, flags.val); - if (err) - return err; - - if (flags.mask & BR_LEARNING) { - err = prestera_hw_port_learning_set(port, - flags.val & BR_LEARNING); - if (err) - return err; - } - - memcpy(&br_port->flags, &flags.val, sizeof(flags.val)); - - return 0; + br_port->flags &= ~flags.mask; + br_port->flags |= flags.val & flags.mask; + return prestera_br_port_flags_set(br_port, port); } static int prestera_port_attr_br_ageing_set(struct prestera_port *port, @@ -716,6 +865,290 @@ err_port_stp_set: return err; } +static int +prestera_br_port_lag_mdb_mc_enable_sync(struct prestera_bridge_port *br_port, + bool enabled) +{ + struct prestera_port *pr_port; + struct prestera_switch *sw; + u16 lag_id; + int err; + + pr_port = prestera_port_dev_lower_find(br_port->dev); + if (!pr_port) + return 0; + + sw = pr_port->sw; + err = prestera_lag_id(sw, br_port->dev, &lag_id); + if (err) + return err; + + list_for_each_entry(pr_port, &sw->port_list, list) { + if (pr_port->lag->lag_id == lag_id) { + err = prestera_port_mc_flood_set(pr_port, enabled); + if (err) + return err; + } + } + + return 0; +} + +static int prestera_br_mdb_mc_enable_sync(struct prestera_bridge *br_dev) +{ + struct prestera_bridge_port *br_port; + struct prestera_port *port; + bool enabled; + int err; + + /* if mrouter exists: + * - make sure every mrouter receives unreg mcast traffic; + * if mrouter doesn't exists: + * - make sure every port receives unreg mcast traffic; + */ + list_for_each_entry(br_port, &br_dev->port_list, head) { + if (br_dev->multicast_enabled && br_dev->mrouter_exist) + enabled = br_port->mrouter; + else + enabled = br_port->flags & BR_MCAST_FLOOD; + + if (netif_is_lag_master(br_port->dev)) { + err = prestera_br_port_lag_mdb_mc_enable_sync(br_port, + enabled); + if (err) + return err; + continue; + } + + port = prestera_port_dev_lower_find(br_port->dev); + if (!port) + continue; + + err = prestera_port_mc_flood_set(port, enabled); + if (err) + return err; + } + + return 0; +} + +static bool +prestera_br_mdb_port_is_member(struct prestera_br_mdb_entry *br_mdb, + struct net_device *orig_dev) +{ + struct prestera_br_mdb_port *tmp_port; + + list_for_each_entry(tmp_port, &br_mdb->br_mdb_port_list, + br_mdb_port_node) + if (tmp_port->br_port->dev == orig_dev) + return true; + + return false; +} + +static int +prestera_mdb_port_add(struct prestera_mdb_entry *mdb, + struct net_device *orig_dev, + const unsigned char addr[ETH_ALEN], u16 vid) +{ + struct prestera_flood_domain *flood_domain = mdb->flood_domain; + int err; + + if (!prestera_flood_domain_port_find(flood_domain, + orig_dev, vid)) { + err = prestera_flood_domain_port_create(flood_domain, orig_dev, + vid); + if (err) + return err; + } + + return 0; +} + +/* Sync bridge mdb (software table) with HW table (if MC is enabled). */ +static int prestera_br_mdb_sync(struct prestera_bridge *br_dev) +{ + struct prestera_br_mdb_port *br_mdb_port; + struct prestera_bridge_port *br_port; + struct prestera_br_mdb_entry *br_mdb; + struct prestera_mdb_entry *mdb; + struct prestera_port *pr_port; + int err = 0; + + if (!br_dev->multicast_enabled) + return 0; + + list_for_each_entry(br_mdb, &br_dev->br_mdb_entry_list, + br_mdb_entry_node) { + mdb = br_mdb->mdb; + /* Make sure every port that explicitly been added to the mdb + * joins the specified group. + */ + list_for_each_entry(br_mdb_port, &br_mdb->br_mdb_port_list, + br_mdb_port_node) { + br_port = br_mdb_port->br_port; + pr_port = prestera_port_dev_lower_find(br_port->dev); + + /* Match only mdb and br_mdb ports that belong to the + * same broadcast domain. + */ + if (br_dev->vlan_enabled && + !prestera_port_vlan_by_vid(pr_port, + mdb->vid)) + continue; + + /* If port is not in MDB or there's no Mrouter + * clear HW mdb. + */ + if (prestera_br_mdb_port_is_member(br_mdb, + br_mdb_port->br_port->dev) && + br_dev->mrouter_exist) + err = prestera_mdb_port_add(mdb, br_port->dev, + mdb->addr, + mdb->vid); + else + prestera_mdb_port_del(mdb, br_port->dev); + + if (err) + return err; + } + + /* Make sure that every mrouter port joins every MC group int + * broadcast domain. If it's not an mrouter - it should leave + */ + list_for_each_entry(br_port, &br_dev->port_list, head) { + pr_port = prestera_port_dev_lower_find(br_port->dev); + + /* Make sure mrouter woudln't receive traffci from + * another broadcast domain (e.g. from a vlan, which + * mrouter port is not a member of). + */ + if (br_dev->vlan_enabled && + !prestera_port_vlan_by_vid(pr_port, + mdb->vid)) + continue; + + if (br_port->mrouter) { + err = prestera_mdb_port_add(mdb, br_port->dev, + mdb->addr, + mdb->vid); + if (err) + return err; + } else if (!br_port->mrouter && + !prestera_br_mdb_port_is_member + (br_mdb, br_port->dev)) { + prestera_mdb_port_del(mdb, br_port->dev); + } + } + } + + return 0; +} + +static int +prestera_mdb_enable_set(struct prestera_br_mdb_entry *br_mdb, bool enable) +{ + int err; + + if (enable != br_mdb->enabled) { + if (enable) + err = prestera_hw_mdb_create(br_mdb->mdb); + else + err = prestera_hw_mdb_destroy(br_mdb->mdb); + + if (err) + return err; + + br_mdb->enabled = enable; + } + + return 0; +} + +static int +prestera_br_mdb_enable_set(struct prestera_bridge *br_dev, bool enable) +{ + struct prestera_br_mdb_entry *br_mdb; + int err; + + list_for_each_entry(br_mdb, &br_dev->br_mdb_entry_list, + br_mdb_entry_node) { + err = prestera_mdb_enable_set(br_mdb, enable); + if (err) + return err; + } + + return 0; +} + +static int prestera_port_attr_br_mc_disabled_set(struct prestera_port *port, + struct net_device *orig_dev, + bool mc_disabled) +{ + struct prestera_switch *sw = port->sw; + struct prestera_bridge *br_dev; + + br_dev = prestera_bridge_find(sw, orig_dev); + if (!br_dev) + return 0; + + br_dev->multicast_enabled = !mc_disabled; + + /* There's no point in enabling mdb back if router is missing. */ + WARN_ON(prestera_br_mdb_enable_set(br_dev, br_dev->multicast_enabled && + br_dev->mrouter_exist)); + + WARN_ON(prestera_br_mdb_sync(br_dev)); + + WARN_ON(prestera_br_mdb_mc_enable_sync(br_dev)); + + return 0; +} + +static bool +prestera_bridge_mdb_mc_mrouter_exists(struct prestera_bridge *br_dev) +{ + struct prestera_bridge_port *br_port; + + list_for_each_entry(br_port, &br_dev->port_list, head) + if (br_port->mrouter) + return true; + + return false; +} + +static int +prestera_port_attr_mrouter_set(struct prestera_port *port, + struct net_device *orig_dev, + bool is_port_mrouter) +{ + struct prestera_bridge_port *br_port; + struct prestera_bridge *br_dev; + + br_port = prestera_bridge_port_find(port->sw, orig_dev); + if (!br_port) + return 0; + + br_dev = br_port->bridge; + br_port->mrouter = is_port_mrouter; + + br_dev->mrouter_exist = prestera_bridge_mdb_mc_mrouter_exists(br_dev); + + /* Enable MDB processing if both mrouter exists and mc is enabled. + * In case if MC enabled, but there is no mrouter, device would flood + * all multicast traffic (even if MDB table is not empty) with the use + * of bridge's flood capabilities (without the use of flood_domain). + */ + WARN_ON(prestera_br_mdb_enable_set(br_dev, br_dev->multicast_enabled && + br_dev->mrouter_exist)); + + WARN_ON(prestera_br_mdb_sync(br_dev)); + + WARN_ON(prestera_br_mdb_mc_enable_sync(br_dev)); + + return 0; +} + static int prestera_port_obj_attr_set(struct net_device *dev, const void *ctx, const struct switchdev_attr *attr, struct netlink_ext_ack *extack) @@ -745,6 +1178,14 @@ static int prestera_port_obj_attr_set(struct net_device *dev, const void *ctx, err = prestera_port_attr_br_vlan_set(port, attr->orig_dev, attr->u.vlan_filtering); break; + case SWITCHDEV_ATTR_ID_PORT_MROUTER: + err = prestera_port_attr_mrouter_set(port, attr->orig_dev, + attr->u.mrouter); + break; + case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED: + err = prestera_port_attr_br_mc_disabled_set(port, attr->orig_dev, + attr->u.mc_disabled); + break; default: err = -EOPNOTSUPP; } @@ -918,14 +1359,9 @@ prestera_port_vlan_bridge_join(struct prestera_port_vlan *port_vlan, if (port_vlan->br_port) return 0; - err = prestera_hw_port_flood_set(port, BR_FLOOD | BR_MCAST_FLOOD, - br_port->flags); + err = prestera_br_port_flags_set(br_port, port); if (err) - return err; - - err = prestera_hw_port_learning_set(port, br_port->flags & BR_LEARNING); - if (err) - goto err_port_learning_set; + goto err_flags2port_set; err = prestera_port_vid_stp_set(port, vid, br_port->stp_state); if (err) @@ -950,8 +1386,8 @@ prestera_port_vlan_bridge_join(struct prestera_port_vlan *port_vlan, err_bridge_vlan_get: prestera_port_vid_stp_set(port, vid, BR_STATE_FORWARDING); err_port_vid_stp_set: - prestera_hw_port_learning_set(port, false); -err_port_learning_set: + prestera_br_port_flags_reset(br_port, port); +err_flags2port_set: return err; } @@ -1048,20 +1484,162 @@ static int prestera_port_vlans_add(struct prestera_port *port, flag_pvid, extack); } +static struct prestera_br_mdb_entry * +prestera_br_mdb_entry_create(struct prestera_switch *sw, + struct prestera_bridge *br_dev, + const unsigned char *addr, u16 vid) +{ + struct prestera_br_mdb_entry *br_mdb_entry; + struct prestera_mdb_entry *mdb_entry; + + br_mdb_entry = kzalloc(sizeof(*br_mdb_entry), GFP_KERNEL); + if (!br_mdb_entry) + return NULL; + + mdb_entry = prestera_mdb_entry_create(sw, addr, vid); + if (!mdb_entry) + goto err_mdb_alloc; + + br_mdb_entry->mdb = mdb_entry; + br_mdb_entry->bridge = br_dev; + br_mdb_entry->enabled = true; + INIT_LIST_HEAD(&br_mdb_entry->br_mdb_port_list); + + list_add(&br_mdb_entry->br_mdb_entry_node, &br_dev->br_mdb_entry_list); + + return br_mdb_entry; + +err_mdb_alloc: + kfree(br_mdb_entry); + return NULL; +} + +static int prestera_br_mdb_port_add(struct prestera_br_mdb_entry *br_mdb, + struct prestera_bridge_port *br_port) +{ + struct prestera_br_mdb_port *br_mdb_port; + + list_for_each_entry(br_mdb_port, &br_mdb->br_mdb_port_list, + br_mdb_port_node) + if (br_mdb_port->br_port == br_port) + return 0; + + br_mdb_port = kzalloc(sizeof(*br_mdb_port), GFP_KERNEL); + if (!br_mdb_port) + return -ENOMEM; + + br_mdb_port->br_port = br_port; + list_add(&br_mdb_port->br_mdb_port_node, + &br_mdb->br_mdb_port_list); + + return 0; +} + +static struct prestera_br_mdb_entry * +prestera_br_mdb_entry_find(struct prestera_bridge *br_dev, + const unsigned char *addr, u16 vid) +{ + struct prestera_br_mdb_entry *br_mdb; + + list_for_each_entry(br_mdb, &br_dev->br_mdb_entry_list, + br_mdb_entry_node) + if (ether_addr_equal(&br_mdb->mdb->addr[0], addr) && + vid == br_mdb->mdb->vid) + return br_mdb; + + return NULL; +} + +static struct prestera_br_mdb_entry * +prestera_br_mdb_entry_get(struct prestera_switch *sw, + struct prestera_bridge *br_dev, + const unsigned char *addr, u16 vid) +{ + struct prestera_br_mdb_entry *br_mdb; + + br_mdb = prestera_br_mdb_entry_find(br_dev, addr, vid); + if (br_mdb) + return br_mdb; + + return prestera_br_mdb_entry_create(sw, br_dev, addr, vid); +} + +static int +prestera_mdb_port_addr_obj_add(const struct switchdev_obj_port_mdb *mdb) +{ + struct prestera_br_mdb_entry *br_mdb; + struct prestera_bridge_port *br_port; + struct prestera_bridge *br_dev; + struct prestera_switch *sw; + struct prestera_port *port; + int err; + + sw = prestera_switch_get(mdb->obj.orig_dev); + port = prestera_port_dev_lower_find(mdb->obj.orig_dev); + + br_port = prestera_bridge_port_find(sw, mdb->obj.orig_dev); + if (!br_port) + return 0; + + br_dev = br_port->bridge; + + if (mdb->vid && !prestera_port_vlan_by_vid(port, mdb->vid)) + return 0; + + if (mdb->vid) + br_mdb = prestera_br_mdb_entry_get(sw, br_dev, &mdb->addr[0], + mdb->vid); + else + br_mdb = prestera_br_mdb_entry_get(sw, br_dev, &mdb->addr[0], + br_dev->bridge_id); + + if (!br_mdb) + return -ENOMEM; + + /* Make sure newly allocated MDB entry gets disabled if either MC is + * disabled, or the mrouter does not exist. + */ + WARN_ON(prestera_mdb_enable_set(br_mdb, br_dev->multicast_enabled && + br_dev->mrouter_exist)); + + err = prestera_br_mdb_port_add(br_mdb, br_port); + if (err) { + prestera_br_mdb_entry_put(br_mdb); + return err; + } + + err = prestera_br_mdb_sync(br_dev); + if (err) + return err; + + return 0; +} + static int prestera_port_obj_add(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj, struct netlink_ext_ack *extack) { struct prestera_port *port = netdev_priv(dev); const struct switchdev_obj_port_vlan *vlan; + const struct switchdev_obj_port_mdb *mdb; + int err = 0; switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); return prestera_port_vlans_add(port, vlan, extack); + case SWITCHDEV_OBJ_ID_PORT_MDB: + mdb = SWITCHDEV_OBJ_PORT_MDB(obj); + err = prestera_mdb_port_addr_obj_add(mdb); + break; + case SWITCHDEV_OBJ_ID_HOST_MDB: + fallthrough; default: - return -EOPNOTSUPP; + err = -EOPNOTSUPP; + break; } + + return err; } static int prestera_port_vlans_del(struct prestera_port *port, @@ -1086,17 +1664,71 @@ static int prestera_port_vlans_del(struct prestera_port *port, return 0; } +static int +prestera_mdb_port_addr_obj_del(struct prestera_port *port, + const struct switchdev_obj_port_mdb *mdb) +{ + struct prestera_br_mdb_entry *br_mdb; + struct prestera_bridge_port *br_port; + struct prestera_bridge *br_dev; + int err; + + /* Bridge port no longer exists - and so does this MDB entry */ + br_port = prestera_bridge_port_find(port->sw, mdb->obj.orig_dev); + if (!br_port) + return 0; + + /* Removing MDB with non-existing VLAN - not supported; */ + if (mdb->vid && !prestera_port_vlan_by_vid(port, mdb->vid)) + return 0; + + br_dev = br_port->bridge; + + if (br_port->bridge->vlan_enabled) + br_mdb = prestera_br_mdb_entry_find(br_dev, &mdb->addr[0], + mdb->vid); + else + br_mdb = prestera_br_mdb_entry_find(br_dev, &mdb->addr[0], + br_port->bridge->bridge_id); + + if (!br_mdb) + return 0; + + /* Since there might be a situation that this port was the last in the + * MDB group, we have to both remove this port from software and HW MDB, + * sync MDB table, and then destroy software MDB (if needed). + */ + prestera_br_mdb_port_del(br_mdb, br_port); + + prestera_br_mdb_entry_put(br_mdb); + + err = prestera_br_mdb_sync(br_dev); + if (err) + return err; + + return 0; +} + static int prestera_port_obj_del(struct net_device *dev, const void *ctx, const struct switchdev_obj *obj) { struct prestera_port *port = netdev_priv(dev); + const struct switchdev_obj_port_mdb *mdb; + int err = 0; switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: return prestera_port_vlans_del(port, SWITCHDEV_OBJ_PORT_VLAN(obj)); + case SWITCHDEV_OBJ_ID_PORT_MDB: + mdb = SWITCHDEV_OBJ_PORT_MDB(obj); + err = prestera_mdb_port_addr_obj_del(port, mdb); + break; default: - return -EOPNOTSUPP; + err = -EOPNOTSUPP; + break; } + + return err; } static int prestera_switchdev_blk_event(struct notifier_block *unused, diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index a1e907c85217..bbea5458000b 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -1863,7 +1863,7 @@ static netdev_tx_t sky2_xmit_frame(struct sk_buff *skb, if (mss != 0) { if (!(hw->flags & SKY2_HW_NEW_LE)) - mss += ETH_HLEN + ip_hdrlen(skb) + tcp_hdrlen(skb); + mss += skb_tcp_all_headers(skb); if (mss != sky2->tx_last_mss) { le = get_tx_le(sky2, &slot); @@ -4711,7 +4711,7 @@ static irqreturn_t sky2_test_intr(int irq, void *dev_id) return IRQ_HANDLED; } -/* Test interrupt path by forcing a a software IRQ */ +/* Test interrupt path by forcing a software IRQ */ static int sky2_test_msi(struct sky2_hw *hw) { struct pci_dev *pdev = hw->pdev; |