diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-09-07 00:45:08 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-09-07 00:45:08 +0300 |
commit | aae3dbb4776e7916b6cd442d00159bea27a695c1 (patch) | |
tree | d074c5d783a81e7e2e084b1eba77f57459da7e37 /drivers/net/ethernet | |
parent | ec3604c7a5aae8953545b0d05495357009a960e5 (diff) | |
parent | 66bed8465a808400eb14562510e26c8818082cb8 (diff) | |
download | linux-aae3dbb4776e7916b6cd442d00159bea27a695c1.tar.xz |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next
Pull networking updates from David Miller:
1) Support ipv6 checksum offload in sunvnet driver, from Shannon
Nelson.
2) Move to RB-tree instead of custom AVL code in inetpeer, from Eric
Dumazet.
3) Allow generic XDP to work on virtual devices, from John Fastabend.
4) Add bpf device maps and XDP_REDIRECT, which can be used to build
arbitrary switching frameworks using XDP. From John Fastabend.
5) Remove UFO offloads from the tree, gave us little other than bugs.
6) Remove the IPSEC flow cache, from Florian Westphal.
7) Support ipv6 route offload in mlxsw driver.
8) Support VF representors in bnxt_en, from Sathya Perla.
9) Add support for forward error correction modes to ethtool, from
Vidya Sagar Ravipati.
10) Add time filter for packet scheduler action dumping, from Jamal Hadi
Salim.
11) Extend the zerocopy sendmsg() used by virtio and tap to regular
sockets via MSG_ZEROCOPY. From Willem de Bruijn.
12) Significantly rework value tracking in the BPF verifier, from Edward
Cree.
13) Add new jump instructions to eBPF, from Daniel Borkmann.
14) Rework rtnetlink plumbing so that operations can be run without
taking the RTNL semaphore. From Florian Westphal.
15) Support XDP in tap driver, from Jason Wang.
16) Add 32-bit eBPF JIT for ARM, from Shubham Bansal.
17) Add Huawei hinic ethernet driver.
18) Allow to report MD5 keys in TCP inet_diag dumps, from Ivan
Delalande.
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next: (1780 commits)
i40e: point wb_desc at the nvm_wb_desc during i40e_read_nvm_aq
i40e: avoid NVM acquire deadlock during NVM update
drivers: net: xgene: Remove return statement from void function
drivers: net: xgene: Configure tx/rx delay for ACPI
drivers: net: xgene: Read tx/rx delay for ACPI
rocker: fix kcalloc parameter order
rds: Fix non-atomic operation on shared flag variable
net: sched: don't use GFP_KERNEL under spin lock
vhost_net: correctly check tx avail during rx busy polling
net: mdio-mux: add mdio_mux parameter to mdio_mux_init()
rxrpc: Make service connection lookup always check for retry
net: stmmac: Delete dead code for MDIO registration
gianfar: Fix Tx flow control deactivation
cxgb4: Ignore MPS_TX_INT_CAUSE[Bubble] for T6
cxgb4: Fix pause frame count in t4_get_port_stats
cxgb4: fix memory leak
tun: rename generic_xdp to skb_xdp
tun: reserve extra headroom only when XDP is set
net: dsa: bcm_sf2: Configure IMP port TC2QOS mapping
net: dsa: bcm_sf2: Advertise number of egress queues
...
Diffstat (limited to 'drivers/net/ethernet')
405 files changed, 48243 insertions, 6506 deletions
diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c index f66c9710cb81..b223769d6a5e 100644 --- a/drivers/net/ethernet/3com/3c509.c +++ b/drivers/net/ethernet/3com/3c509.c @@ -392,7 +392,7 @@ static struct isa_driver el3_isa_driver = { static int isa_registered; #ifdef CONFIG_PNP -static struct pnp_device_id el3_pnp_ids[] = { +static const struct pnp_device_id el3_pnp_ids[] = { { .id = "TCM5090" }, /* 3Com Etherlink III (TP) */ { .id = "TCM5091" }, /* 3Com Etherlink III */ { .id = "TCM5094" }, /* 3Com Etherlink III (combo) */ @@ -474,7 +474,7 @@ static int pnp_registered; #endif /* CONFIG_PNP */ #ifdef CONFIG_EISA -static struct eisa_device_id el3_eisa_ids[] = { +static const struct eisa_device_id el3_eisa_ids[] = { { "TCM5090" }, { "TCM5091" }, { "TCM5092" }, diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c index 3b516ebeeddb..402d9090ad29 100644 --- a/drivers/net/ethernet/3com/3c59x.c +++ b/drivers/net/ethernet/3com/3c59x.c @@ -900,7 +900,7 @@ static const struct dev_pm_ops vortex_pm_ops = { #endif /* !CONFIG_PM */ #ifdef CONFIG_EISA -static struct eisa_device_id vortex_eisa_ids[] = { +static const struct eisa_device_id vortex_eisa_ids[] = { { "TCM5920", CH_3C592 }, { "TCM5970", CH_3C597 }, { "" } diff --git a/drivers/net/ethernet/8390/ax88796.c b/drivers/net/ethernet/8390/ax88796.c index 05d9d3e2e92e..245554707163 100644 --- a/drivers/net/ethernet/8390/ax88796.c +++ b/drivers/net/ethernet/8390/ax88796.c @@ -585,7 +585,7 @@ static int ax_bb_get_data(struct mdiobb_ctrl *ctrl) return reg_memr & AX_MEMR_MDI ? 1 : 0; } -static struct mdiobb_ops bb_ops = { +static const struct mdiobb_ops bb_ops = { .owner = THIS_MODULE, .set_mdc = ax_bb_mdc, .set_mdio_dir = ax_bb_dir, diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig index edae15ac0e98..c60421339a98 100644 --- a/drivers/net/ethernet/Kconfig +++ b/drivers/net/ethernet/Kconfig @@ -78,6 +78,7 @@ source "drivers/net/ethernet/freescale/Kconfig" source "drivers/net/ethernet/fujitsu/Kconfig" source "drivers/net/ethernet/hisilicon/Kconfig" source "drivers/net/ethernet/hp/Kconfig" +source "drivers/net/ethernet/huawei/Kconfig" source "drivers/net/ethernet/ibm/Kconfig" source "drivers/net/ethernet/intel/Kconfig" source "drivers/net/ethernet/i825xx/Kconfig" diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index bf7f4502cabc..a0a03d4d939a 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_NET_VENDOR_FREESCALE) += freescale/ obj-$(CONFIG_NET_VENDOR_FUJITSU) += fujitsu/ obj-$(CONFIG_NET_VENDOR_HISILICON) += hisilicon/ obj-$(CONFIG_NET_VENDOR_HP) += hp/ +obj-$(CONFIG_NET_VENDOR_HUAWEI) += huawei/ obj-$(CONFIG_NET_VENDOR_IBM) += ibm/ obj-$(CONFIG_NET_VENDOR_INTEL) += intel/ obj-$(CONFIG_NET_VENDOR_I825XX) += i825xx/ diff --git a/drivers/net/ethernet/adi/bfin_mac.c b/drivers/net/ethernet/adi/bfin_mac.c index a9ac58c351a0..a251de8d9a91 100644 --- a/drivers/net/ethernet/adi/bfin_mac.c +++ b/drivers/net/ethernet/adi/bfin_mac.c @@ -986,7 +986,7 @@ static int bfin_ptp_enable(struct ptp_clock_info *ptp, return -EOPNOTSUPP; } -static struct ptp_clock_info bfin_ptp_caps = { +static const struct ptp_clock_info bfin_ptp_caps = { .owner = THIS_MODULE, .name = "BF518 clock", .max_adj = 0, diff --git a/drivers/net/ethernet/amd/a2065.c b/drivers/net/ethernet/amd/a2065.c index ee4b94e3cda9..e22f976a0d18 100644 --- a/drivers/net/ethernet/amd/a2065.c +++ b/drivers/net/ethernet/amd/a2065.c @@ -643,7 +643,7 @@ static int a2065_init_one(struct zorro_dev *z, static void a2065_remove_one(struct zorro_dev *z); -static struct zorro_device_id a2065_zorro_tbl[] = { +static const struct zorro_device_id a2065_zorro_tbl[] = { { ZORRO_PROD_CBM_A2065_1 }, { ZORRO_PROD_CBM_A2065_2 }, { ZORRO_PROD_AMERISTAR_A2065 }, diff --git a/drivers/net/ethernet/amd/ariadne.c b/drivers/net/ethernet/amd/ariadne.c index 5fd7b15b0574..4b6a5cb85dd2 100644 --- a/drivers/net/ethernet/amd/ariadne.c +++ b/drivers/net/ethernet/amd/ariadne.c @@ -692,7 +692,7 @@ static void ariadne_remove_one(struct zorro_dev *z) free_netdev(dev); } -static struct zorro_device_id ariadne_zorro_tbl[] = { +static const struct zorro_device_id ariadne_zorro_tbl[] = { { ZORRO_PROD_VILLAGE_TRONIC_ARIADNE }, { 0 } }; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h index 9795419aac2d..7ea72ef11a55 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h @@ -210,11 +210,15 @@ #define DMA_CH_CR_PBLX8_WIDTH 1 #define DMA_CH_CR_SPH_INDEX 24 #define DMA_CH_CR_SPH_WIDTH 1 -#define DMA_CH_IER_AIE_INDEX 15 +#define DMA_CH_IER_AIE20_INDEX 15 +#define DMA_CH_IER_AIE20_WIDTH 1 +#define DMA_CH_IER_AIE_INDEX 14 #define DMA_CH_IER_AIE_WIDTH 1 #define DMA_CH_IER_FBEE_INDEX 12 #define DMA_CH_IER_FBEE_WIDTH 1 -#define DMA_CH_IER_NIE_INDEX 16 +#define DMA_CH_IER_NIE20_INDEX 16 +#define DMA_CH_IER_NIE20_WIDTH 1 +#define DMA_CH_IER_NIE_INDEX 15 #define DMA_CH_IER_NIE_WIDTH 1 #define DMA_CH_IER_RBUE_INDEX 7 #define DMA_CH_IER_RBUE_WIDTH 1 @@ -298,6 +302,7 @@ #define MAC_RWKPFR 0x00c4 #define MAC_LPICSR 0x00d0 #define MAC_LPITCR 0x00d4 +#define MAC_TIR 0x00e0 #define MAC_VR 0x0110 #define MAC_DR 0x0114 #define MAC_HWF0R 0x011c @@ -364,6 +369,8 @@ #define MAC_HWF0R_TXCOESEL_WIDTH 1 #define MAC_HWF0R_VLHASH_INDEX 4 #define MAC_HWF0R_VLHASH_WIDTH 1 +#define MAC_HWF0R_VXN_INDEX 29 +#define MAC_HWF0R_VXN_WIDTH 1 #define MAC_HWF1R_ADDR64_INDEX 14 #define MAC_HWF1R_ADDR64_WIDTH 2 #define MAC_HWF1R_ADVTHWORD_INDEX 13 @@ -448,6 +455,8 @@ #define MAC_PFR_PR_WIDTH 1 #define MAC_PFR_VTFE_INDEX 16 #define MAC_PFR_VTFE_WIDTH 1 +#define MAC_PFR_VUCC_INDEX 22 +#define MAC_PFR_VUCC_WIDTH 1 #define MAC_PMTCSR_MGKPKTEN_INDEX 1 #define MAC_PMTCSR_MGKPKTEN_WIDTH 1 #define MAC_PMTCSR_PWRDWN_INDEX 0 @@ -510,6 +519,12 @@ #define MAC_TCR_SS_WIDTH 2 #define MAC_TCR_TE_INDEX 0 #define MAC_TCR_TE_WIDTH 1 +#define MAC_TCR_VNE_INDEX 24 +#define MAC_TCR_VNE_WIDTH 1 +#define MAC_TCR_VNM_INDEX 25 +#define MAC_TCR_VNM_WIDTH 1 +#define MAC_TIR_TNID_INDEX 0 +#define MAC_TIR_TNID_WIDTH 16 #define MAC_TSCR_AV8021ASMEN_INDEX 28 #define MAC_TSCR_AV8021ASMEN_WIDTH 1 #define MAC_TSCR_SNAPTYPSEL_INDEX 16 @@ -1153,11 +1168,17 @@ #define RX_PACKET_ATTRIBUTES_RSS_HASH_WIDTH 1 #define RX_PACKET_ATTRIBUTES_FIRST_INDEX 7 #define RX_PACKET_ATTRIBUTES_FIRST_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_TNP_INDEX 8 +#define RX_PACKET_ATTRIBUTES_TNP_WIDTH 1 +#define RX_PACKET_ATTRIBUTES_TNPCSUM_DONE_INDEX 9 +#define RX_PACKET_ATTRIBUTES_TNPCSUM_DONE_WIDTH 1 #define RX_NORMAL_DESC0_OVT_INDEX 0 #define RX_NORMAL_DESC0_OVT_WIDTH 16 #define RX_NORMAL_DESC2_HL_INDEX 0 #define RX_NORMAL_DESC2_HL_WIDTH 10 +#define RX_NORMAL_DESC2_TNP_INDEX 11 +#define RX_NORMAL_DESC2_TNP_WIDTH 1 #define RX_NORMAL_DESC3_CDA_INDEX 27 #define RX_NORMAL_DESC3_CDA_WIDTH 1 #define RX_NORMAL_DESC3_CTXT_INDEX 30 @@ -1184,9 +1205,11 @@ #define RX_DESC3_L34T_IPV4_TCP 1 #define RX_DESC3_L34T_IPV4_UDP 2 #define RX_DESC3_L34T_IPV4_ICMP 3 +#define RX_DESC3_L34T_IPV4_UNKNOWN 7 #define RX_DESC3_L34T_IPV6_TCP 9 #define RX_DESC3_L34T_IPV6_UDP 10 #define RX_DESC3_L34T_IPV6_ICMP 11 +#define RX_DESC3_L34T_IPV6_UNKNOWN 15 #define RX_CONTEXT_DESC3_TSA_INDEX 4 #define RX_CONTEXT_DESC3_TSA_WIDTH 1 @@ -1201,6 +1224,8 @@ #define TX_PACKET_ATTRIBUTES_VLAN_CTAG_WIDTH 1 #define TX_PACKET_ATTRIBUTES_PTP_INDEX 3 #define TX_PACKET_ATTRIBUTES_PTP_WIDTH 1 +#define TX_PACKET_ATTRIBUTES_VXLAN_INDEX 4 +#define TX_PACKET_ATTRIBUTES_VXLAN_WIDTH 1 #define TX_CONTEXT_DESC2_MSS_INDEX 0 #define TX_CONTEXT_DESC2_MSS_WIDTH 15 @@ -1241,8 +1266,11 @@ #define TX_NORMAL_DESC3_TCPPL_WIDTH 18 #define TX_NORMAL_DESC3_TSE_INDEX 18 #define TX_NORMAL_DESC3_TSE_WIDTH 1 +#define TX_NORMAL_DESC3_VNP_INDEX 23 +#define TX_NORMAL_DESC3_VNP_WIDTH 3 #define TX_NORMAL_DESC2_VLAN_INSERT 0x2 +#define TX_NORMAL_DESC3_VXLAN_PACKET 0x3 /* MDIO undefined or vendor specific registers */ #ifndef MDIO_PMA_10GBR_PMD_CTRL @@ -1339,6 +1367,7 @@ #define XGBE_AN_CL37_PCS_MODE_BASEX 0x00 #define XGBE_AN_CL37_PCS_MODE_SGMII 0x04 #define XGBE_AN_CL37_TX_CONFIG_MASK 0x08 +#define XGBE_AN_CL37_MII_CTRL_8BIT 0x0100 /* Bit setting and getting macros * The get macro will extract the current bit field value from within diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c index 7546b660d6b5..7d128be61310 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c @@ -527,3 +527,28 @@ void xgbe_debugfs_exit(struct xgbe_prv_data *pdata) debugfs_remove_recursive(pdata->xgbe_debugfs); pdata->xgbe_debugfs = NULL; } + +void xgbe_debugfs_rename(struct xgbe_prv_data *pdata) +{ + struct dentry *pfile; + char *buf; + + if (!pdata->xgbe_debugfs) + return; + + buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name); + if (!buf) + return; + + if (!strcmp(pdata->xgbe_debugfs->d_name.name, buf)) + goto out; + + pfile = debugfs_rename(pdata->xgbe_debugfs->d_parent, + pdata->xgbe_debugfs, + pdata->xgbe_debugfs->d_parent, buf); + if (!pfile) + netdev_err(pdata->netdev, "debugfs_rename failed\n"); + +out: + kfree(buf); +} diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c index 06f953e1e9b2..e107e180e2c8 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-dev.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-dev.c @@ -479,6 +479,50 @@ static bool xgbe_is_pfc_queue(struct xgbe_prv_data *pdata, return false; } +static void xgbe_set_vxlan_id(struct xgbe_prv_data *pdata) +{ + /* Program the VXLAN port */ + XGMAC_IOWRITE_BITS(pdata, MAC_TIR, TNID, pdata->vxlan_port); + + netif_dbg(pdata, drv, pdata->netdev, "VXLAN tunnel id set to %hx\n", + pdata->vxlan_port); +} + +static void xgbe_enable_vxlan(struct xgbe_prv_data *pdata) +{ + if (!pdata->hw_feat.vxn) + return; + + /* Program the VXLAN port */ + xgbe_set_vxlan_id(pdata); + + /* Allow for IPv6/UDP zero-checksum VXLAN packets */ + XGMAC_IOWRITE_BITS(pdata, MAC_PFR, VUCC, 1); + + /* Enable VXLAN tunneling mode */ + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, VNM, 0); + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, VNE, 1); + + netif_dbg(pdata, drv, pdata->netdev, "VXLAN acceleration enabled\n"); +} + +static void xgbe_disable_vxlan(struct xgbe_prv_data *pdata) +{ + if (!pdata->hw_feat.vxn) + return; + + /* Disable tunneling mode */ + XGMAC_IOWRITE_BITS(pdata, MAC_TCR, VNE, 0); + + /* Clear IPv6/UDP zero-checksum VXLAN packets setting */ + XGMAC_IOWRITE_BITS(pdata, MAC_PFR, VUCC, 0); + + /* Clear the VXLAN port */ + XGMAC_IOWRITE_BITS(pdata, MAC_TIR, TNID, 0); + + netif_dbg(pdata, drv, pdata->netdev, "VXLAN acceleration disabled\n"); +} + static int xgbe_disable_tx_flow_control(struct xgbe_prv_data *pdata) { unsigned int max_q_count, q_count; @@ -605,32 +649,38 @@ static void xgbe_config_flow_control(struct xgbe_prv_data *pdata) static void xgbe_enable_dma_interrupts(struct xgbe_prv_data *pdata) { struct xgbe_channel *channel; - unsigned int dma_ch_isr, dma_ch_ier; - unsigned int i; + unsigned int i, ver; /* Set the interrupt mode if supported */ if (pdata->channel_irq_mode) XGMAC_IOWRITE_BITS(pdata, DMA_MR, INTM, pdata->channel_irq_mode); + ver = XGMAC_GET_BITS(pdata->hw_feat.version, MAC_VR, SNPSVER); + for (i = 0; i < pdata->channel_count; i++) { channel = pdata->channel[i]; /* Clear all the interrupts which are set */ - dma_ch_isr = XGMAC_DMA_IOREAD(channel, DMA_CH_SR); - XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, dma_ch_isr); + XGMAC_DMA_IOWRITE(channel, DMA_CH_SR, + XGMAC_DMA_IOREAD(channel, DMA_CH_SR)); /* Clear all interrupt enable bits */ - dma_ch_ier = 0; + channel->curr_ier = 0; /* Enable following interrupts * NIE - Normal Interrupt Summary Enable * AIE - Abnormal Interrupt Summary Enable * FBEE - Fatal Bus Error Enable */ - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, NIE, 1); - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, AIE, 1); - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, FBEE, 1); + if (ver < 0x21) { + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, NIE20, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, AIE20, 1); + } else { + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, NIE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, AIE, 1); + } + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, FBEE, 1); if (channel->tx_ring) { /* Enable the following Tx interrupts @@ -639,7 +689,8 @@ static void xgbe_enable_dma_interrupts(struct xgbe_prv_data *pdata) * mode) */ if (!pdata->per_channel_irq || pdata->channel_irq_mode) - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TIE, 1); + XGMAC_SET_BITS(channel->curr_ier, + DMA_CH_IER, TIE, 1); } if (channel->rx_ring) { /* Enable following Rx interrupts @@ -648,12 +699,13 @@ static void xgbe_enable_dma_interrupts(struct xgbe_prv_data *pdata) * per channel interrupts in edge triggered * mode) */ - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RBUE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RBUE, 1); if (!pdata->per_channel_irq || pdata->channel_irq_mode) - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 1); + XGMAC_SET_BITS(channel->curr_ier, + DMA_CH_IER, RIE, 1); } - XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, dma_ch_ier); + XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, channel->curr_ier); } } @@ -1608,7 +1660,8 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) struct xgbe_ring_data *rdata; struct xgbe_ring_desc *rdesc; struct xgbe_packet_data *packet = &ring->packet_data; - unsigned int csum, tso, vlan; + unsigned int tx_packets, tx_bytes; + unsigned int csum, tso, vlan, vxlan; unsigned int tso_context, vlan_context; unsigned int tx_set_ic; int start_index = ring->cur; @@ -1617,12 +1670,17 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) DBGPR("-->xgbe_dev_xmit\n"); + tx_packets = packet->tx_packets; + tx_bytes = packet->tx_bytes; + csum = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, CSUM_ENABLE); tso = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, TSO_ENABLE); vlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, VLAN_CTAG); + vxlan = XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + VXLAN); if (tso && (packet->mss != ring->tx.cur_mss)) tso_context = 1; @@ -1644,13 +1702,12 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) * - Addition of Tx frame count to the frame count since the * last interrupt was set does not exceed the frame count setting */ - ring->coalesce_count += packet->tx_packets; + ring->coalesce_count += tx_packets; if (!pdata->tx_frames) tx_set_ic = 0; - else if (packet->tx_packets > pdata->tx_frames) + else if (tx_packets > pdata->tx_frames) tx_set_ic = 1; - else if ((ring->coalesce_count % pdata->tx_frames) < - packet->tx_packets) + else if ((ring->coalesce_count % pdata->tx_frames) < tx_packets) tx_set_ic = 1; else tx_set_ic = 0; @@ -1740,7 +1797,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, TCPHDRLEN, packet->tcp_header_len / 4); - pdata->ext_stats.tx_tso_packets++; + pdata->ext_stats.tx_tso_packets += tx_packets; } else { /* Enable CRC and Pad Insertion */ XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CPC, 0); @@ -1755,6 +1812,13 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) packet->length); } + if (vxlan) { + XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, VNP, + TX_NORMAL_DESC3_VXLAN_PACKET); + + pdata->ext_stats.tx_vxlan_packets += packet->tx_packets; + } + for (i = cur_index - start_index + 1; i < packet->rdesc_count; i++) { cur_index++; rdata = XGBE_GET_DESC_DATA(ring, cur_index); @@ -1788,8 +1852,11 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel) XGMAC_SET_BITS_LE(rdesc->desc2, TX_NORMAL_DESC2, IC, 1); /* Save the Tx info to report back during cleanup */ - rdata->tx.packets = packet->tx_packets; - rdata->tx.bytes = packet->tx_bytes; + rdata->tx.packets = tx_packets; + rdata->tx.bytes = tx_bytes; + + pdata->ext_stats.txq_packets[channel->queue_index] += tx_packets; + pdata->ext_stats.txq_bytes[channel->queue_index] += tx_bytes; /* In case the Tx DMA engine is running, make sure everything * is written to the descriptor(s) before setting the OWN bit @@ -1913,9 +1980,28 @@ static int xgbe_dev_read(struct xgbe_channel *channel) rdata->rx.len = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, PL); /* Set checksum done indicator as appropriate */ - if (netdev->features & NETIF_F_RXCSUM) + if (netdev->features & NETIF_F_RXCSUM) { XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, CSUM_DONE, 1); + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNPCSUM_DONE, 1); + } + + /* Set the tunneled packet indicator */ + if (XGMAC_GET_BITS_LE(rdesc->desc2, RX_NORMAL_DESC2, TNP)) { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNP, 1); + pdata->ext_stats.rx_vxlan_packets++; + + l34t = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, L34T); + switch (l34t) { + case RX_DESC3_L34T_IPV4_UNKNOWN: + case RX_DESC3_L34T_IPV6_UNKNOWN: + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNPCSUM_DONE, 0); + break; + } + } /* Check for errors (only valid in last descriptor) */ err = XGMAC_GET_BITS_LE(rdesc->desc3, RX_NORMAL_DESC3, ES); @@ -1935,14 +2021,30 @@ static int xgbe_dev_read(struct xgbe_channel *channel) packet->vlan_ctag); } } else { - if ((etlt == 0x05) || (etlt == 0x06)) + unsigned int tnp = XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, TNP); + + if ((etlt == 0x05) || (etlt == 0x06)) { XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, CSUM_DONE, 0); - else + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNPCSUM_DONE, 0); + pdata->ext_stats.rx_csum_errors++; + } else if (tnp && ((etlt == 0x09) || (etlt == 0x0a))) { + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + CSUM_DONE, 0); + XGMAC_SET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, + TNPCSUM_DONE, 0); + pdata->ext_stats.rx_vxlan_csum_errors++; + } else { XGMAC_SET_BITS(packet->errors, RX_PACKET_ERRORS, FRAME, 1); + } } + pdata->ext_stats.rxq_packets[channel->queue_index]++; + pdata->ext_stats.rxq_bytes[channel->queue_index] += rdata->rx.len; + DBGPR("<--xgbe_dev_read: %s - descriptor=%u (cur=%d)\n", channel->name, ring->cur & (ring->rdesc_count - 1), ring->cur); @@ -1964,44 +2066,40 @@ static int xgbe_is_last_desc(struct xgbe_ring_desc *rdesc) static int xgbe_enable_int(struct xgbe_channel *channel, enum xgbe_int int_id) { - unsigned int dma_ch_ier; - - dma_ch_ier = XGMAC_DMA_IOREAD(channel, DMA_CH_IER); - switch (int_id) { case XGMAC_INT_DMA_CH_SR_TI: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TIE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TIE, 1); break; case XGMAC_INT_DMA_CH_SR_TPS: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TXSE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TXSE, 1); break; case XGMAC_INT_DMA_CH_SR_TBU: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TBUE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TBUE, 1); break; case XGMAC_INT_DMA_CH_SR_RI: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RIE, 1); break; case XGMAC_INT_DMA_CH_SR_RBU: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RBUE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RBUE, 1); break; case XGMAC_INT_DMA_CH_SR_RPS: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RSE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RSE, 1); break; case XGMAC_INT_DMA_CH_SR_TI_RI: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TIE, 1); - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TIE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RIE, 1); break; case XGMAC_INT_DMA_CH_SR_FBE: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, FBEE, 1); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, FBEE, 1); break; case XGMAC_INT_DMA_ALL: - dma_ch_ier |= channel->saved_ier; + channel->curr_ier |= channel->saved_ier; break; default: return -1; } - XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, dma_ch_ier); + XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, channel->curr_ier); return 0; } @@ -2009,45 +2107,41 @@ static int xgbe_enable_int(struct xgbe_channel *channel, static int xgbe_disable_int(struct xgbe_channel *channel, enum xgbe_int int_id) { - unsigned int dma_ch_ier; - - dma_ch_ier = XGMAC_DMA_IOREAD(channel, DMA_CH_IER); - switch (int_id) { case XGMAC_INT_DMA_CH_SR_TI: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TIE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TIE, 0); break; case XGMAC_INT_DMA_CH_SR_TPS: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TXSE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TXSE, 0); break; case XGMAC_INT_DMA_CH_SR_TBU: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TBUE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TBUE, 0); break; case XGMAC_INT_DMA_CH_SR_RI: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RIE, 0); break; case XGMAC_INT_DMA_CH_SR_RBU: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RBUE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RBUE, 0); break; case XGMAC_INT_DMA_CH_SR_RPS: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RSE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RSE, 0); break; case XGMAC_INT_DMA_CH_SR_TI_RI: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, TIE, 0); - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, RIE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, TIE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, RIE, 0); break; case XGMAC_INT_DMA_CH_SR_FBE: - XGMAC_SET_BITS(dma_ch_ier, DMA_CH_IER, FBEE, 0); + XGMAC_SET_BITS(channel->curr_ier, DMA_CH_IER, FBEE, 0); break; case XGMAC_INT_DMA_ALL: - channel->saved_ier = dma_ch_ier & XGBE_DMA_INTERRUPT_MASK; - dma_ch_ier &= ~XGBE_DMA_INTERRUPT_MASK; + channel->saved_ier = channel->curr_ier; + channel->curr_ier = 0; break; default: return -1; } - XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, dma_ch_ier); + XGMAC_DMA_IOWRITE(channel, DMA_CH_IER, channel->curr_ier); return 0; } @@ -3534,5 +3628,10 @@ void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if) hw_if->disable_ecc_ded = xgbe_disable_ecc_ded; hw_if->disable_ecc_sec = xgbe_disable_ecc_sec; + /* For VXLAN */ + hw_if->enable_vxlan = xgbe_enable_vxlan; + hw_if->disable_vxlan = xgbe_disable_vxlan; + hw_if->set_vxlan_id = xgbe_set_vxlan_id; + DBGPR("<--xgbe_init_function_ptrs\n"); } diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index ecef3ee87b17..608693d11bd7 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -124,6 +124,7 @@ #include <linux/if_ether.h> #include <linux/net_tstamp.h> #include <linux/phy.h> +#include <net/vxlan.h> #include "xgbe.h" #include "xgbe-common.h" @@ -732,8 +733,6 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata) unsigned int mac_hfr0, mac_hfr1, mac_hfr2; struct xgbe_hw_features *hw_feat = &pdata->hw_feat; - DBGPR("-->xgbe_get_all_hw_features\n"); - mac_hfr0 = XGMAC_IOREAD(pdata, MAC_HWF0R); mac_hfr1 = XGMAC_IOREAD(pdata, MAC_HWF1R); mac_hfr2 = XGMAC_IOREAD(pdata, MAC_HWF2R); @@ -758,6 +757,7 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata) ADDMACADRSEL); hw_feat->ts_src = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, TSSTSSEL); hw_feat->sa_vlan_ins = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, SAVLANINS); + hw_feat->vxn = XGMAC_GET_BITS(mac_hfr0, MAC_HWF0R, VXN); /* Hardware feature register 1 */ hw_feat->rx_fifo_size = XGMAC_GET_BITS(mac_hfr1, MAC_HWF1R, @@ -828,7 +828,193 @@ void xgbe_get_all_hw_features(struct xgbe_prv_data *pdata) hw_feat->rx_fifo_size = 1 << (hw_feat->rx_fifo_size + 7); hw_feat->tx_fifo_size = 1 << (hw_feat->tx_fifo_size + 7); - DBGPR("<--xgbe_get_all_hw_features\n"); + if (netif_msg_probe(pdata)) { + dev_dbg(pdata->dev, "Hardware features:\n"); + + /* Hardware feature register 0 */ + dev_dbg(pdata->dev, " 1GbE support : %s\n", + hw_feat->gmii ? "yes" : "no"); + dev_dbg(pdata->dev, " VLAN hash filter : %s\n", + hw_feat->vlhash ? "yes" : "no"); + dev_dbg(pdata->dev, " MDIO interface : %s\n", + hw_feat->sma ? "yes" : "no"); + dev_dbg(pdata->dev, " Wake-up packet support : %s\n", + hw_feat->rwk ? "yes" : "no"); + dev_dbg(pdata->dev, " Magic packet support : %s\n", + hw_feat->mgk ? "yes" : "no"); + dev_dbg(pdata->dev, " Management counters : %s\n", + hw_feat->mmc ? "yes" : "no"); + dev_dbg(pdata->dev, " ARP offload : %s\n", + hw_feat->aoe ? "yes" : "no"); + dev_dbg(pdata->dev, " IEEE 1588-2008 Timestamp : %s\n", + hw_feat->ts ? "yes" : "no"); + dev_dbg(pdata->dev, " Energy Efficient Ethernet : %s\n", + hw_feat->eee ? "yes" : "no"); + dev_dbg(pdata->dev, " TX checksum offload : %s\n", + hw_feat->tx_coe ? "yes" : "no"); + dev_dbg(pdata->dev, " RX checksum offload : %s\n", + hw_feat->rx_coe ? "yes" : "no"); + dev_dbg(pdata->dev, " Additional MAC addresses : %u\n", + hw_feat->addn_mac); + dev_dbg(pdata->dev, " Timestamp source : %s\n", + (hw_feat->ts_src == 1) ? "internal" : + (hw_feat->ts_src == 2) ? "external" : + (hw_feat->ts_src == 3) ? "internal/external" : "n/a"); + dev_dbg(pdata->dev, " SA/VLAN insertion : %s\n", + hw_feat->sa_vlan_ins ? "yes" : "no"); + dev_dbg(pdata->dev, " VXLAN/NVGRE support : %s\n", + hw_feat->vxn ? "yes" : "no"); + + /* Hardware feature register 1 */ + dev_dbg(pdata->dev, " RX fifo size : %u\n", + hw_feat->rx_fifo_size); + dev_dbg(pdata->dev, " TX fifo size : %u\n", + hw_feat->tx_fifo_size); + dev_dbg(pdata->dev, " IEEE 1588 high word : %s\n", + hw_feat->adv_ts_hi ? "yes" : "no"); + dev_dbg(pdata->dev, " DMA width : %u\n", + hw_feat->dma_width); + dev_dbg(pdata->dev, " Data Center Bridging : %s\n", + hw_feat->dcb ? "yes" : "no"); + dev_dbg(pdata->dev, " Split header : %s\n", + hw_feat->sph ? "yes" : "no"); + dev_dbg(pdata->dev, " TCP Segmentation Offload : %s\n", + hw_feat->tso ? "yes" : "no"); + dev_dbg(pdata->dev, " Debug memory interface : %s\n", + hw_feat->dma_debug ? "yes" : "no"); + dev_dbg(pdata->dev, " Receive Side Scaling : %s\n", + hw_feat->rss ? "yes" : "no"); + dev_dbg(pdata->dev, " Traffic Class count : %u\n", + hw_feat->tc_cnt); + dev_dbg(pdata->dev, " Hash table size : %u\n", + hw_feat->hash_table_size); + dev_dbg(pdata->dev, " L3/L4 Filters : %u\n", + hw_feat->l3l4_filter_num); + + /* Hardware feature register 2 */ + dev_dbg(pdata->dev, " RX queue count : %u\n", + hw_feat->rx_q_cnt); + dev_dbg(pdata->dev, " TX queue count : %u\n", + hw_feat->tx_q_cnt); + dev_dbg(pdata->dev, " RX DMA channel count : %u\n", + hw_feat->rx_ch_cnt); + dev_dbg(pdata->dev, " TX DMA channel count : %u\n", + hw_feat->rx_ch_cnt); + dev_dbg(pdata->dev, " PPS outputs : %u\n", + hw_feat->pps_out_num); + dev_dbg(pdata->dev, " Auxiliary snapshot inputs : %u\n", + hw_feat->aux_snap_num); + } +} + +static void xgbe_disable_vxlan_offloads(struct xgbe_prv_data *pdata) +{ + struct net_device *netdev = pdata->netdev; + + if (!pdata->vxlan_offloads_set) + return; + + netdev_info(netdev, "disabling VXLAN offloads\n"); + + netdev->hw_enc_features &= ~(NETIF_F_SG | + NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | + NETIF_F_TSO | + NETIF_F_TSO6 | + NETIF_F_GRO | + NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM); + + netdev->features &= ~(NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM); + + pdata->vxlan_offloads_set = 0; +} + +static void xgbe_disable_vxlan_hw(struct xgbe_prv_data *pdata) +{ + if (!pdata->vxlan_port_set) + return; + + pdata->hw_if.disable_vxlan(pdata); + + pdata->vxlan_port_set = 0; + pdata->vxlan_port = 0; +} + +static void xgbe_disable_vxlan_accel(struct xgbe_prv_data *pdata) +{ + xgbe_disable_vxlan_offloads(pdata); + + xgbe_disable_vxlan_hw(pdata); +} + +static void xgbe_enable_vxlan_offloads(struct xgbe_prv_data *pdata) +{ + struct net_device *netdev = pdata->netdev; + + if (pdata->vxlan_offloads_set) + return; + + netdev_info(netdev, "enabling VXLAN offloads\n"); + + netdev->hw_enc_features |= NETIF_F_SG | + NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | + NETIF_F_TSO | + NETIF_F_TSO6 | + NETIF_F_GRO | + pdata->vxlan_features; + + netdev->features |= pdata->vxlan_features; + + pdata->vxlan_offloads_set = 1; +} + +static void xgbe_enable_vxlan_hw(struct xgbe_prv_data *pdata) +{ + struct xgbe_vxlan_data *vdata; + + if (pdata->vxlan_port_set) + return; + + if (list_empty(&pdata->vxlan_ports)) + return; + + vdata = list_first_entry(&pdata->vxlan_ports, + struct xgbe_vxlan_data, list); + + pdata->vxlan_port_set = 1; + pdata->vxlan_port = be16_to_cpu(vdata->port); + + pdata->hw_if.enable_vxlan(pdata); +} + +static void xgbe_enable_vxlan_accel(struct xgbe_prv_data *pdata) +{ + /* VXLAN acceleration desired? */ + if (!pdata->vxlan_features) + return; + + /* VXLAN acceleration possible? */ + if (pdata->vxlan_force_disable) + return; + + xgbe_enable_vxlan_hw(pdata); + + xgbe_enable_vxlan_offloads(pdata); +} + +static void xgbe_reset_vxlan_accel(struct xgbe_prv_data *pdata) +{ + xgbe_disable_vxlan_hw(pdata); + + if (pdata->vxlan_features) + xgbe_enable_vxlan_offloads(pdata); + + pdata->vxlan_force_disable = 0; } static void xgbe_napi_enable(struct xgbe_prv_data *pdata, unsigned int add) @@ -887,7 +1073,7 @@ static int xgbe_request_irqs(struct xgbe_prv_data *pdata) (unsigned long)pdata); ret = devm_request_irq(pdata->dev, pdata->dev_irq, xgbe_isr, 0, - netdev->name, pdata); + netdev_name(netdev), pdata); if (ret) { netdev_alert(netdev, "error requesting irq %d\n", pdata->dev_irq); @@ -1154,6 +1340,8 @@ static int xgbe_start(struct xgbe_prv_data *pdata) hw_if->enable_tx(pdata); hw_if->enable_rx(pdata); + udp_tunnel_get_rx_info(netdev); + netif_tx_start_all_queues(netdev); xgbe_start_timers(pdata); @@ -1195,6 +1383,8 @@ static void xgbe_stop(struct xgbe_prv_data *pdata) xgbe_stop_timers(pdata); flush_workqueue(pdata->dev_workqueue); + xgbe_reset_vxlan_accel(pdata); + hw_if->disable_tx(pdata); hw_if->disable_rx(pdata); @@ -1483,10 +1673,18 @@ static int xgbe_prep_tso(struct sk_buff *skb, struct xgbe_packet_data *packet) if (ret) return ret; - packet->header_len = skb_transport_offset(skb) + tcp_hdrlen(skb); - packet->tcp_header_len = tcp_hdrlen(skb); + if (XGMAC_GET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, VXLAN)) { + packet->header_len = skb_inner_transport_offset(skb) + + inner_tcp_hdrlen(skb); + packet->tcp_header_len = inner_tcp_hdrlen(skb); + } else { + packet->header_len = skb_transport_offset(skb) + + tcp_hdrlen(skb); + packet->tcp_header_len = tcp_hdrlen(skb); + } packet->tcp_payload_len = skb->len - packet->header_len; packet->mss = skb_shinfo(skb)->gso_size; + DBGPR(" packet->header_len=%u\n", packet->header_len); DBGPR(" packet->tcp_header_len=%u, packet->tcp_payload_len=%u\n", packet->tcp_header_len, packet->tcp_payload_len); @@ -1501,6 +1699,49 @@ static int xgbe_prep_tso(struct sk_buff *skb, struct xgbe_packet_data *packet) return 0; } +static bool xgbe_is_vxlan(struct xgbe_prv_data *pdata, struct sk_buff *skb) +{ + struct xgbe_vxlan_data *vdata; + + if (pdata->vxlan_force_disable) + return false; + + if (!skb->encapsulation) + return false; + + if (skb->ip_summed != CHECKSUM_PARTIAL) + return false; + + switch (skb->protocol) { + case htons(ETH_P_IP): + if (ip_hdr(skb)->protocol != IPPROTO_UDP) + return false; + break; + + case htons(ETH_P_IPV6): + if (ipv6_hdr(skb)->nexthdr != IPPROTO_UDP) + return false; + break; + + default: + return false; + } + + /* See if we have the UDP port in our list */ + list_for_each_entry(vdata, &pdata->vxlan_ports, list) { + if ((skb->protocol == htons(ETH_P_IP)) && + (vdata->sa_family == AF_INET) && + (vdata->port == udp_hdr(skb)->dest)) + return true; + else if ((skb->protocol == htons(ETH_P_IPV6)) && + (vdata->sa_family == AF_INET6) && + (vdata->port == udp_hdr(skb)->dest)) + return true; + } + + return false; +} + static int xgbe_is_tso(struct sk_buff *skb) { if (skb->ip_summed != CHECKSUM_PARTIAL) @@ -1549,6 +1790,10 @@ static void xgbe_packet_info(struct xgbe_prv_data *pdata, XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, CSUM_ENABLE, 1); + if (xgbe_is_vxlan(pdata, skb)) + XGMAC_SET_BITS(packet->attributes, TX_PACKET_ATTRIBUTES, + VXLAN, 1); + if (skb_vlan_tag_present(skb)) { /* VLAN requires an extra descriptor if tag is different */ if (skb_vlan_tag_get(skb) != ring->tx.cur_vlan_ctag) @@ -1589,16 +1834,42 @@ static int xgbe_open(struct net_device *netdev) DBGPR("-->xgbe_open\n"); + /* Create the various names based on netdev name */ + snprintf(pdata->an_name, sizeof(pdata->an_name) - 1, "%s-pcs", + netdev_name(netdev)); + + snprintf(pdata->ecc_name, sizeof(pdata->ecc_name) - 1, "%s-ecc", + netdev_name(netdev)); + + snprintf(pdata->i2c_name, sizeof(pdata->i2c_name) - 1, "%s-i2c", + netdev_name(netdev)); + + /* Create workqueues */ + pdata->dev_workqueue = + create_singlethread_workqueue(netdev_name(netdev)); + if (!pdata->dev_workqueue) { + netdev_err(netdev, "device workqueue creation failed\n"); + return -ENOMEM; + } + + pdata->an_workqueue = + create_singlethread_workqueue(pdata->an_name); + if (!pdata->an_workqueue) { + netdev_err(netdev, "phy workqueue creation failed\n"); + ret = -ENOMEM; + goto err_dev_wq; + } + /* Reset the phy settings */ ret = xgbe_phy_reset(pdata); if (ret) - return ret; + goto err_an_wq; /* Enable the clocks */ ret = clk_prepare_enable(pdata->sysclk); if (ret) { netdev_alert(netdev, "dma clk_prepare_enable failed\n"); - return ret; + goto err_an_wq; } ret = clk_prepare_enable(pdata->ptpclk); @@ -1651,6 +1922,12 @@ err_ptpclk: err_sysclk: clk_disable_unprepare(pdata->sysclk); +err_an_wq: + destroy_workqueue(pdata->an_workqueue); + +err_dev_wq: + destroy_workqueue(pdata->dev_workqueue); + return ret; } @@ -1674,6 +1951,12 @@ static int xgbe_close(struct net_device *netdev) clk_disable_unprepare(pdata->ptpclk); clk_disable_unprepare(pdata->sysclk); + flush_workqueue(pdata->an_workqueue); + destroy_workqueue(pdata->an_workqueue); + + flush_workqueue(pdata->dev_workqueue); + destroy_workqueue(pdata->dev_workqueue); + set_bit(XGBE_DOWN, &pdata->dev_state); DBGPR("<--xgbe_close\n"); @@ -1918,18 +2201,18 @@ static void xgbe_poll_controller(struct net_device *netdev) } #endif /* End CONFIG_NET_POLL_CONTROLLER */ -static int xgbe_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, - __be16 proto, - struct tc_to_netdev *tc_to_netdev) +static int xgbe_setup_tc(struct net_device *netdev, enum tc_setup_type type, + void *type_data) { struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct tc_mqprio_qopt *mqprio = type_data; u8 tc; - if (tc_to_netdev->type != TC_SETUP_MQPRIO) - return -EINVAL; + if (type != TC_SETUP_MQPRIO) + return -EOPNOTSUPP; - tc_to_netdev->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - tc = tc_to_netdev->mqprio->num_tc; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + tc = mqprio->num_tc; if (tc > pdata->hw_feat.tc_cnt) return -EINVAL; @@ -1940,18 +2223,83 @@ static int xgbe_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, return 0; } +static netdev_features_t xgbe_fix_features(struct net_device *netdev, + netdev_features_t features) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + netdev_features_t vxlan_base, vxlan_mask; + + vxlan_base = NETIF_F_GSO_UDP_TUNNEL | NETIF_F_RX_UDP_TUNNEL_PORT; + vxlan_mask = vxlan_base | NETIF_F_GSO_UDP_TUNNEL_CSUM; + + pdata->vxlan_features = features & vxlan_mask; + + /* Only fix VXLAN-related features */ + if (!pdata->vxlan_features) + return features; + + /* If VXLAN isn't supported then clear any features: + * This is needed because NETIF_F_RX_UDP_TUNNEL_PORT gets + * automatically set if ndo_udp_tunnel_add is set. + */ + if (!pdata->hw_feat.vxn) + return features & ~vxlan_mask; + + /* VXLAN CSUM requires VXLAN base */ + if ((features & NETIF_F_GSO_UDP_TUNNEL_CSUM) && + !(features & NETIF_F_GSO_UDP_TUNNEL)) { + netdev_notice(netdev, + "forcing tx udp tunnel support\n"); + features |= NETIF_F_GSO_UDP_TUNNEL; + } + + /* Can't do one without doing the other */ + if ((features & vxlan_base) != vxlan_base) { + netdev_notice(netdev, + "forcing both tx and rx udp tunnel support\n"); + features |= vxlan_base; + } + + if (features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) { + if (!(features & NETIF_F_GSO_UDP_TUNNEL_CSUM)) { + netdev_notice(netdev, + "forcing tx udp tunnel checksumming on\n"); + features |= NETIF_F_GSO_UDP_TUNNEL_CSUM; + } + } else { + if (features & NETIF_F_GSO_UDP_TUNNEL_CSUM) { + netdev_notice(netdev, + "forcing tx udp tunnel checksumming off\n"); + features &= ~NETIF_F_GSO_UDP_TUNNEL_CSUM; + } + } + + pdata->vxlan_features = features & vxlan_mask; + + /* Adjust UDP Tunnel based on current state */ + if (pdata->vxlan_force_disable) { + netdev_notice(netdev, + "VXLAN acceleration disabled, turning off udp tunnel features\n"); + features &= ~vxlan_mask; + } + + return features; +} + static int xgbe_set_features(struct net_device *netdev, netdev_features_t features) { struct xgbe_prv_data *pdata = netdev_priv(netdev); struct xgbe_hw_if *hw_if = &pdata->hw_if; netdev_features_t rxhash, rxcsum, rxvlan, rxvlan_filter; + netdev_features_t udp_tunnel; int ret = 0; rxhash = pdata->netdev_features & NETIF_F_RXHASH; rxcsum = pdata->netdev_features & NETIF_F_RXCSUM; rxvlan = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_RX; rxvlan_filter = pdata->netdev_features & NETIF_F_HW_VLAN_CTAG_FILTER; + udp_tunnel = pdata->netdev_features & NETIF_F_GSO_UDP_TUNNEL; if ((features & NETIF_F_RXHASH) && !rxhash) ret = hw_if->enable_rss(pdata); @@ -1975,6 +2323,11 @@ static int xgbe_set_features(struct net_device *netdev, else if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER) && rxvlan_filter) hw_if->disable_rx_vlan_filtering(pdata); + if ((features & NETIF_F_GSO_UDP_TUNNEL) && !udp_tunnel) + xgbe_enable_vxlan_accel(pdata); + else if (!(features & NETIF_F_GSO_UDP_TUNNEL) && udp_tunnel) + xgbe_disable_vxlan_accel(pdata); + pdata->netdev_features = features; DBGPR("<--xgbe_set_features\n"); @@ -1982,6 +2335,111 @@ static int xgbe_set_features(struct net_device *netdev, return 0; } +static void xgbe_udp_tunnel_add(struct net_device *netdev, + struct udp_tunnel_info *ti) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_vxlan_data *vdata; + + if (!pdata->hw_feat.vxn) + return; + + if (ti->type != UDP_TUNNEL_TYPE_VXLAN) + return; + + pdata->vxlan_port_count++; + + netif_dbg(pdata, drv, netdev, + "adding VXLAN tunnel, family=%hx/port=%hx\n", + ti->sa_family, be16_to_cpu(ti->port)); + + if (pdata->vxlan_force_disable) + return; + + vdata = kzalloc(sizeof(*vdata), GFP_ATOMIC); + if (!vdata) { + /* Can no longer properly track VXLAN ports */ + pdata->vxlan_force_disable = 1; + netif_dbg(pdata, drv, netdev, + "internal error, disabling VXLAN accelerations\n"); + + xgbe_disable_vxlan_accel(pdata); + + return; + } + vdata->sa_family = ti->sa_family; + vdata->port = ti->port; + + list_add_tail(&vdata->list, &pdata->vxlan_ports); + + /* First port added? */ + if (pdata->vxlan_port_count == 1) { + xgbe_enable_vxlan_accel(pdata); + + return; + } +} + +static void xgbe_udp_tunnel_del(struct net_device *netdev, + struct udp_tunnel_info *ti) +{ + struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct xgbe_vxlan_data *vdata; + + if (!pdata->hw_feat.vxn) + return; + + if (ti->type != UDP_TUNNEL_TYPE_VXLAN) + return; + + netif_dbg(pdata, drv, netdev, + "deleting VXLAN tunnel, family=%hx/port=%hx\n", + ti->sa_family, be16_to_cpu(ti->port)); + + /* Don't need safe version since loop terminates with deletion */ + list_for_each_entry(vdata, &pdata->vxlan_ports, list) { + if (vdata->sa_family != ti->sa_family) + continue; + + if (vdata->port != ti->port) + continue; + + list_del(&vdata->list); + kfree(vdata); + + break; + } + + pdata->vxlan_port_count--; + if (!pdata->vxlan_port_count) { + xgbe_reset_vxlan_accel(pdata); + + return; + } + + if (pdata->vxlan_force_disable) + return; + + /* See if VXLAN tunnel id needs to be changed */ + vdata = list_first_entry(&pdata->vxlan_ports, + struct xgbe_vxlan_data, list); + if (pdata->vxlan_port == be16_to_cpu(vdata->port)) + return; + + pdata->vxlan_port = be16_to_cpu(vdata->port); + pdata->hw_if.set_vxlan_id(pdata); +} + +static netdev_features_t xgbe_features_check(struct sk_buff *skb, + struct net_device *netdev, + netdev_features_t features) +{ + features = vlan_features_check(skb, features); + features = vxlan_features_check(skb, features); + + return features; +} + static const struct net_device_ops xgbe_netdev_ops = { .ndo_open = xgbe_open, .ndo_stop = xgbe_close, @@ -1999,7 +2457,11 @@ static const struct net_device_ops xgbe_netdev_ops = { .ndo_poll_controller = xgbe_poll_controller, #endif .ndo_setup_tc = xgbe_setup_tc, + .ndo_fix_features = xgbe_fix_features, .ndo_set_features = xgbe_set_features, + .ndo_udp_tunnel_add = xgbe_udp_tunnel_add, + .ndo_udp_tunnel_del = xgbe_udp_tunnel_del, + .ndo_features_check = xgbe_features_check, }; const struct net_device_ops *xgbe_get_netdev_ops(void) @@ -2312,6 +2774,15 @@ skip_data: skb->ip_summed = CHECKSUM_UNNECESSARY; if (XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, TNP)) { + skb->encapsulation = 1; + + if (XGMAC_GET_BITS(packet->attributes, + RX_PACKET_ATTRIBUTES, TNPCSUM_DONE)) + skb->csum_level = 1; + } + + if (XGMAC_GET_BITS(packet->attributes, RX_PACKET_ATTRIBUTES, VLAN_CTAG)) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), packet->vlan_ctag); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index 67a2e52ad25d..ff397bb25042 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -146,6 +146,7 @@ static const struct xgbe_stats xgbe_gstring_stats[] = { XGMAC_MMC_STAT("tx_broadcast_packets", txbroadcastframes_gb), XGMAC_MMC_STAT("tx_multicast_packets", txmulticastframes_gb), XGMAC_MMC_STAT("tx_vlan_packets", txvlanframes_g), + XGMAC_EXT_STAT("tx_vxlan_packets", tx_vxlan_packets), XGMAC_EXT_STAT("tx_tso_packets", tx_tso_packets), XGMAC_MMC_STAT("tx_64_byte_packets", tx64octets_gb), XGMAC_MMC_STAT("tx_65_to_127_byte_packets", tx65to127octets_gb), @@ -162,6 +163,7 @@ static const struct xgbe_stats xgbe_gstring_stats[] = { XGMAC_MMC_STAT("rx_broadcast_packets", rxbroadcastframes_g), XGMAC_MMC_STAT("rx_multicast_packets", rxmulticastframes_g), XGMAC_MMC_STAT("rx_vlan_packets", rxvlanframes_gb), + XGMAC_EXT_STAT("rx_vxlan_packets", rx_vxlan_packets), XGMAC_MMC_STAT("rx_64_byte_packets", rx64octets_gb), XGMAC_MMC_STAT("rx_65_to_127_byte_packets", rx65to127octets_gb), XGMAC_MMC_STAT("rx_128_to_255_byte_packets", rx128to255octets_gb), @@ -177,6 +179,8 @@ static const struct xgbe_stats xgbe_gstring_stats[] = { XGMAC_MMC_STAT("rx_out_of_range_errors", rxoutofrangetype), XGMAC_MMC_STAT("rx_fifo_overflow_errors", rxfifooverflow), XGMAC_MMC_STAT("rx_watchdog_errors", rxwatchdogerror), + XGMAC_EXT_STAT("rx_csum_errors", rx_csum_errors), + XGMAC_EXT_STAT("rx_vxlan_csum_errors", rx_vxlan_csum_errors), XGMAC_MMC_STAT("rx_pause_frames", rxpauseframes), XGMAC_EXT_STAT("rx_split_header_packets", rx_split_header_packets), XGMAC_EXT_STAT("rx_buffer_unavailable", rx_buffer_unavailable), @@ -186,6 +190,7 @@ static const struct xgbe_stats xgbe_gstring_stats[] = { static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { + struct xgbe_prv_data *pdata = netdev_priv(netdev); int i; switch (stringset) { @@ -195,6 +200,18 @@ static void xgbe_get_strings(struct net_device *netdev, u32 stringset, u8 *data) ETH_GSTRING_LEN); data += ETH_GSTRING_LEN; } + for (i = 0; i < pdata->tx_ring_count; i++) { + sprintf(data, "txq_%u_packets", i); + data += ETH_GSTRING_LEN; + sprintf(data, "txq_%u_bytes", i); + data += ETH_GSTRING_LEN; + } + for (i = 0; i < pdata->rx_ring_count; i++) { + sprintf(data, "rxq_%u_packets", i); + data += ETH_GSTRING_LEN; + sprintf(data, "rxq_%u_bytes", i); + data += ETH_GSTRING_LEN; + } break; } } @@ -211,15 +228,26 @@ static void xgbe_get_ethtool_stats(struct net_device *netdev, stat = (u8 *)pdata + xgbe_gstring_stats[i].stat_offset; *data++ = *(u64 *)stat; } + for (i = 0; i < pdata->tx_ring_count; i++) { + *data++ = pdata->ext_stats.txq_packets[i]; + *data++ = pdata->ext_stats.txq_bytes[i]; + } + for (i = 0; i < pdata->rx_ring_count; i++) { + *data++ = pdata->ext_stats.rxq_packets[i]; + *data++ = pdata->ext_stats.rxq_bytes[i]; + } } static int xgbe_get_sset_count(struct net_device *netdev, int stringset) { + struct xgbe_prv_data *pdata = netdev_priv(netdev); int ret; switch (stringset) { case ETH_SS_STATS: - ret = XGBE_STATS_COUNT; + ret = XGBE_STATS_COUNT + + (pdata->tx_ring_count * 2) + + (pdata->rx_ring_count * 2); break; default: @@ -243,6 +271,7 @@ static int xgbe_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct ethtool_link_ksettings *lks = &pdata->phy.lks; int ret = 0; if (pause->autoneg && (pdata->phy.autoneg != AUTONEG_ENABLE)) { @@ -255,16 +284,21 @@ static int xgbe_set_pauseparam(struct net_device *netdev, pdata->phy.tx_pause = pause->tx_pause; pdata->phy.rx_pause = pause->rx_pause; - pdata->phy.advertising &= ~ADVERTISED_Pause; - pdata->phy.advertising &= ~ADVERTISED_Asym_Pause; + XGBE_CLR_ADV(lks, Pause); + XGBE_CLR_ADV(lks, Asym_Pause); if (pause->rx_pause) { - pdata->phy.advertising |= ADVERTISED_Pause; - pdata->phy.advertising |= ADVERTISED_Asym_Pause; + XGBE_SET_ADV(lks, Pause); + XGBE_SET_ADV(lks, Asym_Pause); } - if (pause->tx_pause) - pdata->phy.advertising ^= ADVERTISED_Asym_Pause; + if (pause->tx_pause) { + /* Equivalent to XOR of Asym_Pause */ + if (XGBE_ADV(lks, Asym_Pause)) + XGBE_CLR_ADV(lks, Asym_Pause); + else + XGBE_SET_ADV(lks, Asym_Pause); + } if (netif_running(netdev)) ret = pdata->phy_if.phy_config_aneg(pdata); @@ -276,22 +310,20 @@ static int xgbe_get_link_ksettings(struct net_device *netdev, struct ethtool_link_ksettings *cmd) { struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct ethtool_link_ksettings *lks = &pdata->phy.lks; cmd->base.phy_address = pdata->phy.address; - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, - pdata->phy.supported); - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, - pdata->phy.advertising); - ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising, - pdata->phy.lp_advertising); - cmd->base.autoneg = pdata->phy.autoneg; cmd->base.speed = pdata->phy.speed; cmd->base.duplex = pdata->phy.duplex; cmd->base.port = PORT_NONE; + XGBE_LM_COPY(cmd, supported, lks, supported); + XGBE_LM_COPY(cmd, advertising, lks, advertising); + XGBE_LM_COPY(cmd, lp_advertising, lks, lp_advertising); + return 0; } @@ -299,7 +331,8 @@ static int xgbe_set_link_ksettings(struct net_device *netdev, const struct ethtool_link_ksettings *cmd) { struct xgbe_prv_data *pdata = netdev_priv(netdev); - u32 advertising; + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising); u32 speed; int ret; @@ -331,15 +364,17 @@ static int xgbe_set_link_ksettings(struct net_device *netdev, } } - ethtool_convert_link_mode_to_legacy_u32(&advertising, - cmd->link_modes.advertising); - netif_dbg(pdata, link, netdev, - "requested advertisement %#x, phy supported %#x\n", - advertising, pdata->phy.supported); + "requested advertisement 0x%*pb, phy supported 0x%*pb\n", + __ETHTOOL_LINK_MODE_MASK_NBITS, cmd->link_modes.advertising, + __ETHTOOL_LINK_MODE_MASK_NBITS, lks->link_modes.supported); + + bitmap_and(advertising, + cmd->link_modes.advertising, lks->link_modes.supported, + __ETHTOOL_LINK_MODE_MASK_NBITS); - advertising &= pdata->phy.supported; - if ((cmd->base.autoneg == AUTONEG_ENABLE) && !advertising) { + if ((cmd->base.autoneg == AUTONEG_ENABLE) && + bitmap_empty(advertising, __ETHTOOL_LINK_MODE_MASK_NBITS)) { netdev_err(netdev, "unsupported requested advertisement\n"); return -EINVAL; @@ -349,12 +384,13 @@ static int xgbe_set_link_ksettings(struct net_device *netdev, pdata->phy.autoneg = cmd->base.autoneg; pdata->phy.speed = speed; pdata->phy.duplex = cmd->base.duplex; - pdata->phy.advertising = advertising; + bitmap_copy(lks->link_modes.advertising, advertising, + __ETHTOOL_LINK_MODE_MASK_NBITS); if (cmd->base.autoneg == AUTONEG_ENABLE) - pdata->phy.advertising |= ADVERTISED_Autoneg; + XGBE_SET_ADV(lks, Autoneg); else - pdata->phy.advertising &= ~ADVERTISED_Autoneg; + XGBE_CLR_ADV(lks, Autoneg); if (netif_running(netdev)) ret = pdata->phy_if.phy_config_aneg(pdata); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index 500147d9e3c8..d91fa595be98 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -120,6 +120,7 @@ #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/io.h> +#include <linux/notifier.h> #include "xgbe.h" #include "xgbe-common.h" @@ -192,6 +193,7 @@ struct xgbe_prv_data *xgbe_alloc_pdata(struct device *dev) mutex_init(&pdata->i2c_mutex); init_completion(&pdata->i2c_complete); init_completion(&pdata->mdio_complete); + INIT_LIST_HEAD(&pdata->vxlan_ports); pdata->msg_enable = netif_msg_init(debug, default_msg_level); @@ -373,6 +375,28 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata) if (pdata->hw_feat.rss) netdev->hw_features |= NETIF_F_RXHASH; + if (pdata->hw_feat.vxn) { + netdev->hw_enc_features = NETIF_F_SG | + NETIF_F_IP_CSUM | + NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | + NETIF_F_TSO | + NETIF_F_TSO6 | + NETIF_F_GRO | + NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_RX_UDP_TUNNEL_PORT; + + netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_RX_UDP_TUNNEL_PORT; + + pdata->vxlan_offloads_set = 1; + pdata->vxlan_features = NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM | + NETIF_F_RX_UDP_TUNNEL_PORT; + } + netdev->vlan_features |= NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | @@ -399,35 +423,6 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata) return ret; } - /* Create the PHY/ANEG name based on netdev name */ - snprintf(pdata->an_name, sizeof(pdata->an_name) - 1, "%s-pcs", - netdev_name(netdev)); - - /* Create the ECC name based on netdev name */ - snprintf(pdata->ecc_name, sizeof(pdata->ecc_name) - 1, "%s-ecc", - netdev_name(netdev)); - - /* Create the I2C name based on netdev name */ - snprintf(pdata->i2c_name, sizeof(pdata->i2c_name) - 1, "%s-i2c", - netdev_name(netdev)); - - /* Create workqueues */ - pdata->dev_workqueue = - create_singlethread_workqueue(netdev_name(netdev)); - if (!pdata->dev_workqueue) { - netdev_err(netdev, "device workqueue creation failed\n"); - ret = -ENOMEM; - goto err_netdev; - } - - pdata->an_workqueue = - create_singlethread_workqueue(pdata->an_name); - if (!pdata->an_workqueue) { - netdev_err(netdev, "phy workqueue creation failed\n"); - ret = -ENOMEM; - goto err_wq; - } - if (IS_REACHABLE(CONFIG_PTP_1588_CLOCK)) xgbe_ptp_register(pdata); @@ -439,14 +434,6 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata) pdata->rx_ring_count); return 0; - -err_wq: - destroy_workqueue(pdata->dev_workqueue); - -err_netdev: - unregister_netdev(netdev); - - return ret; } void xgbe_deconfig_netdev(struct xgbe_prv_data *pdata) @@ -458,21 +445,45 @@ void xgbe_deconfig_netdev(struct xgbe_prv_data *pdata) if (IS_REACHABLE(CONFIG_PTP_1588_CLOCK)) xgbe_ptp_unregister(pdata); + unregister_netdev(netdev); + pdata->phy_if.phy_exit(pdata); +} - flush_workqueue(pdata->an_workqueue); - destroy_workqueue(pdata->an_workqueue); +static int xgbe_netdev_event(struct notifier_block *nb, unsigned long event, + void *data) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(data); + struct xgbe_prv_data *pdata = netdev_priv(netdev); - flush_workqueue(pdata->dev_workqueue); - destroy_workqueue(pdata->dev_workqueue); + if (netdev->netdev_ops != xgbe_get_netdev_ops()) + goto out; - unregister_netdev(netdev); + switch (event) { + case NETDEV_CHANGENAME: + xgbe_debugfs_rename(pdata); + break; + + default: + break; + } + +out: + return NOTIFY_DONE; } +static struct notifier_block xgbe_netdev_notifier = { + .notifier_call = xgbe_netdev_event, +}; + static int __init xgbe_mod_init(void) { int ret; + ret = register_netdevice_notifier(&xgbe_netdev_notifier); + if (ret) + return ret; + ret = xgbe_platform_init(); if (ret) return ret; @@ -489,6 +500,8 @@ static void __exit xgbe_mod_exit(void) xgbe_pci_exit(); xgbe_platform_exit(); + + unregister_netdevice_notifier(&xgbe_netdev_notifier); } module_init(xgbe_mod_init); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 80684914dd8a..072b9f664597 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -615,12 +615,14 @@ static enum xgbe_an xgbe_an73_page_received(struct xgbe_prv_data *pdata) static enum xgbe_an xgbe_an73_incompat_link(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + /* Be sure we aren't looping trying to negotiate */ if (xgbe_in_kr_mode(pdata)) { pdata->kr_state = XGBE_RX_ERROR; - if (!(pdata->phy.advertising & ADVERTISED_1000baseKX_Full) && - !(pdata->phy.advertising & ADVERTISED_2500baseX_Full)) + if (!XGBE_ADV(lks, 1000baseKX_Full) && + !XGBE_ADV(lks, 2500baseX_Full)) return XGBE_AN_NO_LINK; if (pdata->kx_state != XGBE_RX_BPA) @@ -628,7 +630,7 @@ static enum xgbe_an xgbe_an73_incompat_link(struct xgbe_prv_data *pdata) } else { pdata->kx_state = XGBE_RX_ERROR; - if (!(pdata->phy.advertising & ADVERTISED_10000baseKR_Full)) + if (!XGBE_ADV(lks, 10000baseKR_Full)) return XGBE_AN_NO_LINK; if (pdata->kr_state != XGBE_RX_BPA) @@ -944,18 +946,19 @@ static void xgbe_an_state_machine(struct work_struct *work) static void xgbe_an37_init(struct xgbe_prv_data *pdata) { - unsigned int advertising, reg; + struct ethtool_link_ksettings lks; + unsigned int reg; - advertising = pdata->phy_if.phy_impl.an_advertising(pdata); + pdata->phy_if.phy_impl.an_advertising(pdata, &lks); /* Set up Advertisement register */ reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE); - if (advertising & ADVERTISED_Pause) + if (XGBE_ADV(&lks, Pause)) reg |= 0x100; else reg &= ~0x100; - if (advertising & ADVERTISED_Asym_Pause) + if (XGBE_ADV(&lks, Asym_Pause)) reg |= 0x80; else reg &= ~0x80; @@ -982,6 +985,8 @@ static void xgbe_an37_init(struct xgbe_prv_data *pdata) break; } + reg |= XGBE_AN_CL37_MII_CTRL_8BIT; + XMDIO_WRITE(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_CTRL, reg); netif_dbg(pdata, link, pdata->netdev, "CL37 AN (%s) initialized\n", @@ -990,13 +995,14 @@ static void xgbe_an37_init(struct xgbe_prv_data *pdata) static void xgbe_an73_init(struct xgbe_prv_data *pdata) { - unsigned int advertising, reg; + struct ethtool_link_ksettings lks; + unsigned int reg; - advertising = pdata->phy_if.phy_impl.an_advertising(pdata); + pdata->phy_if.phy_impl.an_advertising(pdata, &lks); /* Set up Advertisement register 3 first */ reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); - if (advertising & ADVERTISED_10000baseR_FEC) + if (XGBE_ADV(&lks, 10000baseR_FEC)) reg |= 0xc000; else reg &= ~0xc000; @@ -1005,13 +1011,13 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata) /* Set up Advertisement register 2 next */ reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); - if (advertising & ADVERTISED_10000baseKR_Full) + if (XGBE_ADV(&lks, 10000baseKR_Full)) reg |= 0x80; else reg &= ~0x80; - if ((advertising & ADVERTISED_1000baseKX_Full) || - (advertising & ADVERTISED_2500baseX_Full)) + if (XGBE_ADV(&lks, 1000baseKX_Full) || + XGBE_ADV(&lks, 2500baseX_Full)) reg |= 0x20; else reg &= ~0x20; @@ -1020,12 +1026,12 @@ static void xgbe_an73_init(struct xgbe_prv_data *pdata) /* Set up Advertisement register 1 last */ reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); - if (advertising & ADVERTISED_Pause) + if (XGBE_ADV(&lks, Pause)) reg |= 0x400; else reg &= ~0x400; - if (advertising & ADVERTISED_Asym_Pause) + if (XGBE_ADV(&lks, Asym_Pause)) reg |= 0x800; else reg &= ~0x800; @@ -1281,9 +1287,10 @@ static enum xgbe_mode xgbe_phy_status_aneg(struct xgbe_prv_data *pdata) static void xgbe_phy_status_result(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; enum xgbe_mode mode; - pdata->phy.lp_advertising = 0; + XGBE_ZERO_LP_ADV(lks); if ((pdata->phy.autoneg != AUTONEG_ENABLE) || pdata->parallel_detect) mode = xgbe_cur_mode(pdata); @@ -1513,17 +1520,21 @@ static void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata) static int xgbe_phy_best_advertised_speed(struct xgbe_prv_data *pdata) { - if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full) + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + + if (XGBE_ADV(lks, 10000baseKR_Full)) return SPEED_10000; - else if (pdata->phy.advertising & ADVERTISED_10000baseT_Full) + else if (XGBE_ADV(lks, 10000baseT_Full)) return SPEED_10000; - else if (pdata->phy.advertising & ADVERTISED_2500baseX_Full) + else if (XGBE_ADV(lks, 2500baseX_Full)) return SPEED_2500; - else if (pdata->phy.advertising & ADVERTISED_1000baseKX_Full) + else if (XGBE_ADV(lks, 2500baseT_Full)) + return SPEED_2500; + else if (XGBE_ADV(lks, 1000baseKX_Full)) return SPEED_1000; - else if (pdata->phy.advertising & ADVERTISED_1000baseT_Full) + else if (XGBE_ADV(lks, 1000baseT_Full)) return SPEED_1000; - else if (pdata->phy.advertising & ADVERTISED_100baseT_Full) + else if (XGBE_ADV(lks, 100baseT_Full)) return SPEED_100; return SPEED_UNKNOWN; @@ -1531,13 +1542,12 @@ static int xgbe_phy_best_advertised_speed(struct xgbe_prv_data *pdata) static void xgbe_phy_exit(struct xgbe_prv_data *pdata) { - xgbe_phy_stop(pdata); - pdata->phy_if.phy_impl.exit(pdata); } static int xgbe_phy_init(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; int ret; mutex_init(&pdata->an_mutex); @@ -1555,11 +1565,13 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) ret = pdata->phy_if.phy_impl.init(pdata); if (ret) return ret; - pdata->phy.advertising = pdata->phy.supported; + + /* Copy supported link modes to advertising link modes */ + XGBE_LM_COPY(lks, advertising, lks, supported); pdata->phy.address = 0; - if (pdata->phy.advertising & ADVERTISED_Autoneg) { + if (XGBE_ADV(lks, Autoneg)) { pdata->phy.autoneg = AUTONEG_ENABLE; pdata->phy.speed = SPEED_UNKNOWN; pdata->phy.duplex = DUPLEX_UNKNOWN; @@ -1576,16 +1588,21 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) pdata->phy.rx_pause = pdata->rx_pause; /* Fix up Flow Control advertising */ - pdata->phy.advertising &= ~ADVERTISED_Pause; - pdata->phy.advertising &= ~ADVERTISED_Asym_Pause; + XGBE_CLR_ADV(lks, Pause); + XGBE_CLR_ADV(lks, Asym_Pause); if (pdata->rx_pause) { - pdata->phy.advertising |= ADVERTISED_Pause; - pdata->phy.advertising |= ADVERTISED_Asym_Pause; + XGBE_SET_ADV(lks, Pause); + XGBE_SET_ADV(lks, Asym_Pause); } - if (pdata->tx_pause) - pdata->phy.advertising ^= ADVERTISED_Asym_Pause; + if (pdata->tx_pause) { + /* Equivalent to XOR of Asym_Pause */ + if (XGBE_ADV(lks, Asym_Pause)) + XGBE_CLR_ADV(lks, Asym_Pause); + else + XGBE_SET_ADV(lks, Asym_Pause); + } if (netif_msg_drv(pdata)) xgbe_dump_phy_registers(pdata); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c index 1e56ad7bd9a5..3e5833cf1fab 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c @@ -292,6 +292,10 @@ static int xgbe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) pdata->xpcs_window_size = 1 << (pdata->xpcs_window_size + 7); pdata->xpcs_window_mask = pdata->xpcs_window_size - 1; if (netif_msg_probe(pdata)) { + dev_dbg(dev, "xpcs window def = %#010x\n", + pdata->xpcs_window_def_reg); + dev_dbg(dev, "xpcs window sel = %#010x\n", + pdata->xpcs_window_sel_reg); dev_dbg(dev, "xpcs window = %#010x\n", pdata->xpcs_window); dev_dbg(dev, "xpcs window size = %#010x\n", diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c index c75edcac5e0a..d16eae415f72 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c @@ -231,20 +231,21 @@ static void xgbe_phy_kr_training_post(struct xgbe_prv_data *pdata) static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; enum xgbe_mode mode; unsigned int ad_reg, lp_reg; - pdata->phy.lp_advertising |= ADVERTISED_Autoneg; - pdata->phy.lp_advertising |= ADVERTISED_Backplane; + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, Backplane); /* Compare Advertisement and Link Partner register 1 */ ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); if (lp_reg & 0x400) - pdata->phy.lp_advertising |= ADVERTISED_Pause; + XGBE_SET_LP_ADV(lks, Pause); if (lp_reg & 0x800) - pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; + XGBE_SET_LP_ADV(lks, Asym_Pause); if (pdata->phy.pause_autoneg) { /* Set flow control based on auto-negotiation result */ @@ -266,12 +267,12 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); if (lp_reg & 0x80) - pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_LP_ADV(lks, 10000baseKR_Full); if (lp_reg & 0x20) { if (phy_data->speed_set == XGBE_SPEEDSET_2500_10000) - pdata->phy.lp_advertising |= ADVERTISED_2500baseX_Full; + XGBE_SET_LP_ADV(lks, 2500baseX_Full); else - pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full; + XGBE_SET_LP_ADV(lks, 1000baseKX_Full); } ad_reg &= lp_reg; @@ -290,14 +291,17 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); if (lp_reg & 0xc000) - pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC; + XGBE_SET_LP_ADV(lks, 10000baseR_FEC); return mode; } -static unsigned int xgbe_phy_an_advertising(struct xgbe_prv_data *pdata) +static void xgbe_phy_an_advertising(struct xgbe_prv_data *pdata, + struct ethtool_link_ksettings *dlks) { - return pdata->phy.advertising; + struct ethtool_link_ksettings *slks = &pdata->phy.lks; + + XGBE_LM_COPY(dlks, advertising, slks, advertising); } static int xgbe_phy_an_config(struct xgbe_prv_data *pdata) @@ -565,11 +569,10 @@ static void xgbe_phy_set_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) } static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata, - enum xgbe_mode mode, u32 advert) + enum xgbe_mode mode, bool advert) { if (pdata->phy.autoneg == AUTONEG_ENABLE) { - if (pdata->phy.advertising & advert) - return true; + return advert; } else { enum xgbe_mode cur_mode; @@ -583,16 +586,18 @@ static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata, static bool xgbe_phy_use_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + switch (mode) { case XGBE_MODE_KX_1000: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_1000baseKX_Full); + XGBE_ADV(lks, 1000baseKX_Full)); case XGBE_MODE_KX_2500: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_2500baseX_Full); + XGBE_ADV(lks, 2500baseX_Full)); case XGBE_MODE_KR: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_10000baseKR_Full); + XGBE_ADV(lks, 10000baseKR_Full)); default: return false; } @@ -672,6 +677,7 @@ static void xgbe_phy_exit(struct xgbe_prv_data *pdata) static int xgbe_phy_init(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data; int ret; @@ -790,21 +796,23 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) } /* Initialize supported features */ - pdata->phy.supported = SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_Backplane; - pdata->phy.supported |= SUPPORTED_10000baseKR_Full; + XGBE_ZERO_SUP(lks); + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, Backplane); + XGBE_SET_SUP(lks, 10000baseKR_Full); switch (phy_data->speed_set) { case XGBE_SPEEDSET_1000_10000: - pdata->phy.supported |= SUPPORTED_1000baseKX_Full; + XGBE_SET_SUP(lks, 1000baseKX_Full); break; case XGBE_SPEEDSET_2500_10000: - pdata->phy.supported |= SUPPORTED_2500baseX_Full; + XGBE_SET_SUP(lks, 2500baseX_Full); break; } if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) - pdata->phy.supported |= SUPPORTED_10000baseR_FEC; + XGBE_SET_SUP(lks, 10000baseR_FEC); pdata->phy_data = phy_data; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c index 04b5c149caca..3304a291aa96 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c @@ -709,18 +709,13 @@ static int xgbe_phy_mii_read(struct mii_bus *mii, int addr, int reg) static void xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; if (!phy_data->sfp_mod_absent && !phy_data->sfp_changed) return; - pdata->phy.supported &= ~SUPPORTED_Autoneg; - pdata->phy.supported &= ~(SUPPORTED_Pause | SUPPORTED_Asym_Pause); - pdata->phy.supported &= ~SUPPORTED_TP; - pdata->phy.supported &= ~SUPPORTED_FIBRE; - pdata->phy.supported &= ~SUPPORTED_100baseT_Full; - pdata->phy.supported &= ~SUPPORTED_1000baseT_Full; - pdata->phy.supported &= ~SUPPORTED_10000baseT_Full; + XGBE_ZERO_SUP(lks); if (phy_data->sfp_mod_absent) { pdata->phy.speed = SPEED_UNKNOWN; @@ -728,18 +723,13 @@ static void xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata) pdata->phy.autoneg = AUTONEG_ENABLE; pdata->phy.pause_autoneg = AUTONEG_ENABLE; - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_TP; - pdata->phy.supported |= SUPPORTED_FIBRE; - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) - pdata->phy.supported |= SUPPORTED_100baseT_Full; - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) - pdata->phy.supported |= SUPPORTED_1000baseT_Full; - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) - pdata->phy.supported |= SUPPORTED_10000baseT_Full; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); + XGBE_SET_SUP(lks, FIBRE); - pdata->phy.advertising = pdata->phy.supported; + XGBE_LM_COPY(lks, advertising, lks, supported); return; } @@ -753,8 +743,18 @@ static void xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata) pdata->phy.duplex = DUPLEX_UNKNOWN; pdata->phy.autoneg = AUTONEG_ENABLE; pdata->phy.pause_autoneg = AUTONEG_ENABLE; - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + if (phy_data->sfp_base == XGBE_SFP_BASE_1000_T) { + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) + XGBE_SET_SUP(lks, 100baseT_Full); + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) + XGBE_SET_SUP(lks, 1000baseT_Full); + } else { + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) + XGBE_SET_SUP(lks, 1000baseX_Full); + } break; case XGBE_SFP_BASE_10000_SR: case XGBE_SFP_BASE_10000_LR: @@ -765,6 +765,27 @@ static void xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata) pdata->phy.duplex = DUPLEX_FULL; pdata->phy.autoneg = AUTONEG_DISABLE; pdata->phy.pause_autoneg = AUTONEG_DISABLE; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) { + switch (phy_data->sfp_base) { + case XGBE_SFP_BASE_10000_SR: + XGBE_SET_SUP(lks, 10000baseSR_Full); + break; + case XGBE_SFP_BASE_10000_LR: + XGBE_SET_SUP(lks, 10000baseLR_Full); + break; + case XGBE_SFP_BASE_10000_LRM: + XGBE_SET_SUP(lks, 10000baseLRM_Full); + break; + case XGBE_SFP_BASE_10000_ER: + XGBE_SET_SUP(lks, 10000baseER_Full); + break; + case XGBE_SFP_BASE_10000_CR: + XGBE_SET_SUP(lks, 10000baseCR_Full); + break; + default: + break; + } + } break; default: pdata->phy.speed = SPEED_UNKNOWN; @@ -778,38 +799,14 @@ static void xgbe_phy_sfp_phy_settings(struct xgbe_prv_data *pdata) case XGBE_SFP_BASE_1000_T: case XGBE_SFP_BASE_1000_CX: case XGBE_SFP_BASE_10000_CR: - pdata->phy.supported |= SUPPORTED_TP; + XGBE_SET_SUP(lks, TP); break; default: - pdata->phy.supported |= SUPPORTED_FIBRE; - } - - switch (phy_data->sfp_speed) { - case XGBE_SFP_SPEED_100_1000: - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) - pdata->phy.supported |= SUPPORTED_100baseT_Full; - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) - pdata->phy.supported |= SUPPORTED_1000baseT_Full; - break; - case XGBE_SFP_SPEED_1000: - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) - pdata->phy.supported |= SUPPORTED_1000baseT_Full; + XGBE_SET_SUP(lks, FIBRE); break; - case XGBE_SFP_SPEED_10000: - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) - pdata->phy.supported |= SUPPORTED_10000baseT_Full; - break; - default: - /* Choose the fastest supported speed */ - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) - pdata->phy.supported |= SUPPORTED_10000baseT_Full; - else if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) - pdata->phy.supported |= SUPPORTED_1000baseT_Full; - else if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) - pdata->phy.supported |= SUPPORTED_100baseT_Full; } - pdata->phy.advertising = pdata->phy.supported; + XGBE_LM_COPY(lks, advertising, lks, supported); } static bool xgbe_phy_sfp_bit_rate(struct xgbe_sfp_eeprom *sfp_eeprom, @@ -886,8 +883,10 @@ static void xgbe_phy_external_phy_quirks(struct xgbe_prv_data *pdata) static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; struct phy_device *phydev; + u32 advertising; int ret; /* If we already have a PHY, just return */ @@ -943,7 +942,10 @@ static int xgbe_phy_find_phy_device(struct xgbe_prv_data *pdata) phy_data->phydev = phydev; xgbe_phy_external_phy_quirks(pdata); - phydev->advertising &= pdata->phy.advertising; + + ethtool_convert_link_mode_to_legacy_u32(&advertising, + lks->link_modes.advertising); + phydev->advertising &= advertising; phy_start_aneg(phy_data->phydev); @@ -1277,6 +1279,7 @@ put: static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; u16 lcl_adv = 0, rmt_adv = 0; u8 fc; @@ -1293,11 +1296,11 @@ static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata) lcl_adv |= ADVERTISE_PAUSE_ASYM; if (phy_data->phydev->pause) { - pdata->phy.lp_advertising |= ADVERTISED_Pause; + XGBE_SET_LP_ADV(lks, Pause); rmt_adv |= LPA_PAUSE_CAP; } if (phy_data->phydev->asym_pause) { - pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; + XGBE_SET_LP_ADV(lks, Asym_Pause); rmt_adv |= LPA_PAUSE_ASYM; } @@ -1310,10 +1313,11 @@ static void xgbe_phy_phydev_flowctrl(struct xgbe_prv_data *pdata) static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; enum xgbe_mode mode; - pdata->phy.lp_advertising |= ADVERTISED_Autoneg; - pdata->phy.lp_advertising |= ADVERTISED_TP; + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, TP); /* Use external PHY to determine flow control */ if (pdata->phy.pause_autoneg) @@ -1322,21 +1326,21 @@ static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata) switch (pdata->an_status & XGBE_SGMII_AN_LINK_SPEED) { case XGBE_SGMII_AN_LINK_SPEED_100: if (pdata->an_status & XGBE_SGMII_AN_LINK_DUPLEX) { - pdata->phy.lp_advertising |= ADVERTISED_100baseT_Full; + XGBE_SET_LP_ADV(lks, 100baseT_Full); mode = XGBE_MODE_SGMII_100; } else { /* Half-duplex not supported */ - pdata->phy.lp_advertising |= ADVERTISED_100baseT_Half; + XGBE_SET_LP_ADV(lks, 100baseT_Half); mode = XGBE_MODE_UNKNOWN; } break; case XGBE_SGMII_AN_LINK_SPEED_1000: if (pdata->an_status & XGBE_SGMII_AN_LINK_DUPLEX) { - pdata->phy.lp_advertising |= ADVERTISED_1000baseT_Full; + XGBE_SET_LP_ADV(lks, 1000baseT_Full); mode = XGBE_MODE_SGMII_1000; } else { /* Half-duplex not supported */ - pdata->phy.lp_advertising |= ADVERTISED_1000baseT_Half; + XGBE_SET_LP_ADV(lks, 1000baseT_Half); mode = XGBE_MODE_UNKNOWN; } break; @@ -1349,19 +1353,20 @@ static enum xgbe_mode xgbe_phy_an37_sgmii_outcome(struct xgbe_prv_data *pdata) static enum xgbe_mode xgbe_phy_an37_outcome(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; enum xgbe_mode mode; unsigned int ad_reg, lp_reg; - pdata->phy.lp_advertising |= ADVERTISED_Autoneg; - pdata->phy.lp_advertising |= ADVERTISED_FIBRE; + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, FIBRE); /* Compare Advertisement and Link Partner register */ ad_reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_ADVERTISE); lp_reg = XMDIO_READ(pdata, MDIO_MMD_VEND2, MDIO_VEND2_AN_LP_ABILITY); if (lp_reg & 0x100) - pdata->phy.lp_advertising |= ADVERTISED_Pause; + XGBE_SET_LP_ADV(lks, Pause); if (lp_reg & 0x80) - pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; + XGBE_SET_LP_ADV(lks, Asym_Pause); if (pdata->phy.pause_autoneg) { /* Set flow control based on auto-negotiation result */ @@ -1379,10 +1384,8 @@ static enum xgbe_mode xgbe_phy_an37_outcome(struct xgbe_prv_data *pdata) } } - if (lp_reg & 0x40) - pdata->phy.lp_advertising |= ADVERTISED_1000baseT_Half; if (lp_reg & 0x20) - pdata->phy.lp_advertising |= ADVERTISED_1000baseT_Full; + XGBE_SET_LP_ADV(lks, 1000baseX_Full); /* Half duplex is not supported */ ad_reg &= lp_reg; @@ -1393,12 +1396,13 @@ static enum xgbe_mode xgbe_phy_an37_outcome(struct xgbe_prv_data *pdata) static enum xgbe_mode xgbe_phy_an73_redrv_outcome(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; enum xgbe_mode mode; unsigned int ad_reg, lp_reg; - pdata->phy.lp_advertising |= ADVERTISED_Autoneg; - pdata->phy.lp_advertising |= ADVERTISED_Backplane; + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, Backplane); /* Use external PHY to determine flow control */ if (pdata->phy.pause_autoneg) @@ -1408,9 +1412,9 @@ static enum xgbe_mode xgbe_phy_an73_redrv_outcome(struct xgbe_prv_data *pdata) ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); if (lp_reg & 0x80) - pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_LP_ADV(lks, 10000baseKR_Full); if (lp_reg & 0x20) - pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full; + XGBE_SET_LP_ADV(lks, 1000baseKX_Full); ad_reg &= lp_reg; if (ad_reg & 0x80) { @@ -1463,26 +1467,27 @@ static enum xgbe_mode xgbe_phy_an73_redrv_outcome(struct xgbe_prv_data *pdata) ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); if (lp_reg & 0xc000) - pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC; + XGBE_SET_LP_ADV(lks, 10000baseR_FEC); return mode; } static enum xgbe_mode xgbe_phy_an73_outcome(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; enum xgbe_mode mode; unsigned int ad_reg, lp_reg; - pdata->phy.lp_advertising |= ADVERTISED_Autoneg; - pdata->phy.lp_advertising |= ADVERTISED_Backplane; + XGBE_SET_LP_ADV(lks, Autoneg); + XGBE_SET_LP_ADV(lks, Backplane); /* Compare Advertisement and Link Partner register 1 */ ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); if (lp_reg & 0x400) - pdata->phy.lp_advertising |= ADVERTISED_Pause; + XGBE_SET_LP_ADV(lks, Pause); if (lp_reg & 0x800) - pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause; + XGBE_SET_LP_ADV(lks, Asym_Pause); if (pdata->phy.pause_autoneg) { /* Set flow control based on auto-negotiation result */ @@ -1504,9 +1509,9 @@ static enum xgbe_mode xgbe_phy_an73_outcome(struct xgbe_prv_data *pdata) ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); if (lp_reg & 0x80) - pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_LP_ADV(lks, 10000baseKR_Full); if (lp_reg & 0x20) - pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full; + XGBE_SET_LP_ADV(lks, 1000baseKX_Full); ad_reg &= lp_reg; if (ad_reg & 0x80) @@ -1520,7 +1525,7 @@ static enum xgbe_mode xgbe_phy_an73_outcome(struct xgbe_prv_data *pdata) ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); if (lp_reg & 0xc000) - pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC; + XGBE_SET_LP_ADV(lks, 10000baseR_FEC); return mode; } @@ -1541,41 +1546,43 @@ static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata) } } -static unsigned int xgbe_phy_an_advertising(struct xgbe_prv_data *pdata) +static void xgbe_phy_an_advertising(struct xgbe_prv_data *pdata, + struct ethtool_link_ksettings *dlks) { + struct ethtool_link_ksettings *slks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; - unsigned int advertising; + + XGBE_LM_COPY(dlks, advertising, slks, advertising); /* Without a re-driver, just return current advertising */ if (!phy_data->redrv) - return pdata->phy.advertising; + return; /* With the KR re-driver we need to advertise a single speed */ - advertising = pdata->phy.advertising; - advertising &= ~ADVERTISED_1000baseKX_Full; - advertising &= ~ADVERTISED_10000baseKR_Full; + XGBE_CLR_ADV(dlks, 1000baseKX_Full); + XGBE_CLR_ADV(dlks, 10000baseKR_Full); switch (phy_data->port_mode) { case XGBE_PORT_MODE_BACKPLANE: - advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_ADV(dlks, 10000baseKR_Full); break; case XGBE_PORT_MODE_BACKPLANE_2500: - advertising |= ADVERTISED_1000baseKX_Full; + XGBE_SET_ADV(dlks, 1000baseKX_Full); break; case XGBE_PORT_MODE_1000BASE_T: case XGBE_PORT_MODE_1000BASE_X: case XGBE_PORT_MODE_NBASE_T: - advertising |= ADVERTISED_1000baseKX_Full; + XGBE_SET_ADV(dlks, 1000baseKX_Full); break; case XGBE_PORT_MODE_10GBASE_T: if (phy_data->phydev && (phy_data->phydev->speed == SPEED_10000)) - advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_ADV(dlks, 10000baseKR_Full); else - advertising |= ADVERTISED_1000baseKX_Full; + XGBE_SET_ADV(dlks, 1000baseKX_Full); break; case XGBE_PORT_MODE_10GBASE_R: - advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_ADV(dlks, 10000baseKR_Full); break; case XGBE_PORT_MODE_SFP: switch (phy_data->sfp_base) { @@ -1583,24 +1590,24 @@ static unsigned int xgbe_phy_an_advertising(struct xgbe_prv_data *pdata) case XGBE_SFP_BASE_1000_SX: case XGBE_SFP_BASE_1000_LX: case XGBE_SFP_BASE_1000_CX: - advertising |= ADVERTISED_1000baseKX_Full; + XGBE_SET_ADV(dlks, 1000baseKX_Full); break; default: - advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_ADV(dlks, 10000baseKR_Full); break; } break; default: - advertising |= ADVERTISED_10000baseKR_Full; + XGBE_SET_ADV(dlks, 10000baseKR_Full); break; } - - return advertising; } static int xgbe_phy_an_config(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; + u32 advertising; int ret; ret = xgbe_phy_find_phy_device(pdata); @@ -1610,9 +1617,12 @@ static int xgbe_phy_an_config(struct xgbe_prv_data *pdata) if (!phy_data->phydev) return 0; + ethtool_convert_link_mode_to_legacy_u32(&advertising, + lks->link_modes.advertising); + phy_data->phydev->autoneg = pdata->phy.autoneg; phy_data->phydev->advertising = phy_data->phydev->supported & - pdata->phy.advertising; + advertising; if (pdata->phy.autoneg != AUTONEG_ENABLE) { phy_data->phydev->speed = pdata->phy.speed; @@ -2073,11 +2083,10 @@ static void xgbe_phy_set_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) } static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata, - enum xgbe_mode mode, u32 advert) + enum xgbe_mode mode, bool advert) { if (pdata->phy.autoneg == AUTONEG_ENABLE) { - if (pdata->phy.advertising & advert) - return true; + return advert; } else { enum xgbe_mode cur_mode; @@ -2092,13 +2101,15 @@ static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata, static bool xgbe_phy_use_basex_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + switch (mode) { case XGBE_MODE_X: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_1000baseT_Full); + XGBE_ADV(lks, 1000baseX_Full)); case XGBE_MODE_KR: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_10000baseT_Full); + XGBE_ADV(lks, 10000baseKR_Full)); default: return false; } @@ -2107,19 +2118,21 @@ static bool xgbe_phy_use_basex_mode(struct xgbe_prv_data *pdata, static bool xgbe_phy_use_baset_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + switch (mode) { case XGBE_MODE_SGMII_100: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_100baseT_Full); + XGBE_ADV(lks, 100baseT_Full)); case XGBE_MODE_SGMII_1000: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_1000baseT_Full); + XGBE_ADV(lks, 1000baseT_Full)); case XGBE_MODE_KX_2500: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_2500baseX_Full); + XGBE_ADV(lks, 2500baseT_Full)); case XGBE_MODE_KR: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_10000baseT_Full); + XGBE_ADV(lks, 10000baseT_Full)); default: return false; } @@ -2128,6 +2141,7 @@ static bool xgbe_phy_use_baset_mode(struct xgbe_prv_data *pdata, static bool xgbe_phy_use_sfp_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data = pdata->phy_data; switch (mode) { @@ -2135,22 +2149,26 @@ static bool xgbe_phy_use_sfp_mode(struct xgbe_prv_data *pdata, if (phy_data->sfp_base == XGBE_SFP_BASE_1000_T) return false; return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_1000baseT_Full); + XGBE_ADV(lks, 1000baseX_Full)); case XGBE_MODE_SGMII_100: if (phy_data->sfp_base != XGBE_SFP_BASE_1000_T) return false; return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_100baseT_Full); + XGBE_ADV(lks, 100baseT_Full)); case XGBE_MODE_SGMII_1000: if (phy_data->sfp_base != XGBE_SFP_BASE_1000_T) return false; return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_1000baseT_Full); + XGBE_ADV(lks, 1000baseT_Full)); case XGBE_MODE_SFI: if (phy_data->sfp_mod_absent) return true; return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_10000baseT_Full); + XGBE_ADV(lks, 10000baseSR_Full) || + XGBE_ADV(lks, 10000baseLR_Full) || + XGBE_ADV(lks, 10000baseLRM_Full) || + XGBE_ADV(lks, 10000baseER_Full) || + XGBE_ADV(lks, 10000baseCR_Full)); default: return false; } @@ -2159,10 +2177,12 @@ static bool xgbe_phy_use_sfp_mode(struct xgbe_prv_data *pdata, static bool xgbe_phy_use_bp_2500_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + switch (mode) { case XGBE_MODE_KX_2500: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_2500baseX_Full); + XGBE_ADV(lks, 2500baseX_Full)); default: return false; } @@ -2171,13 +2191,15 @@ static bool xgbe_phy_use_bp_2500_mode(struct xgbe_prv_data *pdata, static bool xgbe_phy_use_bp_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; + switch (mode) { case XGBE_MODE_KX_1000: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_1000baseKX_Full); + XGBE_ADV(lks, 1000baseKX_Full)); case XGBE_MODE_KR: return xgbe_phy_check_mode(pdata, mode, - ADVERTISED_10000baseKR_Full); + XGBE_ADV(lks, 10000baseKR_Full)); default: return false; } @@ -2744,6 +2766,7 @@ static void xgbe_phy_exit(struct xgbe_prv_data *pdata) static int xgbe_phy_init(struct xgbe_prv_data *pdata) { + struct ethtool_link_ksettings *lks = &pdata->phy.lks; struct xgbe_phy_data *phy_data; struct mii_bus *mii; unsigned int reg; @@ -2823,32 +2846,33 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) phy_data->cur_mode = XGBE_MODE_UNKNOWN; /* Initialize supported features */ - pdata->phy.supported = 0; + XGBE_ZERO_SUP(lks); switch (phy_data->port_mode) { /* Backplane support */ case XGBE_PORT_MODE_BACKPLANE: - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_Backplane; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, Backplane); if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { - pdata->phy.supported |= SUPPORTED_1000baseKX_Full; + XGBE_SET_SUP(lks, 1000baseKX_Full); phy_data->start_mode = XGBE_MODE_KX_1000; } if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) { - pdata->phy.supported |= SUPPORTED_10000baseKR_Full; + XGBE_SET_SUP(lks, 10000baseKR_Full); if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) - pdata->phy.supported |= - SUPPORTED_10000baseR_FEC; + XGBE_SET_SUP(lks, 10000baseR_FEC); phy_data->start_mode = XGBE_MODE_KR; } phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; break; case XGBE_PORT_MODE_BACKPLANE_2500: - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_Backplane; - pdata->phy.supported |= SUPPORTED_2500baseX_Full; + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, Backplane); + XGBE_SET_SUP(lks, 2500baseX_Full); phy_data->start_mode = XGBE_MODE_KX_2500; phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; @@ -2856,15 +2880,16 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) /* MDIO 1GBase-T support */ case XGBE_PORT_MODE_1000BASE_T: - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_TP; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { - pdata->phy.supported |= SUPPORTED_100baseT_Full; + XGBE_SET_SUP(lks, 100baseT_Full); phy_data->start_mode = XGBE_MODE_SGMII_100; } if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { - pdata->phy.supported |= SUPPORTED_1000baseT_Full; + XGBE_SET_SUP(lks, 1000baseT_Full); phy_data->start_mode = XGBE_MODE_SGMII_1000; } @@ -2873,10 +2898,11 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) /* MDIO Base-X support */ case XGBE_PORT_MODE_1000BASE_X: - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_FIBRE; - pdata->phy.supported |= SUPPORTED_1000baseT_Full; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, FIBRE); + XGBE_SET_SUP(lks, 1000baseX_Full); phy_data->start_mode = XGBE_MODE_X; phy_data->phydev_mode = XGBE_MDIO_MODE_CL22; @@ -2884,19 +2910,20 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) /* MDIO NBase-T support */ case XGBE_PORT_MODE_NBASE_T: - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_TP; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { - pdata->phy.supported |= SUPPORTED_100baseT_Full; + XGBE_SET_SUP(lks, 100baseT_Full); phy_data->start_mode = XGBE_MODE_SGMII_100; } if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { - pdata->phy.supported |= SUPPORTED_1000baseT_Full; + XGBE_SET_SUP(lks, 1000baseT_Full); phy_data->start_mode = XGBE_MODE_SGMII_1000; } if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_2500) { - pdata->phy.supported |= SUPPORTED_2500baseX_Full; + XGBE_SET_SUP(lks, 2500baseT_Full); phy_data->start_mode = XGBE_MODE_KX_2500; } @@ -2905,33 +2932,38 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) /* 10GBase-T support */ case XGBE_PORT_MODE_10GBASE_T: - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_TP; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { - pdata->phy.supported |= SUPPORTED_100baseT_Full; + XGBE_SET_SUP(lks, 100baseT_Full); phy_data->start_mode = XGBE_MODE_SGMII_100; } if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { - pdata->phy.supported |= SUPPORTED_1000baseT_Full; + XGBE_SET_SUP(lks, 1000baseT_Full); phy_data->start_mode = XGBE_MODE_SGMII_1000; } if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) { - pdata->phy.supported |= SUPPORTED_10000baseT_Full; + XGBE_SET_SUP(lks, 10000baseT_Full); phy_data->start_mode = XGBE_MODE_KR; } - phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; + phy_data->phydev_mode = XGBE_MDIO_MODE_CL45; break; /* 10GBase-R support */ case XGBE_PORT_MODE_10GBASE_R: - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_TP; - pdata->phy.supported |= SUPPORTED_10000baseT_Full; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, FIBRE); + XGBE_SET_SUP(lks, 10000baseSR_Full); + XGBE_SET_SUP(lks, 10000baseLR_Full); + XGBE_SET_SUP(lks, 10000baseLRM_Full); + XGBE_SET_SUP(lks, 10000baseER_Full); if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE) - pdata->phy.supported |= SUPPORTED_10000baseR_FEC; + XGBE_SET_SUP(lks, 10000baseR_FEC); phy_data->start_mode = XGBE_MODE_SFI; phy_data->phydev_mode = XGBE_MDIO_MODE_NONE; @@ -2939,22 +2971,17 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) /* SFP support */ case XGBE_PORT_MODE_SFP: - pdata->phy.supported |= SUPPORTED_Autoneg; - pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - pdata->phy.supported |= SUPPORTED_TP; - pdata->phy.supported |= SUPPORTED_FIBRE; - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) { - pdata->phy.supported |= SUPPORTED_100baseT_Full; + XGBE_SET_SUP(lks, Autoneg); + XGBE_SET_SUP(lks, Pause); + XGBE_SET_SUP(lks, Asym_Pause); + XGBE_SET_SUP(lks, TP); + XGBE_SET_SUP(lks, FIBRE); + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) phy_data->start_mode = XGBE_MODE_SGMII_100; - } - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) { - pdata->phy.supported |= SUPPORTED_1000baseT_Full; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) phy_data->start_mode = XGBE_MODE_SGMII_1000; - } - if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) { - pdata->phy.supported |= SUPPORTED_10000baseT_Full; + if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000) phy_data->start_mode = XGBE_MODE_SFI; - } phy_data->phydev_mode = XGBE_MDIO_MODE_CL22; @@ -2965,8 +2992,9 @@ static int xgbe_phy_init(struct xgbe_prv_data *pdata) } if (netif_msg_probe(pdata)) - dev_dbg(pdata->dev, "phy supported=%#x\n", - pdata->phy.supported); + dev_dbg(pdata->dev, "phy supported=0x%*pb\n", + __ETHTOOL_LINK_MODE_MASK_NBITS, + lks->link_modes.supported); if ((phy_data->conn_type & XGBE_CONN_TYPE_MDIO) && (phy_data->phydev_mode != XGBE_MDIO_MODE_NONE)) { diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 0938294f640a..ad102c8bac7b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -129,6 +129,10 @@ #include <net/dcbnl.h> #include <linux/completion.h> #include <linux/cpumask.h> +#include <linux/interrupt.h> +#include <linux/dcache.h> +#include <linux/ethtool.h> +#include <linux/list.h> #define XGBE_DRV_NAME "amd-xgbe" #define XGBE_DRV_VERSION "1.0.3" @@ -180,8 +184,6 @@ #define XGBE_IRQ_MODE_EDGE 0 #define XGBE_IRQ_MODE_LEVEL 1 -#define XGBE_DMA_INTERRUPT_MASK 0x31c7 - #define XGMAC_MIN_PACKET 60 #define XGMAC_STD_PACKET_MTU 1500 #define XGMAC_MAX_STD_PACKET 1518 @@ -296,6 +298,48 @@ /* MDIO port types */ #define XGMAC_MAX_C22_PORT 3 +/* Link mode bit operations */ +#define XGBE_ZERO_SUP(_ls) \ + ethtool_link_ksettings_zero_link_mode((_ls), supported) + +#define XGBE_SET_SUP(_ls, _mode) \ + ethtool_link_ksettings_add_link_mode((_ls), supported, _mode) + +#define XGBE_CLR_SUP(_ls, _mode) \ + ethtool_link_ksettings_del_link_mode((_ls), supported, _mode) + +#define XGBE_IS_SUP(_ls, _mode) \ + ethtool_link_ksettings_test_link_mode((_ls), supported, _mode) + +#define XGBE_ZERO_ADV(_ls) \ + ethtool_link_ksettings_zero_link_mode((_ls), advertising) + +#define XGBE_SET_ADV(_ls, _mode) \ + ethtool_link_ksettings_add_link_mode((_ls), advertising, _mode) + +#define XGBE_CLR_ADV(_ls, _mode) \ + ethtool_link_ksettings_del_link_mode((_ls), advertising, _mode) + +#define XGBE_ADV(_ls, _mode) \ + ethtool_link_ksettings_test_link_mode((_ls), advertising, _mode) + +#define XGBE_ZERO_LP_ADV(_ls) \ + ethtool_link_ksettings_zero_link_mode((_ls), lp_advertising) + +#define XGBE_SET_LP_ADV(_ls, _mode) \ + ethtool_link_ksettings_add_link_mode((_ls), lp_advertising, _mode) + +#define XGBE_CLR_LP_ADV(_ls, _mode) \ + ethtool_link_ksettings_del_link_mode((_ls), lp_advertising, _mode) + +#define XGBE_LP_ADV(_ls, _mode) \ + ethtool_link_ksettings_test_link_mode((_ls), lp_advertising, _mode) + +#define XGBE_LM_COPY(_dst, _dname, _src, _sname) \ + bitmap_copy((_dst)->link_modes._dname, \ + (_src)->link_modes._sname, \ + __ETHTOOL_LINK_MODE_MASK_NBITS) + struct xgbe_prv_data; struct xgbe_packet_data { @@ -460,6 +504,8 @@ struct xgbe_channel { /* Netdev related settings */ struct napi_struct napi; + /* Per channel interrupt enablement tracker */ + unsigned int curr_ier; unsigned int saved_ier; unsigned int tx_timer_active; @@ -561,9 +607,7 @@ enum xgbe_mdio_mode { }; struct xgbe_phy { - u32 supported; - u32 advertising; - u32 lp_advertising; + struct ethtool_link_ksettings lks; int address; @@ -666,6 +710,16 @@ struct xgbe_ext_stats { u64 tx_tso_packets; u64 rx_split_header_packets; u64 rx_buffer_unavailable; + + u64 txq_packets[XGBE_MAX_DMA_CHANNELS]; + u64 txq_bytes[XGBE_MAX_DMA_CHANNELS]; + u64 rxq_packets[XGBE_MAX_DMA_CHANNELS]; + u64 rxq_bytes[XGBE_MAX_DMA_CHANNELS]; + + u64 tx_vxlan_packets; + u64 rx_vxlan_packets; + u64 rx_csum_errors; + u64 rx_vxlan_csum_errors; }; struct xgbe_hw_if { @@ -769,6 +823,11 @@ struct xgbe_hw_if { /* For ECC */ void (*disable_ecc_ded)(struct xgbe_prv_data *); void (*disable_ecc_sec)(struct xgbe_prv_data *, enum xgbe_ecc_sec); + + /* For VXLAN */ + void (*enable_vxlan)(struct xgbe_prv_data *); + void (*disable_vxlan)(struct xgbe_prv_data *); + void (*set_vxlan_id)(struct xgbe_prv_data *); }; /* This structure represents implementation specific routines for an @@ -810,7 +869,8 @@ struct xgbe_phy_impl_if { int (*an_config)(struct xgbe_prv_data *); /* Set/override auto-negotiation advertisement settings */ - unsigned int (*an_advertising)(struct xgbe_prv_data *); + void (*an_advertising)(struct xgbe_prv_data *, + struct ethtool_link_ksettings *); /* Process results of auto-negotiation */ enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *); @@ -892,6 +952,7 @@ struct xgbe_hw_features { unsigned int addn_mac; /* Additional MAC Addresses */ unsigned int ts_src; /* Timestamp Source */ unsigned int sa_vlan_ins; /* Source Address or VLAN Insertion */ + unsigned int vxn; /* VXLAN/NVGRE */ /* HW Feature Register1 */ unsigned int rx_fifo_size; /* MTL Receive FIFO Size */ @@ -930,6 +991,12 @@ struct xgbe_version_data { unsigned int rx_desc_prefetch; }; +struct xgbe_vxlan_data { + struct list_head list; + sa_family_t sa_family; + __be16 port; +}; + struct xgbe_prv_data { struct net_device *netdev; struct pci_dev *pcidev; @@ -1071,6 +1138,15 @@ struct xgbe_prv_data { u32 rss_table[XGBE_RSS_MAX_TABLE_SIZE]; u32 rss_options; + /* VXLAN settings */ + unsigned int vxlan_port_set; + unsigned int vxlan_offloads_set; + unsigned int vxlan_force_disable; + unsigned int vxlan_port_count; + struct list_head vxlan_ports; + u16 vxlan_port; + netdev_features_t vxlan_features; + /* Netdev related settings */ unsigned char mac_addr[ETH_ALEN]; netdev_features_t netdev_features; @@ -1171,7 +1247,6 @@ struct xgbe_prv_data { struct tasklet_struct tasklet_i2c; struct tasklet_struct tasklet_an; -#ifdef CONFIG_DEBUG_FS struct dentry *xgbe_debugfs; unsigned int debugfs_xgmac_reg; @@ -1182,7 +1257,6 @@ struct xgbe_prv_data { unsigned int debugfs_xprop_reg; unsigned int debugfs_xi2c_reg; -#endif }; /* Function prototypes*/ @@ -1231,9 +1305,11 @@ void xgbe_init_tx_coalesce(struct xgbe_prv_data *); #ifdef CONFIG_DEBUG_FS void xgbe_debugfs_init(struct xgbe_prv_data *); void xgbe_debugfs_exit(struct xgbe_prv_data *); +void xgbe_debugfs_rename(struct xgbe_prv_data *pdata); #else static inline void xgbe_debugfs_init(struct xgbe_prv_data *pdata) {} static inline void xgbe_debugfs_exit(struct xgbe_prv_data *pdata) {} +static inline void xgbe_debugfs_rename(struct xgbe_prv_data *pdata) {} #endif /* CONFIG_DEBUG_FS */ /* NOTE: Uncomment for function trace log messages in KERNEL LOG */ diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index e45b587c2994..3188f553da35 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -468,7 +468,6 @@ static void xgene_enet_configure_clock(struct xgene_enet_pdata *pdata) static void xgene_gmac_set_speed(struct xgene_enet_pdata *pdata) { - struct device *dev = &pdata->pdev->dev; u32 icm0, icm2, mc2; u32 intf_ctl, rgmii, value; @@ -500,10 +499,8 @@ static void xgene_gmac_set_speed(struct xgene_enet_pdata *pdata) intf_ctl |= ENET_GHD_MODE; CFG_MACMODE_SET(&icm0, 2); CFG_WAITASYNCRD_SET(&icm2, 0); - if (dev->of_node) { - CFG_TXCLK_MUXSEL0_SET(&rgmii, pdata->tx_delay); - CFG_RXCLK_MUXSEL0_SET(&rgmii, pdata->rx_delay); - } + CFG_TXCLK_MUXSEL0_SET(&rgmii, pdata->tx_delay); + CFG_RXCLK_MUXSEL0_SET(&rgmii, pdata->rx_delay); rgmii |= CFG_SPEED_1250; xgene_enet_rd_csr(pdata, DEBUG_REG_ADDR, &value); diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 6e253d913fe2..3b889efddf78 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -1591,7 +1591,7 @@ static int xgene_get_tx_delay(struct xgene_enet_pdata *pdata) struct device *dev = &pdata->pdev->dev; int delay, ret; - ret = of_property_read_u32(dev->of_node, "tx-delay", &delay); + ret = device_property_read_u32(dev, "tx-delay", &delay); if (ret) { pdata->tx_delay = 4; return 0; @@ -1612,7 +1612,7 @@ static int xgene_get_rx_delay(struct xgene_enet_pdata *pdata) struct device *dev = &pdata->pdev->dev; int delay, ret; - ret = of_property_read_u32(dev->of_node, "rx-delay", &delay); + ret = device_property_read_u32(dev, "rx-delay", &delay); if (ret) { pdata->rx_delay = 2; return 0; @@ -1674,8 +1674,6 @@ static void xgene_enet_check_phy_handle(struct xgene_enet_pdata *pdata) ret = xgene_enet_phy_connect(pdata->ndev); if (!ret) pdata->mdio_driver = true; - - return; } static void xgene_enet_gpiod_get(struct xgene_enet_pdata *pdata) diff --git a/drivers/net/ethernet/apple/mace.c b/drivers/net/ethernet/apple/mace.c index 96dd5300e0e5..e58b157b7d7c 100644 --- a/drivers/net/ethernet/apple/mace.c +++ b/drivers/net/ethernet/apple/mace.c @@ -114,8 +114,8 @@ static int mace_probe(struct macio_dev *mdev, const struct of_device_id *match) int j, rev, rc = -EBUSY; if (macio_resource_count(mdev) != 3 || macio_irq_count(mdev) != 3) { - printk(KERN_ERR "can't use MACE %s: need 3 addrs and 3 irqs\n", - mace->full_name); + printk(KERN_ERR "can't use MACE %pOF: need 3 addrs and 3 irqs\n", + mace); return -ENODEV; } @@ -123,8 +123,8 @@ static int mace_probe(struct macio_dev *mdev, const struct of_device_id *match) if (addr == NULL) { addr = of_get_property(mace, "local-mac-address", NULL); if (addr == NULL) { - printk(KERN_ERR "Can't get mac-address for MACE %s\n", - mace->full_name); + printk(KERN_ERR "Can't get mac-address for MACE %pOF\n", + mace); return -ENODEV; } } diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c index ec5579fb8268..4eee1996a825 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c @@ -133,7 +133,10 @@ static inline unsigned int aq_ring_dx_in_range(unsigned int h, unsigned int i, } #define AQ_SKB_ALIGN SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) -int aq_ring_rx_clean(struct aq_ring_s *self, int *work_done, int budget) +int aq_ring_rx_clean(struct aq_ring_s *self, + struct napi_struct *napi, + int *work_done, + int budget) { struct net_device *ndev = aq_nic_get_ndev(self->aq_nic); int err = 0; @@ -239,7 +242,7 @@ int aq_ring_rx_clean(struct aq_ring_s *self, int *work_done, int budget) skb_record_rx_queue(skb, self->idx); - netif_receive_skb(skb); + napi_gro_receive(napi, skb); ++self->stats.rx.packets; self->stats.rx.bytes += skb->len; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h index eecd6d1c4d73..782176c5f4f8 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h @@ -148,7 +148,10 @@ int aq_ring_init(struct aq_ring_s *self); void aq_ring_rx_deinit(struct aq_ring_s *self); void aq_ring_free(struct aq_ring_s *self); void aq_ring_tx_clean(struct aq_ring_s *self); -int aq_ring_rx_clean(struct aq_ring_s *self, int *work_done, int budget); +int aq_ring_rx_clean(struct aq_ring_s *self, + struct napi_struct *napi, + int *work_done, + int budget); int aq_ring_rx_fill(struct aq_ring_s *self); #endif /* AQ_RING_H */ diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c index fee446af748f..ebf588004c46 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c @@ -76,6 +76,7 @@ static int aq_vec_poll(struct napi_struct *napi, int budget) if (ring[AQ_VEC_RX_ID].sw_head != ring[AQ_VEC_RX_ID].hw_head) { err = aq_ring_rx_clean(&ring[AQ_VEC_RX_ID], + napi, &work_done, budget - work_done); if (err < 0) diff --git a/drivers/net/ethernet/arc/emac_main.c b/drivers/net/ethernet/arc/emac_main.c index 68de2f2652f2..3241af1ce718 100644 --- a/drivers/net/ethernet/arc/emac_main.c +++ b/drivers/net/ethernet/arc/emac_main.c @@ -720,6 +720,18 @@ static int arc_emac_set_address(struct net_device *ndev, void *p) return 0; } +static int arc_emac_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + if (!netif_running(dev)) + return -EINVAL; + + if (!dev->phydev) + return -ENODEV; + + return phy_mii_ioctl(dev->phydev, rq, cmd); +} + + static const struct net_device_ops arc_emac_netdev_ops = { .ndo_open = arc_emac_open, .ndo_stop = arc_emac_stop, @@ -727,6 +739,7 @@ static const struct net_device_ops arc_emac_netdev_ops = { .ndo_set_mac_address = arc_emac_set_address, .ndo_get_stats = arc_emac_stats, .ndo_set_rx_mode = arc_emac_set_rx_mode, + .ndo_do_ioctl = arc_emac_ioctl, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = arc_emac_poll_controller, #endif diff --git a/drivers/net/ethernet/broadcom/Kconfig b/drivers/net/ethernet/broadcom/Kconfig index 96413808c726..67134ece1107 100644 --- a/drivers/net/ethernet/broadcom/Kconfig +++ b/drivers/net/ethernet/broadcom/Kconfig @@ -61,10 +61,12 @@ config BCM63XX_ENET config BCMGENET tristate "Broadcom GENET internal MAC support" + depends on OF && HAS_IOMEM select MII select PHYLIB select FIXED_PHY select BCM7XXX_PHY + select MDIO_BCM_UNIMAC help This driver supports the built-in Ethernet MACs found in the Broadcom BCM7xxx Set Top Box family chipset. @@ -193,6 +195,7 @@ config SYSTEMPORT config BNXT tristate "Broadcom NetXtreme-C/E support" depends on PCI + depends on MAY_USE_DEVLINK select FW_LOADER select LIBCRC32C ---help--- @@ -209,6 +212,15 @@ config BNXT_SRIOV Virtualization support in the NetXtreme-C/E products. This allows for virtual function acceleration in virtual environments. +config BNXT_FLOWER_OFFLOAD + bool "TC Flower offload support for NetXtreme-C/E" + depends on BNXT + default y + ---help--- + This configuration parameter enables TC Flower packet classifier + offload for eswitch. This option enables SR-IOV switchdev eswitch + offload. + config BNXT_DCB bool "Data Center Bridging (DCB) Support" default n diff --git a/drivers/net/ethernet/broadcom/bcm63xx_enet.c b/drivers/net/ethernet/broadcom/bcm63xx_enet.c index 61a88b64bd39..4f3845a58126 100644 --- a/drivers/net/ethernet/broadcom/bcm63xx_enet.c +++ b/drivers/net/ethernet/broadcom/bcm63xx_enet.c @@ -2674,7 +2674,7 @@ static int bcm_enetsw_set_ringparam(struct net_device *dev, return 0; } -static struct ethtool_ops bcm_enetsw_ethtool_ops = { +static const struct ethtool_ops bcm_enetsw_ethtool_ops = { .get_strings = bcm_enetsw_get_strings, .get_sset_count = bcm_enetsw_get_sset_count, .get_ethtool_stats = bcm_enetsw_get_ethtool_stats, diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index c28fa5a8734c..a6572b51435a 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -32,13 +32,13 @@ #define BCM_SYSPORT_IO_MACRO(name, offset) \ static inline u32 name##_readl(struct bcm_sysport_priv *priv, u32 off) \ { \ - u32 reg = __raw_readl(priv->base + offset + off); \ + u32 reg = readl_relaxed(priv->base + offset + off); \ return reg; \ } \ static inline void name##_writel(struct bcm_sysport_priv *priv, \ u32 val, u32 off) \ { \ - __raw_writel(val, priv->base + offset + off); \ + writel_relaxed(val, priv->base + offset + off); \ } \ BCM_SYSPORT_IO_MACRO(intrl2_0, SYS_PORT_INTRL2_0_OFFSET); @@ -59,14 +59,14 @@ static inline u32 rdma_readl(struct bcm_sysport_priv *priv, u32 off) { if (priv->is_lite && off >= RDMA_STATUS) off += 4; - return __raw_readl(priv->base + SYS_PORT_RDMA_OFFSET + off); + return readl_relaxed(priv->base + SYS_PORT_RDMA_OFFSET + off); } static inline void rdma_writel(struct bcm_sysport_priv *priv, u32 val, u32 off) { if (priv->is_lite && off >= RDMA_STATUS) off += 4; - __raw_writel(val, priv->base + SYS_PORT_RDMA_OFFSET + off); + writel_relaxed(val, priv->base + SYS_PORT_RDMA_OFFSET + off); } static inline u32 tdma_control_bit(struct bcm_sysport_priv *priv, u32 bit) @@ -110,10 +110,10 @@ static inline void dma_desc_set_addr(struct bcm_sysport_priv *priv, dma_addr_t addr) { #ifdef CONFIG_PHYS_ADDR_T_64BIT - __raw_writel(upper_32_bits(addr) & DESC_ADDR_HI_MASK, + writel_relaxed(upper_32_bits(addr) & DESC_ADDR_HI_MASK, d + DESC_ADDR_HI_STATUS_LEN); #endif - __raw_writel(lower_32_bits(addr), d + DESC_ADDR_LO); + writel_relaxed(lower_32_bits(addr), d + DESC_ADDR_LO); } static inline void tdma_port_write_desc_addr(struct bcm_sysport_priv *priv, @@ -201,10 +201,10 @@ static int bcm_sysport_set_features(struct net_device *dev, */ static const struct bcm_sysport_stats bcm_sysport_gstrings_stats[] = { /* general stats */ - STAT_NETDEV(rx_packets), - STAT_NETDEV(tx_packets), - STAT_NETDEV(rx_bytes), - STAT_NETDEV(tx_bytes), + STAT_NETDEV64(rx_packets), + STAT_NETDEV64(tx_packets), + STAT_NETDEV64(rx_bytes), + STAT_NETDEV64(tx_bytes), STAT_NETDEV(rx_errors), STAT_NETDEV(tx_errors), STAT_NETDEV(rx_dropped), @@ -316,6 +316,7 @@ static inline bool bcm_sysport_lite_stat_valid(enum bcm_sysport_stat_type type) { switch (type) { case BCM_SYSPORT_STAT_NETDEV: + case BCM_SYSPORT_STAT_NETDEV64: case BCM_SYSPORT_STAT_RXCHK: case BCM_SYSPORT_STAT_RBUF: case BCM_SYSPORT_STAT_SOFT: @@ -398,6 +399,7 @@ static void bcm_sysport_update_mib_counters(struct bcm_sysport_priv *priv) s = &bcm_sysport_gstrings_stats[i]; switch (s->type) { case BCM_SYSPORT_STAT_NETDEV: + case BCM_SYSPORT_STAT_NETDEV64: case BCM_SYSPORT_STAT_SOFT: continue; case BCM_SYSPORT_STAT_MIB_RX: @@ -434,7 +436,10 @@ static void bcm_sysport_get_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { struct bcm_sysport_priv *priv = netdev_priv(dev); + struct bcm_sysport_stats64 *stats64 = &priv->stats64; + struct u64_stats_sync *syncp = &priv->syncp; struct bcm_sysport_tx_ring *ring; + unsigned int start; int i, j; if (netif_running(dev)) @@ -447,14 +452,22 @@ static void bcm_sysport_get_stats(struct net_device *dev, s = &bcm_sysport_gstrings_stats[i]; if (s->type == BCM_SYSPORT_STAT_NETDEV) p = (char *)&dev->stats; + else if (s->type == BCM_SYSPORT_STAT_NETDEV64) + p = (char *)stats64; else p = (char *)priv; if (priv->is_lite && !bcm_sysport_lite_stat_valid(s->type)) continue; - p += s->stat_offset; - data[j] = *(unsigned long *)p; + + if (s->stat_sizeof == sizeof(u64)) + do { + start = u64_stats_fetch_begin_irq(syncp); + data[i] = *(u64 *)p; + } while (u64_stats_fetch_retry_irq(syncp, start)); + else + data[i] = *(u32 *)p; j++; } @@ -666,6 +679,7 @@ static int bcm_sysport_alloc_rx_bufs(struct bcm_sysport_priv *priv) static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv, unsigned int budget) { + struct bcm_sysport_stats64 *stats64 = &priv->stats64; struct net_device *ndev = priv->netdev; unsigned int processed = 0, to_process; struct bcm_sysport_cb *cb; @@ -769,6 +783,10 @@ static unsigned int bcm_sysport_desc_rx(struct bcm_sysport_priv *priv, skb->protocol = eth_type_trans(skb, ndev); ndev->stats.rx_packets++; ndev->stats.rx_bytes += len; + u64_stats_update_begin(&priv->syncp); + stats64->rx_packets++; + stats64->rx_bytes += len; + u64_stats_update_end(&priv->syncp); napi_gro_receive(&priv->napi, skb); next: @@ -791,17 +809,15 @@ static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_tx_ring *ring, struct device *kdev = &priv->pdev->dev; if (cb->skb) { - ring->bytes += cb->skb->len; *bytes_compl += cb->skb->len; dma_unmap_single(kdev, dma_unmap_addr(cb, dma_addr), dma_unmap_len(cb, dma_len), DMA_TO_DEVICE); - ring->packets++; (*pkts_compl)++; bcm_sysport_free_cb(cb); /* SKB fragment */ } else if (dma_unmap_addr(cb, dma_addr)) { - ring->bytes += dma_unmap_len(cb, dma_len); + *bytes_compl += dma_unmap_len(cb, dma_len); dma_unmap_page(kdev, dma_unmap_addr(cb, dma_addr), dma_unmap_len(cb, dma_len), DMA_TO_DEVICE); dma_unmap_addr_set(cb, dma_addr, 0); @@ -812,9 +828,9 @@ static void bcm_sysport_tx_reclaim_one(struct bcm_sysport_tx_ring *ring, static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, struct bcm_sysport_tx_ring *ring) { - struct net_device *ndev = priv->netdev; unsigned int c_index, last_c_index, last_tx_cn, num_tx_cbs; unsigned int pkts_compl = 0, bytes_compl = 0; + struct net_device *ndev = priv->netdev; struct bcm_sysport_cb *cb; u32 hw_ind; @@ -853,6 +869,11 @@ static unsigned int __bcm_sysport_tx_reclaim(struct bcm_sysport_priv *priv, last_c_index &= (num_tx_cbs - 1); } + u64_stats_update_begin(&priv->syncp); + ring->packets += pkts_compl; + ring->bytes += bytes_compl; + u64_stats_update_end(&priv->syncp); + ring->c_index = c_index; netif_dbg(priv, tx_done, ndev, @@ -1371,6 +1392,19 @@ static int bcm_sysport_init_tx_ring(struct bcm_sysport_priv *priv, tdma_writel(priv, RING_IGNORE_STATUS, TDMA_DESC_RING_MAPPING(index)); tdma_writel(priv, 0, TDMA_DESC_RING_PCP_DEI_VID(index)); + /* Do not use tdma_control_bit() here because TSB_SWAP1 collides + * with the original definition of ACB_ALGO + */ + reg = tdma_readl(priv, TDMA_CONTROL); + if (priv->is_lite) + reg &= ~BIT(TSB_SWAP1); + /* Set a correct TSB format based on host endian */ + if (!IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + reg |= tdma_control_bit(priv, TSB_SWAP0); + else + reg &= ~tdma_control_bit(priv, TSB_SWAP0); + tdma_writel(priv, reg, TDMA_CONTROL); + /* Program the number of descriptors as MAX_THRESHOLD and half of * its size for the hysteresis trigger */ @@ -1677,22 +1711,41 @@ static int bcm_sysport_change_mac(struct net_device *dev, void *p) return 0; } -static struct net_device_stats *bcm_sysport_get_nstats(struct net_device *dev) +static void bcm_sysport_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) { struct bcm_sysport_priv *priv = netdev_priv(dev); - unsigned long tx_bytes = 0, tx_packets = 0; + struct bcm_sysport_stats64 *stats64 = &priv->stats64; struct bcm_sysport_tx_ring *ring; + u64 tx_packets = 0, tx_bytes = 0; + unsigned int start; unsigned int q; + netdev_stats_to_stats64(stats, &dev->stats); + for (q = 0; q < dev->num_tx_queues; q++) { ring = &priv->tx_rings[q]; - tx_bytes += ring->bytes; - tx_packets += ring->packets; + do { + start = u64_stats_fetch_begin_irq(&priv->syncp); + tx_bytes = ring->bytes; + tx_packets = ring->packets; + } while (u64_stats_fetch_retry_irq(&priv->syncp, start)); + + stats->tx_bytes += tx_bytes; + stats->tx_packets += tx_packets; } - dev->stats.tx_bytes = tx_bytes; - dev->stats.tx_packets = tx_packets; - return &dev->stats; + /* lockless update tx_bytes and tx_packets */ + u64_stats_update_begin(&priv->syncp); + stats64->tx_bytes = stats->tx_bytes; + stats64->tx_packets = stats->tx_packets; + u64_stats_update_end(&priv->syncp); + + do { + start = u64_stats_fetch_begin_irq(&priv->syncp); + stats->rx_packets = stats64->rx_packets; + stats->rx_bytes = stats64->rx_bytes; + } while (u64_stats_fetch_retry_irq(&priv->syncp, start)); } static void bcm_sysport_netif_start(struct net_device *dev) @@ -1724,10 +1777,14 @@ static void rbuf_init(struct bcm_sysport_priv *priv) reg = rbuf_readl(priv, RBUF_CONTROL); reg |= RBUF_4B_ALGN | RBUF_RSB_EN; /* Set a correct RSB format on SYSTEMPORT Lite */ - if (priv->is_lite) { + if (priv->is_lite) reg &= ~RBUF_RSB_SWAP1; + + /* Set a correct RSB format based on host endian */ + if (!IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) reg |= RBUF_RSB_SWAP0; - } + else + reg &= ~RBUF_RSB_SWAP0; rbuf_writel(priv, reg, RBUF_CONTROL); } @@ -1956,7 +2013,7 @@ static const struct net_device_ops bcm_sysport_netdev_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = bcm_sysport_poll_controller, #endif - .ndo_get_stats = bcm_sysport_get_nstats, + .ndo_get_stats64 = bcm_sysport_get_stats64, }; #define REV_FMT "v%2x.%02x" @@ -2104,6 +2161,8 @@ static int bcm_sysport_probe(struct platform_device *pdev) /* libphy will adjust the link state accordingly */ netif_carrier_off(dev); + u64_stats_init(&priv->syncp); + ret = register_netdev(dev); if (ret) { dev_err(&pdev->dev, "failed to register net_device\n"); diff --git a/drivers/net/ethernet/broadcom/bcmsysport.h b/drivers/net/ethernet/broadcom/bcmsysport.h index 77a51c167a69..82e401df199e 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.h +++ b/drivers/net/ethernet/broadcom/bcmsysport.h @@ -449,7 +449,8 @@ struct bcm_rsb { /* Uses 2 bits on SYSTEMPORT Lite and shifts everything by 1 bit, we * keep the SYSTEMPORT layout here and adjust with tdma_control_bit() */ -#define TSB_SWAP 2 +#define TSB_SWAP0 2 +#define TSB_SWAP1 3 #define ACB_ALGO 3 #define BUF_DATA_OFFSET_SHIFT 4 #define BUF_DATA_OFFSET_MASK 0x3ff @@ -603,6 +604,7 @@ struct bcm_sysport_mib { /* HW maintains a large list of counters */ enum bcm_sysport_stat_type { BCM_SYSPORT_STAT_NETDEV = -1, + BCM_SYSPORT_STAT_NETDEV64, BCM_SYSPORT_STAT_MIB_RX, BCM_SYSPORT_STAT_MIB_TX, BCM_SYSPORT_STAT_RUNT, @@ -619,6 +621,13 @@ enum bcm_sysport_stat_type { .type = BCM_SYSPORT_STAT_NETDEV, \ } +#define STAT_NETDEV64(m) { \ + .stat_string = __stringify(m), \ + .stat_sizeof = sizeof(((struct bcm_sysport_stats64 *)0)->m), \ + .stat_offset = offsetof(struct bcm_sysport_stats64, m), \ + .type = BCM_SYSPORT_STAT_NETDEV64, \ +} + #define STAT_MIB(str, m, _type) { \ .stat_string = str, \ .stat_sizeof = sizeof(((struct bcm_sysport_priv *)0)->m), \ @@ -659,6 +668,14 @@ struct bcm_sysport_stats { u16 reg_offset; }; +struct bcm_sysport_stats64 { + /* 64bit stats on 32bit/64bit Machine */ + u64 rx_packets; + u64 rx_bytes; + u64 tx_packets; + u64 tx_bytes; +}; + /* Software house keeping helper structure */ struct bcm_sysport_cb { struct sk_buff *skb; /* SKB for RX packets */ @@ -743,5 +760,10 @@ struct bcm_sysport_priv { /* Ethtool */ u32 msg_enable; + + struct bcm_sysport_stats64 stats64; + + /* For atomic update generic 64bit value on 32bit Machine */ + struct u64_stats_sync syncp; }; #endif /* __BCM_SYSPORT_H */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 67fe3d826566..1216c1f1e052 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4284,15 +4284,17 @@ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc) return 0; } -int __bnx2x_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) { - if (tc->type != TC_SETUP_MQPRIO) - return -EINVAL; + struct tc_mqprio_qopt *mqprio = type_data; + + if (type != TC_SETUP_MQPRIO) + return -EOPNOTSUPP; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - return bnx2x_setup_tc(dev, tc->mqprio->num_tc); + return bnx2x_setup_tc(dev, mqprio->num_tc); } /* called with rtnl_lock */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index c26688d2f326..a5265e1344f1 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -486,8 +486,8 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev); /* setup_tc callback */ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc); -int __bnx2x_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc); +int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data); int bnx2x_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi); diff --git a/drivers/net/ethernet/broadcom/bnxt/Makefile b/drivers/net/ethernet/broadcom/bnxt/Makefile index a7ca45b251cb..4f0cb8e1ffc0 100644 --- a/drivers/net/ethernet/broadcom/bnxt/Makefile +++ b/drivers/net/ethernet/broadcom/bnxt/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_BNXT) += bnxt_en.o -bnxt_en-y := bnxt.o bnxt_sriov.o bnxt_ethtool.o bnxt_dcb.o bnxt_ulp.o bnxt_xdp.o +bnxt_en-y := bnxt.o bnxt_sriov.o bnxt_ethtool.o bnxt_dcb.o bnxt_ulp.o bnxt_xdp.o bnxt_vfr.o bnxt_tc.o diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index f20b3d2a4c23..aacec8bc19d5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -33,6 +33,7 @@ #include <linux/mii.h> #include <linux/if.h> #include <linux/if_vlan.h> +#include <linux/if_bridge.h> #include <linux/rtc.h> #include <linux/bpf.h> #include <net/ip.h> @@ -48,6 +49,8 @@ #include <linux/aer.h> #include <linux/bitmap.h> #include <linux/cpu_rmap.h> +#include <linux/cpumask.h> +#include <net/pkt_cls.h> #include "bnxt_hsi.h" #include "bnxt.h" @@ -56,6 +59,8 @@ #include "bnxt_ethtool.h" #include "bnxt_dcb.h" #include "bnxt_xdp.h" +#include "bnxt_vfr.h" +#include "bnxt_tc.h" #define BNXT_TX_TIMEOUT (5 * HZ) @@ -101,6 +106,8 @@ enum board_idx { BCM57416_NPAR, BCM57452, BCM57454, + BCM58802, + BCM58808, NETXTREME_E_VF, NETXTREME_C_VF, }; @@ -109,39 +116,42 @@ enum board_idx { static const struct { char *name; } board_info[] = { - { "Broadcom BCM57301 NetXtreme-C 10Gb Ethernet" }, - { "Broadcom BCM57302 NetXtreme-C 10Gb/25Gb Ethernet" }, - { "Broadcom BCM57304 NetXtreme-C 10Gb/25Gb/40Gb/50Gb Ethernet" }, - { "Broadcom BCM57417 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM58700 Nitro 1Gb/2.5Gb/10Gb Ethernet" }, - { "Broadcom BCM57311 NetXtreme-C 10Gb Ethernet" }, - { "Broadcom BCM57312 NetXtreme-C 10Gb/25Gb Ethernet" }, - { "Broadcom BCM57402 NetXtreme-E 10Gb Ethernet" }, - { "Broadcom BCM57404 NetXtreme-E 10Gb/25Gb Ethernet" }, - { "Broadcom BCM57406 NetXtreme-E 10GBase-T Ethernet" }, - { "Broadcom BCM57402 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM57407 NetXtreme-E 10GBase-T Ethernet" }, - { "Broadcom BCM57412 NetXtreme-E 10Gb Ethernet" }, - { "Broadcom BCM57414 NetXtreme-E 10Gb/25Gb Ethernet" }, - { "Broadcom BCM57416 NetXtreme-E 10GBase-T Ethernet" }, - { "Broadcom BCM57417 NetXtreme-E 10GBase-T Ethernet" }, - { "Broadcom BCM57412 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM57314 NetXtreme-C 10Gb/25Gb/40Gb/50Gb Ethernet" }, - { "Broadcom BCM57417 NetXtreme-E 10Gb/25Gb Ethernet" }, - { "Broadcom BCM57416 NetXtreme-E 10Gb Ethernet" }, - { "Broadcom BCM57404 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM57406 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM57407 NetXtreme-E 25Gb Ethernet" }, - { "Broadcom BCM57407 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM57414 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM57416 NetXtreme-E Ethernet Partition" }, - { "Broadcom BCM57452 NetXtreme-E 10Gb/25Gb/40Gb/50Gb Ethernet" }, - { "Broadcom BCM57454 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb Ethernet" }, - { "Broadcom NetXtreme-E Ethernet Virtual Function" }, - { "Broadcom NetXtreme-C Ethernet Virtual Function" }, + [BCM57301] = { "Broadcom BCM57301 NetXtreme-C 10Gb Ethernet" }, + [BCM57302] = { "Broadcom BCM57302 NetXtreme-C 10Gb/25Gb Ethernet" }, + [BCM57304] = { "Broadcom BCM57304 NetXtreme-C 10Gb/25Gb/40Gb/50Gb Ethernet" }, + [BCM57417_NPAR] = { "Broadcom BCM57417 NetXtreme-E Ethernet Partition" }, + [BCM58700] = { "Broadcom BCM58700 Nitro 1Gb/2.5Gb/10Gb Ethernet" }, + [BCM57311] = { "Broadcom BCM57311 NetXtreme-C 10Gb Ethernet" }, + [BCM57312] = { "Broadcom BCM57312 NetXtreme-C 10Gb/25Gb Ethernet" }, + [BCM57402] = { "Broadcom BCM57402 NetXtreme-E 10Gb Ethernet" }, + [BCM57404] = { "Broadcom BCM57404 NetXtreme-E 10Gb/25Gb Ethernet" }, + [BCM57406] = { "Broadcom BCM57406 NetXtreme-E 10GBase-T Ethernet" }, + [BCM57402_NPAR] = { "Broadcom BCM57402 NetXtreme-E Ethernet Partition" }, + [BCM57407] = { "Broadcom BCM57407 NetXtreme-E 10GBase-T Ethernet" }, + [BCM57412] = { "Broadcom BCM57412 NetXtreme-E 10Gb Ethernet" }, + [BCM57414] = { "Broadcom BCM57414 NetXtreme-E 10Gb/25Gb Ethernet" }, + [BCM57416] = { "Broadcom BCM57416 NetXtreme-E 10GBase-T Ethernet" }, + [BCM57417] = { "Broadcom BCM57417 NetXtreme-E 10GBase-T Ethernet" }, + [BCM57412_NPAR] = { "Broadcom BCM57412 NetXtreme-E Ethernet Partition" }, + [BCM57314] = { "Broadcom BCM57314 NetXtreme-C 10Gb/25Gb/40Gb/50Gb Ethernet" }, + [BCM57417_SFP] = { "Broadcom BCM57417 NetXtreme-E 10Gb/25Gb Ethernet" }, + [BCM57416_SFP] = { "Broadcom BCM57416 NetXtreme-E 10Gb Ethernet" }, + [BCM57404_NPAR] = { "Broadcom BCM57404 NetXtreme-E Ethernet Partition" }, + [BCM57406_NPAR] = { "Broadcom BCM57406 NetXtreme-E Ethernet Partition" }, + [BCM57407_SFP] = { "Broadcom BCM57407 NetXtreme-E 25Gb Ethernet" }, + [BCM57407_NPAR] = { "Broadcom BCM57407 NetXtreme-E Ethernet Partition" }, + [BCM57414_NPAR] = { "Broadcom BCM57414 NetXtreme-E Ethernet Partition" }, + [BCM57416_NPAR] = { "Broadcom BCM57416 NetXtreme-E Ethernet Partition" }, + [BCM57452] = { "Broadcom BCM57452 NetXtreme-E 10Gb/25Gb/40Gb/50Gb Ethernet" }, + [BCM57454] = { "Broadcom BCM57454 NetXtreme-E 10Gb/25Gb/40Gb/50Gb/100Gb Ethernet" }, + [BCM58802] = { "Broadcom BCM58802 NetXtreme-S 10Gb/25Gb/40Gb/50Gb Ethernet" }, + [BCM58808] = { "Broadcom BCM58808 NetXtreme-S 10Gb/25Gb/40Gb/50Gb/100Gb Ethernet" }, + [NETXTREME_E_VF] = { "Broadcom NetXtreme-E Ethernet Virtual Function" }, + [NETXTREME_C_VF] = { "Broadcom NetXtreme-C Ethernet Virtual Function" }, }; static const struct pci_device_id bnxt_pci_tbl[] = { + { PCI_VDEVICE(BROADCOM, 0x1614), .driver_data = BCM57454 }, { PCI_VDEVICE(BROADCOM, 0x16c0), .driver_data = BCM57417_NPAR }, { PCI_VDEVICE(BROADCOM, 0x16c8), .driver_data = BCM57301 }, { PCI_VDEVICE(BROADCOM, 0x16c9), .driver_data = BCM57302 }, @@ -172,8 +182,9 @@ static const struct pci_device_id bnxt_pci_tbl[] = { { PCI_VDEVICE(BROADCOM, 0x16ed), .driver_data = BCM57414_NPAR }, { PCI_VDEVICE(BROADCOM, 0x16ee), .driver_data = BCM57416_NPAR }, { PCI_VDEVICE(BROADCOM, 0x16ef), .driver_data = BCM57416_NPAR }, + { PCI_VDEVICE(BROADCOM, 0x16f0), .driver_data = BCM58808 }, { PCI_VDEVICE(BROADCOM, 0x16f1), .driver_data = BCM57452 }, - { PCI_VDEVICE(BROADCOM, 0x1614), .driver_data = BCM57454 }, + { PCI_VDEVICE(BROADCOM, 0xd802), .driver_data = BCM58802 }, #ifdef CONFIG_BNXT_SRIOV { PCI_VDEVICE(BROADCOM, 0x1606), .driver_data = NETXTREME_E_VF }, { PCI_VDEVICE(BROADCOM, 0x1609), .driver_data = NETXTREME_E_VF }, @@ -243,6 +254,16 @@ const u16 bnxt_lhint_arr[] = { TX_BD_FLAGS_LHINT_2048_AND_LARGER, }; +static u16 bnxt_xmit_get_cfa_action(struct sk_buff *skb) +{ + struct metadata_dst *md_dst = skb_metadata_dst(skb); + + if (!md_dst || md_dst->type != METADATA_HW_PORT_MUX) + return 0; + + return md_dst->u.port_info.port_id; +} + static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct bnxt *bp = netdev_priv(dev); @@ -287,7 +308,7 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) tx_buf->nr_frags = last_frag; vlan_tag_flags = 0; - cfa_action = 0; + cfa_action = bnxt_xmit_get_cfa_action(skb); if (skb_vlan_tag_present(skb)) { vlan_tag_flags = TX_BD_CFA_META_KEY_VLAN | skb_vlan_tag_get(skb); @@ -322,7 +343,8 @@ static netdev_tx_t bnxt_start_xmit(struct sk_buff *skb, struct net_device *dev) tx_push1->tx_bd_hsize_lflags = 0; tx_push1->tx_bd_cfa_meta = cpu_to_le32(vlan_tag_flags); - tx_push1->tx_bd_cfa_action = cpu_to_le32(cfa_action); + tx_push1->tx_bd_cfa_action = + cpu_to_le32(cfa_action << TX_BD_CFA_ACTION_SHIFT); end = pdata + length; end = PTR_ALIGN(end, 8) - 1; @@ -427,7 +449,8 @@ normal_tx: txbd->tx_bd_len_flags_type = cpu_to_le32(flags); txbd1->tx_bd_cfa_meta = cpu_to_le32(vlan_tag_flags); - txbd1->tx_bd_cfa_action = cpu_to_le32(cfa_action); + txbd1->tx_bd_cfa_action = + cpu_to_le32(cfa_action << TX_BD_CFA_ACTION_SHIFT); for (i = 0; i < last_frag; i++) { skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; @@ -1032,7 +1055,10 @@ static void bnxt_tpa_start(struct bnxt *bp, struct bnxt_rx_ring_info *rxr, bnxt_sched_reset(bp, rxr); return; } - + /* Store cfa_code in tpa_info to use in tpa_end + * completion processing. + */ + tpa_info->cfa_code = TPA_START_CFA_CODE(tpa_start1); prod_rx_buf->data = tpa_info->data; prod_rx_buf->data_ptr = tpa_info->data_ptr; @@ -1267,6 +1293,17 @@ static inline struct sk_buff *bnxt_gro_skb(struct bnxt *bp, return skb; } +/* Given the cfa_code of a received packet determine which + * netdev (vf-rep or PF) the packet is destined to. + */ +static struct net_device *bnxt_get_pkt_dev(struct bnxt *bp, u16 cfa_code) +{ + struct net_device *dev = bnxt_get_vf_rep(bp, cfa_code); + + /* if vf-rep dev is NULL, the must belongs to the PF */ + return dev ? dev : bp->dev; +} + static inline struct sk_buff *bnxt_tpa_end(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, @@ -1360,7 +1397,9 @@ static inline struct sk_buff *bnxt_tpa_end(struct bnxt *bp, return NULL; } } - skb->protocol = eth_type_trans(skb, bp->dev); + + skb->protocol = + eth_type_trans(skb, bnxt_get_pkt_dev(bp, tpa_info->cfa_code)); if (tpa_info->hash_type != PKT_HASH_TYPE_NONE) skb_set_hash(skb, tpa_info->rss_hash, tpa_info->hash_type); @@ -1387,6 +1426,18 @@ static inline struct sk_buff *bnxt_tpa_end(struct bnxt *bp, return skb; } +static void bnxt_deliver_skb(struct bnxt *bp, struct bnxt_napi *bnapi, + struct sk_buff *skb) +{ + if (skb->dev != bp->dev) { + /* this packet belongs to a vf-rep */ + bnxt_vf_rep_rx(bp, skb); + return; + } + skb_record_rx_queue(skb, bnapi->index); + napi_gro_receive(&bnapi->napi, skb); +} + /* returns the following: * 1 - 1 packet successfully received * 0 - successful TPA_START, packet not completed yet @@ -1403,7 +1454,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, struct rx_cmp *rxcmp; struct rx_cmp_ext *rxcmp1; u32 tmp_raw_cons = *raw_cons; - u16 cons, prod, cp_cons = RING_CMP(tmp_raw_cons); + u16 cfa_code, cons, prod, cp_cons = RING_CMP(tmp_raw_cons); struct bnxt_sw_rx_bd *rx_buf; unsigned int len; u8 *data_ptr, agg_bufs, cmp_type; @@ -1445,8 +1496,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, rc = -ENOMEM; if (likely(skb)) { - skb_record_rx_queue(skb, bnapi->index); - napi_gro_receive(&bnapi->napi, skb); + bnxt_deliver_skb(bp, bnapi, skb); rc = 1; } *event |= BNXT_RX_EVENT; @@ -1535,7 +1585,8 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, skb_set_hash(skb, le32_to_cpu(rxcmp->rx_cmp_rss_hash), type); } - skb->protocol = eth_type_trans(skb, dev); + cfa_code = RX_CMP_CFA_CODE(rxcmp1); + skb->protocol = eth_type_trans(skb, bnxt_get_pkt_dev(bp, cfa_code)); if ((rxcmp1->rx_cmp_flags2 & cpu_to_le32(RX_CMP_FLAGS2_META_FORMAT_VLAN)) && @@ -1560,8 +1611,7 @@ static int bnxt_rx_pkt(struct bnxt *bp, struct bnxt_napi *bnapi, u32 *raw_cons, } } - skb_record_rx_queue(skb, bnapi->index); - napi_gro_receive(&bnapi->napi, skb); + bnxt_deliver_skb(bp, bnapi, skb); rc = 1; next_rx: @@ -1802,6 +1852,13 @@ static int bnxt_poll_work(struct bnxt *bp, struct bnxt_napi *bnapi, int budget) &event); if (likely(rc >= 0)) rx_pkts += rc; + /* Increment rx_pkts when rc is -ENOMEM to count towards + * the NAPI budget. Otherwise, we may potentially loop + * here forever if we consistently cannot allocate + * buffers. + */ + else if (rc == -ENOMEM) + rx_pkts++; else if (rc == -EBUSY) /* partial completion */ break; } else if (unlikely((TX_CMP_TYPE(txcmp) == @@ -4420,9 +4477,33 @@ static int bnxt_hwrm_reserve_tx_rings(struct bnxt *bp, int *tx_rings) mutex_lock(&bp->hwrm_cmd_lock); rc = __bnxt_hwrm_get_tx_rings(bp, 0xffff, tx_rings); mutex_unlock(&bp->hwrm_cmd_lock); + if (!rc) + bp->tx_reserved_rings = *tx_rings; return rc; } +static int bnxt_hwrm_check_tx_rings(struct bnxt *bp, int tx_rings) +{ + struct hwrm_func_cfg_input req = {0}; + int rc; + + if (bp->hwrm_spec_code < 0x10801) + return 0; + + if (BNXT_VF(bp)) + return 0; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1); + req.fid = cpu_to_le16(0xffff); + req.flags = cpu_to_le32(FUNC_CFG_REQ_FLAGS_TX_ASSETS_TEST); + req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_NUM_TX_RINGS); + req.num_tx_rings = cpu_to_le16(tx_rings); + rc = hwrm_send_message_silent(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (rc) + return -ENOMEM; + return 0; +} + static void bnxt_hwrm_set_coal_params(struct bnxt *bp, u32 max_bufs, u32 buf_tmrs, u16 flags, struct hwrm_ring_cmpl_ring_cfg_aggint_params_input *req) @@ -4577,6 +4658,7 @@ static int bnxt_hwrm_func_qcfg(struct bnxt *bp) { struct hwrm_func_qcfg_input req = {0}; struct hwrm_func_qcfg_output *resp = bp->hwrm_cmd_resp_addr; + u16 flags; int rc; bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_QCFG, -1, -1); @@ -4593,15 +4675,15 @@ static int bnxt_hwrm_func_qcfg(struct bnxt *bp) vf->vlan = le16_to_cpu(resp->vlan) & VLAN_VID_MASK; } #endif - if (BNXT_PF(bp)) { - u16 flags = le16_to_cpu(resp->flags); - - if (flags & (FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED | - FUNC_QCFG_RESP_FLAGS_FW_LLDP_AGENT_ENABLED)) - bp->flags |= BNXT_FLAG_FW_LLDP_AGENT; - if (flags & FUNC_QCFG_RESP_FLAGS_MULTI_HOST) - bp->flags |= BNXT_FLAG_MULTI_HOST; + flags = le16_to_cpu(resp->flags); + if (flags & (FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED | + FUNC_QCFG_RESP_FLAGS_FW_LLDP_AGENT_ENABLED)) { + bp->flags |= BNXT_FLAG_FW_LLDP_AGENT; + if (flags & FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED) + bp->flags |= BNXT_FLAG_FW_DCBX_AGENT; } + if (BNXT_PF(bp) && (flags & FUNC_QCFG_RESP_FLAGS_MULTI_HOST)) + bp->flags |= BNXT_FLAG_MULTI_HOST; switch (resp->port_partition_type) { case FUNC_QCFG_RESP_PORT_PARTITION_TYPE_NPAR1_0: @@ -4610,6 +4692,13 @@ static int bnxt_hwrm_func_qcfg(struct bnxt *bp) bp->port_partition_type = resp->port_partition_type; break; } + if (bp->hwrm_spec_code < 0x10707 || + resp->evb_mode == FUNC_QCFG_RESP_EVB_MODE_VEB) + bp->br_mode = BRIDGE_MODE_VEB; + else if (resp->evb_mode == FUNC_QCFG_RESP_EVB_MODE_VEPA) + bp->br_mode = BRIDGE_MODE_VEPA; + else + bp->br_mode = BRIDGE_MODE_UNDEF; func_qcfg_exit: mutex_unlock(&bp->hwrm_cmd_lock); @@ -4900,6 +4989,26 @@ static void bnxt_hwrm_resource_free(struct bnxt *bp, bool close_path, } } +static int bnxt_hwrm_set_br_mode(struct bnxt *bp, u16 br_mode) +{ + struct hwrm_func_cfg_input req = {0}; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_FUNC_CFG, -1, -1); + req.fid = cpu_to_le16(0xffff); + req.enables = cpu_to_le32(FUNC_CFG_REQ_ENABLES_EVB_MODE); + if (br_mode == BRIDGE_MODE_VEB) + req.evb_mode = FUNC_CFG_REQ_EVB_MODE_VEB; + else if (br_mode == BRIDGE_MODE_VEPA) + req.evb_mode = FUNC_CFG_REQ_EVB_MODE_VEPA; + else + return -EINVAL; + rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (rc) + rc = -EIO; + return rc; +} + static int bnxt_setup_vnic(struct bnxt *bp, u16 vnic_id) { struct bnxt_vnic_info *vnic = &bp->vnic_info[vnic_id]; @@ -5035,6 +5144,15 @@ static int bnxt_init_chip(struct bnxt *bp, bool irq_re_init) rc); goto err_out; } + if (bp->tx_reserved_rings != bp->tx_nr_rings) { + int tx = bp->tx_nr_rings; + + if (bnxt_hwrm_reserve_tx_rings(bp, &tx) || + tx < bp->tx_nr_rings) { + rc = -ENOMEM; + goto err_out; + } + } } rc = bnxt_hwrm_ring_alloc(bp); @@ -5441,8 +5559,15 @@ static void bnxt_free_irq(struct bnxt *bp) for (i = 0; i < bp->cp_nr_rings; i++) { irq = &bp->irq_tbl[i]; - if (irq->requested) + if (irq->requested) { + if (irq->have_cpumask) { + irq_set_affinity_hint(irq->vector, NULL); + free_cpumask_var(irq->cpu_mask); + irq->have_cpumask = 0; + } free_irq(irq->vector, bp->bnapi[i]); + } + irq->requested = 0; } } @@ -5475,6 +5600,21 @@ static int bnxt_request_irq(struct bnxt *bp) break; irq->requested = 1; + + if (zalloc_cpumask_var(&irq->cpu_mask, GFP_KERNEL)) { + int numa_node = dev_to_node(&bp->pdev->dev); + + irq->have_cpumask = 1; + cpumask_set_cpu(cpumask_local_spread(i, numa_node), + irq->cpu_mask); + rc = irq_set_affinity_hint(irq->vector, irq->cpu_mask); + if (rc) { + netdev_warn(bp->dev, + "Set affinity failed, IRQ = %d\n", + irq->vector); + break; + } + } } return rc; } @@ -5548,12 +5688,10 @@ void bnxt_tx_disable(struct bnxt *bp) { int i; struct bnxt_tx_ring_info *txr; - struct netdev_queue *txq; if (bp->tx_ring) { for (i = 0; i < bp->tx_nr_rings; i++) { txr = &bp->tx_ring[i]; - txq = netdev_get_tx_queue(bp->dev, i); txr->dev_state = BNXT_DEV_STATE_CLOSING; } } @@ -5566,11 +5704,9 @@ void bnxt_tx_enable(struct bnxt *bp) { int i; struct bnxt_tx_ring_info *txr; - struct netdev_queue *txq; for (i = 0; i < bp->tx_nr_rings; i++) { txr = &bp->tx_ring[i]; - txq = netdev_get_tx_queue(bp->dev, i); txr->dev_state = 0; } netif_tx_wake_all_queues(bp->dev); @@ -5635,7 +5771,7 @@ static int bnxt_hwrm_phy_qcaps(struct bnxt *bp) if (rc) goto hwrm_phy_qcaps_exit; - if (resp->eee_supported & PORT_PHY_QCAPS_RESP_EEE_SUPPORTED) { + if (resp->flags & PORT_PHY_QCAPS_RESP_FLAGS_EEE_SUPPORTED) { struct ethtool_eee *eee = &bp->eee; u16 fw_speeds = le16_to_cpu(resp->supported_speeds_eee_mode); @@ -5650,6 +5786,8 @@ static int bnxt_hwrm_phy_qcaps(struct bnxt *bp) link_info->support_auto_speeds = le16_to_cpu(resp->supported_speeds_auto_mode); + bp->port_count = resp->port_cnt; + hwrm_phy_qcaps_exit: mutex_unlock(&bp->hwrm_cmd_lock); return rc; @@ -5675,13 +5813,15 @@ static int bnxt_update_link(struct bnxt *bp, bool chng_link_state) memcpy(&link_info->phy_qcfg_resp, resp, sizeof(*resp)); link_info->phy_link_status = resp->link; - link_info->duplex = resp->duplex; + link_info->duplex = resp->duplex_cfg; + if (bp->hwrm_spec_code >= 0x10800) + link_info->duplex = resp->duplex_state; link_info->pause = resp->pause; link_info->auto_mode = resp->auto_mode; link_info->auto_pause_setting = resp->auto_pause; link_info->lp_pause = resp->link_partner_adv_pause; link_info->force_pause_setting = resp->force_pause; - link_info->duplex_setting = resp->duplex; + link_info->duplex_setting = resp->duplex_cfg; if (link_info->phy_link_status == BNXT_LINK_LINK) link_info->link_speed = le16_to_cpu(resp->link_speed); else @@ -6203,6 +6343,9 @@ static int __bnxt_open_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) /* Poll link status and check for SFP+ module status */ bnxt_get_port_module_status(bp); + /* VF-reps may need to be re-opened after the PF is re-opened */ + if (BNXT_PF(bp)) + bnxt_vf_reps_open(bp); return 0; open_err: @@ -6291,6 +6434,10 @@ int bnxt_close_nic(struct bnxt *bp, bool irq_re_init, bool link_re_init) if (rc) netdev_warn(bp->dev, "timeout waiting for SRIOV config operation to complete!\n"); } + + /* Close the VF-reps before closing PF */ + if (BNXT_PF(bp)) + bnxt_vf_reps_close(bp); #endif /* Change device state to avoid TX queue wake up's */ bnxt_tx_disable(bp); @@ -6802,7 +6949,8 @@ static void bnxt_timer(unsigned long data) if (atomic_read(&bp->intr_sem) != 0) goto bnxt_restart_timer; - if (bp->link_info.link_up && (bp->flags & BNXT_FLAG_PORT_STATS)) { + if (bp->link_info.link_up && (bp->flags & BNXT_FLAG_PORT_STATS) && + bp->stats_coal_ticks) { set_bit(BNXT_PERIODIC_STATS_SP_EVENT, &bp->sp_event); schedule_work(&bp->sp_task); } @@ -6912,8 +7060,8 @@ static void bnxt_sp_task(struct work_struct *work) } /* Under rtnl_lock */ -int bnxt_reserve_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, - int tx_xdp) +int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, + int tx_xdp) { int max_rx, max_tx, tx_sets = 1; int tx_rings_needed; @@ -6933,10 +7081,7 @@ int bnxt_reserve_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, if (max_tx < tx_rings_needed) return -ENOMEM; - if (bnxt_hwrm_reserve_tx_rings(bp, &tx_rings_needed) || - tx_rings_needed < (tx * tx_sets + tx_xdp)) - return -ENOMEM; - return 0; + return bnxt_hwrm_check_tx_rings(bp, tx_rings_needed); } static void bnxt_unmap_bars(struct bnxt *bp, struct pci_dev *pdev) @@ -7125,8 +7270,8 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) if (bp->flags & BNXT_FLAG_SHARED_RINGS) sh = true; - rc = bnxt_reserve_rings(bp, bp->tx_nr_rings_per_tc, bp->rx_nr_rings, - sh, tc, bp->tx_nr_rings_xdp); + rc = bnxt_check_rings(bp, bp->tx_nr_rings_per_tc, bp->rx_nr_rings, + sh, tc, bp->tx_nr_rings_xdp); if (rc) return rc; @@ -7152,15 +7297,33 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) return 0; } -static int bnxt_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *ntc) +static int bnxt_setup_flower(struct net_device *dev, + struct tc_cls_flower_offload *cls_flower) { - if (ntc->type != TC_SETUP_MQPRIO) - return -EINVAL; + struct bnxt *bp = netdev_priv(dev); - ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + if (BNXT_VF(bp)) + return -EOPNOTSUPP; - return bnxt_setup_mq_tc(dev, ntc->mqprio->num_tc); + return bnxt_tc_setup_flower(bp, bp->pf.fw_fid, cls_flower); +} + +static int bnxt_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + switch (type) { + case TC_SETUP_CLSFLOWER: + return bnxt_setup_flower(dev, type_data); + case TC_SETUP_MQPRIO: { + struct tc_mqprio_qopt *mqprio = type_data; + + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + + return bnxt_setup_mq_tc(dev, mqprio->num_tc); + } + default: + return -EOPNOTSUPP; + } } #ifdef CONFIG_RFS_ACCEL @@ -7412,6 +7575,102 @@ static void bnxt_udp_tunnel_del(struct net_device *dev, schedule_work(&bp->sp_task); } +static int bnxt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, + struct net_device *dev, u32 filter_mask, + int nlflags) +{ + struct bnxt *bp = netdev_priv(dev); + + return ndo_dflt_bridge_getlink(skb, pid, seq, dev, bp->br_mode, 0, 0, + nlflags, filter_mask, NULL); +} + +static int bnxt_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, + u16 flags) +{ + struct bnxt *bp = netdev_priv(dev); + struct nlattr *attr, *br_spec; + int rem, rc = 0; + + if (bp->hwrm_spec_code < 0x10708 || !BNXT_SINGLE_PF(bp)) + return -EOPNOTSUPP; + + br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); + if (!br_spec) + return -EINVAL; + + nla_for_each_nested(attr, br_spec, rem) { + u16 mode; + + if (nla_type(attr) != IFLA_BRIDGE_MODE) + continue; + + if (nla_len(attr) < sizeof(mode)) + return -EINVAL; + + mode = nla_get_u16(attr); + if (mode == bp->br_mode) + break; + + rc = bnxt_hwrm_set_br_mode(bp, mode); + if (!rc) + bp->br_mode = mode; + break; + } + return rc; +} + +static int bnxt_get_phys_port_name(struct net_device *dev, char *buf, + size_t len) +{ + struct bnxt *bp = netdev_priv(dev); + int rc; + + /* The PF and it's VF-reps only support the switchdev framework */ + if (!BNXT_PF(bp)) + return -EOPNOTSUPP; + + rc = snprintf(buf, len, "p%d", bp->pf.port_id); + + if (rc >= len) + return -EOPNOTSUPP; + return 0; +} + +int bnxt_port_attr_get(struct bnxt *bp, struct switchdev_attr *attr) +{ + if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) + return -EOPNOTSUPP; + + /* The PF and it's VF-reps only support the switchdev framework */ + if (!BNXT_PF(bp)) + return -EOPNOTSUPP; + + switch (attr->id) { + case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: + /* In SRIOV each PF-pool (PF + child VFs) serves as a + * switching domain, the PF's perm mac-addr can be used + * as the unique parent-id + */ + attr->u.ppid.id_len = ETH_ALEN; + ether_addr_copy(attr->u.ppid.id, bp->pf.mac_addr); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int bnxt_swdev_port_attr_get(struct net_device *dev, + struct switchdev_attr *attr) +{ + return bnxt_port_attr_get(netdev_priv(dev), attr); +} + +static const struct switchdev_ops bnxt_switchdev_ops = { + .switchdev_port_attr_get = bnxt_swdev_port_attr_get +}; + static const struct net_device_ops bnxt_netdev_ops = { .ndo_open = bnxt_open, .ndo_start_xmit = bnxt_start_xmit, @@ -7443,6 +7702,9 @@ static const struct net_device_ops bnxt_netdev_ops = { .ndo_udp_tunnel_add = bnxt_udp_tunnel_add, .ndo_udp_tunnel_del = bnxt_udp_tunnel_del, .ndo_xdp = bnxt_xdp, + .ndo_bridge_getlink = bnxt_bridge_getlink, + .ndo_bridge_setlink = bnxt_bridge_setlink, + .ndo_get_phys_port_name = bnxt_get_phys_port_name }; static void bnxt_remove_one(struct pci_dev *pdev) @@ -7450,11 +7712,14 @@ static void bnxt_remove_one(struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); struct bnxt *bp = netdev_priv(dev); - if (BNXT_PF(bp)) + if (BNXT_PF(bp)) { bnxt_sriov_disable(bp); + bnxt_dl_unregister(bp); + } pci_disable_pcie_error_reporting(pdev); unregister_netdev(dev); + bnxt_shutdown_tc(bp); cancel_work_sync(&bp->sp_task); bp->sp_event = 0; @@ -7623,6 +7888,9 @@ static int bnxt_set_dflt_rings(struct bnxt *bp, bool sh) if (sh) bp->flags |= BNXT_FLAG_SHARED_RINGS; dflt_rings = netif_get_num_default_rss_queues(); + /* Reduce default rings to reduce memory usage on multi-port cards */ + if (bp->port_count > 1) + dflt_rings = min_t(int, dflt_rings, 4); rc = bnxt_get_dflt_rings(bp, &max_rx_rings, &max_tx_rings, sh); if (rc) return rc; @@ -7722,6 +7990,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) dev->netdev_ops = &bnxt_netdev_ops; dev->watchdog_timeo = BNXT_TX_TIMEOUT; dev->ethtool_ops = &bnxt_ethtool_ops; + SWITCHDEV_SET_OPS(dev, &bnxt_switchdev_ops); pci_set_drvdata(pdev, dev); rc = bnxt_alloc_hwrm_resources(bp); @@ -7776,6 +8045,7 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) #ifdef CONFIG_BNXT_SRIOV init_waitqueue_head(&bp->sriov_cfg_wait); + mutex_init(&bp->sriov_lock); #endif bp->gro_func = bnxt_gro_func_5730x; if (BNXT_CHIP_P4_PLUS(bp)) @@ -7820,6 +8090,10 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) bnxt_ethtool_init(bp); bnxt_dcb_init(bp); + rc = bnxt_probe_phy(bp); + if (rc) + goto init_err_pci_clean; + bnxt_set_rx_skb_mode(bp, false); bnxt_set_tpa_flags(bp); bnxt_set_ring_params(bp); @@ -7854,10 +8128,6 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (dev->hw_features & NETIF_F_HW_VLAN_CTAG_RX) bp->flags |= BNXT_FLAG_STRIP_VLAN; - rc = bnxt_probe_phy(bp); - if (rc) - goto init_err_pci_clean; - rc = bnxt_init_int_mode(bp); if (rc) goto init_err_pci_clean; @@ -7868,9 +8138,15 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) else device_set_wakeup_capable(&pdev->dev, false); + if (BNXT_PF(bp)) + bnxt_init_tc(bp); + rc = register_netdev(dev); if (rc) - goto init_err_clr_int; + goto init_err_cleanup_tc; + + if (BNXT_PF(bp)) + bnxt_dl_register(bp); netdev_info(dev, "%s found at mem %lx, node addr %pM\n", board_info[ent->driver_data].name, @@ -7880,7 +8156,8 @@ static int bnxt_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return 0; -init_err_clr_int: +init_err_cleanup_tc: + bnxt_shutdown_tc(bp); bnxt_clear_int_mode(bp); init_err_pci_clean: diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index f34691f85602..7b888d4b2b55 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -12,13 +12,17 @@ #define BNXT_H #define DRV_MODULE_NAME "bnxt_en" -#define DRV_MODULE_VERSION "1.7.0" +#define DRV_MODULE_VERSION "1.8.0" #define DRV_VER_MAJ 1 -#define DRV_VER_MIN 7 +#define DRV_VER_MIN 8 #define DRV_VER_UPD 0 #include <linux/interrupt.h> +#include <linux/rhashtable.h> +#include <net/devlink.h> +#include <net/dst_metadata.h> +#include <net/switchdev.h> struct tx_bd { __le32 tx_bd_len_flags_type; @@ -242,6 +246,10 @@ struct rx_cmp_ext { ((le32_to_cpu((rxcmp1)->rx_cmp_flags2) & \ RX_CMP_FLAGS2_T_L4_CS_CALC) >> 3) +#define RX_CMP_CFA_CODE(rxcmpl1) \ + ((le32_to_cpu((rxcmpl1)->rx_cmp_cfa_code_errors_v2) & \ + RX_CMPL_CFA_CODE_MASK) >> RX_CMPL_CFA_CODE_SFT) + struct rx_agg_cmp { __le32 rx_agg_cmp_len_flags_type; #define RX_AGG_CMP_TYPE (0x3f << 0) @@ -311,6 +319,10 @@ struct rx_tpa_start_cmp_ext { __le32 rx_tpa_start_cmp_hdr_info; }; +#define TPA_START_CFA_CODE(rx_tpa_start) \ + ((le32_to_cpu((rx_tpa_start)->rx_tpa_start_cmp_cfa_code_v2) & \ + RX_TPA_START_CMP_CFA_CODE) >> RX_TPA_START_CMPL_CFA_CODE_SHIFT) + struct rx_tpa_end_cmp { __le32 rx_tpa_end_cmp_len_flags_type; #define RX_TPA_END_CMP_TYPE (0x3f << 0) @@ -618,6 +630,8 @@ struct bnxt_tpa_info { #define BNXT_TPA_OUTER_L3_OFF(hdr_info) \ ((hdr_info) & 0x1ff) + + u16 cfa_code; /* cfa_code in TPA start compl */ }; struct bnxt_rx_ring_info { @@ -688,8 +702,10 @@ struct bnxt_napi { struct bnxt_irq { irq_handler_t handler; unsigned int vector; - u8 requested; + u8 requested:1; + u8 have_cpumask:1; char name[IFNAMSIZ + 2]; + cpumask_var_t cpu_mask; }; #define HWRM_RING_ALLOC_TX 0x1 @@ -825,8 +841,8 @@ struct bnxt_link_info { u8 loop_back; u8 link_up; u8 duplex; -#define BNXT_LINK_DUPLEX_HALF PORT_PHY_QCFG_RESP_DUPLEX_HALF -#define BNXT_LINK_DUPLEX_FULL PORT_PHY_QCFG_RESP_DUPLEX_FULL +#define BNXT_LINK_DUPLEX_HALF PORT_PHY_QCFG_RESP_DUPLEX_STATE_HALF +#define BNXT_LINK_DUPLEX_FULL PORT_PHY_QCFG_RESP_DUPLEX_STATE_FULL u8 pause; #define BNXT_LINK_PAUSE_TX PORT_PHY_QCFG_RESP_PAUSE_TX #define BNXT_LINK_PAUSE_RX PORT_PHY_QCFG_RESP_PAUSE_RX @@ -928,6 +944,45 @@ struct bnxt_test_info { #define BNXT_CAG_REG_LEGACY_INT_STATUS 0x4014 #define BNXT_CAG_REG_BASE 0x300000 +struct bnxt_tc_info { + bool enabled; + + /* hash table to store TC offloaded flows */ + struct rhashtable flow_table; + struct rhashtable_params flow_ht_params; + + /* hash table to store L2 keys of TC flows */ + struct rhashtable l2_table; + struct rhashtable_params l2_ht_params; + + /* lock to atomically add/del an l2 node when a flow is + * added or deleted. + */ + struct mutex lock; + + /* Stat counter mask (width) */ + u64 bytes_mask; + u64 packets_mask; +}; + +struct bnxt_vf_rep_stats { + u64 packets; + u64 bytes; + u64 dropped; +}; + +struct bnxt_vf_rep { + struct bnxt *bp; + struct net_device *dev; + struct metadata_dst *dst; + u16 vf_idx; + u16 tx_cfa_action; + u16 rx_cfa_code; + + struct bnxt_vf_rep_stats rx_stats; + struct bnxt_vf_rep_stats tx_stats; +}; + struct bnxt { void __iomem *bar0; void __iomem *bar1; @@ -957,6 +1012,9 @@ struct bnxt { #define CHIP_NUM_5745X 0xd730 +#define CHIP_NUM_58802 0xd802 +#define CHIP_NUM_58808 0xd808 + #define BNXT_CHIP_NUM_5730X(chip_num) \ ((chip_num) >= CHIP_NUM_57301 && \ (chip_num) <= CHIP_NUM_57304) @@ -988,6 +1046,10 @@ struct bnxt { #define BNXT_CHIP_NUM_57X1X(chip_num) \ (BNXT_CHIP_NUM_5731X(chip_num) || BNXT_CHIP_NUM_5741X(chip_num)) +#define BNXT_CHIP_NUM_588XX(chip_num) \ + ((chip_num) == CHIP_NUM_58802 || \ + (chip_num) == CHIP_NUM_58808) + struct net_device *dev; struct pci_dev *pdev; @@ -1027,6 +1089,7 @@ struct bnxt { #define BNXT_FLAG_MULTI_HOST 0x100000 #define BNXT_FLAG_SHORT_CMD 0x200000 #define BNXT_FLAG_DOUBLE_DB 0x400000 + #define BNXT_FLAG_FW_DCBX_AGENT 0x800000 #define BNXT_FLAG_CHIP_NITRO_A0 0x1000000 #define BNXT_FLAG_ALL_CONFIG_FEATS (BNXT_FLAG_TPA | \ @@ -1045,6 +1108,7 @@ struct bnxt { #define BNXT_CHIP_P4_PLUS(bp) \ (BNXT_CHIP_NUM_57X1X((bp)->chip_num) || \ BNXT_CHIP_NUM_5745X((bp)->chip_num) || \ + BNXT_CHIP_NUM_588XX((bp)->chip_num) || \ (BNXT_CHIP_NUM_58700((bp)->chip_num) && \ !BNXT_CHIP_TYPE_NITRO_A0(bp))) @@ -1086,6 +1150,7 @@ struct bnxt { int tx_nr_rings; int tx_nr_rings_per_tc; int tx_nr_rings_xdp; + int tx_reserved_rings; int tx_wake_thresh; int tx_push_thresh; @@ -1164,6 +1229,8 @@ struct bnxt { u8 nge_port_cnt; __le16 nge_fw_dst_port_id; u8 port_partition_type; + u8 port_count; + u16 br_mode; u16 rx_coal_ticks; u16 rx_coal_ticks_irq; @@ -1206,6 +1273,12 @@ struct bnxt { wait_queue_head_t sriov_cfg_wait; bool sriov_cfg; #define BNXT_SRIOV_CFG_WAIT_TMO msecs_to_jiffies(10000) + + /* lock to protect VF-rep creation/cleanup via + * multiple paths such as ->sriov_configure() and + * devlink ->eswitch_mode_set() + */ + struct mutex sriov_lock; #endif #define BNXT_NTP_FLTR_MAX_FLTR 4096 @@ -1232,6 +1305,13 @@ struct bnxt { struct bnxt_led_info leds[BNXT_MAX_LED]; struct bpf_prog *xdp_prog; + + /* devlink interface and vf-rep structs */ + struct devlink *dl; + enum devlink_eswitch_mode eswitch_mode; + struct bnxt_vf_rep **vf_reps; /* array of vf-rep ptrs */ + u16 *cfa_code_map; /* cfa_code -> vf_idx map */ + struct bnxt_tc_info tc_info; }; #define BNXT_RX_STATS_OFFSET(counter) \ @@ -1301,9 +1381,10 @@ int bnxt_open_nic(struct bnxt *, bool, bool); int bnxt_half_open_nic(struct bnxt *bp); void bnxt_half_close_nic(struct bnxt *bp); int bnxt_close_nic(struct bnxt *, bool, bool); -int bnxt_reserve_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, - int tx_xdp); +int bnxt_check_rings(struct bnxt *bp, int tx, int rx, bool sh, int tcs, + int tx_xdp); int bnxt_setup_mq_tc(struct net_device *dev, u8 tc); int bnxt_get_max_rings(struct bnxt *, int *, int *, bool); void bnxt_restore_pf_fw_resources(struct bnxt *bp); +int bnxt_port_attr_get(struct bnxt *bp, struct switchdev_attr *attr); #endif diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c index 5c6dd0ce209f..aa1f3a2c7a78 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.c @@ -93,6 +93,12 @@ static int bnxt_hwrm_queue_cos2bw_cfg(struct bnxt *bp, struct ieee_ets *ets, cos2bw.tsa = QUEUE_COS2BW_QCFG_RESP_QUEUE_ID0_TSA_ASSIGN_ETS; cos2bw.bw_weight = ets->tc_tx_bw[i]; + /* older firmware requires min_bw to be set to the + * same weight value in percent. + */ + cos2bw.min_bw = + cpu_to_le32((ets->tc_tx_bw[i] * 100) | + BW_VALUE_UNIT_PERCENT1_100); } memcpy(data, &cos2bw.queue_id, sizeof(cos2bw) - 4); if (i == 0) { @@ -549,13 +555,18 @@ static u8 bnxt_dcbnl_setdcbx(struct net_device *dev, u8 mode) { struct bnxt *bp = netdev_priv(dev); - /* only support IEEE */ - if ((mode & DCB_CAP_DCBX_VER_CEE) || !(mode & DCB_CAP_DCBX_VER_IEEE)) + /* All firmware DCBX settings are set in NVRAM */ + if (bp->dcbx_cap & DCB_CAP_DCBX_LLD_MANAGED) return 1; if (mode & DCB_CAP_DCBX_HOST) { if (BNXT_VF(bp) || (bp->flags & BNXT_FLAG_FW_LLDP_AGENT)) return 1; + + /* only support IEEE */ + if ((mode & DCB_CAP_DCBX_VER_CEE) || + !(mode & DCB_CAP_DCBX_VER_IEEE)) + return 1; } if (mode == bp->dcbx_cap) @@ -584,7 +595,7 @@ void bnxt_dcb_init(struct bnxt *bp) bp->dcbx_cap = DCB_CAP_DCBX_VER_IEEE; if (BNXT_PF(bp) && !(bp->flags & BNXT_FLAG_FW_LLDP_AGENT)) bp->dcbx_cap |= DCB_CAP_DCBX_HOST; - else + else if (bp->flags & BNXT_FLAG_FW_DCBX_AGENT) bp->dcbx_cap |= DCB_CAP_DCBX_LLD_MANAGED; bp->dev->dcbnl_ops = &dcbnl_ops; } diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h index ecd0a5e46a49..d2e0af960bf5 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_dcb.h @@ -26,6 +26,7 @@ struct bnxt_cos2bw_cfg { u8 queue_id; __le32 min_bw; __le32 max_bw; +#define BW_VALUE_UNIT_PERCENT1_100 (0x1UL << 29) u8 tsa; u8 pri_lvl; u8 bw_weight; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index be6acadcb202..8eff05a3e0e4 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -86,9 +86,11 @@ static int bnxt_set_coalesce(struct net_device *dev, if (bp->stats_coal_ticks != coal->stats_block_coalesce_usecs) { u32 stats_ticks = coal->stats_block_coalesce_usecs; - stats_ticks = clamp_t(u32, stats_ticks, - BNXT_MIN_STATS_COAL_TICKS, - BNXT_MAX_STATS_COAL_TICKS); + /* Allow 0, which means disable. */ + if (stats_ticks) + stats_ticks = clamp_t(u32, stats_ticks, + BNXT_MIN_STATS_COAL_TICKS, + BNXT_MAX_STATS_COAL_TICKS); stats_ticks = rounddown(stats_ticks, BNXT_MIN_STATS_COAL_TICKS); bp->stats_coal_ticks = stats_ticks; update_stats = true; @@ -198,19 +200,23 @@ static const struct { #define BNXT_NUM_PORT_STATS ARRAY_SIZE(bnxt_port_stats_arr) +static int bnxt_get_num_stats(struct bnxt *bp) +{ + int num_stats = BNXT_NUM_STATS * bp->cp_nr_rings; + + if (bp->flags & BNXT_FLAG_PORT_STATS) + num_stats += BNXT_NUM_PORT_STATS; + + return num_stats; +} + static int bnxt_get_sset_count(struct net_device *dev, int sset) { struct bnxt *bp = netdev_priv(dev); switch (sset) { - case ETH_SS_STATS: { - int num_stats = BNXT_NUM_STATS * bp->cp_nr_rings; - - if (bp->flags & BNXT_FLAG_PORT_STATS) - num_stats += BNXT_NUM_PORT_STATS; - - return num_stats; - } + case ETH_SS_STATS: + return bnxt_get_num_stats(bp); case ETH_SS_TEST: if (!bp->num_tests) return -EOPNOTSUPP; @@ -225,11 +231,8 @@ static void bnxt_get_ethtool_stats(struct net_device *dev, { u32 i, j = 0; struct bnxt *bp = netdev_priv(dev); - u32 buf_size = sizeof(struct ctx_hw_stats) * bp->cp_nr_rings; u32 stat_fields = sizeof(struct ctx_hw_stats) / 8; - memset(buf, 0, buf_size); - if (!bp->bnapi) return; @@ -432,8 +435,7 @@ static int bnxt_set_channels(struct net_device *dev, } tx_xdp = req_rx_rings; } - rc = bnxt_reserve_rings(bp, req_tx_rings, req_rx_rings, sh, tcs, - tx_xdp); + rc = bnxt_check_rings(bp, req_tx_rings, req_rx_rings, sh, tcs, tx_xdp); if (rc) { netdev_warn(dev, "Unable to allocate the requested rings\n"); return rc; @@ -520,7 +522,7 @@ static int bnxt_grxclsrule(struct bnxt *bp, struct ethtool_rxnfc *cmd) struct flow_keys *fkeys; int i, rc = -EINVAL; - if (fs->location < 0 || fs->location >= BNXT_NTP_FLTR_MAX_FLTR) + if (fs->location >= BNXT_NTP_FLTR_MAX_FLTR) return rc; for (i = 0; i < BNXT_NTP_FLTR_HASH_SIZE; i++) { @@ -835,7 +837,7 @@ static void bnxt_get_drvinfo(struct net_device *dev, strlcpy(info->fw_version, bp->fw_ver_str, sizeof(info->fw_version)); strlcpy(info->bus_info, pci_name(bp->pdev), sizeof(info->bus_info)); - info->n_stats = BNXT_NUM_STATS * bp->cp_nr_rings; + info->n_stats = bnxt_get_num_stats(bp); info->testinfo_len = bp->num_tests; /* TODO CHIMP_FW: eeprom dump details */ info->eedump_len = 0; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h index 7dc71bb95837..cb04cc76e8ad 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_hsi.h @@ -11,14 +11,14 @@ #ifndef BNXT_HSI_H #define BNXT_HSI_H -/* HSI and HWRM Specification 1.7.6 */ +/* HSI and HWRM Specification 1.8.1 */ #define HWRM_VERSION_MAJOR 1 -#define HWRM_VERSION_MINOR 7 -#define HWRM_VERSION_UPDATE 6 +#define HWRM_VERSION_MINOR 8 +#define HWRM_VERSION_UPDATE 1 -#define HWRM_VERSION_RSVD 2 /* non-zero means beta version */ +#define HWRM_VERSION_RSVD 4 /* non-zero means beta version */ -#define HWRM_VERSION_STR "1.7.6.2" +#define HWRM_VERSION_STR "1.8.1.4" /* * Following is the signature for HWRM message field that indicates not * applicable (All F's). Need to cast it the size of the field if needed. @@ -813,7 +813,7 @@ struct hwrm_func_qcfg_output { #define FUNC_QCFG_RESP_FLAGS_FW_DCBX_AGENT_ENABLED 0x4UL #define FUNC_QCFG_RESP_FLAGS_STD_TX_RING_MODE_ENABLED 0x8UL #define FUNC_QCFG_RESP_FLAGS_FW_LLDP_AGENT_ENABLED 0x10UL - #define FUNC_QCFG_RESP_FLAGS_MULTI_HOST 0x20UL + #define FUNC_QCFG_RESP_FLAGS_MULTI_HOST 0x20UL u8 mac_address[6]; __le16 pci_id; __le16 alloc_rsscos_ctx; @@ -835,9 +835,8 @@ struct hwrm_func_qcfg_output { u8 port_pf_cnt; #define FUNC_QCFG_RESP_PORT_PF_CNT_UNAVAIL 0x0UL __le16 dflt_vnic_id; - u8 host_cnt; - #define FUNC_QCFG_RESP_HOST_CNT_UNAVAIL 0x0UL u8 unused_0; + u8 unused_1; __le32 min_bw; #define FUNC_QCFG_RESP_MIN_BW_BW_VALUE_MASK 0xfffffffUL #define FUNC_QCFG_RESP_MIN_BW_BW_VALUE_SFT 0 @@ -874,12 +873,56 @@ struct hwrm_func_qcfg_output { #define FUNC_QCFG_RESP_EVB_MODE_NO_EVB 0x0UL #define FUNC_QCFG_RESP_EVB_MODE_VEB 0x1UL #define FUNC_QCFG_RESP_EVB_MODE_VEPA 0x2UL - u8 unused_1; + u8 unused_2; __le16 alloc_vfs; __le32 alloc_mcast_filters; __le32 alloc_hw_ring_grps; __le16 alloc_sp_tx_rings; + u8 unused_3; + u8 valid; +}; + +/* hwrm_func_vlan_cfg */ +/* Input (48 bytes) */ +struct hwrm_func_vlan_cfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 fid; + u8 unused_0; + u8 unused_1; + __le32 enables; + #define FUNC_VLAN_CFG_REQ_ENABLES_STAG_VID 0x1UL + #define FUNC_VLAN_CFG_REQ_ENABLES_CTAG_VID 0x2UL + #define FUNC_VLAN_CFG_REQ_ENABLES_STAG_PCP 0x4UL + #define FUNC_VLAN_CFG_REQ_ENABLES_CTAG_PCP 0x8UL + #define FUNC_VLAN_CFG_REQ_ENABLES_STAG_TPID 0x10UL + #define FUNC_VLAN_CFG_REQ_ENABLES_CTAG_TPID 0x20UL + __le16 stag_vid; + u8 stag_pcp; + u8 unused_2; + __be16 stag_tpid; + __le16 ctag_vid; + u8 ctag_pcp; + u8 unused_3; + __be16 ctag_tpid; + __le32 rsvd1; + __le32 rsvd2; + __le32 unused_4; +}; + +/* Output (16 bytes) */ +struct hwrm_func_vlan_cfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 unused_0; + u8 unused_1; u8 unused_2; + u8 unused_3; u8 valid; }; @@ -902,6 +945,8 @@ struct hwrm_func_cfg_input { #define FUNC_CFG_REQ_FLAGS_STD_TX_RING_MODE_ENABLE 0x200UL #define FUNC_CFG_REQ_FLAGS_STD_TX_RING_MODE_DISABLE 0x400UL #define FUNC_CFG_REQ_FLAGS_VIRT_MAC_PERSIST 0x800UL + #define FUNC_CFG_REQ_FLAGS_NO_AUTOCLEAR_STATISTIC 0x1000UL + #define FUNC_CFG_REQ_FLAGS_TX_ASSETS_TEST 0x2000UL __le32 enables; #define FUNC_CFG_REQ_ENABLES_MTU 0x1UL #define FUNC_CFG_REQ_ENABLES_MRU 0x2UL @@ -1456,9 +1501,9 @@ struct hwrm_port_phy_qcfg_output { #define PORT_PHY_QCFG_RESP_LINK_SPEED_50GB 0x1f4UL #define PORT_PHY_QCFG_RESP_LINK_SPEED_100GB 0x3e8UL #define PORT_PHY_QCFG_RESP_LINK_SPEED_10MB 0xffffUL - u8 duplex; - #define PORT_PHY_QCFG_RESP_DUPLEX_HALF 0x0UL - #define PORT_PHY_QCFG_RESP_DUPLEX_FULL 0x1UL + u8 duplex_cfg; + #define PORT_PHY_QCFG_RESP_DUPLEX_CFG_HALF 0x0UL + #define PORT_PHY_QCFG_RESP_DUPLEX_CFG_FULL 0x1UL u8 pause; #define PORT_PHY_QCFG_RESP_PAUSE_TX 0x1UL #define PORT_PHY_QCFG_RESP_PAUSE_RX 0x2UL @@ -1573,6 +1618,9 @@ struct hwrm_port_phy_qcfg_output { #define PORT_PHY_QCFG_RESP_PHY_TYPE_40G_BASELR4 0x16UL #define PORT_PHY_QCFG_RESP_PHY_TYPE_40G_BASEER4 0x17UL #define PORT_PHY_QCFG_RESP_PHY_TYPE_40G_ACTIVE_CABLE 0x18UL + #define PORT_PHY_QCFG_RESP_PHY_TYPE_1G_BASET 0x19UL + #define PORT_PHY_QCFG_RESP_PHY_TYPE_1G_BASESX 0x1aUL + #define PORT_PHY_QCFG_RESP_PHY_TYPE_1G_BASECX 0x1bUL u8 media_type; #define PORT_PHY_QCFG_RESP_MEDIA_TYPE_UNKNOWN 0x0UL #define PORT_PHY_QCFG_RESP_MEDIA_TYPE_TP 0x1UL @@ -1651,14 +1699,16 @@ struct hwrm_port_phy_qcfg_output { #define PORT_PHY_QCFG_RESP_FEC_CFG_FEC_CLAUSE74_ENABLED 0x10UL #define PORT_PHY_QCFG_RESP_FEC_CFG_FEC_CLAUSE91_SUPPORTED 0x20UL #define PORT_PHY_QCFG_RESP_FEC_CFG_FEC_CLAUSE91_ENABLED 0x40UL + u8 duplex_state; + #define PORT_PHY_QCFG_RESP_DUPLEX_STATE_HALF 0x0UL + #define PORT_PHY_QCFG_RESP_DUPLEX_STATE_FULL 0x1UL u8 unused_1; - u8 unused_2; char phy_vendor_name[16]; char phy_vendor_partnumber[16]; - __le32 unused_3; + __le32 unused_2; + u8 unused_3; u8 unused_4; u8 unused_5; - u8 unused_6; u8 valid; }; @@ -1744,6 +1794,51 @@ struct hwrm_port_mac_cfg_output { u8 valid; }; +/* hwrm_port_mac_ptp_qcfg */ +/* Input (24 bytes) */ +struct hwrm_port_mac_ptp_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 port_id; + __le16 unused_0[3]; +}; + +/* Output (80 bytes) */ +struct hwrm_port_mac_ptp_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 flags; + #define PORT_MAC_PTP_QCFG_RESP_FLAGS_DIRECT_ACCESS 0x1UL + #define PORT_MAC_PTP_QCFG_RESP_FLAGS_HWRM_ACCESS 0x2UL + u8 unused_0; + __le16 unused_1; + __le32 rx_ts_reg_off_lower; + __le32 rx_ts_reg_off_upper; + __le32 rx_ts_reg_off_seq_id; + __le32 rx_ts_reg_off_src_id_0; + __le32 rx_ts_reg_off_src_id_1; + __le32 rx_ts_reg_off_src_id_2; + __le32 rx_ts_reg_off_domain_id; + __le32 rx_ts_reg_off_fifo; + __le32 rx_ts_reg_off_fifo_adv; + __le32 rx_ts_reg_off_granularity; + __le32 tx_ts_reg_off_lower; + __le32 tx_ts_reg_off_upper; + __le32 tx_ts_reg_off_seq_id; + __le32 tx_ts_reg_off_fifo; + __le32 tx_ts_reg_off_granularity; + __le32 unused_2; + u8 unused_3; + u8 unused_4; + u8 unused_5; + u8 valid; +}; + /* hwrm_port_qstats */ /* Input (40 bytes) */ struct hwrm_port_qstats_input { @@ -1874,11 +1969,16 @@ struct hwrm_port_phy_qcaps_output { __le16 req_type; __le16 seq_id; __le16 resp_len; - u8 eee_supported; - #define PORT_PHY_QCAPS_RESP_EEE_SUPPORTED 0x1UL - #define PORT_PHY_QCAPS_RESP_RSVD1_MASK 0xfeUL - #define PORT_PHY_QCAPS_RESP_RSVD1_SFT 1 - u8 unused_0; + u8 flags; + #define PORT_PHY_QCAPS_RESP_FLAGS_EEE_SUPPORTED 0x1UL + #define PORT_PHY_QCAPS_RESP_FLAGS_RSVD1_MASK 0xfeUL + #define PORT_PHY_QCAPS_RESP_FLAGS_RSVD1_SFT 1 + u8 port_cnt; + #define PORT_PHY_QCAPS_RESP_PORT_CNT_UNKNOWN 0x0UL + #define PORT_PHY_QCAPS_RESP_PORT_CNT_1 0x1UL + #define PORT_PHY_QCAPS_RESP_PORT_CNT_2 0x2UL + #define PORT_PHY_QCAPS_RESP_PORT_CNT_3 0x3UL + #define PORT_PHY_QCAPS_RESP_PORT_CNT_4 0x4UL __le16 supported_speeds_force_mode; #define PORT_PHY_QCAPS_RESP_SUPPORTED_SPEEDS_FORCE_MODE_100MBHD 0x1UL #define PORT_PHY_QCAPS_RESP_SUPPORTED_SPEEDS_FORCE_MODE_100MB 0x2UL @@ -3152,6 +3252,95 @@ struct hwrm_queue_cos2bw_cfg_output { u8 valid; }; +/* hwrm_queue_dscp_qcaps */ +/* Input (24 bytes) */ +struct hwrm_queue_dscp_qcaps_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + u8 port_id; + u8 unused_0[7]; +}; + +/* Output (16 bytes) */ +struct hwrm_queue_dscp_qcaps_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + u8 num_dscp_bits; + u8 unused_0; + __le16 max_entries; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + +/* hwrm_queue_dscp2pri_qcfg */ +/* Input (32 bytes) */ +struct hwrm_queue_dscp2pri_qcfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le64 dest_data_addr; + u8 port_id; + u8 unused_0; + __le16 dest_data_buffer_size; + __le32 unused_1; +}; + +/* Output (16 bytes) */ +struct hwrm_queue_dscp2pri_qcfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le16 entry_cnt; + u8 default_pri; + u8 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + +/* hwrm_queue_dscp2pri_cfg */ +/* Input (40 bytes) */ +struct hwrm_queue_dscp2pri_cfg_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le64 src_data_addr; + __le32 flags; + #define QUEUE_DSCP2PRI_CFG_REQ_FLAGS_USE_HW_DEFAULT_PRI 0x1UL + __le32 enables; + #define QUEUE_DSCP2PRI_CFG_REQ_ENABLES_DEFAULT_PRI 0x1UL + u8 port_id; + u8 default_pri; + __le16 entry_cnt; + __le32 unused_0; +}; + +/* Output (16 bytes) */ +struct hwrm_queue_dscp2pri_cfg_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + /* hwrm_vnic_alloc */ /* Input (24 bytes) */ struct hwrm_vnic_alloc_input { @@ -4038,7 +4227,7 @@ struct hwrm_cfa_encap_record_alloc_input { #define CFA_ENCAP_RECORD_ALLOC_REQ_ENCAP_TYPE_IPGRE 0x8UL u8 unused_0; __le16 unused_1; - __le32 encap_data[16]; + __le32 encap_data[20]; }; /* Output (16 bytes) */ @@ -4120,8 +4309,8 @@ struct hwrm_cfa_ntuple_filter_alloc_input { #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_ADDR_TYPE_IPV6 0x6UL u8 ip_protocol; #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UNKNOWN 0x0UL - #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP 0x6UL - #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_TCP 0x11UL + #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_TCP 0x6UL + #define CFA_NTUPLE_FILTER_ALLOC_REQ_IP_PROTOCOL_UDP 0x11UL __le16 dst_id; __le16 mirror_vnic_id; u8 tunnel_type; @@ -4224,6 +4413,216 @@ struct hwrm_cfa_ntuple_filter_cfg_output { u8 valid; }; +/* hwrm_cfa_flow_alloc */ +/* Input (128 bytes) */ +struct hwrm_cfa_flow_alloc_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 flags; + #define CFA_FLOW_ALLOC_REQ_FLAGS_TUNNEL 0x1UL + #define CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_MASK 0x6UL + #define CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_SFT 1 + #define CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_NONE (0x0UL << 1) + #define CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_ONE (0x1UL << 1) + #define CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_TWO (0x2UL << 1) + #define CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_LAST CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_TWO + #define CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_MASK 0x38UL + #define CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_SFT 3 + #define CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_L2 (0x0UL << 3) + #define CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV4 (0x1UL << 3) + #define CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV6 (0x2UL << 3) + #define CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_LAST CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV6 + __le16 src_fid; + __le32 tunnel_handle; + __le16 action_flags; + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_FWD 0x1UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_RECYCLE 0x2UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_DROP 0x4UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_METER 0x8UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TUNNEL 0x10UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_SRC 0x20UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_DEST 0x40UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_NAT_IPV4_ADDRESS 0x80UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE 0x100UL + #define CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_TTL_DECREMENT 0x200UL + __le16 dst_fid; + __be16 l2_rewrite_vlan_tpid; + __be16 l2_rewrite_vlan_tci; + __le16 act_meter_id; + __le16 ref_flow_handle; + __be16 ethertype; + __be16 outer_vlan_tci; + __be16 dmac[3]; + __be16 inner_vlan_tci; + __be16 smac[3]; + u8 ip_dst_mask_len; + u8 ip_src_mask_len; + __be32 ip_dst[4]; + __be32 ip_src[4]; + __be16 l4_src_port; + __be16 l4_src_port_mask; + __be16 l4_dst_port; + __be16 l4_dst_port_mask; + __be32 nat_ip_address[4]; + __be16 l2_rewrite_dmac[3]; + __be16 nat_port; + __be16 l2_rewrite_smac[3]; + u8 ip_proto; + u8 unused_0; +}; + +/* Output (16 bytes) */ +struct hwrm_cfa_flow_alloc_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le16 flow_handle; + u8 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 unused_4; + u8 valid; +}; + +/* hwrm_cfa_flow_free */ +/* Input (24 bytes) */ +struct hwrm_cfa_flow_free_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 flow_handle; + __le16 unused_0[3]; +}; + +/* Output (32 bytes) */ +struct hwrm_cfa_flow_free_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le64 packet; + __le64 byte; + __le32 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + +/* hwrm_cfa_flow_stats */ +/* Input (40 bytes) */ +struct hwrm_cfa_flow_stats_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 num_flows; + __le16 flow_handle_0; + __le16 flow_handle_1; + __le16 flow_handle_2; + __le16 flow_handle_3; + __le16 flow_handle_4; + __le16 flow_handle_5; + __le16 flow_handle_6; + __le16 flow_handle_7; + __le16 flow_handle_8; + __le16 flow_handle_9; + __le16 unused_0; +}; + +/* Output (176 bytes) */ +struct hwrm_cfa_flow_stats_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le64 packet_0; + __le64 packet_1; + __le64 packet_2; + __le64 packet_3; + __le64 packet_4; + __le64 packet_5; + __le64 packet_6; + __le64 packet_7; + __le64 packet_8; + __le64 packet_9; + __le64 byte_0; + __le64 byte_1; + __le64 byte_2; + __le64 byte_3; + __le64 byte_4; + __le64 byte_5; + __le64 byte_6; + __le64 byte_7; + __le64 byte_8; + __le64 byte_9; + __le32 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + +/* hwrm_cfa_vfr_alloc */ +/* Input (32 bytes) */ +struct hwrm_cfa_vfr_alloc_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + __le16 vf_id; + __le16 reserved; + __le32 unused_0; + char vfr_name[32]; +}; + +/* Output (16 bytes) */ +struct hwrm_cfa_vfr_alloc_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le16 rx_cfa_code; + __le16 tx_cfa_action; + u8 unused_0; + u8 unused_1; + u8 unused_2; + u8 valid; +}; + +/* hwrm_cfa_vfr_free */ +/* Input (24 bytes) */ +struct hwrm_cfa_vfr_free_input { + __le16 req_type; + __le16 cmpl_ring; + __le16 seq_id; + __le16 target_id; + __le64 resp_addr; + char vfr_name[32]; +}; + +/* Output (16 bytes) */ +struct hwrm_cfa_vfr_free_output { + __le16 error_code; + __le16 req_type; + __le16 seq_id; + __le16 resp_len; + __le32 unused_0; + u8 unused_1; + u8 unused_2; + u8 unused_3; + u8 valid; +}; + /* hwrm_tunnel_dst_port_query */ /* Input (24 bytes) */ struct hwrm_tunnel_dst_port_query_input { @@ -4448,12 +4847,13 @@ struct hwrm_fw_reset_input { #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_MGMT 0x1UL #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_NETCTRL 0x2UL #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_ROCE 0x3UL - #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_RSVD 0x4UL + #define FW_RESET_REQ_EMBEDDED_PROC_TYPE_HOST 0x4UL u8 selfrst_status; #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTNONE 0x0UL #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTASAP 0x1UL #define FW_RESET_REQ_SELFRST_STATUS_SELFRSTPCIERST 0x2UL - __le16 unused_0[3]; + u8 host_idx; + u8 unused_0[5]; }; /* Output (16 bytes) */ @@ -4487,7 +4887,7 @@ struct hwrm_fw_qstatus_input { #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_MGMT 0x1UL #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_NETCTRL 0x2UL #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_ROCE 0x3UL - #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_RSVD 0x4UL + #define FW_QSTATUS_REQ_EMBEDDED_PROC_TYPE_HOST 0x4UL u8 unused_0[7]; }; @@ -4572,6 +4972,16 @@ struct hwrm_fw_set_structured_data_output { u8 valid; }; +/* Command specific Error Codes (8 bytes) */ +struct hwrm_fw_set_structured_data_cmd_err { + u8 code; + #define FW_SET_STRUCTURED_DATA_CMD_ERR_CODE_UNKNOWN 0x0UL + #define FW_SET_STRUCTURED_DATA_CMD_ERR_CODE_BAD_HDR_CNT 0x1UL + #define FW_SET_STRUCTURED_DATA_CMD_ERR_CODE_BAD_FMT 0x2UL + #define FW_SET_STRUCTURED_DATA_CMD_ERR_CODE_BAD_ID 0x3UL + u8 unused_0[7]; +}; + /* hwrm_fw_get_structured_data */ /* Input (32 bytes) */ struct hwrm_fw_get_structured_data_input { @@ -4611,6 +5021,14 @@ struct hwrm_fw_get_structured_data_output { u8 valid; }; +/* Command specific Error Codes (8 bytes) */ +struct hwrm_fw_get_structured_data_cmd_err { + u8 code; + #define FW_GET_STRUCTURED_DATA_CMD_ERR_CODE_UNKNOWN 0x0UL + #define FW_GET_STRUCTURED_DATA_CMD_ERR_CODE_BAD_ID 0x3UL + u8 unused_0[7]; +}; + /* hwrm_exec_fwd_resp */ /* Input (128 bytes) */ struct hwrm_exec_fwd_resp_input { @@ -5280,11 +5698,15 @@ struct hwrm_selftest_qlist_output { #define SELFTEST_QLIST_RESP_AVAILABLE_TESTS_LINK_TEST 0x2UL #define SELFTEST_QLIST_RESP_AVAILABLE_TESTS_REGISTER_TEST 0x4UL #define SELFTEST_QLIST_RESP_AVAILABLE_TESTS_MEMORY_TEST 0x8UL + #define SELFTEST_QLIST_RESP_AVAILABLE_TESTS_PCIE_EYE_TEST 0x10UL + #define SELFTEST_QLIST_RESP_AVAILABLE_TESTS_ETHERNET_EYE_TEST 0x20UL u8 offline_tests; #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_NVM_TEST 0x1UL #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_LINK_TEST 0x2UL #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_REGISTER_TEST 0x4UL #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_MEMORY_TEST 0x8UL + #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_PCIE_EYE_TEST 0x10UL + #define SELFTEST_QLIST_RESP_OFFLINE_TESTS_ETHERNET_EYE_TEST 0x20UL u8 unused_0; __le16 test_timeout; u8 unused_1; @@ -5312,6 +5734,8 @@ struct hwrm_selftest_exec_input { #define SELFTEST_EXEC_REQ_FLAGS_LINK_TEST 0x2UL #define SELFTEST_EXEC_REQ_FLAGS_REGISTER_TEST 0x4UL #define SELFTEST_EXEC_REQ_FLAGS_MEMORY_TEST 0x8UL + #define SELFTEST_EXEC_REQ_FLAGS_PCIE_EYE_TEST 0x10UL + #define SELFTEST_EXEC_REQ_FLAGS_ETHERNET_EYE_TEST 0x20UL u8 unused_0[7]; }; @@ -5326,11 +5750,15 @@ struct hwrm_selftest_exec_output { #define SELFTEST_EXEC_RESP_REQUESTED_TESTS_LINK_TEST 0x2UL #define SELFTEST_EXEC_RESP_REQUESTED_TESTS_REGISTER_TEST 0x4UL #define SELFTEST_EXEC_RESP_REQUESTED_TESTS_MEMORY_TEST 0x8UL + #define SELFTEST_EXEC_RESP_REQUESTED_TESTS_PCIE_EYE_TEST 0x10UL + #define SELFTEST_EXEC_RESP_REQUESTED_TESTS_ETHERNET_EYE_TEST 0x20UL u8 test_success; #define SELFTEST_EXEC_RESP_TEST_SUCCESS_NVM_TEST 0x1UL #define SELFTEST_EXEC_RESP_TEST_SUCCESS_LINK_TEST 0x2UL #define SELFTEST_EXEC_RESP_TEST_SUCCESS_REGISTER_TEST 0x4UL #define SELFTEST_EXEC_RESP_TEST_SUCCESS_MEMORY_TEST 0x8UL + #define SELFTEST_EXEC_RESP_TEST_SUCCESS_PCIE_EYE_TEST 0x10UL + #define SELFTEST_EXEC_RESP_TEST_SUCCESS_ETHERNET_EYE_TEST 0x20UL __le16 unused_0[3]; }; @@ -5411,7 +5839,7 @@ struct cmd_nums { #define HWRM_PORT_LPBK_CLR_STATS (0x26UL) #define HWRM_PORT_PHY_QCFG (0x27UL) #define HWRM_PORT_MAC_QCFG (0x28UL) - #define RESERVED7 (0x29UL) + #define HWRM_PORT_MAC_PTP_QCFG (0x29UL) #define HWRM_PORT_PHY_QCAPS (0x2aUL) #define HWRM_PORT_PHY_I2C_WRITE (0x2bUL) #define HWRM_PORT_PHY_I2C_READ (0x2cUL) @@ -5421,14 +5849,17 @@ struct cmd_nums { #define HWRM_QUEUE_QPORTCFG (0x30UL) #define HWRM_QUEUE_QCFG (0x31UL) #define HWRM_QUEUE_CFG (0x32UL) - #define RESERVED2 (0x33UL) - #define RESERVED3 (0x34UL) + #define HWRM_FUNC_VLAN_CFG (0x33UL) + #define HWRM_FUNC_VLAN_QCFG (0x34UL) #define HWRM_QUEUE_PFCENABLE_QCFG (0x35UL) #define HWRM_QUEUE_PFCENABLE_CFG (0x36UL) #define HWRM_QUEUE_PRI2COS_QCFG (0x37UL) #define HWRM_QUEUE_PRI2COS_CFG (0x38UL) #define HWRM_QUEUE_COS2BW_QCFG (0x39UL) #define HWRM_QUEUE_COS2BW_CFG (0x3aUL) + #define HWRM_QUEUE_DSCP_QCAPS (0x3bUL) + #define HWRM_QUEUE_DSCP2PRI_QCFG (0x3cUL) + #define HWRM_QUEUE_DSCP2PRI_CFG (0x3dUL) #define HWRM_VNIC_ALLOC (0x40UL) #define HWRM_VNIC_FREE (0x41UL) #define HWRM_VNIC_CFG (0x42UL) @@ -5455,7 +5886,7 @@ struct cmd_nums { #define HWRM_CFA_L2_FILTER_FREE (0x91UL) #define HWRM_CFA_L2_FILTER_CFG (0x92UL) #define HWRM_CFA_L2_SET_RX_MASK (0x93UL) - #define RESERVED4 (0x94UL) + #define HWRM_CFA_VLAN_ANTISPOOF_CFG (0x94UL) #define HWRM_CFA_TUNNEL_FILTER_ALLOC (0x95UL) #define HWRM_CFA_TUNNEL_FILTER_FREE (0x96UL) #define HWRM_CFA_ENCAP_RECORD_ALLOC (0x97UL) @@ -5494,6 +5925,8 @@ struct cmd_nums { #define HWRM_CFA_METER_PROFILE_CFG (0xf7UL) #define HWRM_CFA_METER_INSTANCE_ALLOC (0xf8UL) #define HWRM_CFA_METER_INSTANCE_FREE (0xf9UL) + #define HWRM_CFA_VFR_ALLOC (0xfdUL) + #define HWRM_CFA_VFR_FREE (0xfeUL) #define HWRM_CFA_VF_PAIR_ALLOC (0x100UL) #define HWRM_CFA_VF_PAIR_FREE (0x101UL) #define HWRM_CFA_VF_PAIR_INFO (0x102UL) @@ -5502,14 +5935,20 @@ struct cmd_nums { #define HWRM_CFA_FLOW_FLUSH (0x105UL) #define HWRM_CFA_FLOW_STATS (0x106UL) #define HWRM_CFA_FLOW_INFO (0x107UL) + #define HWRM_CFA_DECAP_FILTER_ALLOC (0x108UL) + #define HWRM_CFA_DECAP_FILTER_FREE (0x109UL) + #define HWRM_CFA_VLAN_ANTISPOOF_QCFG (0x10aUL) #define HWRM_SELFTEST_QLIST (0x200UL) #define HWRM_SELFTEST_EXEC (0x201UL) #define HWRM_SELFTEST_IRQ (0x202UL) + #define HWRM_SELFTEST_RETREIVE_EYE_DATA (0x203UL) #define HWRM_DBG_READ_DIRECT (0xff10UL) #define HWRM_DBG_READ_INDIRECT (0xff11UL) #define HWRM_DBG_WRITE_DIRECT (0xff12UL) #define HWRM_DBG_WRITE_INDIRECT (0xff13UL) #define HWRM_DBG_DUMP (0xff14UL) + #define HWRM_DBG_ERASE_NVM (0xff15UL) + #define HWRM_DBG_CFG (0xff16UL) #define HWRM_NVM_FACTORY_DEFAULTS (0xffeeUL) #define HWRM_NVM_VALIDATE_OPTION (0xffefUL) #define HWRM_NVM_FLUSH (0xfff0UL) @@ -5720,6 +6159,7 @@ struct hwrm_struct_hdr { #define STRUCT_HDR_STRUCT_ID_LLDP_DEVICE 0x426UL #define STRUCT_HDR_STRUCT_ID_AFM_OPAQUE 0x1UL #define STRUCT_HDR_STRUCT_ID_PORT_DESCRIPTION 0xaUL + #define STRUCT_HDR_STRUCT_ID_RSS_V2 0x64UL __le16 len; u8 version; u8 count; diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c index b8e7248294d9..d37925a8a65b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_sriov.c @@ -18,6 +18,7 @@ #include "bnxt.h" #include "bnxt_ulp.h" #include "bnxt_sriov.h" +#include "bnxt_vfr.h" #include "bnxt_ethtool.h" #ifdef CONFIG_BNXT_SRIOV @@ -587,6 +588,10 @@ void bnxt_sriov_disable(struct bnxt *bp) if (!num_vfs) return; + /* synchronize VF and VF-rep create and destroy */ + mutex_lock(&bp->sriov_lock); + bnxt_vf_reps_destroy(bp); + if (pci_vfs_assigned(bp->pdev)) { bnxt_hwrm_fwd_async_event_cmpl( bp, NULL, ASYNC_EVENT_CMPL_EVENT_ID_PF_DRVR_UNLOAD); @@ -597,6 +602,7 @@ void bnxt_sriov_disable(struct bnxt *bp) /* Free the HW resources reserved for various VF's */ bnxt_hwrm_func_vf_resource_free(bp, num_vfs); } + mutex_unlock(&bp->sriov_lock); bnxt_free_vf_resources(bp); @@ -794,8 +800,10 @@ static int bnxt_vf_set_link(struct bnxt *bp, struct bnxt_vf_info *vf) PORT_PHY_QCFG_RESP_LINK_LINK; phy_qcfg_resp.link_speed = cpu_to_le16( PORT_PHY_QCFG_RESP_LINK_SPEED_10GB); - phy_qcfg_resp.duplex = - PORT_PHY_QCFG_RESP_DUPLEX_FULL; + phy_qcfg_resp.duplex_cfg = + PORT_PHY_QCFG_RESP_DUPLEX_CFG_FULL; + phy_qcfg_resp.duplex_state = + PORT_PHY_QCFG_RESP_DUPLEX_STATE_FULL; phy_qcfg_resp.pause = (PORT_PHY_QCFG_RESP_PAUSE_TX | PORT_PHY_QCFG_RESP_PAUSE_RX); @@ -804,7 +812,8 @@ static int bnxt_vf_set_link(struct bnxt *bp, struct bnxt_vf_info *vf) /* force link down */ phy_qcfg_resp.link = PORT_PHY_QCFG_RESP_LINK_NO_LINK; phy_qcfg_resp.link_speed = 0; - phy_qcfg_resp.duplex = PORT_PHY_QCFG_RESP_DUPLEX_HALF; + phy_qcfg_resp.duplex_state = + PORT_PHY_QCFG_RESP_DUPLEX_STATE_HALF; phy_qcfg_resp.pause = 0; } rc = bnxt_hwrm_fwd_resp(bp, vf, &phy_qcfg_resp, diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c new file mode 100644 index 000000000000..ccd699fb2d70 --- /dev/null +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.c @@ -0,0 +1,834 @@ +/* Broadcom NetXtreme-C/E network driver. + * + * Copyright (c) 2017 Broadcom Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + */ + +#include <linux/netdevice.h> +#include <linux/inetdevice.h> +#include <linux/if_vlan.h> +#include <net/flow_dissector.h> +#include <net/pkt_cls.h> +#include <net/tc_act/tc_gact.h> +#include <net/tc_act/tc_skbedit.h> +#include <net/tc_act/tc_mirred.h> +#include <net/tc_act/tc_vlan.h> + +#include "bnxt_hsi.h" +#include "bnxt.h" +#include "bnxt_sriov.h" +#include "bnxt_tc.h" +#include "bnxt_vfr.h" + +#ifdef CONFIG_BNXT_FLOWER_OFFLOAD + +#define BNXT_FID_INVALID 0xffff +#define VLAN_TCI(vid, prio) ((vid) | ((prio) << VLAN_PRIO_SHIFT)) + +/* Return the dst fid of the func for flow forwarding + * For PFs: src_fid is the fid of the PF + * For VF-reps: src_fid the fid of the VF + */ +static u16 bnxt_flow_get_dst_fid(struct bnxt *pf_bp, struct net_device *dev) +{ + struct bnxt *bp; + + /* check if dev belongs to the same switch */ + if (!switchdev_port_same_parent_id(pf_bp->dev, dev)) { + netdev_info(pf_bp->dev, "dev(ifindex=%d) not on same switch", + dev->ifindex); + return BNXT_FID_INVALID; + } + + /* Is dev a VF-rep? */ + if (dev != pf_bp->dev) + return bnxt_vf_rep_get_fid(dev); + + bp = netdev_priv(dev); + return bp->pf.fw_fid; +} + +static int bnxt_tc_parse_redir(struct bnxt *bp, + struct bnxt_tc_actions *actions, + const struct tc_action *tc_act) +{ + int ifindex = tcf_mirred_ifindex(tc_act); + struct net_device *dev; + u16 dst_fid; + + dev = __dev_get_by_index(dev_net(bp->dev), ifindex); + if (!dev) { + netdev_info(bp->dev, "no dev for ifindex=%d", ifindex); + return -EINVAL; + } + + /* find the FID from dev */ + dst_fid = bnxt_flow_get_dst_fid(bp, dev); + if (dst_fid == BNXT_FID_INVALID) { + netdev_info(bp->dev, "can't get fid for ifindex=%d", ifindex); + return -EINVAL; + } + + actions->flags |= BNXT_TC_ACTION_FLAG_FWD; + actions->dst_fid = dst_fid; + actions->dst_dev = dev; + return 0; +} + +static void bnxt_tc_parse_vlan(struct bnxt *bp, + struct bnxt_tc_actions *actions, + const struct tc_action *tc_act) +{ + if (tcf_vlan_action(tc_act) == TCA_VLAN_ACT_POP) { + actions->flags |= BNXT_TC_ACTION_FLAG_POP_VLAN; + } else if (tcf_vlan_action(tc_act) == TCA_VLAN_ACT_PUSH) { + actions->flags |= BNXT_TC_ACTION_FLAG_PUSH_VLAN; + actions->push_vlan_tci = htons(tcf_vlan_push_vid(tc_act)); + actions->push_vlan_tpid = tcf_vlan_push_proto(tc_act); + } +} + +static int bnxt_tc_parse_actions(struct bnxt *bp, + struct bnxt_tc_actions *actions, + struct tcf_exts *tc_exts) +{ + const struct tc_action *tc_act; + LIST_HEAD(tc_actions); + int rc; + + if (!tcf_exts_has_actions(tc_exts)) { + netdev_info(bp->dev, "no actions"); + return -EINVAL; + } + + tcf_exts_to_list(tc_exts, &tc_actions); + list_for_each_entry(tc_act, &tc_actions, list) { + /* Drop action */ + if (is_tcf_gact_shot(tc_act)) { + actions->flags |= BNXT_TC_ACTION_FLAG_DROP; + return 0; /* don't bother with other actions */ + } + + /* Redirect action */ + if (is_tcf_mirred_egress_redirect(tc_act)) { + rc = bnxt_tc_parse_redir(bp, actions, tc_act); + if (rc) + return rc; + continue; + } + + /* Push/pop VLAN */ + if (is_tcf_vlan(tc_act)) { + bnxt_tc_parse_vlan(bp, actions, tc_act); + continue; + } + } + + return 0; +} + +#define GET_KEY(flow_cmd, key_type) \ + skb_flow_dissector_target((flow_cmd)->dissector, key_type,\ + (flow_cmd)->key) +#define GET_MASK(flow_cmd, key_type) \ + skb_flow_dissector_target((flow_cmd)->dissector, key_type,\ + (flow_cmd)->mask) + +static int bnxt_tc_parse_flow(struct bnxt *bp, + struct tc_cls_flower_offload *tc_flow_cmd, + struct bnxt_tc_flow *flow) +{ + struct flow_dissector *dissector = tc_flow_cmd->dissector; + u16 addr_type = 0; + + /* KEY_CONTROL and KEY_BASIC are needed for forming a meaningful key */ + if ((dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_CONTROL)) == 0 || + (dissector->used_keys & BIT(FLOW_DISSECTOR_KEY_BASIC)) == 0) { + netdev_info(bp->dev, "cannot form TC key: used_keys = 0x%x", + dissector->used_keys); + return -EOPNOTSUPP; + } + + if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_CONTROL)) { + struct flow_dissector_key_control *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_CONTROL); + + addr_type = key->addr_type; + } + + if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_BASIC)) { + struct flow_dissector_key_basic *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_BASIC); + struct flow_dissector_key_basic *mask = + GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_BASIC); + + flow->l2_key.ether_type = key->n_proto; + flow->l2_mask.ether_type = mask->n_proto; + + if (key->n_proto == htons(ETH_P_IP) || + key->n_proto == htons(ETH_P_IPV6)) { + flow->l4_key.ip_proto = key->ip_proto; + flow->l4_mask.ip_proto = mask->ip_proto; + } + } + + if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + struct flow_dissector_key_eth_addrs *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ETH_ADDRS); + struct flow_dissector_key_eth_addrs *mask = + GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_ETH_ADDRS); + + flow->flags |= BNXT_TC_FLOW_FLAGS_ETH_ADDRS; + ether_addr_copy(flow->l2_key.dmac, key->dst); + ether_addr_copy(flow->l2_mask.dmac, mask->dst); + ether_addr_copy(flow->l2_key.smac, key->src); + ether_addr_copy(flow->l2_mask.smac, mask->src); + } + + if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_VLAN)) { + struct flow_dissector_key_vlan *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_VLAN); + struct flow_dissector_key_vlan *mask = + GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_VLAN); + + flow->l2_key.inner_vlan_tci = + cpu_to_be16(VLAN_TCI(key->vlan_id, key->vlan_priority)); + flow->l2_mask.inner_vlan_tci = + cpu_to_be16((VLAN_TCI(mask->vlan_id, mask->vlan_priority))); + flow->l2_key.inner_vlan_tpid = htons(ETH_P_8021Q); + flow->l2_mask.inner_vlan_tpid = htons(0xffff); + flow->l2_key.num_vlans = 1; + } + + if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) { + struct flow_dissector_key_ipv4_addrs *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV4_ADDRS); + struct flow_dissector_key_ipv4_addrs *mask = + GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV4_ADDRS); + + flow->flags |= BNXT_TC_FLOW_FLAGS_IPV4_ADDRS; + flow->l3_key.ipv4.daddr.s_addr = key->dst; + flow->l3_mask.ipv4.daddr.s_addr = mask->dst; + flow->l3_key.ipv4.saddr.s_addr = key->src; + flow->l3_mask.ipv4.saddr.s_addr = mask->src; + } else if (dissector_uses_key(dissector, + FLOW_DISSECTOR_KEY_IPV6_ADDRS)) { + struct flow_dissector_key_ipv6_addrs *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV6_ADDRS); + struct flow_dissector_key_ipv6_addrs *mask = + GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_IPV6_ADDRS); + + flow->flags |= BNXT_TC_FLOW_FLAGS_IPV6_ADDRS; + flow->l3_key.ipv6.daddr = key->dst; + flow->l3_mask.ipv6.daddr = mask->dst; + flow->l3_key.ipv6.saddr = key->src; + flow->l3_mask.ipv6.saddr = mask->src; + } + + if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_PORTS)) { + struct flow_dissector_key_ports *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_PORTS); + struct flow_dissector_key_ports *mask = + GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_PORTS); + + flow->flags |= BNXT_TC_FLOW_FLAGS_PORTS; + flow->l4_key.ports.dport = key->dst; + flow->l4_mask.ports.dport = mask->dst; + flow->l4_key.ports.sport = key->src; + flow->l4_mask.ports.sport = mask->src; + } + + if (dissector_uses_key(dissector, FLOW_DISSECTOR_KEY_ICMP)) { + struct flow_dissector_key_icmp *key = + GET_KEY(tc_flow_cmd, FLOW_DISSECTOR_KEY_ICMP); + struct flow_dissector_key_icmp *mask = + GET_MASK(tc_flow_cmd, FLOW_DISSECTOR_KEY_ICMP); + + flow->flags |= BNXT_TC_FLOW_FLAGS_ICMP; + flow->l4_key.icmp.type = key->type; + flow->l4_key.icmp.code = key->code; + flow->l4_mask.icmp.type = mask->type; + flow->l4_mask.icmp.code = mask->code; + } + + return bnxt_tc_parse_actions(bp, &flow->actions, tc_flow_cmd->exts); +} + +static int bnxt_hwrm_cfa_flow_free(struct bnxt *bp, __le16 flow_handle) +{ + struct hwrm_cfa_flow_free_input req = { 0 }; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_FREE, -1, -1); + req.flow_handle = flow_handle; + + rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (rc) + netdev_info(bp->dev, "Error: %s: flow_handle=0x%x rc=%d", + __func__, flow_handle, rc); + return rc; +} + +static int ipv6_mask_len(struct in6_addr *mask) +{ + int mask_len = 0, i; + + for (i = 0; i < 4; i++) + mask_len += inet_mask_len(mask->s6_addr32[i]); + + return mask_len; +} + +static bool is_wildcard(void *mask, int len) +{ + const u8 *p = mask; + int i; + + for (i = 0; i < len; i++) { + if (p[i] != 0) + return false; + } + return true; +} + +static int bnxt_hwrm_cfa_flow_alloc(struct bnxt *bp, struct bnxt_tc_flow *flow, + __le16 ref_flow_handle, __le16 *flow_handle) +{ + struct hwrm_cfa_flow_alloc_output *resp = bp->hwrm_cmd_resp_addr; + struct bnxt_tc_actions *actions = &flow->actions; + struct bnxt_tc_l3_key *l3_mask = &flow->l3_mask; + struct bnxt_tc_l3_key *l3_key = &flow->l3_key; + struct hwrm_cfa_flow_alloc_input req = { 0 }; + u16 flow_flags = 0, action_flags = 0; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_ALLOC, -1, -1); + + req.src_fid = cpu_to_le16(flow->src_fid); + req.ref_flow_handle = ref_flow_handle; + req.ethertype = flow->l2_key.ether_type; + req.ip_proto = flow->l4_key.ip_proto; + + if (flow->flags & BNXT_TC_FLOW_FLAGS_ETH_ADDRS) { + memcpy(req.dmac, flow->l2_key.dmac, ETH_ALEN); + memcpy(req.smac, flow->l2_key.smac, ETH_ALEN); + } + + if (flow->l2_key.num_vlans > 0) { + flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_NUM_VLAN_ONE; + /* FW expects the inner_vlan_tci value to be set + * in outer_vlan_tci when num_vlans is 1 (which is + * always the case in TC.) + */ + req.outer_vlan_tci = flow->l2_key.inner_vlan_tci; + } + + /* If all IP and L4 fields are wildcarded then this is an L2 flow */ + if (is_wildcard(&l3_mask, sizeof(l3_mask)) && + is_wildcard(&flow->l4_mask, sizeof(flow->l4_mask))) { + flow_flags |= CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_L2; + } else { + flow_flags |= flow->l2_key.ether_type == htons(ETH_P_IP) ? + CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV4 : + CFA_FLOW_ALLOC_REQ_FLAGS_FLOWTYPE_IPV6; + + if (flow->flags & BNXT_TC_FLOW_FLAGS_IPV4_ADDRS) { + req.ip_dst[0] = l3_key->ipv4.daddr.s_addr; + req.ip_dst_mask_len = + inet_mask_len(l3_mask->ipv4.daddr.s_addr); + req.ip_src[0] = l3_key->ipv4.saddr.s_addr; + req.ip_src_mask_len = + inet_mask_len(l3_mask->ipv4.saddr.s_addr); + } else if (flow->flags & BNXT_TC_FLOW_FLAGS_IPV6_ADDRS) { + memcpy(req.ip_dst, l3_key->ipv6.daddr.s6_addr32, + sizeof(req.ip_dst)); + req.ip_dst_mask_len = + ipv6_mask_len(&l3_mask->ipv6.daddr); + memcpy(req.ip_src, l3_key->ipv6.saddr.s6_addr32, + sizeof(req.ip_src)); + req.ip_src_mask_len = + ipv6_mask_len(&l3_mask->ipv6.saddr); + } + } + + if (flow->flags & BNXT_TC_FLOW_FLAGS_PORTS) { + req.l4_src_port = flow->l4_key.ports.sport; + req.l4_src_port_mask = flow->l4_mask.ports.sport; + req.l4_dst_port = flow->l4_key.ports.dport; + req.l4_dst_port_mask = flow->l4_mask.ports.dport; + } else if (flow->flags & BNXT_TC_FLOW_FLAGS_ICMP) { + /* l4 ports serve as type/code when ip_proto is ICMP */ + req.l4_src_port = htons(flow->l4_key.icmp.type); + req.l4_src_port_mask = htons(flow->l4_mask.icmp.type); + req.l4_dst_port = htons(flow->l4_key.icmp.code); + req.l4_dst_port_mask = htons(flow->l4_mask.icmp.code); + } + req.flags = cpu_to_le16(flow_flags); + + if (actions->flags & BNXT_TC_ACTION_FLAG_DROP) { + action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_DROP; + } else { + if (actions->flags & BNXT_TC_ACTION_FLAG_FWD) { + action_flags |= CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_FWD; + req.dst_fid = cpu_to_le16(actions->dst_fid); + } + if (actions->flags & BNXT_TC_ACTION_FLAG_PUSH_VLAN) { + action_flags |= + CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE; + req.l2_rewrite_vlan_tpid = actions->push_vlan_tpid; + req.l2_rewrite_vlan_tci = actions->push_vlan_tci; + memcpy(&req.l2_rewrite_dmac, &req.dmac, ETH_ALEN); + memcpy(&req.l2_rewrite_smac, &req.smac, ETH_ALEN); + } + if (actions->flags & BNXT_TC_ACTION_FLAG_POP_VLAN) { + action_flags |= + CFA_FLOW_ALLOC_REQ_ACTION_FLAGS_L2_HEADER_REWRITE; + /* Rewrite config with tpid = 0 implies vlan pop */ + req.l2_rewrite_vlan_tpid = 0; + memcpy(&req.l2_rewrite_dmac, &req.dmac, ETH_ALEN); + memcpy(&req.l2_rewrite_smac, &req.smac, ETH_ALEN); + } + } + req.action_flags = cpu_to_le16(action_flags); + + mutex_lock(&bp->hwrm_cmd_lock); + + rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (!rc) + *flow_handle = resp->flow_handle; + + mutex_unlock(&bp->hwrm_cmd_lock); + + return rc; +} + +/* Add val to accum while handling a possible wraparound + * of val. Eventhough val is of type u64, its actual width + * is denoted by mask and will wrap-around beyond that width. + */ +static void accumulate_val(u64 *accum, u64 val, u64 mask) +{ +#define low_bits(x, mask) ((x) & (mask)) +#define high_bits(x, mask) ((x) & ~(mask)) + bool wrapped = val < low_bits(*accum, mask); + + *accum = high_bits(*accum, mask) + val; + if (wrapped) + *accum += (mask + 1); +} + +/* The HW counters' width is much less than 64bits. + * Handle possible wrap-around while updating the stat counters + */ +static void bnxt_flow_stats_fix_wraparound(struct bnxt_tc_info *tc_info, + struct bnxt_tc_flow_stats *stats, + struct bnxt_tc_flow_stats *hw_stats) +{ + accumulate_val(&stats->bytes, hw_stats->bytes, tc_info->bytes_mask); + accumulate_val(&stats->packets, hw_stats->packets, + tc_info->packets_mask); +} + +/* Fix possible wraparound of the stats queried from HW, calculate + * the delta from prev_stats, and also update the prev_stats. + * The HW flow stats are fetched under the hwrm_cmd_lock mutex. + * This routine is best called while under the mutex so that the + * stats processing happens atomically. + */ +static void bnxt_flow_stats_calc(struct bnxt_tc_info *tc_info, + struct bnxt_tc_flow *flow, + struct bnxt_tc_flow_stats *stats) +{ + struct bnxt_tc_flow_stats *acc_stats, *prev_stats; + + acc_stats = &flow->stats; + bnxt_flow_stats_fix_wraparound(tc_info, acc_stats, stats); + + prev_stats = &flow->prev_stats; + stats->bytes = acc_stats->bytes - prev_stats->bytes; + stats->packets = acc_stats->packets - prev_stats->packets; + *prev_stats = *acc_stats; +} + +static int bnxt_hwrm_cfa_flow_stats_get(struct bnxt *bp, + __le16 flow_handle, + struct bnxt_tc_flow *flow, + struct bnxt_tc_flow_stats *stats) +{ + struct hwrm_cfa_flow_stats_output *resp = bp->hwrm_cmd_resp_addr; + struct hwrm_cfa_flow_stats_input req = { 0 }; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_FLOW_STATS, -1, -1); + req.num_flows = cpu_to_le16(1); + req.flow_handle_0 = flow_handle; + + mutex_lock(&bp->hwrm_cmd_lock); + rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (!rc) { + stats->packets = le64_to_cpu(resp->packet_0); + stats->bytes = le64_to_cpu(resp->byte_0); + bnxt_flow_stats_calc(&bp->tc_info, flow, stats); + } else { + netdev_info(bp->dev, "error rc=%d", rc); + } + + mutex_unlock(&bp->hwrm_cmd_lock); + return rc; +} + +static int bnxt_tc_put_l2_node(struct bnxt *bp, + struct bnxt_tc_flow_node *flow_node) +{ + struct bnxt_tc_l2_node *l2_node = flow_node->l2_node; + struct bnxt_tc_info *tc_info = &bp->tc_info; + int rc; + + /* remove flow_node from the L2 shared flow list */ + list_del(&flow_node->l2_list_node); + if (--l2_node->refcount == 0) { + rc = rhashtable_remove_fast(&tc_info->l2_table, &l2_node->node, + tc_info->l2_ht_params); + if (rc) + netdev_err(bp->dev, + "Error: %s: rhashtable_remove_fast: %d", + __func__, rc); + kfree_rcu(l2_node, rcu); + } + return 0; +} + +static struct bnxt_tc_l2_node * +bnxt_tc_get_l2_node(struct bnxt *bp, struct rhashtable *l2_table, + struct rhashtable_params ht_params, + struct bnxt_tc_l2_key *l2_key) +{ + struct bnxt_tc_l2_node *l2_node; + int rc; + + l2_node = rhashtable_lookup_fast(l2_table, l2_key, ht_params); + if (!l2_node) { + l2_node = kzalloc(sizeof(*l2_node), GFP_KERNEL); + if (!l2_node) { + rc = -ENOMEM; + return NULL; + } + + l2_node->key = *l2_key; + rc = rhashtable_insert_fast(l2_table, &l2_node->node, + ht_params); + if (rc) { + kfree(l2_node); + netdev_err(bp->dev, + "Error: %s: rhashtable_insert_fast: %d", + __func__, rc); + return NULL; + } + INIT_LIST_HEAD(&l2_node->common_l2_flows); + } + return l2_node; +} + +/* Get the ref_flow_handle for a flow by checking if there are any other + * flows that share the same L2 key as this flow. + */ +static int +bnxt_tc_get_ref_flow_handle(struct bnxt *bp, struct bnxt_tc_flow *flow, + struct bnxt_tc_flow_node *flow_node, + __le16 *ref_flow_handle) +{ + struct bnxt_tc_info *tc_info = &bp->tc_info; + struct bnxt_tc_flow_node *ref_flow_node; + struct bnxt_tc_l2_node *l2_node; + + l2_node = bnxt_tc_get_l2_node(bp, &tc_info->l2_table, + tc_info->l2_ht_params, + &flow->l2_key); + if (!l2_node) + return -1; + + /* If any other flow is using this l2_node, use it's flow_handle + * as the ref_flow_handle + */ + if (l2_node->refcount > 0) { + ref_flow_node = list_first_entry(&l2_node->common_l2_flows, + struct bnxt_tc_flow_node, + l2_list_node); + *ref_flow_handle = ref_flow_node->flow_handle; + } else { + *ref_flow_handle = cpu_to_le16(0xffff); + } + + /* Insert the l2_node into the flow_node so that subsequent flows + * with a matching l2 key can use the flow_handle of this flow + * as their ref_flow_handle + */ + flow_node->l2_node = l2_node; + list_add(&flow_node->l2_list_node, &l2_node->common_l2_flows); + l2_node->refcount++; + return 0; +} + +/* After the flow parsing is done, this routine is used for checking + * if there are any aspects of the flow that prevent it from being + * offloaded. + */ +static bool bnxt_tc_can_offload(struct bnxt *bp, struct bnxt_tc_flow *flow) +{ + /* If L4 ports are specified then ip_proto must be TCP or UDP */ + if ((flow->flags & BNXT_TC_FLOW_FLAGS_PORTS) && + (flow->l4_key.ip_proto != IPPROTO_TCP && + flow->l4_key.ip_proto != IPPROTO_UDP)) { + netdev_info(bp->dev, "Cannot offload non-TCP/UDP (%d) ports", + flow->l4_key.ip_proto); + return false; + } + + return true; +} + +static int __bnxt_tc_del_flow(struct bnxt *bp, + struct bnxt_tc_flow_node *flow_node) +{ + struct bnxt_tc_info *tc_info = &bp->tc_info; + int rc; + + /* send HWRM cmd to free the flow-id */ + bnxt_hwrm_cfa_flow_free(bp, flow_node->flow_handle); + + mutex_lock(&tc_info->lock); + + /* release reference to l2 node */ + bnxt_tc_put_l2_node(bp, flow_node); + + mutex_unlock(&tc_info->lock); + + rc = rhashtable_remove_fast(&tc_info->flow_table, &flow_node->node, + tc_info->flow_ht_params); + if (rc) + netdev_err(bp->dev, "Error: %s: rhashtable_remove_fast rc=%d", + __func__, rc); + + kfree_rcu(flow_node, rcu); + return 0; +} + +/* Add a new flow or replace an existing flow. + * Notes on locking: + * There are essentially two critical sections here. + * 1. while adding a new flow + * a) lookup l2-key + * b) issue HWRM cmd and get flow_handle + * c) link l2-key with flow + * 2. while deleting a flow + * a) unlinking l2-key from flow + * A lock is needed to protect these two critical sections. + * + * The hash-tables are already protected by the rhashtable API. + */ +static int bnxt_tc_add_flow(struct bnxt *bp, u16 src_fid, + struct tc_cls_flower_offload *tc_flow_cmd) +{ + struct bnxt_tc_flow_node *new_node, *old_node; + struct bnxt_tc_info *tc_info = &bp->tc_info; + struct bnxt_tc_flow *flow; + __le16 ref_flow_handle; + int rc; + + /* allocate memory for the new flow and it's node */ + new_node = kzalloc(sizeof(*new_node), GFP_KERNEL); + if (!new_node) { + rc = -ENOMEM; + goto done; + } + new_node->cookie = tc_flow_cmd->cookie; + flow = &new_node->flow; + + rc = bnxt_tc_parse_flow(bp, tc_flow_cmd, flow); + if (rc) + goto free_node; + flow->src_fid = src_fid; + + if (!bnxt_tc_can_offload(bp, flow)) { + rc = -ENOSPC; + goto free_node; + } + + /* If a flow exists with the same cookie, delete it */ + old_node = rhashtable_lookup_fast(&tc_info->flow_table, + &tc_flow_cmd->cookie, + tc_info->flow_ht_params); + if (old_node) + __bnxt_tc_del_flow(bp, old_node); + + /* Check if the L2 part of the flow has been offloaded already. + * If so, bump up it's refcnt and get it's reference handle. + */ + mutex_lock(&tc_info->lock); + rc = bnxt_tc_get_ref_flow_handle(bp, flow, new_node, &ref_flow_handle); + if (rc) + goto unlock; + + /* send HWRM cmd to alloc the flow */ + rc = bnxt_hwrm_cfa_flow_alloc(bp, flow, ref_flow_handle, + &new_node->flow_handle); + if (rc) + goto put_l2; + + /* add new flow to flow-table */ + rc = rhashtable_insert_fast(&tc_info->flow_table, &new_node->node, + tc_info->flow_ht_params); + if (rc) + goto hwrm_flow_free; + + mutex_unlock(&tc_info->lock); + return 0; + +hwrm_flow_free: + bnxt_hwrm_cfa_flow_free(bp, new_node->flow_handle); +put_l2: + bnxt_tc_put_l2_node(bp, new_node); +unlock: + mutex_unlock(&tc_info->lock); +free_node: + kfree(new_node); +done: + netdev_err(bp->dev, "Error: %s: cookie=0x%lx error=%d", + __func__, tc_flow_cmd->cookie, rc); + return rc; +} + +static int bnxt_tc_del_flow(struct bnxt *bp, + struct tc_cls_flower_offload *tc_flow_cmd) +{ + struct bnxt_tc_info *tc_info = &bp->tc_info; + struct bnxt_tc_flow_node *flow_node; + + flow_node = rhashtable_lookup_fast(&tc_info->flow_table, + &tc_flow_cmd->cookie, + tc_info->flow_ht_params); + if (!flow_node) { + netdev_info(bp->dev, "ERROR: no flow_node for cookie %lx", + tc_flow_cmd->cookie); + return -EINVAL; + } + + return __bnxt_tc_del_flow(bp, flow_node); +} + +static int bnxt_tc_get_flow_stats(struct bnxt *bp, + struct tc_cls_flower_offload *tc_flow_cmd) +{ + struct bnxt_tc_info *tc_info = &bp->tc_info; + struct bnxt_tc_flow_node *flow_node; + struct bnxt_tc_flow_stats stats; + int rc; + + flow_node = rhashtable_lookup_fast(&tc_info->flow_table, + &tc_flow_cmd->cookie, + tc_info->flow_ht_params); + if (!flow_node) { + netdev_info(bp->dev, "Error: no flow_node for cookie %lx", + tc_flow_cmd->cookie); + return -1; + } + + rc = bnxt_hwrm_cfa_flow_stats_get(bp, flow_node->flow_handle, + &flow_node->flow, &stats); + if (rc) + return rc; + + tcf_exts_stats_update(tc_flow_cmd->exts, stats.bytes, stats.packets, 0); + return 0; +} + +int bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid, + struct tc_cls_flower_offload *cls_flower) +{ + int rc = 0; + + switch (cls_flower->command) { + case TC_CLSFLOWER_REPLACE: + rc = bnxt_tc_add_flow(bp, src_fid, cls_flower); + break; + + case TC_CLSFLOWER_DESTROY: + rc = bnxt_tc_del_flow(bp, cls_flower); + break; + + case TC_CLSFLOWER_STATS: + rc = bnxt_tc_get_flow_stats(bp, cls_flower); + break; + } + return rc; +} + +static const struct rhashtable_params bnxt_tc_flow_ht_params = { + .head_offset = offsetof(struct bnxt_tc_flow_node, node), + .key_offset = offsetof(struct bnxt_tc_flow_node, cookie), + .key_len = sizeof(((struct bnxt_tc_flow_node *)0)->cookie), + .automatic_shrinking = true +}; + +static const struct rhashtable_params bnxt_tc_l2_ht_params = { + .head_offset = offsetof(struct bnxt_tc_l2_node, node), + .key_offset = offsetof(struct bnxt_tc_l2_node, key), + .key_len = BNXT_TC_L2_KEY_LEN, + .automatic_shrinking = true +}; + +/* convert counter width in bits to a mask */ +#define mask(width) ((u64)~0 >> (64 - (width))) + +int bnxt_init_tc(struct bnxt *bp) +{ + struct bnxt_tc_info *tc_info = &bp->tc_info; + int rc; + + if (bp->hwrm_spec_code < 0x10800) { + netdev_warn(bp->dev, + "Firmware does not support TC flower offload.\n"); + return -ENOTSUPP; + } + mutex_init(&tc_info->lock); + + /* Counter widths are programmed by FW */ + tc_info->bytes_mask = mask(36); + tc_info->packets_mask = mask(28); + + tc_info->flow_ht_params = bnxt_tc_flow_ht_params; + rc = rhashtable_init(&tc_info->flow_table, &tc_info->flow_ht_params); + if (rc) + return rc; + + tc_info->l2_ht_params = bnxt_tc_l2_ht_params; + rc = rhashtable_init(&tc_info->l2_table, &tc_info->l2_ht_params); + if (rc) + goto destroy_flow_table; + + tc_info->enabled = true; + bp->dev->hw_features |= NETIF_F_HW_TC; + bp->dev->features |= NETIF_F_HW_TC; + return 0; + +destroy_flow_table: + rhashtable_destroy(&tc_info->flow_table); + return rc; +} + +void bnxt_shutdown_tc(struct bnxt *bp) +{ + struct bnxt_tc_info *tc_info = &bp->tc_info; + + if (!tc_info->enabled) + return; + + rhashtable_destroy(&tc_info->flow_table); + rhashtable_destroy(&tc_info->l2_table); +} + +#else +#endif diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h new file mode 100644 index 000000000000..6c4c1ed279ef --- /dev/null +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_tc.h @@ -0,0 +1,158 @@ +/* Broadcom NetXtreme-C/E network driver. + * + * Copyright (c) 2017 Broadcom Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + */ + +#ifndef BNXT_TC_H +#define BNXT_TC_H + +#ifdef CONFIG_BNXT_FLOWER_OFFLOAD + +/* Structs used for storing the filter/actions of the TC cmd. + */ +struct bnxt_tc_l2_key { + u8 dmac[ETH_ALEN]; + u8 smac[ETH_ALEN]; + __be16 inner_vlan_tpid; + __be16 inner_vlan_tci; + __be16 ether_type; + u8 num_vlans; +}; + +struct bnxt_tc_l3_key { + union { + struct { + struct in_addr daddr; + struct in_addr saddr; + } ipv4; + struct { + struct in6_addr daddr; + struct in6_addr saddr; + } ipv6; + }; +}; + +struct bnxt_tc_l4_key { + u8 ip_proto; + union { + struct { + __be16 sport; + __be16 dport; + } ports; + struct { + u8 type; + u8 code; + } icmp; + }; +}; + +struct bnxt_tc_actions { + u32 flags; +#define BNXT_TC_ACTION_FLAG_FWD BIT(0) +#define BNXT_TC_ACTION_FLAG_FWD_VXLAN BIT(1) +#define BNXT_TC_ACTION_FLAG_PUSH_VLAN BIT(3) +#define BNXT_TC_ACTION_FLAG_POP_VLAN BIT(4) +#define BNXT_TC_ACTION_FLAG_DROP BIT(5) + + u16 dst_fid; + struct net_device *dst_dev; + __be16 push_vlan_tpid; + __be16 push_vlan_tci; +}; + +struct bnxt_tc_flow_stats { + u64 packets; + u64 bytes; +}; + +struct bnxt_tc_flow { + u32 flags; +#define BNXT_TC_FLOW_FLAGS_ETH_ADDRS BIT(1) +#define BNXT_TC_FLOW_FLAGS_IPV4_ADDRS BIT(2) +#define BNXT_TC_FLOW_FLAGS_IPV6_ADDRS BIT(3) +#define BNXT_TC_FLOW_FLAGS_PORTS BIT(4) +#define BNXT_TC_FLOW_FLAGS_ICMP BIT(5) + + /* flow applicable to pkts ingressing on this fid */ + u16 src_fid; + struct bnxt_tc_l2_key l2_key; + struct bnxt_tc_l2_key l2_mask; + struct bnxt_tc_l3_key l3_key; + struct bnxt_tc_l3_key l3_mask; + struct bnxt_tc_l4_key l4_key; + struct bnxt_tc_l4_key l4_mask; + + struct bnxt_tc_actions actions; + + /* updated stats accounting for hw-counter wrap-around */ + struct bnxt_tc_flow_stats stats; + /* previous snap-shot of stats */ + struct bnxt_tc_flow_stats prev_stats; + unsigned long lastused; /* jiffies */ +}; + +/* L2 hash table + * This data-struct is used for L2-flow table. + * The L2 part of a flow is stored in a hash table. + * A flow that shares the same L2 key/mask with an + * already existing flow must refer to it's flow handle. + */ +struct bnxt_tc_l2_node { + /* hash key: first 16b of key */ +#define BNXT_TC_L2_KEY_LEN 16 + struct bnxt_tc_l2_key key; + struct rhash_head node; + + /* a linked list of flows that share the same l2 key */ + struct list_head common_l2_flows; + + /* number of flows sharing the l2 key */ + u16 refcount; + + struct rcu_head rcu; +}; + +struct bnxt_tc_flow_node { + /* hash key: provided by TC */ + unsigned long cookie; + struct rhash_head node; + + struct bnxt_tc_flow flow; + + __le16 flow_handle; + + /* L2 node in l2 hashtable that shares flow's l2 key */ + struct bnxt_tc_l2_node *l2_node; + /* for the shared_flows list maintained in l2_node */ + struct list_head l2_list_node; + + struct rcu_head rcu; +}; + +int bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid, + struct tc_cls_flower_offload *cls_flower); +int bnxt_init_tc(struct bnxt *bp); +void bnxt_shutdown_tc(struct bnxt *bp); + +#else /* CONFIG_BNXT_FLOWER_OFFLOAD */ + +static inline int bnxt_tc_setup_flower(struct bnxt *bp, u16 src_fid, + struct tc_cls_flower_offload *cls_flower) +{ + return -EOPNOTSUPP; +} + +static inline int bnxt_init_tc(struct bnxt *bp) +{ + return 0; +} + +static inline void bnxt_shutdown_tc(struct bnxt *bp) +{ +} +#endif /* CONFIG_BNXT_FLOWER_OFFLOAD */ +#endif /* BNXT_TC_H */ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c new file mode 100644 index 000000000000..e75db04c6cdc --- /dev/null +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.c @@ -0,0 +1,513 @@ +/* Broadcom NetXtreme-C/E network driver. + * + * Copyright (c) 2016-2017 Broadcom Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + */ +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/rtnetlink.h> +#include <linux/jhash.h> +#include <net/pkt_cls.h> + +#include "bnxt_hsi.h" +#include "bnxt.h" +#include "bnxt_vfr.h" +#include "bnxt_tc.h" + +#ifdef CONFIG_BNXT_SRIOV + +#define CFA_HANDLE_INVALID 0xffff +#define VF_IDX_INVALID 0xffff + +static int hwrm_cfa_vfr_alloc(struct bnxt *bp, u16 vf_idx, + u16 *tx_cfa_action, u16 *rx_cfa_code) +{ + struct hwrm_cfa_vfr_alloc_output *resp = bp->hwrm_cmd_resp_addr; + struct hwrm_cfa_vfr_alloc_input req = { 0 }; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_VFR_ALLOC, -1, -1); + req.vf_id = cpu_to_le16(vf_idx); + sprintf(req.vfr_name, "vfr%d", vf_idx); + + mutex_lock(&bp->hwrm_cmd_lock); + rc = _hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (!rc) { + *tx_cfa_action = le16_to_cpu(resp->tx_cfa_action); + *rx_cfa_code = le16_to_cpu(resp->rx_cfa_code); + netdev_dbg(bp->dev, "tx_cfa_action=0x%x, rx_cfa_code=0x%x", + *tx_cfa_action, *rx_cfa_code); + } else { + netdev_info(bp->dev, "%s error rc=%d", __func__, rc); + } + + mutex_unlock(&bp->hwrm_cmd_lock); + return rc; +} + +static int hwrm_cfa_vfr_free(struct bnxt *bp, u16 vf_idx) +{ + struct hwrm_cfa_vfr_free_input req = { 0 }; + int rc; + + bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_CFA_VFR_FREE, -1, -1); + sprintf(req.vfr_name, "vfr%d", vf_idx); + + rc = hwrm_send_message(bp, &req, sizeof(req), HWRM_CMD_TIMEOUT); + if (rc) + netdev_info(bp->dev, "%s error rc=%d", __func__, rc); + return rc; +} + +static int bnxt_vf_rep_open(struct net_device *dev) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + struct bnxt *bp = vf_rep->bp; + + /* Enable link and TX only if the parent PF is open. */ + if (netif_running(bp->dev)) { + netif_carrier_on(dev); + netif_tx_start_all_queues(dev); + } + return 0; +} + +static int bnxt_vf_rep_close(struct net_device *dev) +{ + netif_carrier_off(dev); + netif_tx_disable(dev); + + return 0; +} + +static netdev_tx_t bnxt_vf_rep_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + int rc, len = skb->len; + + skb_dst_drop(skb); + dst_hold((struct dst_entry *)vf_rep->dst); + skb_dst_set(skb, (struct dst_entry *)vf_rep->dst); + skb->dev = vf_rep->dst->u.port_info.lower_dev; + + rc = dev_queue_xmit(skb); + if (!rc) { + vf_rep->tx_stats.packets++; + vf_rep->tx_stats.bytes += len; + } + return rc; +} + +static void +bnxt_vf_rep_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + + stats->rx_packets = vf_rep->rx_stats.packets; + stats->rx_bytes = vf_rep->rx_stats.bytes; + stats->tx_packets = vf_rep->tx_stats.packets; + stats->tx_bytes = vf_rep->tx_stats.bytes; +} + +static int bnxt_vf_rep_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + struct bnxt *bp = vf_rep->bp; + int vf_fid = bp->pf.vf[vf_rep->vf_idx].fw_fid; + + switch (type) { + case TC_SETUP_CLSFLOWER: + return bnxt_tc_setup_flower(bp, vf_fid, type_data); + default: + return -EOPNOTSUPP; + } +} + +struct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code) +{ + u16 vf_idx; + + if (cfa_code && bp->cfa_code_map && BNXT_PF(bp)) { + vf_idx = bp->cfa_code_map[cfa_code]; + if (vf_idx != VF_IDX_INVALID) + return bp->vf_reps[vf_idx]->dev; + } + return NULL; +} + +void bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(skb->dev); + struct bnxt_vf_rep_stats *rx_stats; + + rx_stats = &vf_rep->rx_stats; + vf_rep->rx_stats.bytes += skb->len; + vf_rep->rx_stats.packets++; + + netif_receive_skb(skb); +} + +static int bnxt_vf_rep_get_phys_port_name(struct net_device *dev, char *buf, + size_t len) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + struct pci_dev *pf_pdev = vf_rep->bp->pdev; + int rc; + + rc = snprintf(buf, len, "pf%dvf%d", PCI_FUNC(pf_pdev->devfn), + vf_rep->vf_idx); + if (rc >= len) + return -EOPNOTSUPP; + return 0; +} + +static void bnxt_vf_rep_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strlcpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); +} + +static int bnxt_vf_rep_port_attr_get(struct net_device *dev, + struct switchdev_attr *attr) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + + /* as only PORT_PARENT_ID is supported currently use common code + * between PF and VF-rep for now. + */ + return bnxt_port_attr_get(vf_rep->bp, attr); +} + +static const struct switchdev_ops bnxt_vf_rep_switchdev_ops = { + .switchdev_port_attr_get = bnxt_vf_rep_port_attr_get +}; + +static const struct ethtool_ops bnxt_vf_rep_ethtool_ops = { + .get_drvinfo = bnxt_vf_rep_get_drvinfo +}; + +static const struct net_device_ops bnxt_vf_rep_netdev_ops = { + .ndo_open = bnxt_vf_rep_open, + .ndo_stop = bnxt_vf_rep_close, + .ndo_start_xmit = bnxt_vf_rep_xmit, + .ndo_get_stats64 = bnxt_vf_rep_get_stats64, + .ndo_setup_tc = bnxt_vf_rep_setup_tc, + .ndo_get_phys_port_name = bnxt_vf_rep_get_phys_port_name +}; + +/* Called when the parent PF interface is closed: + * As the mode transition from SWITCHDEV to LEGACY + * happens under the rtnl_lock() this routine is safe + * under the rtnl_lock() + */ +void bnxt_vf_reps_close(struct bnxt *bp) +{ + struct bnxt_vf_rep *vf_rep; + u16 num_vfs, i; + + if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) + return; + + num_vfs = pci_num_vf(bp->pdev); + for (i = 0; i < num_vfs; i++) { + vf_rep = bp->vf_reps[i]; + if (netif_running(vf_rep->dev)) + bnxt_vf_rep_close(vf_rep->dev); + } +} + +/* Called when the parent PF interface is opened (re-opened): + * As the mode transition from SWITCHDEV to LEGACY + * happen under the rtnl_lock() this routine is safe + * under the rtnl_lock() + */ +void bnxt_vf_reps_open(struct bnxt *bp) +{ + int i; + + if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) + return; + + for (i = 0; i < pci_num_vf(bp->pdev); i++) + bnxt_vf_rep_open(bp->vf_reps[i]->dev); +} + +static void __bnxt_vf_reps_destroy(struct bnxt *bp) +{ + u16 num_vfs = pci_num_vf(bp->pdev); + struct bnxt_vf_rep *vf_rep; + int i; + + for (i = 0; i < num_vfs; i++) { + vf_rep = bp->vf_reps[i]; + if (vf_rep) { + dst_release((struct dst_entry *)vf_rep->dst); + + if (vf_rep->tx_cfa_action != CFA_HANDLE_INVALID) + hwrm_cfa_vfr_free(bp, vf_rep->vf_idx); + + if (vf_rep->dev) { + /* if register_netdev failed, then netdev_ops + * would have been set to NULL + */ + if (vf_rep->dev->netdev_ops) + unregister_netdev(vf_rep->dev); + free_netdev(vf_rep->dev); + } + } + } + + kfree(bp->vf_reps); + bp->vf_reps = NULL; +} + +void bnxt_vf_reps_destroy(struct bnxt *bp) +{ + bool closed = false; + + if (bp->eswitch_mode != DEVLINK_ESWITCH_MODE_SWITCHDEV) + return; + + if (!bp->vf_reps) + return; + + /* Ensure that parent PF's and VF-reps' RX/TX has been quiesced + * before proceeding with VF-rep cleanup. + */ + rtnl_lock(); + if (netif_running(bp->dev)) { + bnxt_close_nic(bp, false, false); + closed = true; + } + /* un-publish cfa_code_map so that RX path can't see it anymore */ + kfree(bp->cfa_code_map); + bp->cfa_code_map = NULL; + bp->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY; + + if (closed) + bnxt_open_nic(bp, false, false); + rtnl_unlock(); + + /* Need to call vf_reps_destroy() outside of rntl_lock + * as unregister_netdev takes rtnl_lock + */ + __bnxt_vf_reps_destroy(bp); +} + +/* Use the OUI of the PF's perm addr and report the same mac addr + * for the same VF-rep each time + */ +static void bnxt_vf_rep_eth_addr_gen(u8 *src_mac, u16 vf_idx, u8 *mac) +{ + u32 addr; + + ether_addr_copy(mac, src_mac); + + addr = jhash(src_mac, ETH_ALEN, 0) + vf_idx; + mac[3] = (u8)(addr & 0xFF); + mac[4] = (u8)((addr >> 8) & 0xFF); + mac[5] = (u8)((addr >> 16) & 0xFF); +} + +static void bnxt_vf_rep_netdev_init(struct bnxt *bp, struct bnxt_vf_rep *vf_rep, + struct net_device *dev) +{ + struct net_device *pf_dev = bp->dev; + + dev->netdev_ops = &bnxt_vf_rep_netdev_ops; + dev->ethtool_ops = &bnxt_vf_rep_ethtool_ops; + SWITCHDEV_SET_OPS(dev, &bnxt_vf_rep_switchdev_ops); + /* Just inherit all the featues of the parent PF as the VF-R + * uses the RX/TX rings of the parent PF + */ + dev->hw_features = pf_dev->hw_features; + dev->gso_partial_features = pf_dev->gso_partial_features; + dev->vlan_features = pf_dev->vlan_features; + dev->hw_enc_features = pf_dev->hw_enc_features; + dev->features |= pf_dev->features; + bnxt_vf_rep_eth_addr_gen(bp->pf.mac_addr, vf_rep->vf_idx, + dev->perm_addr); + ether_addr_copy(dev->dev_addr, dev->perm_addr); +} + +static int bnxt_vf_reps_create(struct bnxt *bp) +{ + u16 *cfa_code_map = NULL, num_vfs = pci_num_vf(bp->pdev); + struct bnxt_vf_rep *vf_rep; + struct net_device *dev; + int rc, i; + + bp->vf_reps = kcalloc(num_vfs, sizeof(vf_rep), GFP_KERNEL); + if (!bp->vf_reps) + return -ENOMEM; + + /* storage for cfa_code to vf-idx mapping */ + cfa_code_map = kmalloc(sizeof(*bp->cfa_code_map) * MAX_CFA_CODE, + GFP_KERNEL); + if (!cfa_code_map) { + rc = -ENOMEM; + goto err; + } + for (i = 0; i < MAX_CFA_CODE; i++) + cfa_code_map[i] = VF_IDX_INVALID; + + for (i = 0; i < num_vfs; i++) { + dev = alloc_etherdev(sizeof(*vf_rep)); + if (!dev) { + rc = -ENOMEM; + goto err; + } + + vf_rep = netdev_priv(dev); + bp->vf_reps[i] = vf_rep; + vf_rep->dev = dev; + vf_rep->bp = bp; + vf_rep->vf_idx = i; + vf_rep->tx_cfa_action = CFA_HANDLE_INVALID; + + /* get cfa handles from FW */ + rc = hwrm_cfa_vfr_alloc(bp, vf_rep->vf_idx, + &vf_rep->tx_cfa_action, + &vf_rep->rx_cfa_code); + if (rc) { + rc = -ENOLINK; + goto err; + } + cfa_code_map[vf_rep->rx_cfa_code] = vf_rep->vf_idx; + + vf_rep->dst = metadata_dst_alloc(0, METADATA_HW_PORT_MUX, + GFP_KERNEL); + if (!vf_rep->dst) { + rc = -ENOMEM; + goto err; + } + /* only cfa_action is needed to mux a packet while TXing */ + vf_rep->dst->u.port_info.port_id = vf_rep->tx_cfa_action; + vf_rep->dst->u.port_info.lower_dev = bp->dev; + + bnxt_vf_rep_netdev_init(bp, vf_rep, dev); + rc = register_netdev(dev); + if (rc) { + /* no need for unregister_netdev in cleanup */ + dev->netdev_ops = NULL; + goto err; + } + } + + /* publish cfa_code_map only after all VF-reps have been initialized */ + bp->cfa_code_map = cfa_code_map; + bp->eswitch_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV; + netif_keep_dst(bp->dev); + return 0; + +err: + netdev_info(bp->dev, "%s error=%d", __func__, rc); + kfree(cfa_code_map); + __bnxt_vf_reps_destroy(bp); + return rc; +} + +/* Devlink related routines */ +static int bnxt_dl_eswitch_mode_get(struct devlink *devlink, u16 *mode) +{ + struct bnxt *bp = bnxt_get_bp_from_dl(devlink); + + *mode = bp->eswitch_mode; + return 0; +} + +static int bnxt_dl_eswitch_mode_set(struct devlink *devlink, u16 mode) +{ + struct bnxt *bp = bnxt_get_bp_from_dl(devlink); + int rc = 0; + + mutex_lock(&bp->sriov_lock); + if (bp->eswitch_mode == mode) { + netdev_info(bp->dev, "already in %s eswitch mode", + mode == DEVLINK_ESWITCH_MODE_LEGACY ? + "legacy" : "switchdev"); + rc = -EINVAL; + goto done; + } + + switch (mode) { + case DEVLINK_ESWITCH_MODE_LEGACY: + bnxt_vf_reps_destroy(bp); + break; + + case DEVLINK_ESWITCH_MODE_SWITCHDEV: + if (pci_num_vf(bp->pdev) == 0) { + netdev_info(bp->dev, + "Enable VFs before setting switchdev mode"); + rc = -EPERM; + goto done; + } + rc = bnxt_vf_reps_create(bp); + break; + + default: + rc = -EINVAL; + goto done; + } +done: + mutex_unlock(&bp->sriov_lock); + return rc; +} + +static const struct devlink_ops bnxt_dl_ops = { + .eswitch_mode_set = bnxt_dl_eswitch_mode_set, + .eswitch_mode_get = bnxt_dl_eswitch_mode_get +}; + +int bnxt_dl_register(struct bnxt *bp) +{ + struct devlink *dl; + int rc; + + if (!pci_find_ext_capability(bp->pdev, PCI_EXT_CAP_ID_SRIOV)) + return 0; + + if (bp->hwrm_spec_code < 0x10800) { + netdev_warn(bp->dev, "Firmware does not support SR-IOV E-Switch SWITCHDEV mode.\n"); + return -ENOTSUPP; + } + + dl = devlink_alloc(&bnxt_dl_ops, sizeof(struct bnxt_dl)); + if (!dl) { + netdev_warn(bp->dev, "devlink_alloc failed"); + return -ENOMEM; + } + + bnxt_link_bp_to_dl(bp, dl); + bp->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY; + rc = devlink_register(dl, &bp->pdev->dev); + if (rc) { + bnxt_link_bp_to_dl(bp, NULL); + devlink_free(dl); + netdev_warn(bp->dev, "devlink_register failed. rc=%d", rc); + return rc; + } + + return 0; +} + +void bnxt_dl_unregister(struct bnxt *bp) +{ + struct devlink *dl = bp->dl; + + if (!dl) + return; + + devlink_unregister(dl); + devlink_free(dl); +} + +#endif diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h new file mode 100644 index 000000000000..7787cd24606a --- /dev/null +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_vfr.h @@ -0,0 +1,89 @@ +/* Broadcom NetXtreme-C/E network driver. + * + * Copyright (c) 2016-2017 Broadcom Limited + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation. + */ + +#ifndef BNXT_VFR_H +#define BNXT_VFR_H + +#ifdef CONFIG_BNXT_SRIOV + +#define MAX_CFA_CODE 65536 + +/* Struct to hold housekeeping info needed by devlink interface */ +struct bnxt_dl { + struct bnxt *bp; /* back ptr to the controlling dev */ +}; + +static inline struct bnxt *bnxt_get_bp_from_dl(struct devlink *dl) +{ + return ((struct bnxt_dl *)devlink_priv(dl))->bp; +} + +/* To clear devlink pointer from bp, pass NULL dl */ +static inline void bnxt_link_bp_to_dl(struct bnxt *bp, struct devlink *dl) +{ + bp->dl = dl; + + /* add a back pointer in dl to bp */ + if (dl) { + struct bnxt_dl *bp_dl = devlink_priv(dl); + + bp_dl->bp = bp; + } +} + +int bnxt_dl_register(struct bnxt *bp); +void bnxt_dl_unregister(struct bnxt *bp); +void bnxt_vf_reps_destroy(struct bnxt *bp); +void bnxt_vf_reps_close(struct bnxt *bp); +void bnxt_vf_reps_open(struct bnxt *bp); +void bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb); +struct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code); + +static inline u16 bnxt_vf_rep_get_fid(struct net_device *dev) +{ + struct bnxt_vf_rep *vf_rep = netdev_priv(dev); + struct bnxt *bp = vf_rep->bp; + + return bp->pf.vf[vf_rep->vf_idx].fw_fid; +} + +#else + +static inline int bnxt_dl_register(struct bnxt *bp) +{ + return 0; +} + +static inline void bnxt_dl_unregister(struct bnxt *bp) +{ +} + +static inline void bnxt_vf_reps_close(struct bnxt *bp) +{ +} + +static inline void bnxt_vf_reps_open(struct bnxt *bp) +{ +} + +static inline void bnxt_vf_rep_rx(struct bnxt *bp, struct sk_buff *skb) +{ +} + +static inline struct net_device *bnxt_get_vf_rep(struct bnxt *bp, u16 cfa_code) +{ + return NULL; +} + +static inline u16 bnxt_vf_rep_get_fid(struct net_device *dev) +{ + return 0; +} +#endif /* CONFIG_BNXT_SRIOV */ +#endif /* BNXT_VFR_H */ diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c index 3961a6807454..d8f0c837b72c 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_xdp.c @@ -169,8 +169,8 @@ static int bnxt_xdp_set(struct bnxt *bp, struct bpf_prog *prog) tc = netdev_get_num_tc(dev); if (!tc) tc = 1; - rc = bnxt_reserve_rings(bp, bp->tx_nr_rings_per_tc, bp->rx_nr_rings, - true, tc, tx_xdp); + rc = bnxt_check_rings(bp, bp->tx_nr_rings_per_tc, bp->rx_nr_rings, + true, tc, tx_xdp); if (rc) { netdev_warn(dev, "Unable to reserve enough TX rings to support XDP.\n"); return rc; diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index fea3f9a5fb2d..9cebca896913 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -72,23 +72,42 @@ #define GENET_RDMA_REG_OFF (priv->hw_params->rdma_offset + \ TOTAL_DESC * DMA_DESC_SIZE) +static inline void bcmgenet_writel(u32 value, void __iomem *offset) +{ + /* MIPS chips strapped for BE will automagically configure the + * peripheral registers for CPU-native byte order. + */ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + __raw_writel(value, offset); + else + writel_relaxed(value, offset); +} + +static inline u32 bcmgenet_readl(void __iomem *offset) +{ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) + return __raw_readl(offset); + else + return readl_relaxed(offset); +} + static inline void dmadesc_set_length_status(struct bcmgenet_priv *priv, void __iomem *d, u32 value) { - __raw_writel(value, d + DMA_DESC_LENGTH_STATUS); + bcmgenet_writel(value, d + DMA_DESC_LENGTH_STATUS); } static inline u32 dmadesc_get_length_status(struct bcmgenet_priv *priv, void __iomem *d) { - return __raw_readl(d + DMA_DESC_LENGTH_STATUS); + return bcmgenet_readl(d + DMA_DESC_LENGTH_STATUS); } static inline void dmadesc_set_addr(struct bcmgenet_priv *priv, void __iomem *d, dma_addr_t addr) { - __raw_writel(lower_32_bits(addr), d + DMA_DESC_ADDRESS_LO); + bcmgenet_writel(lower_32_bits(addr), d + DMA_DESC_ADDRESS_LO); /* Register writes to GISB bus can take couple hundred nanoseconds * and are done for each packet, save these expensive writes unless @@ -96,7 +115,7 @@ static inline void dmadesc_set_addr(struct bcmgenet_priv *priv, */ #ifdef CONFIG_PHYS_ADDR_T_64BIT if (priv->hw_params->flags & GENET_HAS_40BITS) - __raw_writel(upper_32_bits(addr), d + DMA_DESC_ADDRESS_HI); + bcmgenet_writel(upper_32_bits(addr), d + DMA_DESC_ADDRESS_HI); #endif } @@ -113,7 +132,7 @@ static inline dma_addr_t dmadesc_get_addr(struct bcmgenet_priv *priv, { dma_addr_t addr; - addr = __raw_readl(d + DMA_DESC_ADDRESS_LO); + addr = bcmgenet_readl(d + DMA_DESC_ADDRESS_LO); /* Register writes to GISB bus can take couple hundred nanoseconds * and are done for each packet, save these expensive writes unless @@ -121,7 +140,7 @@ static inline dma_addr_t dmadesc_get_addr(struct bcmgenet_priv *priv, */ #ifdef CONFIG_PHYS_ADDR_T_64BIT if (priv->hw_params->flags & GENET_HAS_40BITS) - addr |= (u64)__raw_readl(d + DMA_DESC_ADDRESS_HI) << 32; + addr |= (u64)bcmgenet_readl(d + DMA_DESC_ADDRESS_HI) << 32; #endif return addr; } @@ -156,8 +175,8 @@ static inline u32 bcmgenet_tbuf_ctrl_get(struct bcmgenet_priv *priv) if (GENET_IS_V1(priv)) return bcmgenet_rbuf_readl(priv, TBUF_CTRL_V1); else - return __raw_readl(priv->base + - priv->hw_params->tbuf_offset + TBUF_CTRL); + return bcmgenet_readl(priv->base + + priv->hw_params->tbuf_offset + TBUF_CTRL); } static inline void bcmgenet_tbuf_ctrl_set(struct bcmgenet_priv *priv, u32 val) @@ -165,7 +184,7 @@ static inline void bcmgenet_tbuf_ctrl_set(struct bcmgenet_priv *priv, u32 val) if (GENET_IS_V1(priv)) bcmgenet_rbuf_writel(priv, val, TBUF_CTRL_V1); else - __raw_writel(val, priv->base + + bcmgenet_writel(val, priv->base + priv->hw_params->tbuf_offset + TBUF_CTRL); } @@ -174,8 +193,8 @@ static inline u32 bcmgenet_bp_mc_get(struct bcmgenet_priv *priv) if (GENET_IS_V1(priv)) return bcmgenet_rbuf_readl(priv, TBUF_BP_MC_V1); else - return __raw_readl(priv->base + - priv->hw_params->tbuf_offset + TBUF_BP_MC); + return bcmgenet_readl(priv->base + + priv->hw_params->tbuf_offset + TBUF_BP_MC); } static inline void bcmgenet_bp_mc_set(struct bcmgenet_priv *priv, u32 val) @@ -183,7 +202,7 @@ static inline void bcmgenet_bp_mc_set(struct bcmgenet_priv *priv, u32 val) if (GENET_IS_V1(priv)) bcmgenet_rbuf_writel(priv, val, TBUF_BP_MC_V1); else - __raw_writel(val, priv->base + + bcmgenet_writel(val, priv->base + priv->hw_params->tbuf_offset + TBUF_BP_MC); } @@ -326,28 +345,28 @@ static inline struct bcmgenet_priv *dev_to_priv(struct device *dev) static inline u32 bcmgenet_tdma_readl(struct bcmgenet_priv *priv, enum dma_reg r) { - return __raw_readl(priv->base + GENET_TDMA_REG_OFF + - DMA_RINGS_SIZE + bcmgenet_dma_regs[r]); + return bcmgenet_readl(priv->base + GENET_TDMA_REG_OFF + + DMA_RINGS_SIZE + bcmgenet_dma_regs[r]); } static inline void bcmgenet_tdma_writel(struct bcmgenet_priv *priv, u32 val, enum dma_reg r) { - __raw_writel(val, priv->base + GENET_TDMA_REG_OFF + + bcmgenet_writel(val, priv->base + GENET_TDMA_REG_OFF + DMA_RINGS_SIZE + bcmgenet_dma_regs[r]); } static inline u32 bcmgenet_rdma_readl(struct bcmgenet_priv *priv, enum dma_reg r) { - return __raw_readl(priv->base + GENET_RDMA_REG_OFF + - DMA_RINGS_SIZE + bcmgenet_dma_regs[r]); + return bcmgenet_readl(priv->base + GENET_RDMA_REG_OFF + + DMA_RINGS_SIZE + bcmgenet_dma_regs[r]); } static inline void bcmgenet_rdma_writel(struct bcmgenet_priv *priv, u32 val, enum dma_reg r) { - __raw_writel(val, priv->base + GENET_RDMA_REG_OFF + + bcmgenet_writel(val, priv->base + GENET_RDMA_REG_OFF + DMA_RINGS_SIZE + bcmgenet_dma_regs[r]); } @@ -418,16 +437,16 @@ static inline u32 bcmgenet_tdma_ring_readl(struct bcmgenet_priv *priv, unsigned int ring, enum dma_ring_reg r) { - return __raw_readl(priv->base + GENET_TDMA_REG_OFF + - (DMA_RING_SIZE * ring) + - genet_dma_ring_regs[r]); + return bcmgenet_readl(priv->base + GENET_TDMA_REG_OFF + + (DMA_RING_SIZE * ring) + + genet_dma_ring_regs[r]); } static inline void bcmgenet_tdma_ring_writel(struct bcmgenet_priv *priv, unsigned int ring, u32 val, enum dma_ring_reg r) { - __raw_writel(val, priv->base + GENET_TDMA_REG_OFF + + bcmgenet_writel(val, priv->base + GENET_TDMA_REG_OFF + (DMA_RING_SIZE * ring) + genet_dma_ring_regs[r]); } @@ -436,16 +455,16 @@ static inline u32 bcmgenet_rdma_ring_readl(struct bcmgenet_priv *priv, unsigned int ring, enum dma_ring_reg r) { - return __raw_readl(priv->base + GENET_RDMA_REG_OFF + - (DMA_RING_SIZE * ring) + - genet_dma_ring_regs[r]); + return bcmgenet_readl(priv->base + GENET_RDMA_REG_OFF + + (DMA_RING_SIZE * ring) + + genet_dma_ring_regs[r]); } static inline void bcmgenet_rdma_ring_writel(struct bcmgenet_priv *priv, unsigned int ring, u32 val, enum dma_ring_reg r) { - __raw_writel(val, priv->base + GENET_RDMA_REG_OFF + + bcmgenet_writel(val, priv->base + GENET_RDMA_REG_OFF + (DMA_RING_SIZE * ring) + genet_dma_ring_regs[r]); } @@ -991,12 +1010,12 @@ static void bcmgenet_eee_enable_set(struct net_device *dev, bool enable) bcmgenet_umac_writel(priv, reg, UMAC_EEE_CTRL); /* Enable EEE and switch to a 27Mhz clock automatically */ - reg = __raw_readl(priv->base + off); + reg = bcmgenet_readl(priv->base + off); if (enable) reg |= TBUF_EEE_EN | TBUF_PM_EN; else reg &= ~(TBUF_EEE_EN | TBUF_PM_EN); - __raw_writel(reg, priv->base + off); + bcmgenet_writel(reg, priv->base + off); /* Do the same for thing for RBUF */ reg = bcmgenet_rbuf_readl(priv, RBUF_ENERGY_CTRL); diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.h b/drivers/net/ethernet/broadcom/genet/bcmgenet.h index 3a34fdba5301..4c49d0b97748 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.h +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.h @@ -657,6 +657,7 @@ struct bcmgenet_priv { struct clk *clk; struct platform_device *pdev; + struct platform_device *mii_pdev; /* WOL */ struct clk *clk_wol; @@ -671,12 +672,21 @@ struct bcmgenet_priv { static inline u32 bcmgenet_##name##_readl(struct bcmgenet_priv *priv, \ u32 off) \ { \ - return __raw_readl(priv->base + offset + off); \ + /* MIPS chips strapped for BE will automagically configure the \ + * peripheral registers for CPU-native byte order. \ + */ \ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \ + return __raw_readl(priv->base + offset + off); \ + else \ + return readl_relaxed(priv->base + offset + off); \ } \ static inline void bcmgenet_##name##_writel(struct bcmgenet_priv *priv, \ u32 val, u32 off) \ { \ - __raw_writel(val, priv->base + offset + off); \ + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) \ + __raw_writel(val, priv->base + offset + off); \ + else \ + writel_relaxed(val, priv->base + offset + off); \ } GENET_IO_MACRO(ext, GENET_EXT_OFF); diff --git a/drivers/net/ethernet/broadcom/genet/bcmmii.c b/drivers/net/ethernet/broadcom/genet/bcmmii.c index 30cb97b4a1d7..18f5723be2c9 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmmii.c +++ b/drivers/net/ethernet/broadcom/genet/bcmmii.c @@ -24,62 +24,10 @@ #include <linux/of_net.h> #include <linux/of_mdio.h> #include <linux/platform_data/bcmgenet.h> +#include <linux/platform_data/mdio-bcm-unimac.h> #include "bcmgenet.h" -/* read a value from the MII */ -static int bcmgenet_mii_read(struct mii_bus *bus, int phy_id, int location) -{ - int ret; - struct net_device *dev = bus->priv; - struct bcmgenet_priv *priv = netdev_priv(dev); - u32 reg; - - bcmgenet_umac_writel(priv, (MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | - (location << MDIO_REG_SHIFT)), UMAC_MDIO_CMD); - /* Start MDIO transaction*/ - reg = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); - reg |= MDIO_START_BUSY; - bcmgenet_umac_writel(priv, reg, UMAC_MDIO_CMD); - wait_event_timeout(priv->wq, - !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) - & MDIO_START_BUSY), - HZ / 100); - ret = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); - - /* Some broken devices are known not to release the line during - * turn-around, e.g: Broadcom BCM53125 external switches, so check for - * that condition here and ignore the MDIO controller read failure - * indication. - */ - if (!(bus->phy_ignore_ta_mask & 1 << phy_id) && (ret & MDIO_READ_FAIL)) - return -EIO; - - return ret & 0xffff; -} - -/* write a value to the MII */ -static int bcmgenet_mii_write(struct mii_bus *bus, int phy_id, - int location, u16 val) -{ - struct net_device *dev = bus->priv; - struct bcmgenet_priv *priv = netdev_priv(dev); - u32 reg; - - bcmgenet_umac_writel(priv, (MDIO_WR | (phy_id << MDIO_PMD_SHIFT) | - (location << MDIO_REG_SHIFT) | (0xffff & val)), - UMAC_MDIO_CMD); - reg = bcmgenet_umac_readl(priv, UMAC_MDIO_CMD); - reg |= MDIO_START_BUSY; - bcmgenet_umac_writel(priv, reg, UMAC_MDIO_CMD); - wait_event_timeout(priv->wq, - !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) & - MDIO_START_BUSY), - HZ / 100); - - return 0; -} - /* setup netdev link state when PHY link status change and * update UMAC and RGMII block when link up */ @@ -393,104 +341,121 @@ int bcmgenet_mii_probe(struct net_device *dev) return 0; } -/* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with - * their internal MDIO management controller making them fail to successfully - * be read from or written to for the first transaction. We insert a dummy - * BMSR read here to make sure that phy_get_device() and get_phy_id() can - * correctly read the PHY MII_PHYSID1/2 registers and successfully register a - * PHY device for this peripheral. - * - * Once the PHY driver is registered, we can workaround subsequent reads from - * there (e.g: during system-wide power management). - * - * bus->reset is invoked before mdiobus_scan during mdiobus_register and is - * therefore the right location to stick that workaround. Since we do not want - * to read from non-existing PHYs, we either use bus->phy_mask or do a manual - * Device Tree scan to limit the search area. - */ -static int bcmgenet_mii_bus_reset(struct mii_bus *bus) +static struct device_node *bcmgenet_mii_of_find_mdio(struct bcmgenet_priv *priv) { - struct net_device *dev = bus->priv; - struct bcmgenet_priv *priv = netdev_priv(dev); - struct device_node *np = priv->mdio_dn; - struct device_node *child = NULL; - u32 read_mask = 0; - int addr = 0; + struct device_node *dn = priv->pdev->dev.of_node; + struct device *kdev = &priv->pdev->dev; + char *compat; - if (!np) { - read_mask = 1 << priv->phy_addr; - } else { - for_each_available_child_of_node(np, child) { - addr = of_mdio_parse_addr(&dev->dev, child); - if (addr < 0) - continue; + compat = kasprintf(GFP_KERNEL, "brcm,genet-mdio-v%d", priv->version); + if (!compat) + return NULL; - read_mask |= 1 << addr; - } + priv->mdio_dn = of_find_compatible_node(dn, NULL, compat); + kfree(compat); + if (!priv->mdio_dn) { + dev_err(kdev, "unable to find MDIO bus node\n"); + return NULL; } - for (addr = 0; addr < PHY_MAX_ADDR; addr++) { - if (read_mask & 1 << addr) { - dev_dbg(&dev->dev, "Workaround for PHY @ %d\n", addr); - mdiobus_read(bus, addr, MII_BMSR); - } + return priv->mdio_dn; +} + +static void bcmgenet_mii_pdata_init(struct bcmgenet_priv *priv, + struct unimac_mdio_pdata *ppd) +{ + struct device *kdev = &priv->pdev->dev; + struct bcmgenet_platform_data *pd = kdev->platform_data; + + if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) { + /* + * Internal or external PHY with MDIO access + */ + if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR) + ppd->phy_mask = 1 << pd->phy_address; + else + ppd->phy_mask = 0; } +} +static int bcmgenet_mii_wait(void *wait_func_data) +{ + struct bcmgenet_priv *priv = wait_func_data; + + wait_event_timeout(priv->wq, + !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) + & MDIO_START_BUSY), + HZ / 100); return 0; } -static int bcmgenet_mii_alloc(struct bcmgenet_priv *priv) +static int bcmgenet_mii_register(struct bcmgenet_priv *priv) { - struct mii_bus *bus; + struct platform_device *pdev = priv->pdev; + struct bcmgenet_platform_data *pdata = pdev->dev.platform_data; + struct device_node *dn = pdev->dev.of_node; + struct unimac_mdio_pdata ppd; + struct platform_device *ppdev; + struct resource *pres, res; + int id, ret; + + pres = platform_get_resource(pdev, IORESOURCE_MEM, 0); + memset(&res, 0, sizeof(res)); + memset(&ppd, 0, sizeof(ppd)); + + ppd.wait_func = bcmgenet_mii_wait; + ppd.wait_func_data = priv; + ppd.bus_name = "bcmgenet MII bus"; + + /* Unimac MDIO bus controller starts at UniMAC offset + MDIO_CMD + * and is 2 * 32-bits word long, 8 bytes total. + */ + res.start = pres->start + GENET_UMAC_OFF + UMAC_MDIO_CMD; + res.end = res.start + 8; + res.flags = IORESOURCE_MEM; - if (priv->mii_bus) - return 0; + if (dn) + id = of_alias_get_id(dn, "eth"); + else + id = pdev->id; - priv->mii_bus = mdiobus_alloc(); - if (!priv->mii_bus) { - pr_err("failed to allocate\n"); + ppdev = platform_device_alloc(UNIMAC_MDIO_DRV_NAME, id); + if (!ppdev) return -ENOMEM; - } - bus = priv->mii_bus; - bus->priv = priv->dev; - bus->name = "bcmgenet MII bus"; - bus->parent = &priv->pdev->dev; - bus->read = bcmgenet_mii_read; - bus->write = bcmgenet_mii_write; - bus->reset = bcmgenet_mii_bus_reset; - snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", - priv->pdev->name, priv->pdev->id); + /* Retain this platform_device pointer for later cleanup */ + priv->mii_pdev = ppdev; + ppdev->dev.parent = &pdev->dev; + ppdev->dev.of_node = bcmgenet_mii_of_find_mdio(priv); + if (pdata) + bcmgenet_mii_pdata_init(priv, &ppd); + + ret = platform_device_add_resources(ppdev, &res, 1); + if (ret) + goto out; + + ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd)); + if (ret) + goto out; + + ret = platform_device_add(ppdev); + if (ret) + goto out; return 0; +out: + platform_device_put(ppdev); + return ret; } static int bcmgenet_mii_of_init(struct bcmgenet_priv *priv) { struct device_node *dn = priv->pdev->dev.of_node; struct device *kdev = &priv->pdev->dev; - struct phy_device *phydev = NULL; - char *compat; + struct phy_device *phydev; int phy_mode; int ret; - compat = kasprintf(GFP_KERNEL, "brcm,genet-mdio-v%d", priv->version); - if (!compat) - return -ENOMEM; - - priv->mdio_dn = of_find_compatible_node(dn, NULL, compat); - kfree(compat); - if (!priv->mdio_dn) { - dev_err(kdev, "unable to find MDIO bus node\n"); - return -ENODEV; - } - - ret = of_mdiobus_register(priv->mii_bus, priv->mdio_dn); - if (ret) { - dev_err(kdev, "failed to register MDIO bus\n"); - return ret; - } - /* Fetch the PHY phandle */ priv->phy_dn = of_parse_phandle(dn, "phy-handle", 0); @@ -537,33 +502,23 @@ static int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv) { struct device *kdev = &priv->pdev->dev; struct bcmgenet_platform_data *pd = kdev->platform_data; - struct mii_bus *mdio = priv->mii_bus; + char phy_name[MII_BUS_ID_SIZE + 3]; + char mdio_bus_id[MII_BUS_ID_SIZE]; struct phy_device *phydev; - int ret; + + snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d", + UNIMAC_MDIO_DRV_NAME, priv->pdev->id); if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) { + snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, + mdio_bus_id, pd->phy_address); + /* * Internal or external PHY with MDIO access */ - if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR) - mdio->phy_mask = ~(1 << pd->phy_address); - else - mdio->phy_mask = 0; - - ret = mdiobus_register(mdio); - if (ret) { - dev_err(kdev, "failed to register MDIO bus\n"); - return ret; - } - - if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR) - phydev = mdiobus_get_phy(mdio, pd->phy_address); - else - phydev = phy_find_first(mdio); - + phydev = phy_attach(priv->dev, phy_name, pd->phy_interface); if (!phydev) { dev_err(kdev, "failed to register PHY device\n"); - mdiobus_unregister(mdio); return -ENODEV; } } else { @@ -609,10 +564,9 @@ static int bcmgenet_mii_bus_init(struct bcmgenet_priv *priv) int bcmgenet_mii_init(struct net_device *dev) { struct bcmgenet_priv *priv = netdev_priv(dev); - struct device_node *dn = priv->pdev->dev.of_node; int ret; - ret = bcmgenet_mii_alloc(priv); + ret = bcmgenet_mii_register(priv); if (ret) return ret; @@ -623,11 +577,7 @@ int bcmgenet_mii_init(struct net_device *dev) return 0; out: - if (of_phy_is_fixed_link(dn)) - of_phy_deregister_fixed_link(dn); - of_node_put(priv->phy_dn); - mdiobus_unregister(priv->mii_bus); - mdiobus_free(priv->mii_bus); + bcmgenet_mii_exit(dev); return ret; } @@ -639,6 +589,6 @@ void bcmgenet_mii_exit(struct net_device *dev) if (of_phy_is_fixed_link(dn)) of_phy_deregister_fixed_link(dn); of_node_put(priv->phy_dn); - mdiobus_unregister(priv->mii_bus); - mdiobus_free(priv->mii_bus); + platform_device_unregister(priv->mii_pdev); + platform_device_put(priv->mii_pdev); } diff --git a/drivers/net/ethernet/broadcom/sb1250-mac.c b/drivers/net/ethernet/broadcom/sb1250-mac.c index 16a0f192daec..ecdef42f0ae6 100644 --- a/drivers/net/ethernet/broadcom/sb1250-mac.c +++ b/drivers/net/ethernet/broadcom/sb1250-mac.c @@ -1367,15 +1367,11 @@ static int sbmac_initctx(struct sbmac_softc *s) static void sbdma_uninitctx(struct sbmacdma *d) { - if (d->sbdma_dscrtable_unaligned) { - kfree(d->sbdma_dscrtable_unaligned); - d->sbdma_dscrtable_unaligned = d->sbdma_dscrtable = NULL; - } + kfree(d->sbdma_dscrtable_unaligned); + d->sbdma_dscrtable_unaligned = d->sbdma_dscrtable = NULL; - if (d->sbdma_ctxtable) { - kfree(d->sbdma_ctxtable); - d->sbdma_ctxtable = NULL; - } + kfree(d->sbdma_ctxtable); + d->sbdma_ctxtable = NULL; } diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index d600c41fb1dc..af33dc15c55f 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -6587,7 +6587,7 @@ static void tg3_tx(struct tg3_napi *tnapi) pkts_compl++; bytes_compl += skb->len; - dev_kfree_skb_any(skb); + dev_consume_skb_any(skb); if (unlikely(tx_bug)) { tg3_tx_recover(tp); @@ -7829,7 +7829,7 @@ static int tigon3_dma_hwbug_workaround(struct tg3_napi *tnapi, } } - dev_kfree_skb_any(skb); + dev_consume_skb_any(skb); *pskb = new_skb; return ret; } @@ -7882,7 +7882,7 @@ static int tg3_tso_bug(struct tg3 *tp, struct tg3_napi *tnapi, } while (segs); tg3_tso_bug_end: - dev_kfree_skb_any(skb); + dev_consume_skb_any(skb); return NETDEV_TX_OK; } @@ -8543,7 +8543,7 @@ static void tg3_free_rings(struct tg3 *tp) tg3_tx_skb_unmap(tnapi, i, skb_shinfo(skb)->nr_frags - 1); - dev_kfree_skb_any(skb); + dev_consume_skb_any(skb); } netdev_tx_reset_queue(netdev_get_tx_queue(tp->dev, j)); } diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 26d25749c3e4..6df2cad61647 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -68,7 +68,7 @@ #define GEM_MAX_TX_LEN ((unsigned int)((1 << GEM_TX_FRMLEN_SIZE) - 1) & ~((unsigned int)(MACB_TX_LEN_ALIGN - 1))) #define GEM_MTU_MIN_SIZE ETH_MIN_MTU -#define MACB_NETIF_LSO (NETIF_F_TSO | NETIF_F_UFO) +#define MACB_NETIF_LSO NETIF_F_TSO #define MACB_WOL_HAS_MAGIC_PACKET (0x1 << 0) #define MACB_WOL_ENABLED (0x1 << 1) diff --git a/drivers/net/ethernet/cadence/macb_pci.c b/drivers/net/ethernet/cadence/macb_pci.c index 9906fda76087..248a8fc45069 100644 --- a/drivers/net/ethernet/cadence/macb_pci.c +++ b/drivers/net/ethernet/cadence/macb_pci.c @@ -128,7 +128,7 @@ static void macb_remove(struct pci_dev *pdev) clk_unregister(plat_data->hclk); } -static struct pci_device_id dev_id_table[] = { +static const struct pci_device_id dev_id_table[] = { { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), }, { 0, } }; diff --git a/drivers/net/ethernet/cadence/macb_ptp.c b/drivers/net/ethernet/cadence/macb_ptp.c index 67cca08472b7..2220c771092b 100755 --- a/drivers/net/ethernet/cadence/macb_ptp.c +++ b/drivers/net/ethernet/cadence/macb_ptp.c @@ -192,7 +192,7 @@ static int gem_ptp_enable(struct ptp_clock_info *ptp, return -EOPNOTSUPP; } -static struct ptp_clock_info gem_ptp_caps_template = { +static const struct ptp_clock_info gem_ptp_caps_template = { .owner = THIS_MODULE, .name = GEM_PTP_TIMER_NAME, .max_adj = 0, diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c index 4b0ca9fb2cb4..e8b290473ee2 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.c @@ -1150,14 +1150,50 @@ static void cn23xx_get_pcie_qlmport(struct octeon_device *oct) oct->pcie_port); } -static void cn23xx_get_pf_num(struct octeon_device *oct) +static int cn23xx_get_pf_num(struct octeon_device *oct) { u32 fdl_bit = 0; + u64 pkt0_in_ctl, d64; + int pfnum, mac, trs, ret; + + ret = 0; /** Read Function Dependency Link reg to get the function number */ - pci_read_config_dword(oct->pci_dev, CN23XX_PCIE_SRIOV_FDL, &fdl_bit); - oct->pf_num = ((fdl_bit >> CN23XX_PCIE_SRIOV_FDL_BIT_POS) & - CN23XX_PCIE_SRIOV_FDL_MASK); + if (pci_read_config_dword(oct->pci_dev, CN23XX_PCIE_SRIOV_FDL, + &fdl_bit) == 0) { + oct->pf_num = ((fdl_bit >> CN23XX_PCIE_SRIOV_FDL_BIT_POS) & + CN23XX_PCIE_SRIOV_FDL_MASK); + } else { + ret = EINVAL; + + /* Under some virtual environments, extended PCI regs are + * inaccessible, in which case the above read will have failed. + * In this case, read the PF number from the + * SLI_PKT0_INPUT_CONTROL reg (written by f/w) + */ + pkt0_in_ctl = octeon_read_csr64(oct, + CN23XX_SLI_IQ_PKT_CONTROL64(0)); + pfnum = (pkt0_in_ctl >> CN23XX_PKT_INPUT_CTL_PF_NUM_POS) & + CN23XX_PKT_INPUT_CTL_PF_NUM_MASK; + mac = (octeon_read_csr(oct, CN23XX_SLI_MAC_NUMBER)) & 0xff; + + /* validate PF num by reading RINFO; f/w writes RINFO.trs == 1*/ + d64 = octeon_read_csr64(oct, + CN23XX_SLI_PKT_MAC_RINFO64(mac, pfnum)); + trs = (int)(d64 >> CN23XX_PKT_MAC_CTL_RINFO_TRS_BIT_POS) & 0xff; + if (trs == 1) { + dev_err(&oct->pci_dev->dev, + "OCTEON: error reading PCI cfg space pfnum, re-read %u\n", + pfnum); + oct->pf_num = pfnum; + ret = 0; + } else { + dev_err(&oct->pci_dev->dev, + "OCTEON: error reading PCI cfg space pfnum; could not ascertain PF number\n"); + } + } + + return ret; } static void cn23xx_setup_reg_address(struct octeon_device *oct) @@ -1269,6 +1305,26 @@ static int cn23xx_sriov_config(struct octeon_device *oct) int setup_cn23xx_octeon_pf_device(struct octeon_device *oct) { + u32 data32; + u64 BAR0, BAR1; + + pci_read_config_dword(oct->pci_dev, PCI_BASE_ADDRESS_0, &data32); + BAR0 = (u64)(data32 & ~0xf); + pci_read_config_dword(oct->pci_dev, PCI_BASE_ADDRESS_1, &data32); + BAR0 |= ((u64)data32 << 32); + pci_read_config_dword(oct->pci_dev, PCI_BASE_ADDRESS_2, &data32); + BAR1 = (u64)(data32 & ~0xf); + pci_read_config_dword(oct->pci_dev, PCI_BASE_ADDRESS_3, &data32); + BAR1 |= ((u64)data32 << 32); + + if (!BAR0 || !BAR1) { + if (!BAR0) + dev_err(&oct->pci_dev->dev, "device BAR0 unassigned\n"); + if (!BAR1) + dev_err(&oct->pci_dev->dev, "device BAR1 unassigned\n"); + return 1; + } + if (octeon_map_pci_barx(oct, 0, 0)) return 1; @@ -1279,7 +1335,8 @@ int setup_cn23xx_octeon_pf_device(struct octeon_device *oct) return 1; } - cn23xx_get_pf_num(oct); + if (cn23xx_get_pf_num(oct) != 0) + return 1; if (cn23xx_sriov_config(oct)) { octeon_unmap_pci_barx(oct, 0); @@ -1405,8 +1462,19 @@ int cn23xx_fw_loaded(struct octeon_device *oct) { u64 val; - val = octeon_read_csr64(oct, CN23XX_SLI_SCRATCH1); - return (val >> 1) & 1ULL; + /* If there's more than one active PF on this NIC, then that + * implies that the NIC firmware is loaded and running. This check + * prevents a rare false negative that might occur if we only relied + * on checking the SCR2_BIT_FW_LOADED flag. The false negative would + * happen if the PF driver sees SCR2_BIT_FW_LOADED as cleared even + * though the firmware was already loaded but still booting and has yet + * to set SCR2_BIT_FW_LOADED. + */ + if (atomic_read(oct->adapter_refcount) > 1) + return 1; + + val = octeon_read_csr64(oct, CN23XX_SLI_SCRATCH2); + return (val >> SCR2_BIT_FW_LOADED) & 1ULL; } void cn23xx_tell_vf_its_macaddr_changed(struct octeon_device *oct, int vfidx, diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h index dee604651ba7..2aba5247b6d8 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_pf_device.h @@ -24,8 +24,6 @@ #include "cn23xx_pf_regs.h" -#define LIO_CMD_WAIT_TM 100 - /* Register address and configuration for a CN23XX devices. * If device specific changes need to be made then add a struct to include * device specific fields as shown in the commented section diff --git a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h index 3f98c7334957..2d06097d3f61 100644 --- a/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h +++ b/drivers/net/ethernet/cavium/liquidio/cn23xx_vf_device.h @@ -36,8 +36,6 @@ struct octeon_cn23xx_vf { #define CN23XX_MAILBOX_MSGPARAM_SIZE 6 -#define MAX_VF_IP_OP_PENDING_PKT_COUNT 100 - void cn23xx_vf_ask_pf_to_do_flr(struct octeon_device *oct); int cn23xx_octeon_pfvf_handshake(struct octeon_device *oct); diff --git a/drivers/net/ethernet/cavium/liquidio/lio_core.c b/drivers/net/ethernet/cavium/liquidio/lio_core.c index adde7745d069..23f6b60030c5 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_core.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_core.c @@ -165,9 +165,6 @@ void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr) /* If command is successful, change the MTU. */ netif_info(lio, probe, lio->netdev, "MTU Changed from %d to %d\n", netdev->mtu, nctrl->ncmd.s.param1); - dev_info(&oct->pci_dev->dev, "%s MTU Changed from %d to %d\n", - netdev->name, netdev->mtu, - nctrl->ncmd.s.param1); netdev->mtu = nctrl->ncmd.s.param1; queue_delayed_work(lio->link_status_wq.wq, &lio->link_status_wq.wk.work, 0); @@ -275,6 +272,11 @@ void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr) netif_info(lio, probe, lio->netdev, "Set RX/TX flow control parameters\n"); break; + case OCTNET_CMD_QUEUE_COUNT_CTL: + netif_info(lio, probe, lio->netdev, "Queue count updated to %d\n", + nctrl->ncmd.s.param1); + break; + default: dev_err(&oct->pci_dev->dev, "%s Unknown cmd %d\n", __func__, nctrl->ncmd.s.cmd); @@ -364,3 +366,723 @@ void cleanup_rx_oom_poll_fn(struct net_device *netdev) destroy_workqueue(lio->rxq_status_wq.wq); } } + +/* Runs in interrupt context. */ +static void lio_update_txq_status(struct octeon_device *oct, int iq_num) +{ + struct octeon_instr_queue *iq = oct->instr_queue[iq_num]; + struct net_device *netdev; + struct lio *lio; + + netdev = oct->props[iq->ifidx].netdev; + + /* This is needed because the first IQ does not have + * a netdev associated with it. + */ + if (!netdev) + return; + + lio = GET_LIO(netdev); + if (netif_is_multiqueue(netdev)) { + if (__netif_subqueue_stopped(netdev, iq->q_index) && + lio->linfo.link.s.link_up && + (!octnet_iq_is_full(oct, iq_num))) { + netif_wake_subqueue(netdev, iq->q_index); + INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq_num, + tx_restart, 1); + } + } else if (netif_queue_stopped(netdev) && + lio->linfo.link.s.link_up && + (!octnet_iq_is_full(oct, lio->txq))) { + INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, lio->txq, + tx_restart, 1); + netif_wake_queue(netdev); + } +} + +/** + * \brief Setup output queue + * @param oct octeon device + * @param q_no which queue + * @param num_descs how many descriptors + * @param desc_size size of each descriptor + * @param app_ctx application context + */ +static int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, + int desc_size, void *app_ctx) +{ + int ret_val; + + dev_dbg(&oct->pci_dev->dev, "Creating Droq: %d\n", q_no); + /* droq creation and local register settings. */ + ret_val = octeon_create_droq(oct, q_no, num_descs, desc_size, app_ctx); + if (ret_val < 0) + return ret_val; + + if (ret_val == 1) { + dev_dbg(&oct->pci_dev->dev, "Using default droq %d\n", q_no); + return 0; + } + + /* Enable the droq queues */ + octeon_set_droq_pkt_op(oct, q_no, 1); + + /* Send Credit for Octeon Output queues. Credits are always + * sent after the output queue is enabled. + */ + writel(oct->droq[q_no]->max_count, oct->droq[q_no]->pkts_credit_reg); + + return ret_val; +} + +/** Routine to push packets arriving on Octeon interface upto network layer. + * @param oct_id - octeon device id. + * @param skbuff - skbuff struct to be passed to network layer. + * @param len - size of total data received. + * @param rh - Control header associated with the packet + * @param param - additional control data with the packet + * @param arg - farg registered in droq_ops + */ +static void +liquidio_push_packet(u32 octeon_id __attribute__((unused)), + void *skbuff, + u32 len, + union octeon_rh *rh, + void *param, + void *arg) +{ + struct net_device *netdev = (struct net_device *)arg; + struct octeon_droq *droq = + container_of(param, struct octeon_droq, napi); + struct sk_buff *skb = (struct sk_buff *)skbuff; + struct skb_shared_hwtstamps *shhwtstamps; + struct napi_struct *napi = param; + u16 vtag = 0; + u32 r_dh_off; + u64 ns; + + if (netdev) { + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + int packet_was_received; + + /* Do not proceed if the interface is not in RUNNING state. */ + if (!ifstate_check(lio, LIO_IFSTATE_RUNNING)) { + recv_buffer_free(skb); + droq->stats.rx_dropped++; + return; + } + + skb->dev = netdev; + + skb_record_rx_queue(skb, droq->q_no); + if (likely(len > MIN_SKB_SIZE)) { + struct octeon_skb_page_info *pg_info; + unsigned char *va; + + pg_info = ((struct octeon_skb_page_info *)(skb->cb)); + if (pg_info->page) { + /* For Paged allocation use the frags */ + va = page_address(pg_info->page) + + pg_info->page_offset; + memcpy(skb->data, va, MIN_SKB_SIZE); + skb_put(skb, MIN_SKB_SIZE); + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, + pg_info->page, + pg_info->page_offset + + MIN_SKB_SIZE, + len - MIN_SKB_SIZE, + LIO_RXBUFFER_SZ); + } + } else { + struct octeon_skb_page_info *pg_info = + ((struct octeon_skb_page_info *)(skb->cb)); + skb_copy_to_linear_data(skb, page_address(pg_info->page) + + pg_info->page_offset, len); + skb_put(skb, len); + put_page(pg_info->page); + } + + r_dh_off = (rh->r_dh.len - 1) * BYTES_PER_DHLEN_UNIT; + + if (oct->ptp_enable) { + if (rh->r_dh.has_hwtstamp) { + /* timestamp is included from the hardware at + * the beginning of the packet. + */ + if (ifstate_check + (lio, + LIO_IFSTATE_RX_TIMESTAMP_ENABLED)) { + /* Nanoseconds are in the first 64-bits + * of the packet. + */ + memcpy(&ns, (skb->data + r_dh_off), + sizeof(ns)); + r_dh_off -= BYTES_PER_DHLEN_UNIT; + shhwtstamps = skb_hwtstamps(skb); + shhwtstamps->hwtstamp = + ns_to_ktime(ns + + lio->ptp_adjust); + } + } + } + + if (rh->r_dh.has_hash) { + __be32 *hash_be = (__be32 *)(skb->data + r_dh_off); + u32 hash = be32_to_cpu(*hash_be); + + skb_set_hash(skb, hash, PKT_HASH_TYPE_L4); + r_dh_off -= BYTES_PER_DHLEN_UNIT; + } + + skb_pull(skb, rh->r_dh.len * BYTES_PER_DHLEN_UNIT); + skb->protocol = eth_type_trans(skb, skb->dev); + + if ((netdev->features & NETIF_F_RXCSUM) && + (((rh->r_dh.encap_on) && + (rh->r_dh.csum_verified & CNNIC_TUN_CSUM_VERIFIED)) || + (!(rh->r_dh.encap_on) && + (rh->r_dh.csum_verified & CNNIC_CSUM_VERIFIED)))) + /* checksum has already been verified */ + skb->ip_summed = CHECKSUM_UNNECESSARY; + else + skb->ip_summed = CHECKSUM_NONE; + + /* Setting Encapsulation field on basis of status received + * from the firmware + */ + if (rh->r_dh.encap_on) { + skb->encapsulation = 1; + skb->csum_level = 1; + droq->stats.rx_vxlan++; + } + + /* inbound VLAN tag */ + if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && + rh->r_dh.vlan) { + u16 priority = rh->r_dh.priority; + u16 vid = rh->r_dh.vlan; + + vtag = (priority << VLAN_PRIO_SHIFT) | vid; + __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vtag); + } + + packet_was_received = (napi_gro_receive(napi, skb) != GRO_DROP); + + if (packet_was_received) { + droq->stats.rx_bytes_received += len; + droq->stats.rx_pkts_received++; + } else { + droq->stats.rx_dropped++; + netif_info(lio, rx_err, lio->netdev, + "droq:%d error rx_dropped:%llu\n", + droq->q_no, droq->stats.rx_dropped); + } + + } else { + recv_buffer_free(skb); + } +} + +/** + * \brief wrapper for calling napi_schedule + * @param param parameters to pass to napi_schedule + * + * Used when scheduling on different CPUs + */ +static void napi_schedule_wrapper(void *param) +{ + struct napi_struct *napi = param; + + napi_schedule(napi); +} + +/** + * \brief callback when receive interrupt occurs and we are in NAPI mode + * @param arg pointer to octeon output queue + */ +static void liquidio_napi_drv_callback(void *arg) +{ + struct octeon_device *oct; + struct octeon_droq *droq = arg; + int this_cpu = smp_processor_id(); + + oct = droq->oct_dev; + + if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct) || + droq->cpu_id == this_cpu) { + napi_schedule_irqoff(&droq->napi); + } else { + call_single_data_t *csd = &droq->csd; + + csd->func = napi_schedule_wrapper; + csd->info = &droq->napi; + csd->flags = 0; + + smp_call_function_single_async(droq->cpu_id, csd); + } +} + +/** + * \brief Entry point for NAPI polling + * @param napi NAPI structure + * @param budget maximum number of items to process + */ +static int liquidio_napi_poll(struct napi_struct *napi, int budget) +{ + struct octeon_instr_queue *iq; + struct octeon_device *oct; + struct octeon_droq *droq; + int tx_done = 0, iq_no; + int work_done; + + droq = container_of(napi, struct octeon_droq, napi); + oct = droq->oct_dev; + iq_no = droq->q_no; + + /* Handle Droq descriptors */ + work_done = octeon_process_droq_poll_cmd(oct, droq->q_no, + POLL_EVENT_PROCESS_PKTS, + budget); + + /* Flush the instruction queue */ + iq = oct->instr_queue[iq_no]; + if (iq) { + /* TODO: move this check to inside octeon_flush_iq, + * once check_db_timeout is removed + */ + if (atomic_read(&iq->instr_pending)) + /* Process iq buffers with in the budget limits */ + tx_done = octeon_flush_iq(oct, iq, budget); + else + tx_done = 1; + /* Update iq read-index rather than waiting for next interrupt. + * Return back if tx_done is false. + */ + /* sub-queue status update */ + lio_update_txq_status(oct, iq_no); + } else { + dev_err(&oct->pci_dev->dev, "%s: iq (%d) num invalid\n", + __func__, iq_no); + } + +#define MAX_REG_CNT 2000000U + /* force enable interrupt if reg cnts are high to avoid wraparound */ + if ((work_done < budget && tx_done) || + (iq && iq->pkt_in_done >= MAX_REG_CNT) || + (droq->pkt_count >= MAX_REG_CNT)) { + tx_done = 1; + napi_complete_done(napi, work_done); + + octeon_process_droq_poll_cmd(droq->oct_dev, droq->q_no, + POLL_EVENT_ENABLE_INTR, 0); + return 0; + } + + return (!tx_done) ? (budget) : (work_done); +} + +/** + * \brief Setup input and output queues + * @param octeon_dev octeon device + * @param ifidx Interface index + * + * Note: Queues are with respect to the octeon device. Thus + * an input queue is for egress packets, and output queues + * are for ingress packets. + */ +int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx, + u32 num_iqs, u32 num_oqs) +{ + struct octeon_droq_ops droq_ops; + struct net_device *netdev; + struct octeon_droq *droq; + struct napi_struct *napi; + int cpu_id_modulus; + int num_tx_descs; + struct lio *lio; + int retval = 0; + int q, q_no; + int cpu_id; + + netdev = octeon_dev->props[ifidx].netdev; + + lio = GET_LIO(netdev); + + memset(&droq_ops, 0, sizeof(struct octeon_droq_ops)); + + droq_ops.fptr = liquidio_push_packet; + droq_ops.farg = netdev; + + droq_ops.poll_mode = 1; + droq_ops.napi_fn = liquidio_napi_drv_callback; + cpu_id = 0; + cpu_id_modulus = num_present_cpus(); + + /* set up DROQs. */ + for (q = 0; q < num_oqs; q++) { + q_no = lio->linfo.rxpciq[q].s.q_no; + dev_dbg(&octeon_dev->pci_dev->dev, + "%s index:%d linfo.rxpciq.s.q_no:%d\n", + __func__, q, q_no); + retval = octeon_setup_droq( + octeon_dev, q_no, + CFG_GET_NUM_RX_DESCS_NIC_IF(octeon_get_conf(octeon_dev), + lio->ifidx), + CFG_GET_NUM_RX_BUF_SIZE_NIC_IF(octeon_get_conf(octeon_dev), + lio->ifidx), + NULL); + if (retval) { + dev_err(&octeon_dev->pci_dev->dev, + "%s : Runtime DROQ(RxQ) creation failed.\n", + __func__); + return 1; + } + + droq = octeon_dev->droq[q_no]; + napi = &droq->napi; + dev_dbg(&octeon_dev->pci_dev->dev, "netif_napi_add netdev:%llx oct:%llx\n", + (u64)netdev, (u64)octeon_dev); + netif_napi_add(netdev, napi, liquidio_napi_poll, 64); + + /* designate a CPU for this droq */ + droq->cpu_id = cpu_id; + cpu_id++; + if (cpu_id >= cpu_id_modulus) + cpu_id = 0; + + octeon_register_droq_ops(octeon_dev, q_no, &droq_ops); + } + + if (OCTEON_CN23XX_PF(octeon_dev) || OCTEON_CN23XX_VF(octeon_dev)) { + /* 23XX PF/VF can send/recv control messages (via the first + * PF/VF-owned droq) from the firmware even if the ethX + * interface is down, so that's why poll_mode must be off + * for the first droq. + */ + octeon_dev->droq[0]->ops.poll_mode = 0; + } + + /* set up IQs. */ + for (q = 0; q < num_iqs; q++) { + num_tx_descs = CFG_GET_NUM_TX_DESCS_NIC_IF( + octeon_get_conf(octeon_dev), lio->ifidx); + retval = octeon_setup_iq(octeon_dev, ifidx, q, + lio->linfo.txpciq[q], num_tx_descs, + netdev_get_tx_queue(netdev, q)); + if (retval) { + dev_err(&octeon_dev->pci_dev->dev, + " %s : Runtime IQ(TxQ) creation failed.\n", + __func__); + return 1; + } + + /* XPS */ + if (!OCTEON_CN23XX_VF(octeon_dev) && octeon_dev->msix_on && + octeon_dev->ioq_vector) { + struct octeon_ioq_vector *ioq_vector; + + ioq_vector = &octeon_dev->ioq_vector[q]; + netif_set_xps_queue(netdev, + &ioq_vector->affinity_mask, + ioq_vector->iq_index); + } + } + + return 0; +} + +static +int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret) +{ + struct octeon_device *oct = droq->oct_dev; + struct octeon_device_priv *oct_priv = + (struct octeon_device_priv *)oct->priv; + + if (droq->ops.poll_mode) { + droq->ops.napi_fn(droq); + } else { + if (ret & MSIX_PO_INT) { + if (OCTEON_CN23XX_VF(oct)) + dev_err(&oct->pci_dev->dev, + "should not come here should not get rx when poll mode = 0 for vf\n"); + tasklet_schedule(&oct_priv->droq_tasklet); + return 1; + } + /* this will be flushed periodically by check iq db */ + if (ret & MSIX_PI_INT) + return 0; + } + + return 0; +} + +irqreturn_t +liquidio_msix_intr_handler(int irq __attribute__((unused)), void *dev) +{ + struct octeon_ioq_vector *ioq_vector = (struct octeon_ioq_vector *)dev; + struct octeon_device *oct = ioq_vector->oct_dev; + struct octeon_droq *droq = oct->droq[ioq_vector->droq_index]; + u64 ret; + + ret = oct->fn_list.msix_interrupt_handler(ioq_vector); + + if (ret & MSIX_PO_INT || ret & MSIX_PI_INT) + liquidio_schedule_msix_droq_pkt_handler(droq, ret); + + return IRQ_HANDLED; +} + +/** + * \brief Droq packet processor sceduler + * @param oct octeon device + */ +static void liquidio_schedule_droq_pkt_handlers(struct octeon_device *oct) +{ + struct octeon_device_priv *oct_priv = + (struct octeon_device_priv *)oct->priv; + struct octeon_droq *droq; + u64 oq_no; + + if (oct->int_status & OCT_DEV_INTR_PKT_DATA) { + for (oq_no = 0; oq_no < MAX_OCTEON_OUTPUT_QUEUES(oct); + oq_no++) { + if (!(oct->droq_intr & BIT_ULL(oq_no))) + continue; + + droq = oct->droq[oq_no]; + + if (droq->ops.poll_mode) { + droq->ops.napi_fn(droq); + oct_priv->napi_mask |= (1 << oq_no); + } else { + tasklet_schedule(&oct_priv->droq_tasklet); + } + } + } +} + +/** + * \brief Interrupt handler for octeon + * @param irq unused + * @param dev octeon device + */ +static +irqreturn_t liquidio_legacy_intr_handler(int irq __attribute__((unused)), + void *dev) +{ + struct octeon_device *oct = (struct octeon_device *)dev; + irqreturn_t ret; + + /* Disable our interrupts for the duration of ISR */ + oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR); + + ret = oct->fn_list.process_interrupt_regs(oct); + + if (ret == IRQ_HANDLED) + liquidio_schedule_droq_pkt_handlers(oct); + + /* Re-enable our interrupts */ + if (!(atomic_read(&oct->status) == OCT_DEV_IN_RESET)) + oct->fn_list.enable_interrupt(oct, OCTEON_ALL_INTR); + + return ret; +} + +/** + * \brief Setup interrupt for octeon device + * @param oct octeon device + * + * Enable interrupt in Octeon device as given in the PCI interrupt mask. + */ +int octeon_setup_interrupt(struct octeon_device *oct, u32 num_ioqs) +{ + struct msix_entry *msix_entries; + char *queue_irq_names = NULL; + int i, num_interrupts = 0; + int num_alloc_ioq_vectors; + char *aux_irq_name = NULL; + int num_ioq_vectors; + int irqret, err; + + oct->num_msix_irqs = num_ioqs; + if (oct->msix_on) { + if (OCTEON_CN23XX_PF(oct)) { + num_interrupts = MAX_IOQ_INTERRUPTS_PER_PF + 1; + + /* one non ioq interrupt for handling + * sli_mac_pf_int_sum + */ + oct->num_msix_irqs += 1; + } else if (OCTEON_CN23XX_VF(oct)) { + num_interrupts = MAX_IOQ_INTERRUPTS_PER_VF; + } + + /* allocate storage for the names assigned to each irq */ + oct->irq_name_storage = + kcalloc(num_interrupts, INTRNAMSIZ, GFP_KERNEL); + if (!oct->irq_name_storage) { + dev_err(&oct->pci_dev->dev, "Irq name storage alloc failed...\n"); + return -ENOMEM; + } + + queue_irq_names = oct->irq_name_storage; + + if (OCTEON_CN23XX_PF(oct)) + aux_irq_name = &queue_irq_names + [IRQ_NAME_OFF(MAX_IOQ_INTERRUPTS_PER_PF)]; + + oct->msix_entries = kcalloc(oct->num_msix_irqs, + sizeof(struct msix_entry), + GFP_KERNEL); + if (!oct->msix_entries) { + dev_err(&oct->pci_dev->dev, "Memory Alloc failed...\n"); + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + return -ENOMEM; + } + + msix_entries = (struct msix_entry *)oct->msix_entries; + + /*Assumption is that pf msix vectors start from pf srn to pf to + * trs and not from 0. if not change this code + */ + if (OCTEON_CN23XX_PF(oct)) { + for (i = 0; i < oct->num_msix_irqs - 1; i++) + msix_entries[i].entry = + oct->sriov_info.pf_srn + i; + + msix_entries[oct->num_msix_irqs - 1].entry = + oct->sriov_info.trs; + } else if (OCTEON_CN23XX_VF(oct)) { + for (i = 0; i < oct->num_msix_irqs; i++) + msix_entries[i].entry = i; + } + num_alloc_ioq_vectors = pci_enable_msix_range( + oct->pci_dev, msix_entries, + oct->num_msix_irqs, + oct->num_msix_irqs); + if (num_alloc_ioq_vectors < 0) { + dev_err(&oct->pci_dev->dev, "unable to Allocate MSI-X interrupts\n"); + kfree(oct->msix_entries); + oct->msix_entries = NULL; + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + return num_alloc_ioq_vectors; + } + + dev_dbg(&oct->pci_dev->dev, "OCTEON: Enough MSI-X interrupts are allocated...\n"); + + num_ioq_vectors = oct->num_msix_irqs; + /** For PF, there is one non-ioq interrupt handler */ + if (OCTEON_CN23XX_PF(oct)) { + num_ioq_vectors -= 1; + + snprintf(aux_irq_name, INTRNAMSIZ, + "LiquidIO%u-pf%u-aux", oct->octeon_id, + oct->pf_num); + irqret = request_irq( + msix_entries[num_ioq_vectors].vector, + liquidio_legacy_intr_handler, 0, + aux_irq_name, oct); + if (irqret) { + dev_err(&oct->pci_dev->dev, + "Request_irq failed for MSIX interrupt Error: %d\n", + irqret); + pci_disable_msix(oct->pci_dev); + kfree(oct->msix_entries); + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + oct->msix_entries = NULL; + return irqret; + } + } + for (i = 0 ; i < num_ioq_vectors ; i++) { + if (OCTEON_CN23XX_PF(oct)) + snprintf(&queue_irq_names[IRQ_NAME_OFF(i)], + INTRNAMSIZ, "LiquidIO%u-pf%u-rxtx-%u", + oct->octeon_id, oct->pf_num, i); + + if (OCTEON_CN23XX_VF(oct)) + snprintf(&queue_irq_names[IRQ_NAME_OFF(i)], + INTRNAMSIZ, "LiquidIO%u-vf%u-rxtx-%u", + oct->octeon_id, oct->vf_num, i); + + irqret = request_irq(msix_entries[i].vector, + liquidio_msix_intr_handler, 0, + &queue_irq_names[IRQ_NAME_OFF(i)], + &oct->ioq_vector[i]); + + if (irqret) { + dev_err(&oct->pci_dev->dev, + "Request_irq failed for MSIX interrupt Error: %d\n", + irqret); + /** Freeing the non-ioq irq vector here . */ + free_irq(msix_entries[num_ioq_vectors].vector, + oct); + + while (i) { + i--; + /** clearing affinity mask. */ + irq_set_affinity_hint( + msix_entries[i].vector, + NULL); + free_irq(msix_entries[i].vector, + &oct->ioq_vector[i]); + } + pci_disable_msix(oct->pci_dev); + kfree(oct->msix_entries); + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + oct->msix_entries = NULL; + return irqret; + } + oct->ioq_vector[i].vector = msix_entries[i].vector; + /* assign the cpu mask for this msix interrupt vector */ + irq_set_affinity_hint(msix_entries[i].vector, + &oct->ioq_vector[i].affinity_mask + ); + } + dev_dbg(&oct->pci_dev->dev, "OCTEON[%d]: MSI-X enabled\n", + oct->octeon_id); + } else { + err = pci_enable_msi(oct->pci_dev); + if (err) + dev_warn(&oct->pci_dev->dev, "Reverting to legacy interrupts. Error: %d\n", + err); + else + oct->flags |= LIO_FLAG_MSI_ENABLED; + + /* allocate storage for the names assigned to the irq */ + oct->irq_name_storage = kcalloc(1, INTRNAMSIZ, GFP_KERNEL); + if (!oct->irq_name_storage) + return -ENOMEM; + + queue_irq_names = oct->irq_name_storage; + + if (OCTEON_CN23XX_PF(oct)) + snprintf(&queue_irq_names[IRQ_NAME_OFF(0)], INTRNAMSIZ, + "LiquidIO%u-pf%u-rxtx-%u", + oct->octeon_id, oct->pf_num, 0); + + if (OCTEON_CN23XX_VF(oct)) + snprintf(&queue_irq_names[IRQ_NAME_OFF(0)], INTRNAMSIZ, + "LiquidIO%u-vf%u-rxtx-%u", + oct->octeon_id, oct->vf_num, 0); + + irqret = request_irq(oct->pci_dev->irq, + liquidio_legacy_intr_handler, + IRQF_SHARED, + &queue_irq_names[IRQ_NAME_OFF(0)], oct); + if (irqret) { + if (oct->flags & LIO_FLAG_MSI_ENABLED) + pci_disable_msi(oct->pci_dev); + dev_err(&oct->pci_dev->dev, "Request IRQ failed with code: %d\n", + irqret); + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + return irqret; + } + } + return 0; +} diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index ebd353bc78ff..a63ddf07f168 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -31,6 +31,7 @@ #include "cn23xx_pf_device.h" #include "cn23xx_vf_device.h" +static int lio_reset_queues(struct net_device *netdev, uint32_t num_qs); static int octnet_get_link_stats(struct net_device *netdev); struct oct_intrmod_context { @@ -105,6 +106,7 @@ static const char oct_stats_strings[][ETH_GSTRING_LEN] = { "tx_total_sent", "tx_total_fwd", "tx_err_pko", + "tx_err_pki", "tx_err_link", "tx_err_drop", @@ -299,6 +301,35 @@ lio_get_vf_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) strncpy(drvinfo->bus_info, pci_name(oct->pci_dev), 32); } +static int +lio_send_queue_count_update(struct net_device *netdev, uint32_t num_queues) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + struct octnic_ctrl_pkt nctrl; + int ret = 0; + + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); + + nctrl.ncmd.u64 = 0; + nctrl.ncmd.s.cmd = OCTNET_CMD_QUEUE_COUNT_CTL; + nctrl.ncmd.s.param1 = num_queues; + nctrl.ncmd.s.param2 = num_queues; + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + nctrl.wait_time = 100; + nctrl.netpndev = (u64)netdev; + nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; + + ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl); + if (ret < 0) { + dev_err(&oct->pci_dev->dev, "Failed to send Queue reset command (ret: 0x%x)\n", + ret); + return -1; + } + + return 0; +} + static void lio_ethtool_get_channels(struct net_device *dev, struct ethtool_channels *channel) @@ -306,6 +337,7 @@ lio_ethtool_get_channels(struct net_device *dev, struct lio *lio = GET_LIO(dev); struct octeon_device *oct = lio->oct_dev; u32 max_rx = 0, max_tx = 0, tx_count = 0, rx_count = 0; + u32 combined_count = 0, max_combined = 0; if (OCTEON_CN6XXX(oct)) { struct octeon_config *conf6x = CHIP_CONF(oct, cn6xxx); @@ -315,22 +347,137 @@ lio_ethtool_get_channels(struct net_device *dev, rx_count = CFG_GET_NUM_RXQS_NIC_IF(conf6x, lio->ifidx); tx_count = CFG_GET_NUM_TXQS_NIC_IF(conf6x, lio->ifidx); } else if (OCTEON_CN23XX_PF(oct)) { - - max_rx = oct->sriov_info.num_pf_rings; - max_tx = oct->sriov_info.num_pf_rings; - rx_count = lio->linfo.num_rxpciq; - tx_count = lio->linfo.num_txpciq; + max_combined = lio->linfo.num_txpciq; + combined_count = oct->num_iqs; } else if (OCTEON_CN23XX_VF(oct)) { - max_tx = oct->sriov_info.rings_per_vf; - max_rx = oct->sriov_info.rings_per_vf; - rx_count = lio->linfo.num_rxpciq; - tx_count = lio->linfo.num_txpciq; + u64 reg_val = 0ULL; + u64 ctrl = CN23XX_VF_SLI_IQ_PKT_CONTROL64(0); + + reg_val = octeon_read_csr64(oct, ctrl); + reg_val = reg_val >> CN23XX_PKT_INPUT_CTL_RPVF_POS; + max_combined = reg_val & CN23XX_PKT_INPUT_CTL_RPVF_MASK; + combined_count = oct->num_iqs; } channel->max_rx = max_rx; channel->max_tx = max_tx; + channel->max_combined = max_combined; channel->rx_count = rx_count; channel->tx_count = tx_count; + channel->combined_count = combined_count; +} + +static int +lio_irq_reallocate_irqs(struct octeon_device *oct, uint32_t num_ioqs) +{ + struct msix_entry *msix_entries; + int num_msix_irqs = 0; + int i; + + if (!oct->msix_on) + return 0; + + /* Disable the input and output queues now. No more packets will + * arrive from Octeon. + */ + oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR); + + if (oct->msix_on) { + if (OCTEON_CN23XX_PF(oct)) + num_msix_irqs = oct->num_msix_irqs - 1; + else if (OCTEON_CN23XX_VF(oct)) + num_msix_irqs = oct->num_msix_irqs; + + msix_entries = (struct msix_entry *)oct->msix_entries; + for (i = 0; i < num_msix_irqs; i++) { + if (oct->ioq_vector[i].vector) { + /* clear the affinity_cpumask */ + irq_set_affinity_hint(msix_entries[i].vector, + NULL); + free_irq(msix_entries[i].vector, + &oct->ioq_vector[i]); + oct->ioq_vector[i].vector = 0; + } + } + + /* non-iov vector's argument is oct struct */ + if (OCTEON_CN23XX_PF(oct)) + free_irq(msix_entries[i].vector, oct); + + pci_disable_msix(oct->pci_dev); + kfree(oct->msix_entries); + oct->msix_entries = NULL; + } + + kfree(oct->irq_name_storage); + oct->irq_name_storage = NULL; + if (octeon_setup_interrupt(oct, num_ioqs)) { + dev_info(&oct->pci_dev->dev, "Setup interrupt failed\n"); + return 1; + } + + /* Enable Octeon device interrupts */ + oct->fn_list.enable_interrupt(oct, OCTEON_ALL_INTR); + + return 0; +} + +static int +lio_ethtool_set_channels(struct net_device *dev, + struct ethtool_channels *channel) +{ + u32 combined_count, max_combined; + struct lio *lio = GET_LIO(dev); + struct octeon_device *oct = lio->oct_dev; + int stopped = 0; + + if (strcmp(oct->fw_info.liquidio_firmware_version, "1.6.1") < 0) { + dev_err(&oct->pci_dev->dev, "Minimum firmware version required is 1.6.1\n"); + return -EINVAL; + } + + if (!channel->combined_count || channel->other_count || + channel->rx_count || channel->tx_count) + return -EINVAL; + + combined_count = channel->combined_count; + + if (OCTEON_CN23XX_PF(oct)) { + max_combined = channel->max_combined; + } else if (OCTEON_CN23XX_VF(oct)) { + u64 reg_val = 0ULL; + u64 ctrl = CN23XX_VF_SLI_IQ_PKT_CONTROL64(0); + + reg_val = octeon_read_csr64(oct, ctrl); + reg_val = reg_val >> CN23XX_PKT_INPUT_CTL_RPVF_POS; + max_combined = reg_val & CN23XX_PKT_INPUT_CTL_RPVF_MASK; + } else { + return -EINVAL; + } + + if (combined_count > max_combined || combined_count < 1) + return -EINVAL; + + if (combined_count == oct->num_iqs) + return 0; + + ifstate_set(lio, LIO_IFSTATE_RESETTING); + + if (netif_running(dev)) { + dev->netdev_ops->ndo_stop(dev); + stopped = 1; + } + + if (lio_reset_queues(dev, combined_count)) + return -EINVAL; + + lio_irq_reallocate_irqs(oct, combined_count); + if (stopped) + dev->netdev_ops->ndo_open(dev); + + ifstate_reset(lio, LIO_IFSTATE_RESETTING); + + return 0; } static int lio_get_eeprom_len(struct net_device *netdev) @@ -577,23 +724,18 @@ static int lio_set_phys_id(struct net_device *netdev, break; case ETHTOOL_ID_ON: - if (oct->chip_id == OCTEON_CN66XX) { + if (oct->chip_id == OCTEON_CN66XX) octnet_gpio_access(netdev, VITESSE_PHY_GPIO_CFG, VITESSE_PHY_GPIO_HIGH); - - } else if (oct->chip_id == OCTEON_CN68XX) { - return -EINVAL; - } else { + else return -EINVAL; - } + break; case ETHTOOL_ID_OFF: if (oct->chip_id == OCTEON_CN66XX) octnet_gpio_access(netdev, VITESSE_PHY_GPIO_CFG, VITESSE_PHY_GPIO_LOW); - else if (oct->chip_id == OCTEON_CN68XX) - return -EINVAL; else return -EINVAL; @@ -641,6 +783,9 @@ lio_ethtool_get_ringparam(struct net_device *netdev, u32 tx_max_pending = 0, rx_max_pending = 0, tx_pending = 0, rx_pending = 0; + if (ifstate_check(lio, LIO_IFSTATE_RESETTING)) + return; + if (OCTEON_CN6XXX(oct)) { struct octeon_config *conf6x = CHIP_CONF(oct, cn6xxx); @@ -648,33 +793,147 @@ lio_ethtool_get_ringparam(struct net_device *netdev, rx_max_pending = CN6XXX_MAX_OQ_DESCRIPTORS; rx_pending = CFG_GET_NUM_RX_DESCS_NIC_IF(conf6x, lio->ifidx); tx_pending = CFG_GET_NUM_TX_DESCS_NIC_IF(conf6x, lio->ifidx); - } else if (OCTEON_CN23XX_PF(oct)) { - struct octeon_config *conf23 = CHIP_CONF(oct, cn23xx_pf); - + } else if (OCTEON_CN23XX_PF(oct) || OCTEON_CN23XX_VF(oct)) { tx_max_pending = CN23XX_MAX_IQ_DESCRIPTORS; rx_max_pending = CN23XX_MAX_OQ_DESCRIPTORS; - rx_pending = CFG_GET_NUM_RX_DESCS_NIC_IF(conf23, lio->ifidx); - tx_pending = CFG_GET_NUM_TX_DESCS_NIC_IF(conf23, lio->ifidx); - } - - if (lio->mtu > OCTNET_DEFAULT_FRM_SIZE - OCTNET_FRM_HEADER_SIZE) { - ering->rx_pending = 0; - ering->rx_max_pending = 0; - ering->rx_mini_pending = 0; - ering->rx_jumbo_pending = rx_pending; - ering->rx_mini_max_pending = 0; - ering->rx_jumbo_max_pending = rx_max_pending; - } else { - ering->rx_pending = rx_pending; - ering->rx_max_pending = rx_max_pending; - ering->rx_mini_pending = 0; - ering->rx_jumbo_pending = 0; - ering->rx_mini_max_pending = 0; - ering->rx_jumbo_max_pending = 0; + rx_pending = oct->droq[0]->max_count; + tx_pending = oct->instr_queue[0]->max_count; } ering->tx_pending = tx_pending; ering->tx_max_pending = tx_max_pending; + ering->rx_pending = rx_pending; + ering->rx_max_pending = rx_max_pending; + ering->rx_mini_pending = 0; + ering->rx_jumbo_pending = 0; + ering->rx_mini_max_pending = 0; + ering->rx_jumbo_max_pending = 0; +} + +static int lio_reset_queues(struct net_device *netdev, uint32_t num_qs) +{ + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + struct napi_struct *napi, *n; + int i, update = 0; + + if (wait_for_pending_requests(oct)) + dev_err(&oct->pci_dev->dev, "There were pending requests\n"); + + if (lio_wait_for_instr_fetch(oct)) + dev_err(&oct->pci_dev->dev, "IQ had pending instructions\n"); + + if (octeon_set_io_queues_off(oct)) { + dev_err(&oct->pci_dev->dev, "setting io queues off failed\n"); + return -1; + } + + /* Disable the input and output queues now. No more packets will + * arrive from Octeon. + */ + oct->fn_list.disable_io_queues(oct); + /* Delete NAPI */ + list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) + netif_napi_del(napi); + + if (num_qs != oct->num_iqs) { + netif_set_real_num_rx_queues(netdev, num_qs); + netif_set_real_num_tx_queues(netdev, num_qs); + update = 1; + } + + for (i = 0; i < MAX_OCTEON_OUTPUT_QUEUES(oct); i++) { + if (!(oct->io_qmask.oq & BIT_ULL(i))) + continue; + octeon_delete_droq(oct, i); + } + + for (i = 0; i < MAX_OCTEON_INSTR_QUEUES(oct); i++) { + if (!(oct->io_qmask.iq & BIT_ULL(i))) + continue; + octeon_delete_instr_queue(oct, i); + } + + if (oct->fn_list.setup_device_regs(oct)) { + dev_err(&oct->pci_dev->dev, "Failed to configure device registers\n"); + return -1; + } + + if (liquidio_setup_io_queues(oct, 0, num_qs, num_qs)) { + dev_err(&oct->pci_dev->dev, "IO queues initialization failed\n"); + return -1; + } + + /* Enable the input and output queues for this Octeon device */ + if (oct->fn_list.enable_io_queues(oct)) { + dev_err(&oct->pci_dev->dev, "Failed to enable input/output queues"); + return -1; + } + + if (update && lio_send_queue_count_update(netdev, num_qs)) + return -1; + + return 0; +} + +static int lio_ethtool_set_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ering) +{ + u32 rx_count, tx_count, rx_count_old, tx_count_old; + struct lio *lio = GET_LIO(netdev); + struct octeon_device *oct = lio->oct_dev; + int stopped = 0; + + if (!OCTEON_CN23XX_PF(oct) && !OCTEON_CN23XX_VF(oct)) + return -EINVAL; + + if (ering->rx_mini_pending || ering->rx_jumbo_pending) + return -EINVAL; + + rx_count = clamp_t(u32, ering->rx_pending, CN23XX_MIN_OQ_DESCRIPTORS, + CN23XX_MAX_OQ_DESCRIPTORS); + tx_count = clamp_t(u32, ering->tx_pending, CN23XX_MIN_IQ_DESCRIPTORS, + CN23XX_MAX_IQ_DESCRIPTORS); + + rx_count_old = oct->droq[0]->max_count; + tx_count_old = oct->instr_queue[0]->max_count; + + if (rx_count == rx_count_old && tx_count == tx_count_old) + return 0; + + ifstate_set(lio, LIO_IFSTATE_RESETTING); + + if (netif_running(netdev)) { + netdev->netdev_ops->ndo_stop(netdev); + stopped = 1; + } + + /* Change RX/TX DESCS count */ + if (tx_count != tx_count_old) + CFG_SET_NUM_TX_DESCS_NIC_IF(octeon_get_conf(oct), lio->ifidx, + tx_count); + if (rx_count != rx_count_old) + CFG_SET_NUM_RX_DESCS_NIC_IF(octeon_get_conf(oct), lio->ifidx, + rx_count); + + if (lio_reset_queues(netdev, lio->linfo.num_txpciq)) + goto err_lio_reset_queues; + + if (stopped) + netdev->netdev_ops->ndo_open(netdev); + + ifstate_reset(lio, LIO_IFSTATE_RESETTING); + + return 0; + +err_lio_reset_queues: + if (tx_count != tx_count_old) + CFG_SET_NUM_TX_DESCS_NIC_IF(octeon_get_conf(oct), lio->ifidx, + tx_count_old); + if (rx_count != rx_count_old) + CFG_SET_NUM_RX_DESCS_NIC_IF(octeon_get_conf(oct), lio->ifidx, + rx_count_old); + return -EINVAL; } static u32 lio_get_msglevel(struct net_device *netdev) @@ -795,6 +1054,9 @@ lio_get_ethtool_stats(struct net_device *netdev, struct net_device_stats *netstats = &netdev->stats; int i = 0, j; + if (ifstate_check(lio, LIO_IFSTATE_RESETTING)) + return; + netdev->netdev_ops->ndo_get_stats(netdev); octnet_get_link_stats(netdev); @@ -826,6 +1088,8 @@ lio_get_ethtool_stats(struct net_device *netdev, data[i++] = CVM_CAST64(oct_dev->link_stats.fromhost.fw_total_fwd); /*per_core_stats[j].link_stats[i].fromhost.fw_err_pko */ data[i++] = CVM_CAST64(oct_dev->link_stats.fromhost.fw_err_pko); + /*per_core_stats[j].link_stats[i].fromhost.fw_err_pki */ + data[i++] = CVM_CAST64(oct_dev->link_stats.fromhost.fw_err_pki); /*per_core_stats[j].link_stats[i].fromhost.fw_err_link */ data[i++] = CVM_CAST64(oct_dev->link_stats.fromhost.fw_err_link); /*per_core_stats[cvmx_get_core_num()].link_stats[idx].fromhost. @@ -1057,6 +1321,9 @@ static void lio_vf_get_ethtool_stats(struct net_device *netdev, struct octeon_device *oct_dev = lio->oct_dev; int i = 0, j, vj; + if (ifstate_check(lio, LIO_IFSTATE_RESETTING)) + return; + netdev->netdev_ops->ndo_get_stats(netdev); /* sum of oct->droq[oq_no]->stats->rx_pkts_received */ data[i++] = CVM_CAST64(netstats->rx_packets); @@ -1079,7 +1346,7 @@ static void lio_vf_get_ethtool_stats(struct net_device *netdev, /* lio->link_changes */ data[i++] = CVM_CAST64(lio->link_changes); - for (vj = 0; vj < lio->linfo.num_txpciq; vj++) { + for (vj = 0; vj < oct_dev->num_iqs; vj++) { j = lio->linfo.txpciq[vj].s.q_no; /* packets to network port */ @@ -1121,7 +1388,7 @@ static void lio_vf_get_ethtool_stats(struct net_device *netdev, } /* RX */ - for (vj = 0; vj < lio->linfo.num_rxpciq; vj++) { + for (vj = 0; vj < oct_dev->num_oqs; vj++) { j = lio->linfo.rxpciq[vj].s.q_no; /* packets send to TCP/IP network stack */ @@ -1568,6 +1835,7 @@ octnet_nic_stats_callback(struct octeon_device *oct_dev, tstats->fw_total_sent = rsp_tstats->fw_total_sent; tstats->fw_total_fwd = rsp_tstats->fw_total_fwd; tstats->fw_err_pko = rsp_tstats->fw_err_pko; + tstats->fw_err_pki = rsp_tstats->fw_err_pki; tstats->fw_err_link = rsp_tstats->fw_err_link; tstats->fw_err_drop = rsp_tstats->fw_err_drop; tstats->fw_tso = rsp_tstats->fw_tso; @@ -2587,7 +2855,9 @@ static const struct ethtool_ops lio_ethtool_ops = { .get_link = ethtool_op_get_link, .get_drvinfo = lio_get_drvinfo, .get_ringparam = lio_ethtool_get_ringparam, + .set_ringparam = lio_ethtool_set_ringparam, .get_channels = lio_ethtool_get_channels, + .set_channels = lio_ethtool_set_channels, .set_phys_id = lio_set_phys_id, .get_eeprom_len = lio_get_eeprom_len, .get_eeprom = lio_get_eeprom, @@ -2612,7 +2882,9 @@ static const struct ethtool_ops lio_vf_ethtool_ops = { .get_link = ethtool_op_get_link, .get_drvinfo = lio_get_vf_drvinfo, .get_ringparam = lio_ethtool_get_ringparam, + .set_ringparam = lio_ethtool_set_ringparam, .get_channels = lio_ethtool_get_channels, + .set_channels = lio_ethtool_set_channels, .get_strings = lio_vf_get_strings, .get_ethtool_stats = lio_vf_get_ethtool_stats, .get_regs_len = lio_get_regs_len, diff --git a/drivers/net/ethernet/cavium/liquidio/lio_main.c b/drivers/net/ethernet/cavium/liquidio/lio_main.c index 120b6e537b28..e7f54948173f 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_main.c @@ -39,10 +39,14 @@ MODULE_AUTHOR("Cavium Networks, <support@cavium.com>"); MODULE_DESCRIPTION("Cavium LiquidIO Intelligent Server Adapter Driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(LIQUIDIO_VERSION); -MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_210SV_NAME LIO_FW_NAME_SUFFIX); -MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_210NV_NAME LIO_FW_NAME_SUFFIX); -MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_410NV_NAME LIO_FW_NAME_SUFFIX); -MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_23XX_NAME LIO_FW_NAME_SUFFIX); +MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_210SV_NAME + "_" LIO_FW_NAME_TYPE_NIC LIO_FW_NAME_SUFFIX); +MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_210NV_NAME + "_" LIO_FW_NAME_TYPE_NIC LIO_FW_NAME_SUFFIX); +MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_410NV_NAME + "_" LIO_FW_NAME_TYPE_NIC LIO_FW_NAME_SUFFIX); +MODULE_FIRMWARE(LIO_FW_DIR LIO_FW_BASE_NAME LIO_23XX_NAME + "_" LIO_FW_NAME_TYPE_NIC LIO_FW_NAME_SUFFIX); static int ddr_timeout = 10000; module_param(ddr_timeout, int, 0644); @@ -55,11 +59,24 @@ static int debug = -1; module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "NETIF_MSG debug bits"); -static char fw_type[LIO_MAX_FW_TYPE_LEN]; -module_param_string(fw_type, fw_type, sizeof(fw_type), 0000); -MODULE_PARM_DESC(fw_type, "Type of firmware to be loaded. Default \"nic\""); +static char fw_type[LIO_MAX_FW_TYPE_LEN] = LIO_FW_NAME_TYPE_NIC; +module_param_string(fw_type, fw_type, sizeof(fw_type), 0444); +MODULE_PARM_DESC(fw_type, "Type of firmware to be loaded. Default \"nic\". Use \"none\" to load firmware from flash."); -static int ptp_enable = 1; +static u32 console_bitmask; +module_param(console_bitmask, int, 0644); +MODULE_PARM_DESC(console_bitmask, + "Bitmask indicating which consoles have debug output redirected to syslog."); + +/** + * \brief determines if a given console has debug enabled. + * @param console console to check + * @returns 1 = enabled. 0 otherwise + */ +static int octeon_console_debug_enabled(u32 console) +{ + return (console_bitmask >> (console)) & 0x1; +} /* Polling interval for determining when NIC application is alive */ #define LIQUIDIO_STARTER_POLL_INTERVAL_MS 100 @@ -158,16 +175,13 @@ struct handshake { int started_ok; }; -struct octeon_device_priv { - /** Tasklet structures for this device. */ - struct tasklet_struct droq_tasklet; - unsigned long napi_mask; -}; - #ifdef CONFIG_PCI_IOV static int liquidio_enable_sriov(struct pci_dev *dev, int num_vfs); #endif +static int octeon_dbg_console_print(struct octeon_device *oct, u32 console_num, + char *prefix, char *suffix); + static int octeon_device_init(struct octeon_device *); static int liquidio_stop(struct net_device *netdev); static void liquidio_remove(struct pci_dev *pdev); @@ -256,32 +270,6 @@ static void force_io_queues_off(struct octeon_device *oct) } /** - * \brief wait for all pending requests to complete - * @param oct Pointer to Octeon device - * - * Called during shutdown sequence - */ -static int wait_for_pending_requests(struct octeon_device *oct) -{ - int i, pcount = 0; - - for (i = 0; i < 100; i++) { - pcount = - atomic_read(&oct->response_list - [OCTEON_ORDERED_SC_LIST].pending_req_count); - if (pcount) - schedule_timeout_uninterruptible(HZ / 10); - else - break; - } - - if (pcount) - return 1; - - return 0; -} - -/** * \brief Cause device to go quiet so it can be safely removed/reset/etc * @param oct Pointer to Octeon device */ @@ -572,7 +560,7 @@ static inline void txqs_wake(struct net_device *netdev) for (i = 0; i < netdev->num_tx_queues; i++) { int qno = lio->linfo.txpciq[i % - (lio->linfo.num_txpciq)].s.q_no; + lio->oct_dev->num_iqs].s.q_no; if (__netif_subqueue_stopped(netdev, i)) { INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, qno, @@ -652,7 +640,7 @@ static inline int check_txq_status(struct lio *lio) /* check each sub-queue state */ for (q = 0; q < numqs; q++) { iq = lio->linfo.txpciq[q % - (lio->linfo.num_txpciq)].s.q_no; + lio->oct_dev->num_iqs].s.q_no; if (octnet_iq_is_full(lio->oct_dev, iq)) continue; if (__netif_subqueue_stopped(lio->netdev, q)) { @@ -823,7 +811,8 @@ static void print_link_info(struct net_device *netdev) { struct lio *lio = GET_LIO(netdev); - if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED) { + if (!ifstate_check(lio, LIO_IFSTATE_RESETTING) && + ifstate_check(lio, LIO_IFSTATE_REGISTERED)) { struct oct_link_info *linfo = &lio->linfo; if (linfo->link.s.link_up) { @@ -912,295 +901,6 @@ static inline void update_link_status(struct net_device *netdev, } } -/* Runs in interrupt context. */ -static void update_txq_status(struct octeon_device *oct, int iq_num) -{ - struct net_device *netdev; - struct lio *lio; - struct octeon_instr_queue *iq = oct->instr_queue[iq_num]; - - netdev = oct->props[iq->ifidx].netdev; - - /* This is needed because the first IQ does not have - * a netdev associated with it. - */ - if (!netdev) - return; - - lio = GET_LIO(netdev); - if (netif_is_multiqueue(netdev)) { - if (__netif_subqueue_stopped(netdev, iq->q_index) && - lio->linfo.link.s.link_up && - (!octnet_iq_is_full(oct, iq_num))) { - INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq_num, - tx_restart, 1); - netif_wake_subqueue(netdev, iq->q_index); - } - } else if (netif_queue_stopped(netdev) && - lio->linfo.link.s.link_up && - (!octnet_iq_is_full(oct, lio->txq))) { - INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, - lio->txq, tx_restart, 1); - netif_wake_queue(netdev); - } -} - -static -int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret) -{ - struct octeon_device *oct = droq->oct_dev; - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; - - if (droq->ops.poll_mode) { - droq->ops.napi_fn(droq); - } else { - if (ret & MSIX_PO_INT) { - tasklet_schedule(&oct_priv->droq_tasklet); - return 1; - } - /* this will be flushed periodically by check iq db */ - if (ret & MSIX_PI_INT) - return 0; - } - return 0; -} - -/** - * \brief Droq packet processor sceduler - * @param oct octeon device - */ -static void liquidio_schedule_droq_pkt_handlers(struct octeon_device *oct) -{ - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; - u64 oq_no; - struct octeon_droq *droq; - - if (oct->int_status & OCT_DEV_INTR_PKT_DATA) { - for (oq_no = 0; oq_no < MAX_OCTEON_OUTPUT_QUEUES(oct); - oq_no++) { - if (!(oct->droq_intr & BIT_ULL(oq_no))) - continue; - - droq = oct->droq[oq_no]; - - if (droq->ops.poll_mode) { - droq->ops.napi_fn(droq); - oct_priv->napi_mask |= (1 << oq_no); - } else { - tasklet_schedule(&oct_priv->droq_tasklet); - } - } - } -} - -static irqreturn_t -liquidio_msix_intr_handler(int irq __attribute__((unused)), void *dev) -{ - u64 ret; - struct octeon_ioq_vector *ioq_vector = (struct octeon_ioq_vector *)dev; - struct octeon_device *oct = ioq_vector->oct_dev; - struct octeon_droq *droq = oct->droq[ioq_vector->droq_index]; - - ret = oct->fn_list.msix_interrupt_handler(ioq_vector); - - if ((ret & MSIX_PO_INT) || (ret & MSIX_PI_INT)) - liquidio_schedule_msix_droq_pkt_handler(droq, ret); - - return IRQ_HANDLED; -} - -/** - * \brief Interrupt handler for octeon - * @param irq unused - * @param dev octeon device - */ -static -irqreturn_t liquidio_legacy_intr_handler(int irq __attribute__((unused)), - void *dev) -{ - struct octeon_device *oct = (struct octeon_device *)dev; - irqreturn_t ret; - - /* Disable our interrupts for the duration of ISR */ - oct->fn_list.disable_interrupt(oct, OCTEON_ALL_INTR); - - ret = oct->fn_list.process_interrupt_regs(oct); - - if (ret == IRQ_HANDLED) - liquidio_schedule_droq_pkt_handlers(oct); - - /* Re-enable our interrupts */ - if (!(atomic_read(&oct->status) == OCT_DEV_IN_RESET)) - oct->fn_list.enable_interrupt(oct, OCTEON_ALL_INTR); - - return ret; -} - -/** - * \brief Setup interrupt for octeon device - * @param oct octeon device - * - * Enable interrupt in Octeon device as given in the PCI interrupt mask. - */ -static int octeon_setup_interrupt(struct octeon_device *oct) -{ - int irqret, err; - struct msix_entry *msix_entries; - int i; - int num_ioq_vectors; - int num_alloc_ioq_vectors; - char *queue_irq_names = NULL; - char *aux_irq_name = NULL; - - if (OCTEON_CN23XX_PF(oct) && oct->msix_on) { - oct->num_msix_irqs = oct->sriov_info.num_pf_rings; - /* one non ioq interrupt for handling sli_mac_pf_int_sum */ - oct->num_msix_irqs += 1; - - /* allocate storage for the names assigned to each irq */ - oct->irq_name_storage = - kcalloc((MAX_IOQ_INTERRUPTS_PER_PF + 1), INTRNAMSIZ, - GFP_KERNEL); - if (!oct->irq_name_storage) { - dev_err(&oct->pci_dev->dev, "Irq name storage alloc failed...\n"); - return -ENOMEM; - } - - queue_irq_names = oct->irq_name_storage; - aux_irq_name = &queue_irq_names - [IRQ_NAME_OFF(MAX_IOQ_INTERRUPTS_PER_PF)]; - - oct->msix_entries = kcalloc( - oct->num_msix_irqs, sizeof(struct msix_entry), GFP_KERNEL); - if (!oct->msix_entries) { - dev_err(&oct->pci_dev->dev, "Memory Alloc failed...\n"); - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return -ENOMEM; - } - - msix_entries = (struct msix_entry *)oct->msix_entries; - /*Assumption is that pf msix vectors start from pf srn to pf to - * trs and not from 0. if not change this code - */ - for (i = 0; i < oct->num_msix_irqs - 1; i++) - msix_entries[i].entry = oct->sriov_info.pf_srn + i; - msix_entries[oct->num_msix_irqs - 1].entry = - oct->sriov_info.trs; - num_alloc_ioq_vectors = pci_enable_msix_range( - oct->pci_dev, msix_entries, - oct->num_msix_irqs, - oct->num_msix_irqs); - if (num_alloc_ioq_vectors < 0) { - dev_err(&oct->pci_dev->dev, "unable to Allocate MSI-X interrupts\n"); - kfree(oct->msix_entries); - oct->msix_entries = NULL; - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return num_alloc_ioq_vectors; - } - dev_dbg(&oct->pci_dev->dev, "OCTEON: Enough MSI-X interrupts are allocated...\n"); - - num_ioq_vectors = oct->num_msix_irqs; - - /** For PF, there is one non-ioq interrupt handler */ - num_ioq_vectors -= 1; - - snprintf(aux_irq_name, INTRNAMSIZ, - "LiquidIO%u-pf%u-aux", oct->octeon_id, oct->pf_num); - irqret = request_irq(msix_entries[num_ioq_vectors].vector, - liquidio_legacy_intr_handler, 0, - aux_irq_name, oct); - if (irqret) { - dev_err(&oct->pci_dev->dev, - "OCTEON: Request_irq failed for MSIX interrupt Error: %d\n", - irqret); - pci_disable_msix(oct->pci_dev); - kfree(oct->msix_entries); - oct->msix_entries = NULL; - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return irqret; - } - - for (i = 0; i < num_ioq_vectors; i++) { - snprintf(&queue_irq_names[IRQ_NAME_OFF(i)], INTRNAMSIZ, - "LiquidIO%u-pf%u-rxtx-%u", - oct->octeon_id, oct->pf_num, i); - - irqret = request_irq(msix_entries[i].vector, - liquidio_msix_intr_handler, 0, - &queue_irq_names[IRQ_NAME_OFF(i)], - &oct->ioq_vector[i]); - if (irqret) { - dev_err(&oct->pci_dev->dev, - "OCTEON: Request_irq failed for MSIX interrupt Error: %d\n", - irqret); - /** Freeing the non-ioq irq vector here . */ - free_irq(msix_entries[num_ioq_vectors].vector, - oct); - - while (i) { - i--; - /** clearing affinity mask. */ - irq_set_affinity_hint( - msix_entries[i].vector, NULL); - free_irq(msix_entries[i].vector, - &oct->ioq_vector[i]); - } - pci_disable_msix(oct->pci_dev); - kfree(oct->msix_entries); - oct->msix_entries = NULL; - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return irqret; - } - oct->ioq_vector[i].vector = msix_entries[i].vector; - /* assign the cpu mask for this msix interrupt vector */ - irq_set_affinity_hint( - msix_entries[i].vector, - (&oct->ioq_vector[i].affinity_mask)); - } - dev_dbg(&oct->pci_dev->dev, "OCTEON[%d]: MSI-X enabled\n", - oct->octeon_id); - } else { - err = pci_enable_msi(oct->pci_dev); - if (err) - dev_warn(&oct->pci_dev->dev, "Reverting to legacy interrupts. Error: %d\n", - err); - else - oct->flags |= LIO_FLAG_MSI_ENABLED; - - /* allocate storage for the names assigned to the irq */ - oct->irq_name_storage = kcalloc(1, INTRNAMSIZ, GFP_KERNEL); - if (!oct->irq_name_storage) - return -ENOMEM; - - queue_irq_names = oct->irq_name_storage; - - snprintf(&queue_irq_names[IRQ_NAME_OFF(0)], INTRNAMSIZ, - "LiquidIO%u-pf%u-rxtx-%u", - oct->octeon_id, oct->pf_num, 0); - - irqret = request_irq(oct->pci_dev->irq, - liquidio_legacy_intr_handler, - IRQF_SHARED, - &queue_irq_names[IRQ_NAME_OFF(0)], oct); - if (irqret) { - if (oct->flags & LIO_FLAG_MSI_ENABLED) - pci_disable_msi(oct->pci_dev); - dev_err(&oct->pci_dev->dev, "Request IRQ failed with code: %d\n", - irqret); - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return irqret; - } - } - return 0; -} - static struct octeon_device *get_other_octeon_device(struct octeon_device *oct) { struct octeon_device *other_oct; @@ -1344,6 +1044,13 @@ liquidio_probe(struct pci_dev *pdev, if (pdev->device == OCTEON_CN23XX_PF_VID) oct_dev->msix_on = LIO_FLAG_MSIX_ENABLED; + /* Enable PTP for 6XXX Device */ + if (((pdev->device == OCTEON_CN66XX) || + (pdev->device == OCTEON_CN68XX))) + oct_dev->ptp_enable = true; + else + oct_dev->ptp_enable = false; + dev_info(&pdev->dev, "Initializing device %x:%x.\n", (u32)pdev->vendor, (u32)pdev->device); @@ -1415,6 +1122,33 @@ static bool fw_type_is_none(void) } /** + * \brief PCI FLR for each Octeon device. + * @param oct octeon device + */ +static void octeon_pci_flr(struct octeon_device *oct) +{ + int rc; + + pci_save_state(oct->pci_dev); + + pci_cfg_access_lock(oct->pci_dev); + + /* Quiesce the device completely */ + pci_write_config_word(oct->pci_dev, PCI_COMMAND, + PCI_COMMAND_INTX_DISABLE); + + rc = __pci_reset_function_locked(oct->pci_dev); + + if (rc != 0) + dev_err(&oct->pci_dev->dev, "Error %d resetting PCI function %d\n", + rc, oct->pf_num); + + pci_cfg_access_unlock(oct->pci_dev); + + pci_restore_state(oct->pci_dev); +} + +/** *\brief Destroy resources associated with octeon device * @param pdev PCI device structure * @param ent unused @@ -1474,11 +1208,15 @@ static void octeon_destroy_resources(struct octeon_device *oct) if (oct->msix_on) { msix_entries = (struct msix_entry *)oct->msix_entries; for (i = 0; i < oct->num_msix_irqs - 1; i++) { - /* clear the affinity_cpumask */ - irq_set_affinity_hint(msix_entries[i].vector, - NULL); - free_irq(msix_entries[i].vector, - &oct->ioq_vector[i]); + if (oct->ioq_vector[i].vector) { + /* clear the affinity_cpumask */ + irq_set_affinity_hint( + msix_entries[i].vector, + NULL); + free_irq(msix_entries[i].vector, + &oct->ioq_vector[i]); + oct->ioq_vector[i].vector = 0; + } } /* non-iov vector's argument is oct struct */ free_irq(msix_entries[i].vector, oct); @@ -1558,14 +1296,16 @@ static void octeon_destroy_resources(struct octeon_device *oct) case OCT_DEV_PCI_MAP_DONE: refcount = octeon_deregister_device(oct); - if (!fw_type_is_none()) { - /* Soft reset the octeon device before exiting. - * Implementation note: here, we reset the device - * if it is a CN6XXX OR the last CN23XX device. - */ - if (OCTEON_CN6XXX(oct) || !refcount) - oct->fn_list.soft_reset(oct); - } + /* Soft reset the octeon device before exiting. + * However, if fw was loaded from card (i.e. autoboot), + * perform an FLR instead. + * Implementation note: only soft-reset the device + * if it is a CN6XXX OR the LAST CN23XX device. + */ + if (fw_type_is_none()) + octeon_pci_flr(oct); + else if (OCTEON_CN6XXX(oct) || !refcount) + oct->fn_list.soft_reset(oct); octeon_unmap_pci_barx(oct, 0); octeon_unmap_pci_barx(oct, 1); @@ -1698,15 +1438,6 @@ static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) if (atomic_read(&lio->ifstate) & LIO_IFSTATE_RUNNING) liquidio_stop(netdev); - if (fw_type_is_none()) { - struct octnic_ctrl_pkt nctrl; - - memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); - nctrl.ncmd.s.cmd = OCTNET_CMD_RESET_PF; - nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; - octnet_send_nic_ctrl_pkt(oct, &nctrl); - } - if (oct->props[lio->ifidx].napi_enabled == 1) { list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) napi_disable(napi); @@ -1717,6 +1448,10 @@ static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) oct->droq[0]->ops.poll_mode = 0; } + /* Delete NAPI */ + list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) + netif_napi_del(napi); + if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED) unregister_netdev(netdev); @@ -1754,7 +1489,7 @@ static int liquidio_stop_nic_module(struct octeon_device *oct) for (i = 0; i < oct->ifcount; i++) { lio = GET_LIO(oct->props[i].netdev); - for (j = 0; j < lio->linfo.num_rxpciq; j++) + for (j = 0; j < oct->num_oqs; j++) octeon_unregister_droq_ops(oct, lio->linfo.rxpciq[j].s.q_no); } @@ -1825,6 +1560,13 @@ static int octeon_chip_specific_setup(struct octeon_device *oct) case OCTEON_CN23XX_PCIID_PF: oct->chip_id = OCTEON_CN23XX_PF_VID; ret = setup_cn23xx_octeon_pf_device(oct); + if (ret) + break; +#ifdef CONFIG_PCI_IOV + if (!ret) + pci_sriov_set_totalvfs(oct->pci_dev, + oct->sriov_info.max_vfs); +#endif s = "CN23XX"; break; @@ -1889,7 +1631,7 @@ static inline int check_txq_state(struct lio *lio, struct sk_buff *skb) if (netif_is_multiqueue(lio->netdev)) { q = skb->queue_mapping; - iq = lio->linfo.txpciq[(q % (lio->linfo.num_txpciq))].s.q_no; + iq = lio->linfo.txpciq[(q % lio->oct_dev->num_iqs)].s.q_no; } else { iq = lio->txq; q = iq; @@ -2192,11 +1934,6 @@ static int load_firmware(struct octeon_device *oct) char fw_name[LIO_MAX_FW_FILENAME_LEN]; char *tmp_fw_type; - if (fw_type_is_none()) { - dev_info(&oct->pci_dev->dev, "Skipping firmware load\n"); - return ret; - } - if (fw_type[0] == '\0') tmp_fw_type = LIO_FW_NAME_TYPE_NIC; else @@ -2222,43 +1959,6 @@ static int load_firmware(struct octeon_device *oct) } /** - * \brief Setup output queue - * @param oct octeon device - * @param q_no which queue - * @param num_descs how many descriptors - * @param desc_size size of each descriptor - * @param app_ctx application context - */ -static int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, - int desc_size, void *app_ctx) -{ - int ret_val = 0; - - dev_dbg(&oct->pci_dev->dev, "Creating Droq: %d\n", q_no); - /* droq creation and local register settings. */ - ret_val = octeon_create_droq(oct, q_no, num_descs, desc_size, app_ctx); - if (ret_val < 0) - return ret_val; - - if (ret_val == 1) { - dev_dbg(&oct->pci_dev->dev, "Using default droq %d\n", q_no); - return 0; - } - /* tasklet creation for the droq */ - - /* Enable the droq queues */ - octeon_set_droq_pkt_op(oct, q_no, 1); - - /* Send Credit for Octeon Output queues. Credits are always - * sent after the output queue is enabled. - */ - writel(oct->droq[q_no]->max_count, - oct->droq[q_no]->pkts_credit_reg); - - return ret_val; -} - -/** * \brief Callback for getting interface configuration * @param status status of request * @param buf pointer to resp structure @@ -2291,352 +1991,6 @@ static void if_cfg_callback(struct octeon_device *oct, wake_up_interruptible(&ctx->wc); } -/** Routine to push packets arriving on Octeon interface upto network layer. - * @param oct_id - octeon device id. - * @param skbuff - skbuff struct to be passed to network layer. - * @param len - size of total data received. - * @param rh - Control header associated with the packet - * @param param - additional control data with the packet - * @param arg - farg registered in droq_ops - */ -static void -liquidio_push_packet(u32 octeon_id __attribute__((unused)), - void *skbuff, - u32 len, - union octeon_rh *rh, - void *param, - void *arg) -{ - struct napi_struct *napi = param; - struct sk_buff *skb = (struct sk_buff *)skbuff; - struct skb_shared_hwtstamps *shhwtstamps; - u64 ns; - u16 vtag = 0; - u32 r_dh_off; - struct net_device *netdev = (struct net_device *)arg; - struct octeon_droq *droq = container_of(param, struct octeon_droq, - napi); - if (netdev) { - int packet_was_received; - struct lio *lio = GET_LIO(netdev); - struct octeon_device *oct = lio->oct_dev; - - /* Do not proceed if the interface is not in RUNNING state. */ - if (!ifstate_check(lio, LIO_IFSTATE_RUNNING)) { - recv_buffer_free(skb); - droq->stats.rx_dropped++; - return; - } - - skb->dev = netdev; - - skb_record_rx_queue(skb, droq->q_no); - if (likely(len > MIN_SKB_SIZE)) { - struct octeon_skb_page_info *pg_info; - unsigned char *va; - - pg_info = ((struct octeon_skb_page_info *)(skb->cb)); - if (pg_info->page) { - /* For Paged allocation use the frags */ - va = page_address(pg_info->page) + - pg_info->page_offset; - memcpy(skb->data, va, MIN_SKB_SIZE); - skb_put(skb, MIN_SKB_SIZE); - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - pg_info->page, - pg_info->page_offset + - MIN_SKB_SIZE, - len - MIN_SKB_SIZE, - LIO_RXBUFFER_SZ); - } - } else { - struct octeon_skb_page_info *pg_info = - ((struct octeon_skb_page_info *)(skb->cb)); - skb_copy_to_linear_data(skb, page_address(pg_info->page) - + pg_info->page_offset, len); - skb_put(skb, len); - put_page(pg_info->page); - } - - r_dh_off = (rh->r_dh.len - 1) * BYTES_PER_DHLEN_UNIT; - - if (((oct->chip_id == OCTEON_CN66XX) || - (oct->chip_id == OCTEON_CN68XX)) && - ptp_enable) { - if (rh->r_dh.has_hwtstamp) { - /* timestamp is included from the hardware at - * the beginning of the packet. - */ - if (ifstate_check - (lio, LIO_IFSTATE_RX_TIMESTAMP_ENABLED)) { - /* Nanoseconds are in the first 64-bits - * of the packet. - */ - memcpy(&ns, (skb->data + r_dh_off), - sizeof(ns)); - r_dh_off -= BYTES_PER_DHLEN_UNIT; - shhwtstamps = skb_hwtstamps(skb); - shhwtstamps->hwtstamp = - ns_to_ktime(ns + - lio->ptp_adjust); - } - } - } - - if (rh->r_dh.has_hash) { - __be32 *hash_be = (__be32 *)(skb->data + r_dh_off); - u32 hash = be32_to_cpu(*hash_be); - - skb_set_hash(skb, hash, PKT_HASH_TYPE_L4); - r_dh_off -= BYTES_PER_DHLEN_UNIT; - } - - skb_pull(skb, rh->r_dh.len * BYTES_PER_DHLEN_UNIT); - - skb->protocol = eth_type_trans(skb, skb->dev); - if ((netdev->features & NETIF_F_RXCSUM) && - (((rh->r_dh.encap_on) && - (rh->r_dh.csum_verified & CNNIC_TUN_CSUM_VERIFIED)) || - (!(rh->r_dh.encap_on) && - (rh->r_dh.csum_verified & CNNIC_CSUM_VERIFIED)))) - /* checksum has already been verified */ - skb->ip_summed = CHECKSUM_UNNECESSARY; - else - skb->ip_summed = CHECKSUM_NONE; - - /* Setting Encapsulation field on basis of status received - * from the firmware - */ - if (rh->r_dh.encap_on) { - skb->encapsulation = 1; - skb->csum_level = 1; - droq->stats.rx_vxlan++; - } - - /* inbound VLAN tag */ - if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && - (rh->r_dh.vlan != 0)) { - u16 vid = rh->r_dh.vlan; - u16 priority = rh->r_dh.priority; - - vtag = priority << 13 | vid; - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vtag); - } - - packet_was_received = napi_gro_receive(napi, skb) != GRO_DROP; - - if (packet_was_received) { - droq->stats.rx_bytes_received += len; - droq->stats.rx_pkts_received++; - } else { - droq->stats.rx_dropped++; - netif_info(lio, rx_err, lio->netdev, - "droq:%d error rx_dropped:%llu\n", - droq->q_no, droq->stats.rx_dropped); - } - - } else { - recv_buffer_free(skb); - } -} - -/** - * \brief wrapper for calling napi_schedule - * @param param parameters to pass to napi_schedule - * - * Used when scheduling on different CPUs - */ -static void napi_schedule_wrapper(void *param) -{ - struct napi_struct *napi = param; - - napi_schedule(napi); -} - -/** - * \brief callback when receive interrupt occurs and we are in NAPI mode - * @param arg pointer to octeon output queue - */ -static void liquidio_napi_drv_callback(void *arg) -{ - struct octeon_device *oct; - struct octeon_droq *droq = arg; - int this_cpu = smp_processor_id(); - - oct = droq->oct_dev; - - if (OCTEON_CN23XX_PF(oct) || droq->cpu_id == this_cpu) { - napi_schedule_irqoff(&droq->napi); - } else { - call_single_data_t *csd = &droq->csd; - - csd->func = napi_schedule_wrapper; - csd->info = &droq->napi; - csd->flags = 0; - - smp_call_function_single_async(droq->cpu_id, csd); - } -} - -/** - * \brief Entry point for NAPI polling - * @param napi NAPI structure - * @param budget maximum number of items to process - */ -static int liquidio_napi_poll(struct napi_struct *napi, int budget) -{ - struct octeon_droq *droq; - int work_done; - int tx_done = 0, iq_no; - struct octeon_instr_queue *iq; - struct octeon_device *oct; - - droq = container_of(napi, struct octeon_droq, napi); - oct = droq->oct_dev; - iq_no = droq->q_no; - /* Handle Droq descriptors */ - work_done = octeon_process_droq_poll_cmd(oct, droq->q_no, - POLL_EVENT_PROCESS_PKTS, - budget); - - /* Flush the instruction queue */ - iq = oct->instr_queue[iq_no]; - if (iq) { - if (atomic_read(&iq->instr_pending)) - /* Process iq buffers with in the budget limits */ - tx_done = octeon_flush_iq(oct, iq, budget); - else - tx_done = 1; - /* Update iq read-index rather than waiting for next interrupt. - * Return back if tx_done is false. - */ - update_txq_status(oct, iq_no); - } else { - dev_err(&oct->pci_dev->dev, "%s: iq (%d) num invalid\n", - __func__, iq_no); - } - - /* force enable interrupt if reg cnts are high to avoid wraparound */ - if ((work_done < budget && tx_done) || - (iq && iq->pkt_in_done >= MAX_REG_CNT) || - (droq->pkt_count >= MAX_REG_CNT)) { - tx_done = 1; - napi_complete_done(napi, work_done); - octeon_process_droq_poll_cmd(droq->oct_dev, droq->q_no, - POLL_EVENT_ENABLE_INTR, 0); - return 0; - } - - return (!tx_done) ? (budget) : (work_done); -} - -/** - * \brief Setup input and output queues - * @param octeon_dev octeon device - * @param ifidx Interface Index - * - * Note: Queues are with respect to the octeon device. Thus - * an input queue is for egress packets, and output queues - * are for ingress packets. - */ -static inline int setup_io_queues(struct octeon_device *octeon_dev, - int ifidx) -{ - struct octeon_droq_ops droq_ops; - struct net_device *netdev; - static int cpu_id; - static int cpu_id_modulus; - struct octeon_droq *droq; - struct napi_struct *napi; - int q, q_no, retval = 0; - struct lio *lio; - int num_tx_descs; - - netdev = octeon_dev->props[ifidx].netdev; - - lio = GET_LIO(netdev); - - memset(&droq_ops, 0, sizeof(struct octeon_droq_ops)); - - droq_ops.fptr = liquidio_push_packet; - droq_ops.farg = (void *)netdev; - - droq_ops.poll_mode = 1; - droq_ops.napi_fn = liquidio_napi_drv_callback; - cpu_id = 0; - cpu_id_modulus = num_present_cpus(); - - /* set up DROQs. */ - for (q = 0; q < lio->linfo.num_rxpciq; q++) { - q_no = lio->linfo.rxpciq[q].s.q_no; - dev_dbg(&octeon_dev->pci_dev->dev, - "setup_io_queues index:%d linfo.rxpciq.s.q_no:%d\n", - q, q_no); - retval = octeon_setup_droq(octeon_dev, q_no, - CFG_GET_NUM_RX_DESCS_NIC_IF - (octeon_get_conf(octeon_dev), - lio->ifidx), - CFG_GET_NUM_RX_BUF_SIZE_NIC_IF - (octeon_get_conf(octeon_dev), - lio->ifidx), NULL); - if (retval) { - dev_err(&octeon_dev->pci_dev->dev, - "%s : Runtime DROQ(RxQ) creation failed.\n", - __func__); - return 1; - } - - droq = octeon_dev->droq[q_no]; - napi = &droq->napi; - dev_dbg(&octeon_dev->pci_dev->dev, "netif_napi_add netdev:%llx oct:%llx pf_num:%d\n", - (u64)netdev, (u64)octeon_dev, octeon_dev->pf_num); - netif_napi_add(netdev, napi, liquidio_napi_poll, 64); - - /* designate a CPU for this droq */ - droq->cpu_id = cpu_id; - cpu_id++; - if (cpu_id >= cpu_id_modulus) - cpu_id = 0; - - octeon_register_droq_ops(octeon_dev, q_no, &droq_ops); - } - - if (OCTEON_CN23XX_PF(octeon_dev)) { - /* 23XX PF can receive control messages (via the first PF-owned - * droq) from the firmware even if the ethX interface is down, - * so that's why poll_mode must be off for the first droq. - */ - octeon_dev->droq[0]->ops.poll_mode = 0; - } - - /* set up IQs. */ - for (q = 0; q < lio->linfo.num_txpciq; q++) { - num_tx_descs = CFG_GET_NUM_TX_DESCS_NIC_IF(octeon_get_conf - (octeon_dev), - lio->ifidx); - retval = octeon_setup_iq(octeon_dev, ifidx, q, - lio->linfo.txpciq[q], num_tx_descs, - netdev_get_tx_queue(netdev, q)); - if (retval) { - dev_err(&octeon_dev->pci_dev->dev, - " %s : Runtime IQ(TxQ) creation failed.\n", - __func__); - return 1; - } - - if (octeon_dev->ioq_vector) { - struct octeon_ioq_vector *ioq_vector; - - ioq_vector = &octeon_dev->ioq_vector[q]; - netif_set_xps_queue(netdev, - &ioq_vector->affinity_mask, - ioq_vector->iq_index); - } - } - - return 0; -} - /** * \brief Poll routine for checking transmit queue status * @param work work_struct data structure @@ -2707,8 +2061,7 @@ static int liquidio_open(struct net_device *netdev) oct->droq[0]->ops.poll_mode = 1; } - if ((oct->chip_id == OCTEON_CN66XX || oct->chip_id == OCTEON_CN68XX) && - ptp_enable) + if (oct->ptp_enable) oct_ptp_open(netdev); ifstate_set(lio, LIO_IFSTATE_RUNNING); @@ -2746,6 +2099,17 @@ static int liquidio_stop(struct net_device *netdev) { struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; + struct napi_struct *napi, *n; + + if (oct->props[lio->ifidx].napi_enabled) { + list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) + napi_disable(napi); + + oct->props[lio->ifidx].napi_enabled = 0; + + if (OCTEON_CN23XX_PF(oct)) + oct->droq[0]->ops.poll_mode = 0; + } ifstate_reset(lio, LIO_IFSTATE_RUNNING); @@ -2916,7 +2280,10 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev) oct = lio->oct_dev; - for (i = 0; i < lio->linfo.num_txpciq; i++) { + if (ifstate_check(lio, LIO_IFSTATE_RESETTING)) + return stats; + + for (i = 0; i < oct->num_iqs; i++) { iq_no = lio->linfo.txpciq[i].s.q_no; iq_stats = &oct->instr_queue[iq_no]->stats; pkts += iq_stats->tx_done; @@ -2932,7 +2299,7 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev) drop = 0; bytes = 0; - for (i = 0; i < lio->linfo.num_rxpciq; i++) { + for (i = 0; i < oct->num_oqs; i++) { oq_no = lio->linfo.rxpciq[i].s.q_no; oq_stats = &oct->droq[oq_no]->stats; pkts += oq_stats->rx_pkts_received; @@ -3052,8 +2419,7 @@ static int liquidio_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) switch (cmd) { case SIOCSHWTSTAMP: - if ((lio->oct_dev->chip_id == OCTEON_CN66XX || - lio->oct_dev->chip_id == OCTEON_CN68XX) && ptp_enable) + if (lio->oct_dev->ptp_enable) return hwtstamp_ioctl(netdev, ifr); default: return -EOPNOTSUPP; @@ -4188,7 +3554,9 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) */ lio->txq = lio->linfo.txpciq[0].s.q_no; lio->rxq = lio->linfo.rxpciq[0].s.q_no; - if (setup_io_queues(octeon_dev, i)) { + if (liquidio_setup_io_queues(octeon_dev, i, + lio->linfo.num_txpciq, + lio->linfo.num_rxpciq)) { dev_err(&octeon_dev->pci_dev->dev, "I/O queues creation failed\n"); goto setup_nic_dev_fail; } @@ -4516,6 +3884,7 @@ static int octeon_device_init(struct octeon_device *octeon_dev) int j, ret; int fw_loaded = 0; char bootcmd[] = "\n"; + char *dbg_enb = NULL; struct octeon_device_priv *oct_priv = (struct octeon_device_priv *)octeon_dev->priv; atomic_set(&octeon_dev->status, OCT_DEV_BEGIN_STATE); @@ -4548,18 +3917,16 @@ static int octeon_device_init(struct octeon_device *octeon_dev) octeon_dev->app_mode = CVM_DRV_INVALID_APP; if (OCTEON_CN23XX_PF(octeon_dev)) { - if (!cn23xx_fw_loaded(octeon_dev)) { + if (!cn23xx_fw_loaded(octeon_dev) && !fw_type_is_none()) { fw_loaded = 0; - if (!fw_type_is_none()) { - /* Do a soft reset of the Octeon device. */ - if (octeon_dev->fn_list.soft_reset(octeon_dev)) - return 1; - /* things might have changed */ - if (!cn23xx_fw_loaded(octeon_dev)) - fw_loaded = 0; - else - fw_loaded = 1; - } + /* Do a soft reset of the Octeon device. */ + if (octeon_dev->fn_list.soft_reset(octeon_dev)) + return 1; + /* things might have changed */ + if (!cn23xx_fw_loaded(octeon_dev)) + fw_loaded = 0; + else + fw_loaded = 1; } else { fw_loaded = 1; } @@ -4666,7 +4033,8 @@ static int octeon_device_init(struct octeon_device *octeon_dev) /* Setup the interrupt handler and record the INT SUM register address */ - if (octeon_setup_interrupt(octeon_dev)) + if (octeon_setup_interrupt(octeon_dev, + octeon_dev->sriov_info.num_pf_rings)) return 1; /* Enable Octeon device interrupts */ @@ -4674,6 +4042,18 @@ static int octeon_device_init(struct octeon_device *octeon_dev) atomic_set(&octeon_dev->status, OCT_DEV_INTR_SET_DONE); + /* Send Credit for Octeon Output queues. Credits are always sent BEFORE + * the output queue is enabled. + * This ensures that we'll receive the f/w CORE DRV_ACTIVE message in + * case we've configured CN23XX_SLI_GBL_CONTROL[NOPTR_D] = 0. + * Otherwise, it is possible that the DRV_ACTIVE message will be sent + * before any credits have been issued, causing the ring to be reset + * (and the f/w appear to never have started). + */ + for (j = 0; j < octeon_dev->num_oqs; j++) + writel(octeon_dev->droq[j]->max_count, + octeon_dev->droq[j]->pkts_credit_reg); + /* Enable the input and output queues for this Octeon device */ ret = octeon_dev->fn_list.enable_io_queues(octeon_dev); if (ret) { @@ -4722,10 +4102,19 @@ static int octeon_device_init(struct octeon_device *octeon_dev) dev_err(&octeon_dev->pci_dev->dev, "Could not access board consoles\n"); return 1; } - ret = octeon_add_console(octeon_dev, 0); + /* If console debug enabled, specify empty string to use default + * enablement ELSE specify NULL string for 'disabled'. + */ + dbg_enb = octeon_console_debug_enabled(0) ? "" : NULL; + ret = octeon_add_console(octeon_dev, 0, dbg_enb); if (ret) { dev_err(&octeon_dev->pci_dev->dev, "Could not access board console\n"); return 1; + } else if (octeon_console_debug_enabled(0)) { + /* If console was added AND we're logging console output + * then set our console print function. + */ + octeon_dev->console[0].print = octeon_dbg_console_print; } atomic_set(&octeon_dev->status, OCT_DEV_CONSOLE_INIT_DONE); @@ -4736,12 +4125,6 @@ static int octeon_device_init(struct octeon_device *octeon_dev) dev_err(&octeon_dev->pci_dev->dev, "Could not load firmware to board\n"); return 1; } - /* set bit 1 of SLI_SCRATCH_1 to indicate that firmware is - * loaded - */ - if (OCTEON_CN23XX_PF(octeon_dev)) - octeon_write_csr64(octeon_dev, CN23XX_SLI_SCRATCH1, - 2ULL); } handshake[octeon_dev->octeon_id].init_ok = 1; @@ -4749,14 +4132,33 @@ static int octeon_device_init(struct octeon_device *octeon_dev) atomic_set(&octeon_dev->status, OCT_DEV_HOST_OK); - /* Send Credit for Octeon Output queues. Credits are always sent after - * the output queue is enabled. - */ - for (j = 0; j < octeon_dev->num_oqs; j++) - writel(octeon_dev->droq[j]->max_count, - octeon_dev->droq[j]->pkts_credit_reg); + return 0; +} + +/** + * \brief Debug console print function + * @param octeon_dev octeon device + * @param console_num console number + * @param prefix first portion of line to display + * @param suffix second portion of line to display + * + * The OCTEON debug console outputs entire lines (excluding '\n'). + * Normally, the line will be passed in the 'prefix' parameter. + * However, due to buffering, it is possible for a line to be split into two + * parts, in which case they will be passed as the 'prefix' parameter and + * 'suffix' parameter. + */ +static int octeon_dbg_console_print(struct octeon_device *oct, u32 console_num, + char *prefix, char *suffix) +{ + if (prefix && suffix) + dev_info(&oct->pci_dev->dev, "%u: %s%s\n", console_num, prefix, + suffix); + else if (prefix) + dev_info(&oct->pci_dev->dev, "%u: %s\n", console_num, prefix); + else if (suffix) + dev_info(&oct->pci_dev->dev, "%u: %s\n", console_num, suffix); - /* Packets can start arriving on the output queues from this point. */ return 0; } diff --git a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c index 9b247102eb92..2e993ce43b66 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_vf_main.c @@ -107,12 +107,6 @@ struct octnic_gather { dma_addr_t sg_dma_ptr; }; -struct octeon_device_priv { - /* Tasklet structures for this device. */ - struct tasklet_struct droq_tasklet; - unsigned long napi_mask; -}; - static int liquidio_vf_probe(struct pci_dev *pdev, const struct pci_device_id *ent); static void liquidio_vf_remove(struct pci_dev *pdev); @@ -123,7 +117,7 @@ static int lio_wait_for_oq_pkts(struct octeon_device *oct) { struct octeon_device_priv *oct_priv = (struct octeon_device_priv *)oct->priv; - int retry = MAX_VF_IP_OP_PENDING_PKT_COUNT; + int retry = MAX_IO_PENDING_PKT_COUNT; int pkt_cnt = 0, pending_pkts; int i; @@ -148,32 +142,6 @@ static int lio_wait_for_oq_pkts(struct octeon_device *oct) } /** - * \brief wait for all pending requests to complete - * @param oct Pointer to Octeon device - * - * Called during shutdown sequence - */ -static int wait_for_pending_requests(struct octeon_device *oct) -{ - int i, pcount = 0; - - for (i = 0; i < MAX_VF_IP_OP_PENDING_PKT_COUNT; i++) { - pcount = atomic_read( - &oct->response_list[OCTEON_ORDERED_SC_LIST] - .pending_req_count); - if (pcount) - schedule_timeout_uninterruptible(HZ / 10); - else - break; - } - - if (pcount) - return 1; - - return 0; -} - -/** * \brief Cause device to go quiet so it can be safely removed/reset/etc * @param oct Pointer to Octeon device */ @@ -374,7 +342,7 @@ static void txqs_wake(struct net_device *netdev) int i; for (i = 0; i < netdev->num_tx_queues; i++) { - int qno = lio->linfo.txpciq[i % (lio->linfo.num_txpciq)] + int qno = lio->linfo.txpciq[i % lio->oct_dev->num_iqs] .s.q_no; if (__netif_subqueue_stopped(netdev, i)) { INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, qno, @@ -574,7 +542,8 @@ static void print_link_info(struct net_device *netdev) { struct lio *lio = GET_LIO(netdev); - if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED) { + if (!ifstate_check(lio, LIO_IFSTATE_RESETTING) && + ifstate_check(lio, LIO_IFSTATE_REGISTERED)) { struct oct_link_info *linfo = &lio->linfo; if (linfo->link.s.link_up) { @@ -661,6 +630,12 @@ static void update_link_status(struct net_device *netdev, txqs_stop(netdev); } + if (lio->linfo.link.s.mtu != netdev->max_mtu) { + dev_info(&oct->pci_dev->dev, "Max MTU Changed from %d to %d\n", + netdev->max_mtu, lio->linfo.link.s.mtu); + netdev->max_mtu = lio->linfo.link.s.mtu; + } + if (lio->linfo.link.s.mtu < netdev->mtu) { dev_warn(&oct->pci_dev->dev, "PF has changed the MTU for gmx port. Reducing the mtu from %d to %d\n", @@ -673,167 +648,6 @@ static void update_link_status(struct net_device *netdev, } } -static void update_txq_status(struct octeon_device *oct, int iq_num) -{ - struct octeon_instr_queue *iq = oct->instr_queue[iq_num]; - struct net_device *netdev; - struct lio *lio; - - netdev = oct->props[iq->ifidx].netdev; - lio = GET_LIO(netdev); - if (netif_is_multiqueue(netdev)) { - if (__netif_subqueue_stopped(netdev, iq->q_index) && - lio->linfo.link.s.link_up && - (!octnet_iq_is_full(oct, iq_num))) { - netif_wake_subqueue(netdev, iq->q_index); - INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, iq_num, - tx_restart, 1); - } - } else if (netif_queue_stopped(netdev) && lio->linfo.link.s.link_up && - (!octnet_iq_is_full(oct, lio->txq))) { - INCR_INSTRQUEUE_PKT_COUNT(lio->oct_dev, - lio->txq, tx_restart, 1); - netif_wake_queue(netdev); - } -} - -static -int liquidio_schedule_msix_droq_pkt_handler(struct octeon_droq *droq, u64 ret) -{ - struct octeon_device *oct = droq->oct_dev; - struct octeon_device_priv *oct_priv = - (struct octeon_device_priv *)oct->priv; - - if (droq->ops.poll_mode) { - droq->ops.napi_fn(droq); - } else { - if (ret & MSIX_PO_INT) { - dev_err(&oct->pci_dev->dev, - "should not come here should not get rx when poll mode = 0 for vf\n"); - tasklet_schedule(&oct_priv->droq_tasklet); - return 1; - } - /* this will be flushed periodically by check iq db */ - if (ret & MSIX_PI_INT) - return 0; - } - return 0; -} - -static irqreturn_t -liquidio_msix_intr_handler(int irq __attribute__((unused)), void *dev) -{ - struct octeon_ioq_vector *ioq_vector = (struct octeon_ioq_vector *)dev; - struct octeon_device *oct = ioq_vector->oct_dev; - struct octeon_droq *droq = oct->droq[ioq_vector->droq_index]; - u64 ret; - - ret = oct->fn_list.msix_interrupt_handler(ioq_vector); - - if ((ret & MSIX_PO_INT) || (ret & MSIX_PI_INT)) - liquidio_schedule_msix_droq_pkt_handler(droq, ret); - - return IRQ_HANDLED; -} - -/** - * \brief Setup interrupt for octeon device - * @param oct octeon device - * - * Enable interrupt in Octeon device as given in the PCI interrupt mask. - */ -static int octeon_setup_interrupt(struct octeon_device *oct) -{ - struct msix_entry *msix_entries; - char *queue_irq_names = NULL; - int num_alloc_ioq_vectors; - int num_ioq_vectors; - int irqret; - int i; - - if (oct->msix_on) { - oct->num_msix_irqs = oct->sriov_info.rings_per_vf; - - /* allocate storage for the names assigned to each irq */ - oct->irq_name_storage = - kcalloc(MAX_IOQ_INTERRUPTS_PER_VF, INTRNAMSIZ, - GFP_KERNEL); - if (!oct->irq_name_storage) { - dev_err(&oct->pci_dev->dev, "Irq name storage alloc failed...\n"); - return -ENOMEM; - } - - queue_irq_names = oct->irq_name_storage; - - oct->msix_entries = kcalloc( - oct->num_msix_irqs, sizeof(struct msix_entry), GFP_KERNEL); - if (!oct->msix_entries) { - dev_err(&oct->pci_dev->dev, "Memory Alloc failed...\n"); - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return -ENOMEM; - } - - msix_entries = (struct msix_entry *)oct->msix_entries; - - for (i = 0; i < oct->num_msix_irqs; i++) - msix_entries[i].entry = i; - num_alloc_ioq_vectors = pci_enable_msix_range( - oct->pci_dev, msix_entries, - oct->num_msix_irqs, - oct->num_msix_irqs); - if (num_alloc_ioq_vectors < 0) { - dev_err(&oct->pci_dev->dev, "unable to Allocate MSI-X interrupts\n"); - kfree(oct->msix_entries); - oct->msix_entries = NULL; - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return num_alloc_ioq_vectors; - } - dev_dbg(&oct->pci_dev->dev, "OCTEON: Enough MSI-X interrupts are allocated...\n"); - - num_ioq_vectors = oct->num_msix_irqs; - - for (i = 0; i < num_ioq_vectors; i++) { - snprintf(&queue_irq_names[IRQ_NAME_OFF(i)], INTRNAMSIZ, - "LiquidIO%u-vf%u-rxtx-%u", - oct->octeon_id, oct->vf_num, i); - - irqret = request_irq(msix_entries[i].vector, - liquidio_msix_intr_handler, 0, - &queue_irq_names[IRQ_NAME_OFF(i)], - &oct->ioq_vector[i]); - if (irqret) { - dev_err(&oct->pci_dev->dev, - "OCTEON: Request_irq failed for MSIX interrupt Error: %d\n", - irqret); - - while (i) { - i--; - irq_set_affinity_hint( - msix_entries[i].vector, NULL); - free_irq(msix_entries[i].vector, - &oct->ioq_vector[i]); - } - pci_disable_msix(oct->pci_dev); - kfree(oct->msix_entries); - oct->msix_entries = NULL; - kfree(oct->irq_name_storage); - oct->irq_name_storage = NULL; - return irqret; - } - oct->ioq_vector[i].vector = msix_entries[i].vector; - /* assign the cpu mask for this msix interrupt vector */ - irq_set_affinity_hint( - msix_entries[i].vector, - (&oct->ioq_vector[i].affinity_mask)); - } - dev_dbg(&oct->pci_dev->dev, - "OCTEON[%d]: MSI-X enabled\n", oct->octeon_id); - } - return 0; -} - /** * \brief PCI probe handler * @param pdev PCI device structure @@ -942,10 +756,14 @@ static void octeon_destroy_resources(struct octeon_device *oct) if (oct->msix_on) { msix_entries = (struct msix_entry *)oct->msix_entries; for (i = 0; i < oct->num_msix_irqs; i++) { - irq_set_affinity_hint(msix_entries[i].vector, - NULL); - free_irq(msix_entries[i].vector, - &oct->ioq_vector[i]); + if (oct->ioq_vector[i].vector) { + irq_set_affinity_hint( + msix_entries[i].vector, + NULL); + free_irq(msix_entries[i].vector, + &oct->ioq_vector[i]); + oct->ioq_vector[i].vector = 0; + } } pci_disable_msix(oct->pci_dev); kfree(oct->msix_entries); @@ -1137,6 +955,10 @@ static void liquidio_destroy_nic_device(struct octeon_device *oct, int ifidx) oct->droq[0]->ops.poll_mode = 0; } + /* Delete NAPI */ + list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) + netif_napi_del(napi); + if (atomic_read(&lio->ifstate) & LIO_IFSTATE_REGISTERED) unregister_netdev(netdev); @@ -1174,7 +996,7 @@ static int liquidio_stop_nic_module(struct octeon_device *oct) for (i = 0; i < oct->ifcount; i++) { lio = GET_LIO(oct->props[i].netdev); - for (j = 0; j < lio->linfo.num_rxpciq; j++) + for (j = 0; j < oct->num_oqs; j++) octeon_unregister_droq_ops(oct, lio->linfo.rxpciq[j].s.q_no); } @@ -1262,7 +1084,7 @@ static int check_txq_state(struct lio *lio, struct sk_buff *skb) if (netif_is_multiqueue(lio->netdev)) { q = skb->queue_mapping; - iq = lio->linfo.txpciq[(q % (lio->linfo.num_txpciq))].s.q_no; + iq = lio->linfo.txpciq[q % lio->oct_dev->num_iqs].s.q_no; } else { iq = lio->txq; q = iq; @@ -1391,41 +1213,6 @@ static void free_netsgbuf_with_resp(void *buf) } /** - * \brief Setup output queue - * @param oct octeon device - * @param q_no which queue - * @param num_descs how many descriptors - * @param desc_size size of each descriptor - * @param app_ctx application context - */ -static int octeon_setup_droq(struct octeon_device *oct, int q_no, int num_descs, - int desc_size, void *app_ctx) -{ - int ret_val; - - dev_dbg(&oct->pci_dev->dev, "Creating Droq: %d\n", q_no); - /* droq creation and local register settings. */ - ret_val = octeon_create_droq(oct, q_no, num_descs, desc_size, app_ctx); - if (ret_val < 0) - return ret_val; - - if (ret_val == 1) { - dev_dbg(&oct->pci_dev->dev, "Using default droq %d\n", q_no); - return 0; - } - - /* Enable the droq queues */ - octeon_set_droq_pkt_op(oct, q_no, 1); - - /* Send Credit for Octeon Output queues. Credits are always - * sent after the output queue is enabled. - */ - writel(oct->droq[q_no]->max_count, oct->droq[q_no]->pkts_credit_reg); - - return ret_val; -} - -/** * \brief Callback for getting interface configuration * @param status status of request * @param buf pointer to resp structure @@ -1457,290 +1244,6 @@ static void if_cfg_callback(struct octeon_device *oct, wake_up_interruptible(&ctx->wc); } -/** Routine to push packets arriving on Octeon interface upto network layer. - * @param oct_id - octeon device id. - * @param skbuff - skbuff struct to be passed to network layer. - * @param len - size of total data received. - * @param rh - Control header associated with the packet - * @param param - additional control data with the packet - * @param arg - farg registered in droq_ops - */ -static void -liquidio_push_packet(u32 octeon_id __attribute__((unused)), - void *skbuff, - u32 len, - union octeon_rh *rh, - void *param, - void *arg) -{ - struct napi_struct *napi = param; - struct octeon_droq *droq = - container_of(param, struct octeon_droq, napi); - struct net_device *netdev = (struct net_device *)arg; - struct sk_buff *skb = (struct sk_buff *)skbuff; - u16 vtag = 0; - u32 r_dh_off; - - if (netdev) { - struct lio *lio = GET_LIO(netdev); - int packet_was_received; - - /* Do not proceed if the interface is not in RUNNING state. */ - if (!ifstate_check(lio, LIO_IFSTATE_RUNNING)) { - recv_buffer_free(skb); - droq->stats.rx_dropped++; - return; - } - - skb->dev = netdev; - - skb_record_rx_queue(skb, droq->q_no); - if (likely(len > MIN_SKB_SIZE)) { - struct octeon_skb_page_info *pg_info; - unsigned char *va; - - pg_info = ((struct octeon_skb_page_info *)(skb->cb)); - if (pg_info->page) { - /* For Paged allocation use the frags */ - va = page_address(pg_info->page) + - pg_info->page_offset; - memcpy(skb->data, va, MIN_SKB_SIZE); - skb_put(skb, MIN_SKB_SIZE); - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, - pg_info->page, - pg_info->page_offset + - MIN_SKB_SIZE, - len - MIN_SKB_SIZE, - LIO_RXBUFFER_SZ); - } - } else { - struct octeon_skb_page_info *pg_info = - ((struct octeon_skb_page_info *)(skb->cb)); - skb_copy_to_linear_data(skb, - page_address(pg_info->page) + - pg_info->page_offset, len); - skb_put(skb, len); - put_page(pg_info->page); - } - - r_dh_off = (rh->r_dh.len - 1) * BYTES_PER_DHLEN_UNIT; - - if (rh->r_dh.has_hwtstamp) - r_dh_off -= BYTES_PER_DHLEN_UNIT; - - if (rh->r_dh.has_hash) { - __be32 *hash_be = (__be32 *)(skb->data + r_dh_off); - u32 hash = be32_to_cpu(*hash_be); - - skb_set_hash(skb, hash, PKT_HASH_TYPE_L4); - r_dh_off -= BYTES_PER_DHLEN_UNIT; - } - - skb_pull(skb, rh->r_dh.len * BYTES_PER_DHLEN_UNIT); - skb->protocol = eth_type_trans(skb, skb->dev); - - if ((netdev->features & NETIF_F_RXCSUM) && - (((rh->r_dh.encap_on) && - (rh->r_dh.csum_verified & CNNIC_TUN_CSUM_VERIFIED)) || - (!(rh->r_dh.encap_on) && - (rh->r_dh.csum_verified & CNNIC_CSUM_VERIFIED)))) - /* checksum has already been verified */ - skb->ip_summed = CHECKSUM_UNNECESSARY; - else - skb->ip_summed = CHECKSUM_NONE; - - /* Setting Encapsulation field on basis of status received - * from the firmware - */ - if (rh->r_dh.encap_on) { - skb->encapsulation = 1; - skb->csum_level = 1; - droq->stats.rx_vxlan++; - } - - /* inbound VLAN tag */ - if ((netdev->features & NETIF_F_HW_VLAN_CTAG_RX) && - rh->r_dh.vlan) { - u16 priority = rh->r_dh.priority; - u16 vid = rh->r_dh.vlan; - - vtag = (priority << VLAN_PRIO_SHIFT) | vid; - __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vtag); - } - - packet_was_received = (napi_gro_receive(napi, skb) != GRO_DROP); - - if (packet_was_received) { - droq->stats.rx_bytes_received += len; - droq->stats.rx_pkts_received++; - } else { - droq->stats.rx_dropped++; - netif_info(lio, rx_err, lio->netdev, - "droq:%d error rx_dropped:%llu\n", - droq->q_no, droq->stats.rx_dropped); - } - - } else { - recv_buffer_free(skb); - } -} - -/** - * \brief callback when receive interrupt occurs and we are in NAPI mode - * @param arg pointer to octeon output queue - */ -static void liquidio_vf_napi_drv_callback(void *arg) -{ - struct octeon_droq *droq = arg; - - napi_schedule_irqoff(&droq->napi); -} - -/** - * \brief Entry point for NAPI polling - * @param napi NAPI structure - * @param budget maximum number of items to process - */ -static int liquidio_napi_poll(struct napi_struct *napi, int budget) -{ - struct octeon_instr_queue *iq; - struct octeon_device *oct; - struct octeon_droq *droq; - int tx_done = 0, iq_no; - int work_done; - - droq = container_of(napi, struct octeon_droq, napi); - oct = droq->oct_dev; - iq_no = droq->q_no; - - /* Handle Droq descriptors */ - work_done = octeon_process_droq_poll_cmd(oct, droq->q_no, - POLL_EVENT_PROCESS_PKTS, - budget); - - /* Flush the instruction queue */ - iq = oct->instr_queue[iq_no]; - if (iq) { - if (atomic_read(&iq->instr_pending)) - /* Process iq buffers with in the budget limits */ - tx_done = octeon_flush_iq(oct, iq, budget); - else - tx_done = 1; - - /* Update iq read-index rather than waiting for next interrupt. - * Return back if tx_done is false. - */ - update_txq_status(oct, iq_no); - } else { - dev_err(&oct->pci_dev->dev, "%s: iq (%d) num invalid\n", - __func__, iq_no); - } - - /* force enable interrupt if reg cnts are high to avoid wraparound */ - if ((work_done < budget && tx_done) || - (iq && iq->pkt_in_done >= MAX_REG_CNT) || - (droq->pkt_count >= MAX_REG_CNT)) { - tx_done = 1; - napi_complete_done(napi, work_done); - octeon_process_droq_poll_cmd(droq->oct_dev, droq->q_no, - POLL_EVENT_ENABLE_INTR, 0); - return 0; - } - - return (!tx_done) ? (budget) : (work_done); -} - -/** - * \brief Setup input and output queues - * @param octeon_dev octeon device - * @param ifidx Interface index - * - * Note: Queues are with respect to the octeon device. Thus - * an input queue is for egress packets, and output queues - * are for ingress packets. - */ -static int setup_io_queues(struct octeon_device *octeon_dev, int ifidx) -{ - struct octeon_droq_ops droq_ops; - struct net_device *netdev; - static int cpu_id_modulus; - struct octeon_droq *droq; - struct napi_struct *napi; - static int cpu_id; - int num_tx_descs; - struct lio *lio; - int retval = 0; - int q, q_no; - - netdev = octeon_dev->props[ifidx].netdev; - - lio = GET_LIO(netdev); - - memset(&droq_ops, 0, sizeof(struct octeon_droq_ops)); - - droq_ops.fptr = liquidio_push_packet; - droq_ops.farg = netdev; - - droq_ops.poll_mode = 1; - droq_ops.napi_fn = liquidio_vf_napi_drv_callback; - cpu_id = 0; - cpu_id_modulus = num_present_cpus(); - - /* set up DROQs. */ - for (q = 0; q < lio->linfo.num_rxpciq; q++) { - q_no = lio->linfo.rxpciq[q].s.q_no; - - retval = octeon_setup_droq( - octeon_dev, q_no, - CFG_GET_NUM_RX_DESCS_NIC_IF(octeon_get_conf(octeon_dev), - lio->ifidx), - CFG_GET_NUM_RX_BUF_SIZE_NIC_IF(octeon_get_conf(octeon_dev), - lio->ifidx), - NULL); - if (retval) { - dev_err(&octeon_dev->pci_dev->dev, - "%s : Runtime DROQ(RxQ) creation failed.\n", - __func__); - return 1; - } - - droq = octeon_dev->droq[q_no]; - napi = &droq->napi; - netif_napi_add(netdev, napi, liquidio_napi_poll, 64); - - /* designate a CPU for this droq */ - droq->cpu_id = cpu_id; - cpu_id++; - if (cpu_id >= cpu_id_modulus) - cpu_id = 0; - - octeon_register_droq_ops(octeon_dev, q_no, &droq_ops); - } - - /* 23XX VF can send/recv control messages (via the first VF-owned - * droq) from the firmware even if the ethX interface is down, - * so that's why poll_mode must be off for the first droq. - */ - octeon_dev->droq[0]->ops.poll_mode = 0; - - /* set up IQs. */ - for (q = 0; q < lio->linfo.num_txpciq; q++) { - num_tx_descs = CFG_GET_NUM_TX_DESCS_NIC_IF( - octeon_get_conf(octeon_dev), lio->ifidx); - retval = octeon_setup_iq(octeon_dev, ifidx, q, - lio->linfo.txpciq[q], num_tx_descs, - netdev_get_tx_queue(netdev, q)); - if (retval) { - dev_err(&octeon_dev->pci_dev->dev, - " %s : Runtime IQ(TxQ) creation failed.\n", - __func__); - return 1; - } - } - - return 0; -} - /** * \brief Net device open for LiquidIO * @param netdev network device @@ -1784,6 +1287,16 @@ static int liquidio_stop(struct net_device *netdev) { struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; + struct napi_struct *napi, *n; + + if (oct->props[lio->ifidx].napi_enabled) { + list_for_each_entry_safe(napi, n, &netdev->napi_list, dev_list) + napi_disable(napi); + + oct->props[lio->ifidx].napi_enabled = 0; + + oct->droq[0]->ops.poll_mode = 0; + } netif_info(lio, ifdown, lio->netdev, "Stopping interface!\n"); /* Inform that netif carrier is down */ @@ -1988,7 +1501,10 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev) oct = lio->oct_dev; - for (i = 0; i < lio->linfo.num_txpciq; i++) { + if (ifstate_check(lio, LIO_IFSTATE_RESETTING)) + return stats; + + for (i = 0; i < oct->num_iqs; i++) { iq_no = lio->linfo.txpciq[i].s.q_no; iq_stats = &oct->instr_queue[iq_no]->stats; pkts += iq_stats->tx_done; @@ -2004,7 +1520,7 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev) drop = 0; bytes = 0; - for (i = 0; i < lio->linfo.num_rxpciq; i++) { + for (i = 0; i < oct->num_oqs; i++) { oq_no = lio->linfo.rxpciq[i].s.q_no; oq_stats = &oct->droq[oq_no]->stats; pkts += oq_stats->rx_pkts_received; @@ -2028,17 +1544,31 @@ static struct net_device_stats *liquidio_get_stats(struct net_device *netdev) */ static int liquidio_change_mtu(struct net_device *netdev, int new_mtu) { - struct lio *lio = GET_LIO(netdev); - struct octeon_device *oct = lio->oct_dev; + struct octnic_ctrl_pkt nctrl; + struct octeon_device *oct; + struct lio *lio; + int ret = 0; - lio->mtu = new_mtu; + lio = GET_LIO(netdev); + oct = lio->oct_dev; + + memset(&nctrl, 0, sizeof(struct octnic_ctrl_pkt)); - netif_info(lio, probe, lio->netdev, "MTU Changed from %d to %d\n", - netdev->mtu, new_mtu); - dev_info(&oct->pci_dev->dev, "%s MTU Changed from %d to %d\n", - netdev->name, netdev->mtu, new_mtu); + nctrl.ncmd.u64 = 0; + nctrl.ncmd.s.cmd = OCTNET_CMD_CHANGE_MTU; + nctrl.ncmd.s.param1 = new_mtu; + nctrl.iq_no = lio->linfo.txpciq[0].s.q_no; + nctrl.wait_time = LIO_CMD_WAIT_TM; + nctrl.netpndev = (u64)netdev; + nctrl.cb_fn = liquidio_link_ctrl_cmd_completion; - netdev->mtu = new_mtu; + ret = octnet_send_nic_ctrl_pkt(lio->oct_dev, &nctrl); + if (ret < 0) { + dev_err(&oct->pci_dev->dev, "Failed to set MTU\n"); + return -EIO; + } + + lio->mtu = new_mtu; return 0; } @@ -2959,7 +2489,9 @@ static int setup_nic_devices(struct octeon_device *octeon_dev) /* Copy MAC Address to OS network device structure */ ether_addr_copy(netdev->dev_addr, mac); - if (setup_io_queues(octeon_dev, i)) { + if (liquidio_setup_io_queues(octeon_dev, i, + lio->linfo.num_txpciq, + lio->linfo.num_rxpciq)) { dev_err(&octeon_dev->pci_dev->dev, "I/O queues creation failed\n"); goto setup_nic_dev_fail; } @@ -3182,7 +2714,7 @@ static int octeon_device_init(struct octeon_device *oct) LIQUIDIO_VERSION, oct->sriov_info.rings_per_vf); /* Setup the interrupt handler and record the INT SUM register address*/ - if (octeon_setup_interrupt(oct)) + if (octeon_setup_interrupt(oct, oct->sriov_info.rings_per_vf)) return 1; atomic_set(&oct->status, OCT_DEV_INTR_SET_DONE); diff --git a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h index 231dd7fbfb80..3788c8cd082a 100644 --- a/drivers/net/ethernet/cavium/liquidio/liquidio_common.h +++ b/drivers/net/ethernet/cavium/liquidio/liquidio_common.h @@ -27,7 +27,7 @@ #define LIQUIDIO_PACKAGE "" #define LIQUIDIO_BASE_MAJOR_VERSION 1 -#define LIQUIDIO_BASE_MINOR_VERSION 5 +#define LIQUIDIO_BASE_MINOR_VERSION 6 #define LIQUIDIO_BASE_MICRO_VERSION 1 #define LIQUIDIO_BASE_VERSION __stringify(LIQUIDIO_BASE_MAJOR_VERSION) "." \ __stringify(LIQUIDIO_BASE_MINOR_VERSION) @@ -106,6 +106,7 @@ enum octeon_tag_type { #define MAX_IOQ_INTERRUPTS_PER_PF (64 * 2) #define MAX_IOQ_INTERRUPTS_PER_VF (8 * 2) +#define SCR2_BIT_FW_LOADED 63 static inline u32 incr_index(u32 index, u32 count, u32 max) { @@ -189,7 +190,6 @@ static inline void add_sg_size(struct octeon_sg_entry *sg_entry, #define OCTNET_CMD_Q 0 /* NIC Command types */ -#define OCTNET_CMD_RESET_PF 0x0 #define OCTNET_CMD_CHANGE_MTU 0x1 #define OCTNET_CMD_CHANGE_MACADDR 0x2 #define OCTNET_CMD_CHANGE_DEVFLAGS 0x3 @@ -226,6 +226,9 @@ static inline void add_sg_size(struct octeon_sg_entry *sg_entry, #define OCTNET_CMD_SET_UC_LIST 0x1b #define OCTNET_CMD_SET_VF_LINKSTATE 0x1c + +#define OCTNET_CMD_QUEUE_COUNT_CTL 0x1f + #define OCTNET_CMD_VXLAN_PORT_ADD 0x0 #define OCTNET_CMD_VXLAN_PORT_DEL 0x1 #define OCTNET_CMD_RXCSUM_ENABLE 0x0 @@ -235,6 +238,8 @@ static inline void add_sg_size(struct octeon_sg_entry *sg_entry, #define OCTNET_CMD_VLAN_FILTER_ENABLE 0x1 #define OCTNET_CMD_VLAN_FILTER_DISABLE 0x0 +#define LIO_CMD_WAIT_TM 100 + /* RX(packets coming from wire) Checksum verification flags */ /* TCP/UDP csum */ #define CNNIC_L4SUM_VERIFIED 0x1 @@ -768,6 +773,7 @@ struct nic_rx_stats { /* firmware stats */ u64 fw_total_rcvd; u64 fw_total_fwd; + u64 fw_total_fwd_bytes; u64 fw_err_pko; u64 fw_err_link; u64 fw_err_drop; @@ -814,6 +820,7 @@ struct nic_tx_stats { u64 fw_tso; /* number of tso requests */ u64 fw_tso_fwd; /* number of packets segmented in tso */ u64 fw_tx_vxlan; + u64 fw_err_pki; }; struct oct_link_stats { diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_config.h b/drivers/net/ethernet/cavium/liquidio/octeon_config.h index f229d792c2b3..63bd9c94e547 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_config.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_config.h @@ -71,13 +71,17 @@ #define CN23XX_MAX_RINGS_PER_VF 8 #define CN23XX_MAX_INPUT_QUEUES CN23XX_MAX_RINGS_PER_PF -#define CN23XX_MAX_IQ_DESCRIPTORS 512 +#define CN23XX_MAX_IQ_DESCRIPTORS 2048 +#define CN23XX_DEFAULT_IQ_DESCRIPTORS 512 +#define CN23XX_MIN_IQ_DESCRIPTORS 128 #define CN23XX_DB_MIN 1 #define CN23XX_DB_MAX 8 #define CN23XX_DB_TIMEOUT 1 #define CN23XX_MAX_OUTPUT_QUEUES CN23XX_MAX_RINGS_PER_PF -#define CN23XX_MAX_OQ_DESCRIPTORS 512 +#define CN23XX_MAX_OQ_DESCRIPTORS 2048 +#define CN23XX_DEFAULT_OQ_DESCRIPTORS 512 +#define CN23XX_MIN_OQ_DESCRIPTORS 128 #define CN23XX_OQ_BUF_SIZE 1664 #define CN23XX_OQ_PKTSPER_INTR 128 /*#define CAVIUM_ONLY_CN23XX_RX_PERF*/ @@ -163,6 +167,11 @@ ((cfg)->misc.oct_link_query_interval) #define CFG_GET_IS_SLI_BP_ON(cfg) ((cfg)->misc.enable_sli_oq_bp) +#define CFG_SET_NUM_RX_DESCS_NIC_IF(cfg, idx, value) \ + ((cfg)->nic_if_cfg[idx].num_rx_descs = value) +#define CFG_SET_NUM_TX_DESCS_NIC_IF(cfg, idx, value) \ + ((cfg)->nic_if_cfg[idx].num_tx_descs = value) + /* Max IOQs per OCTEON Link */ #define MAX_IOQS_PER_NICIF 64 diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_console.c b/drivers/net/ethernet/cavium/liquidio/octeon_console.c index e08f7600f986..ec3dd69cd6b2 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_console.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_console.c @@ -37,13 +37,6 @@ static u64 cvmx_bootmem_phy_named_block_find(struct octeon_device *oct, u32 flags); static int octeon_console_read(struct octeon_device *oct, u32 console_num, char *buffer, u32 buf_size); -static u32 console_bitmask; -module_param(console_bitmask, int, 0644); -MODULE_PARM_DESC(console_bitmask, - "Bitmask indicating which consoles have debug output redirected to syslog."); - -#define MIN(a, b) min((a), (b)) -#define CAST_ULL(v) ((u64)(v)) #define BOOTLOADER_PCI_READ_BUFFER_DATA_ADDR 0x0006c008 #define BOOTLOADER_PCI_READ_BUFFER_LEN_ADDR 0x0006c004 @@ -139,16 +132,6 @@ struct octeon_pci_console_desc { }; /** - * \brief determines if a given console has debug enabled. - * @param console console to check - * @returns 1 = enabled. 0 otherwise - */ -static int octeon_console_debug_enabled(u32 console) -{ - return (console_bitmask >> (console)) & 0x1; -} - -/** * This function is the implementation of the get macros defined * for individual structure members. The argument are generated * by the macros inorder to read only the needed memory. @@ -234,7 +217,7 @@ static int __cvmx_bootmem_check_version(struct octeon_device *oct, (exact_match && major_version != exact_match)) { dev_err(&oct->pci_dev->dev, "bootmem ver mismatch %d.%d addr:0x%llx\n", major_version, minor_version, - CAST_ULL(oct->bootmem_desc_addr)); + (long long)oct->bootmem_desc_addr); return -1; } else { return 0; @@ -454,20 +437,31 @@ static void output_console_line(struct octeon_device *oct, { char *line; s32 i; + size_t len; line = console_buffer; for (i = 0; i < bytes_read; i++) { /* Output a line at a time, prefixed */ if (console_buffer[i] == '\n') { console_buffer[i] = '\0'; - if (console->leftover[0]) { - dev_info(&oct->pci_dev->dev, "%lu: %s%s\n", - console_num, console->leftover, - line); + /* We need to output 'line', prefaced by 'leftover'. + * However, it is possible we're being called to + * output 'leftover' by itself (in the case of nothing + * having been read from the console). + * + * To avoid duplication, check for this condition. + */ + if (console->leftover[0] && + (line != console->leftover)) { + if (console->print) + (*console->print)(oct, (u32)console_num, + console->leftover, + line); console->leftover[0] = '\0'; } else { - dev_info(&oct->pci_dev->dev, "%lu: %s\n", - console_num, line); + if (console->print) + (*console->print)(oct, (u32)console_num, + line, NULL); } line = &console_buffer[i + 1]; } @@ -476,13 +470,16 @@ static void output_console_line(struct octeon_device *oct, /* Save off any leftovers */ if (line != &console_buffer[bytes_read]) { console_buffer[bytes_read] = '\0'; - strcpy(console->leftover, line); + len = strlen(console->leftover); + strncpy(&console->leftover[len], line, + sizeof(console->leftover) - len); } } static void check_console(struct work_struct *work) { s32 bytes_read, tries, total_read; + size_t len; struct octeon_console *console; struct cavium_wk *wk = (struct cavium_wk *)work; struct octeon_device *oct = (struct octeon_device *)wk->ctxptr; @@ -504,7 +501,7 @@ static void check_console(struct work_struct *work) total_read += bytes_read; if (console->waiting) octeon_console_handle_result(oct, console_num); - if (octeon_console_debug_enabled(console_num)) { + if (console->print) { output_console_line(oct, console, console_num, console_buffer, bytes_read); } @@ -519,10 +516,13 @@ static void check_console(struct work_struct *work) /* If nothing is read after polling the console, * output any leftovers if any */ - if (octeon_console_debug_enabled(console_num) && - (total_read == 0) && (console->leftover[0])) { - dev_info(&oct->pci_dev->dev, "%u: %s\n", - console_num, console->leftover); + if (console->print && (total_read == 0) && + (console->leftover[0])) { + /* append '\n' as terminator for 'output_console_line' */ + len = strlen(console->leftover); + console->leftover[len] = '\n'; + output_console_line(oct, console, console_num, + console->leftover, (s32)(len + 1)); console->leftover[0] = '\0'; } @@ -574,7 +574,84 @@ int octeon_init_consoles(struct octeon_device *oct) return ret; } -int octeon_add_console(struct octeon_device *oct, u32 console_num) +static void octeon_get_uboot_version(struct octeon_device *oct) +{ + s32 bytes_read, tries, total_read; + struct octeon_console *console; + u32 console_num = 0; + char *uboot_ver; + char *buf; + char *p; + +#define OCTEON_UBOOT_VER_BUF_SIZE 512 + buf = kmalloc(OCTEON_UBOOT_VER_BUF_SIZE, GFP_KERNEL); + if (!buf) + return; + + if (octeon_console_send_cmd(oct, "setenv stdout pci\n", 50)) { + kfree(buf); + return; + } + + if (octeon_console_send_cmd(oct, "version\n", 1)) { + kfree(buf); + return; + } + + console = &oct->console[console_num]; + tries = 0; + total_read = 0; + + do { + /* Take console output regardless of whether it will + * be logged + */ + bytes_read = + octeon_console_read(oct, + console_num, buf + total_read, + OCTEON_UBOOT_VER_BUF_SIZE - 1 - + total_read); + if (bytes_read > 0) { + buf[bytes_read] = '\0'; + + total_read += bytes_read; + if (console->waiting) + octeon_console_handle_result(oct, console_num); + } else if (bytes_read < 0) { + dev_err(&oct->pci_dev->dev, "Error reading console %u, ret=%d\n", + console_num, bytes_read); + } + + tries++; + } while ((bytes_read > 0) && (tries < 16)); + + /* If nothing is read after polling the console, + * output any leftovers if any + */ + if ((total_read == 0) && (console->leftover[0])) { + dev_dbg(&oct->pci_dev->dev, "%u: %s\n", + console_num, console->leftover); + console->leftover[0] = '\0'; + } + + buf[OCTEON_UBOOT_VER_BUF_SIZE - 1] = '\0'; + + uboot_ver = strstr(buf, "U-Boot"); + if (uboot_ver) { + p = strstr(uboot_ver, "mips"); + if (p) { + p--; + *p = '\0'; + dev_info(&oct->pci_dev->dev, "%s\n", uboot_ver); + } + } + + kfree(buf); + octeon_console_send_cmd(oct, "setenv stdout serial\n", 50); +} + +int octeon_add_console(struct octeon_device *oct, u32 console_num, + char *dbg_enb) { int ret = 0; u32 delay; @@ -610,17 +687,19 @@ int octeon_add_console(struct octeon_device *oct, u32 console_num) work = &oct->console_poll_work[console_num].work; + octeon_get_uboot_version(oct); + INIT_DELAYED_WORK(work, check_console); oct->console_poll_work[console_num].ctxptr = (void *)oct; oct->console_poll_work[console_num].ctxul = console_num; delay = OCTEON_CONSOLE_POLL_INTERVAL_MS; schedule_delayed_work(work, msecs_to_jiffies(delay)); - if (octeon_console_debug_enabled(console_num)) { - ret = octeon_console_send_cmd(oct, - "setenv pci_console_active 1", - 2000); - } + /* an empty string means use default debug console enablement */ + if (dbg_enb && !dbg_enb[0]) + dbg_enb = "setenv pci_console_active 1"; + if (dbg_enb) + ret = octeon_console_send_cmd(oct, dbg_enb, 2000); console->active = 1; } @@ -704,7 +783,7 @@ static int octeon_console_read(struct octeon_device *oct, u32 console_num, if (bytes_to_read <= 0) return bytes_to_read; - bytes_to_read = MIN(bytes_to_read, (s32)buf_size); + bytes_to_read = min_t(s32, bytes_to_read, buf_size); /* Check to see if what we want to read is not contiguous, and limit * ourselves to the contiguous block diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.c b/drivers/net/ethernet/cavium/liquidio/octeon_device.c index 623e28ca736e..29d53b1763a7 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.c @@ -418,7 +418,7 @@ static struct octeon_config default_cn23xx_conf = { /** IQ attributes */ .iq = { .max_iqs = CN23XX_CFG_IO_QUEUES, - .pending_list_size = (CN23XX_MAX_IQ_DESCRIPTORS * + .pending_list_size = (CN23XX_DEFAULT_IQ_DESCRIPTORS * CN23XX_CFG_IO_QUEUES), .instr_type = OCTEON_64BYTE_INSTR, .db_min = CN23XX_DB_MIN, @@ -436,8 +436,8 @@ static struct octeon_config default_cn23xx_conf = { }, .num_nic_ports = DEFAULT_NUM_NIC_PORTS_23XX, - .num_def_rx_descs = CN23XX_MAX_OQ_DESCRIPTORS, - .num_def_tx_descs = CN23XX_MAX_IQ_DESCRIPTORS, + .num_def_rx_descs = CN23XX_DEFAULT_OQ_DESCRIPTORS, + .num_def_tx_descs = CN23XX_DEFAULT_IQ_DESCRIPTORS, .def_rx_buf_size = CN23XX_OQ_BUF_SIZE, /* For ethernet interface 0: Port cfg Attributes */ @@ -455,10 +455,10 @@ static struct octeon_config default_cn23xx_conf = { .num_rxqs = DEF_RXQS_PER_INTF, /* Num of desc for rx rings */ - .num_rx_descs = CN23XX_MAX_OQ_DESCRIPTORS, + .num_rx_descs = CN23XX_DEFAULT_OQ_DESCRIPTORS, /* Num of desc for tx rings */ - .num_tx_descs = CN23XX_MAX_IQ_DESCRIPTORS, + .num_tx_descs = CN23XX_DEFAULT_IQ_DESCRIPTORS, /* SKB size, We need not change buf size even for Jumbo frames. * Octeon can send jumbo frames in 4 consecutive descriptors, @@ -484,10 +484,10 @@ static struct octeon_config default_cn23xx_conf = { .num_rxqs = DEF_RXQS_PER_INTF, /* Num of desc for rx rings */ - .num_rx_descs = CN23XX_MAX_OQ_DESCRIPTORS, + .num_rx_descs = CN23XX_DEFAULT_OQ_DESCRIPTORS, /* Num of desc for tx rings */ - .num_tx_descs = CN23XX_MAX_IQ_DESCRIPTORS, + .num_tx_descs = CN23XX_DEFAULT_IQ_DESCRIPTORS, /* SKB size, We need not change buf size even for Jumbo frames. * Octeon can send jumbo frames in 4 consecutive descriptors, @@ -528,9 +528,10 @@ static struct octeon_config_ptr { }; static char oct_dev_state_str[OCT_DEV_STATES + 1][32] = { - "BEGIN", "PCI-MAP-DONE", "DISPATCH-INIT-DONE", + "BEGIN", "PCI-ENABLE-DONE", "PCI-MAP-DONE", "DISPATCH-INIT-DONE", "IQ-INIT-DONE", "SCBUFF-POOL-INIT-DONE", "RESPLIST-INIT-DONE", - "DROQ-INIT-DONE", "IO-QUEUES-INIT-DONE", "CONSOLE-INIT-DONE", + "DROQ-INIT-DONE", "MBOX-SETUP-DONE", "MSIX-ALLOC-VECTOR-DONE", + "INTR-SET-DONE", "IO-QUEUES-INIT-DONE", "CONSOLE-INIT-DONE", "HOST-READY", "CORE-READY", "RUNNING", "IN-RESET", "INVALID" }; @@ -876,11 +877,11 @@ int octeon_setup_instr_queues(struct octeon_device *oct) oct->num_iqs = 0; - oct->instr_queue[0] = vmalloc_node(sizeof(*oct->instr_queue[0]), + oct->instr_queue[0] = vzalloc_node(sizeof(*oct->instr_queue[0]), numa_node); if (!oct->instr_queue[0]) oct->instr_queue[0] = - vmalloc(sizeof(struct octeon_instr_queue)); + vzalloc(sizeof(struct octeon_instr_queue)); if (!oct->instr_queue[0]) return 1; memset(oct->instr_queue[0], 0, sizeof(struct octeon_instr_queue)); @@ -923,9 +924,9 @@ int octeon_setup_output_queues(struct octeon_device *oct) desc_size = CFG_GET_DEF_RX_BUF_SIZE(CHIP_CONF(oct, cn23xx_vf)); } oct->num_oqs = 0; - oct->droq[0] = vmalloc_node(sizeof(*oct->droq[0]), numa_node); + oct->droq[0] = vzalloc_node(sizeof(*oct->droq[0]), numa_node); if (!oct->droq[0]) - oct->droq[0] = vmalloc(sizeof(*oct->droq[0])); + oct->droq[0] = vzalloc(sizeof(*oct->droq[0])); if (!oct->droq[0]) return 1; diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_device.h b/drivers/net/ethernet/cavium/liquidio/octeon_device.h index c90ed48ae8ab..894af199ddef 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_device.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_device.h @@ -22,6 +22,8 @@ #ifndef _OCTEON_DEVICE_H_ #define _OCTEON_DEVICE_H_ +#include <linux/interrupt.h> + /** PCI VendorId Device Id */ #define OCTEON_CN68XX_PCIID 0x91177d #define OCTEON_CN66XX_PCIID 0x92177d @@ -192,6 +194,8 @@ struct octeon_reg_list { }; #define OCTEON_CONSOLE_MAX_READ_BYTES 512 +typedef int (*octeon_console_print_fn)(struct octeon_device *oct, + u32 num, char *pre, char *suf); struct octeon_console { u32 active; u32 waiting; @@ -199,6 +203,7 @@ struct octeon_console { u32 buffer_size; u64 input_base_addr; u64 output_base_addr; + octeon_console_print_fn print; char leftover[OCTEON_CONSOLE_MAX_READ_BYTES]; }; @@ -552,6 +557,7 @@ struct octeon_device { } loc; atomic_t *adapter_refcount; /* reference count of adapter */ + bool ptp_enable; }; #define OCT_DRV_ONLINE 1 @@ -565,6 +571,8 @@ struct octeon_device { #define CHIP_CONF(oct, TYPE) \ (((struct octeon_ ## TYPE *)((oct)->chip))->conf) +#define MAX_IO_PENDING_PKT_COUNT 100 + /*------------------ Function Prototypes ----------------------*/ /** Initialize device list memory */ @@ -740,11 +748,17 @@ int octeon_init_consoles(struct octeon_device *oct); /** * Adds access to a console to the device. * - * @param oct which octeon to add to - * @param console_num which console + * @param oct: which octeon to add to + * @param console_num: which console + * @param dbg_enb: ptr to debug enablement string, one of: + * * NULL for no debug output (i.e. disabled) + * * empty string enables debug output (via default method) + * * specific string to enable debug console output + * * @return Zero on success, negative on failure. */ -int octeon_add_console(struct octeon_device *oct, u32 console_num); +int octeon_add_console(struct octeon_device *oct, u32 console_num, + char *dbg_enb); /** write or read from a console */ int octeon_console_write(struct octeon_device *oct, u32 console_num, diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c index 2e190deb2233..9372d4ce9954 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_droq.c +++ b/drivers/net/ethernet/cavium/liquidio/octeon_droq.c @@ -145,6 +145,8 @@ octeon_droq_destroy_ring_buffers(struct octeon_device *oct, for (i = 0; i < droq->max_count; i++) { pg_info = &droq->recv_buf_list[i].pg_info; + if (!pg_info) + continue; if (pg_info->dma) lio_unmap_ring(oct->pci_dev, @@ -207,6 +209,10 @@ int octeon_delete_droq(struct octeon_device *oct, u32 q_no) droq->desc_ring, droq->desc_ring_dma); memset(droq, 0, OCT_DROQ_SIZE); + oct->io_qmask.oq &= ~(1ULL << q_no); + vfree(oct->droq[q_no]); + oct->droq[q_no] = NULL; + oct->num_oqs--; return 0; } @@ -275,12 +281,12 @@ int octeon_init_droq(struct octeon_device *oct, droq->max_count); droq->recv_buf_list = (struct octeon_recv_buffer *) - vmalloc_node(droq->max_count * + vzalloc_node(droq->max_count * OCT_DROQ_RECVBUF_SIZE, numa_node); if (!droq->recv_buf_list) droq->recv_buf_list = (struct octeon_recv_buffer *) - vmalloc(droq->max_count * + vzalloc(droq->max_count * OCT_DROQ_RECVBUF_SIZE); if (!droq->recv_buf_list) { dev_err(&oct->pci_dev->dev, "Output queue recv buf list alloc failed\n"); diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_main.h b/drivers/net/ethernet/cavium/liquidio/octeon_main.h index 7ccffbb0019e..32ef3a7d88d8 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_main.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_main.h @@ -35,6 +35,12 @@ #define DRV_NAME "LiquidIO" +struct octeon_device_priv { + /** Tasklet structures for this device. */ + struct tasklet_struct droq_tasklet; + unsigned long napi_mask; +}; + /** This structure is used by NIC driver to store information required * to free the sk_buff when the packet has been fetched by Octeon. * Bytes offset below assume worst-case of a 64-bit system. diff --git a/drivers/net/ethernet/cavium/liquidio/octeon_network.h b/drivers/net/ethernet/cavium/liquidio/octeon_network.h index ec8504b2942d..9e36319cead6 100644 --- a/drivers/net/ethernet/cavium/liquidio/octeon_network.h +++ b/drivers/net/ethernet/cavium/liquidio/octeon_network.h @@ -33,6 +33,7 @@ #define LIO_IFSTATE_REGISTERED 0x02 #define LIO_IFSTATE_RUNNING 0x04 #define LIO_IFSTATE_RX_TIMESTAMP_ENABLED 0x08 +#define LIO_IFSTATE_RESETTING 0x10 struct oct_nic_stats_resp { u64 rh; @@ -166,6 +167,14 @@ void cleanup_rx_oom_poll_fn(struct net_device *netdev); */ void liquidio_link_ctrl_cmd_completion(void *nctrl_ptr); +int liquidio_setup_io_queues(struct octeon_device *octeon_dev, int ifidx, + u32 num_iqs, u32 num_oqs); + +irqreturn_t liquidio_msix_intr_handler(int irq __attribute__((unused)), + void *dev); + +int octeon_setup_interrupt(struct octeon_device *oct, u32 num_ioqs); + /** * \brief Register ethtool operations * @param netdev pointer to network device @@ -448,4 +457,30 @@ static inline void ifstate_reset(struct lio *lio, int state_flag) atomic_set(&lio->ifstate, (atomic_read(&lio->ifstate) & ~(state_flag))); } +/** + * \brief wait for all pending requests to complete + * @param oct Pointer to Octeon device + * + * Called during shutdown sequence + */ +static inline int wait_for_pending_requests(struct octeon_device *oct) +{ + int i, pcount = 0; + + for (i = 0; i < MAX_IO_PENDING_PKT_COUNT; i++) { + pcount = atomic_read( + &oct->response_list[OCTEON_ORDERED_SC_LIST] + .pending_req_count); + if (pcount) + schedule_timeout_uninterruptible(HZ / 10); + else + break; + } + + if (pcount) + return 1; + + return 0; +} + #endif diff --git a/drivers/net/ethernet/cavium/liquidio/request_manager.c b/drivers/net/ethernet/cavium/liquidio/request_manager.c index 7b297f1f6dbe..1e0fbce86d60 100644 --- a/drivers/net/ethernet/cavium/liquidio/request_manager.c +++ b/drivers/net/ethernet/cavium/liquidio/request_manager.c @@ -77,13 +77,6 @@ int octeon_init_instr_queue(struct octeon_device *oct, return 1; } - if (num_descs & (num_descs - 1)) { - dev_err(&oct->pci_dev->dev, - "Number of descriptors for instr queue %d not in power of 2.\n", - iq_no); - return 1; - } - q_size = (u32)conf->instr_type * num_descs; iq = oct->instr_queue[iq_no]; @@ -190,6 +183,10 @@ int octeon_delete_instr_queue(struct octeon_device *oct, u32 iq_no) q_size = iq->max_count * desc_size; lio_dma_free(oct, (u32)q_size, iq->base_addr, iq->base_addr_dma); + oct->io_qmask.iq &= ~(1ULL << iq_no); + vfree(oct->instr_queue[iq_no]); + oct->instr_queue[iq_no] = NULL; + oct->num_iqs--; return 0; } return 1; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h index 57858522c33c..67d1a3230773 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_queues.h +++ b/drivers/net/ethernet/cavium/thunder/nicvf_queues.h @@ -277,7 +277,6 @@ struct snd_queue { u16 xdp_free_cnt; bool is_xdp; -#define TSO_HEADER_SIZE 128 /* For TSO segment's header */ char *tso_hdrs; dma_addr_t tso_hdrs_phys; diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 0bc6a4ffce30..6a015362c340 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -793,7 +793,9 @@ static struct attribute *cxgb3_attrs[] = { NULL }; -static struct attribute_group cxgb3_attr_group = {.attrs = cxgb3_attrs }; +static const struct attribute_group cxgb3_attr_group = { + .attrs = cxgb3_attrs, +}; static ssize_t tm_attr_show(struct device *d, char *buf, int sched) @@ -880,7 +882,9 @@ static struct attribute *offload_attrs[] = { NULL }; -static struct attribute_group offload_attr_group = {.attrs = offload_attrs }; +static const struct attribute_group offload_attr_group = { + .attrs = offload_attrs, +}; /* * Sends an sk_buff to an offload queue driver diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 09ea62ee96d3..ea72d2d2e1b4 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -104,13 +104,13 @@ enum dev_state { DEV_STATE_ERR }; -enum { +enum cc_pause { PAUSE_RX = 1 << 0, PAUSE_TX = 1 << 1, PAUSE_AUTONEG = 1 << 2 }; -enum { +enum cc_fec { FEC_AUTO = 1 << 0, /* IEEE 802.3 "automatic" */ FEC_RS = 1 << 1, /* Reed-Solomon */ FEC_BASER_RS = 1 << 2 /* BaseR/Reed-Solomon */ @@ -338,10 +338,12 @@ struct adapter_params { unsigned int sf_nsec; /* # of flash sectors */ unsigned int sf_fw_start; /* start of FW image in flash */ - unsigned int fw_vers; - unsigned int bs_vers; /* bootstrap version */ - unsigned int tp_vers; - unsigned int er_vers; /* expansion ROM version */ + unsigned int fw_vers; /* firmware version */ + unsigned int bs_vers; /* bootstrap version */ + unsigned int tp_vers; /* TP microcode version */ + unsigned int er_vers; /* expansion ROM version */ + unsigned int scfg_vers; /* Serial Configuration version */ + unsigned int vpd_vers; /* VPD Version */ u8 api_vers[7]; unsigned short mtus[NMTUS]; @@ -364,6 +366,7 @@ struct adapter_params { unsigned int max_ordird_qp; /* Max read depth per RDMA QP */ unsigned int max_ird_adapter; /* Max read depth per adapter */ bool fr_nsmr_tpte_wr_support; /* FW support for FR_NSMR_TPTE_WR */ + u8 fw_caps_support; /* 32-bit Port Capabilities */ /* MPS Buffer Group Map[per Port]. Bit i is set if buffer group i is * used by the Port @@ -437,18 +440,34 @@ struct trace_params { unsigned char port; }; +/* Firmware Port Capabilities types. */ + +typedef u16 fw_port_cap16_t; /* 16-bit Port Capabilities integral value */ +typedef u32 fw_port_cap32_t; /* 32-bit Port Capabilities integral value */ + +enum fw_caps { + FW_CAPS_UNKNOWN = 0, /* 0'ed out initial state */ + FW_CAPS16 = 1, /* old Firmware: 16-bit Port Capabilities */ + FW_CAPS32 = 2, /* new Firmware: 32-bit Port Capabilities */ +}; + struct link_config { - unsigned short supported; /* link capabilities */ - unsigned short advertising; /* advertised capabilities */ - unsigned short lp_advertising; /* peer advertised capabilities */ - unsigned int requested_speed; /* speed user has requested */ - unsigned int speed; /* actual link speed */ - unsigned char requested_fc; /* flow control user has requested */ - unsigned char fc; /* actual link flow control */ - unsigned char auto_fec; /* Forward Error Correction: */ - unsigned char requested_fec; /* "automatic" (IEEE 802.3), */ - unsigned char fec; /* requested, and actual in use */ + fw_port_cap32_t pcaps; /* link capabilities */ + fw_port_cap32_t def_acaps; /* default advertised capabilities */ + fw_port_cap32_t acaps; /* advertised capabilities */ + fw_port_cap32_t lpacaps; /* peer advertised capabilities */ + + fw_port_cap32_t speed_caps; /* speed(s) user has requested */ + unsigned int speed; /* actual link speed (Mb/s) */ + + enum cc_pause requested_fc; /* flow control user has requested */ + enum cc_pause fc; /* actual link flow control */ + + enum cc_fec requested_fec; /* Forward Error Correction: */ + enum cc_fec fec; /* requested and actual in use */ + unsigned char autoneg; /* autonegotiating? */ + unsigned char link_ok; /* link up? */ unsigned char link_down_rc; /* link down reason */ }; @@ -1404,10 +1423,15 @@ int t4_fw_upgrade(struct adapter *adap, unsigned int mbox, int t4_fl_pkt_align(struct adapter *adap); unsigned int t4_flash_cfg_addr(struct adapter *adapter); int t4_check_fw_version(struct adapter *adap); +int t4_load_cfg(struct adapter *adapter, const u8 *cfg_data, unsigned int size); int t4_get_fw_version(struct adapter *adapter, u32 *vers); int t4_get_bs_version(struct adapter *adapter, u32 *vers); int t4_get_tp_version(struct adapter *adapter, u32 *vers); int t4_get_exprom_version(struct adapter *adapter, u32 *vers); +int t4_get_scfg_version(struct adapter *adapter, u32 *vers); +int t4_get_vpd_version(struct adapter *adapter, u32 *vers); +int t4_get_version_info(struct adapter *adapter); +void t4_dump_version_info(struct adapter *adapter); int t4_prep_fw(struct adapter *adap, struct fw_info *fw_info, const u8 *fw_data, unsigned int fw_size, struct fw_hdr *card_fw, enum dev_state state, int *reset); @@ -1573,6 +1597,8 @@ int t4_ofld_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, int t4_sge_ctxt_flush(struct adapter *adap, unsigned int mbox); void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl); int t4_update_port_info(struct port_info *pi); +int t4_get_link_params(struct port_info *pi, unsigned int *link_okp, + unsigned int *speedp, unsigned int *mtup); int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl); void t4_db_full(struct adapter *adapter); void t4_db_dropped(struct adapter *adapter); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c index 26eb00a45db1..a71af1e587e2 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c @@ -533,17 +533,23 @@ static int from_fw_port_mod_type(enum fw_port_type port_type, static unsigned int speed_to_fw_caps(int speed) { if (speed == 100) - return FW_PORT_CAP_SPEED_100M; + return FW_PORT_CAP32_SPEED_100M; if (speed == 1000) - return FW_PORT_CAP_SPEED_1G; + return FW_PORT_CAP32_SPEED_1G; if (speed == 10000) - return FW_PORT_CAP_SPEED_10G; + return FW_PORT_CAP32_SPEED_10G; if (speed == 25000) - return FW_PORT_CAP_SPEED_25G; + return FW_PORT_CAP32_SPEED_25G; if (speed == 40000) - return FW_PORT_CAP_SPEED_40G; + return FW_PORT_CAP32_SPEED_40G; + if (speed == 50000) + return FW_PORT_CAP32_SPEED_50G; if (speed == 100000) - return FW_PORT_CAP_SPEED_100G; + return FW_PORT_CAP32_SPEED_100G; + if (speed == 200000) + return FW_PORT_CAP32_SPEED_200G; + if (speed == 400000) + return FW_PORT_CAP32_SPEED_400G; return 0; } @@ -560,12 +566,13 @@ static void fw_caps_to_lmm(enum fw_port_type port_type, unsigned int fw_caps, unsigned long *link_mode_mask) { - #define SET_LMM(__lmm_name) __set_bit(ETHTOOL_LINK_MODE_ ## __lmm_name \ - ## _BIT, link_mode_mask) + #define SET_LMM(__lmm_name) \ + __set_bit(ETHTOOL_LINK_MODE_ ## __lmm_name ## _BIT, \ + link_mode_mask) #define FW_CAPS_TO_LMM(__fw_name, __lmm_name) \ do { \ - if (fw_caps & FW_PORT_CAP_ ## __fw_name) \ + if (fw_caps & FW_PORT_CAP32_ ## __fw_name) \ SET_LMM(__lmm_name); \ } while (0) @@ -645,7 +652,10 @@ static void fw_caps_to_lmm(enum fw_port_type port_type, case FW_PORT_TYPE_KR4_100G: case FW_PORT_TYPE_CR4_QSFP: SET_LMM(FIBRE); - SET_LMM(100000baseCR4_Full); + FW_CAPS_TO_LMM(SPEED_40G, 40000baseSR4_Full); + FW_CAPS_TO_LMM(SPEED_25G, 25000baseCR_Full); + FW_CAPS_TO_LMM(SPEED_50G, 50000baseCR2_Full); + FW_CAPS_TO_LMM(SPEED_100G, 100000baseCR4_Full); break; default: @@ -663,8 +673,7 @@ static void fw_caps_to_lmm(enum fw_port_type port_type, /** * lmm_to_fw_caps - translate ethtool Link Mode Mask to Firmware * capabilities - * - * @link_mode_mask: ethtool Link Mode Mask + * @et_lmm: ethtool Link Mode Mask * * Translate ethtool Link Mode Mask into a Firmware Port capabilities * value. @@ -677,7 +686,7 @@ static unsigned int lmm_to_fw_caps(const unsigned long *link_mode_mask) do { \ if (test_bit(ETHTOOL_LINK_MODE_ ## __lmm_name ## _BIT, \ link_mode_mask)) \ - fw_caps |= FW_PORT_CAP_ ## __fw_name; \ + fw_caps |= FW_PORT_CAP32_ ## __fw_name; \ } while (0) LMM_TO_FW_CAPS(100baseT_Full, SPEED_100M); @@ -685,6 +694,7 @@ static unsigned int lmm_to_fw_caps(const unsigned long *link_mode_mask) LMM_TO_FW_CAPS(10000baseT_Full, SPEED_10G); LMM_TO_FW_CAPS(40000baseSR4_Full, SPEED_40G); LMM_TO_FW_CAPS(25000baseCR_Full, SPEED_25G); + LMM_TO_FW_CAPS(50000baseCR2_Full, SPEED_50G); LMM_TO_FW_CAPS(100000baseCR4_Full, SPEED_100G); #undef LMM_TO_FW_CAPS @@ -698,10 +708,6 @@ static int get_link_ksettings(struct net_device *dev, struct port_info *pi = netdev_priv(dev); struct ethtool_link_settings *base = &link_ksettings->base; - ethtool_link_ksettings_zero_link_mode(link_ksettings, supported); - ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); - ethtool_link_ksettings_zero_link_mode(link_ksettings, lp_advertising); - /* For the nonce, the Firmware doesn't send up Port State changes * when the Virtual Interface attached to the Port is down. So * if it's down, let's grab any changes. @@ -709,6 +715,10 @@ static int get_link_ksettings(struct net_device *dev, if (!netif_running(dev)) (void)t4_update_port_info(pi); + ethtool_link_ksettings_zero_link_mode(link_ksettings, supported); + ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); + ethtool_link_ksettings_zero_link_mode(link_ksettings, lp_advertising); + base->port = from_fw_port_mod_type(pi->port_type, pi->mod_type); if (pi->mdio_addr >= 0) { @@ -721,11 +731,11 @@ static int get_link_ksettings(struct net_device *dev, base->mdio_support = 0; } - fw_caps_to_lmm(pi->port_type, pi->link_cfg.supported, + fw_caps_to_lmm(pi->port_type, pi->link_cfg.pcaps, link_ksettings->link_modes.supported); - fw_caps_to_lmm(pi->port_type, pi->link_cfg.advertising, + fw_caps_to_lmm(pi->port_type, pi->link_cfg.acaps, link_ksettings->link_modes.advertising); - fw_caps_to_lmm(pi->port_type, pi->link_cfg.lp_advertising, + fw_caps_to_lmm(pi->port_type, pi->link_cfg.lpacaps, link_ksettings->link_modes.lp_advertising); if (netif_carrier_ok(dev)) { @@ -736,8 +746,24 @@ static int get_link_ksettings(struct net_device *dev, base->duplex = DUPLEX_UNKNOWN; } + if (pi->link_cfg.fc & PAUSE_RX) { + if (pi->link_cfg.fc & PAUSE_TX) { + ethtool_link_ksettings_add_link_mode(link_ksettings, + advertising, + Pause); + } else { + ethtool_link_ksettings_add_link_mode(link_ksettings, + advertising, + Asym_Pause); + } + } else if (pi->link_cfg.fc & PAUSE_TX) { + ethtool_link_ksettings_add_link_mode(link_ksettings, + advertising, + Asym_Pause); + } + base->autoneg = pi->link_cfg.autoneg; - if (pi->link_cfg.supported & FW_PORT_CAP_ANEG) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_ANEG) ethtool_link_ksettings_add_link_mode(link_ksettings, supported, Autoneg); if (pi->link_cfg.autoneg) @@ -748,8 +774,7 @@ static int get_link_ksettings(struct net_device *dev, } static int set_link_ksettings(struct net_device *dev, - const struct ethtool_link_ksettings - *link_ksettings) + const struct ethtool_link_ksettings *link_ksettings) { struct port_info *pi = netdev_priv(dev); struct link_config *lc = &pi->link_cfg; @@ -762,12 +787,12 @@ static int set_link_ksettings(struct net_device *dev, if (base->duplex != DUPLEX_FULL) return -EINVAL; - if (!(lc->supported & FW_PORT_CAP_ANEG)) { + if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) { /* PHY offers a single speed. See if that's what's * being requested. */ if (base->autoneg == AUTONEG_DISABLE && - (lc->supported & speed_to_fw_caps(base->speed))) + (lc->pcaps & speed_to_fw_caps(base->speed))) return 0; return -EINVAL; } @@ -776,18 +801,17 @@ static int set_link_ksettings(struct net_device *dev, if (base->autoneg == AUTONEG_DISABLE) { fw_caps = speed_to_fw_caps(base->speed); - if (!(lc->supported & fw_caps)) + if (!(lc->pcaps & fw_caps)) return -EINVAL; - lc->requested_speed = fw_caps; - lc->advertising = 0; + lc->speed_caps = fw_caps; + lc->acaps = 0; } else { fw_caps = - lmm_to_fw_caps(link_ksettings->link_modes.advertising); - - if (!(lc->supported & fw_caps)) + lmm_to_fw_caps(link_ksettings->link_modes.advertising); + if (!(lc->pcaps & fw_caps)) return -EINVAL; - lc->requested_speed = 0; - lc->advertising = fw_caps | FW_PORT_CAP_ANEG; + lc->speed_caps = 0; + lc->acaps = fw_caps | FW_PORT_CAP32_ANEG; } lc->autoneg = base->autoneg; @@ -801,6 +825,104 @@ static int set_link_ksettings(struct net_device *dev, return ret; } +/* Translate the Firmware FEC value into the ethtool value. */ +static inline unsigned int fwcap_to_eth_fec(unsigned int fw_fec) +{ + unsigned int eth_fec = 0; + + if (fw_fec & FW_PORT_CAP32_FEC_RS) + eth_fec |= ETHTOOL_FEC_RS; + if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS) + eth_fec |= ETHTOOL_FEC_BASER; + + /* if nothing is set, then FEC is off */ + if (!eth_fec) + eth_fec = ETHTOOL_FEC_OFF; + + return eth_fec; +} + +/* Translate Common Code FEC value into ethtool value. */ +static inline unsigned int cc_to_eth_fec(unsigned int cc_fec) +{ + unsigned int eth_fec = 0; + + if (cc_fec & FEC_AUTO) + eth_fec |= ETHTOOL_FEC_AUTO; + if (cc_fec & FEC_RS) + eth_fec |= ETHTOOL_FEC_RS; + if (cc_fec & FEC_BASER_RS) + eth_fec |= ETHTOOL_FEC_BASER; + + /* if nothing is set, then FEC is off */ + if (!eth_fec) + eth_fec = ETHTOOL_FEC_OFF; + + return eth_fec; +} + +/* Translate ethtool FEC value into Common Code value. */ +static inline unsigned int eth_to_cc_fec(unsigned int eth_fec) +{ + unsigned int cc_fec = 0; + + if (eth_fec & ETHTOOL_FEC_OFF) + return cc_fec; + + if (eth_fec & ETHTOOL_FEC_AUTO) + cc_fec |= FEC_AUTO; + if (eth_fec & ETHTOOL_FEC_RS) + cc_fec |= FEC_RS; + if (eth_fec & ETHTOOL_FEC_BASER) + cc_fec |= FEC_BASER_RS; + + return cc_fec; +} + +static int get_fecparam(struct net_device *dev, struct ethtool_fecparam *fec) +{ + const struct port_info *pi = netdev_priv(dev); + const struct link_config *lc = &pi->link_cfg; + + /* Translate the Firmware FEC Support into the ethtool value. We + * always support IEEE 802.3 "automatic" selection of Link FEC type if + * any FEC is supported. + */ + fec->fec = fwcap_to_eth_fec(lc->pcaps); + if (fec->fec != ETHTOOL_FEC_OFF) + fec->fec |= ETHTOOL_FEC_AUTO; + + /* Translate the current internal FEC parameters into the + * ethtool values. + */ + fec->active_fec = cc_to_eth_fec(lc->fec); + + return 0; +} + +static int set_fecparam(struct net_device *dev, struct ethtool_fecparam *fec) +{ + struct port_info *pi = netdev_priv(dev); + struct link_config *lc = &pi->link_cfg; + struct link_config old_lc; + int ret; + + /* Save old Link Configuration in case the L1 Configure below + * fails. + */ + old_lc = *lc; + + /* Try to perform the L1 Configure and return the result of that + * effort. If it fails, revert the attempted change. + */ + lc->requested_fec = eth_to_cc_fec(fec->fec); + ret = t4_link_l1cfg(pi->adapter, pi->adapter->mbox, + pi->tx_chan, lc); + if (ret) + *lc = old_lc; + return ret; +} + static void get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *epause) { @@ -819,7 +941,7 @@ static int set_pauseparam(struct net_device *dev, if (epause->autoneg == AUTONEG_DISABLE) lc->requested_fc = 0; - else if (lc->supported & FW_PORT_CAP_ANEG) + else if (lc->pcaps & FW_PORT_CAP32_ANEG) lc->requested_fc = PAUSE_AUTONEG; else return -EINVAL; @@ -1255,6 +1377,8 @@ static int get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, static const struct ethtool_ops cxgb_ethtool_ops = { .get_link_ksettings = get_link_ksettings, .set_link_ksettings = set_link_ksettings, + .get_fecparam = get_fecparam, + .set_fecparam = set_fecparam, .get_drvinfo = get_drvinfo, .get_msglevel = get_msglevel, .set_msglevel = set_msglevel, diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 33bb8678833a..92d9d795d874 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -530,15 +530,22 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp, FW_PORT_CMD_ACTION_G(ntohl(pcmd->action_to_len16)); if (cmd == FW_PORT_CMD && - action == FW_PORT_ACTION_GET_PORT_INFO) { + (action == FW_PORT_ACTION_GET_PORT_INFO || + action == FW_PORT_ACTION_GET_PORT_INFO32)) { int port = FW_PORT_CMD_PORTID_G( be32_to_cpu(pcmd->op_to_portid)); - struct net_device *dev = - q->adap->port[q->adap->chan_map[port]]; - int state_input = ((pcmd->u.info.dcbxdis_pkd & - FW_PORT_CMD_DCBXDIS_F) - ? CXGB4_DCB_INPUT_FW_DISABLED - : CXGB4_DCB_INPUT_FW_ENABLED); + struct net_device *dev; + int dcbxdis, state_input; + + dev = q->adap->port[q->adap->chan_map[port]]; + dcbxdis = (action == FW_PORT_ACTION_GET_PORT_INFO + ? !!(pcmd->u.info.dcbxdis_pkd & + FW_PORT_CMD_DCBXDIS_F) + : !!(pcmd->u.info32.lstatus32_to_cbllen32 & + FW_PORT_CMD_DCBXDIS32_F)); + state_input = (dcbxdis + ? CXGB4_DCB_INPUT_FW_DISABLED + : CXGB4_DCB_INPUT_FW_ENABLED); cxgb4_dcb_state_fsm(dev, state_input); } @@ -2672,11 +2679,10 @@ static int cxgb_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate, { struct port_info *pi = netdev_priv(dev); struct adapter *adap = pi->adapter; - struct fw_port_cmd port_cmd, port_rpl; - u32 link_status, speed = 0; + unsigned int link_ok, speed, mtu; u32 fw_pfvf, fw_class; int class_id = vf; - int link_ok, ret; + int ret; u16 pktsize; if (vf >= adap->num_vfs) @@ -2688,41 +2694,18 @@ static int cxgb_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate, min_tx_rate, vf); return -EINVAL; } - /* Retrieve link details for VF port */ - memset(&port_cmd, 0, sizeof(port_cmd)); - port_cmd.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | - FW_CMD_REQUEST_F | - FW_CMD_READ_F | - FW_PORT_CMD_PORTID_V(pi->port_id)); - port_cmd.action_to_len16 = - cpu_to_be32(FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_GET_PORT_INFO) | - FW_LEN16(port_cmd)); - ret = t4_wr_mbox(adap, adap->mbox, &port_cmd, sizeof(port_cmd), - &port_rpl); + + ret = t4_get_link_params(pi, &link_ok, &speed, &mtu); if (ret != FW_SUCCESS) { dev_err(adap->pdev_dev, - "Failed to get link status for VF %d\n", vf); + "Failed to get link information for VF %d\n", vf); return -EINVAL; } - link_status = be32_to_cpu(port_rpl.u.info.lstatus_to_modtype); - link_ok = (link_status & FW_PORT_CMD_LSTATUS_F) != 0; + if (!link_ok) { dev_err(adap->pdev_dev, "Link down for VF %d\n", vf); return -EINVAL; } - /* Determine link speed */ - if (link_status & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M)) - speed = 100; - else if (link_status & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G)) - speed = 1000; - else if (link_status & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G)) - speed = 10000; - else if (link_status & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_25G)) - speed = 25000; - else if (link_status & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G)) - speed = 40000; - else if (link_status & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100G)) - speed = 100000; if (max_tx_rate > speed) { dev_err(adap->pdev_dev, @@ -2730,7 +2713,8 @@ static int cxgb_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate, max_tx_rate, vf, speed); return -EINVAL; } - pktsize = be16_to_cpu(port_rpl.u.info.mtu); + + pktsize = mtu; /* subtract ethhdr size and 4 bytes crc since, f/w appends it */ pktsize = pktsize - sizeof(struct ethhdr) - 4; /* subtract ipv4 hdr size, tcp hdr size to get typical IPv4 MSS size */ @@ -2741,7 +2725,7 @@ static int cxgb_set_vf_rate(struct net_device *dev, int vf, int min_tx_rate, SCHED_CLASS_MODE_CLASS, SCHED_CLASS_RATEUNIT_BITS, SCHED_CLASS_RATEMODE_ABS, - pi->port_id, class_id, 0, + pi->tx_chan, class_id, 0, max_tx_rate * 1000, 0, pktsize); if (ret) { dev_err(adap->pdev_dev, "Err %d for Traffic Class config\n", @@ -2889,14 +2873,29 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate) return err; } -static int cxgb_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +static int cxgb_setup_tc_cls_u32(struct net_device *dev, + struct tc_cls_u32_offload *cls_u32) { - struct port_info *pi = netdev2pinfo(dev); - struct adapter *adap = netdev2adap(dev); + if (!is_classid_clsact_ingress(cls_u32->common.classid) || + cls_u32->common.chain_index) + return -EOPNOTSUPP; - if (chain_index) + switch (cls_u32->command) { + case TC_CLSU32_NEW_KNODE: + case TC_CLSU32_REPLACE_KNODE: + return cxgb4_config_knode(dev, cls_u32); + case TC_CLSU32_DELETE_KNODE: + return cxgb4_delete_knode(dev, cls_u32); + default: return -EOPNOTSUPP; + } +} + +static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + struct port_info *pi = netdev2pinfo(dev); + struct adapter *adap = netdev2adap(dev); if (!(adap->flags & FULL_INIT_DONE)) { dev_err(adap->pdev_dev, @@ -2905,20 +2904,12 @@ static int cxgb_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, return -EINVAL; } - if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) && - tc->type == TC_SETUP_CLSU32) { - switch (tc->cls_u32->command) { - case TC_CLSU32_NEW_KNODE: - case TC_CLSU32_REPLACE_KNODE: - return cxgb4_config_knode(dev, proto, tc->cls_u32); - case TC_CLSU32_DELETE_KNODE: - return cxgb4_delete_knode(dev, proto, tc->cls_u32); - default: - return -EOPNOTSUPP; - } + switch (type) { + case TC_SETUP_CLSU32: + return cxgb_setup_tc_cls_u32(dev, type_data); + default: + return -EOPNOTSUPP; } - - return -EOPNOTSUPP; } static netdev_features_t cxgb_fix_features(struct net_device *dev, @@ -3610,11 +3601,8 @@ static int adap_init0(struct adapter *adap) * later reporting and B. to warn if the currently loaded firmware * is excessively mismatched relative to the driver.) */ - t4_get_fw_version(adap, &adap->params.fw_vers); - t4_get_bs_version(adap, &adap->params.bs_vers); - t4_get_tp_version(adap, &adap->params.tp_vers); - t4_get_exprom_version(adap, &adap->params.er_vers); + t4_get_version_info(adap); ret = t4_check_fw_version(adap); /* If firmware is too old (not supported by driver) force an update. */ if (ret) @@ -4204,8 +4192,9 @@ static inline bool is_x_10g_port(const struct link_config *lc) { unsigned int speeds, high_speeds; - speeds = FW_PORT_CAP_SPEED_V(FW_PORT_CAP_SPEED_G(lc->supported)); - high_speeds = speeds & ~(FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G); + speeds = FW_PORT_CAP32_SPEED_V(FW_PORT_CAP32_SPEED_G(lc->pcaps)); + high_speeds = speeds & + ~(FW_PORT_CAP32_SPEED_100M | FW_PORT_CAP32_SPEED_1G); return high_speeds != 0; } @@ -4560,56 +4549,8 @@ static void cxgb4_check_pcie_caps(struct adapter *adap) /* Dump basic information about the adapter */ static void print_adapter_info(struct adapter *adapter) { - /* Device information */ - dev_info(adapter->pdev_dev, "Chelsio %s rev %d\n", - adapter->params.vpd.id, - CHELSIO_CHIP_RELEASE(adapter->params.chip)); - dev_info(adapter->pdev_dev, "S/N: %s, P/N: %s\n", - adapter->params.vpd.sn, adapter->params.vpd.pn); - - /* Firmware Version */ - if (!adapter->params.fw_vers) - dev_warn(adapter->pdev_dev, "No firmware loaded\n"); - else - dev_info(adapter->pdev_dev, "Firmware version: %u.%u.%u.%u\n", - FW_HDR_FW_VER_MAJOR_G(adapter->params.fw_vers), - FW_HDR_FW_VER_MINOR_G(adapter->params.fw_vers), - FW_HDR_FW_VER_MICRO_G(adapter->params.fw_vers), - FW_HDR_FW_VER_BUILD_G(adapter->params.fw_vers)); - - /* Bootstrap Firmware Version. (Some adapters don't have Bootstrap - * Firmware, so dev_info() is more appropriate here.) - */ - if (!adapter->params.bs_vers) - dev_info(adapter->pdev_dev, "No bootstrap loaded\n"); - else - dev_info(adapter->pdev_dev, "Bootstrap version: %u.%u.%u.%u\n", - FW_HDR_FW_VER_MAJOR_G(adapter->params.bs_vers), - FW_HDR_FW_VER_MINOR_G(adapter->params.bs_vers), - FW_HDR_FW_VER_MICRO_G(adapter->params.bs_vers), - FW_HDR_FW_VER_BUILD_G(adapter->params.bs_vers)); - - /* TP Microcode Version */ - if (!adapter->params.tp_vers) - dev_warn(adapter->pdev_dev, "No TP Microcode loaded\n"); - else - dev_info(adapter->pdev_dev, - "TP Microcode version: %u.%u.%u.%u\n", - FW_HDR_FW_VER_MAJOR_G(adapter->params.tp_vers), - FW_HDR_FW_VER_MINOR_G(adapter->params.tp_vers), - FW_HDR_FW_VER_MICRO_G(adapter->params.tp_vers), - FW_HDR_FW_VER_BUILD_G(adapter->params.tp_vers)); - - /* Expansion ROM version */ - if (!adapter->params.er_vers) - dev_info(adapter->pdev_dev, "No Expansion ROM loaded\n"); - else - dev_info(adapter->pdev_dev, - "Expansion ROM version: %u.%u.%u.%u\n", - FW_HDR_FW_VER_MAJOR_G(adapter->params.er_vers), - FW_HDR_FW_VER_MINOR_G(adapter->params.er_vers), - FW_HDR_FW_VER_MICRO_G(adapter->params.er_vers), - FW_HDR_FW_VER_BUILD_G(adapter->params.er_vers)); + /* Hardware/Firmware/etc. Version/Revision IDs */ + t4_dump_version_info(adapter); /* Software/Hardware configuration */ dev_info(adapter->pdev_dev, "Configuration: %sNIC %s, %s capable\n", @@ -4634,18 +4575,24 @@ static void print_port_info(const struct net_device *dev) else if (adap->params.pci.speed == PCI_EXP_LNKSTA_CLS_8_0GB) spd = " 8 GT/s"; - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_100M) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_100M) bufp += sprintf(bufp, "100M/"); - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_1G) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_1G) bufp += sprintf(bufp, "1G/"); - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_10G) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_10G) bufp += sprintf(bufp, "10G/"); - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_25G) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_25G) bufp += sprintf(bufp, "25G/"); - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_40G) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_40G) bufp += sprintf(bufp, "40G/"); - if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_100G) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_50G) + bufp += sprintf(bufp, "50G/"); + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_100G) bufp += sprintf(bufp, "100G/"); + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_200G) + bufp += sprintf(bufp, "200G/"); + if (pi->link_cfg.pcaps & FW_PORT_CAP32_SPEED_400G) + bufp += sprintf(bufp, "400G/"); if (bufp != buf) --bufp; sprintf(bufp, "BASE-%s", t4_get_port_type_description(pi->port_type)); @@ -4751,10 +4698,11 @@ static int config_mgmt_dev(struct pci_dev *pdev) pi = netdev_priv(netdev); pi->adapter = adap; - pi->port_id = adap->pf % adap->params.nports; + pi->tx_chan = adap->pf % adap->params.nports; SET_NETDEV_DEV(netdev, &pdev->dev); adap->port[0] = netdev; + pi->port_id = 0; err = register_netdev(adap->port[0]); if (err) { diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c index ef06ce8247ab..48970ba08bdc 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c @@ -96,7 +96,7 @@ static int fill_action_fields(struct adapter *adap, LIST_HEAD(actions); exts = cls->knode.exts; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return -EINVAL; tcf_exts_to_list(exts, &actions); @@ -146,11 +146,11 @@ static int fill_action_fields(struct adapter *adap, return 0; } -int cxgb4_config_knode(struct net_device *dev, __be16 protocol, - struct tc_cls_u32_offload *cls) +int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) { const struct cxgb4_match_field *start, *link_start = NULL; struct adapter *adapter = netdev2adap(dev); + __be16 protocol = cls->common.protocol; struct ch_filter_specification fs; struct cxgb4_tc_u32_table *t; struct cxgb4_link *link; @@ -338,8 +338,7 @@ out: return ret; } -int cxgb4_delete_knode(struct net_device *dev, __be16 protocol, - struct tc_cls_u32_offload *cls) +int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) { struct adapter *adapter = netdev2adap(dev); unsigned int filter_id, max_tids, i, j; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h index 021261a41c13..70a07b7cca56 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h @@ -44,10 +44,8 @@ static inline bool can_tc_u32_offload(struct net_device *dev) return (dev->features & NETIF_F_HW_TC) && adap->tc_u32 ? true : false; } -int cxgb4_config_knode(struct net_device *dev, __be16 protocol, - struct tc_cls_u32_offload *cls); -int cxgb4_delete_knode(struct net_device *dev, __be16 protocol, - struct tc_cls_u32_offload *cls); +int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls); +int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls); void cxgb4_cleanup_tc_u32(struct adapter *adapter); struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap); diff --git a/drivers/net/ethernet/chelsio/cxgb4/sched.c b/drivers/net/ethernet/chelsio/cxgb4/sched.c index 02acff741f11..9148abb7994c 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/sched.c +++ b/drivers/net/ethernet/chelsio/cxgb4/sched.c @@ -533,10 +533,10 @@ struct sched_table *t4_init_sched(unsigned int sched_size) void t4_cleanup_sched(struct adapter *adap) { struct sched_table *s; - unsigned int i; + unsigned int j, i; - for_each_port(adap, i) { - struct port_info *pi = netdev2pinfo(adap->port[i]); + for_each_port(adap, j) { + struct port_info *pi = netdev2pinfo(adap->port[j]); s = pi->sched_tbl; for (i = 0; i < s->sched_size; i++) { diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 0293b41171a5..b65ce26ff72f 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -913,7 +913,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0xd010, 0xd03c, 0xdfc0, 0xdfe0, 0xe000, 0xea7c, - 0xf000, 0x11190, + 0xf000, 0x11110, + 0x11118, 0x11190, 0x19040, 0x1906c, 0x19078, 0x19080, 0x1908c, 0x190e4, @@ -1439,8 +1440,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x1ff00, 0x1ff84, 0x1ffc0, 0x1ffc8, 0x30000, 0x30030, - 0x30038, 0x30038, - 0x30040, 0x30040, 0x30100, 0x30144, 0x30190, 0x301a0, 0x301a8, 0x301b8, @@ -1551,8 +1550,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x33c3c, 0x33c50, 0x33cf0, 0x33cfc, 0x34000, 0x34030, - 0x34038, 0x34038, - 0x34040, 0x34040, 0x34100, 0x34144, 0x34190, 0x341a0, 0x341a8, 0x341b8, @@ -1663,8 +1660,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x37c3c, 0x37c50, 0x37cf0, 0x37cfc, 0x38000, 0x38030, - 0x38038, 0x38038, - 0x38040, 0x38040, 0x38100, 0x38144, 0x38190, 0x381a0, 0x381a8, 0x381b8, @@ -1775,8 +1770,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x3bc3c, 0x3bc50, 0x3bcf0, 0x3bcfc, 0x3c000, 0x3c030, - 0x3c038, 0x3c038, - 0x3c040, 0x3c040, 0x3c100, 0x3c144, 0x3c190, 0x3c1a0, 0x3c1a8, 0x3c1b8, @@ -2040,12 +2033,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x1190, 0x1194, 0x11a0, 0x11a4, 0x11b0, 0x11b4, - 0x11fc, 0x1258, - 0x1280, 0x12d4, - 0x12d9, 0x12d9, - 0x12de, 0x12de, - 0x12e3, 0x12e3, - 0x12e8, 0x133c, + 0x11fc, 0x1274, + 0x1280, 0x133c, 0x1800, 0x18fc, 0x3000, 0x302c, 0x3060, 0x30b0, @@ -2076,6 +2065,9 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x5ea0, 0x5eb0, 0x5ec0, 0x5ec0, 0x5ec8, 0x5ed0, + 0x5ee0, 0x5ee0, + 0x5ef0, 0x5ef0, + 0x5f00, 0x5f00, 0x6000, 0x6020, 0x6028, 0x6040, 0x6058, 0x609c, @@ -2133,6 +2125,8 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0xd300, 0xd31c, 0xdfc0, 0xdfe0, 0xe000, 0xf008, + 0xf010, 0xf018, + 0xf020, 0xf028, 0x11000, 0x11014, 0x11048, 0x1106c, 0x11074, 0x11088, @@ -2256,13 +2250,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x1ff00, 0x1ff84, 0x1ffc0, 0x1ffc8, 0x30000, 0x30030, - 0x30038, 0x30038, - 0x30040, 0x30040, - 0x30048, 0x30048, - 0x30050, 0x30050, - 0x3005c, 0x30060, - 0x30068, 0x30068, - 0x30070, 0x30070, 0x30100, 0x30168, 0x30190, 0x301a0, 0x301a8, 0x301b8, @@ -2325,13 +2312,12 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x326a8, 0x326a8, 0x326ec, 0x326ec, 0x32a00, 0x32abc, - 0x32b00, 0x32b38, + 0x32b00, 0x32b18, + 0x32b20, 0x32b38, 0x32b40, 0x32b58, 0x32b60, 0x32b78, 0x32c00, 0x32c00, 0x32c08, 0x32c3c, - 0x32e00, 0x32e2c, - 0x32f00, 0x32f2c, 0x33000, 0x3302c, 0x33034, 0x33050, 0x33058, 0x33058, @@ -2396,13 +2382,6 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x33c38, 0x33c50, 0x33cf0, 0x33cfc, 0x34000, 0x34030, - 0x34038, 0x34038, - 0x34040, 0x34040, - 0x34048, 0x34048, - 0x34050, 0x34050, - 0x3405c, 0x34060, - 0x34068, 0x34068, - 0x34070, 0x34070, 0x34100, 0x34168, 0x34190, 0x341a0, 0x341a8, 0x341b8, @@ -2465,13 +2444,12 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x366a8, 0x366a8, 0x366ec, 0x366ec, 0x36a00, 0x36abc, - 0x36b00, 0x36b38, + 0x36b00, 0x36b18, + 0x36b20, 0x36b38, 0x36b40, 0x36b58, 0x36b60, 0x36b78, 0x36c00, 0x36c00, 0x36c08, 0x36c3c, - 0x36e00, 0x36e2c, - 0x36f00, 0x36f2c, 0x37000, 0x3702c, 0x37034, 0x37050, 0x37058, 0x37058, @@ -2545,8 +2523,7 @@ void t4_get_regs(struct adapter *adap, void *buf, size_t buf_size) 0x40280, 0x40280, 0x40304, 0x40304, 0x40330, 0x4033c, - 0x41304, 0x413b8, - 0x413c0, 0x413c8, + 0x41304, 0x413c8, 0x413d0, 0x413dc, 0x413f0, 0x413f0, 0x41400, 0x4140c, @@ -3100,6 +3077,179 @@ int t4_get_exprom_version(struct adapter *adap, u32 *vers) } /** + * t4_get_vpd_version - return the VPD version + * @adapter: the adapter + * @vers: where to place the version + * + * Reads the VPD via the Firmware interface (thus this can only be called + * once we're ready to issue Firmware commands). The format of the + * VPD version is adapter specific. Returns 0 on success, an error on + * failure. + * + * Note that early versions of the Firmware didn't include the ability + * to retrieve the VPD version, so we zero-out the return-value parameter + * in that case to avoid leaving it with garbage in it. + * + * Also note that the Firmware will return its cached copy of the VPD + * Revision ID, not the actual Revision ID as written in the Serial + * EEPROM. This is only an issue if a new VPD has been written and the + * Firmware/Chip haven't yet gone through a RESET sequence. So it's best + * to defer calling this routine till after a FW_RESET_CMD has been issued + * if the Host Driver will be performing a full adapter initialization. + */ +int t4_get_vpd_version(struct adapter *adapter, u32 *vers) +{ + u32 vpdrev_param; + int ret; + + vpdrev_param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | + FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_VPDREV)); + ret = t4_query_params(adapter, adapter->mbox, adapter->pf, 0, + 1, &vpdrev_param, vers); + if (ret) + *vers = 0; + return ret; +} + +/** + * t4_get_scfg_version - return the Serial Configuration version + * @adapter: the adapter + * @vers: where to place the version + * + * Reads the Serial Configuration Version via the Firmware interface + * (thus this can only be called once we're ready to issue Firmware + * commands). The format of the Serial Configuration version is + * adapter specific. Returns 0 on success, an error on failure. + * + * Note that early versions of the Firmware didn't include the ability + * to retrieve the Serial Configuration version, so we zero-out the + * return-value parameter in that case to avoid leaving it with + * garbage in it. + * + * Also note that the Firmware will return its cached copy of the Serial + * Initialization Revision ID, not the actual Revision ID as written in + * the Serial EEPROM. This is only an issue if a new VPD has been written + * and the Firmware/Chip haven't yet gone through a RESET sequence. So + * it's best to defer calling this routine till after a FW_RESET_CMD has + * been issued if the Host Driver will be performing a full adapter + * initialization. + */ +int t4_get_scfg_version(struct adapter *adapter, u32 *vers) +{ + u32 scfgrev_param; + int ret; + + scfgrev_param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_DEV) | + FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_DEV_SCFGREV)); + ret = t4_query_params(adapter, adapter->mbox, adapter->pf, 0, + 1, &scfgrev_param, vers); + if (ret) + *vers = 0; + return ret; +} + +/** + * t4_get_version_info - extract various chip/firmware version information + * @adapter: the adapter + * + * Reads various chip/firmware version numbers and stores them into the + * adapter Adapter Parameters structure. If any of the efforts fails + * the first failure will be returned, but all of the version numbers + * will be read. + */ +int t4_get_version_info(struct adapter *adapter) +{ + int ret = 0; + + #define FIRST_RET(__getvinfo) \ + do { \ + int __ret = __getvinfo; \ + if (__ret && !ret) \ + ret = __ret; \ + } while (0) + + FIRST_RET(t4_get_fw_version(adapter, &adapter->params.fw_vers)); + FIRST_RET(t4_get_bs_version(adapter, &adapter->params.bs_vers)); + FIRST_RET(t4_get_tp_version(adapter, &adapter->params.tp_vers)); + FIRST_RET(t4_get_exprom_version(adapter, &adapter->params.er_vers)); + FIRST_RET(t4_get_scfg_version(adapter, &adapter->params.scfg_vers)); + FIRST_RET(t4_get_vpd_version(adapter, &adapter->params.vpd_vers)); + + #undef FIRST_RET + return ret; +} + +/** + * t4_dump_version_info - dump all of the adapter configuration IDs + * @adapter: the adapter + * + * Dumps all of the various bits of adapter configuration version/revision + * IDs information. This is typically called at some point after + * t4_get_version_info() has been called. + */ +void t4_dump_version_info(struct adapter *adapter) +{ + /* Device information */ + dev_info(adapter->pdev_dev, "Chelsio %s rev %d\n", + adapter->params.vpd.id, + CHELSIO_CHIP_RELEASE(adapter->params.chip)); + dev_info(adapter->pdev_dev, "S/N: %s, P/N: %s\n", + adapter->params.vpd.sn, adapter->params.vpd.pn); + + /* Firmware Version */ + if (!adapter->params.fw_vers) + dev_warn(adapter->pdev_dev, "No firmware loaded\n"); + else + dev_info(adapter->pdev_dev, "Firmware version: %u.%u.%u.%u\n", + FW_HDR_FW_VER_MAJOR_G(adapter->params.fw_vers), + FW_HDR_FW_VER_MINOR_G(adapter->params.fw_vers), + FW_HDR_FW_VER_MICRO_G(adapter->params.fw_vers), + FW_HDR_FW_VER_BUILD_G(adapter->params.fw_vers)); + + /* Bootstrap Firmware Version. (Some adapters don't have Bootstrap + * Firmware, so dev_info() is more appropriate here.) + */ + if (!adapter->params.bs_vers) + dev_info(adapter->pdev_dev, "No bootstrap loaded\n"); + else + dev_info(adapter->pdev_dev, "Bootstrap version: %u.%u.%u.%u\n", + FW_HDR_FW_VER_MAJOR_G(adapter->params.bs_vers), + FW_HDR_FW_VER_MINOR_G(adapter->params.bs_vers), + FW_HDR_FW_VER_MICRO_G(adapter->params.bs_vers), + FW_HDR_FW_VER_BUILD_G(adapter->params.bs_vers)); + + /* TP Microcode Version */ + if (!adapter->params.tp_vers) + dev_warn(adapter->pdev_dev, "No TP Microcode loaded\n"); + else + dev_info(adapter->pdev_dev, + "TP Microcode version: %u.%u.%u.%u\n", + FW_HDR_FW_VER_MAJOR_G(adapter->params.tp_vers), + FW_HDR_FW_VER_MINOR_G(adapter->params.tp_vers), + FW_HDR_FW_VER_MICRO_G(adapter->params.tp_vers), + FW_HDR_FW_VER_BUILD_G(adapter->params.tp_vers)); + + /* Expansion ROM version */ + if (!adapter->params.er_vers) + dev_info(adapter->pdev_dev, "No Expansion ROM loaded\n"); + else + dev_info(adapter->pdev_dev, + "Expansion ROM version: %u.%u.%u.%u\n", + FW_HDR_FW_VER_MAJOR_G(adapter->params.er_vers), + FW_HDR_FW_VER_MINOR_G(adapter->params.er_vers), + FW_HDR_FW_VER_MICRO_G(adapter->params.er_vers), + FW_HDR_FW_VER_BUILD_G(adapter->params.er_vers)); + + /* Serial Configuration version */ + dev_info(adapter->pdev_dev, "Serial Configuration version: %#x\n", + adapter->params.scfg_vers); + + /* VPD Version */ + dev_info(adapter->pdev_dev, "VPD version: %#x\n", + adapter->params.vpd_vers); +} + +/** * t4_check_fw_version - check if the FW is supported with this driver * @adap: the adapter * @@ -3685,16 +3835,143 @@ void t4_ulprx_read_la(struct adapter *adap, u32 *la_buf) } } -#define ADVERT_MASK (FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G |\ - FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_SPEED_25G | \ - FW_PORT_CAP_SPEED_40G | FW_PORT_CAP_SPEED_100G | \ - FW_PORT_CAP_ANEG) +#define ADVERT_MASK (FW_PORT_CAP32_SPEED_V(FW_PORT_CAP32_SPEED_M) | \ + FW_PORT_CAP32_ANEG) + +/** + * fwcaps16_to_caps32 - convert 16-bit Port Capabilities to 32-bits + * @caps16: a 16-bit Port Capabilities value + * + * Returns the equivalent 32-bit Port Capabilities value. + */ +static fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16) +{ + fw_port_cap32_t caps32 = 0; + + #define CAP16_TO_CAP32(__cap) \ + do { \ + if (caps16 & FW_PORT_CAP_##__cap) \ + caps32 |= FW_PORT_CAP32_##__cap; \ + } while (0) + + CAP16_TO_CAP32(SPEED_100M); + CAP16_TO_CAP32(SPEED_1G); + CAP16_TO_CAP32(SPEED_25G); + CAP16_TO_CAP32(SPEED_10G); + CAP16_TO_CAP32(SPEED_40G); + CAP16_TO_CAP32(SPEED_100G); + CAP16_TO_CAP32(FC_RX); + CAP16_TO_CAP32(FC_TX); + CAP16_TO_CAP32(ANEG); + CAP16_TO_CAP32(MDIX); + CAP16_TO_CAP32(MDIAUTO); + CAP16_TO_CAP32(FEC_RS); + CAP16_TO_CAP32(FEC_BASER_RS); + CAP16_TO_CAP32(802_3_PAUSE); + CAP16_TO_CAP32(802_3_ASM_DIR); + + #undef CAP16_TO_CAP32 + + return caps32; +} + +/** + * fwcaps32_to_caps16 - convert 32-bit Port Capabilities to 16-bits + * @caps32: a 32-bit Port Capabilities value + * + * Returns the equivalent 16-bit Port Capabilities value. Note that + * not all 32-bit Port Capabilities can be represented in the 16-bit + * Port Capabilities and some fields/values may not make it. + */ +static fw_port_cap16_t fwcaps32_to_caps16(fw_port_cap32_t caps32) +{ + fw_port_cap16_t caps16 = 0; + + #define CAP32_TO_CAP16(__cap) \ + do { \ + if (caps32 & FW_PORT_CAP32_##__cap) \ + caps16 |= FW_PORT_CAP_##__cap; \ + } while (0) + + CAP32_TO_CAP16(SPEED_100M); + CAP32_TO_CAP16(SPEED_1G); + CAP32_TO_CAP16(SPEED_10G); + CAP32_TO_CAP16(SPEED_25G); + CAP32_TO_CAP16(SPEED_40G); + CAP32_TO_CAP16(SPEED_100G); + CAP32_TO_CAP16(FC_RX); + CAP32_TO_CAP16(FC_TX); + CAP32_TO_CAP16(802_3_PAUSE); + CAP32_TO_CAP16(802_3_ASM_DIR); + CAP32_TO_CAP16(ANEG); + CAP32_TO_CAP16(MDIX); + CAP32_TO_CAP16(MDIAUTO); + CAP32_TO_CAP16(FEC_RS); + CAP32_TO_CAP16(FEC_BASER_RS); + + #undef CAP32_TO_CAP16 + + return caps16; +} + +/* Translate Firmware Port Capabilities Pause specification to Common Code */ +static inline enum cc_pause fwcap_to_cc_pause(fw_port_cap32_t fw_pause) +{ + enum cc_pause cc_pause = 0; + + if (fw_pause & FW_PORT_CAP32_FC_RX) + cc_pause |= PAUSE_RX; + if (fw_pause & FW_PORT_CAP32_FC_TX) + cc_pause |= PAUSE_TX; + + return cc_pause; +} + +/* Translate Common Code Pause specification into Firmware Port Capabilities */ +static inline fw_port_cap32_t cc_to_fwcap_pause(enum cc_pause cc_pause) +{ + fw_port_cap32_t fw_pause = 0; + + if (cc_pause & PAUSE_RX) + fw_pause |= FW_PORT_CAP32_FC_RX; + if (cc_pause & PAUSE_TX) + fw_pause |= FW_PORT_CAP32_FC_TX; + + return fw_pause; +} + +/* Translate Firmware Forward Error Correction specification to Common Code */ +static inline enum cc_fec fwcap_to_cc_fec(fw_port_cap32_t fw_fec) +{ + enum cc_fec cc_fec = 0; + + if (fw_fec & FW_PORT_CAP32_FEC_RS) + cc_fec |= FEC_RS; + if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS) + cc_fec |= FEC_BASER_RS; + + return cc_fec; +} + +/* Translate Common Code Forward Error Correction specification to Firmware */ +static inline fw_port_cap32_t cc_to_fwcap_fec(enum cc_fec cc_fec) +{ + fw_port_cap32_t fw_fec = 0; + + if (cc_fec & FEC_RS) + fw_fec |= FW_PORT_CAP32_FEC_RS; + if (cc_fec & FEC_BASER_RS) + fw_fec |= FW_PORT_CAP32_FEC_BASER_RS; + + return fw_fec; +} /** * t4_link_l1cfg - apply link configuration to MAC/PHY - * @phy: the PHY to setup - * @mac: the MAC to setup - * @lc: the requested link configuration + * @adapter: the adapter + * @mbox: the Firmware Mailbox to use + * @port: the Port ID + * @lc: the Port's Link Configuration * * Set up a port's MAC and PHY according to a desired link configuration. * - If the PHY can auto-negotiate first decide what to advertise, then @@ -3703,47 +3980,64 @@ void t4_ulprx_read_la(struct adapter *adap, u32 *la_buf) * - If auto-negotiation is off set the MAC to the proper speed/duplex/FC, * otherwise do it later based on the outcome of auto-negotiation. */ -int t4_link_l1cfg(struct adapter *adap, unsigned int mbox, unsigned int port, - struct link_config *lc) +int t4_link_l1cfg(struct adapter *adapter, unsigned int mbox, + unsigned int port, struct link_config *lc) { - struct fw_port_cmd c; - unsigned int mdi = FW_PORT_CAP_MDI_V(FW_PORT_CAP_MDI_AUTO); - unsigned int fc = 0, fec = 0, fw_fec = 0; + unsigned int fw_caps = adapter->params.fw_caps_support; + struct fw_port_cmd cmd; + unsigned int fw_mdi = FW_PORT_CAP32_MDI_V(FW_PORT_CAP32_MDI_AUTO); + fw_port_cap32_t fw_fc, cc_fec, fw_fec, rcap; lc->link_ok = 0; - if (lc->requested_fc & PAUSE_RX) - fc |= FW_PORT_CAP_FC_RX; - if (lc->requested_fc & PAUSE_TX) - fc |= FW_PORT_CAP_FC_TX; - - fec = lc->requested_fec & FEC_AUTO ? lc->auto_fec : lc->requested_fec; - - if (fec & FEC_RS) - fw_fec |= FW_PORT_CAP_FEC_RS; - if (fec & FEC_BASER_RS) - fw_fec |= FW_PORT_CAP_FEC_BASER_RS; - memset(&c, 0, sizeof(c)); - c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | - FW_CMD_REQUEST_F | FW_CMD_EXEC_F | - FW_PORT_CMD_PORTID_V(port)); - c.action_to_len16 = - cpu_to_be32(FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_L1_CFG) | - FW_LEN16(c)); + /* Convert driver coding of Pause Frame Flow Control settings into the + * Firmware's API. + */ + fw_fc = cc_to_fwcap_pause(lc->requested_fc); + + /* Convert Common Code Forward Error Control settings into the + * Firmware's API. If the current Requested FEC has "Automatic" + * (IEEE 802.3) specified, then we use whatever the Firmware + * sent us as part of it's IEEE 802.3-based interpratation of + * the Transceiver Module EPROM FEC parameters. Otherwise we + * use whatever is in the current Requested FEC settings. + */ + if (lc->requested_fec & FEC_AUTO) + cc_fec = fwcap_to_cc_fec(lc->def_acaps); + else + cc_fec = lc->requested_fec; + fw_fec = cc_to_fwcap_fec(cc_fec); - if (!(lc->supported & FW_PORT_CAP_ANEG)) { - c.u.l1cfg.rcap = cpu_to_be32((lc->supported & ADVERT_MASK) | - fc | fw_fec); - lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); + /* Figure out what our Requested Port Capabilities are going to be. + */ + if (!(lc->pcaps & FW_PORT_CAP32_ANEG)) { + rcap = (lc->pcaps & ADVERT_MASK) | fw_fc | fw_fec; + lc->fc = lc->requested_fc & ~PAUSE_AUTONEG; + lc->fec = cc_fec; } else if (lc->autoneg == AUTONEG_DISABLE) { - c.u.l1cfg.rcap = cpu_to_be32(lc->requested_speed | fc | - fw_fec | mdi); - lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); - } else - c.u.l1cfg.rcap = cpu_to_be32(lc->advertising | fc | - fw_fec | mdi); + rcap = lc->speed_caps | fw_fc | fw_fec | fw_mdi; + lc->fc = lc->requested_fc & ~PAUSE_AUTONEG; + lc->fec = cc_fec; + } else { + rcap = lc->acaps | fw_fc | fw_fec | fw_mdi; + } - return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); + /* And send that on to the Firmware ... + */ + memset(&cmd, 0, sizeof(cmd)); + cmd.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | + FW_CMD_REQUEST_F | FW_CMD_EXEC_F | + FW_PORT_CMD_PORTID_V(port)); + cmd.action_to_len16 = + cpu_to_be32(FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16 + ? FW_PORT_ACTION_L1_CFG + : FW_PORT_ACTION_L1_CFG32) | + FW_LEN16(cmd)); + if (fw_caps == FW_CAPS16) + cmd.u.l1cfg.rcap = cpu_to_be32(fwcaps32_to_caps16(rcap)); + else + cmd.u.l1cfg32.rcap32 = cpu_to_be32(rcap); + return t4_wr_mbox(adapter, mbox, &cmd, sizeof(cmd), NULL); } /** @@ -3765,7 +4059,7 @@ int t4_restart_aneg(struct adapter *adap, unsigned int mbox, unsigned int port) c.action_to_len16 = cpu_to_be32(FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_L1_CFG) | FW_LEN16(c)); - c.u.l1cfg.rcap = cpu_to_be32(FW_PORT_CAP_ANEG); + c.u.l1cfg.rcap = cpu_to_be32(FW_PORT_CAP32_ANEG); return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); } @@ -4254,6 +4548,18 @@ static void mps_intr_handler(struct adapter *adapter) { FRMERR_F, "MPS Tx framing error", -1, 1 }, { 0 } }; + static const struct intr_info t6_mps_tx_intr_info[] = { + { TPFIFO_V(TPFIFO_M), "MPS Tx TP FIFO parity error", -1, 1 }, + { NCSIFIFO_F, "MPS Tx NC-SI FIFO parity error", -1, 1 }, + { TXDATAFIFO_V(TXDATAFIFO_M), "MPS Tx data FIFO parity error", + -1, 1 }, + { TXDESCFIFO_V(TXDESCFIFO_M), "MPS Tx desc FIFO parity error", + -1, 1 }, + /* MPS Tx Bubble is normal for T6 */ + { SECNTERR_F, "MPS Tx SOP/EOP error", -1, 1 }, + { FRMERR_F, "MPS Tx framing error", -1, 1 }, + { 0 } + }; static const struct intr_info mps_trc_intr_info[] = { { FILTMEM_V(FILTMEM_M), "MPS TRC filter parity error", -1, 1 }, { PKTFIFO_V(PKTFIFO_M), "MPS TRC packet FIFO parity error", @@ -4285,7 +4591,9 @@ static void mps_intr_handler(struct adapter *adapter) fat = t4_handle_intr_status(adapter, MPS_RX_PERR_INT_CAUSE_A, mps_rx_intr_info) + t4_handle_intr_status(adapter, MPS_TX_INT_CAUSE_A, - mps_tx_intr_info) + + is_t6(adapter->params.chip) + ? t6_mps_tx_intr_info + : mps_tx_intr_info) + t4_handle_intr_status(adapter, MPS_TRC_INT_CAUSE_A, mps_trc_intr_info) + t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_SRAM_A, @@ -5693,10 +6001,8 @@ void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p) p->tx_ppp7 = GET_STAT(TX_PORT_PPP7); if (CHELSIO_CHIP_VERSION(adap->params.chip) >= CHELSIO_T5) { - if (stat_ctl & COUNTPAUSESTATTX_F) { - p->tx_frames -= p->tx_pause; - p->tx_octets -= p->tx_pause * 64; - } + if (stat_ctl & COUNTPAUSESTATTX_F) + p->tx_frames_64 -= p->tx_pause; if (stat_ctl & COUNTPAUSEMCTX_F) p->tx_mcast_frames -= p->tx_pause; } @@ -5729,10 +6035,8 @@ void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p) p->rx_ppp7 = GET_STAT(RX_PORT_PPP7); if (CHELSIO_CHIP_VERSION(adap->params.chip) >= CHELSIO_T5) { - if (stat_ctl & COUNTPAUSESTATRX_F) { - p->rx_frames -= p->rx_pause; - p->rx_octets -= p->rx_pause * 64; - } + if (stat_ctl & COUNTPAUSESTATRX_F) + p->rx_frames_64 -= p->rx_pause; if (stat_ctl & COUNTPAUSEMCRX_F) p->rx_mcast_frames -= p->rx_pause; } @@ -6449,6 +6753,17 @@ int t4_fw_upgrade(struct adapter *adap, unsigned int mbox, goto out; /* + * If there was a Firmware Configuration File stored in FLASH, + * there's a good chance that it won't be compatible with the new + * Firmware. In order to prevent difficult to diagnose adapter + * initialization issues, we clear out the Firmware Configuration File + * portion of the FLASH . The user will need to re-FLASH a new + * Firmware Configuration File which is compatible with the new + * Firmware if that's desired. + */ + (void)t4_load_cfg(adap, NULL, 0); + + /* * Older versions of the firmware don't understand the new * PCIE_FW.HALT flag and so won't know to perform a RESET when they * restart. So for newly loaded older firmware we'll have to do the @@ -7471,6 +7786,98 @@ static const char *t4_link_down_rc_str(unsigned char link_down_rc) } /** + * Return the highest speed set in the port capabilities, in Mb/s. + */ +static unsigned int fwcap_to_speed(fw_port_cap32_t caps) +{ + #define TEST_SPEED_RETURN(__caps_speed, __speed) \ + do { \ + if (caps & FW_PORT_CAP32_SPEED_##__caps_speed) \ + return __speed; \ + } while (0) + + TEST_SPEED_RETURN(400G, 400000); + TEST_SPEED_RETURN(200G, 200000); + TEST_SPEED_RETURN(100G, 100000); + TEST_SPEED_RETURN(50G, 50000); + TEST_SPEED_RETURN(40G, 40000); + TEST_SPEED_RETURN(25G, 25000); + TEST_SPEED_RETURN(10G, 10000); + TEST_SPEED_RETURN(1G, 1000); + TEST_SPEED_RETURN(100M, 100); + + #undef TEST_SPEED_RETURN + + return 0; +} + +/** + * fwcap_to_fwspeed - return highest speed in Port Capabilities + * @acaps: advertised Port Capabilities + * + * Get the highest speed for the port from the advertised Port + * Capabilities. It will be either the highest speed from the list of + * speeds or whatever user has set using ethtool. + */ +static fw_port_cap32_t fwcap_to_fwspeed(fw_port_cap32_t acaps) +{ + #define TEST_SPEED_RETURN(__caps_speed) \ + do { \ + if (acaps & FW_PORT_CAP32_SPEED_##__caps_speed) \ + return FW_PORT_CAP32_SPEED_##__caps_speed; \ + } while (0) + + TEST_SPEED_RETURN(400G); + TEST_SPEED_RETURN(200G); + TEST_SPEED_RETURN(100G); + TEST_SPEED_RETURN(50G); + TEST_SPEED_RETURN(40G); + TEST_SPEED_RETURN(25G); + TEST_SPEED_RETURN(10G); + TEST_SPEED_RETURN(1G); + TEST_SPEED_RETURN(100M); + + #undef TEST_SPEED_RETURN + + return 0; +} + +/** + * lstatus_to_fwcap - translate old lstatus to 32-bit Port Capabilities + * @lstatus: old FW_PORT_ACTION_GET_PORT_INFO lstatus value + * + * Translates old FW_PORT_ACTION_GET_PORT_INFO lstatus field into new + * 32-bit Port Capabilities value. + */ +static fw_port_cap32_t lstatus_to_fwcap(u32 lstatus) +{ + fw_port_cap32_t linkattr = 0; + + /* Unfortunately the format of the Link Status in the old + * 16-bit Port Information message isn't the same as the + * 16-bit Port Capabilities bitfield used everywhere else ... + */ + if (lstatus & FW_PORT_CMD_RXPAUSE_F) + linkattr |= FW_PORT_CAP32_FC_RX; + if (lstatus & FW_PORT_CMD_TXPAUSE_F) + linkattr |= FW_PORT_CAP32_FC_TX; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M)) + linkattr |= FW_PORT_CAP32_SPEED_100M; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G)) + linkattr |= FW_PORT_CAP32_SPEED_1G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G)) + linkattr |= FW_PORT_CAP32_SPEED_10G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_25G)) + linkattr |= FW_PORT_CAP32_SPEED_25G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G)) + linkattr |= FW_PORT_CAP32_SPEED_40G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100G)) + linkattr |= FW_PORT_CAP32_SPEED_100G; + + return linkattr; +} + +/** * t4_handle_get_port_info - process a FW reply message * @pi: the port info * @rpl: start of the FW message @@ -7479,56 +7886,123 @@ static const char *t4_link_down_rc_str(unsigned char link_down_rc) */ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) { - const struct fw_port_cmd *p = (const void *)rpl; - struct adapter *adap = pi->adapter; - - /* link/module state change message */ - int speed = 0, fc = 0; - struct link_config *lc; - u32 stat = be32_to_cpu(p->u.info.lstatus_to_modtype); - int link_ok = (stat & FW_PORT_CMD_LSTATUS_F) != 0; - u32 mod = FW_PORT_CMD_MODTYPE_G(stat); - - if (stat & FW_PORT_CMD_RXPAUSE_F) - fc |= PAUSE_RX; - if (stat & FW_PORT_CMD_TXPAUSE_F) - fc |= PAUSE_TX; - if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M)) - speed = 100; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G)) - speed = 1000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G)) - speed = 10000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_25G)) - speed = 25000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G)) - speed = 40000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100G)) - speed = 100000; - - lc = &pi->link_cfg; - - if (mod != pi->mod_type) { - pi->mod_type = mod; - t4_os_portmod_changed(adap, pi->port_id); + const struct fw_port_cmd *cmd = (const void *)rpl; + int action = FW_PORT_CMD_ACTION_G(be32_to_cpu(cmd->action_to_len16)); + struct adapter *adapter = pi->adapter; + struct link_config *lc = &pi->link_cfg; + int link_ok, linkdnrc; + enum fw_port_type port_type; + enum fw_port_module_type mod_type; + unsigned int speed, fc, fec; + fw_port_cap32_t pcaps, acaps, lpacaps, linkattr; + + /* Extract the various fields from the Port Information message. + */ + switch (action) { + case FW_PORT_ACTION_GET_PORT_INFO: { + u32 lstatus = be32_to_cpu(cmd->u.info.lstatus_to_modtype); + + link_ok = (lstatus & FW_PORT_CMD_LSTATUS_F) != 0; + linkdnrc = FW_PORT_CMD_LINKDNRC_G(lstatus); + port_type = FW_PORT_CMD_PTYPE_G(lstatus); + mod_type = FW_PORT_CMD_MODTYPE_G(lstatus); + pcaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.pcap)); + acaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.acap)); + lpacaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.lpacap)); + linkattr = lstatus_to_fwcap(lstatus); + break; } + + case FW_PORT_ACTION_GET_PORT_INFO32: { + u32 lstatus32; + + lstatus32 = be32_to_cpu(cmd->u.info32.lstatus32_to_cbllen32); + link_ok = (lstatus32 & FW_PORT_CMD_LSTATUS32_F) != 0; + linkdnrc = FW_PORT_CMD_LINKDNRC32_G(lstatus32); + port_type = FW_PORT_CMD_PORTTYPE32_G(lstatus32); + mod_type = FW_PORT_CMD_MODTYPE32_G(lstatus32); + pcaps = be32_to_cpu(cmd->u.info32.pcaps32); + acaps = be32_to_cpu(cmd->u.info32.acaps32); + lpacaps = be32_to_cpu(cmd->u.info32.lpacaps32); + linkattr = be32_to_cpu(cmd->u.info32.linkattr32); + break; + } + + default: + dev_err(adapter->pdev_dev, "Handle Port Information: Bad Command/Action %#x\n", + be32_to_cpu(cmd->action_to_len16)); + return; + } + + fec = fwcap_to_cc_fec(acaps); + fc = fwcap_to_cc_pause(linkattr); + speed = fwcap_to_speed(linkattr); + + if (mod_type != pi->mod_type) { + /* With the newer SFP28 and QSFP28 Transceiver Module Types, + * various fundamental Port Capabilities which used to be + * immutable can now change radically. We can now have + * Speeds, Auto-Negotiation, Forward Error Correction, etc. + * all change based on what Transceiver Module is inserted. + * So we need to record the Physical "Port" Capabilities on + * every Transceiver Module change. + */ + lc->pcaps = pcaps; + + /* When a new Transceiver Module is inserted, the Firmware + * will examine its i2c EPROM to determine its type and + * general operating parameters including things like Forward + * Error Control, etc. Various IEEE 802.3 standards dictate + * how to interpret these i2c values to determine default + * "sutomatic" settings. We record these for future use when + * the user explicitly requests these standards-based values. + */ + lc->def_acaps = acaps; + + /* Some versions of the early T6 Firmware "cheated" when + * handling different Transceiver Modules by changing the + * underlaying Port Type reported to the Host Drivers. As + * such we need to capture whatever Port Type the Firmware + * sends us and record it in case it's different from what we + * were told earlier. Unfortunately, since Firmware is + * forever, we'll need to keep this code here forever, but in + * later T6 Firmware it should just be an assignment of the + * same value already recorded. + */ + pi->port_type = port_type; + + pi->mod_type = mod_type; + t4_os_portmod_changed(adapter, pi->port_id); + } + if (link_ok != lc->link_ok || speed != lc->speed || - fc != lc->fc) { /* something changed */ + fc != lc->fc || fec != lc->fec) { /* something changed */ if (!link_ok && lc->link_ok) { - unsigned char rc = FW_PORT_CMD_LINKDNRC_G(stat); - - lc->link_down_rc = rc; - dev_warn(adap->pdev_dev, - "Port %d link down, reason: %s\n", - pi->port_id, t4_link_down_rc_str(rc)); + lc->link_down_rc = linkdnrc; + dev_warn(adapter->pdev_dev, "Port %d link down, reason: %s\n", + pi->tx_chan, t4_link_down_rc_str(linkdnrc)); } lc->link_ok = link_ok; lc->speed = speed; lc->fc = fc; - lc->supported = be16_to_cpu(p->u.info.pcap); - lc->lp_advertising = be16_to_cpu(p->u.info.lpacap); + lc->fec = fec; - t4_os_link_changed(adap, pi->port_id, link_ok); + lc->lpacaps = lpacaps; + lc->acaps = acaps & ADVERT_MASK; + + if (lc->acaps & FW_PORT_CAP32_ANEG) { + lc->autoneg = AUTONEG_ENABLE; + } else { + /* When Autoneg is disabled, user needs to set + * single speed. + * Similar to cxgb4_ethtool.c: set_link_ksettings + */ + lc->acaps = 0; + lc->speed_caps = fwcap_to_fwspeed(acaps); + lc->autoneg = AUTONEG_DISABLE; + } + + t4_os_link_changed(adapter, pi->port_id, link_ok); } } @@ -7542,15 +8016,18 @@ void t4_handle_get_port_info(struct port_info *pi, const __be64 *rpl) */ int t4_update_port_info(struct port_info *pi) { + unsigned int fw_caps = pi->adapter->params.fw_caps_support; struct fw_port_cmd port_cmd; int ret; memset(&port_cmd, 0, sizeof(port_cmd)); port_cmd.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | FW_CMD_REQUEST_F | FW_CMD_READ_F | - FW_PORT_CMD_PORTID_V(pi->port_id)); + FW_PORT_CMD_PORTID_V(pi->tx_chan)); port_cmd.action_to_len16 = cpu_to_be32( - FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_GET_PORT_INFO) | + FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16 + ? FW_PORT_ACTION_GET_PORT_INFO + : FW_PORT_ACTION_GET_PORT_INFO32) | FW_LEN16(port_cmd)); ret = t4_wr_mbox(pi->adapter, pi->adapter->mbox, &port_cmd, sizeof(port_cmd), &port_cmd); @@ -7562,6 +8039,65 @@ int t4_update_port_info(struct port_info *pi) } /** + * t4_get_link_params - retrieve basic link parameters for given port + * @pi: the port + * @link_okp: value return pointer for link up/down + * @speedp: value return pointer for speed (Mb/s) + * @mtup: value return pointer for mtu + * + * Retrieves basic link parameters for a port: link up/down, speed (Mb/s), + * and MTU for a specified port. A negative error is returned on + * failure; 0 on success. + */ +int t4_get_link_params(struct port_info *pi, unsigned int *link_okp, + unsigned int *speedp, unsigned int *mtup) +{ + unsigned int fw_caps = pi->adapter->params.fw_caps_support; + struct fw_port_cmd port_cmd; + unsigned int action, link_ok, speed, mtu; + fw_port_cap32_t linkattr; + int ret; + + memset(&port_cmd, 0, sizeof(port_cmd)); + port_cmd.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | + FW_CMD_REQUEST_F | FW_CMD_READ_F | + FW_PORT_CMD_PORTID_V(pi->tx_chan)); + action = (fw_caps == FW_CAPS16 + ? FW_PORT_ACTION_GET_PORT_INFO + : FW_PORT_ACTION_GET_PORT_INFO32); + port_cmd.action_to_len16 = cpu_to_be32( + FW_PORT_CMD_ACTION_V(action) | + FW_LEN16(port_cmd)); + ret = t4_wr_mbox(pi->adapter, pi->adapter->mbox, + &port_cmd, sizeof(port_cmd), &port_cmd); + if (ret) + return ret; + + if (action == FW_PORT_ACTION_GET_PORT_INFO) { + u32 lstatus = be32_to_cpu(port_cmd.u.info.lstatus_to_modtype); + + link_ok = !!(lstatus & FW_PORT_CMD_LSTATUS_F); + linkattr = lstatus_to_fwcap(lstatus); + mtu = be16_to_cpu(port_cmd.u.info.mtu); + } else { + u32 lstatus32 = + be32_to_cpu(port_cmd.u.info32.lstatus32_to_cbllen32); + + link_ok = !!(lstatus32 & FW_PORT_CMD_LSTATUS32_F); + linkattr = be32_to_cpu(port_cmd.u.info32.linkattr32); + mtu = FW_PORT_CMD_MTU32_G( + be32_to_cpu(port_cmd.u.info32.auxlinfo32_mtu32)); + } + speed = fwcap_to_speed(linkattr); + + *link_okp = link_ok; + *speedp = fwcap_to_speed(linkattr); + *mtup = mtu; + + return 0; +} + +/** * t4_handle_fw_rpl - process a FW reply message * @adap: the adapter * @rpl: start of the FW message @@ -7581,7 +8117,9 @@ int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl) unsigned int action = FW_PORT_CMD_ACTION_G(be32_to_cpu(p->action_to_len16)); - if (opcode == FW_PORT_CMD && action == FW_PORT_ACTION_GET_PORT_INFO) { + if (opcode == FW_PORT_CMD && + (action == FW_PORT_ACTION_GET_PORT_INFO || + action == FW_PORT_ACTION_GET_PORT_INFO32)) { int i; int chan = FW_PORT_CMD_PORTID_G(be32_to_cpu(p->op_to_portid)); struct port_info *pi = NULL; @@ -7594,7 +8132,8 @@ int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl) t4_handle_get_port_info(pi, rpl); } else { - dev_warn(adap->pdev_dev, "Unknown firmware reply %d\n", opcode); + dev_warn(adap->pdev_dev, "Unknown firmware reply %d\n", + opcode); return -EINVAL; } return 0; @@ -7613,38 +8152,35 @@ static void get_pci_mode(struct adapter *adapter, struct pci_params *p) /** * init_link_config - initialize a link's SW state - * @lc: structure holding the link state - * @caps: link capabilities + * @lc: pointer to structure holding the link state + * @pcaps: link Port Capabilities + * @acaps: link current Advertised Port Capabilities * * Initializes the SW state maintained for each link, including the link's * capabilities and default speed/flow-control/autonegotiation settings. */ -static void init_link_config(struct link_config *lc, unsigned int pcaps, - unsigned int acaps) +static void init_link_config(struct link_config *lc, fw_port_cap32_t pcaps, + fw_port_cap32_t acaps) { - lc->supported = pcaps; - lc->lp_advertising = 0; - lc->requested_speed = 0; + lc->pcaps = pcaps; + lc->def_acaps = acaps; + lc->lpacaps = 0; + lc->speed_caps = 0; lc->speed = 0; lc->requested_fc = lc->fc = PAUSE_RX | PAUSE_TX; - lc->auto_fec = 0; /* For Forward Error Control, we default to whatever the Firmware * tells us the Link is currently advertising. */ - if (acaps & FW_PORT_CAP_FEC_RS) - lc->auto_fec |= FEC_RS; - if (acaps & FW_PORT_CAP_FEC_BASER_RS) - lc->auto_fec |= FEC_BASER_RS; lc->requested_fec = FEC_AUTO; - lc->fec = lc->auto_fec; + lc->fec = fwcap_to_cc_fec(lc->def_acaps); - if (lc->supported & FW_PORT_CAP_ANEG) { - lc->advertising = lc->supported & ADVERT_MASK; + if (lc->pcaps & FW_PORT_CAP32_ANEG) { + lc->acaps = lc->pcaps & ADVERT_MASK; lc->autoneg = AUTONEG_ENABLE; lc->requested_fc |= PAUSE_AUTONEG; } else { - lc->advertising = 0; + lc->acaps = 0; lc->autoneg = AUTONEG_DISABLE; } } @@ -8169,7 +8705,7 @@ int t4_init_rss_mode(struct adapter *adap, int mbox) } /** - * t4_init_portinfo - allocate a virtual interface amd initialize port_info + * t4_init_portinfo - allocate a virtual interface and initialize port_info * @pi: the port_info * @mbox: mailbox to use for the FW command * @port: physical port associated with the VI @@ -8185,21 +8721,67 @@ int t4_init_rss_mode(struct adapter *adap, int mbox) int t4_init_portinfo(struct port_info *pi, int mbox, int port, int pf, int vf, u8 mac[]) { - int ret; - struct fw_port_cmd c; + struct adapter *adapter = pi->adapter; + unsigned int fw_caps = adapter->params.fw_caps_support; + struct fw_port_cmd cmd; unsigned int rss_size; + enum fw_port_type port_type; + int mdio_addr; + fw_port_cap32_t pcaps, acaps; + int ret; - memset(&c, 0, sizeof(c)); - c.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | - FW_CMD_REQUEST_F | FW_CMD_READ_F | - FW_PORT_CMD_PORTID_V(port)); - c.action_to_len16 = cpu_to_be32( - FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_GET_PORT_INFO) | - FW_LEN16(c)); - ret = t4_wr_mbox(pi->adapter, mbox, &c, sizeof(c), &c); + /* If we haven't yet determined whether we're talking to Firmware + * which knows the new 32-bit Port Capabilities, it's time to find + * out now. This will also tell new Firmware to send us Port Status + * Updates using the new 32-bit Port Capabilities version of the + * Port Information message. + */ + if (fw_caps == FW_CAPS_UNKNOWN) { + u32 param, val; + + param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_PFVF) | + FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_PFVF_PORT_CAPS32)); + val = 1; + ret = t4_set_params(adapter, mbox, pf, vf, 1, ¶m, &val); + fw_caps = (ret == 0 ? FW_CAPS32 : FW_CAPS16); + adapter->params.fw_caps_support = fw_caps; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | + FW_CMD_REQUEST_F | FW_CMD_READ_F | + FW_PORT_CMD_PORTID_V(port)); + cmd.action_to_len16 = cpu_to_be32( + FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16 + ? FW_PORT_ACTION_GET_PORT_INFO + : FW_PORT_ACTION_GET_PORT_INFO32) | + FW_LEN16(cmd)); + ret = t4_wr_mbox(pi->adapter, mbox, &cmd, sizeof(cmd), &cmd); if (ret) return ret; + /* Extract the various fields from the Port Information message. + */ + if (fw_caps == FW_CAPS16) { + u32 lstatus = be32_to_cpu(cmd.u.info.lstatus_to_modtype); + + port_type = FW_PORT_CMD_PTYPE_G(lstatus); + mdio_addr = ((lstatus & FW_PORT_CMD_MDIOCAP_F) + ? FW_PORT_CMD_MDIOADDR_G(lstatus) + : -1); + pcaps = fwcaps16_to_caps32(be16_to_cpu(cmd.u.info.pcap)); + acaps = fwcaps16_to_caps32(be16_to_cpu(cmd.u.info.acap)); + } else { + u32 lstatus32 = be32_to_cpu(cmd.u.info32.lstatus32_to_cbllen32); + + port_type = FW_PORT_CMD_PORTTYPE32_G(lstatus32); + mdio_addr = ((lstatus32 & FW_PORT_CMD_MDIOCAP32_F) + ? FW_PORT_CMD_MDIOADDR32_G(lstatus32) + : -1); + pcaps = be32_to_cpu(cmd.u.info32.pcaps32); + acaps = be32_to_cpu(cmd.u.info32.acaps32); + } + ret = t4_alloc_vi(pi->adapter, mbox, port, pf, vf, 1, mac, &rss_size); if (ret < 0) return ret; @@ -8209,14 +8791,11 @@ int t4_init_portinfo(struct port_info *pi, int mbox, pi->lport = port; pi->rss_size = rss_size; - ret = be32_to_cpu(c.u.info.lstatus_to_modtype); - pi->mdio_addr = (ret & FW_PORT_CMD_MDIOCAP_F) ? - FW_PORT_CMD_MDIOADDR_G(ret) : -1; - pi->port_type = FW_PORT_CMD_PTYPE_G(ret); + pi->port_type = port_type; + pi->mdio_addr = mdio_addr; pi->mod_type = FW_PORT_MOD_TYPE_NA; - init_link_config(&pi->link_cfg, be16_to_cpu(c.u.info.pcap), - be16_to_cpu(c.u.info.acap)); + init_link_config(&pi->link_cfg, pcaps, acaps); return 0; } @@ -8664,6 +9243,65 @@ void t4_idma_monitor(struct adapter *adapter, } /** + * t4_load_cfg - download config file + * @adap: the adapter + * @cfg_data: the cfg text file to write + * @size: text file size + * + * Write the supplied config text file to the card's serial flash. + */ +int t4_load_cfg(struct adapter *adap, const u8 *cfg_data, unsigned int size) +{ + int ret, i, n, cfg_addr; + unsigned int addr; + unsigned int flash_cfg_start_sec; + unsigned int sf_sec_size = adap->params.sf_size / adap->params.sf_nsec; + + cfg_addr = t4_flash_cfg_addr(adap); + if (cfg_addr < 0) + return cfg_addr; + + addr = cfg_addr; + flash_cfg_start_sec = addr / SF_SEC_SIZE; + + if (size > FLASH_CFG_MAX_SIZE) { + dev_err(adap->pdev_dev, "cfg file too large, max is %u bytes\n", + FLASH_CFG_MAX_SIZE); + return -EFBIG; + } + + i = DIV_ROUND_UP(FLASH_CFG_MAX_SIZE, /* # of sectors spanned */ + sf_sec_size); + ret = t4_flash_erase_sectors(adap, flash_cfg_start_sec, + flash_cfg_start_sec + i - 1); + /* If size == 0 then we're simply erasing the FLASH sectors associated + * with the on-adapter Firmware Configuration File. + */ + if (ret || size == 0) + goto out; + + /* this will write to the flash up to SF_PAGE_SIZE at a time */ + for (i = 0; i < size; i += SF_PAGE_SIZE) { + if ((size - i) < SF_PAGE_SIZE) + n = size - i; + else + n = SF_PAGE_SIZE; + ret = t4_write_flash(adap, addr, n, cfg_data); + if (ret) + goto out; + + addr += SF_PAGE_SIZE; + cfg_data += SF_PAGE_SIZE; + } + +out: + if (ret) + dev_err(adap->pdev_dev, "config file %s failed %d\n", + (size == 0 ? "clear" : "download"), ret); + return ret; +} + +/** * t4_set_vf_mac - Set MAC address for the specified VF * @adapter: The adapter * @vf: one of the VFs instantiated by the specified PF diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h index 0ebed64d62d3..ca2756dcefc5 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h @@ -1124,6 +1124,8 @@ enum fw_params_param_dev { FW_PARAMS_PARAM_DEV_MAXIRD_ADAPTER = 0x14, /* max supported adap IRD */ FW_PARAMS_PARAM_DEV_ULPTX_MEMWRITE_DSGL = 0x17, FW_PARAMS_PARAM_DEV_FWCACHE = 0x18, + FW_PARAMS_PARAM_DEV_SCFGREV = 0x1A, + FW_PARAMS_PARAM_DEV_VPDREV = 0x1B, FW_PARAMS_PARAM_DEV_RI_FR_NSMR_TPTE_WR = 0x1C, FW_PARAMS_PARAM_DEV_MPSBGMAP = 0x1E, }; @@ -1171,7 +1173,8 @@ enum fw_params_param_pfvf { FW_PARAMS_PARAM_PFVF_ACTIVE_FILTER_END = 0x2E, FW_PARAMS_PARAM_PFVF_ETHOFLD_END = 0x30, FW_PARAMS_PARAM_PFVF_CPLFW4MSG_ENCAP = 0x31, - FW_PARAMS_PARAM_PFVF_NCRYPTO_LOOKASIDE = 0x32 + FW_PARAMS_PARAM_PFVF_NCRYPTO_LOOKASIDE = 0x32, + FW_PARAMS_PARAM_PFVF_PORT_CAPS32 = 0x3A, }; /* @@ -2254,6 +2257,7 @@ struct fw_acl_vlan_cmd { #define FW_ACL_VLAN_CMD_FM_S 6 #define FW_ACL_VLAN_CMD_FM_V(x) ((x) << FW_ACL_VLAN_CMD_FM_S) +/* old 16-bit port capabilities bitmap (fw_port_cap16_t) */ enum fw_port_cap { FW_PORT_CAP_SPEED_100M = 0x0001, FW_PORT_CAP_SPEED_1G = 0x0002, @@ -2289,6 +2293,84 @@ enum fw_port_mdi { #define FW_PORT_CAP_MDI_S 9 #define FW_PORT_CAP_MDI_V(x) ((x) << FW_PORT_CAP_MDI_S) +/* new 32-bit port capabilities bitmap (fw_port_cap32_t) */ +#define FW_PORT_CAP32_SPEED_100M 0x00000001UL +#define FW_PORT_CAP32_SPEED_1G 0x00000002UL +#define FW_PORT_CAP32_SPEED_10G 0x00000004UL +#define FW_PORT_CAP32_SPEED_25G 0x00000008UL +#define FW_PORT_CAP32_SPEED_40G 0x00000010UL +#define FW_PORT_CAP32_SPEED_50G 0x00000020UL +#define FW_PORT_CAP32_SPEED_100G 0x00000040UL +#define FW_PORT_CAP32_SPEED_200G 0x00000080UL +#define FW_PORT_CAP32_SPEED_400G 0x00000100UL +#define FW_PORT_CAP32_SPEED_RESERVED1 0x00000200UL +#define FW_PORT_CAP32_SPEED_RESERVED2 0x00000400UL +#define FW_PORT_CAP32_SPEED_RESERVED3 0x00000800UL +#define FW_PORT_CAP32_RESERVED1 0x0000f000UL +#define FW_PORT_CAP32_FC_RX 0x00010000UL +#define FW_PORT_CAP32_FC_TX 0x00020000UL +#define FW_PORT_CAP32_802_3_PAUSE 0x00040000UL +#define FW_PORT_CAP32_802_3_ASM_DIR 0x00080000UL +#define FW_PORT_CAP32_ANEG 0x00100000UL +#define FW_PORT_CAP32_MDIX 0x00200000UL +#define FW_PORT_CAP32_MDIAUTO 0x00400000UL +#define FW_PORT_CAP32_FEC_RS 0x00800000UL +#define FW_PORT_CAP32_FEC_BASER_RS 0x01000000UL +#define FW_PORT_CAP32_FEC_RESERVED1 0x02000000UL +#define FW_PORT_CAP32_FEC_RESERVED2 0x04000000UL +#define FW_PORT_CAP32_FEC_RESERVED3 0x08000000UL +#define FW_PORT_CAP32_RESERVED2 0xf0000000UL + +#define FW_PORT_CAP32_SPEED_S 0 +#define FW_PORT_CAP32_SPEED_M 0xfff +#define FW_PORT_CAP32_SPEED_V(x) ((x) << FW_PORT_CAP32_SPEED_S) +#define FW_PORT_CAP32_SPEED_G(x) \ + (((x) >> FW_PORT_CAP32_SPEED_S) & FW_PORT_CAP32_SPEED_M) + +#define FW_PORT_CAP32_FC_S 16 +#define FW_PORT_CAP32_FC_M 0x3 +#define FW_PORT_CAP32_FC_V(x) ((x) << FW_PORT_CAP32_FC_S) +#define FW_PORT_CAP32_FC_G(x) \ + (((x) >> FW_PORT_CAP32_FC_S) & FW_PORT_CAP32_FC_M) + +#define FW_PORT_CAP32_802_3_S 18 +#define FW_PORT_CAP32_802_3_M 0x3 +#define FW_PORT_CAP32_802_3_V(x) ((x) << FW_PORT_CAP32_802_3_S) +#define FW_PORT_CAP32_802_3_G(x) \ + (((x) >> FW_PORT_CAP32_802_3_S) & FW_PORT_CAP32_802_3_M) + +#define FW_PORT_CAP32_ANEG_S 20 +#define FW_PORT_CAP32_ANEG_M 0x1 +#define FW_PORT_CAP32_ANEG_V(x) ((x) << FW_PORT_CAP32_ANEG_S) +#define FW_PORT_CAP32_ANEG_G(x) \ + (((x) >> FW_PORT_CAP32_ANEG_S) & FW_PORT_CAP32_ANEG_M) + +enum fw_port_mdi32 { + FW_PORT_CAP32_MDI_UNCHANGED, + FW_PORT_CAP32_MDI_AUTO, + FW_PORT_CAP32_MDI_F_STRAIGHT, + FW_PORT_CAP32_MDI_F_CROSSOVER +}; + +#define FW_PORT_CAP32_MDI_S 21 +#define FW_PORT_CAP32_MDI_M 3 +#define FW_PORT_CAP32_MDI_V(x) ((x) << FW_PORT_CAP32_MDI_S) +#define FW_PORT_CAP32_MDI_G(x) \ + (((x) >> FW_PORT_CAP32_MDI_S) & FW_PORT_CAP32_MDI_M) + +#define FW_PORT_CAP32_FEC_S 23 +#define FW_PORT_CAP32_FEC_M 0x1f +#define FW_PORT_CAP32_FEC_V(x) ((x) << FW_PORT_CAP32_FEC_S) +#define FW_PORT_CAP32_FEC_G(x) \ + (((x) >> FW_PORT_CAP32_FEC_S) & FW_PORT_CAP32_FEC_M) + +/* macros to isolate various 32-bit Port Capabilities sub-fields */ +#define CAP32_SPEED(__cap32) \ + (FW_PORT_CAP32_SPEED_V(FW_PORT_CAP32_SPEED_M) & __cap32) + +#define CAP32_FEC(__cap32) \ + (FW_PORT_CAP32_FEC_V(FW_PORT_CAP32_FEC_M) & __cap32) + enum fw_port_action { FW_PORT_ACTION_L1_CFG = 0x0001, FW_PORT_ACTION_L2_CFG = 0x0002, @@ -2298,6 +2380,8 @@ enum fw_port_action { FW_PORT_ACTION_DCB_READ_TRANS = 0x0006, FW_PORT_ACTION_DCB_READ_RECV = 0x0007, FW_PORT_ACTION_DCB_READ_DET = 0x0008, + FW_PORT_ACTION_L1_CFG32 = 0x0009, + FW_PORT_ACTION_GET_PORT_INFO32 = 0x000a, FW_PORT_ACTION_LOW_PWR_TO_NORMAL = 0x0010, FW_PORT_ACTION_L1_LOW_PWR_EN = 0x0011, FW_PORT_ACTION_L2_WOL_MODE_EN = 0x0012, @@ -2445,6 +2529,18 @@ struct fw_port_cmd { __be64 r12; } control; } dcb; + struct fw_port_l1cfg32 { + __be32 rcap32; + __be32 r; + } l1cfg32; + struct fw_port_info32 { + __be32 lstatus32_to_cbllen32; + __be32 auxlinfo32_mtu32; + __be32 linkattr32; + __be32 pcaps32; + __be32 acaps32; + __be32 lpacaps32; + } info32; } u; }; @@ -2553,6 +2649,85 @@ struct fw_port_cmd { #define FW_PORT_CMD_DCB_VERSION_G(x) \ (((x) >> FW_PORT_CMD_DCB_VERSION_S) & FW_PORT_CMD_DCB_VERSION_M) +#define FW_PORT_CMD_LSTATUS32_S 31 +#define FW_PORT_CMD_LSTATUS32_M 0x1 +#define FW_PORT_CMD_LSTATUS32_V(x) ((x) << FW_PORT_CMD_LSTATUS32_S) +#define FW_PORT_CMD_LSTATUS32_G(x) \ + (((x) >> FW_PORT_CMD_LSTATUS32_S) & FW_PORT_CMD_LSTATUS32_M) +#define FW_PORT_CMD_LSTATUS32_F FW_PORT_CMD_LSTATUS32_V(1U) + +#define FW_PORT_CMD_LINKDNRC32_S 28 +#define FW_PORT_CMD_LINKDNRC32_M 0x7 +#define FW_PORT_CMD_LINKDNRC32_V(x) ((x) << FW_PORT_CMD_LINKDNRC32_S) +#define FW_PORT_CMD_LINKDNRC32_G(x) \ + (((x) >> FW_PORT_CMD_LINKDNRC32_S) & FW_PORT_CMD_LINKDNRC32_M) + +#define FW_PORT_CMD_DCBXDIS32_S 27 +#define FW_PORT_CMD_DCBXDIS32_M 0x1 +#define FW_PORT_CMD_DCBXDIS32_V(x) ((x) << FW_PORT_CMD_DCBXDIS32_S) +#define FW_PORT_CMD_DCBXDIS32_G(x) \ + (((x) >> FW_PORT_CMD_DCBXDIS32_S) & FW_PORT_CMD_DCBXDIS32_M) +#define FW_PORT_CMD_DCBXDIS32_F FW_PORT_CMD_DCBXDIS32_V(1U) + +#define FW_PORT_CMD_MDIOCAP32_S 26 +#define FW_PORT_CMD_MDIOCAP32_M 0x1 +#define FW_PORT_CMD_MDIOCAP32_V(x) ((x) << FW_PORT_CMD_MDIOCAP32_S) +#define FW_PORT_CMD_MDIOCAP32_G(x) \ + (((x) >> FW_PORT_CMD_MDIOCAP32_S) & FW_PORT_CMD_MDIOCAP32_M) +#define FW_PORT_CMD_MDIOCAP32_F FW_PORT_CMD_MDIOCAP32_V(1U) + +#define FW_PORT_CMD_MDIOADDR32_S 21 +#define FW_PORT_CMD_MDIOADDR32_M 0x1f +#define FW_PORT_CMD_MDIOADDR32_V(x) ((x) << FW_PORT_CMD_MDIOADDR32_S) +#define FW_PORT_CMD_MDIOADDR32_G(x) \ + (((x) >> FW_PORT_CMD_MDIOADDR32_S) & FW_PORT_CMD_MDIOADDR32_M) + +#define FW_PORT_CMD_PORTTYPE32_S 13 +#define FW_PORT_CMD_PORTTYPE32_M 0xff +#define FW_PORT_CMD_PORTTYPE32_V(x) ((x) << FW_PORT_CMD_PORTTYPE32_S) +#define FW_PORT_CMD_PORTTYPE32_G(x) \ + (((x) >> FW_PORT_CMD_PORTTYPE32_S) & FW_PORT_CMD_PORTTYPE32_M) + +#define FW_PORT_CMD_MODTYPE32_S 8 +#define FW_PORT_CMD_MODTYPE32_M 0x1f +#define FW_PORT_CMD_MODTYPE32_V(x) ((x) << FW_PORT_CMD_MODTYPE32_S) +#define FW_PORT_CMD_MODTYPE32_G(x) \ + (((x) >> FW_PORT_CMD_MODTYPE32_S) & FW_PORT_CMD_MODTYPE32_M) + +#define FW_PORT_CMD_CBLLEN32_S 0 +#define FW_PORT_CMD_CBLLEN32_M 0xff +#define FW_PORT_CMD_CBLLEN32_V(x) ((x) << FW_PORT_CMD_CBLLEN32_S) +#define FW_PORT_CMD_CBLLEN32_G(x) \ + (((x) >> FW_PORT_CMD_CBLLEN32_S) & FW_PORT_CMD_CBLLEN32_M) + +#define FW_PORT_CMD_AUXLINFO32_S 24 +#define FW_PORT_CMD_AUXLINFO32_M 0xff +#define FW_PORT_CMD_AUXLINFO32_V(x) ((x) << FW_PORT_CMD_AUXLINFO32_S) +#define FW_PORT_CMD_AUXLINFO32_G(x) \ + (((x) >> FW_PORT_CMD_AUXLINFO32_S) & FW_PORT_CMD_AUXLINFO32_M) + +#define FW_PORT_AUXLINFO32_KX4_S 2 +#define FW_PORT_AUXLINFO32_KX4_M 0x1 +#define FW_PORT_AUXLINFO32_KX4_V(x) \ + ((x) << FW_PORT_AUXLINFO32_KX4_S) +#define FW_PORT_AUXLINFO32_KX4_G(x) \ + (((x) >> FW_PORT_AUXLINFO32_KX4_S) & FW_PORT_AUXLINFO32_KX4_M) +#define FW_PORT_AUXLINFO32_KX4_F FW_PORT_AUXLINFO32_KX4_V(1U) + +#define FW_PORT_AUXLINFO32_KR_S 1 +#define FW_PORT_AUXLINFO32_KR_M 0x1 +#define FW_PORT_AUXLINFO32_KR_V(x) \ + ((x) << FW_PORT_AUXLINFO32_KR_S) +#define FW_PORT_AUXLINFO32_KR_G(x) \ + (((x) >> FW_PORT_AUXLINFO32_KR_S) & FW_PORT_AUXLINFO32_KR_M) +#define FW_PORT_AUXLINFO32_KR_F FW_PORT_AUXLINFO32_KR_V(1U) + +#define FW_PORT_CMD_MTU32_S 0 +#define FW_PORT_CMD_MTU32_M 0xffff +#define FW_PORT_CMD_MTU32_V(x) ((x) << FW_PORT_CMD_MTU32_S) +#define FW_PORT_CMD_MTU32_G(x) \ + (((x) >> FW_PORT_CMD_MTU32_S) & FW_PORT_CMD_MTU32_M) + enum fw_port_type { FW_PORT_TYPE_FIBER_XFI, FW_PORT_TYPE_FIBER_XAUI, diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index 2b85b874fd0d..8996ebbd222e 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -182,7 +182,7 @@ void t4vf_os_link_changed(struct adapter *adapter, int pidx, int link_ok) break; } - switch (pi->link_cfg.fc) { + switch ((int)pi->link_cfg.fc) { case PAUSE_RX: fc = "RX"; break; @@ -191,7 +191,7 @@ void t4vf_os_link_changed(struct adapter *adapter, int pidx, int link_ok) fc = "TX"; break; - case PAUSE_RX|PAUSE_TX: + case PAUSE_RX | PAUSE_TX: fc = "RX/TX"; break; @@ -1213,7 +1213,11 @@ static int from_fw_port_mod_type(enum fw_port_type port_type, } else if (port_type == FW_PORT_TYPE_SFP || port_type == FW_PORT_TYPE_QSFP_10G || port_type == FW_PORT_TYPE_QSA || - port_type == FW_PORT_TYPE_QSFP) { + port_type == FW_PORT_TYPE_QSFP || + port_type == FW_PORT_TYPE_CR4_QSFP || + port_type == FW_PORT_TYPE_CR_QSFP || + port_type == FW_PORT_TYPE_CR2_QSFP || + port_type == FW_PORT_TYPE_SFP28) { if (mod_type == FW_PORT_MOD_TYPE_LR || mod_type == FW_PORT_MOD_TYPE_SR || mod_type == FW_PORT_MOD_TYPE_ER || @@ -1224,6 +1228,9 @@ static int from_fw_port_mod_type(enum fw_port_type port_type, return PORT_DA; else return PORT_OTHER; + } else if (port_type == FW_PORT_TYPE_KR4_100G || + port_type == FW_PORT_TYPE_KR_SFP28) { + return PORT_NONE; } return PORT_OTHER; @@ -1242,12 +1249,13 @@ static void fw_caps_to_lmm(enum fw_port_type port_type, unsigned int fw_caps, unsigned long *link_mode_mask) { - #define SET_LMM(__lmm_name) __set_bit(ETHTOOL_LINK_MODE_ ## __lmm_name\ - ## _BIT, link_mode_mask) + #define SET_LMM(__lmm_name) \ + __set_bit(ETHTOOL_LINK_MODE_ ## __lmm_name ## _BIT, \ + link_mode_mask) #define FW_CAPS_TO_LMM(__fw_name, __lmm_name) \ do { \ - if (fw_caps & FW_PORT_CAP_ ## __fw_name) \ + if (fw_caps & FW_PORT_CAP32_ ## __fw_name) \ SET_LMM(__lmm_name); \ } while (0) @@ -1310,6 +1318,16 @@ static void fw_caps_to_lmm(enum fw_port_type port_type, SET_LMM(25000baseCR_Full); break; + case FW_PORT_TYPE_KR_SFP28: + SET_LMM(Backplane); + SET_LMM(25000baseKR_Full); + break; + + case FW_PORT_TYPE_CR2_QSFP: + SET_LMM(FIBRE); + SET_LMM(50000baseSR2_Full); + break; + case FW_PORT_TYPE_KR4_100G: case FW_PORT_TYPE_CR4_QSFP: SET_LMM(FIBRE); @@ -1329,12 +1347,18 @@ static void fw_caps_to_lmm(enum fw_port_type port_type, } static int cxgb4vf_get_link_ksettings(struct net_device *dev, - struct ethtool_link_ksettings - *link_ksettings) + struct ethtool_link_ksettings *link_ksettings) { - const struct port_info *pi = netdev_priv(dev); + struct port_info *pi = netdev_priv(dev); struct ethtool_link_settings *base = &link_ksettings->base; + /* For the nonce, the Firmware doesn't send up Port State changes + * when the Virtual Interface attached to the Port is down. So + * if it's down, let's grab any changes. + */ + if (!netif_running(dev)) + (void)t4vf_update_port_info(pi); + ethtool_link_ksettings_zero_link_mode(link_ksettings, supported); ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); ethtool_link_ksettings_zero_link_mode(link_ksettings, lp_advertising); @@ -1351,11 +1375,11 @@ static int cxgb4vf_get_link_ksettings(struct net_device *dev, base->mdio_support = 0; } - fw_caps_to_lmm(pi->port_type, pi->link_cfg.supported, + fw_caps_to_lmm(pi->port_type, pi->link_cfg.pcaps, link_ksettings->link_modes.supported); - fw_caps_to_lmm(pi->port_type, pi->link_cfg.advertising, + fw_caps_to_lmm(pi->port_type, pi->link_cfg.acaps, link_ksettings->link_modes.advertising); - fw_caps_to_lmm(pi->port_type, pi->link_cfg.lp_advertising, + fw_caps_to_lmm(pi->port_type, pi->link_cfg.lpacaps, link_ksettings->link_modes.lp_advertising); if (netif_carrier_ok(dev)) { @@ -1367,7 +1391,7 @@ static int cxgb4vf_get_link_ksettings(struct net_device *dev, } base->autoneg = pi->link_cfg.autoneg; - if (pi->link_cfg.supported & FW_PORT_CAP_ANEG) + if (pi->link_cfg.pcaps & FW_PORT_CAP32_ANEG) ethtool_link_ksettings_add_link_mode(link_ksettings, supported, Autoneg); if (pi->link_cfg.autoneg) diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h index b3903fe411aa..9cf9c56b0f73 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_common.h @@ -104,24 +104,62 @@ struct t4vf_port_stats { /* * Per-"port" (Virtual Interface) link configuration ... */ -struct link_config { - unsigned int supported; /* link capabilities */ - unsigned int advertising; /* advertised capabilities */ - unsigned short lp_advertising; /* peer advertised capabilities */ - unsigned int requested_speed; /* speed user has requested */ - unsigned int speed; /* actual link speed */ - unsigned char requested_fc; /* flow control user has requested */ - unsigned char fc; /* actual link flow control */ - unsigned char autoneg; /* autonegotiating? */ - unsigned char link_ok; /* link up? */ +typedef u16 fw_port_cap16_t; /* 16-bit Port Capabilities integral value */ +typedef u32 fw_port_cap32_t; /* 32-bit Port Capabilities integral value */ + +enum fw_caps { + FW_CAPS_UNKNOWN = 0, /* 0'ed out initial state */ + FW_CAPS16 = 1, /* old Firmware: 16-bit Port Capabilities */ + FW_CAPS32 = 2, /* new Firmware: 32-bit Port Capabilities */ }; -enum { - PAUSE_RX = 1 << 0, - PAUSE_TX = 1 << 1, - PAUSE_AUTONEG = 1 << 2 +enum cc_pause { + PAUSE_RX = 1 << 0, + PAUSE_TX = 1 << 1, + PAUSE_AUTONEG = 1 << 2 +}; + +enum cc_fec { + FEC_AUTO = 1 << 0, /* IEEE 802.3 "automatic" */ + FEC_RS = 1 << 1, /* Reed-Solomon */ + FEC_BASER_RS = 1 << 2, /* BaseR/Reed-Solomon */ +}; + +struct link_config { + fw_port_cap32_t pcaps; /* link capabilities */ + fw_port_cap32_t acaps; /* advertised capabilities */ + fw_port_cap32_t lpacaps; /* peer advertised capabilities */ + + fw_port_cap32_t speed_caps; /* speed(s) user has requested */ + u32 speed; /* actual link speed */ + + enum cc_pause requested_fc; /* flow control user has requested */ + enum cc_pause fc; /* actual link flow control */ + + enum cc_fec auto_fec; /* Forward Error Correction: */ + enum cc_fec requested_fec; /* "automatic" (IEEE 802.3), */ + enum cc_fec fec; /* requested, and actual in use */ + + unsigned char autoneg; /* autonegotiating? */ + + unsigned char link_ok; /* link up? */ + unsigned char link_down_rc; /* link down reason */ }; +/* Return true if the Link Configuration supports "High Speeds" (those greater + * than 1Gb/s). + */ +static inline bool is_x_10g_port(const struct link_config *lc) +{ + fw_port_cap32_t speeds, high_speeds; + + speeds = FW_PORT_CAP32_SPEED_V(FW_PORT_CAP32_SPEED_G(lc->pcaps)); + high_speeds = + speeds & ~(FW_PORT_CAP32_SPEED_100M | FW_PORT_CAP32_SPEED_1G); + + return high_speeds != 0; +} + /* * General device parameters ... */ @@ -227,6 +265,7 @@ struct adapter_params { struct arch_specific_params arch; /* chip specific params */ enum chip_type chip; /* chip code */ u8 nports; /* # of Ethernet "ports" */ + u8 fw_caps_support; /* 32-bit Port Capabilities */ }; /* Firmware Mailbox Command/Reply log. All values are in Host-Endian format. @@ -266,24 +305,6 @@ static inline struct mbox_cmd *mbox_cmd_log_entry(struct mbox_cmd_log *log, #define for_each_port(adapter, iter) \ for (iter = 0; iter < (adapter)->params.nports; iter++) -static inline bool is_10g_port(const struct link_config *lc) -{ - return (lc->supported & FW_PORT_CAP_SPEED_10G) != 0; -} - -/* Return true if the Link Configuration supports "High Speeds" (those greater - * than 1Gb/s). - */ -static inline bool is_x_10g_port(const struct link_config *lc) -{ - unsigned int speeds, high_speeds; - - speeds = FW_PORT_CAP_SPEED_V(FW_PORT_CAP_SPEED_G(lc->supported)); - high_speeds = speeds & ~(FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G); - - return high_speeds != 0; -} - static inline unsigned int core_ticks_per_usec(const struct adapter *adapter) { return adapter->params.vpd.cclk / 1000; @@ -387,6 +408,7 @@ int t4vf_iq_free(struct adapter *, unsigned int, unsigned int, unsigned int, unsigned int); int t4vf_eth_eq_free(struct adapter *, unsigned int); +int t4vf_update_port_info(struct port_info *pi); int t4vf_handle_fw_rpl(struct adapter *, const __be64 *); int t4vf_prep_adapter(struct adapter *); int t4vf_get_vf_mac_acl(struct adapter *adapter, unsigned int pf, diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c index e98248f00fef..a8d94963b4d0 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/t4vf_hw.c @@ -313,32 +313,130 @@ int t4vf_wr_mbox_core(struct adapter *adapter, const void *cmd, int size, return ret; } -#define ADVERT_MASK (FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G |\ - FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_SPEED_25G | \ - FW_PORT_CAP_SPEED_40G | FW_PORT_CAP_SPEED_100G | \ - FW_PORT_CAP_ANEG) +#define ADVERT_MASK (FW_PORT_CAP32_SPEED_V(FW_PORT_CAP32_SPEED_M) | \ + FW_PORT_CAP32_ANEG) /** + * fwcaps16_to_caps32 - convert 16-bit Port Capabilities to 32-bits + * @caps16: a 16-bit Port Capabilities value + * + * Returns the equivalent 32-bit Port Capabilities value. + */ +static fw_port_cap32_t fwcaps16_to_caps32(fw_port_cap16_t caps16) +{ + fw_port_cap32_t caps32 = 0; + + #define CAP16_TO_CAP32(__cap) \ + do { \ + if (caps16 & FW_PORT_CAP_##__cap) \ + caps32 |= FW_PORT_CAP32_##__cap; \ + } while (0) + + CAP16_TO_CAP32(SPEED_100M); + CAP16_TO_CAP32(SPEED_1G); + CAP16_TO_CAP32(SPEED_25G); + CAP16_TO_CAP32(SPEED_10G); + CAP16_TO_CAP32(SPEED_40G); + CAP16_TO_CAP32(SPEED_100G); + CAP16_TO_CAP32(FC_RX); + CAP16_TO_CAP32(FC_TX); + CAP16_TO_CAP32(ANEG); + CAP16_TO_CAP32(MDIX); + CAP16_TO_CAP32(MDIAUTO); + CAP16_TO_CAP32(FEC_RS); + CAP16_TO_CAP32(FEC_BASER_RS); + CAP16_TO_CAP32(802_3_PAUSE); + CAP16_TO_CAP32(802_3_ASM_DIR); + + #undef CAP16_TO_CAP32 + + return caps32; +} + +/* Translate Firmware Pause specification to Common Code */ +static inline enum cc_pause fwcap_to_cc_pause(fw_port_cap32_t fw_pause) +{ + enum cc_pause cc_pause = 0; + + if (fw_pause & FW_PORT_CAP32_FC_RX) + cc_pause |= PAUSE_RX; + if (fw_pause & FW_PORT_CAP32_FC_TX) + cc_pause |= PAUSE_TX; + + return cc_pause; +} + +/* Translate Firmware Forward Error Correction specification to Common Code */ +static inline enum cc_fec fwcap_to_cc_fec(fw_port_cap32_t fw_fec) +{ + enum cc_fec cc_fec = 0; + + if (fw_fec & FW_PORT_CAP32_FEC_RS) + cc_fec |= FEC_RS; + if (fw_fec & FW_PORT_CAP32_FEC_BASER_RS) + cc_fec |= FEC_BASER_RS; + + return cc_fec; +} + +/** + * Return the highest speed set in the port capabilities, in Mb/s. + */ +static unsigned int fwcap_to_speed(fw_port_cap32_t caps) +{ + #define TEST_SPEED_RETURN(__caps_speed, __speed) \ + do { \ + if (caps & FW_PORT_CAP32_SPEED_##__caps_speed) \ + return __speed; \ + } while (0) + + TEST_SPEED_RETURN(400G, 400000); + TEST_SPEED_RETURN(200G, 200000); + TEST_SPEED_RETURN(100G, 100000); + TEST_SPEED_RETURN(50G, 50000); + TEST_SPEED_RETURN(40G, 40000); + TEST_SPEED_RETURN(25G, 25000); + TEST_SPEED_RETURN(10G, 10000); + TEST_SPEED_RETURN(1G, 1000); + TEST_SPEED_RETURN(100M, 100); + + #undef TEST_SPEED_RETURN + + return 0; +} + +/* * init_link_config - initialize a link's SW state * @lc: structure holding the link state - * @caps: link capabilities + * @pcaps: link Port Capabilities + * @acaps: link current Advertised Port Capabilities * * Initializes the SW state maintained for each link, including the link's * capabilities and default speed/flow-control/autonegotiation settings. */ -static void init_link_config(struct link_config *lc, unsigned int caps) +static void init_link_config(struct link_config *lc, + fw_port_cap32_t pcaps, + fw_port_cap32_t acaps) { - lc->supported = caps; - lc->lp_advertising = 0; - lc->requested_speed = 0; + lc->pcaps = pcaps; + lc->lpacaps = 0; + lc->speed_caps = 0; lc->speed = 0; lc->requested_fc = lc->fc = PAUSE_RX | PAUSE_TX; - if (lc->supported & FW_PORT_CAP_ANEG) { - lc->advertising = lc->supported & ADVERT_MASK; + + /* For Forward Error Control, we default to whatever the Firmware + * tells us the Link is currently advertising. + */ + lc->auto_fec = fwcap_to_cc_fec(acaps); + lc->requested_fec = FEC_AUTO; + lc->fec = lc->auto_fec; + + if (lc->pcaps & FW_PORT_CAP32_ANEG) { + lc->acaps = acaps & ADVERT_MASK; lc->autoneg = AUTONEG_ENABLE; lc->requested_fc |= PAUSE_AUTONEG; } else { - lc->advertising = 0; + lc->acaps = 0; lc->autoneg = AUTONEG_DISABLE; } } @@ -351,9 +449,30 @@ static void init_link_config(struct link_config *lc, unsigned int caps) int t4vf_port_init(struct adapter *adapter, int pidx) { struct port_info *pi = adap2pinfo(adapter, pidx); + unsigned int fw_caps = adapter->params.fw_caps_support; struct fw_vi_cmd vi_cmd, vi_rpl; struct fw_port_cmd port_cmd, port_rpl; - int v; + enum fw_port_type port_type; + int mdio_addr; + fw_port_cap32_t pcaps, acaps; + int ret; + + /* If we haven't yet determined whether we're talking to Firmware + * which knows the new 32-bit Port Capabilities, it's time to find + * out now. This will also tell new Firmware to send us Port Status + * Updates using the new 32-bit Port Capabilities version of the + * Port Information message. + */ + if (fw_caps == FW_CAPS_UNKNOWN) { + u32 param, val; + + param = (FW_PARAMS_MNEM_V(FW_PARAMS_MNEM_PFVF) | + FW_PARAMS_PARAM_X_V(FW_PARAMS_PARAM_PFVF_PORT_CAPS32)); + val = 1; + ret = t4vf_set_params(adapter, 1, ¶m, &val); + fw_caps = (ret == 0 ? FW_CAPS32 : FW_CAPS16); + adapter->params.fw_caps_support = fw_caps; + } /* * Execute a VI Read command to get our Virtual Interface information @@ -365,9 +484,9 @@ int t4vf_port_init(struct adapter *adapter, int pidx) FW_CMD_READ_F); vi_cmd.alloc_to_len16 = cpu_to_be32(FW_LEN16(vi_cmd)); vi_cmd.type_viid = cpu_to_be16(FW_VI_CMD_VIID_V(pi->viid)); - v = t4vf_wr_mbox(adapter, &vi_cmd, sizeof(vi_cmd), &vi_rpl); - if (v) - return v; + ret = t4vf_wr_mbox(adapter, &vi_cmd, sizeof(vi_cmd), &vi_rpl); + if (ret != FW_SUCCESS) + return ret; BUG_ON(pi->port_id != FW_VI_CMD_PORTID_G(vi_rpl.portid_pkd)); pi->rss_size = FW_VI_CMD_RSSSIZE_G(be16_to_cpu(vi_rpl.rsssize_pkd)); @@ -385,21 +504,42 @@ int t4vf_port_init(struct adapter *adapter, int pidx) FW_CMD_REQUEST_F | FW_CMD_READ_F | FW_PORT_CMD_PORTID_V(pi->port_id)); - port_cmd.action_to_len16 = - cpu_to_be32(FW_PORT_CMD_ACTION_V(FW_PORT_ACTION_GET_PORT_INFO) | - FW_LEN16(port_cmd)); - v = t4vf_wr_mbox(adapter, &port_cmd, sizeof(port_cmd), &port_rpl); - if (v) - return v; + port_cmd.action_to_len16 = cpu_to_be32( + FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16 + ? FW_PORT_ACTION_GET_PORT_INFO + : FW_PORT_ACTION_GET_PORT_INFO32) | + FW_LEN16(port_cmd)); + ret = t4vf_wr_mbox(adapter, &port_cmd, sizeof(port_cmd), &port_rpl); + if (ret != FW_SUCCESS) + return ret; - v = be32_to_cpu(port_rpl.u.info.lstatus_to_modtype); - pi->mdio_addr = (v & FW_PORT_CMD_MDIOCAP_F) ? - FW_PORT_CMD_MDIOADDR_G(v) : -1; - pi->port_type = FW_PORT_CMD_PTYPE_G(v); - pi->mod_type = FW_PORT_MOD_TYPE_NA; + /* Extract the various fields from the Port Information message. */ + if (fw_caps == FW_CAPS16) { + u32 lstatus = be32_to_cpu(port_rpl.u.info.lstatus_to_modtype); - init_link_config(&pi->link_cfg, be16_to_cpu(port_rpl.u.info.pcap)); + port_type = FW_PORT_CMD_PTYPE_G(lstatus); + mdio_addr = ((lstatus & FW_PORT_CMD_MDIOCAP_F) + ? FW_PORT_CMD_MDIOADDR_G(lstatus) + : -1); + pcaps = fwcaps16_to_caps32(be16_to_cpu(port_rpl.u.info.pcap)); + acaps = fwcaps16_to_caps32(be16_to_cpu(port_rpl.u.info.acap)); + } else { + u32 lstatus32 = + be32_to_cpu(port_rpl.u.info32.lstatus32_to_cbllen32); + + port_type = FW_PORT_CMD_PORTTYPE32_G(lstatus32); + mdio_addr = ((lstatus32 & FW_PORT_CMD_MDIOCAP32_F) + ? FW_PORT_CMD_MDIOADDR32_G(lstatus32) + : -1); + pcaps = be32_to_cpu(port_rpl.u.info32.pcaps32); + acaps = be32_to_cpu(port_rpl.u.info32.acaps32); + } + pi->port_type = port_type; + pi->mdio_addr = mdio_addr; + pi->mod_type = FW_PORT_MOD_TYPE_NA; + + init_link_config(&pi->link_cfg, pcaps, acaps); return 0; } @@ -1667,6 +1807,202 @@ int t4vf_eth_eq_free(struct adapter *adapter, unsigned int eqid) } /** + * t4vf_link_down_rc_str - return a string for a Link Down Reason Code + * @link_down_rc: Link Down Reason Code + * + * Returns a string representation of the Link Down Reason Code. + */ +const char *t4vf_link_down_rc_str(unsigned char link_down_rc) +{ + static const char * const reason[] = { + "Link Down", + "Remote Fault", + "Auto-negotiation Failure", + "Reserved", + "Insufficient Airflow", + "Unable To Determine Reason", + "No RX Signal Detected", + "Reserved", + }; + + if (link_down_rc >= ARRAY_SIZE(reason)) + return "Bad Reason Code"; + + return reason[link_down_rc]; +} + +/** + * t4vf_handle_get_port_info - process a FW reply message + * @pi: the port info + * @rpl: start of the FW message + * + * Processes a GET_PORT_INFO FW reply message. + */ +void t4vf_handle_get_port_info(struct port_info *pi, + const struct fw_port_cmd *cmd) +{ + int action = FW_PORT_CMD_ACTION_G(be32_to_cpu(cmd->action_to_len16)); + struct adapter *adapter = pi->adapter; + struct link_config *lc = &pi->link_cfg; + int link_ok, linkdnrc; + enum fw_port_type port_type; + enum fw_port_module_type mod_type; + unsigned int speed, fc, fec; + fw_port_cap32_t pcaps, acaps, lpacaps, linkattr; + + /* Extract the various fields from the Port Information message. */ + switch (action) { + case FW_PORT_ACTION_GET_PORT_INFO: { + u32 lstatus = be32_to_cpu(cmd->u.info.lstatus_to_modtype); + + link_ok = (lstatus & FW_PORT_CMD_LSTATUS_F) != 0; + linkdnrc = FW_PORT_CMD_LINKDNRC_G(lstatus); + port_type = FW_PORT_CMD_PTYPE_G(lstatus); + mod_type = FW_PORT_CMD_MODTYPE_G(lstatus); + pcaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.pcap)); + acaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.acap)); + lpacaps = fwcaps16_to_caps32(be16_to_cpu(cmd->u.info.lpacap)); + + /* Unfortunately the format of the Link Status in the old + * 16-bit Port Information message isn't the same as the + * 16-bit Port Capabilities bitfield used everywhere else ... + */ + linkattr = 0; + if (lstatus & FW_PORT_CMD_RXPAUSE_F) + linkattr |= FW_PORT_CAP32_FC_RX; + if (lstatus & FW_PORT_CMD_TXPAUSE_F) + linkattr |= FW_PORT_CAP32_FC_TX; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M)) + linkattr |= FW_PORT_CAP32_SPEED_100M; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G)) + linkattr |= FW_PORT_CAP32_SPEED_1G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G)) + linkattr |= FW_PORT_CAP32_SPEED_10G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_25G)) + linkattr |= FW_PORT_CAP32_SPEED_25G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G)) + linkattr |= FW_PORT_CAP32_SPEED_40G; + if (lstatus & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100G)) + linkattr |= FW_PORT_CAP32_SPEED_100G; + + break; + } + + case FW_PORT_ACTION_GET_PORT_INFO32: { + u32 lstatus32; + + lstatus32 = be32_to_cpu(cmd->u.info32.lstatus32_to_cbllen32); + link_ok = (lstatus32 & FW_PORT_CMD_LSTATUS32_F) != 0; + linkdnrc = FW_PORT_CMD_LINKDNRC32_G(lstatus32); + port_type = FW_PORT_CMD_PORTTYPE32_G(lstatus32); + mod_type = FW_PORT_CMD_MODTYPE32_G(lstatus32); + pcaps = be32_to_cpu(cmd->u.info32.pcaps32); + acaps = be32_to_cpu(cmd->u.info32.acaps32); + lpacaps = be32_to_cpu(cmd->u.info32.lpacaps32); + linkattr = be32_to_cpu(cmd->u.info32.linkattr32); + break; + } + + default: + dev_err(adapter->pdev_dev, "Handle Port Information: Bad Command/Action %#x\n", + be32_to_cpu(cmd->action_to_len16)); + return; + } + + fec = fwcap_to_cc_fec(acaps); + fc = fwcap_to_cc_pause(linkattr); + speed = fwcap_to_speed(linkattr); + + if (mod_type != pi->mod_type) { + /* When a new Transceiver Module is inserted, the Firmware + * will examine any Forward Error Correction parameters + * present in the Transceiver Module i2c EPROM and determine + * the supported and recommended FEC settings from those + * based on IEEE 802.3 standards. We always record the + * IEEE 802.3 recommended "automatic" settings. + */ + lc->auto_fec = fec; + + /* Some versions of the early T6 Firmware "cheated" when + * handling different Transceiver Modules by changing the + * underlaying Port Type reported to the Host Drivers. As + * such we need to capture whatever Port Type the Firmware + * sends us and record it in case it's different from what we + * were told earlier. Unfortunately, since Firmware is + * forever, we'll need to keep this code here forever, but in + * later T6 Firmware it should just be an assignment of the + * same value already recorded. + */ + pi->port_type = port_type; + + pi->mod_type = mod_type; + t4vf_os_portmod_changed(adapter, pi->pidx); + } + + if (link_ok != lc->link_ok || speed != lc->speed || + fc != lc->fc || fec != lc->fec) { /* something changed */ + if (!link_ok && lc->link_ok) { + lc->link_down_rc = linkdnrc; + dev_warn(adapter->pdev_dev, "Port %d link down, reason: %s\n", + pi->port_id, t4vf_link_down_rc_str(linkdnrc)); + } + lc->link_ok = link_ok; + lc->speed = speed; + lc->fc = fc; + lc->fec = fec; + + lc->pcaps = pcaps; + lc->lpacaps = lpacaps; + lc->acaps = acaps & ADVERT_MASK; + + if (lc->acaps & FW_PORT_CAP32_ANEG) { + lc->autoneg = AUTONEG_ENABLE; + } else { + /* When Autoneg is disabled, user needs to set + * single speed. + * Similar to cxgb4_ethtool.c: set_link_ksettings + */ + lc->acaps = 0; + lc->speed_caps = fwcap_to_speed(acaps); + lc->autoneg = AUTONEG_DISABLE; + } + + t4vf_os_link_changed(adapter, pi->pidx, link_ok); + } +} + +/** + * t4vf_update_port_info - retrieve and update port information if changed + * @pi: the port_info + * + * We issue a Get Port Information Command to the Firmware and, if + * successful, we check to see if anything is different from what we + * last recorded and update things accordingly. + */ +int t4vf_update_port_info(struct port_info *pi) +{ + unsigned int fw_caps = pi->adapter->params.fw_caps_support; + struct fw_port_cmd port_cmd; + int ret; + + memset(&port_cmd, 0, sizeof(port_cmd)); + port_cmd.op_to_portid = cpu_to_be32(FW_CMD_OP_V(FW_PORT_CMD) | + FW_CMD_REQUEST_F | FW_CMD_READ_F | + FW_PORT_CMD_PORTID_V(pi->port_id)); + port_cmd.action_to_len16 = cpu_to_be32( + FW_PORT_CMD_ACTION_V(fw_caps == FW_CAPS16 + ? FW_PORT_ACTION_GET_PORT_INFO + : FW_PORT_ACTION_GET_PORT_INFO32) | + FW_LEN16(port_cmd)); + ret = t4vf_wr_mbox(pi->adapter, &port_cmd, sizeof(port_cmd), + &port_cmd); + if (ret) + return ret; + t4vf_handle_get_port_info(pi, &port_cmd); + return 0; +} + +/** * t4vf_handle_fw_rpl - process a firmware reply message * @adapter: the adapter * @rpl: start of the firmware message @@ -1685,15 +2021,12 @@ int t4vf_handle_fw_rpl(struct adapter *adapter, const __be64 *rpl) */ const struct fw_port_cmd *port_cmd = (const struct fw_port_cmd *)rpl; - u32 stat, mod; - int action, port_id, link_ok, speed, fc, pidx; - - /* - * Extract various fields from port status change message. - */ - action = FW_PORT_CMD_ACTION_G( + int action = FW_PORT_CMD_ACTION_G( be32_to_cpu(port_cmd->action_to_len16)); - if (action != FW_PORT_ACTION_GET_PORT_INFO) { + int port_id, pidx; + + if (action != FW_PORT_ACTION_GET_PORT_INFO && + action != FW_PORT_ACTION_GET_PORT_INFO32) { dev_err(adapter->pdev_dev, "Unknown firmware PORT reply action %x\n", action); @@ -1702,61 +2035,12 @@ int t4vf_handle_fw_rpl(struct adapter *adapter, const __be64 *rpl) port_id = FW_PORT_CMD_PORTID_G( be32_to_cpu(port_cmd->op_to_portid)); - - stat = be32_to_cpu(port_cmd->u.info.lstatus_to_modtype); - link_ok = (stat & FW_PORT_CMD_LSTATUS_F) != 0; - speed = 0; - fc = 0; - if (stat & FW_PORT_CMD_RXPAUSE_F) - fc |= PAUSE_RX; - if (stat & FW_PORT_CMD_TXPAUSE_F) - fc |= PAUSE_TX; - if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100M)) - speed = 100; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_1G)) - speed = 1000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_10G)) - speed = 10000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_25G)) - speed = 25000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_40G)) - speed = 40000; - else if (stat & FW_PORT_CMD_LSPEED_V(FW_PORT_CAP_SPEED_100G)) - speed = 100000; - - /* - * Scan all of our "ports" (Virtual Interfaces) looking for - * those bound to the physical port which has changed. If - * our recorded state doesn't match the current state, - * signal that change to the OS code. - */ for_each_port(adapter, pidx) { struct port_info *pi = adap2pinfo(adapter, pidx); - struct link_config *lc; if (pi->port_id != port_id) continue; - - lc = &pi->link_cfg; - - mod = FW_PORT_CMD_MODTYPE_G(stat); - if (mod != pi->mod_type) { - pi->mod_type = mod; - t4vf_os_portmod_changed(adapter, pidx); - } - - if (link_ok != lc->link_ok || speed != lc->speed || - fc != lc->fc) { - /* something changed */ - lc->link_ok = link_ok; - lc->speed = speed; - lc->fc = fc; - lc->supported = - be16_to_cpu(port_cmd->u.info.pcap); - lc->lp_advertising = - be16_to_cpu(port_cmd->u.info.lpacap); - t4vf_os_link_changed(adapter, pidx, link_ok); - } + t4vf_handle_get_port_info(pi, port_cmd); } break; } diff --git a/drivers/net/ethernet/dec/tulip/de4x5.c b/drivers/net/ethernet/dec/tulip/de4x5.c index 47be5018d35d..0affee9c8aa2 100644 --- a/drivers/net/ethernet/dec/tulip/de4x5.c +++ b/drivers/net/ethernet/dec/tulip/de4x5.c @@ -2094,7 +2094,7 @@ static int de4x5_eisa_remove(struct device *device) return 0; } -static struct eisa_device_id de4x5_eisa_ids[] = { +static const struct eisa_device_id de4x5_eisa_ids[] = { { "DEC4250", 0 }, /* 0 is the board name index... */ { "" } }; diff --git a/drivers/net/ethernet/dec/tulip/tulip_core.c b/drivers/net/ethernet/dec/tulip/tulip_core.c index 17e566a8b345..84394b43c0a1 100644 --- a/drivers/net/ethernet/dec/tulip/tulip_core.c +++ b/drivers/net/ethernet/dec/tulip/tulip_core.c @@ -1303,7 +1303,6 @@ static int tulip_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 0x00, 'L', 'i', 'n', 'u', 'x' }; static int last_irq; - static int multiport_cnt; /* For four-port boards w/one EEPROM */ int i, irq; unsigned short sum; unsigned char *ee_data; @@ -1557,7 +1556,6 @@ static int tulip_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } else if (ee_data[0] == 0xff && ee_data[1] == 0xff && ee_data[2] == 0) { sa_offset = 2; /* Grrr, damn Matrox boards. */ - multiport_cnt = 4; } #ifdef CONFIG_MIPS_COBALT if ((pdev->bus->number == 0) && diff --git a/drivers/net/ethernet/ec_bhf.c b/drivers/net/ethernet/ec_bhf.c index 4ee042c034a1..1b79a6defd56 100644 --- a/drivers/net/ethernet/ec_bhf.c +++ b/drivers/net/ethernet/ec_bhf.c @@ -73,7 +73,7 @@ #define ETHERCAT_MASTER_ID 0x14 -static struct pci_device_id ids[] = { +static const struct pci_device_id ids[] = { { PCI_DEVICE(0x15ec, 0x5000), }, { 0, } }; diff --git a/drivers/net/ethernet/emulex/benet/be_roce.c b/drivers/net/ethernet/emulex/benet/be_roce.c index 2b62841c4c63..05989aafaf32 100644 --- a/drivers/net/ethernet/emulex/benet/be_roce.c +++ b/drivers/net/ethernet/emulex/benet/be_roce.c @@ -139,10 +139,7 @@ int be_roce_register_driver(struct ocrdma_driver *drv) } ocrdma_drv = drv; list_for_each_entry(dev, &be_adapter_list, entry) { - struct net_device *netdev; - _be_roce_dev_add(dev); - netdev = dev->netdev; } mutex_unlock(&be_adapter_list_lock); return 0; diff --git a/drivers/net/ethernet/faraday/ftgmac100.c b/drivers/net/ethernet/faraday/ftgmac100.c index 59da7ac3c108..9ed8e4b81530 100644 --- a/drivers/net/ethernet/faraday/ftgmac100.c +++ b/drivers/net/ethernet/faraday/ftgmac100.c @@ -1623,6 +1623,8 @@ static const struct net_device_ops ftgmac100_netdev_ops = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = ftgmac100_poll_controller, #endif + .ndo_vlan_rx_add_vid = ncsi_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = ncsi_vlan_rx_kill_vid, }; static int ftgmac100_setup_mdio(struct net_device *netdev) @@ -1837,6 +1839,9 @@ static int ftgmac100_probe(struct platform_device *pdev) NETIF_F_GRO | NETIF_F_SG | NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX; + if (priv->use_ncsi) + netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; + /* AST2400 doesn't have working HW checksum generation */ if (np && (of_device_is_compatible(np, "aspeed,ast2400-mac"))) netdev->hw_features &= ~NETIF_F_HW_CSUM; diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 757b873735a5..42258060f142 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -158,7 +158,7 @@ MODULE_PARM_DESC(tx_timeout, "The Tx timeout in ms"); #define DPAA_RX_PRIV_DATA_SIZE (u16)(DPAA_TX_PRIV_DATA_SIZE + \ dpaa_rx_extra_headroom) -#define DPAA_ETH_RX_QUEUES 128 +#define DPAA_ETH_PCD_RXQ_NUM 128 #define DPAA_ENQUEUE_RETRIES 100000 @@ -169,6 +169,7 @@ struct fm_port_fqs { struct dpaa_fq *tx_errq; struct dpaa_fq *rx_defq; struct dpaa_fq *rx_errq; + struct dpaa_fq *rx_pcdq; }; /* All the dpa bps in use at any moment */ @@ -235,7 +236,7 @@ static int dpaa_netdev_init(struct net_device *net_dev, net_dev->max_mtu = dpaa_get_max_mtu(); net_dev->hw_features |= (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_LLTX); + NETIF_F_LLTX | NETIF_F_RXHASH); net_dev->hw_features |= NETIF_F_SG | NETIF_F_HIGHDMA; /* The kernels enables GSO automatically, if we declare NETIF_F_SG. @@ -342,18 +343,19 @@ static void dpaa_get_stats64(struct net_device *net_dev, } } -static int dpaa_setup_tc(struct net_device *net_dev, u32 handle, - u32 chain_index, __be16 proto, struct tc_to_netdev *tc) +static int dpaa_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + void *type_data) { struct dpaa_priv *priv = netdev_priv(net_dev); + struct tc_mqprio_qopt *mqprio = type_data; u8 num_tc; int i; - if (tc->type != TC_SETUP_MQPRIO) - return -EINVAL; + if (type != TC_SETUP_MQPRIO) + return -EOPNOTSUPP; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - num_tc = tc->mqprio->num_tc; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + num_tc = mqprio->num_tc; if (num_tc == priv->num_tc) return 0; @@ -398,8 +400,8 @@ static struct mac_device *dpaa_mac_dev_get(struct platform_device *pdev) of_dev = of_find_device_by_node(mac_node); if (!of_dev) { - dev_err(dpaa_dev, "of_find_device_by_node(%s) failed\n", - mac_node->full_name); + dev_err(dpaa_dev, "of_find_device_by_node(%pOF) failed\n", + mac_node); of_node_put(mac_node); return ERR_PTR(-EINVAL); } @@ -627,6 +629,7 @@ static inline void dpaa_assign_wq(struct dpaa_fq *fq, int idx) fq->wq = 5; break; case FQ_TYPE_RX_DEFAULT: + case FQ_TYPE_RX_PCD: fq->wq = 6; break; case FQ_TYPE_TX: @@ -687,6 +690,7 @@ static int dpaa_alloc_all_fqs(struct device *dev, struct list_head *list, struct fm_port_fqs *port_fqs) { struct dpaa_fq *dpaa_fq; + u32 fq_base, fq_base_aligned, i; dpaa_fq = dpaa_fq_alloc(dev, 0, 1, list, FQ_TYPE_RX_ERROR); if (!dpaa_fq) @@ -700,6 +704,26 @@ static int dpaa_alloc_all_fqs(struct device *dev, struct list_head *list, port_fqs->rx_defq = &dpaa_fq[0]; + /* the PCD FQIDs range needs to be aligned for correct operation */ + if (qman_alloc_fqid_range(&fq_base, 2 * DPAA_ETH_PCD_RXQ_NUM)) + goto fq_alloc_failed; + + fq_base_aligned = ALIGN(fq_base, DPAA_ETH_PCD_RXQ_NUM); + + for (i = fq_base; i < fq_base_aligned; i++) + qman_release_fqid(i); + + for (i = fq_base_aligned + DPAA_ETH_PCD_RXQ_NUM; + i < (fq_base + 2 * DPAA_ETH_PCD_RXQ_NUM); i++) + qman_release_fqid(i); + + dpaa_fq = dpaa_fq_alloc(dev, fq_base_aligned, DPAA_ETH_PCD_RXQ_NUM, + list, FQ_TYPE_RX_PCD); + if (!dpaa_fq) + goto fq_alloc_failed; + + port_fqs->rx_pcdq = &dpaa_fq[0]; + if (!dpaa_fq_alloc(dev, 0, DPAA_ETH_TXQ_NUM, list, FQ_TYPE_TX_CONF_MQ)) goto fq_alloc_failed; @@ -869,13 +893,14 @@ static void dpaa_fq_setup(struct dpaa_priv *priv, const struct dpaa_fq_cbs *fq_cbs, struct fman_port *tx_port) { - int egress_cnt = 0, conf_cnt = 0, num_portals = 0, cpu; + int egress_cnt = 0, conf_cnt = 0, num_portals = 0, portal_cnt = 0, cpu; const cpumask_t *affine_cpus = qman_affine_cpus(); - u16 portals[NR_CPUS]; + u16 channels[NR_CPUS]; struct dpaa_fq *fq; for_each_cpu(cpu, affine_cpus) - portals[num_portals++] = qman_affine_channel(cpu); + channels[num_portals++] = qman_affine_channel(cpu); + if (num_portals == 0) dev_err(priv->net_dev->dev.parent, "No Qman software (affine) channels found"); @@ -889,6 +914,12 @@ static void dpaa_fq_setup(struct dpaa_priv *priv, case FQ_TYPE_RX_ERROR: dpaa_setup_ingress(priv, fq, &fq_cbs->rx_errq); break; + case FQ_TYPE_RX_PCD: + if (!num_portals) + continue; + dpaa_setup_ingress(priv, fq, &fq_cbs->rx_defq); + fq->channel = channels[portal_cnt++ % num_portals]; + break; case FQ_TYPE_TX: dpaa_setup_egress(priv, fq, tx_port, &fq_cbs->egress_ern); @@ -1038,7 +1069,8 @@ static int dpaa_fq_init(struct dpaa_fq *dpaa_fq, bool td_enable) /* Put all the ingress queues in our "ingress CGR". */ if (priv->use_ingress_cgr && (dpaa_fq->fq_type == FQ_TYPE_RX_DEFAULT || - dpaa_fq->fq_type == FQ_TYPE_RX_ERROR)) { + dpaa_fq->fq_type == FQ_TYPE_RX_ERROR || + dpaa_fq->fq_type == FQ_TYPE_RX_PCD)) { initfq.we_mask |= cpu_to_be16(QM_INITFQ_WE_CGID); initfq.fqd.fq_ctrl |= cpu_to_be16(QM_FQCTRL_CGE); initfq.fqd.cgid = (u8)priv->ingress_cgr.cgrid; @@ -1169,7 +1201,7 @@ static int dpaa_eth_init_tx_port(struct fman_port *port, struct dpaa_fq *errq, static int dpaa_eth_init_rx_port(struct fman_port *port, struct dpaa_bp **bps, size_t count, struct dpaa_fq *errq, - struct dpaa_fq *defq, + struct dpaa_fq *defq, struct dpaa_fq *pcdq, struct dpaa_buffer_layout *buf_layout) { struct fman_buffer_prefix_content buf_prefix_content; @@ -1189,6 +1221,10 @@ static int dpaa_eth_init_rx_port(struct fman_port *port, struct dpaa_bp **bps, rx_p = ¶ms.specific_params.rx_params; rx_p->err_fqid = errq->fqid; rx_p->dflt_fqid = defq->fqid; + if (pcdq) { + rx_p->pcd_base_fqid = pcdq->fqid; + rx_p->pcd_fqs_count = DPAA_ETH_PCD_RXQ_NUM; + } count = min(ARRAY_SIZE(rx_p->ext_buf_pools.ext_buf_pool), count); rx_p->ext_buf_pools.num_of_pools_used = (u8)count; @@ -1233,7 +1269,8 @@ static int dpaa_eth_init_ports(struct mac_device *mac_dev, return err; err = dpaa_eth_init_rx_port(rxport, bps, count, port_fqs->rx_errq, - port_fqs->rx_defq, &buf_layout[RX]); + port_fqs->rx_defq, port_fqs->rx_pcdq, + &buf_layout[RX]); return err; } @@ -2200,12 +2237,13 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal, dma_addr_t addr = qm_fd_addr(fd); enum qm_fd_format fd_format; struct net_device *net_dev; - u32 fd_status; + u32 fd_status, hash_offset; struct dpaa_bp *dpaa_bp; struct dpaa_priv *priv; unsigned int skb_len; struct sk_buff *skb; int *count_ptr; + void *vaddr; fd_status = be32_to_cpu(fd->status); fd_format = qm_fd_get_format(fd); @@ -2251,7 +2289,8 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal, dma_unmap_single(dpaa_bp->dev, addr, dpaa_bp->size, DMA_FROM_DEVICE); /* prefetch the first 64 bytes of the frame or the SGT start */ - prefetch(phys_to_virt(addr) + qm_fd_get_offset(fd)); + vaddr = phys_to_virt(addr); + prefetch(vaddr + qm_fd_get_offset(fd)); fd_format = qm_fd_get_format(fd); /* The only FD types that we may receive are contig and S/G */ @@ -2272,6 +2311,18 @@ static enum qman_cb_dqrr_result rx_default_dqrr(struct qman_portal *portal, skb->protocol = eth_type_trans(skb, net_dev); + if (net_dev->features & NETIF_F_RXHASH && priv->keygen_in_use && + !fman_port_get_hash_result_offset(priv->mac_dev->port[RX], + &hash_offset)) { + enum pkt_hash_types type; + + /* if L4 exists, it was used in the hash generation */ + type = be32_to_cpu(fd->status) & FM_FD_STAT_L4CV ? + PKT_HASH_TYPE_L4 : PKT_HASH_TYPE_L3; + skb_set_hash(skb, be32_to_cpu(*(u32 *)(vaddr + hash_offset)), + type); + } + skb_len = skb->len; if (unlikely(netif_receive_skb(skb) == NET_RX_DROP)) @@ -2510,6 +2561,9 @@ static struct dpaa_bp *dpaa_bp_alloc(struct device *dev) dpaa_bp->bpid = FSL_DPAA_BPID_INV; dpaa_bp->percpu_count = devm_alloc_percpu(dev, *dpaa_bp->percpu_count); + if (!dpaa_bp->percpu_count) + return ERR_PTR(-ENOMEM); + dpaa_bp->config_count = FSL_DPAA_ETH_MAX_BUF_COUNT; dpaa_bp->seed_cb = dpaa_bp_seed; @@ -2737,6 +2791,9 @@ static int dpaa_eth_probe(struct platform_device *pdev) if (err) goto init_ports_failed; + /* Rx traffic distribution based on keygen hashing defaults to on */ + priv->keygen_in_use = true; + priv->percpu_priv = devm_alloc_percpu(dev, *priv->percpu_priv); if (!priv->percpu_priv) { dev_err(dev, "devm_alloc_percpu() failed\n"); @@ -2829,7 +2886,7 @@ static int dpaa_remove(struct platform_device *pdev) return err; } -static struct platform_device_id dpaa_devtype[] = { +static const struct platform_device_id dpaa_devtype[] = { { .name = "dpaa-ethernet", .driver_data = 0, diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h index 9941a7866ebe..bd9422082f83 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.h @@ -52,6 +52,7 @@ enum dpaa_fq_type { FQ_TYPE_RX_DEFAULT = 1, /* Rx Default FQs */ FQ_TYPE_RX_ERROR, /* Rx Error FQs */ + FQ_TYPE_RX_PCD, /* Rx Parse Classify Distribute FQs */ FQ_TYPE_TX, /* "Real" Tx FQs */ FQ_TYPE_TX_CONFIRM, /* Tx default Conf FQ (actually an Rx FQ) */ FQ_TYPE_TX_CONF_MQ, /* Tx conf FQs (one for each Tx FQ) */ @@ -158,6 +159,7 @@ struct dpaa_priv { struct list_head dpaa_fq_list; u8 num_tc; + bool keygen_in_use; u32 msg_enable; /* net_device message level */ struct { diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c index ec75d1c6fa89..0d9b185e317f 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth_sysfs.c @@ -71,6 +71,9 @@ static ssize_t dpaa_eth_show_fqids(struct device *dev, case FQ_TYPE_RX_ERROR: str = "Rx error"; break; + case FQ_TYPE_RX_PCD: + str = "Rx PCD"; + break; case FQ_TYPE_TX_CONFIRM: str = "Tx default confirmation"; break; diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c index aad825088357..faea674094b9 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c @@ -399,6 +399,122 @@ static void dpaa_get_strings(struct net_device *net_dev, u32 stringset, memcpy(strings, dpaa_stats_global, size); } +static int dpaa_get_hash_opts(struct net_device *dev, + struct ethtool_rxnfc *cmd) +{ + struct dpaa_priv *priv = netdev_priv(dev); + + cmd->data = 0; + + switch (cmd->flow_type) { + case TCP_V4_FLOW: + case TCP_V6_FLOW: + case UDP_V4_FLOW: + case UDP_V6_FLOW: + if (priv->keygen_in_use) + cmd->data |= RXH_L4_B_0_1 | RXH_L4_B_2_3; + /* Fall through */ + case IPV4_FLOW: + case IPV6_FLOW: + case SCTP_V4_FLOW: + case SCTP_V6_FLOW: + case AH_ESP_V4_FLOW: + case AH_ESP_V6_FLOW: + case AH_V4_FLOW: + case AH_V6_FLOW: + case ESP_V4_FLOW: + case ESP_V6_FLOW: + if (priv->keygen_in_use) + cmd->data |= RXH_IP_SRC | RXH_IP_DST; + break; + default: + cmd->data = 0; + break; + } + + return 0; +} + +static int dpaa_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, + u32 *unused) +{ + int ret = -EOPNOTSUPP; + + switch (cmd->cmd) { + case ETHTOOL_GRXFH: + ret = dpaa_get_hash_opts(dev, cmd); + break; + default: + break; + } + + return ret; +} + +static void dpaa_set_hash(struct net_device *net_dev, bool enable) +{ + struct mac_device *mac_dev; + struct fman_port *rxport; + struct dpaa_priv *priv; + + priv = netdev_priv(net_dev); + mac_dev = priv->mac_dev; + rxport = mac_dev->port[0]; + + fman_port_use_kg_hash(rxport, enable); + priv->keygen_in_use = enable; +} + +static int dpaa_set_hash_opts(struct net_device *dev, + struct ethtool_rxnfc *nfc) +{ + int ret = -EINVAL; + + /* we support hashing on IPv4/v6 src/dest IP and L4 src/dest port */ + if (nfc->data & + ~(RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3)) + return -EINVAL; + + switch (nfc->flow_type) { + case TCP_V4_FLOW: + case TCP_V6_FLOW: + case UDP_V4_FLOW: + case UDP_V6_FLOW: + case IPV4_FLOW: + case IPV6_FLOW: + case SCTP_V4_FLOW: + case SCTP_V6_FLOW: + case AH_ESP_V4_FLOW: + case AH_ESP_V6_FLOW: + case AH_V4_FLOW: + case AH_V6_FLOW: + case ESP_V4_FLOW: + case ESP_V6_FLOW: + dpaa_set_hash(dev, !!nfc->data); + ret = 0; + break; + default: + break; + } + + return ret; +} + +static int dpaa_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd) +{ + int ret = -EOPNOTSUPP; + + switch (cmd->cmd) { + case ETHTOOL_SRXFH: + ret = dpaa_set_hash_opts(dev, cmd); + break; + default: + break; + } + + return ret; +} + const struct ethtool_ops dpaa_ethtool_ops = { .get_drvinfo = dpaa_get_drvinfo, .get_msglevel = dpaa_get_msglevel, @@ -412,4 +528,6 @@ const struct ethtool_ops dpaa_ethtool_ops = { .get_strings = dpaa_get_strings, .get_link_ksettings = dpaa_get_link_ksettings, .set_link_ksettings = dpaa_set_link_ksettings, + .get_rxnfc = dpaa_get_rxnfc, + .set_rxnfc = dpaa_set_rxnfc, }; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index a6e323f15637..56f56d6ada9c 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -173,10 +173,12 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #endif /* CONFIG_M5272 */ /* The FEC stores dest/src/type/vlan, data, and checksum for receive packets. + * + * 2048 byte skbufs are allocated. However, alignment requirements + * varies between FEC variants. Worst case is 64, so round down by 64. */ -#define PKT_MAXBUF_SIZE 1522 +#define PKT_MAXBUF_SIZE (round_down(2048 - 64, 64)) #define PKT_MINBUF_SIZE 64 -#define PKT_MAXBLR_SIZE 1536 /* FEC receive acceleration */ #define FEC_RACC_IPDIS (1 << 1) @@ -224,7 +226,6 @@ MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); #define COPYBREAK_DEFAULT 256 -#define TSO_HEADER_SIZE 128 /* Max number of allowed TCP segments for software TSO */ #define FEC_MAX_TSO_SEGS 100 #define FEC_MAX_SKB_DESCS (FEC_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) @@ -851,7 +852,7 @@ static void fec_enet_enable_ring(struct net_device *ndev) for (i = 0; i < fep->num_rx_queues; i++) { rxq = fep->rx_queue[i]; writel(rxq->bd.dma, fep->hwp + FEC_R_DES_START(i)); - writel(PKT_MAXBLR_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i)); + writel(PKT_MAXBUF_SIZE, fep->hwp + FEC_R_BUFF_SIZE(i)); /* enable DMA1/2 */ if (i) @@ -1904,8 +1905,10 @@ static int fec_enet_mii_probe(struct net_device *ndev) phy_dev = of_phy_connect(ndev, fep->phy_node, &fec_enet_adjust_link, 0, fep->phy_interface); - if (!phy_dev) + if (!phy_dev) { + netdev_err(ndev, "Unable to connect to phy\n"); return -ENODEV; + } } else { /* check for attached phy */ for (phy_id = 0; (phy_id < PHY_MAX_ADDR); phy_id++) { diff --git a/drivers/net/ethernet/freescale/fec_mpc52xx.c b/drivers/net/ethernet/freescale/fec_mpc52xx.c index aa8cf5d2a53c..6d7269d87a85 100644 --- a/drivers/net/ethernet/freescale/fec_mpc52xx.c +++ b/drivers/net/ethernet/freescale/fec_mpc52xx.c @@ -960,8 +960,8 @@ static int mpc52xx_fec_probe(struct platform_device *op) /* We're done ! */ platform_set_drvdata(op, ndev); - netdev_info(ndev, "%s MAC %pM\n", - op->dev.of_node->full_name, ndev->dev_addr); + netdev_info(ndev, "%pOF MAC %pM\n", + op->dev.of_node, ndev->dev_addr); return 0; diff --git a/drivers/net/ethernet/freescale/fman/Makefile b/drivers/net/ethernet/freescale/fman/Makefile index 60491779e49f..2c38119b172c 100644 --- a/drivers/net/ethernet/freescale/fman/Makefile +++ b/drivers/net/ethernet/freescale/fman/Makefile @@ -4,6 +4,6 @@ obj-$(CONFIG_FSL_FMAN) += fsl_fman.o obj-$(CONFIG_FSL_FMAN) += fsl_fman_port.o obj-$(CONFIG_FSL_FMAN) += fsl_mac.o -fsl_fman-objs := fman_muram.o fman.o fman_sp.o +fsl_fman-objs := fman_muram.o fman.o fman_sp.o fman_keygen.o fsl_fman_port-objs := fman_port.o fsl_mac-objs:= mac.o fman_dtsec.o fman_memac.o fman_tgec.o diff --git a/drivers/net/ethernet/freescale/fman/fman.c b/drivers/net/ethernet/freescale/fman/fman.c index 4aefe2438969..9530405030a7 100644 --- a/drivers/net/ethernet/freescale/fman/fman.c +++ b/drivers/net/ethernet/freescale/fman/fman.c @@ -32,9 +32,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include "fman.h" -#include "fman_muram.h" - #include <linux/fsl/guts.h> #include <linux/slab.h> #include <linux/delay.h> @@ -46,6 +43,10 @@ #include <linux/interrupt.h> #include <linux/libfdt_env.h> +#include "fman.h" +#include "fman_muram.h" +#include "fman_keygen.h" + /* General defines */ #define FMAN_LIODN_TBL 64 /* size of LIODN table */ #define MAX_NUM_OF_MACS 10 @@ -56,6 +57,7 @@ /* Modules registers offsets */ #define BMI_OFFSET 0x00080000 #define QMI_OFFSET 0x00080400 +#define KG_OFFSET 0x000C1000 #define DMA_OFFSET 0x000C2000 #define FPM_OFFSET 0x000C3000 #define IMEM_OFFSET 0x000C4000 @@ -564,80 +566,6 @@ struct fman_cfg { u32 qmi_def_tnums_thresh; }; -/* Structure that holds information received from device tree */ -struct fman_dts_params { - void __iomem *base_addr; /* FMan virtual address */ - struct resource *res; /* FMan memory resource */ - u8 id; /* FMan ID */ - - int err_irq; /* FMan Error IRQ */ - - u16 clk_freq; /* FMan clock freq (In Mhz) */ - - u32 qman_channel_base; /* QMan channels base */ - u32 num_of_qman_channels; /* Number of QMan channels */ - - struct resource muram_res; /* MURAM resource */ -}; - -/** fman_exceptions_cb - * fman - Pointer to FMan - * exception - The exception. - * - * Exceptions user callback routine, will be called upon an exception - * passing the exception identification. - * - * Return: irq status - */ -typedef irqreturn_t (fman_exceptions_cb)(struct fman *fman, - enum fman_exceptions exception); - -/** fman_bus_error_cb - * fman - Pointer to FMan - * port_id - Port id - * addr - Address that caused the error - * tnum - Owner of error - * liodn - Logical IO device number - * - * Bus error user callback routine, will be called upon bus error, - * passing parameters describing the errors and the owner. - * - * Return: IRQ status - */ -typedef irqreturn_t (fman_bus_error_cb)(struct fman *fman, u8 port_id, - u64 addr, u8 tnum, u16 liodn); - -struct fman { - struct device *dev; - void __iomem *base_addr; - struct fman_intr_src intr_mng[FMAN_EV_CNT]; - - struct fman_fpm_regs __iomem *fpm_regs; - struct fman_bmi_regs __iomem *bmi_regs; - struct fman_qmi_regs __iomem *qmi_regs; - struct fman_dma_regs __iomem *dma_regs; - struct fman_hwp_regs __iomem *hwp_regs; - fman_exceptions_cb *exception_cb; - fman_bus_error_cb *bus_error_cb; - /* Spinlock for FMan use */ - spinlock_t spinlock; - struct fman_state_struct *state; - - struct fman_cfg *cfg; - struct muram_info *muram; - /* cam section in muram */ - unsigned long cam_offset; - size_t cam_size; - /* Fifo in MURAM */ - unsigned long fifo_offset; - size_t fifo_size; - - u32 liodn_base[64]; - u32 liodn_offset[64]; - - struct fman_dts_params dts_params; -}; - static irqreturn_t fman_exceptions(struct fman *fman, enum fman_exceptions exception) { @@ -1811,6 +1739,7 @@ static int fman_config(struct fman *fman) fman->qmi_regs = base_addr + QMI_OFFSET; fman->dma_regs = base_addr + DMA_OFFSET; fman->hwp_regs = base_addr + HWP_OFFSET; + fman->kg_regs = base_addr + KG_OFFSET; fman->base_addr = base_addr; spin_lock_init(&fman->spinlock); @@ -1925,8 +1854,8 @@ static int fman_reset(struct fman *fman) guts_regs = of_iomap(guts_node, 0); if (!guts_regs) { - dev_err(fman->dev, "%s: Couldn't map %s regs\n", - __func__, guts_node->full_name); + dev_err(fman->dev, "%s: Couldn't map %pOF regs\n", + __func__, guts_node); goto guts_regs; } #define FMAN1_ALL_MACS_MASK 0xFCC00000 @@ -2083,6 +2012,11 @@ static int fman_init(struct fman *fman) /* Init HW Parser */ hwp_init(fman->hwp_regs); + /* Init KeyGen */ + fman->keygen = keygen_init(fman->kg_regs); + if (!fman->keygen) + return -EINVAL; + err = enable(fman, cfg); if (err != 0) return err; @@ -2434,15 +2368,21 @@ u32 fman_get_qman_channel_id(struct fman *fman, u32 port_id) int i; if (fman->state->rev_info.major >= 6) { - u32 port_ids[] = {0x30, 0x31, 0x28, 0x29, 0x2a, 0x2b, - 0x2c, 0x2d, 0x2, 0x3, 0x4, 0x5, 0x7, 0x7}; + static const u32 port_ids[] = { + 0x30, 0x31, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2, 0x3, 0x4, 0x5, 0x7, 0x7 + }; + for (i = 0; i < fman->state->num_of_qman_channels; i++) { if (port_ids[i] == port_id) break; } } else { - u32 port_ids[] = {0x30, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x1, - 0x2, 0x3, 0x4, 0x5, 0x7, 0x7}; + static const u32 port_ids[] = { + 0x30, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x1, + 0x2, 0x3, 0x4, 0x5, 0x7, 0x7 + }; + for (i = 0; i < fman->state->num_of_qman_channels; i++) { if (port_ids[i] == port_id) break; @@ -2780,8 +2720,8 @@ static struct fman *read_dts_node(struct platform_device *of_dev) err = of_property_read_u32(fm_node, "cell-index", &val); if (err) { - dev_err(&of_dev->dev, "%s: failed to read cell-index for %s\n", - __func__, fm_node->full_name); + dev_err(&of_dev->dev, "%s: failed to read cell-index for %pOF\n", + __func__, fm_node); goto fman_node_put; } fman->dts_params.id = (u8)val; @@ -2834,8 +2774,8 @@ static struct fman *read_dts_node(struct platform_device *of_dev) err = of_property_read_u32_array(fm_node, "fsl,qman-channel-range", &range[0], 2); if (err) { - dev_err(&of_dev->dev, "%s: failed to read fsl,qman-channel-range for %s\n", - __func__, fm_node->full_name); + dev_err(&of_dev->dev, "%s: failed to read fsl,qman-channel-range for %pOF\n", + __func__, fm_node); goto fman_node_put; } fman->dts_params.qman_channel_base = range[0]; diff --git a/drivers/net/ethernet/freescale/fman/fman.h b/drivers/net/ethernet/freescale/fman/fman.h index f53e1473dbcc..bfa02e0014ae 100644 --- a/drivers/net/ethernet/freescale/fman/fman.h +++ b/drivers/net/ethernet/freescale/fman/fman.h @@ -34,6 +34,8 @@ #define __FM_H #include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/of_irq.h> /* FM Frame descriptor macros */ /* Frame queue Context Override */ @@ -274,6 +276,81 @@ struct fman_intr_src { void *src_handle; }; +/** fman_exceptions_cb + * fman - Pointer to FMan + * exception - The exception. + * + * Exceptions user callback routine, will be called upon an exception + * passing the exception identification. + * + * Return: irq status + */ +typedef irqreturn_t (fman_exceptions_cb)(struct fman *fman, + enum fman_exceptions exception); +/** fman_bus_error_cb + * fman - Pointer to FMan + * port_id - Port id + * addr - Address that caused the error + * tnum - Owner of error + * liodn - Logical IO device number + * + * Bus error user callback routine, will be called upon bus error, + * passing parameters describing the errors and the owner. + * + * Return: IRQ status + */ +typedef irqreturn_t (fman_bus_error_cb)(struct fman *fman, u8 port_id, + u64 addr, u8 tnum, u16 liodn); + +/* Structure that holds information received from device tree */ +struct fman_dts_params { + void __iomem *base_addr; /* FMan virtual address */ + struct resource *res; /* FMan memory resource */ + u8 id; /* FMan ID */ + + int err_irq; /* FMan Error IRQ */ + + u16 clk_freq; /* FMan clock freq (In Mhz) */ + + u32 qman_channel_base; /* QMan channels base */ + u32 num_of_qman_channels; /* Number of QMan channels */ + + struct resource muram_res; /* MURAM resource */ +}; + +struct fman { + struct device *dev; + void __iomem *base_addr; + struct fman_intr_src intr_mng[FMAN_EV_CNT]; + + struct fman_fpm_regs __iomem *fpm_regs; + struct fman_bmi_regs __iomem *bmi_regs; + struct fman_qmi_regs __iomem *qmi_regs; + struct fman_dma_regs __iomem *dma_regs; + struct fman_hwp_regs __iomem *hwp_regs; + struct fman_kg_regs __iomem *kg_regs; + fman_exceptions_cb *exception_cb; + fman_bus_error_cb *bus_error_cb; + /* Spinlock for FMan use */ + spinlock_t spinlock; + struct fman_state_struct *state; + + struct fman_cfg *cfg; + struct muram_info *muram; + struct fman_keygen *keygen; + /* cam section in muram */ + unsigned long cam_offset; + size_t cam_size; + /* Fifo in MURAM */ + unsigned long fifo_offset; + size_t fifo_size; + + u32 liodn_base[64]; + u32 liodn_offset[64]; + + struct fman_dts_params dts_params; +}; + /* Structure for port-FM communication during fman_port_init. */ struct fman_port_init_params { u8 port_id; /* port Id */ diff --git a/drivers/net/ethernet/freescale/fman/fman_dtsec.c b/drivers/net/ethernet/freescale/fman/fman_dtsec.c index 98bba10fc38c..ea43b4974149 100644 --- a/drivers/net/ethernet/freescale/fman/fman_dtsec.c +++ b/drivers/net/ethernet/freescale/fman/fman_dtsec.c @@ -123,7 +123,7 @@ #define DTSEC_ECNTRL_R100M 0x00000008 #define DTSEC_ECNTRL_QSGMIIM 0x00000001 -#define DTSEC_TCTRL_GTS 0x00000020 +#define TCTRL_GTS 0x00000020 #define RCTRL_PAL_MASK 0x001f0000 #define RCTRL_PAL_SHIFT 16 @@ -863,6 +863,52 @@ int dtsec_cfg_pad_and_crc(struct fman_mac *dtsec, bool new_val) return 0; } +static void graceful_start(struct fman_mac *dtsec, enum comm_mode mode) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + + if (mode & COMM_MODE_TX) + iowrite32be(ioread32be(®s->tctrl) & + ~TCTRL_GTS, ®s->tctrl); + if (mode & COMM_MODE_RX) + iowrite32be(ioread32be(®s->rctrl) & + ~RCTRL_GRS, ®s->rctrl); +} + +static void graceful_stop(struct fman_mac *dtsec, enum comm_mode mode) +{ + struct dtsec_regs __iomem *regs = dtsec->regs; + u32 tmp; + + /* Graceful stop - Assert the graceful Rx stop bit */ + if (mode & COMM_MODE_RX) { + tmp = ioread32be(®s->rctrl) | RCTRL_GRS; + iowrite32be(tmp, ®s->rctrl); + + if (dtsec->fm_rev_info.major == 2) { + /* Workaround for dTSEC Errata A002 */ + usleep_range(100, 200); + } else { + /* Workaround for dTSEC Errata A004839 */ + usleep_range(10, 50); + } + } + + /* Graceful stop - Assert the graceful Tx stop bit */ + if (mode & COMM_MODE_TX) { + if (dtsec->fm_rev_info.major == 2) { + /* dTSEC Errata A004: Do not use TCTRL[GTS]=1 */ + pr_debug("GTS not supported due to DTSEC_A004 Errata.\n"); + } else { + tmp = ioread32be(®s->tctrl) | TCTRL_GTS; + iowrite32be(tmp, ®s->tctrl); + + /* Workaround for dTSEC Errata A0012, A0014 */ + usleep_range(10, 50); + } + } +} + int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode) { struct dtsec_regs __iomem *regs = dtsec->regs; @@ -880,13 +926,8 @@ int dtsec_enable(struct fman_mac *dtsec, enum comm_mode mode) iowrite32be(tmp, ®s->maccfg1); - /* Graceful start - clear the graceful receive stop bit */ - if (mode & COMM_MODE_TX) - iowrite32be(ioread32be(®s->tctrl) & ~DTSEC_TCTRL_GTS, - ®s->tctrl); - if (mode & COMM_MODE_RX) - iowrite32be(ioread32be(®s->rctrl) & ~RCTRL_GRS, - ®s->rctrl); + /* Graceful start - clear the graceful Rx/Tx stop bit */ + graceful_start(dtsec, mode); return 0; } @@ -899,23 +940,8 @@ int dtsec_disable(struct fman_mac *dtsec, enum comm_mode mode) if (!is_init_done(dtsec->dtsec_drv_param)) return -EINVAL; - /* Gracefull stop - Assert the graceful transmit stop bit */ - if (mode & COMM_MODE_RX) { - tmp = ioread32be(®s->rctrl) | RCTRL_GRS; - iowrite32be(tmp, ®s->rctrl); - - if (dtsec->fm_rev_info.major == 2) - usleep_range(100, 200); - else - udelay(10); - } - - if (mode & COMM_MODE_TX) { - if (dtsec->fm_rev_info.major == 2) - pr_debug("GTS not supported due to DTSEC_A004 errata.\n"); - else - pr_debug("GTS not supported due to DTSEC_A0014 errata.\n"); - } + /* Graceful stop - Assert the graceful Rx/Tx stop bit */ + graceful_stop(dtsec, mode); tmp = ioread32be(®s->maccfg1); if (mode & COMM_MODE_RX) @@ -933,11 +959,19 @@ int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, u16 pause_time, u16 __maybe_unused thresh_time) { struct dtsec_regs __iomem *regs = dtsec->regs; + enum comm_mode mode = COMM_MODE_NONE; u32 ptv = 0; if (!is_init_done(dtsec->dtsec_drv_param)) return -EINVAL; + if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) + mode |= COMM_MODE_RX; + if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) + mode |= COMM_MODE_TX; + + graceful_stop(dtsec, mode); + if (pause_time) { /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */ if (dtsec->fm_rev_info.major == 2 && pause_time <= 320) { @@ -958,17 +992,27 @@ int dtsec_set_tx_pause_frames(struct fman_mac *dtsec, iowrite32be(ioread32be(®s->maccfg1) & ~MACCFG1_TX_FLOW, ®s->maccfg1); + graceful_start(dtsec, mode); + return 0; } int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en) { struct dtsec_regs __iomem *regs = dtsec->regs; + enum comm_mode mode = COMM_MODE_NONE; u32 tmp; if (!is_init_done(dtsec->dtsec_drv_param)) return -EINVAL; + if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) + mode |= COMM_MODE_RX; + if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) + mode |= COMM_MODE_TX; + + graceful_stop(dtsec, mode); + tmp = ioread32be(®s->maccfg1); if (en) tmp |= MACCFG1_RX_FLOW; @@ -976,20 +1020,34 @@ int dtsec_accept_rx_pause_frames(struct fman_mac *dtsec, bool en) tmp &= ~MACCFG1_RX_FLOW; iowrite32be(tmp, ®s->maccfg1); + graceful_start(dtsec, mode); + return 0; } int dtsec_modify_mac_address(struct fman_mac *dtsec, enet_addr_t *enet_addr) { + struct dtsec_regs __iomem *regs = dtsec->regs; + enum comm_mode mode = COMM_MODE_NONE; + if (!is_init_done(dtsec->dtsec_drv_param)) return -EINVAL; + if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) + mode |= COMM_MODE_RX; + if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) + mode |= COMM_MODE_TX; + + graceful_stop(dtsec, mode); + /* Initialize MAC Station Address registers (1 & 2) * Station address have to be swapped (big endian to little endian */ dtsec->addr = ENET_ADDR_TO_UINT64(*enet_addr); set_mac_address(dtsec->regs, (u8 *)(*enet_addr)); + graceful_start(dtsec, mode); + return 0; } @@ -1162,11 +1220,19 @@ int dtsec_set_promiscuous(struct fman_mac *dtsec, bool new_val) int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed) { struct dtsec_regs __iomem *regs = dtsec->regs; + enum comm_mode mode = COMM_MODE_NONE; u32 tmp; if (!is_init_done(dtsec->dtsec_drv_param)) return -EINVAL; + if ((ioread32be(®s->rctrl) & RCTRL_GRS) == 0) + mode |= COMM_MODE_RX; + if ((ioread32be(®s->tctrl) & TCTRL_GTS) == 0) + mode |= COMM_MODE_TX; + + graceful_stop(dtsec, mode); + tmp = ioread32be(®s->maccfg2); /* Full Duplex */ @@ -1186,6 +1252,8 @@ int dtsec_adjust_link(struct fman_mac *dtsec, u16 speed) tmp &= ~DTSEC_ECNTRL_R100M; iowrite32be(tmp, ®s->ecntrl); + graceful_start(dtsec, mode); + return 0; } diff --git a/drivers/net/ethernet/freescale/fman/fman_keygen.c b/drivers/net/ethernet/freescale/fman/fman_keygen.c new file mode 100644 index 000000000000..f54da3c684d0 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_keygen.c @@ -0,0 +1,783 @@ +/* + * Copyright 2017 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NXP nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY NXP ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NXP BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/slab.h> + +#include "fman_keygen.h" + +/* Maximum number of HW Ports */ +#define FMAN_MAX_NUM_OF_HW_PORTS 64 + +/* Maximum number of KeyGen Schemes */ +#define FM_KG_MAX_NUM_OF_SCHEMES 32 + +/* Number of generic KeyGen Generic Extract Command Registers */ +#define FM_KG_NUM_OF_GENERIC_REGS 8 + +/* Dummy port ID */ +#define DUMMY_PORT_ID 0 + +/* Select Scheme Value Register */ +#define KG_SCH_DEF_USE_KGSE_DV_0 2 +#define KG_SCH_DEF_USE_KGSE_DV_1 3 + +/* Registers Shifting values */ +#define FM_KG_KGAR_NUM_SHIFT 16 +#define KG_SCH_DEF_L4_PORT_SHIFT 8 +#define KG_SCH_DEF_IP_ADDR_SHIFT 18 +#define KG_SCH_HASH_CONFIG_SHIFT_SHIFT 24 + +/* KeyGen Registers bit field masks: */ + +/* Enable bit field mask for KeyGen General Configuration Register */ +#define FM_KG_KGGCR_EN 0x80000000 + +/* KeyGen Global Registers bit field masks */ +#define FM_KG_KGAR_GO 0x80000000 +#define FM_KG_KGAR_READ 0x40000000 +#define FM_KG_KGAR_WRITE 0x00000000 +#define FM_KG_KGAR_SEL_SCHEME_ENTRY 0x00000000 +#define FM_KG_KGAR_SCM_WSEL_UPDATE_CNT 0x00008000 + +#define FM_KG_KGAR_ERR 0x20000000 +#define FM_KG_KGAR_SEL_CLS_PLAN_ENTRY 0x01000000 +#define FM_KG_KGAR_SEL_PORT_ENTRY 0x02000000 +#define FM_KG_KGAR_SEL_PORT_WSEL_SP 0x00008000 +#define FM_KG_KGAR_SEL_PORT_WSEL_CPP 0x00004000 + +/* Error events exceptions */ +#define FM_EX_KG_DOUBLE_ECC 0x80000000 +#define FM_EX_KG_KEYSIZE_OVERFLOW 0x40000000 + +/* Scheme Registers bit field masks */ +#define KG_SCH_MODE_EN 0x80000000 +#define KG_SCH_VSP_NO_KSP_EN 0x80000000 +#define KG_SCH_HASH_CONFIG_SYM 0x40000000 + +/* Known Protocol field codes */ +#define KG_SCH_KN_PORT_ID 0x80000000 +#define KG_SCH_KN_MACDST 0x40000000 +#define KG_SCH_KN_MACSRC 0x20000000 +#define KG_SCH_KN_TCI1 0x10000000 +#define KG_SCH_KN_TCI2 0x08000000 +#define KG_SCH_KN_ETYPE 0x04000000 +#define KG_SCH_KN_PPPSID 0x02000000 +#define KG_SCH_KN_PPPID 0x01000000 +#define KG_SCH_KN_MPLS1 0x00800000 +#define KG_SCH_KN_MPLS2 0x00400000 +#define KG_SCH_KN_MPLS_LAST 0x00200000 +#define KG_SCH_KN_IPSRC1 0x00100000 +#define KG_SCH_KN_IPDST1 0x00080000 +#define KG_SCH_KN_PTYPE1 0x00040000 +#define KG_SCH_KN_IPTOS_TC1 0x00020000 +#define KG_SCH_KN_IPV6FL1 0x00010000 +#define KG_SCH_KN_IPSRC2 0x00008000 +#define KG_SCH_KN_IPDST2 0x00004000 +#define KG_SCH_KN_PTYPE2 0x00002000 +#define KG_SCH_KN_IPTOS_TC2 0x00001000 +#define KG_SCH_KN_IPV6FL2 0x00000800 +#define KG_SCH_KN_GREPTYPE 0x00000400 +#define KG_SCH_KN_IPSEC_SPI 0x00000200 +#define KG_SCH_KN_IPSEC_NH 0x00000100 +#define KG_SCH_KN_IPPID 0x00000080 +#define KG_SCH_KN_L4PSRC 0x00000004 +#define KG_SCH_KN_L4PDST 0x00000002 +#define KG_SCH_KN_TFLG 0x00000001 + +/* NIA values */ +#define NIA_ENG_BMI 0x00500000 +#define NIA_BMI_AC_ENQ_FRAME 0x00000002 +#define ENQUEUE_KG_DFLT_NIA (NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME) + +/* Hard-coded configuration: + * These values are used as hard-coded values for KeyGen configuration + * and they replace user selections for this hard-coded version + */ + +/* Hash distribution shift */ +#define DEFAULT_HASH_DIST_FQID_SHIFT 0 + +/* Hash shift */ +#define DEFAULT_HASH_SHIFT 0 + +/* Symmetric hash usage: + * Warning: + * - the value for symmetric hash usage must be in accordance with hash + * key defined below + * - according to tests performed, spreading is not working if symmetric + * hash is set on true + * So ultimately symmetric hash functionality should be always disabled: + */ +#define DEFAULT_SYMMETRIC_HASH false + +/* Hash Key extraction fields: */ +#define DEFAULT_HASH_KEY_EXTRACT_FIELDS \ + (KG_SCH_KN_IPSRC1 | KG_SCH_KN_IPDST1 | \ + KG_SCH_KN_L4PSRC | KG_SCH_KN_L4PDST) + +/* Default values to be used as hash key in case IPv4 or L4 (TCP, UDP) + * don't exist in the frame + */ +/* Default IPv4 address */ +#define DEFAULT_HASH_KEY_IPv4_ADDR 0x0A0A0A0A +/* Default L4 port */ +#define DEFAULT_HASH_KEY_L4_PORT 0x0B0B0B0B + +/* KeyGen Memory Mapped Registers: */ + +/* Scheme Configuration RAM Registers */ +struct fman_kg_scheme_regs { + u32 kgse_mode; /* 0x100: MODE */ + u32 kgse_ekfc; /* 0x104: Extract Known Fields Command */ + u32 kgse_ekdv; /* 0x108: Extract Known Default Value */ + u32 kgse_bmch; /* 0x10C: Bit Mask Command High */ + u32 kgse_bmcl; /* 0x110: Bit Mask Command Low */ + u32 kgse_fqb; /* 0x114: Frame Queue Base */ + u32 kgse_hc; /* 0x118: Hash Command */ + u32 kgse_ppc; /* 0x11C: Policer Profile Command */ + u32 kgse_gec[FM_KG_NUM_OF_GENERIC_REGS]; + /* 0x120: Generic Extract Command */ + u32 kgse_spc; + /* 0x140: KeyGen Scheme Entry Statistic Packet Counter */ + u32 kgse_dv0; /* 0x144: KeyGen Scheme Entry Default Value 0 */ + u32 kgse_dv1; /* 0x148: KeyGen Scheme Entry Default Value 1 */ + u32 kgse_ccbs; + /* 0x14C: KeyGen Scheme Entry Coarse Classification Bit*/ + u32 kgse_mv; /* 0x150: KeyGen Scheme Entry Match vector */ + u32 kgse_om; /* 0x154: KeyGen Scheme Entry Operation Mode bits */ + u32 kgse_vsp; + /* 0x158: KeyGen Scheme Entry Virtual Storage Profile */ +}; + +/* Port Partition Configuration Registers */ +struct fman_kg_pe_regs { + u32 fmkg_pe_sp; /* 0x100: KeyGen Port entry Scheme Partition */ + u32 fmkg_pe_cpp; + /* 0x104: KeyGen Port Entry Classification Plan Partition */ +}; + +/* General Configuration and Status Registers + * Global Statistic Counters + * KeyGen Global Registers + */ +struct fman_kg_regs { + u32 fmkg_gcr; /* 0x000: KeyGen General Configuration Register */ + u32 res004; /* 0x004: Reserved */ + u32 res008; /* 0x008: Reserved */ + u32 fmkg_eer; /* 0x00C: KeyGen Error Event Register */ + u32 fmkg_eeer; /* 0x010: KeyGen Error Event Enable Register */ + u32 res014; /* 0x014: Reserved */ + u32 res018; /* 0x018: Reserved */ + u32 fmkg_seer; /* 0x01C: KeyGen Scheme Error Event Register */ + u32 fmkg_seeer; /* 0x020: KeyGen Scheme Error Event Enable Register */ + u32 fmkg_gsr; /* 0x024: KeyGen Global Status Register */ + u32 fmkg_tpc; /* 0x028: Total Packet Counter Register */ + u32 fmkg_serc; /* 0x02C: Soft Error Capture Register */ + u32 res030[4]; /* 0x030: Reserved */ + u32 fmkg_fdor; /* 0x034: Frame Data Offset Register */ + u32 fmkg_gdv0r; /* 0x038: Global Default Value Register 0 */ + u32 fmkg_gdv1r; /* 0x03C: Global Default Value Register 1 */ + u32 res04c[6]; /* 0x040: Reserved */ + u32 fmkg_feer; /* 0x044: Force Error Event Register */ + u32 res068[38]; /* 0x048: Reserved */ + union { + u32 fmkg_indirect[63]; /* 0x100: Indirect Access Registers */ + struct fman_kg_scheme_regs fmkg_sch; /* Scheme Registers */ + struct fman_kg_pe_regs fmkg_pe; /* Port Partition Registers */ + }; + u32 fmkg_ar; /* 0x1FC: KeyGen Action Register */ +}; + +/* KeyGen Scheme data */ +struct keygen_scheme { + bool used; /* Specifies if this scheme is used */ + u8 hw_port_id; + /* Hardware port ID + * schemes sharing between multiple ports is not + * currently supported + * so we have only one port id bound to a scheme + */ + u32 base_fqid; + /* Base FQID: + * Must be between 1 and 2^24-1 + * If hash is used and an even distribution is + * expected according to hash_fqid_count, + * base_fqid must be aligned to hash_fqid_count + */ + u32 hash_fqid_count; + /* FQ range for hash distribution: + * Must be a power of 2 + * Represents the range of queues for spreading + */ + bool use_hashing; /* Usage of Hashing and spreading over FQ */ + bool symmetric_hash; /* Symmetric Hash option usage */ + u8 hashShift; + /* Hash result right shift. + * Select the 24 bits out of the 64 hash result. + * 0 means using the 24 LSB's, otherwise + * use the 24 LSB's after shifting right + */ + u32 match_vector; /* Match Vector */ +}; + +/* KeyGen driver data */ +struct fman_keygen { + struct keygen_scheme schemes[FM_KG_MAX_NUM_OF_SCHEMES]; + /* Array of schemes */ + struct fman_kg_regs __iomem *keygen_regs; /* KeyGen registers */ +}; + +/* keygen_write_ar_wait + * + * Write Action Register with specified value, wait for GO bit field to be + * idle and then read the error + * + * regs: KeyGen registers + * fmkg_ar: Action Register value + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_write_ar_wait(struct fman_kg_regs __iomem *regs, u32 fmkg_ar) +{ + iowrite32be(fmkg_ar, ®s->fmkg_ar); + + /* Wait for GO bit field to be idle */ + while (fmkg_ar & FM_KG_KGAR_GO) + fmkg_ar = ioread32be(®s->fmkg_ar); + + if (fmkg_ar & FM_KG_KGAR_ERR) + return -EINVAL; + + return 0; +} + +/* build_ar_scheme + * + * Build Action Register value for scheme settings + * + * scheme_id: Scheme ID + * update_counter: update scheme counter + * write: true for action to write the scheme or false for read action + * + * Return: AR value + */ +static u32 build_ar_scheme(u8 scheme_id, bool update_counter, bool write) +{ + u32 rw = (u32)(write ? FM_KG_KGAR_WRITE : FM_KG_KGAR_READ); + + return (u32)(FM_KG_KGAR_GO | + rw | + FM_KG_KGAR_SEL_SCHEME_ENTRY | + DUMMY_PORT_ID | + ((u32)scheme_id << FM_KG_KGAR_NUM_SHIFT) | + (update_counter ? FM_KG_KGAR_SCM_WSEL_UPDATE_CNT : 0)); +} + +/* build_ar_bind_scheme + * + * Build Action Register value for port binding to schemes + * + * hwport_id: HW Port ID + * write: true for action to write the bind or false for read action + * + * Return: AR value + */ +static u32 build_ar_bind_scheme(u8 hwport_id, bool write) +{ + u32 rw = write ? (u32)FM_KG_KGAR_WRITE : (u32)FM_KG_KGAR_READ; + + return (u32)(FM_KG_KGAR_GO | + rw | + FM_KG_KGAR_SEL_PORT_ENTRY | + hwport_id | + FM_KG_KGAR_SEL_PORT_WSEL_SP); +} + +/* keygen_write_sp + * + * Write Scheme Partition Register with specified value + * + * regs: KeyGen Registers + * sp: Scheme Partition register value + * add: true to add a scheme partition or false to clear + * + * Return: none + */ +static void keygen_write_sp(struct fman_kg_regs __iomem *regs, u32 sp, bool add) +{ + u32 tmp; + + tmp = ioread32be(®s->fmkg_pe.fmkg_pe_sp); + + if (add) + tmp |= sp; + else + tmp &= ~sp; + + iowrite32be(tmp, ®s->fmkg_pe.fmkg_pe_sp); +} + +/* build_ar_bind_cls_plan + * + * Build Action Register value for Classification Plan + * + * hwport_id: HW Port ID + * write: true for action to write the CP or false for read action + * + * Return: AR value + */ +static u32 build_ar_bind_cls_plan(u8 hwport_id, bool write) +{ + u32 rw = write ? (u32)FM_KG_KGAR_WRITE : (u32)FM_KG_KGAR_READ; + + return (u32)(FM_KG_KGAR_GO | + rw | + FM_KG_KGAR_SEL_PORT_ENTRY | + hwport_id | + FM_KG_KGAR_SEL_PORT_WSEL_CPP); +} + +/* keygen_write_cpp + * + * Write Classification Plan Partition Register with specified value + * + * regs: KeyGen Registers + * cpp: CPP register value + * + * Return: none + */ +static void keygen_write_cpp(struct fman_kg_regs __iomem *regs, u32 cpp) +{ + iowrite32be(cpp, ®s->fmkg_pe.fmkg_pe_cpp); +} + +/* keygen_write_scheme + * + * Write all Schemes Registers with specified values + * + * regs: KeyGen Registers + * scheme_id: Scheme ID + * scheme_regs: Scheme registers values desired to be written + * update_counter: update scheme counter + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_write_scheme(struct fman_kg_regs __iomem *regs, u8 scheme_id, + struct fman_kg_scheme_regs *scheme_regs, + bool update_counter) +{ + u32 ar_reg; + int err, i; + + /* Write indirect scheme registers */ + iowrite32be(scheme_regs->kgse_mode, ®s->fmkg_sch.kgse_mode); + iowrite32be(scheme_regs->kgse_ekfc, ®s->fmkg_sch.kgse_ekfc); + iowrite32be(scheme_regs->kgse_ekdv, ®s->fmkg_sch.kgse_ekdv); + iowrite32be(scheme_regs->kgse_bmch, ®s->fmkg_sch.kgse_bmch); + iowrite32be(scheme_regs->kgse_bmcl, ®s->fmkg_sch.kgse_bmcl); + iowrite32be(scheme_regs->kgse_fqb, ®s->fmkg_sch.kgse_fqb); + iowrite32be(scheme_regs->kgse_hc, ®s->fmkg_sch.kgse_hc); + iowrite32be(scheme_regs->kgse_ppc, ®s->fmkg_sch.kgse_ppc); + iowrite32be(scheme_regs->kgse_spc, ®s->fmkg_sch.kgse_spc); + iowrite32be(scheme_regs->kgse_dv0, ®s->fmkg_sch.kgse_dv0); + iowrite32be(scheme_regs->kgse_dv1, ®s->fmkg_sch.kgse_dv1); + iowrite32be(scheme_regs->kgse_ccbs, ®s->fmkg_sch.kgse_ccbs); + iowrite32be(scheme_regs->kgse_mv, ®s->fmkg_sch.kgse_mv); + iowrite32be(scheme_regs->kgse_om, ®s->fmkg_sch.kgse_om); + iowrite32be(scheme_regs->kgse_vsp, ®s->fmkg_sch.kgse_vsp); + + for (i = 0 ; i < FM_KG_NUM_OF_GENERIC_REGS ; i++) + iowrite32be(scheme_regs->kgse_gec[i], + ®s->fmkg_sch.kgse_gec[i]); + + /* Write AR (Action register) */ + ar_reg = build_ar_scheme(scheme_id, update_counter, true); + err = keygen_write_ar_wait(regs, ar_reg); + if (err != 0) { + pr_err("Writing Action Register failed\n"); + return err; + } + + return err; +} + +/* get_free_scheme_id + * + * Find the first free scheme available to be used + * + * keygen: KeyGen handle + * scheme_id: pointer to scheme id + * + * Return: 0 on success, -EINVAL when the are no available free schemes + */ +static int get_free_scheme_id(struct fman_keygen *keygen, u8 *scheme_id) +{ + u8 i; + + for (i = 0; i < FM_KG_MAX_NUM_OF_SCHEMES; i++) + if (!keygen->schemes[i].used) { + *scheme_id = i; + return 0; + } + + return -EINVAL; +} + +/* get_scheme + * + * Provides the scheme for specified ID + * + * keygen: KeyGen handle + * scheme_id: Scheme ID + * + * Return: handle to required scheme + */ +static struct keygen_scheme *get_scheme(struct fman_keygen *keygen, + u8 scheme_id) +{ + if (scheme_id >= FM_KG_MAX_NUM_OF_SCHEMES) + return NULL; + return &keygen->schemes[scheme_id]; +} + +/* keygen_bind_port_to_schemes + * + * Bind the port to schemes + * + * keygen: KeyGen handle + * scheme_id: id of the scheme to bind to + * bind: true to bind the port or false to unbind it + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_bind_port_to_schemes(struct fman_keygen *keygen, + u8 scheme_id, + bool bind) +{ + struct fman_kg_regs __iomem *keygen_regs = keygen->keygen_regs; + struct keygen_scheme *scheme; + u32 ar_reg; + u32 schemes_vector = 0; + int err; + + scheme = get_scheme(keygen, scheme_id); + if (!scheme) { + pr_err("Requested Scheme does not exist\n"); + return -EINVAL; + } + if (!scheme->used) { + pr_err("Cannot bind port to an invalid scheme\n"); + return -EINVAL; + } + + schemes_vector |= 1 << (31 - scheme_id); + + ar_reg = build_ar_bind_scheme(scheme->hw_port_id, false); + err = keygen_write_ar_wait(keygen_regs, ar_reg); + if (err != 0) { + pr_err("Reading Action Register failed\n"); + return err; + } + + keygen_write_sp(keygen_regs, schemes_vector, bind); + + ar_reg = build_ar_bind_scheme(scheme->hw_port_id, true); + err = keygen_write_ar_wait(keygen_regs, ar_reg); + if (err != 0) { + pr_err("Writing Action Register failed\n"); + return err; + } + + return 0; +} + +/* keygen_scheme_setup + * + * Setup the scheme according to required configuration + * + * keygen: KeyGen handle + * scheme_id: scheme ID + * enable: true to enable scheme or false to disable it + * + * Return: Zero for success or error code in case of failure + */ +static int keygen_scheme_setup(struct fman_keygen *keygen, u8 scheme_id, + bool enable) +{ + struct fman_kg_regs __iomem *keygen_regs = keygen->keygen_regs; + struct fman_kg_scheme_regs scheme_regs; + struct keygen_scheme *scheme; + u32 tmp_reg; + int err; + + scheme = get_scheme(keygen, scheme_id); + if (!scheme) { + pr_err("Requested Scheme does not exist\n"); + return -EINVAL; + } + if (enable && scheme->used) { + pr_err("The requested Scheme is already used\n"); + return -EINVAL; + } + + /* Clear scheme registers */ + memset(&scheme_regs, 0, sizeof(struct fman_kg_scheme_regs)); + + /* Setup all scheme registers: */ + tmp_reg = 0; + + if (enable) { + /* Enable Scheme */ + tmp_reg |= KG_SCH_MODE_EN; + /* Enqueue frame NIA */ + tmp_reg |= ENQUEUE_KG_DFLT_NIA; + } + + scheme_regs.kgse_mode = tmp_reg; + + scheme_regs.kgse_mv = scheme->match_vector; + + /* Scheme don't override StorageProfile: + * valid only for DPAA_VERSION >= 11 + */ + scheme_regs.kgse_vsp = KG_SCH_VSP_NO_KSP_EN; + + /* Configure Hard-Coded Rx Hashing: */ + + if (scheme->use_hashing) { + /* configure kgse_ekfc */ + scheme_regs.kgse_ekfc = DEFAULT_HASH_KEY_EXTRACT_FIELDS; + + /* configure kgse_ekdv */ + tmp_reg = 0; + tmp_reg |= (KG_SCH_DEF_USE_KGSE_DV_0 << + KG_SCH_DEF_IP_ADDR_SHIFT); + tmp_reg |= (KG_SCH_DEF_USE_KGSE_DV_1 << + KG_SCH_DEF_L4_PORT_SHIFT); + scheme_regs.kgse_ekdv = tmp_reg; + + /* configure kgse_dv0 */ + scheme_regs.kgse_dv0 = DEFAULT_HASH_KEY_IPv4_ADDR; + /* configure kgse_dv1 */ + scheme_regs.kgse_dv1 = DEFAULT_HASH_KEY_L4_PORT; + + /* configure kgse_hc */ + tmp_reg = 0; + tmp_reg |= ((scheme->hash_fqid_count - 1) << + DEFAULT_HASH_DIST_FQID_SHIFT); + tmp_reg |= scheme->hashShift << KG_SCH_HASH_CONFIG_SHIFT_SHIFT; + + if (scheme->symmetric_hash) { + /* Normally extraction key should be verified if + * complies with symmetric hash + * But because extraction is hard-coded, we are sure + * the key is symmetric + */ + tmp_reg |= KG_SCH_HASH_CONFIG_SYM; + } + scheme_regs.kgse_hc = tmp_reg; + } else { + scheme_regs.kgse_ekfc = 0; + scheme_regs.kgse_hc = 0; + scheme_regs.kgse_ekdv = 0; + scheme_regs.kgse_dv0 = 0; + scheme_regs.kgse_dv1 = 0; + } + + /* configure kgse_fqb: Scheme FQID base */ + tmp_reg = 0; + tmp_reg |= scheme->base_fqid; + scheme_regs.kgse_fqb = tmp_reg; + + /* features not used by hard-coded configuration */ + scheme_regs.kgse_bmch = 0; + scheme_regs.kgse_bmcl = 0; + scheme_regs.kgse_spc = 0; + + /* Write scheme registers */ + err = keygen_write_scheme(keygen_regs, scheme_id, &scheme_regs, true); + if (err != 0) { + pr_err("Writing scheme registers failed\n"); + return err; + } + + /* Update used field for Scheme */ + scheme->used = enable; + + return 0; +} + +/* keygen_init + * + * KeyGen initialization: + * Initializes and enables KeyGen, allocate driver memory, setup registers, + * clear port bindings, invalidate all schemes + * + * keygen_regs: KeyGen registers base address + * + * Return: Handle to KeyGen driver + */ +struct fman_keygen *keygen_init(struct fman_kg_regs __iomem *keygen_regs) +{ + struct fman_keygen *keygen; + u32 ar; + int i; + + /* Allocate memory for KeyGen driver */ + keygen = kzalloc(sizeof(*keygen), GFP_KERNEL); + if (!keygen) + return NULL; + + keygen->keygen_regs = keygen_regs; + + /* KeyGen initialization (for Master partition): + * Setup KeyGen registers + */ + iowrite32be(ENQUEUE_KG_DFLT_NIA, &keygen_regs->fmkg_gcr); + + iowrite32be(FM_EX_KG_DOUBLE_ECC | FM_EX_KG_KEYSIZE_OVERFLOW, + &keygen_regs->fmkg_eer); + + iowrite32be(0, &keygen_regs->fmkg_fdor); + iowrite32be(0, &keygen_regs->fmkg_gdv0r); + iowrite32be(0, &keygen_regs->fmkg_gdv1r); + + /* Clear binding between ports to schemes and classification plans + * so that all ports are not bound to any scheme/classification plan + */ + for (i = 0; i < FMAN_MAX_NUM_OF_HW_PORTS; i++) { + /* Clear all pe sp schemes registers */ + keygen_write_sp(keygen_regs, 0xffffffff, false); + ar = build_ar_bind_scheme(i, true); + keygen_write_ar_wait(keygen_regs, ar); + + /* Clear all pe cpp classification plans registers */ + keygen_write_cpp(keygen_regs, 0); + ar = build_ar_bind_cls_plan(i, true); + keygen_write_ar_wait(keygen_regs, ar); + } + + /* Enable all scheme interrupts */ + iowrite32be(0xFFFFFFFF, &keygen_regs->fmkg_seer); + iowrite32be(0xFFFFFFFF, &keygen_regs->fmkg_seeer); + + /* Enable KyeGen */ + iowrite32be(ioread32be(&keygen_regs->fmkg_gcr) | FM_KG_KGGCR_EN, + &keygen_regs->fmkg_gcr); + + return keygen; +} +EXPORT_SYMBOL(keygen_init); + +/* keygen_port_hashing_init + * + * Initializes a port for Rx Hashing with specified configuration parameters + * + * keygen: KeyGen handle + * hw_port_id: HW Port ID + * hash_base_fqid: Hashing Base FQID used for spreading + * hash_size: Hashing size + * + * Return: Zero for success or error code in case of failure + */ +int keygen_port_hashing_init(struct fman_keygen *keygen, u8 hw_port_id, + u32 hash_base_fqid, u32 hash_size) +{ + struct keygen_scheme *scheme; + u8 scheme_id; + int err; + + /* Validate Scheme configuration parameters */ + if (hash_base_fqid == 0 || (hash_base_fqid & ~0x00FFFFFF)) { + pr_err("Base FQID must be between 1 and 2^24-1\n"); + return -EINVAL; + } + if (hash_size == 0 || (hash_size & (hash_size - 1)) != 0) { + pr_err("Hash size must be power of two\n"); + return -EINVAL; + } + + /* Find a free scheme */ + err = get_free_scheme_id(keygen, &scheme_id); + if (err) { + pr_err("The maximum number of available Schemes has been exceeded\n"); + return -EINVAL; + } + + /* Create and configure Hard-Coded Scheme: */ + + scheme = get_scheme(keygen, scheme_id); + if (!scheme) { + pr_err("Requested Scheme does not exist\n"); + return -EINVAL; + } + if (scheme->used) { + pr_err("The requested Scheme is already used\n"); + return -EINVAL; + } + + /* Clear all scheme fields because the scheme may have been + * previously used + */ + memset(scheme, 0, sizeof(struct keygen_scheme)); + + /* Setup scheme: */ + scheme->hw_port_id = hw_port_id; + scheme->use_hashing = true; + scheme->base_fqid = hash_base_fqid; + scheme->hash_fqid_count = hash_size; + scheme->symmetric_hash = DEFAULT_SYMMETRIC_HASH; + scheme->hashShift = DEFAULT_HASH_SHIFT; + + /* All Schemes in hard-coded configuration + * are Indirect Schemes + */ + scheme->match_vector = 0; + + err = keygen_scheme_setup(keygen, scheme_id, true); + if (err != 0) { + pr_err("Scheme setup failed\n"); + return err; + } + + /* Bind Rx port to Scheme */ + err = keygen_bind_port_to_schemes(keygen, scheme_id, true); + if (err != 0) { + pr_err("Binding port to schemes failed\n"); + return err; + } + + return 0; +} +EXPORT_SYMBOL(keygen_port_hashing_init); diff --git a/drivers/net/ethernet/freescale/fman/fman_keygen.h b/drivers/net/ethernet/freescale/fman/fman_keygen.h new file mode 100644 index 000000000000..c4640de3f4cb --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/fman_keygen.h @@ -0,0 +1,46 @@ +/* + * Copyright 2017 NXP + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of NXP nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY NXP ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL NXP BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __KEYGEN_H +#define __KEYGEN_H + +#include <linux/io.h> + +struct fman_keygen; +struct fman_kg_regs; + +struct fman_keygen *keygen_init(struct fman_kg_regs __iomem *keygen_regs); + +int keygen_port_hashing_init(struct fman_keygen *keygen, u8 hw_port_id, + u32 hash_base_fqid, u32 hash_size); + +#endif /* __KEYGEN_H */ diff --git a/drivers/net/ethernet/freescale/fman/fman_port.c b/drivers/net/ethernet/freescale/fman/fman_port.c index 57bf44fa16a1..1789b206be58 100644 --- a/drivers/net/ethernet/freescale/fman/fman_port.c +++ b/drivers/net/ethernet/freescale/fman/fman_port.c @@ -32,10 +32,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include "fman_port.h" -#include "fman.h" -#include "fman_sp.h" - #include <linux/io.h> #include <linux/slab.h> #include <linux/module.h> @@ -45,6 +41,11 @@ #include <linux/delay.h> #include <linux/libfdt_env.h> +#include "fman.h" +#include "fman_port.h" +#include "fman_sp.h" +#include "fman_keygen.h" + /* Queue ID */ #define DFLT_FQ_ID 0x00FFFFFF @@ -184,6 +185,7 @@ #define NIA_ENG_QMI_ENQ 0x00540000 #define NIA_ENG_QMI_DEQ 0x00580000 #define NIA_ENG_HWP 0x00440000 +#define NIA_ENG_HWK 0x00480000 #define NIA_BMI_AC_ENQ_FRAME 0x00000002 #define NIA_BMI_AC_TX_RELEASE 0x000002C0 #define NIA_BMI_AC_RELEASE 0x000000C0 @@ -394,6 +396,8 @@ struct fman_port_bpools { struct fman_port_cfg { u32 dflt_fqid; u32 err_fqid; + u32 pcd_base_fqid; + u32 pcd_fqs_count; u8 deq_sp; bool deq_high_priority; enum fman_port_deq_type deq_type; @@ -1271,6 +1275,10 @@ static void set_rx_dflt_cfg(struct fman_port *port, port_params->specific_params.rx_params.err_fqid; port->cfg->dflt_fqid = port_params->specific_params.rx_params.dflt_fqid; + port->cfg->pcd_base_fqid = + port_params->specific_params.rx_params.pcd_base_fqid; + port->cfg->pcd_fqs_count = + port_params->specific_params.rx_params.pcd_fqs_count; } static void set_tx_dflt_cfg(struct fman_port *port, @@ -1398,6 +1406,24 @@ err_port_cfg: EXPORT_SYMBOL(fman_port_config); /** + * fman_port_use_kg_hash + * port: A pointer to a FM Port module. + * Sets the HW KeyGen or the BMI as HW Parser next engine, enabling + * or bypassing the KeyGen hashing of Rx traffic + */ +void fman_port_use_kg_hash(struct fman_port *port, bool enable) +{ + if (enable) + /* After the Parser frames go to KeyGen */ + iowrite32be(NIA_ENG_HWK, &port->bmi_regs->rx.fmbm_rfpne); + else + /* After the Parser frames go to BMI */ + iowrite32be(NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME, + &port->bmi_regs->rx.fmbm_rfpne); +} +EXPORT_SYMBOL(fman_port_use_kg_hash); + +/** * fman_port_init * port: A pointer to a FM Port module. * Initializes the FM PORT module by defining the software structure and @@ -1407,9 +1433,10 @@ EXPORT_SYMBOL(fman_port_config); */ int fman_port_init(struct fman_port *port) { + struct fman_port_init_params params; + struct fman_keygen *keygen; struct fman_port_cfg *cfg; int err; - struct fman_port_init_params params; if (is_init_done(port->cfg)) return -EINVAL; @@ -1472,6 +1499,17 @@ int fman_port_init(struct fman_port *port) if (err) return err; + if (port->cfg->pcd_fqs_count) { + keygen = port->dts_params.fman->keygen; + err = keygen_port_hashing_init(keygen, port->port_id, + port->cfg->pcd_base_fqid, + port->cfg->pcd_fqs_count); + if (err) + return err; + + fman_port_use_kg_hash(port, true); + } + kfree(port->cfg); port->cfg = NULL; @@ -1682,6 +1720,17 @@ u32 fman_port_get_qman_channel_id(struct fman_port *port) } EXPORT_SYMBOL(fman_port_get_qman_channel_id); +int fman_port_get_hash_result_offset(struct fman_port *port, u32 *offset) +{ + if (port->buffer_offsets.hash_result_offset == ILLEGAL_BASE) + return -EINVAL; + + *offset = port->buffer_offsets.hash_result_offset; + + return 0; +} +EXPORT_SYMBOL(fman_port_get_hash_result_offset); + static int fman_port_probe(struct platform_device *of_dev) { struct fman_port *port; @@ -1720,8 +1769,8 @@ static int fman_port_probe(struct platform_device *of_dev) err = of_property_read_u32(port_node, "cell-index", &val); if (err) { - dev_err(port->dev, "%s: reading cell-index for %s failed\n", - __func__, port_node->full_name); + dev_err(port->dev, "%s: reading cell-index for %pOF failed\n", + __func__, port_node); err = -EINVAL; goto return_err; } diff --git a/drivers/net/ethernet/freescale/fman/fman_port.h b/drivers/net/ethernet/freescale/fman/fman_port.h index 8ba901737048..e86ca6a34e4e 100644 --- a/drivers/net/ethernet/freescale/fman/fman_port.h +++ b/drivers/net/ethernet/freescale/fman/fman_port.h @@ -100,6 +100,9 @@ struct fman_port; struct fman_port_rx_params { u32 err_fqid; /* Error Queue Id. */ u32 dflt_fqid; /* Default Queue Id. */ + u32 pcd_base_fqid; /* PCD base Queue Id. */ + u32 pcd_fqs_count; /* Number of PCD FQs. */ + /* Which external buffer pools are used * (up to FMAN_PORT_MAX_EXT_POOLS_NUM), and their sizes. */ @@ -134,6 +137,8 @@ struct fman_port_params { int fman_port_config(struct fman_port *port, struct fman_port_params *params); +void fman_port_use_kg_hash(struct fman_port *port, bool enable); + int fman_port_init(struct fman_port *port); int fman_port_cfg_buf_prefix_content(struct fman_port *port, @@ -146,6 +151,8 @@ int fman_port_enable(struct fman_port *port); u32 fman_port_get_qman_channel_id(struct fman_port *port); +int fman_port_get_hash_result_offset(struct fman_port *port, u32 *offset); + struct fman_port *fman_port_bind(struct device *dev); #endif /* __FMAN_PORT_H */ diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c index 1c7da16ad0ff..387eb4a88b72 100644 --- a/drivers/net/ethernet/freescale/fman/mac.c +++ b/drivers/net/ethernet/freescale/fman/mac.c @@ -700,8 +700,8 @@ static int mac_probe(struct platform_device *_of_dev) priv->internal_phy_node = of_parse_phandle(mac_node, "pcsphy-handle", 0); } else { - dev_err(dev, "MAC node (%s) contains unsupported MAC\n", - mac_node->full_name); + dev_err(dev, "MAC node (%pOF) contains unsupported MAC\n", + mac_node); err = -EINVAL; goto _return; } @@ -714,16 +714,15 @@ static int mac_probe(struct platform_device *_of_dev) /* Get the FM node */ dev_node = of_get_parent(mac_node); if (!dev_node) { - dev_err(dev, "of_get_parent(%s) failed\n", - mac_node->full_name); + dev_err(dev, "of_get_parent(%pOF) failed\n", + mac_node); err = -EINVAL; goto _return_dev_set_drvdata; } of_dev = of_find_device_by_node(dev_node); if (!of_dev) { - dev_err(dev, "of_find_device_by_node(%s) failed\n", - dev_node->full_name); + dev_err(dev, "of_find_device_by_node(%pOF) failed\n", dev_node); err = -EINVAL; goto _return_of_node_put; } @@ -731,8 +730,7 @@ static int mac_probe(struct platform_device *_of_dev) /* Get the FMan cell-index */ err = of_property_read_u32(dev_node, "cell-index", &val); if (err) { - dev_err(dev, "failed to read cell-index for %s\n", - dev_node->full_name); + dev_err(dev, "failed to read cell-index for %pOF\n", dev_node); err = -EINVAL; goto _return_of_node_put; } @@ -741,7 +739,7 @@ static int mac_probe(struct platform_device *_of_dev) priv->fman = fman_bind(&of_dev->dev); if (!priv->fman) { - dev_err(dev, "fman_bind(%s) failed\n", dev_node->full_name); + dev_err(dev, "fman_bind(%pOF) failed\n", dev_node); err = -ENODEV; goto _return_of_node_put; } @@ -751,8 +749,8 @@ static int mac_probe(struct platform_device *_of_dev) /* Get the address of the memory mapped registers */ err = of_address_to_resource(mac_node, 0, &res); if (err < 0) { - dev_err(dev, "of_address_to_resource(%s) = %d\n", - mac_node->full_name, err); + dev_err(dev, "of_address_to_resource(%pOF) = %d\n", + mac_node, err); goto _return_dev_set_drvdata; } @@ -786,8 +784,7 @@ static int mac_probe(struct platform_device *_of_dev) /* Get the cell-index */ err = of_property_read_u32(mac_node, "cell-index", &val); if (err) { - dev_err(dev, "failed to read cell-index for %s\n", - mac_node->full_name); + dev_err(dev, "failed to read cell-index for %pOF\n", mac_node); err = -EINVAL; goto _return_dev_set_drvdata; } @@ -796,8 +793,7 @@ static int mac_probe(struct platform_device *_of_dev) /* Get the MAC address */ mac_addr = of_get_mac_address(mac_node); if (!mac_addr) { - dev_err(dev, "of_get_mac_address(%s) failed\n", - mac_node->full_name); + dev_err(dev, "of_get_mac_address(%pOF) failed\n", mac_node); err = -EINVAL; goto _return_dev_set_drvdata; } @@ -806,15 +802,15 @@ static int mac_probe(struct platform_device *_of_dev) /* Get the port handles */ nph = of_count_phandle_with_args(mac_node, "fsl,fman-ports", NULL); if (unlikely(nph < 0)) { - dev_err(dev, "of_count_phandle_with_args(%s, fsl,fman-ports) failed\n", - mac_node->full_name); + dev_err(dev, "of_count_phandle_with_args(%pOF, fsl,fman-ports) failed\n", + mac_node); err = nph; goto _return_dev_set_drvdata; } if (nph != ARRAY_SIZE(mac_dev->port)) { - dev_err(dev, "Not supported number of fman-ports handles of mac node %s from device tree\n", - mac_node->full_name); + dev_err(dev, "Not supported number of fman-ports handles of mac node %pOF from device tree\n", + mac_node); err = -EINVAL; goto _return_dev_set_drvdata; } @@ -823,24 +819,24 @@ static int mac_probe(struct platform_device *_of_dev) /* Find the port node */ dev_node = of_parse_phandle(mac_node, "fsl,fman-ports", i); if (!dev_node) { - dev_err(dev, "of_parse_phandle(%s, fsl,fman-ports) failed\n", - mac_node->full_name); + dev_err(dev, "of_parse_phandle(%pOF, fsl,fman-ports) failed\n", + mac_node); err = -EINVAL; goto _return_of_node_put; } of_dev = of_find_device_by_node(dev_node); if (!of_dev) { - dev_err(dev, "of_find_device_by_node(%s) failed\n", - dev_node->full_name); + dev_err(dev, "of_find_device_by_node(%pOF) failed\n", + dev_node); err = -EINVAL; goto _return_of_node_put; } mac_dev->port[i] = fman_port_bind(&of_dev->dev); if (!mac_dev->port[i]) { - dev_err(dev, "dev_get_drvdata(%s) failed\n", - dev_node->full_name); + dev_err(dev, "dev_get_drvdata(%pOF) failed\n", + dev_node); err = -EINVAL; goto _return_of_node_put; } @@ -851,8 +847,8 @@ static int mac_probe(struct platform_device *_of_dev) phy_if = of_get_phy_mode(mac_node); if (phy_if < 0) { dev_warn(dev, - "of_get_phy_mode() for %s failed. Defaulting to SGMII\n", - mac_node->full_name); + "of_get_phy_mode() for %pOF failed. Defaulting to SGMII\n", + mac_node); phy_if = PHY_INTERFACE_MODE_SGMII; } priv->phy_if = phy_if; diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c index 1f015edcca22..c8e5d889bd81 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c +++ b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c @@ -100,7 +100,7 @@ static inline void mdc(struct mdiobb_ctrl *ctrl, int what) in_be32(bitbang->dat); } -static struct mdiobb_ops bb_ops = { +static const struct mdiobb_ops bb_ops = { .owner = THIS_MODULE, .set_mdc = mdc, .set_mdio_dir = mdio_dir, diff --git a/drivers/net/ethernet/freescale/fsl_pq_mdio.c b/drivers/net/ethernet/freescale/fsl_pq_mdio.c index a10de1e9c157..80ad16acf0f1 100644 --- a/drivers/net/ethernet/freescale/fsl_pq_mdio.c +++ b/drivers/net/ethernet/freescale/fsl_pq_mdio.c @@ -267,8 +267,8 @@ static void ucc_configure(phys_addr_t start, phys_addr_t end) ret = of_address_to_resource(np, 0, &res); if (ret < 0) { - pr_debug("fsl-pq-mdio: no address range in node %s\n", - np->full_name); + pr_debug("fsl-pq-mdio: no address range in node %pOF\n", + np); continue; } @@ -280,8 +280,8 @@ static void ucc_configure(phys_addr_t start, phys_addr_t end) if (!iprop) { iprop = of_get_property(np, "device-id", NULL); if (!iprop) { - pr_debug("fsl-pq-mdio: no UCC ID in node %s\n", - np->full_name); + pr_debug("fsl-pq-mdio: no UCC ID in node %pOF\n", + np); continue; } } @@ -293,8 +293,8 @@ static void ucc_configure(phys_addr_t start, phys_addr_t end) * numbered from 1, not 0. */ if (ucc_set_qe_mux_mii_mng(id - 1) < 0) { - pr_debug("fsl-pq-mdio: invalid UCC ID in node %s\n", - np->full_name); + pr_debug("fsl-pq-mdio: invalid UCC ID in node %pOF\n", + np); continue; } @@ -442,8 +442,8 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev) if (data->get_tbipa) { for_each_child_of_node(np, tbi) { if (strcmp(tbi->type, "tbi-phy") == 0) { - dev_dbg(&pdev->dev, "found TBI PHY node %s\n", - strrchr(tbi->full_name, '/') + 1); + dev_dbg(&pdev->dev, "found TBI PHY node %pOFP\n", + tbi); break; } } @@ -454,8 +454,8 @@ static int fsl_pq_mdio_probe(struct platform_device *pdev) if (!prop) { dev_err(&pdev->dev, - "missing 'reg' property in node %s\n", - tbi->full_name); + "missing 'reg' property in node %pOF\n", + tbi); err = -EBUSY; goto error; } diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index c4b4b0a1bbf0..5be52d89b182 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -3687,7 +3687,7 @@ static noinline void gfar_update_link_state(struct gfar_private *priv) u32 tempval1 = gfar_read(®s->maccfg1); u32 tempval = gfar_read(®s->maccfg2); u32 ecntrl = gfar_read(®s->ecntrl); - u32 tx_flow_oldval = (tempval & MACCFG1_TX_FLOW); + u32 tx_flow_oldval = (tempval1 & MACCFG1_TX_FLOW); if (phydev->duplex != priv->oldduplex) { if (!(phydev->duplex)) diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c index 721be13081f9..544114281ea7 100644 --- a/drivers/net/ethernet/freescale/gianfar_ptp.c +++ b/drivers/net/ethernet/freescale/gianfar_ptp.c @@ -411,7 +411,7 @@ static int ptp_gianfar_enable(struct ptp_clock_info *ptp, return -EOPNOTSUPP; } -static struct ptp_clock_info ptp_gianfar_caps = { +static const struct ptp_clock_info ptp_gianfar_caps = { .owner = THIS_MODULE, .name = "gianfar clock", .max_adj = 512000, diff --git a/drivers/net/ethernet/hisilicon/Kconfig b/drivers/net/ethernet/hisilicon/Kconfig index d11287e11371..91c7bdb9b43c 100644 --- a/drivers/net/ethernet/hisilicon/Kconfig +++ b/drivers/net/ethernet/hisilicon/Kconfig @@ -76,4 +76,31 @@ config HNS_ENET This selects the general ethernet driver for HNS. This module make use of any HNS AE driver, such as HNS_DSAF +config HNS3 + tristate "Hisilicon Network Subsystem Support HNS3 (Framework)" + depends on PCI + ---help--- + This selects the framework support for Hisilicon Network Subsystem 3. + This layer facilitates clients like ENET, RoCE and user-space ethernet + drivers(like ODP)to register with HNAE devices and their associated + operations. + +config HNS3_HCLGE + tristate "Hisilicon HNS3 HCLGE Acceleration Engine & Compatibility Layer Support" + depends on PCI_MSI + depends on HNS3 + ---help--- + This selects the HNS3_HCLGE network acceleration engine & its hardware + compatibility layer. The engine would be used in Hisilicon hip08 family of + SoCs and further upcoming SoCs. + +config HNS3_ENET + tristate "Hisilicon HNS3 Ethernet Device Support" + depends on 64BIT && PCI + depends on HNS3 && HNS3_HCLGE + ---help--- + This selects the Ethernet Driver for Hisilicon Network Subsystem 3 for hip08 + family of SoCs. This module depends upon HNAE3 driver to access the HNAE3 + devices and their associated operations. + endif # NET_VENDOR_HISILICON diff --git a/drivers/net/ethernet/hisilicon/Makefile b/drivers/net/ethernet/hisilicon/Makefile index 8661695024dc..3828c435c18f 100644 --- a/drivers/net/ethernet/hisilicon/Makefile +++ b/drivers/net/ethernet/hisilicon/Makefile @@ -6,4 +6,5 @@ obj-$(CONFIG_HIX5HD2_GMAC) += hix5hd2_gmac.o obj-$(CONFIG_HIP04_ETH) += hip04_eth.o obj-$(CONFIG_HNS_MDIO) += hns_mdio.o obj-$(CONFIG_HNS) += hns/ +obj-$(CONFIG_HNS3) += hns3/ obj-$(CONFIG_HISI_FEMAC) += hisi_femac.o diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.c b/drivers/net/ethernet/hisilicon/hns/hnae.c index 9d9b6e6dd988..a051e582d541 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.c +++ b/drivers/net/ethernet/hisilicon/hns/hnae.c @@ -202,6 +202,7 @@ hnae_init_ring(struct hnae_queue *q, struct hnae_ring *ring, int flags) ring->q = q; ring->flags = flags; spin_lock_init(&ring->lock); + ring->coal_param = q->handle->coal_param; assert(!ring->desc && !ring->desc_cb && !ring->desc_dma_addr); /* not matter for tx or rx ring, the ntc and ntc start from 0 */ diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h index 7ba653af19cb..3e62692af011 100644 --- a/drivers/net/ethernet/hisilicon/hns/hnae.h +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h @@ -89,6 +89,10 @@ do { \ #define RCB_RING_NAME_LEN 16 +#define HNAE_LOWEST_LATENCY_COAL_PARAM 30 +#define HNAE_LOW_LATENCY_COAL_PARAM 80 +#define HNAE_BULK_LATENCY_COAL_PARAM 150 + enum hnae_led_state { HNAE_LED_INACTIVE, HNAE_LED_ACTIVE, @@ -292,6 +296,12 @@ struct hnae_ring { int flags; /* ring attribute */ int irq_init_flag; + + /* total rx bytes after last rx rate calucated */ + u64 coal_last_rx_bytes; + unsigned long coal_last_jiffies; + u32 coal_param; + u32 coal_rx_rate; /* rx rate in MB */ }; #define ring_ptr_move_fw(ring, p) \ @@ -548,8 +558,13 @@ struct hnae_handle { u32 if_support; int q_num; int vf_id; + unsigned long coal_last_jiffies; + u32 coal_param; /* self adapt coalesce param */ + /* the ring index of last ring that set coal param */ + u32 coal_ring_idx; u32 eport_id; u32 dport_id; /* v2 tx bd should fill the dport_id */ + bool coal_adapt_en; enum hnae_port_type port_type; enum hnae_media_type media_type; struct list_head node; /* list to hnae_ae_dev->handle_list */ diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c index a37166ee577b..bd68379d2bea 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c @@ -99,6 +99,7 @@ struct hnae_handle *hns_ae_get_handle(struct hnae_ae_dev *dev, ae_handle->owner_dev = dsaf_dev->dev; ae_handle->dev = dev; ae_handle->q_num = qnum_per_vf; + ae_handle->coal_param = HNAE_LOWEST_LATENCY_COAL_PARAM; /* find ring pair, and set vf id*/ for (ae_handle->vf_id = 0; diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.c b/drivers/net/ethernet/hisilicon/hns/hns_enet.c index 3987699f8fe6..36520634c96a 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.c @@ -812,6 +812,113 @@ static int hns_desc_unused(struct hnae_ring *ring) return ((ntc >= ntu) ? 0 : ring->desc_num) + ntc - ntu; } +#define HNS_LOWEST_LATENCY_RATE 27 /* 27 MB/s */ +#define HNS_LOW_LATENCY_RATE 80 /* 80 MB/s */ + +#define HNS_COAL_BDNUM 3 + +static u32 hns_coal_rx_bdnum(struct hnae_ring *ring) +{ + bool coal_enable = ring->q->handle->coal_adapt_en; + + if (coal_enable && + ring->coal_last_rx_bytes > HNS_LOWEST_LATENCY_RATE) + return HNS_COAL_BDNUM; + else + return 0; +} + +static void hns_update_rx_rate(struct hnae_ring *ring) +{ + bool coal_enable = ring->q->handle->coal_adapt_en; + u32 time_passed_ms; + u64 total_bytes; + + if (!coal_enable || + time_before(jiffies, ring->coal_last_jiffies + (HZ >> 4))) + return; + + /* ring->stats.rx_bytes overflowed */ + if (ring->coal_last_rx_bytes > ring->stats.rx_bytes) { + ring->coal_last_rx_bytes = ring->stats.rx_bytes; + ring->coal_last_jiffies = jiffies; + return; + } + + total_bytes = ring->stats.rx_bytes - ring->coal_last_rx_bytes; + time_passed_ms = jiffies_to_msecs(jiffies - ring->coal_last_jiffies); + do_div(total_bytes, time_passed_ms); + ring->coal_rx_rate = total_bytes >> 10; + + ring->coal_last_rx_bytes = ring->stats.rx_bytes; + ring->coal_last_jiffies = jiffies; +} + +/** + * smooth_alg - smoothing algrithm for adjusting coalesce parameter + **/ +static u32 smooth_alg(u32 new_param, u32 old_param) +{ + u32 gap = (new_param > old_param) ? new_param - old_param + : old_param - new_param; + + if (gap > 8) + gap >>= 3; + + if (new_param > old_param) + return old_param + gap; + else + return old_param - gap; +} + +/** + * hns_nic_adp_coalesce - self adapte coalesce according to rx rate + * @ring_data: pointer to hns_nic_ring_data + **/ +static void hns_nic_adpt_coalesce(struct hns_nic_ring_data *ring_data) +{ + struct hnae_ring *ring = ring_data->ring; + struct hnae_handle *handle = ring->q->handle; + u32 new_coal_param, old_coal_param = ring->coal_param; + + if (ring->coal_rx_rate < HNS_LOWEST_LATENCY_RATE) + new_coal_param = HNAE_LOWEST_LATENCY_COAL_PARAM; + else if (ring->coal_rx_rate < HNS_LOW_LATENCY_RATE) + new_coal_param = HNAE_LOW_LATENCY_COAL_PARAM; + else + new_coal_param = HNAE_BULK_LATENCY_COAL_PARAM; + + if (new_coal_param == old_coal_param && + new_coal_param == handle->coal_param) + return; + + new_coal_param = smooth_alg(new_coal_param, old_coal_param); + ring->coal_param = new_coal_param; + + /** + * Because all ring in one port has one coalesce param, when one ring + * calculate its own coalesce param, it cannot write to hardware at + * once. There are three conditions as follows: + * 1. current ring's coalesce param is larger than the hardware. + * 2. or ring which adapt last time can change again. + * 3. timeout. + */ + if (new_coal_param == handle->coal_param) { + handle->coal_last_jiffies = jiffies; + handle->coal_ring_idx = ring_data->queue_index; + } else if (new_coal_param > handle->coal_param || + handle->coal_ring_idx == ring_data->queue_index || + time_after(jiffies, handle->coal_last_jiffies + (HZ >> 4))) { + handle->dev->ops->set_coalesce_usecs(handle, + new_coal_param); + handle->dev->ops->set_coalesce_frames(handle, + 1, new_coal_param); + handle->coal_param = new_coal_param; + handle->coal_ring_idx = ring_data->queue_index; + handle->coal_last_jiffies = jiffies; + } +} + static int hns_nic_rx_poll_one(struct hns_nic_ring_data *ring_data, int budget, void *v) { @@ -868,20 +975,27 @@ static bool hns_nic_rx_fini_pro(struct hns_nic_ring_data *ring_data) { struct hnae_ring *ring = ring_data->ring; int num = 0; + bool rx_stopped; - ring_data->ring->q->handle->dev->ops->toggle_ring_irq(ring, 0); + hns_update_rx_rate(ring); /* for hardware bug fixed */ + ring_data->ring->q->handle->dev->ops->toggle_ring_irq(ring, 0); num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); - if (num > 0) { + if (num <= hns_coal_rx_bdnum(ring)) { + if (ring->q->handle->coal_adapt_en) + hns_nic_adpt_coalesce(ring_data); + + rx_stopped = true; + } else { ring_data->ring->q->handle->dev->ops->toggle_ring_irq( ring_data->ring, 1); - return false; - } else { - return true; + rx_stopped = false; } + + return rx_stopped; } static bool hns_nic_rx_fini_pro_v2(struct hns_nic_ring_data *ring_data) @@ -889,12 +1003,17 @@ static bool hns_nic_rx_fini_pro_v2(struct hns_nic_ring_data *ring_data) struct hnae_ring *ring = ring_data->ring; int num; + hns_update_rx_rate(ring); num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM); - if (!num) + if (num <= hns_coal_rx_bdnum(ring)) { + if (ring->q->handle->coal_adapt_en) + hns_nic_adpt_coalesce(ring_data); + return true; - else - return false; + } + + return false; } static inline void hns_nic_reclaim_one_desc(struct hnae_ring *ring, diff --git a/drivers/net/ethernet/hisilicon/hns/hns_enet.h b/drivers/net/ethernet/hisilicon/hns/hns_enet.h index 9cb4c7884201..26e9afcbdd50 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_enet.h +++ b/drivers/net/ethernet/hisilicon/hns/hns_enet.h @@ -38,7 +38,7 @@ struct hns_nic_ring_data { struct hnae_ring *ring; struct napi_struct napi; cpumask_t mask; /* affinity mask */ - int queue_index; + u32 queue_index; int (*poll_one)(struct hns_nic_ring_data *, int, void *); void (*ex_process)(struct hns_nic_ring_data *, struct sk_buff *); bool (*fini_process)(struct hns_nic_ring_data *); diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index a8db27e86a11..7ea7f8a4aa2a 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -595,7 +595,7 @@ static void hns_nic_self_test(struct net_device *ndev, set_bit(NIC_STATE_TESTING, &priv->state); if (if_running) - (void)dev_close(ndev); + dev_close(ndev); for (i = 0; i < SELF_TEST_TPYE_NUM; i++) { if (!st_param[i][1]) @@ -735,8 +735,8 @@ static int hns_get_coalesce(struct net_device *net_dev, ops = priv->ae_handle->dev->ops; - ec->use_adaptive_rx_coalesce = 1; - ec->use_adaptive_tx_coalesce = 1; + ec->use_adaptive_rx_coalesce = priv->ae_handle->coal_adapt_en; + ec->use_adaptive_tx_coalesce = priv->ae_handle->coal_adapt_en; if ((!ops->get_coalesce_usecs) || (!ops->get_max_coalesced_frames)) @@ -787,6 +787,9 @@ static int hns_set_coalesce(struct net_device *net_dev, (!ops->set_coalesce_frames)) return -ESRCH; + if (ec->use_adaptive_rx_coalesce != priv->ae_handle->coal_adapt_en) + priv->ae_handle->coal_adapt_en = ec->use_adaptive_rx_coalesce; + rc1 = ops->set_coalesce_usecs(priv->ae_handle, ec->rx_coalesce_usecs); diff --git a/drivers/net/ethernet/hisilicon/hns3/Makefile b/drivers/net/ethernet/hisilicon/hns3/Makefile new file mode 100644 index 000000000000..a9349e1f3e51 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the HISILICON network device drivers. +# + +obj-$(CONFIG_HNS3) += hns3pf/ + +obj-$(CONFIG_HNS3) += hnae3.o diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.c b/drivers/net/ethernet/hisilicon/hns3/hnae3.c new file mode 100644 index 000000000000..59efbd605416 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.c @@ -0,0 +1,300 @@ +/* + * Copyright (c) 2016-2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include "hnae3.h" + +static LIST_HEAD(hnae3_ae_algo_list); +static LIST_HEAD(hnae3_client_list); +static LIST_HEAD(hnae3_ae_dev_list); + +/* we are keeping things simple and using single lock for all the + * list. This is a non-critical code so other updations, if happen + * in parallel, can wait. + */ +static DEFINE_MUTEX(hnae3_common_lock); + +static bool hnae3_client_match(enum hnae3_client_type client_type, + enum hnae3_dev_type dev_type) +{ + if ((dev_type == HNAE3_DEV_KNIC) && (client_type == HNAE3_CLIENT_KNIC || + client_type == HNAE3_CLIENT_ROCE)) + return true; + + if (dev_type == HNAE3_DEV_UNIC && client_type == HNAE3_CLIENT_UNIC) + return true; + + return false; +} + +static int hnae3_match_n_instantiate(struct hnae3_client *client, + struct hnae3_ae_dev *ae_dev, + bool is_reg, bool *matched) +{ + int ret; + + *matched = false; + + /* check if this client matches the type of ae_dev */ + if (!(hnae3_client_match(client->type, ae_dev->dev_type) && + hnae_get_bit(ae_dev->flag, HNAE3_DEV_INITED_B))) { + return 0; + } + /* there is a match of client and dev */ + *matched = true; + + /* now, (un-)instantiate client by calling lower layer */ + if (is_reg) { + ret = ae_dev->ops->init_client_instance(client, ae_dev); + if (ret) + dev_err(&ae_dev->pdev->dev, + "fail to instantiate client\n"); + return ret; + } + + ae_dev->ops->uninit_client_instance(client, ae_dev); + return 0; +} + +int hnae3_register_client(struct hnae3_client *client) +{ + struct hnae3_client *client_tmp; + struct hnae3_ae_dev *ae_dev; + bool matched; + int ret = 0; + + mutex_lock(&hnae3_common_lock); + /* one system should only have one client for every type */ + list_for_each_entry(client_tmp, &hnae3_client_list, node) { + if (client_tmp->type == client->type) + goto exit; + } + + list_add_tail(&client->node, &hnae3_client_list); + + /* initialize the client on every matched port */ + list_for_each_entry(ae_dev, &hnae3_ae_dev_list, node) { + /* if the client could not be initialized on current port, for + * any error reasons, move on to next available port + */ + ret = hnae3_match_n_instantiate(client, ae_dev, true, &matched); + if (ret) + dev_err(&ae_dev->pdev->dev, + "match and instantiation failed for port\n"); + } + +exit: + mutex_unlock(&hnae3_common_lock); + + return ret; +} +EXPORT_SYMBOL(hnae3_register_client); + +void hnae3_unregister_client(struct hnae3_client *client) +{ + struct hnae3_ae_dev *ae_dev; + bool matched; + + mutex_lock(&hnae3_common_lock); + /* un-initialize the client on every matched port */ + list_for_each_entry(ae_dev, &hnae3_ae_dev_list, node) { + hnae3_match_n_instantiate(client, ae_dev, false, &matched); + } + + list_del(&client->node); + mutex_unlock(&hnae3_common_lock); +} +EXPORT_SYMBOL(hnae3_unregister_client); + +/* hnae3_register_ae_algo - register a AE algorithm to hnae3 framework + * @ae_algo: AE algorithm + * NOTE: the duplicated name will not be checked + */ +int hnae3_register_ae_algo(struct hnae3_ae_algo *ae_algo) +{ + const struct pci_device_id *id; + struct hnae3_ae_dev *ae_dev; + struct hnae3_client *client; + bool matched; + int ret = 0; + + mutex_lock(&hnae3_common_lock); + + list_add_tail(&ae_algo->node, &hnae3_ae_algo_list); + + /* Check if this algo/ops matches the list of ae_devs */ + list_for_each_entry(ae_dev, &hnae3_ae_dev_list, node) { + id = pci_match_id(ae_algo->pdev_id_table, ae_dev->pdev); + if (!id) + continue; + + /* ae_dev init should set flag */ + ae_dev->ops = ae_algo->ops; + ret = ae_algo->ops->init_ae_dev(ae_dev); + if (ret) { + dev_err(&ae_dev->pdev->dev, "init ae_dev error.\n"); + continue; + } + + hnae_set_bit(ae_dev->flag, HNAE3_DEV_INITED_B, 1); + + /* check the client list for the match with this ae_dev type and + * initialize the figure out client instance + */ + list_for_each_entry(client, &hnae3_client_list, node) { + ret = hnae3_match_n_instantiate(client, ae_dev, true, + &matched); + if (ret) + dev_err(&ae_dev->pdev->dev, + "match and instantiation failed\n"); + if (matched) + break; + } + } + + mutex_unlock(&hnae3_common_lock); + + return ret; +} +EXPORT_SYMBOL(hnae3_register_ae_algo); + +/* hnae3_unregister_ae_algo - unregisters a AE algorithm + * @ae_algo: the AE algorithm to unregister + */ +void hnae3_unregister_ae_algo(struct hnae3_ae_algo *ae_algo) +{ + const struct pci_device_id *id; + struct hnae3_ae_dev *ae_dev; + struct hnae3_client *client; + bool matched; + + mutex_lock(&hnae3_common_lock); + /* Check if there are matched ae_dev */ + list_for_each_entry(ae_dev, &hnae3_ae_dev_list, node) { + id = pci_match_id(ae_algo->pdev_id_table, ae_dev->pdev); + if (!id) + continue; + + /* check the client list for the match with this ae_dev type and + * un-initialize the figure out client instance + */ + list_for_each_entry(client, &hnae3_client_list, node) { + hnae3_match_n_instantiate(client, ae_dev, false, + &matched); + if (matched) + break; + } + + ae_algo->ops->uninit_ae_dev(ae_dev); + hnae_set_bit(ae_dev->flag, HNAE3_DEV_INITED_B, 0); + } + + list_del(&ae_algo->node); + mutex_unlock(&hnae3_common_lock); +} +EXPORT_SYMBOL(hnae3_unregister_ae_algo); + +/* hnae3_register_ae_dev - registers a AE device to hnae3 framework + * @ae_dev: the AE device + * NOTE: the duplicated name will not be checked + */ +int hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev) +{ + const struct pci_device_id *id; + struct hnae3_ae_algo *ae_algo; + struct hnae3_client *client; + bool matched; + int ret = 0; + + mutex_lock(&hnae3_common_lock); + list_add_tail(&ae_dev->node, &hnae3_ae_dev_list); + + /* Check if there are matched ae_algo */ + list_for_each_entry(ae_algo, &hnae3_ae_algo_list, node) { + id = pci_match_id(ae_algo->pdev_id_table, ae_dev->pdev); + if (!id) + continue; + + ae_dev->ops = ae_algo->ops; + + if (!ae_dev->ops) { + dev_err(&ae_dev->pdev->dev, "ae_dev ops are null\n"); + goto out_err; + } + + /* ae_dev init should set flag */ + ret = ae_dev->ops->init_ae_dev(ae_dev); + if (ret) { + dev_err(&ae_dev->pdev->dev, "init ae_dev error\n"); + goto out_err; + } + + hnae_set_bit(ae_dev->flag, HNAE3_DEV_INITED_B, 1); + break; + } + + /* check the client list for the match with this ae_dev type and + * initialize the figure out client instance + */ + list_for_each_entry(client, &hnae3_client_list, node) { + ret = hnae3_match_n_instantiate(client, ae_dev, true, + &matched); + if (ret) + dev_err(&ae_dev->pdev->dev, + "match and instantiation failed\n"); + if (matched) + break; + } + +out_err: + mutex_unlock(&hnae3_common_lock); + + return ret; +} +EXPORT_SYMBOL(hnae3_register_ae_dev); + +/* hnae3_unregister_ae_dev - unregisters a AE device + * @ae_dev: the AE device to unregister + */ +void hnae3_unregister_ae_dev(struct hnae3_ae_dev *ae_dev) +{ + const struct pci_device_id *id; + struct hnae3_ae_algo *ae_algo; + struct hnae3_client *client; + bool matched; + + mutex_lock(&hnae3_common_lock); + /* Check if there are matched ae_algo */ + list_for_each_entry(ae_algo, &hnae3_ae_algo_list, node) { + id = pci_match_id(ae_algo->pdev_id_table, ae_dev->pdev); + if (!id) + continue; + + list_for_each_entry(client, &hnae3_client_list, node) { + hnae3_match_n_instantiate(client, ae_dev, false, + &matched); + if (matched) + break; + } + + ae_algo->ops->uninit_ae_dev(ae_dev); + hnae_set_bit(ae_dev->flag, HNAE3_DEV_INITED_B, 0); + } + + list_del(&ae_dev->node); + mutex_unlock(&hnae3_common_lock); +} +EXPORT_SYMBOL(hnae3_unregister_ae_dev); + +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("HNAE3(Hisilicon Network Acceleration Engine) Framework"); diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h new file mode 100644 index 000000000000..b2f28ae81273 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2016-2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HNAE3_H +#define __HNAE3_H + +/* Names used in this framework: + * ae handle (handle): + * a set of queues provided by AE + * ring buffer queue (rbq): + * the channel between upper layer and the AE, can do tx and rx + * ring: + * a tx or rx channel within a rbq + * ring description (desc): + * an element in the ring with packet information + * buffer: + * a memory region referred by desc with the full packet payload + * + * "num" means a static number set as a parameter, "count" mean a dynamic + * number set while running + * "cb" means control block + */ + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/pci.h> +#include <linux/types.h> + +/* Device IDs */ +#define HNAE3_DEV_ID_GE 0xA220 +#define HNAE3_DEV_ID_25GE 0xA221 +#define HNAE3_DEV_ID_25GE_RDMA 0xA222 +#define HNAE3_DEV_ID_25GE_RDMA_MACSEC 0xA223 +#define HNAE3_DEV_ID_50GE_RDMA 0xA224 +#define HNAE3_DEV_ID_50GE_RDMA_MACSEC 0xA225 +#define HNAE3_DEV_ID_100G_RDMA_MACSEC 0xA226 +#define HNAE3_DEV_ID_100G_VF 0xA22E +#define HNAE3_DEV_ID_100G_RDMA_DCB_PFC_VF 0xA22F + +#define HNAE3_CLASS_NAME_SIZE 16 + +#define HNAE3_DEV_INITED_B 0x0 +#define HNAE_DEV_SUPPORT_ROCE_B 0x1 + +#define ring_ptr_move_fw(ring, p) \ + ((ring)->p = ((ring)->p + 1) % (ring)->desc_num) +#define ring_ptr_move_bw(ring, p) \ + ((ring)->p = ((ring)->p - 1 + (ring)->desc_num) % (ring)->desc_num) + +enum hns_desc_type { + DESC_TYPE_SKB, + DESC_TYPE_PAGE, +}; + +struct hnae3_handle; + +struct hnae3_queue { + void __iomem *io_base; + struct hnae3_ae_algo *ae_algo; + struct hnae3_handle *handle; + int tqp_index; /* index in a handle */ + u32 buf_size; /* size for hnae_desc->addr, preset by AE */ + u16 desc_num; /* total number of desc */ +}; + +/*hnae3 loop mode*/ +enum hnae3_loop { + HNAE3_MAC_INTER_LOOP_MAC, + HNAE3_MAC_INTER_LOOP_SERDES, + HNAE3_MAC_INTER_LOOP_PHY, + HNAE3_MAC_LOOP_NONE, +}; + +enum hnae3_client_type { + HNAE3_CLIENT_KNIC, + HNAE3_CLIENT_UNIC, + HNAE3_CLIENT_ROCE, +}; + +enum hnae3_dev_type { + HNAE3_DEV_KNIC, + HNAE3_DEV_UNIC, +}; + +/* mac media type */ +enum hnae3_media_type { + HNAE3_MEDIA_TYPE_UNKNOWN, + HNAE3_MEDIA_TYPE_FIBER, + HNAE3_MEDIA_TYPE_COPPER, + HNAE3_MEDIA_TYPE_BACKPLANE, +}; + +struct hnae3_vector_info { + u8 __iomem *io_addr; + int vector; +}; + +#define HNAE3_RING_TYPE_B 0 +#define HNAE3_RING_TYPE_TX 0 +#define HNAE3_RING_TYPE_RX 1 + +struct hnae3_ring_chain_node { + struct hnae3_ring_chain_node *next; + u32 tqp_index; + u32 flag; +}; + +#define HNAE3_IS_TX_RING(node) \ + (((node)->flag & (1 << HNAE3_RING_TYPE_B)) == HNAE3_RING_TYPE_TX) + +struct hnae3_client_ops { + int (*init_instance)(struct hnae3_handle *handle); + void (*uninit_instance)(struct hnae3_handle *handle, bool reset); + void (*link_status_change)(struct hnae3_handle *handle, bool state); +}; + +#define HNAE3_CLIENT_NAME_LENGTH 16 +struct hnae3_client { + char name[HNAE3_CLIENT_NAME_LENGTH]; + u16 version; + unsigned long state; + enum hnae3_client_type type; + const struct hnae3_client_ops *ops; + struct list_head node; +}; + +struct hnae3_ae_dev { + struct pci_dev *pdev; + const struct hnae3_ae_ops *ops; + struct list_head node; + u32 flag; + enum hnae3_dev_type dev_type; + void *priv; +}; + +/* This struct defines the operation on the handle. + * + * init_ae_dev(): (mandatory) + * Get PF configure from pci_dev and initialize PF hardware + * uninit_ae_dev() + * Disable PF device and release PF resource + * register_client + * Register client to ae_dev + * unregister_client() + * Unregister client from ae_dev + * start() + * Enable the hardware + * stop() + * Disable the hardware + * get_status() + * Get the carrier state of the back channel of the handle, 1 for ok, 0 for + * non-ok + * get_ksettings_an_result() + * Get negotiation status,speed and duplex + * update_speed_duplex_h() + * Update hardware speed and duplex + * get_media_type() + * Get media type of MAC + * adjust_link() + * Adjust link status + * set_loopback() + * Set loopback + * set_promisc_mode + * Set promisc mode + * set_mtu() + * set mtu + * get_pauseparam() + * get tx and rx of pause frame use + * set_pauseparam() + * set tx and rx of pause frame use + * set_autoneg() + * set auto autonegotiation of pause frame use + * get_autoneg() + * get auto autonegotiation of pause frame use + * get_coalesce_usecs() + * get usecs to delay a TX interrupt after a packet is sent + * get_rx_max_coalesced_frames() + * get Maximum number of packets to be sent before a TX interrupt. + * set_coalesce_usecs() + * set usecs to delay a TX interrupt after a packet is sent + * set_coalesce_frames() + * set Maximum number of packets to be sent before a TX interrupt. + * get_mac_addr() + * get mac address + * set_mac_addr() + * set mac address + * add_uc_addr + * Add unicast addr to mac table + * rm_uc_addr + * Remove unicast addr from mac table + * set_mc_addr() + * Set multicast address + * add_mc_addr + * Add multicast address to mac table + * rm_mc_addr + * Remove multicast address from mac table + * update_stats() + * Update Old network device statistics + * get_ethtool_stats() + * Get ethtool network device statistics + * get_strings() + * Get a set of strings that describe the requested objects + * get_sset_count() + * Get number of strings that @get_strings will write + * update_led_status() + * Update the led status + * set_led_id() + * Set led id + * get_regs() + * Get regs dump + * get_regs_len() + * Get the len of the regs dump + * get_rss_key_size() + * Get rss key size + * get_rss_indir_size() + * Get rss indirection table size + * get_rss() + * Get rss table + * set_rss() + * Set rss table + * get_tc_size() + * Get tc size of handle + * get_vector() + * Get vector number and vector information + * map_ring_to_vector() + * Map rings to vector + * unmap_ring_from_vector() + * Unmap rings from vector + * add_tunnel_udp() + * Add tunnel information to hardware + * del_tunnel_udp() + * Delete tunnel information from hardware + * reset_queue() + * Reset queue + * get_fw_version() + * Get firmware version + * get_mdix_mode() + * Get media typr of phy + * set_vlan_filter() + * Set vlan filter config of Ports + * set_vf_vlan_filter() + * Set vlan filter config of vf + */ +struct hnae3_ae_ops { + int (*init_ae_dev)(struct hnae3_ae_dev *ae_dev); + void (*uninit_ae_dev)(struct hnae3_ae_dev *ae_dev); + + int (*init_client_instance)(struct hnae3_client *client, + struct hnae3_ae_dev *ae_dev); + void (*uninit_client_instance)(struct hnae3_client *client, + struct hnae3_ae_dev *ae_dev); + int (*start)(struct hnae3_handle *handle); + void (*stop)(struct hnae3_handle *handle); + int (*get_status)(struct hnae3_handle *handle); + void (*get_ksettings_an_result)(struct hnae3_handle *handle, + u8 *auto_neg, u32 *speed, u8 *duplex); + + int (*update_speed_duplex_h)(struct hnae3_handle *handle); + int (*cfg_mac_speed_dup_h)(struct hnae3_handle *handle, int speed, + u8 duplex); + + void (*get_media_type)(struct hnae3_handle *handle, u8 *media_type); + void (*adjust_link)(struct hnae3_handle *handle, int speed, int duplex); + int (*set_loopback)(struct hnae3_handle *handle, + enum hnae3_loop loop_mode, bool en); + + void (*set_promisc_mode)(struct hnae3_handle *handle, u32 en); + int (*set_mtu)(struct hnae3_handle *handle, int new_mtu); + + void (*get_pauseparam)(struct hnae3_handle *handle, + u32 *auto_neg, u32 *rx_en, u32 *tx_en); + int (*set_pauseparam)(struct hnae3_handle *handle, + u32 auto_neg, u32 rx_en, u32 tx_en); + + int (*set_autoneg)(struct hnae3_handle *handle, bool enable); + int (*get_autoneg)(struct hnae3_handle *handle); + + void (*get_coalesce_usecs)(struct hnae3_handle *handle, + u32 *tx_usecs, u32 *rx_usecs); + void (*get_rx_max_coalesced_frames)(struct hnae3_handle *handle, + u32 *tx_frames, u32 *rx_frames); + int (*set_coalesce_usecs)(struct hnae3_handle *handle, u32 timeout); + int (*set_coalesce_frames)(struct hnae3_handle *handle, + u32 coalesce_frames); + void (*get_coalesce_range)(struct hnae3_handle *handle, + u32 *tx_frames_low, u32 *rx_frames_low, + u32 *tx_frames_high, u32 *rx_frames_high, + u32 *tx_usecs_low, u32 *rx_usecs_low, + u32 *tx_usecs_high, u32 *rx_usecs_high); + + void (*get_mac_addr)(struct hnae3_handle *handle, u8 *p); + int (*set_mac_addr)(struct hnae3_handle *handle, void *p); + int (*add_uc_addr)(struct hnae3_handle *handle, + const unsigned char *addr); + int (*rm_uc_addr)(struct hnae3_handle *handle, + const unsigned char *addr); + int (*set_mc_addr)(struct hnae3_handle *handle, void *addr); + int (*add_mc_addr)(struct hnae3_handle *handle, + const unsigned char *addr); + int (*rm_mc_addr)(struct hnae3_handle *handle, + const unsigned char *addr); + + void (*set_tso_stats)(struct hnae3_handle *handle, int enable); + void (*update_stats)(struct hnae3_handle *handle, + struct net_device_stats *net_stats); + void (*get_stats)(struct hnae3_handle *handle, u64 *data); + + void (*get_strings)(struct hnae3_handle *handle, + u32 stringset, u8 *data); + int (*get_sset_count)(struct hnae3_handle *handle, int stringset); + + void (*get_regs)(struct hnae3_handle *handle, void *data); + int (*get_regs_len)(struct hnae3_handle *handle); + + u32 (*get_rss_key_size)(struct hnae3_handle *handle); + u32 (*get_rss_indir_size)(struct hnae3_handle *handle); + int (*get_rss)(struct hnae3_handle *handle, u32 *indir, u8 *key, + u8 *hfunc); + int (*set_rss)(struct hnae3_handle *handle, const u32 *indir, + const u8 *key, const u8 hfunc); + + int (*get_tc_size)(struct hnae3_handle *handle); + + int (*get_vector)(struct hnae3_handle *handle, u16 vector_num, + struct hnae3_vector_info *vector_info); + int (*map_ring_to_vector)(struct hnae3_handle *handle, + int vector_num, + struct hnae3_ring_chain_node *vr_chain); + int (*unmap_ring_from_vector)(struct hnae3_handle *handle, + int vector_num, + struct hnae3_ring_chain_node *vr_chain); + + int (*add_tunnel_udp)(struct hnae3_handle *handle, u16 port_num); + int (*del_tunnel_udp)(struct hnae3_handle *handle, u16 port_num); + + void (*reset_queue)(struct hnae3_handle *handle, u16 queue_id); + u32 (*get_fw_version)(struct hnae3_handle *handle); + void (*get_mdix_mode)(struct hnae3_handle *handle, + u8 *tp_mdix_ctrl, u8 *tp_mdix); + + int (*set_vlan_filter)(struct hnae3_handle *handle, __be16 proto, + u16 vlan_id, bool is_kill); + int (*set_vf_vlan_filter)(struct hnae3_handle *handle, int vfid, + u16 vlan, u8 qos, __be16 proto); +}; + +struct hnae3_ae_algo { + const struct hnae3_ae_ops *ops; + struct list_head node; + char name[HNAE3_CLASS_NAME_SIZE]; + const struct pci_device_id *pdev_id_table; +}; + +#define HNAE3_INT_NAME_LEN (IFNAMSIZ + 16) +#define HNAE3_ITR_COUNTDOWN_START 100 + +struct hnae3_tc_info { + u16 tqp_offset; /* TQP offset from base TQP */ + u16 tqp_count; /* Total TQPs */ + u8 up; /* user priority */ + u8 tc; /* TC index */ + bool enable; /* If this TC is enable or not */ +}; + +#define HNAE3_MAX_TC 8 +struct hnae3_knic_private_info { + struct net_device *netdev; /* Set by KNIC client when init instance */ + u16 rss_size; /* Allocated RSS queues */ + u16 rx_buf_len; + u16 num_desc; + + u8 num_tc; /* Total number of enabled TCs */ + struct hnae3_tc_info tc_info[HNAE3_MAX_TC]; /* Idx of array is HW TC */ + + u16 num_tqps; /* total number of TQPs in this handle */ + struct hnae3_queue **tqp; /* array base of all TQPs in this instance */ +}; + +struct hnae3_roce_private_info { + struct net_device *netdev; + void __iomem *roce_io_base; + int base_vector; + int num_vectors; +}; + +struct hnae3_unic_private_info { + struct net_device *netdev; + u16 rx_buf_len; + u16 num_desc; + u16 num_tqps; /* total number of tqps in this handle */ + struct hnae3_queue **tqp; /* array base of all TQPs of this instance */ +}; + +#define HNAE3_SUPPORT_MAC_LOOPBACK 1 +#define HNAE3_SUPPORT_PHY_LOOPBACK 2 +#define HNAE3_SUPPORT_SERDES_LOOPBACK 4 + +struct hnae3_handle { + struct hnae3_client *client; + struct pci_dev *pdev; + void *priv; + struct hnae3_ae_algo *ae_algo; /* the class who provides this handle */ + u64 flags; /* Indicate the capabilities for this handle*/ + + union { + struct net_device *netdev; /* first member */ + struct hnae3_knic_private_info kinfo; + struct hnae3_unic_private_info uinfo; + struct hnae3_roce_private_info rinfo; + }; + + u32 numa_node_mask; /* for multi-chip support */ +}; + +#define hnae_set_field(origin, mask, shift, val) \ + do { \ + (origin) &= (~(mask)); \ + (origin) |= ((val) << (shift)) & (mask); \ + } while (0) +#define hnae_get_field(origin, mask, shift) (((origin) & (mask)) >> (shift)) + +#define hnae_set_bit(origin, shift, val) \ + hnae_set_field((origin), (0x1 << (shift)), (shift), (val)) +#define hnae_get_bit(origin, shift) \ + hnae_get_field((origin), (0x1 << (shift)), (shift)) + +int hnae3_register_ae_dev(struct hnae3_ae_dev *ae_dev); +void hnae3_unregister_ae_dev(struct hnae3_ae_dev *ae_dev); + +void hnae3_unregister_ae_algo(struct hnae3_ae_algo *ae_algo); +int hnae3_register_ae_algo(struct hnae3_ae_algo *ae_algo); + +void hnae3_unregister_client(struct hnae3_client *client); +int hnae3_register_client(struct hnae3_client *client); +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/Makefile b/drivers/net/ethernet/hisilicon/hns3/hns3pf/Makefile new file mode 100644 index 000000000000..162e8a42acd0 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the HISILICON network device drivers. +# + +ccflags-y := -Idrivers/net/ethernet/hisilicon/hns3 + +obj-$(CONFIG_HNS3_HCLGE) += hclge.o +hclge-objs = hclge_main.o hclge_cmd.o hclge_mdio.o hclge_tm.o + +obj-$(CONFIG_HNS3_ENET) += hns3.o +hns3-objs = hns3_enet.o hns3_ethtool.o diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c new file mode 100644 index 000000000000..8b511e6e0ce9 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/dma-direction.h> +#include "hclge_cmd.h" +#include "hnae3.h" +#include "hclge_main.h" + +#define hclge_is_csq(ring) ((ring)->flag & HCLGE_TYPE_CSQ) +#define hclge_ring_to_dma_dir(ring) (hclge_is_csq(ring) ? \ + DMA_TO_DEVICE : DMA_FROM_DEVICE) +#define cmq_ring_to_dev(ring) (&(ring)->dev->pdev->dev) + +static int hclge_ring_space(struct hclge_cmq_ring *ring) +{ + int ntu = ring->next_to_use; + int ntc = ring->next_to_clean; + int used = (ntu - ntc + ring->desc_num) % ring->desc_num; + + return ring->desc_num - used - 1; +} + +static int hclge_alloc_cmd_desc(struct hclge_cmq_ring *ring) +{ + int size = ring->desc_num * sizeof(struct hclge_desc); + + ring->desc = kzalloc(size, GFP_KERNEL); + if (!ring->desc) + return -ENOMEM; + + ring->desc_dma_addr = dma_map_single(cmq_ring_to_dev(ring), ring->desc, + size, DMA_BIDIRECTIONAL); + if (dma_mapping_error(cmq_ring_to_dev(ring), ring->desc_dma_addr)) { + ring->desc_dma_addr = 0; + kfree(ring->desc); + ring->desc = NULL; + return -ENOMEM; + } + + return 0; +} + +static void hclge_free_cmd_desc(struct hclge_cmq_ring *ring) +{ + dma_unmap_single(cmq_ring_to_dev(ring), ring->desc_dma_addr, + ring->desc_num * sizeof(ring->desc[0]), + DMA_BIDIRECTIONAL); + + ring->desc_dma_addr = 0; + kfree(ring->desc); + ring->desc = NULL; +} + +static int hclge_init_cmd_queue(struct hclge_dev *hdev, int ring_type) +{ + struct hclge_hw *hw = &hdev->hw; + struct hclge_cmq_ring *ring = + (ring_type == HCLGE_TYPE_CSQ) ? &hw->cmq.csq : &hw->cmq.crq; + int ret; + + ring->flag = ring_type; + ring->dev = hdev; + + ret = hclge_alloc_cmd_desc(ring); + if (ret) { + dev_err(&hdev->pdev->dev, "descriptor %s alloc error %d\n", + (ring_type == HCLGE_TYPE_CSQ) ? "CSQ" : "CRQ", ret); + return ret; + } + + ring->next_to_clean = 0; + ring->next_to_use = 0; + + return 0; +} + +void hclge_cmd_setup_basic_desc(struct hclge_desc *desc, + enum hclge_opcode_type opcode, bool is_read) +{ + memset((void *)desc, 0, sizeof(struct hclge_desc)); + desc->opcode = cpu_to_le16(opcode); + desc->flag = cpu_to_le16(HCLGE_CMD_FLAG_NO_INTR | HCLGE_CMD_FLAG_IN); + + if (is_read) + desc->flag |= cpu_to_le16(HCLGE_CMD_FLAG_WR); + else + desc->flag &= cpu_to_le16(~HCLGE_CMD_FLAG_WR); +} + +static void hclge_cmd_config_regs(struct hclge_cmq_ring *ring) +{ + dma_addr_t dma = ring->desc_dma_addr; + struct hclge_dev *hdev = ring->dev; + struct hclge_hw *hw = &hdev->hw; + + if (ring->flag == HCLGE_TYPE_CSQ) { + hclge_write_dev(hw, HCLGE_NIC_CSQ_BASEADDR_L_REG, + (u32)dma); + hclge_write_dev(hw, HCLGE_NIC_CSQ_BASEADDR_H_REG, + (u32)((dma >> 31) >> 1)); + hclge_write_dev(hw, HCLGE_NIC_CSQ_DEPTH_REG, + (ring->desc_num >> HCLGE_NIC_CMQ_DESC_NUM_S) | + HCLGE_NIC_CMQ_ENABLE); + hclge_write_dev(hw, HCLGE_NIC_CSQ_TAIL_REG, 0); + hclge_write_dev(hw, HCLGE_NIC_CSQ_HEAD_REG, 0); + } else { + hclge_write_dev(hw, HCLGE_NIC_CRQ_BASEADDR_L_REG, + (u32)dma); + hclge_write_dev(hw, HCLGE_NIC_CRQ_BASEADDR_H_REG, + (u32)((dma >> 31) >> 1)); + hclge_write_dev(hw, HCLGE_NIC_CRQ_DEPTH_REG, + (ring->desc_num >> HCLGE_NIC_CMQ_DESC_NUM_S) | + HCLGE_NIC_CMQ_ENABLE); + hclge_write_dev(hw, HCLGE_NIC_CRQ_TAIL_REG, 0); + hclge_write_dev(hw, HCLGE_NIC_CRQ_HEAD_REG, 0); + } +} + +static void hclge_cmd_init_regs(struct hclge_hw *hw) +{ + hclge_cmd_config_regs(&hw->cmq.csq); + hclge_cmd_config_regs(&hw->cmq.crq); +} + +static int hclge_cmd_csq_clean(struct hclge_hw *hw) +{ + struct hclge_cmq_ring *csq = &hw->cmq.csq; + u16 ntc = csq->next_to_clean; + struct hclge_desc *desc; + int clean = 0; + u32 head; + + desc = &csq->desc[ntc]; + head = hclge_read_dev(hw, HCLGE_NIC_CSQ_HEAD_REG); + + while (head != ntc) { + memset(desc, 0, sizeof(*desc)); + ntc++; + if (ntc == csq->desc_num) + ntc = 0; + desc = &csq->desc[ntc]; + clean++; + } + csq->next_to_clean = ntc; + + return clean; +} + +static int hclge_cmd_csq_done(struct hclge_hw *hw) +{ + u32 head = hclge_read_dev(hw, HCLGE_NIC_CSQ_HEAD_REG); + return head == hw->cmq.csq.next_to_use; +} + +static bool hclge_is_special_opcode(u16 opcode) +{ + u16 spec_opcode[3] = {0x0030, 0x0031, 0x0032}; + int i; + + for (i = 0; i < ARRAY_SIZE(spec_opcode); i++) { + if (spec_opcode[i] == opcode) + return true; + } + + return false; +} + +/** + * hclge_cmd_send - send command to command queue + * @hw: pointer to the hw struct + * @desc: prefilled descriptor for describing the command + * @num : the number of descriptors to be sent + * + * This is the main send command for command queue, it + * sends the queue, cleans the queue, etc + **/ +int hclge_cmd_send(struct hclge_hw *hw, struct hclge_desc *desc, int num) +{ + struct hclge_dev *hdev = (struct hclge_dev *)hw->back; + struct hclge_desc *desc_to_use; + bool complete = false; + u32 timeout = 0; + int handle = 0; + int retval = 0; + u16 opcode, desc_ret; + int ntc; + + spin_lock_bh(&hw->cmq.csq.lock); + + if (num > hclge_ring_space(&hw->cmq.csq)) { + spin_unlock_bh(&hw->cmq.csq.lock); + return -EBUSY; + } + + /** + * Record the location of desc in the ring for this time + * which will be use for hardware to write back + */ + ntc = hw->cmq.csq.next_to_use; + opcode = desc[0].opcode; + while (handle < num) { + desc_to_use = &hw->cmq.csq.desc[hw->cmq.csq.next_to_use]; + *desc_to_use = desc[handle]; + (hw->cmq.csq.next_to_use)++; + if (hw->cmq.csq.next_to_use == hw->cmq.csq.desc_num) + hw->cmq.csq.next_to_use = 0; + handle++; + } + + /* Write to hardware */ + hclge_write_dev(hw, HCLGE_NIC_CSQ_TAIL_REG, hw->cmq.csq.next_to_use); + + /** + * If the command is sync, wait for the firmware to write back, + * if multi descriptors to be sent, use the first one to check + */ + if (HCLGE_SEND_SYNC(desc->flag)) { + do { + if (hclge_cmd_csq_done(hw)) + break; + udelay(1); + timeout++; + } while (timeout < hw->cmq.tx_timeout); + } + + if (hclge_cmd_csq_done(hw)) { + complete = true; + handle = 0; + while (handle < num) { + /* Get the result of hardware write back */ + desc_to_use = &hw->cmq.csq.desc[ntc]; + desc[handle] = *desc_to_use; + pr_debug("Get cmd desc:\n"); + + if (likely(!hclge_is_special_opcode(opcode))) + desc_ret = desc[handle].retval; + else + desc_ret = desc[0].retval; + + if ((enum hclge_cmd_return_status)desc_ret == + HCLGE_CMD_EXEC_SUCCESS) + retval = 0; + else + retval = -EIO; + hw->cmq.last_status = (enum hclge_cmd_status)desc_ret; + ntc++; + handle++; + if (ntc == hw->cmq.csq.desc_num) + ntc = 0; + } + } + + if (!complete) + retval = -EAGAIN; + + /* Clean the command send queue */ + handle = hclge_cmd_csq_clean(hw); + if (handle != num) { + dev_warn(&hdev->pdev->dev, + "cleaned %d, need to clean %d\n", handle, num); + } + + spin_unlock_bh(&hw->cmq.csq.lock); + + return retval; +} + +enum hclge_cmd_status hclge_cmd_query_firmware_version(struct hclge_hw *hw, + u32 *version) +{ + struct hclge_query_version *resp; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_FW_VER, 1); + resp = (struct hclge_query_version *)desc.data; + + ret = hclge_cmd_send(hw, &desc, 1); + if (!ret) + *version = le32_to_cpu(resp->firmware); + + return ret; +} + +int hclge_cmd_init(struct hclge_dev *hdev) +{ + u32 version; + int ret; + + /* Setup the queue entries for use cmd queue */ + hdev->hw.cmq.csq.desc_num = HCLGE_NIC_CMQ_DESC_NUM; + hdev->hw.cmq.crq.desc_num = HCLGE_NIC_CMQ_DESC_NUM; + + /* Setup the lock for command queue */ + spin_lock_init(&hdev->hw.cmq.csq.lock); + spin_lock_init(&hdev->hw.cmq.crq.lock); + + /* Setup Tx write back timeout */ + hdev->hw.cmq.tx_timeout = HCLGE_CMDQ_TX_TIMEOUT; + + /* Setup queue rings */ + ret = hclge_init_cmd_queue(hdev, HCLGE_TYPE_CSQ); + if (ret) { + dev_err(&hdev->pdev->dev, + "CSQ ring setup error %d\n", ret); + return ret; + } + + ret = hclge_init_cmd_queue(hdev, HCLGE_TYPE_CRQ); + if (ret) { + dev_err(&hdev->pdev->dev, + "CRQ ring setup error %d\n", ret); + goto err_csq; + } + + hclge_cmd_init_regs(&hdev->hw); + + ret = hclge_cmd_query_firmware_version(&hdev->hw, &version); + if (ret) { + dev_err(&hdev->pdev->dev, + "firmware version query failed %d\n", ret); + return ret; + } + hdev->fw_version = version; + + dev_info(&hdev->pdev->dev, "The firmware version is %08x\n", version); + + return 0; +err_csq: + hclge_free_cmd_desc(&hdev->hw.cmq.csq); + return ret; +} + +static void hclge_destroy_queue(struct hclge_cmq_ring *ring) +{ + spin_lock_bh(&ring->lock); + hclge_free_cmd_desc(ring); + spin_unlock_bh(&ring->lock); +} + +void hclge_destroy_cmd_queue(struct hclge_hw *hw) +{ + hclge_destroy_queue(&hw->cmq.csq); + hclge_destroy_queue(&hw->cmq.crq); +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h new file mode 100644 index 000000000000..91ae0135ee50 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h @@ -0,0 +1,740 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HCLGE_CMD_H +#define __HCLGE_CMD_H +#include <linux/types.h> +#include <linux/io.h> + +#define HCLGE_CMDQ_TX_TIMEOUT 1000 + +struct hclge_dev; +struct hclge_desc { + __le16 opcode; + +#define HCLGE_CMDQ_RX_INVLD_B 0 +#define HCLGE_CMDQ_RX_OUTVLD_B 1 + + __le16 flag; + __le16 retval; + __le16 rsv; + __le32 data[6]; +}; + +struct hclge_desc_cb { + dma_addr_t dma; + void *va; + u32 length; +}; + +struct hclge_cmq_ring { + dma_addr_t desc_dma_addr; + struct hclge_desc *desc; + struct hclge_desc_cb *desc_cb; + struct hclge_dev *dev; + u32 head; + u32 tail; + + u16 buf_size; + u16 desc_num; + int next_to_use; + int next_to_clean; + u8 flag; + spinlock_t lock; /* Command queue lock */ +}; + +enum hclge_cmd_return_status { + HCLGE_CMD_EXEC_SUCCESS = 0, + HCLGE_CMD_NO_AUTH = 1, + HCLGE_CMD_NOT_EXEC = 2, + HCLGE_CMD_QUEUE_FULL = 3, +}; + +enum hclge_cmd_status { + HCLGE_STATUS_SUCCESS = 0, + HCLGE_ERR_CSQ_FULL = -1, + HCLGE_ERR_CSQ_TIMEOUT = -2, + HCLGE_ERR_CSQ_ERROR = -3, +}; + +struct hclge_cmq { + struct hclge_cmq_ring csq; + struct hclge_cmq_ring crq; + u16 tx_timeout; /* Tx timeout */ + enum hclge_cmd_status last_status; +}; + +#define HCLGE_CMD_FLAG_IN_VALID_SHIFT 0 +#define HCLGE_CMD_FLAG_OUT_VALID_SHIFT 1 +#define HCLGE_CMD_FLAG_NEXT_SHIFT 2 +#define HCLGE_CMD_FLAG_WR_OR_RD_SHIFT 3 +#define HCLGE_CMD_FLAG_NO_INTR_SHIFT 4 +#define HCLGE_CMD_FLAG_ERR_INTR_SHIFT 5 + +#define HCLGE_CMD_FLAG_IN BIT(HCLGE_CMD_FLAG_IN_VALID_SHIFT) +#define HCLGE_CMD_FLAG_OUT BIT(HCLGE_CMD_FLAG_OUT_VALID_SHIFT) +#define HCLGE_CMD_FLAG_NEXT BIT(HCLGE_CMD_FLAG_NEXT_SHIFT) +#define HCLGE_CMD_FLAG_WR BIT(HCLGE_CMD_FLAG_WR_OR_RD_SHIFT) +#define HCLGE_CMD_FLAG_NO_INTR BIT(HCLGE_CMD_FLAG_NO_INTR_SHIFT) +#define HCLGE_CMD_FLAG_ERR_INTR BIT(HCLGE_CMD_FLAG_ERR_INTR_SHIFT) + +enum hclge_opcode_type { + /* Generic command */ + HCLGE_OPC_QUERY_FW_VER = 0x0001, + HCLGE_OPC_CFG_RST_TRIGGER = 0x0020, + HCLGE_OPC_GBL_RST_STATUS = 0x0021, + HCLGE_OPC_QUERY_FUNC_STATUS = 0x0022, + HCLGE_OPC_QUERY_PF_RSRC = 0x0023, + HCLGE_OPC_QUERY_VF_RSRC = 0x0024, + HCLGE_OPC_GET_CFG_PARAM = 0x0025, + + HCLGE_OPC_STATS_64_BIT = 0x0030, + HCLGE_OPC_STATS_32_BIT = 0x0031, + HCLGE_OPC_STATS_MAC = 0x0032, + /* Device management command */ + + /* MAC commond */ + HCLGE_OPC_CONFIG_MAC_MODE = 0x0301, + HCLGE_OPC_CONFIG_AN_MODE = 0x0304, + HCLGE_OPC_QUERY_AN_RESULT = 0x0306, + HCLGE_OPC_QUERY_LINK_STATUS = 0x0307, + HCLGE_OPC_CONFIG_MAX_FRM_SIZE = 0x0308, + HCLGE_OPC_CONFIG_SPEED_DUP = 0x0309, + /* MACSEC command */ + + /* PFC/Pause CMD*/ + HCLGE_OPC_CFG_MAC_PAUSE_EN = 0x0701, + HCLGE_OPC_CFG_PFC_PAUSE_EN = 0x0702, + HCLGE_OPC_CFG_MAC_PARA = 0x0703, + HCLGE_OPC_CFG_PFC_PARA = 0x0704, + HCLGE_OPC_QUERY_MAC_TX_PKT_CNT = 0x0705, + HCLGE_OPC_QUERY_MAC_RX_PKT_CNT = 0x0706, + HCLGE_OPC_QUERY_PFC_TX_PKT_CNT = 0x0707, + HCLGE_OPC_QUERY_PFC_RX_PKT_CNT = 0x0708, + HCLGE_OPC_PRI_TO_TC_MAPPING = 0x0709, + HCLGE_OPC_QOS_MAP = 0x070A, + + /* ETS/scheduler commands */ + HCLGE_OPC_TM_PG_TO_PRI_LINK = 0x0804, + HCLGE_OPC_TM_QS_TO_PRI_LINK = 0x0805, + HCLGE_OPC_TM_NQ_TO_QS_LINK = 0x0806, + HCLGE_OPC_TM_RQ_TO_QS_LINK = 0x0807, + HCLGE_OPC_TM_PORT_WEIGHT = 0x0808, + HCLGE_OPC_TM_PG_WEIGHT = 0x0809, + HCLGE_OPC_TM_QS_WEIGHT = 0x080A, + HCLGE_OPC_TM_PRI_WEIGHT = 0x080B, + HCLGE_OPC_TM_PRI_C_SHAPPING = 0x080C, + HCLGE_OPC_TM_PRI_P_SHAPPING = 0x080D, + HCLGE_OPC_TM_PG_C_SHAPPING = 0x080E, + HCLGE_OPC_TM_PG_P_SHAPPING = 0x080F, + HCLGE_OPC_TM_PORT_SHAPPING = 0x0810, + HCLGE_OPC_TM_PG_SCH_MODE_CFG = 0x0812, + HCLGE_OPC_TM_PRI_SCH_MODE_CFG = 0x0813, + HCLGE_OPC_TM_QS_SCH_MODE_CFG = 0x0814, + HCLGE_OPC_TM_BP_TO_QSET_MAPPING = 0x0815, + + /* Packet buffer allocate command */ + HCLGE_OPC_TX_BUFF_ALLOC = 0x0901, + HCLGE_OPC_RX_PRIV_BUFF_ALLOC = 0x0902, + HCLGE_OPC_RX_PRIV_WL_ALLOC = 0x0903, + HCLGE_OPC_RX_COM_THRD_ALLOC = 0x0904, + HCLGE_OPC_RX_COM_WL_ALLOC = 0x0905, + HCLGE_OPC_RX_GBL_PKT_CNT = 0x0906, + + /* PTP command */ + /* TQP management command */ + HCLGE_OPC_SET_TQP_MAP = 0x0A01, + + /* TQP command */ + HCLGE_OPC_CFG_TX_QUEUE = 0x0B01, + HCLGE_OPC_QUERY_TX_POINTER = 0x0B02, + HCLGE_OPC_QUERY_TX_STATUS = 0x0B03, + HCLGE_OPC_CFG_RX_QUEUE = 0x0B11, + HCLGE_OPC_QUERY_RX_POINTER = 0x0B12, + HCLGE_OPC_QUERY_RX_STATUS = 0x0B13, + HCLGE_OPC_STASH_RX_QUEUE_LRO = 0x0B16, + HCLGE_OPC_CFG_RX_QUEUE_LRO = 0x0B17, + HCLGE_OPC_CFG_COM_TQP_QUEUE = 0x0B20, + HCLGE_OPC_RESET_TQP_QUEUE = 0x0B22, + + /* TSO cmd */ + HCLGE_OPC_TSO_GENERIC_CONFIG = 0x0C01, + + /* RSS cmd */ + HCLGE_OPC_RSS_GENERIC_CONFIG = 0x0D01, + HCLGE_OPC_RSS_INDIR_TABLE = 0x0D07, + HCLGE_OPC_RSS_TC_MODE = 0x0D08, + HCLGE_OPC_RSS_INPUT_TUPLE = 0x0D02, + + /* Promisuous mode command */ + HCLGE_OPC_CFG_PROMISC_MODE = 0x0E01, + + /* Interrupts cmd */ + HCLGE_OPC_ADD_RING_TO_VECTOR = 0x1503, + HCLGE_OPC_DEL_RING_TO_VECTOR = 0x1504, + + /* MAC command */ + HCLGE_OPC_MAC_VLAN_ADD = 0x1000, + HCLGE_OPC_MAC_VLAN_REMOVE = 0x1001, + HCLGE_OPC_MAC_VLAN_TYPE_ID = 0x1002, + HCLGE_OPC_MAC_VLAN_INSERT = 0x1003, + HCLGE_OPC_MAC_ETHTYPE_ADD = 0x1010, + HCLGE_OPC_MAC_ETHTYPE_REMOVE = 0x1011, + + /* Multicast linear table cmd */ + HCLGE_OPC_MTA_MAC_MODE_CFG = 0x1020, + HCLGE_OPC_MTA_MAC_FUNC_CFG = 0x1021, + HCLGE_OPC_MTA_TBL_ITEM_CFG = 0x1022, + HCLGE_OPC_MTA_TBL_ITEM_QUERY = 0x1023, + + /* VLAN command */ + HCLGE_OPC_VLAN_FILTER_CTRL = 0x1100, + HCLGE_OPC_VLAN_FILTER_PF_CFG = 0x1101, + HCLGE_OPC_VLAN_FILTER_VF_CFG = 0x1102, + + /* MDIO command */ + HCLGE_OPC_MDIO_CONFIG = 0x1900, + + /* QCN command */ + HCLGE_OPC_QCN_MOD_CFG = 0x1A01, + HCLGE_OPC_QCN_GRP_TMPLT_CFG = 0x1A02, + HCLGE_OPC_QCN_SHAPPING_IR_CFG = 0x1A03, + HCLGE_OPC_QCN_SHAPPING_BS_CFG = 0x1A04, + HCLGE_OPC_QCN_QSET_LINK_CFG = 0x1A05, + HCLGE_OPC_QCN_RP_STATUS_GET = 0x1A06, + HCLGE_OPC_QCN_AJUST_INIT = 0x1A07, + HCLGE_OPC_QCN_DFX_CNT_STATUS = 0x1A08, + + /* Mailbox cmd */ + HCLGEVF_OPC_MBX_PF_TO_VF = 0x2000, +}; + +#define HCLGE_TQP_REG_OFFSET 0x80000 +#define HCLGE_TQP_REG_SIZE 0x200 + +#define HCLGE_RCB_INIT_QUERY_TIMEOUT 10 +#define HCLGE_RCB_INIT_FLAG_EN_B 0 +#define HCLGE_RCB_INIT_FLAG_FINI_B 8 +struct hclge_config_rcb_init { + __le16 rcb_init_flag; + u8 rsv[22]; +}; + +struct hclge_tqp_map { + __le16 tqp_id; /* Absolute tqp id for in this pf */ + u8 tqp_vf; /* VF id */ +#define HCLGE_TQP_MAP_TYPE_PF 0 +#define HCLGE_TQP_MAP_TYPE_VF 1 +#define HCLGE_TQP_MAP_TYPE_B 0 +#define HCLGE_TQP_MAP_EN_B 1 + u8 tqp_flag; /* Indicate it's pf or vf tqp */ + __le16 tqp_vid; /* Virtual id in this pf/vf */ + u8 rsv[18]; +}; + +#define HCLGE_VECTOR_ELEMENTS_PER_CMD 11 + +enum hclge_int_type { + HCLGE_INT_TX, + HCLGE_INT_RX, + HCLGE_INT_EVENT, +}; + +struct hclge_ctrl_vector_chain { + u8 int_vector_id; + u8 int_cause_num; +#define HCLGE_INT_TYPE_S 0 +#define HCLGE_INT_TYPE_M 0x3 +#define HCLGE_TQP_ID_S 2 +#define HCLGE_TQP_ID_M (0x3fff << HCLGE_TQP_ID_S) + __le16 tqp_type_and_id[HCLGE_VECTOR_ELEMENTS_PER_CMD]; +}; + +#define HCLGE_TC_NUM 8 +#define HCLGE_TC0_PRI_BUF_EN_B 15 /* Bit 15 indicate enable or not */ +#define HCLGE_BUF_UNIT_S 7 /* Buf size is united by 128 bytes */ +struct hclge_tx_buff_alloc { + __le16 tx_pkt_buff[HCLGE_TC_NUM]; + u8 tx_buff_rsv[8]; +}; + +struct hclge_rx_priv_buff { + __le16 buf_num[HCLGE_TC_NUM]; + u8 rsv[8]; +}; + +struct hclge_query_version { + __le32 firmware; + __le32 firmware_rsv[5]; +}; + +#define HCLGE_RX_PRIV_EN_B 15 +#define HCLGE_TC_NUM_ONE_DESC 4 +struct hclge_priv_wl { + __le16 high; + __le16 low; +}; + +struct hclge_rx_priv_wl_buf { + struct hclge_priv_wl tc_wl[HCLGE_TC_NUM_ONE_DESC]; +}; + +struct hclge_rx_com_thrd { + struct hclge_priv_wl com_thrd[HCLGE_TC_NUM_ONE_DESC]; +}; + +struct hclge_rx_com_wl { + struct hclge_priv_wl com_wl; +}; + +struct hclge_waterline { + u32 low; + u32 high; +}; + +struct hclge_tc_thrd { + u32 low; + u32 high; +}; + +struct hclge_priv_buf { + struct hclge_waterline wl; /* Waterline for low and high*/ + u32 buf_size; /* TC private buffer size */ + u32 enable; /* Enable TC private buffer or not */ +}; + +#define HCLGE_MAX_TC_NUM 8 +struct hclge_shared_buf { + struct hclge_waterline self; + struct hclge_tc_thrd tc_thrd[HCLGE_MAX_TC_NUM]; + u32 buf_size; +}; + +#define HCLGE_RX_COM_WL_EN_B 15 +struct hclge_rx_com_wl_buf { + __le16 high_wl; + __le16 low_wl; + u8 rsv[20]; +}; + +#define HCLGE_RX_PKT_EN_B 15 +struct hclge_rx_pkt_buf { + __le16 high_pkt; + __le16 low_pkt; + u8 rsv[20]; +}; + +#define HCLGE_PF_STATE_DONE_B 0 +#define HCLGE_PF_STATE_MAIN_B 1 +#define HCLGE_PF_STATE_BOND_B 2 +#define HCLGE_PF_STATE_MAC_N_B 6 +#define HCLGE_PF_MAC_NUM_MASK 0x3 +#define HCLGE_PF_STATE_MAIN BIT(HCLGE_PF_STATE_MAIN_B) +#define HCLGE_PF_STATE_DONE BIT(HCLGE_PF_STATE_DONE_B) +struct hclge_func_status { + __le32 vf_rst_state[4]; + u8 pf_state; + u8 mac_id; + u8 rsv1; + u8 pf_cnt_in_mac; + u8 pf_num; + u8 vf_num; + u8 rsv[2]; +}; + +struct hclge_pf_res { + __le16 tqp_num; + __le16 buf_size; + __le16 msixcap_localid_ba_nic; + __le16 msixcap_localid_ba_rocee; +#define HCLGE_PF_VEC_NUM_S 0 +#define HCLGE_PF_VEC_NUM_M (0xff << HCLGE_PF_VEC_NUM_S) + __le16 pf_intr_vector_number; + __le16 pf_own_fun_number; + __le32 rsv[3]; +}; + +#define HCLGE_CFG_OFFSET_S 0 +#define HCLGE_CFG_OFFSET_M 0xfffff /* Byte (8-10.3) */ +#define HCLGE_CFG_RD_LEN_S 24 +#define HCLGE_CFG_RD_LEN_M (0xf << HCLGE_CFG_RD_LEN_S) +#define HCLGE_CFG_RD_LEN_BYTES 16 +#define HCLGE_CFG_RD_LEN_UNIT 4 + +#define HCLGE_CFG_VMDQ_S 0 +#define HCLGE_CFG_VMDQ_M (0xff << HCLGE_CFG_VMDQ_S) +#define HCLGE_CFG_TC_NUM_S 8 +#define HCLGE_CFG_TC_NUM_M (0xff << HCLGE_CFG_TC_NUM_S) +#define HCLGE_CFG_TQP_DESC_N_S 16 +#define HCLGE_CFG_TQP_DESC_N_M (0xffff << HCLGE_CFG_TQP_DESC_N_S) +#define HCLGE_CFG_PHY_ADDR_S 0 +#define HCLGE_CFG_PHY_ADDR_M (0x1f << HCLGE_CFG_PHY_ADDR_S) +#define HCLGE_CFG_MEDIA_TP_S 8 +#define HCLGE_CFG_MEDIA_TP_M (0xff << HCLGE_CFG_MEDIA_TP_S) +#define HCLGE_CFG_RX_BUF_LEN_S 16 +#define HCLGE_CFG_RX_BUF_LEN_M (0xffff << HCLGE_CFG_RX_BUF_LEN_S) +#define HCLGE_CFG_MAC_ADDR_H_S 0 +#define HCLGE_CFG_MAC_ADDR_H_M (0xffff << HCLGE_CFG_MAC_ADDR_H_S) +#define HCLGE_CFG_DEFAULT_SPEED_S 16 +#define HCLGE_CFG_DEFAULT_SPEED_M (0xff << HCLGE_CFG_DEFAULT_SPEED_S) + +struct hclge_cfg_param { + __le32 offset; + __le32 rsv; + __le32 param[4]; +}; + +#define HCLGE_MAC_MODE 0x0 +#define HCLGE_DESC_NUM 0x40 + +#define HCLGE_ALLOC_VALID_B 0 +struct hclge_vf_num { + u8 alloc_valid; + u8 rsv[23]; +}; + +#define HCLGE_RSS_DEFAULT_OUTPORT_B 4 +#define HCLGE_RSS_HASH_KEY_OFFSET_B 4 +#define HCLGE_RSS_HASH_KEY_NUM 16 +struct hclge_rss_config { + u8 hash_config; + u8 rsv[7]; + u8 hash_key[HCLGE_RSS_HASH_KEY_NUM]; +}; + +struct hclge_rss_input_tuple { + u8 ipv4_tcp_en; + u8 ipv4_udp_en; + u8 ipv4_sctp_en; + u8 ipv4_fragment_en; + u8 ipv6_tcp_en; + u8 ipv6_udp_en; + u8 ipv6_sctp_en; + u8 ipv6_fragment_en; + u8 rsv[16]; +}; + +#define HCLGE_RSS_CFG_TBL_SIZE 16 + +struct hclge_rss_indirection_table { + u16 start_table_index; + u16 rss_set_bitmap; + u8 rsv[4]; + u8 rss_result[HCLGE_RSS_CFG_TBL_SIZE]; +}; + +#define HCLGE_RSS_TC_OFFSET_S 0 +#define HCLGE_RSS_TC_OFFSET_M (0x3ff << HCLGE_RSS_TC_OFFSET_S) +#define HCLGE_RSS_TC_SIZE_S 12 +#define HCLGE_RSS_TC_SIZE_M (0x7 << HCLGE_RSS_TC_SIZE_S) +#define HCLGE_RSS_TC_VALID_B 15 +struct hclge_rss_tc_mode { + u16 rss_tc_mode[HCLGE_MAX_TC_NUM]; + u8 rsv[8]; +}; + +#define HCLGE_LINK_STS_B 0 +#define HCLGE_LINK_STATUS BIT(HCLGE_LINK_STS_B) +struct hclge_link_status { + u8 status; + u8 rsv[23]; +}; + +struct hclge_promisc_param { + u8 vf_id; + u8 enable; +}; + +#define HCLGE_PROMISC_EN_B 1 +#define HCLGE_PROMISC_EN_ALL 0x7 +#define HCLGE_PROMISC_EN_UC 0x1 +#define HCLGE_PROMISC_EN_MC 0x2 +#define HCLGE_PROMISC_EN_BC 0x4 +struct hclge_promisc_cfg { + u8 flag; + u8 vf_id; + __le16 rsv0; + u8 rsv1[20]; +}; + +enum hclge_promisc_type { + HCLGE_UNICAST = 1, + HCLGE_MULTICAST = 2, + HCLGE_BROADCAST = 3, +}; + +#define HCLGE_MAC_TX_EN_B 6 +#define HCLGE_MAC_RX_EN_B 7 +#define HCLGE_MAC_PAD_TX_B 11 +#define HCLGE_MAC_PAD_RX_B 12 +#define HCLGE_MAC_1588_TX_B 13 +#define HCLGE_MAC_1588_RX_B 14 +#define HCLGE_MAC_APP_LP_B 15 +#define HCLGE_MAC_LINE_LP_B 16 +#define HCLGE_MAC_FCS_TX_B 17 +#define HCLGE_MAC_RX_OVERSIZE_TRUNCATE_B 18 +#define HCLGE_MAC_RX_FCS_STRIP_B 19 +#define HCLGE_MAC_RX_FCS_B 20 +#define HCLGE_MAC_TX_UNDER_MIN_ERR_B 21 +#define HCLGE_MAC_TX_OVERSIZE_TRUNCATE_B 22 + +struct hclge_config_mac_mode { + __le32 txrx_pad_fcs_loop_en; + u8 rsv[20]; +}; + +#define HCLGE_CFG_SPEED_S 0 +#define HCLGE_CFG_SPEED_M (0x3f << HCLGE_CFG_SPEED_S) + +#define HCLGE_CFG_DUPLEX_B 7 +#define HCLGE_CFG_DUPLEX_M BIT(HCLGE_CFG_DUPLEX_B) + +struct hclge_config_mac_speed_dup { + u8 speed_dup; + +#define HCLGE_CFG_MAC_SPEED_CHANGE_EN_B 0 + u8 mac_change_fec_en; + u8 rsv[22]; +}; + +#define HCLGE_QUERY_SPEED_S 3 +#define HCLGE_QUERY_AN_B 0 +#define HCLGE_QUERY_DUPLEX_B 2 + +#define HCLGE_QUERY_SPEED_M (0x1f << HCLGE_QUERY_SPEED_S) +#define HCLGE_QUERY_AN_M BIT(HCLGE_QUERY_AN_B) +#define HCLGE_QUERY_DUPLEX_M BIT(HCLGE_QUERY_DUPLEX_B) + +struct hclge_query_an_speed_dup { + u8 an_syn_dup_speed; + u8 pause; + u8 rsv[23]; +}; + +#define HCLGE_RING_ID_MASK 0x3ff +#define HCLGE_TQP_ENABLE_B 0 + +#define HCLGE_MAC_CFG_AN_EN_B 0 +#define HCLGE_MAC_CFG_AN_INT_EN_B 1 +#define HCLGE_MAC_CFG_AN_INT_MSK_B 2 +#define HCLGE_MAC_CFG_AN_INT_CLR_B 3 +#define HCLGE_MAC_CFG_AN_RST_B 4 + +#define HCLGE_MAC_CFG_AN_EN BIT(HCLGE_MAC_CFG_AN_EN_B) + +struct hclge_config_auto_neg { + __le32 cfg_an_cmd_flag; + u8 rsv[20]; +}; + +#define HCLGE_MAC_MIN_MTU 64 +#define HCLGE_MAC_MAX_MTU 9728 +#define HCLGE_MAC_UPLINK_PORT 0x100 + +struct hclge_config_max_frm_size { + __le16 max_frm_size; + u8 rsv[22]; +}; + +enum hclge_mac_vlan_tbl_opcode { + HCLGE_MAC_VLAN_ADD, /* Add new or modify mac_vlan */ + HCLGE_MAC_VLAN_UPDATE, /* Modify other fields of this table */ + HCLGE_MAC_VLAN_REMOVE, /* Remove a entry through mac_vlan key */ + HCLGE_MAC_VLAN_LKUP, /* Lookup a entry through mac_vlan key */ +}; + +#define HCLGE_MAC_VLAN_BIT0_EN_B 0x0 +#define HCLGE_MAC_VLAN_BIT1_EN_B 0x1 +#define HCLGE_MAC_EPORT_SW_EN_B 0xc +#define HCLGE_MAC_EPORT_TYPE_B 0xb +#define HCLGE_MAC_EPORT_VFID_S 0x3 +#define HCLGE_MAC_EPORT_VFID_M (0xff << HCLGE_MAC_EPORT_VFID_S) +#define HCLGE_MAC_EPORT_PFID_S 0x0 +#define HCLGE_MAC_EPORT_PFID_M (0x7 << HCLGE_MAC_EPORT_PFID_S) +struct hclge_mac_vlan_tbl_entry { + u8 flags; + u8 resp_code; + __le16 vlan_tag; + __le32 mac_addr_hi32; + __le16 mac_addr_lo16; + __le16 rsv1; + u8 entry_type; + u8 mc_mac_en; + __le16 egress_port; + __le16 egress_queue; + u8 rsv2[6]; +}; + +#define HCLGE_CFG_MTA_MAC_SEL_S 0x0 +#define HCLGE_CFG_MTA_MAC_SEL_M (0x3 << HCLGE_CFG_MTA_MAC_SEL_S) +#define HCLGE_CFG_MTA_MAC_EN_B 0x7 +struct hclge_mta_filter_mode { + u8 dmac_sel_en; /* Use lowest 2 bit as sel_mode, bit 7 as enable */ + u8 rsv[23]; +}; + +#define HCLGE_CFG_FUNC_MTA_ACCEPT_B 0x0 +struct hclge_cfg_func_mta_filter { + u8 accept; /* Only used lowest 1 bit */ + u8 function_id; + u8 rsv[22]; +}; + +#define HCLGE_CFG_MTA_ITEM_ACCEPT_B 0x0 +#define HCLGE_CFG_MTA_ITEM_IDX_S 0x0 +#define HCLGE_CFG_MTA_ITEM_IDX_M (0xfff << HCLGE_CFG_MTA_ITEM_IDX_S) +struct hclge_cfg_func_mta_item { + u16 item_idx; /* Only used lowest 12 bit */ + u8 accept; /* Only used lowest 1 bit */ + u8 rsv[21]; +}; + +struct hclge_mac_vlan_add { + __le16 flags; + __le16 mac_addr_hi16; + __le32 mac_addr_lo32; + __le32 mac_addr_msk_hi32; + __le16 mac_addr_msk_lo16; + __le16 vlan_tag; + __le16 ingress_port; + __le16 egress_port; + u8 rsv[4]; +}; + +#define HNS3_MAC_VLAN_CFG_FLAG_BIT 0 +struct hclge_mac_vlan_remove { + __le16 flags; + __le16 mac_addr_hi16; + __le32 mac_addr_lo32; + __le32 mac_addr_msk_hi32; + __le16 mac_addr_msk_lo16; + __le16 vlan_tag; + __le16 ingress_port; + __le16 egress_port; + u8 rsv[4]; +}; + +struct hclge_vlan_filter_ctrl { + u8 vlan_type; + u8 vlan_fe; + u8 rsv[22]; +}; + +struct hclge_vlan_filter_pf_cfg { + u8 vlan_offset; + u8 vlan_cfg; + u8 rsv[2]; + u8 vlan_offset_bitmap[20]; +}; + +struct hclge_vlan_filter_vf_cfg { + u16 vlan_id; + u8 resp_code; + u8 rsv; + u8 vlan_cfg; + u8 rsv1[3]; + u8 vf_bitmap[16]; +}; + +struct hclge_cfg_com_tqp_queue { + __le16 tqp_id; + __le16 stream_id; + u8 enable; + u8 rsv[19]; +}; + +struct hclge_cfg_tx_queue_pointer { + __le16 tqp_id; + __le16 tx_tail; + __le16 tx_head; + __le16 fbd_num; + __le16 ring_offset; + u8 rsv[14]; +}; + +#define HCLGE_TSO_MSS_MIN_S 0 +#define HCLGE_TSO_MSS_MIN_M (0x3FFF << HCLGE_TSO_MSS_MIN_S) + +#define HCLGE_TSO_MSS_MAX_S 16 +#define HCLGE_TSO_MSS_MAX_M (0x3FFF << HCLGE_TSO_MSS_MAX_S) + +struct hclge_cfg_tso_status { + __le16 tso_mss_min; + __le16 tso_mss_max; + u8 rsv[20]; +}; + +#define HCLGE_TSO_MSS_MIN 256 +#define HCLGE_TSO_MSS_MAX 9668 + +#define HCLGE_TQP_RESET_B 0 +struct hclge_reset_tqp_queue { + __le16 tqp_id; + u8 reset_req; + u8 ready_to_reset; + u8 rsv[20]; +}; + +#define HCLGE_DEFAULT_TX_BUF 0x4000 /* 16k bytes */ +#define HCLGE_TOTAL_PKT_BUF 0x108000 /* 1.03125M bytes */ +#define HCLGE_DEFAULT_DV 0xA000 /* 40k byte */ + +#define HCLGE_TYPE_CRQ 0 +#define HCLGE_TYPE_CSQ 1 +#define HCLGE_NIC_CSQ_BASEADDR_L_REG 0x27000 +#define HCLGE_NIC_CSQ_BASEADDR_H_REG 0x27004 +#define HCLGE_NIC_CSQ_DEPTH_REG 0x27008 +#define HCLGE_NIC_CSQ_TAIL_REG 0x27010 +#define HCLGE_NIC_CSQ_HEAD_REG 0x27014 +#define HCLGE_NIC_CRQ_BASEADDR_L_REG 0x27018 +#define HCLGE_NIC_CRQ_BASEADDR_H_REG 0x2701c +#define HCLGE_NIC_CRQ_DEPTH_REG 0x27020 +#define HCLGE_NIC_CRQ_TAIL_REG 0x27024 +#define HCLGE_NIC_CRQ_HEAD_REG 0x27028 +#define HCLGE_NIC_CMQ_EN_B 16 +#define HCLGE_NIC_CMQ_ENABLE BIT(HCLGE_NIC_CMQ_EN_B) +#define HCLGE_NIC_CMQ_DESC_NUM 1024 +#define HCLGE_NIC_CMQ_DESC_NUM_S 3 + +int hclge_cmd_init(struct hclge_dev *hdev); +static inline void hclge_write_reg(void __iomem *base, u32 reg, u32 value) +{ + writel(value, base + reg); +} + +#define hclge_write_dev(a, reg, value) \ + hclge_write_reg((a)->io_base, (reg), (value)) +#define hclge_read_dev(a, reg) \ + hclge_read_reg((a)->io_base, (reg)) + +static inline u32 hclge_read_reg(u8 __iomem *base, u32 reg) +{ + u8 __iomem *reg_addr = READ_ONCE(base); + + return readl(reg_addr + reg); +} + +#define HCLGE_SEND_SYNC(flag) \ + ((flag) & HCLGE_CMD_FLAG_NO_INTR) + +struct hclge_hw; +int hclge_cmd_send(struct hclge_hw *hw, struct hclge_desc *desc, int num); +void hclge_cmd_setup_basic_desc(struct hclge_desc *desc, + enum hclge_opcode_type opcode, bool is_read); + +int hclge_cmd_set_promisc_mode(struct hclge_dev *hdev, + struct hclge_promisc_param *param); + +enum hclge_cmd_status hclge_cmd_mdio_write(struct hclge_hw *hw, + struct hclge_desc *desc); +enum hclge_cmd_status hclge_cmd_mdio_read(struct hclge_hw *hw, + struct hclge_desc *desc); + +void hclge_destroy_cmd_queue(struct hclge_hw *hw); +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c new file mode 100644 index 000000000000..bb45365fb817 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -0,0 +1,4265 @@ +/* + * Copyright (c) 2016-2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/etherdevice.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/pci.h> +#include <linux/platform_device.h> + +#include "hclge_cmd.h" +#include "hclge_main.h" +#include "hclge_mdio.h" +#include "hclge_tm.h" +#include "hnae3.h" + +#define HCLGE_NAME "hclge" +#define HCLGE_STATS_READ(p, offset) (*((u64 *)((u8 *)(p) + (offset)))) +#define HCLGE_MAC_STATS_FIELD_OFF(f) (offsetof(struct hclge_mac_stats, f)) +#define HCLGE_64BIT_STATS_FIELD_OFF(f) (offsetof(struct hclge_64_bit_stats, f)) +#define HCLGE_32BIT_STATS_FIELD_OFF(f) (offsetof(struct hclge_32_bit_stats, f)) + +static int hclge_rss_init_hw(struct hclge_dev *hdev); +static int hclge_set_mta_filter_mode(struct hclge_dev *hdev, + enum hclge_mta_dmac_sel_type mta_mac_sel, + bool enable); +static int hclge_init_vlan_config(struct hclge_dev *hdev); + +static struct hnae3_ae_algo ae_algo; + +static const struct pci_device_id ae_algo_pci_tbl[] = { + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_GE), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA_MACSEC), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA_MACSEC), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_RDMA_MACSEC), 0}, + /* Required last entry */ + {0, } +}; + +static const struct pci_device_id roce_pci_tbl[] = { + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA_MACSEC), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA_MACSEC), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_RDMA_MACSEC), 0}, + /* Required last entry */ + {0, } +}; + +static const char hns3_nic_test_strs[][ETH_GSTRING_LEN] = { + "Mac Loopback test", + "Serdes Loopback test", + "Phy Loopback test" +}; + +static const struct hclge_comm_stats_str g_all_64bit_stats_string[] = { + {"igu_rx_oversize_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(igu_rx_oversize_pkt)}, + {"igu_rx_undersize_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(igu_rx_undersize_pkt)}, + {"igu_rx_out_all_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(igu_rx_out_all_pkt)}, + {"igu_rx_uni_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(igu_rx_uni_pkt)}, + {"igu_rx_multi_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(igu_rx_multi_pkt)}, + {"igu_rx_broad_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(igu_rx_broad_pkt)}, + {"egu_tx_out_all_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(egu_tx_out_all_pkt)}, + {"egu_tx_uni_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(egu_tx_uni_pkt)}, + {"egu_tx_multi_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(egu_tx_multi_pkt)}, + {"egu_tx_broad_pkt", + HCLGE_64BIT_STATS_FIELD_OFF(egu_tx_broad_pkt)}, + {"ssu_ppp_mac_key_num", + HCLGE_64BIT_STATS_FIELD_OFF(ssu_ppp_mac_key_num)}, + {"ssu_ppp_host_key_num", + HCLGE_64BIT_STATS_FIELD_OFF(ssu_ppp_host_key_num)}, + {"ppp_ssu_mac_rlt_num", + HCLGE_64BIT_STATS_FIELD_OFF(ppp_ssu_mac_rlt_num)}, + {"ppp_ssu_host_rlt_num", + HCLGE_64BIT_STATS_FIELD_OFF(ppp_ssu_host_rlt_num)}, + {"ssu_tx_in_num", + HCLGE_64BIT_STATS_FIELD_OFF(ssu_tx_in_num)}, + {"ssu_tx_out_num", + HCLGE_64BIT_STATS_FIELD_OFF(ssu_tx_out_num)}, + {"ssu_rx_in_num", + HCLGE_64BIT_STATS_FIELD_OFF(ssu_rx_in_num)}, + {"ssu_rx_out_num", + HCLGE_64BIT_STATS_FIELD_OFF(ssu_rx_out_num)} +}; + +static const struct hclge_comm_stats_str g_all_32bit_stats_string[] = { + {"igu_rx_err_pkt", + HCLGE_32BIT_STATS_FIELD_OFF(igu_rx_err_pkt)}, + {"igu_rx_no_eof_pkt", + HCLGE_32BIT_STATS_FIELD_OFF(igu_rx_no_eof_pkt)}, + {"igu_rx_no_sof_pkt", + HCLGE_32BIT_STATS_FIELD_OFF(igu_rx_no_sof_pkt)}, + {"egu_tx_1588_pkt", + HCLGE_32BIT_STATS_FIELD_OFF(egu_tx_1588_pkt)}, + {"ssu_full_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(ssu_full_drop_num)}, + {"ssu_part_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(ssu_part_drop_num)}, + {"ppp_key_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(ppp_key_drop_num)}, + {"ppp_rlt_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(ppp_rlt_drop_num)}, + {"ssu_key_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(ssu_key_drop_num)}, + {"pkt_curr_buf_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_cnt)}, + {"qcn_fb_rcv_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(qcn_fb_rcv_cnt)}, + {"qcn_fb_drop_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(qcn_fb_drop_cnt)}, + {"qcn_fb_invaild_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(qcn_fb_invaild_cnt)}, + {"rx_packet_tc0_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc0_in_cnt)}, + {"rx_packet_tc1_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc1_in_cnt)}, + {"rx_packet_tc2_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc2_in_cnt)}, + {"rx_packet_tc3_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc3_in_cnt)}, + {"rx_packet_tc4_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc4_in_cnt)}, + {"rx_packet_tc5_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc5_in_cnt)}, + {"rx_packet_tc6_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc6_in_cnt)}, + {"rx_packet_tc7_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc7_in_cnt)}, + {"rx_packet_tc0_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc0_out_cnt)}, + {"rx_packet_tc1_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc1_out_cnt)}, + {"rx_packet_tc2_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc2_out_cnt)}, + {"rx_packet_tc3_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc3_out_cnt)}, + {"rx_packet_tc4_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc4_out_cnt)}, + {"rx_packet_tc5_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc5_out_cnt)}, + {"rx_packet_tc6_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc6_out_cnt)}, + {"rx_packet_tc7_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_packet_tc7_out_cnt)}, + {"tx_packet_tc0_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc0_in_cnt)}, + {"tx_packet_tc1_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc1_in_cnt)}, + {"tx_packet_tc2_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc2_in_cnt)}, + {"tx_packet_tc3_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc3_in_cnt)}, + {"tx_packet_tc4_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc4_in_cnt)}, + {"tx_packet_tc5_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc5_in_cnt)}, + {"tx_packet_tc6_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc6_in_cnt)}, + {"tx_packet_tc7_in_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc7_in_cnt)}, + {"tx_packet_tc0_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc0_out_cnt)}, + {"tx_packet_tc1_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc1_out_cnt)}, + {"tx_packet_tc2_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc2_out_cnt)}, + {"tx_packet_tc3_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc3_out_cnt)}, + {"tx_packet_tc4_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc4_out_cnt)}, + {"tx_packet_tc5_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc5_out_cnt)}, + {"tx_packet_tc6_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc6_out_cnt)}, + {"tx_packet_tc7_out_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_packet_tc7_out_cnt)}, + {"pkt_curr_buf_tc0_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc0_cnt)}, + {"pkt_curr_buf_tc1_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc1_cnt)}, + {"pkt_curr_buf_tc2_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc2_cnt)}, + {"pkt_curr_buf_tc3_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc3_cnt)}, + {"pkt_curr_buf_tc4_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc4_cnt)}, + {"pkt_curr_buf_tc5_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc5_cnt)}, + {"pkt_curr_buf_tc6_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc6_cnt)}, + {"pkt_curr_buf_tc7_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(pkt_curr_buf_tc7_cnt)}, + {"mb_uncopy_num", + HCLGE_32BIT_STATS_FIELD_OFF(mb_uncopy_num)}, + {"lo_pri_unicast_rlt_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(lo_pri_unicast_rlt_drop_num)}, + {"hi_pri_multicast_rlt_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(hi_pri_multicast_rlt_drop_num)}, + {"lo_pri_multicast_rlt_drop_num", + HCLGE_32BIT_STATS_FIELD_OFF(lo_pri_multicast_rlt_drop_num)}, + {"rx_oq_drop_pkt_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(rx_oq_drop_pkt_cnt)}, + {"tx_oq_drop_pkt_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(tx_oq_drop_pkt_cnt)}, + {"nic_l2_err_drop_pkt_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(nic_l2_err_drop_pkt_cnt)}, + {"roc_l2_err_drop_pkt_cnt", + HCLGE_32BIT_STATS_FIELD_OFF(roc_l2_err_drop_pkt_cnt)} +}; + +static const struct hclge_comm_stats_str g_mac_stats_string[] = { + {"mac_tx_mac_pause_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_mac_pause_num)}, + {"mac_rx_mac_pause_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_mac_pause_num)}, + {"mac_tx_pfc_pri0_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri0_pkt_num)}, + {"mac_tx_pfc_pri1_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri1_pkt_num)}, + {"mac_tx_pfc_pri2_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri2_pkt_num)}, + {"mac_tx_pfc_pri3_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri3_pkt_num)}, + {"mac_tx_pfc_pri4_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri4_pkt_num)}, + {"mac_tx_pfc_pri5_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri5_pkt_num)}, + {"mac_tx_pfc_pri6_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri6_pkt_num)}, + {"mac_tx_pfc_pri7_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_pfc_pri7_pkt_num)}, + {"mac_rx_pfc_pri0_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri0_pkt_num)}, + {"mac_rx_pfc_pri1_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri1_pkt_num)}, + {"mac_rx_pfc_pri2_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri2_pkt_num)}, + {"mac_rx_pfc_pri3_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri3_pkt_num)}, + {"mac_rx_pfc_pri4_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri4_pkt_num)}, + {"mac_rx_pfc_pri5_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri5_pkt_num)}, + {"mac_rx_pfc_pri6_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri6_pkt_num)}, + {"mac_rx_pfc_pri7_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_pfc_pri7_pkt_num)}, + {"mac_tx_total_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_total_pkt_num)}, + {"mac_tx_total_oct_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_total_oct_num)}, + {"mac_tx_good_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_good_pkt_num)}, + {"mac_tx_bad_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_bad_pkt_num)}, + {"mac_tx_good_oct_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_good_oct_num)}, + {"mac_tx_bad_oct_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_bad_oct_num)}, + {"mac_tx_uni_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_uni_pkt_num)}, + {"mac_tx_multi_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_multi_pkt_num)}, + {"mac_tx_broad_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_broad_pkt_num)}, + {"mac_tx_undersize_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_undersize_pkt_num)}, + {"mac_tx_overrsize_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_overrsize_pkt_num)}, + {"mac_tx_64_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_64_oct_pkt_num)}, + {"mac_tx_65_127_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_65_127_oct_pkt_num)}, + {"mac_tx_128_255_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_128_255_oct_pkt_num)}, + {"mac_tx_256_511_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_256_511_oct_pkt_num)}, + {"mac_tx_512_1023_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_512_1023_oct_pkt_num)}, + {"mac_tx_1024_1518_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_1024_1518_oct_pkt_num)}, + {"mac_tx_1519_max_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_tx_1519_max_oct_pkt_num)}, + {"mac_rx_total_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_total_pkt_num)}, + {"mac_rx_total_oct_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_total_oct_num)}, + {"mac_rx_good_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_good_pkt_num)}, + {"mac_rx_bad_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_bad_pkt_num)}, + {"mac_rx_good_oct_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_good_oct_num)}, + {"mac_rx_bad_oct_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_bad_oct_num)}, + {"mac_rx_uni_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_uni_pkt_num)}, + {"mac_rx_multi_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_multi_pkt_num)}, + {"mac_rx_broad_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_broad_pkt_num)}, + {"mac_rx_undersize_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_undersize_pkt_num)}, + {"mac_rx_overrsize_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_overrsize_pkt_num)}, + {"mac_rx_64_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_64_oct_pkt_num)}, + {"mac_rx_65_127_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_65_127_oct_pkt_num)}, + {"mac_rx_128_255_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_128_255_oct_pkt_num)}, + {"mac_rx_256_511_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_256_511_oct_pkt_num)}, + {"mac_rx_512_1023_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_512_1023_oct_pkt_num)}, + {"mac_rx_1024_1518_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_1024_1518_oct_pkt_num)}, + {"mac_rx_1519_max_oct_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rx_1519_max_oct_pkt_num)}, + + {"mac_trans_fragment_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_trans_fragment_pkt_num)}, + {"mac_trans_undermin_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_trans_undermin_pkt_num)}, + {"mac_trans_jabber_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_trans_jabber_pkt_num)}, + {"mac_trans_err_all_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_trans_err_all_pkt_num)}, + {"mac_trans_from_app_good_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_trans_from_app_good_pkt_num)}, + {"mac_trans_from_app_bad_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_trans_from_app_bad_pkt_num)}, + {"mac_rcv_fragment_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rcv_fragment_pkt_num)}, + {"mac_rcv_undermin_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rcv_undermin_pkt_num)}, + {"mac_rcv_jabber_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rcv_jabber_pkt_num)}, + {"mac_rcv_fcs_err_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rcv_fcs_err_pkt_num)}, + {"mac_rcv_send_app_good_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rcv_send_app_good_pkt_num)}, + {"mac_rcv_send_app_bad_pkt_num", + HCLGE_MAC_STATS_FIELD_OFF(mac_rcv_send_app_bad_pkt_num)} +}; + +static int hclge_64_bit_update_stats(struct hclge_dev *hdev) +{ +#define HCLGE_64_BIT_CMD_NUM 5 +#define HCLGE_64_BIT_RTN_DATANUM 4 + u64 *data = (u64 *)(&hdev->hw_stats.all_64_bit_stats); + struct hclge_desc desc[HCLGE_64_BIT_CMD_NUM]; + u64 *desc_data; + int i, k, n; + int ret; + + hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_STATS_64_BIT, true); + ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_64_BIT_CMD_NUM); + if (ret) { + dev_err(&hdev->pdev->dev, + "Get 64 bit pkt stats fail, status = %d.\n", ret); + return ret; + } + + for (i = 0; i < HCLGE_64_BIT_CMD_NUM; i++) { + if (unlikely(i == 0)) { + desc_data = (u64 *)(&desc[i].data[0]); + n = HCLGE_64_BIT_RTN_DATANUM - 1; + } else { + desc_data = (u64 *)(&desc[i]); + n = HCLGE_64_BIT_RTN_DATANUM; + } + for (k = 0; k < n; k++) { + *data++ += cpu_to_le64(*desc_data); + desc_data++; + } + } + + return 0; +} + +static void hclge_reset_partial_32bit_counter(struct hclge_32_bit_stats *stats) +{ + stats->pkt_curr_buf_cnt = 0; + stats->pkt_curr_buf_tc0_cnt = 0; + stats->pkt_curr_buf_tc1_cnt = 0; + stats->pkt_curr_buf_tc2_cnt = 0; + stats->pkt_curr_buf_tc3_cnt = 0; + stats->pkt_curr_buf_tc4_cnt = 0; + stats->pkt_curr_buf_tc5_cnt = 0; + stats->pkt_curr_buf_tc6_cnt = 0; + stats->pkt_curr_buf_tc7_cnt = 0; +} + +static int hclge_32_bit_update_stats(struct hclge_dev *hdev) +{ +#define HCLGE_32_BIT_CMD_NUM 8 +#define HCLGE_32_BIT_RTN_DATANUM 8 + + struct hclge_desc desc[HCLGE_32_BIT_CMD_NUM]; + struct hclge_32_bit_stats *all_32_bit_stats; + u32 *desc_data; + int i, k, n; + u64 *data; + int ret; + + all_32_bit_stats = &hdev->hw_stats.all_32_bit_stats; + data = (u64 *)(&all_32_bit_stats->egu_tx_1588_pkt); + + hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_STATS_32_BIT, true); + ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_32_BIT_CMD_NUM); + if (ret) { + dev_err(&hdev->pdev->dev, + "Get 32 bit pkt stats fail, status = %d.\n", ret); + + return ret; + } + + hclge_reset_partial_32bit_counter(all_32_bit_stats); + for (i = 0; i < HCLGE_32_BIT_CMD_NUM; i++) { + if (unlikely(i == 0)) { + all_32_bit_stats->igu_rx_err_pkt += + cpu_to_le32(desc[i].data[0]); + all_32_bit_stats->igu_rx_no_eof_pkt += + cpu_to_le32(desc[i].data[1] & 0xffff); + all_32_bit_stats->igu_rx_no_sof_pkt += + cpu_to_le32((desc[i].data[1] >> 16) & 0xffff); + + desc_data = (u32 *)(&desc[i].data[2]); + n = HCLGE_32_BIT_RTN_DATANUM - 4; + } else { + desc_data = (u32 *)(&desc[i]); + n = HCLGE_32_BIT_RTN_DATANUM; + } + for (k = 0; k < n; k++) { + *data++ += cpu_to_le32(*desc_data); + desc_data++; + } + } + + return 0; +} + +static int hclge_mac_update_stats(struct hclge_dev *hdev) +{ +#define HCLGE_MAC_CMD_NUM 17 +#define HCLGE_RTN_DATA_NUM 4 + + u64 *data = (u64 *)(&hdev->hw_stats.mac_stats); + struct hclge_desc desc[HCLGE_MAC_CMD_NUM]; + u64 *desc_data; + int i, k, n; + int ret; + + hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_STATS_MAC, true); + ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_MAC_CMD_NUM); + if (ret) { + dev_err(&hdev->pdev->dev, + "Get MAC pkt stats fail, status = %d.\n", ret); + + return ret; + } + + for (i = 0; i < HCLGE_MAC_CMD_NUM; i++) { + if (unlikely(i == 0)) { + desc_data = (u64 *)(&desc[i].data[0]); + n = HCLGE_RTN_DATA_NUM - 2; + } else { + desc_data = (u64 *)(&desc[i]); + n = HCLGE_RTN_DATA_NUM; + } + for (k = 0; k < n; k++) { + *data++ += cpu_to_le64(*desc_data); + desc_data++; + } + } + + return 0; +} + +static int hclge_tqps_update_stats(struct hnae3_handle *handle) +{ + struct hnae3_knic_private_info *kinfo = &handle->kinfo; + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hnae3_queue *queue; + struct hclge_desc desc[1]; + struct hclge_tqp *tqp; + int ret, i; + + for (i = 0; i < kinfo->num_tqps; i++) { + queue = handle->kinfo.tqp[i]; + tqp = container_of(queue, struct hclge_tqp, q); + /* command : HCLGE_OPC_QUERY_IGU_STAT */ + hclge_cmd_setup_basic_desc(&desc[0], + HCLGE_OPC_QUERY_RX_STATUS, + true); + + desc[0].data[0] = (tqp->index & 0x1ff); + ret = hclge_cmd_send(&hdev->hw, desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Query tqp stat fail, status = %d,queue = %d\n", + ret, i); + return ret; + } + tqp->tqp_stats.rcb_rx_ring_pktnum_rcd += + cpu_to_le32(desc[0].data[4]); + } + + for (i = 0; i < kinfo->num_tqps; i++) { + queue = handle->kinfo.tqp[i]; + tqp = container_of(queue, struct hclge_tqp, q); + /* command : HCLGE_OPC_QUERY_IGU_STAT */ + hclge_cmd_setup_basic_desc(&desc[0], + HCLGE_OPC_QUERY_TX_STATUS, + true); + + desc[0].data[0] = (tqp->index & 0x1ff); + ret = hclge_cmd_send(&hdev->hw, desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Query tqp stat fail, status = %d,queue = %d\n", + ret, i); + return ret; + } + tqp->tqp_stats.rcb_tx_ring_pktnum_rcd += + cpu_to_le32(desc[0].data[4]); + } + + return 0; +} + +static u64 *hclge_tqps_get_stats(struct hnae3_handle *handle, u64 *data) +{ + struct hnae3_knic_private_info *kinfo = &handle->kinfo; + struct hclge_tqp *tqp; + u64 *buff = data; + int i; + + for (i = 0; i < kinfo->num_tqps; i++) { + tqp = container_of(kinfo->tqp[i], struct hclge_tqp, q); + *buff++ = cpu_to_le64(tqp->tqp_stats.rcb_tx_ring_pktnum_rcd); + } + + for (i = 0; i < kinfo->num_tqps; i++) { + tqp = container_of(kinfo->tqp[i], struct hclge_tqp, q); + *buff++ = cpu_to_le64(tqp->tqp_stats.rcb_rx_ring_pktnum_rcd); + } + + return buff; +} + +static int hclge_tqps_get_sset_count(struct hnae3_handle *handle, int stringset) +{ + struct hnae3_knic_private_info *kinfo = &handle->kinfo; + + return kinfo->num_tqps * (2); +} + +static u8 *hclge_tqps_get_strings(struct hnae3_handle *handle, u8 *data) +{ + struct hnae3_knic_private_info *kinfo = &handle->kinfo; + u8 *buff = data; + int i = 0; + + for (i = 0; i < kinfo->num_tqps; i++) { + struct hclge_tqp *tqp = container_of(handle->kinfo.tqp[i], + struct hclge_tqp, q); + snprintf(buff, ETH_GSTRING_LEN, "rcb_q%d_tx_pktnum_rcd", + tqp->index); + buff = buff + ETH_GSTRING_LEN; + } + + for (i = 0; i < kinfo->num_tqps; i++) { + struct hclge_tqp *tqp = container_of(kinfo->tqp[i], + struct hclge_tqp, q); + snprintf(buff, ETH_GSTRING_LEN, "rcb_q%d_rx_pktnum_rcd", + tqp->index); + buff = buff + ETH_GSTRING_LEN; + } + + return buff; +} + +static u64 *hclge_comm_get_stats(void *comm_stats, + const struct hclge_comm_stats_str strs[], + int size, u64 *data) +{ + u64 *buf = data; + u32 i; + + for (i = 0; i < size; i++) + buf[i] = HCLGE_STATS_READ(comm_stats, strs[i].offset); + + return buf + size; +} + +static u8 *hclge_comm_get_strings(u32 stringset, + const struct hclge_comm_stats_str strs[], + int size, u8 *data) +{ + char *buff = (char *)data; + u32 i; + + if (stringset != ETH_SS_STATS) + return buff; + + for (i = 0; i < size; i++) { + snprintf(buff, ETH_GSTRING_LEN, + strs[i].desc); + buff = buff + ETH_GSTRING_LEN; + } + + return (u8 *)buff; +} + +static void hclge_update_netstat(struct hclge_hw_stats *hw_stats, + struct net_device_stats *net_stats) +{ + net_stats->tx_dropped = 0; + net_stats->rx_dropped = hw_stats->all_32_bit_stats.ssu_full_drop_num; + net_stats->rx_dropped += hw_stats->all_32_bit_stats.ppp_key_drop_num; + net_stats->rx_dropped += hw_stats->all_32_bit_stats.ssu_key_drop_num; + + net_stats->rx_errors = hw_stats->mac_stats.mac_rx_overrsize_pkt_num; + net_stats->rx_errors += hw_stats->mac_stats.mac_rx_undersize_pkt_num; + net_stats->rx_errors += hw_stats->all_32_bit_stats.igu_rx_err_pkt; + net_stats->rx_errors += hw_stats->all_32_bit_stats.igu_rx_no_eof_pkt; + net_stats->rx_errors += hw_stats->all_32_bit_stats.igu_rx_no_sof_pkt; + net_stats->rx_errors += hw_stats->mac_stats.mac_rcv_fcs_err_pkt_num; + + net_stats->multicast = hw_stats->mac_stats.mac_tx_multi_pkt_num; + net_stats->multicast += hw_stats->mac_stats.mac_rx_multi_pkt_num; + + net_stats->rx_crc_errors = hw_stats->mac_stats.mac_rcv_fcs_err_pkt_num; + net_stats->rx_length_errors = + hw_stats->mac_stats.mac_rx_undersize_pkt_num; + net_stats->rx_length_errors += + hw_stats->mac_stats.mac_rx_overrsize_pkt_num; + net_stats->rx_over_errors = + hw_stats->mac_stats.mac_rx_overrsize_pkt_num; +} + +static void hclge_update_stats_for_all(struct hclge_dev *hdev) +{ + struct hnae3_handle *handle; + int status; + + handle = &hdev->vport[0].nic; + if (handle->client) { + status = hclge_tqps_update_stats(handle); + if (status) { + dev_err(&hdev->pdev->dev, + "Update TQPS stats fail, status = %d.\n", + status); + } + } + + status = hclge_mac_update_stats(hdev); + if (status) + dev_err(&hdev->pdev->dev, + "Update MAC stats fail, status = %d.\n", status); + + status = hclge_32_bit_update_stats(hdev); + if (status) + dev_err(&hdev->pdev->dev, + "Update 32 bit stats fail, status = %d.\n", + status); + + hclge_update_netstat(&hdev->hw_stats, &handle->kinfo.netdev->stats); +} + +static void hclge_update_stats(struct hnae3_handle *handle, + struct net_device_stats *net_stats) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hclge_hw_stats *hw_stats = &hdev->hw_stats; + int status; + + status = hclge_mac_update_stats(hdev); + if (status) + dev_err(&hdev->pdev->dev, + "Update MAC stats fail, status = %d.\n", + status); + + status = hclge_32_bit_update_stats(hdev); + if (status) + dev_err(&hdev->pdev->dev, + "Update 32 bit stats fail, status = %d.\n", + status); + + status = hclge_64_bit_update_stats(hdev); + if (status) + dev_err(&hdev->pdev->dev, + "Update 64 bit stats fail, status = %d.\n", + status); + + status = hclge_tqps_update_stats(handle); + if (status) + dev_err(&hdev->pdev->dev, + "Update TQPS stats fail, status = %d.\n", + status); + + hclge_update_netstat(hw_stats, net_stats); +} + +static int hclge_get_sset_count(struct hnae3_handle *handle, int stringset) +{ +#define HCLGE_LOOPBACK_TEST_FLAGS 0x7 + + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + int count = 0; + + /* Loopback test support rules: + * mac: only GE mode support + * serdes: all mac mode will support include GE/XGE/LGE/CGE + * phy: only support when phy device exist on board + */ + if (stringset == ETH_SS_TEST) { + /* clear loopback bit flags at first */ + handle->flags = (handle->flags & (~HCLGE_LOOPBACK_TEST_FLAGS)); + if (hdev->hw.mac.speed == HCLGE_MAC_SPEED_10M || + hdev->hw.mac.speed == HCLGE_MAC_SPEED_100M || + hdev->hw.mac.speed == HCLGE_MAC_SPEED_1G) { + count += 1; + handle->flags |= HNAE3_SUPPORT_MAC_LOOPBACK; + } else { + count = -EOPNOTSUPP; + } + } else if (stringset == ETH_SS_STATS) { + count = ARRAY_SIZE(g_mac_stats_string) + + ARRAY_SIZE(g_all_32bit_stats_string) + + ARRAY_SIZE(g_all_64bit_stats_string) + + hclge_tqps_get_sset_count(handle, stringset); + } + + return count; +} + +static void hclge_get_strings(struct hnae3_handle *handle, + u32 stringset, + u8 *data) +{ + u8 *p = (char *)data; + int size; + + if (stringset == ETH_SS_STATS) { + size = ARRAY_SIZE(g_mac_stats_string); + p = hclge_comm_get_strings(stringset, + g_mac_stats_string, + size, + p); + size = ARRAY_SIZE(g_all_32bit_stats_string); + p = hclge_comm_get_strings(stringset, + g_all_32bit_stats_string, + size, + p); + size = ARRAY_SIZE(g_all_64bit_stats_string); + p = hclge_comm_get_strings(stringset, + g_all_64bit_stats_string, + size, + p); + p = hclge_tqps_get_strings(handle, p); + } else if (stringset == ETH_SS_TEST) { + if (handle->flags & HNAE3_SUPPORT_MAC_LOOPBACK) { + memcpy(p, + hns3_nic_test_strs[HNAE3_MAC_INTER_LOOP_MAC], + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + if (handle->flags & HNAE3_SUPPORT_SERDES_LOOPBACK) { + memcpy(p, + hns3_nic_test_strs[HNAE3_MAC_INTER_LOOP_SERDES], + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + if (handle->flags & HNAE3_SUPPORT_PHY_LOOPBACK) { + memcpy(p, + hns3_nic_test_strs[HNAE3_MAC_INTER_LOOP_PHY], + ETH_GSTRING_LEN); + p += ETH_GSTRING_LEN; + } + } +} + +static void hclge_get_stats(struct hnae3_handle *handle, u64 *data) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + u64 *p; + + p = hclge_comm_get_stats(&hdev->hw_stats.mac_stats, + g_mac_stats_string, + ARRAY_SIZE(g_mac_stats_string), + data); + p = hclge_comm_get_stats(&hdev->hw_stats.all_32_bit_stats, + g_all_32bit_stats_string, + ARRAY_SIZE(g_all_32bit_stats_string), + p); + p = hclge_comm_get_stats(&hdev->hw_stats.all_64_bit_stats, + g_all_64bit_stats_string, + ARRAY_SIZE(g_all_64bit_stats_string), + p); + p = hclge_tqps_get_stats(handle, p); +} + +static int hclge_parse_func_status(struct hclge_dev *hdev, + struct hclge_func_status *status) +{ + if (!(status->pf_state & HCLGE_PF_STATE_DONE)) + return -EINVAL; + + /* Set the pf to main pf */ + if (status->pf_state & HCLGE_PF_STATE_MAIN) + hdev->flag |= HCLGE_FLAG_MAIN; + else + hdev->flag &= ~HCLGE_FLAG_MAIN; + + hdev->num_req_vfs = status->vf_num / status->pf_num; + return 0; +} + +static int hclge_query_function_status(struct hclge_dev *hdev) +{ + struct hclge_func_status *req; + struct hclge_desc desc; + int timeout = 0; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_FUNC_STATUS, true); + req = (struct hclge_func_status *)desc.data; + + do { + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "query function status failed %d.\n", + ret); + + return ret; + } + + /* Check pf reset is done */ + if (req->pf_state) + break; + usleep_range(1000, 2000); + } while (timeout++ < 5); + + ret = hclge_parse_func_status(hdev, req); + + return ret; +} + +static int hclge_query_pf_resource(struct hclge_dev *hdev) +{ + struct hclge_pf_res *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_PF_RSRC, true); + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "query pf resource failed %d.\n", ret); + return ret; + } + + req = (struct hclge_pf_res *)desc.data; + hdev->num_tqps = __le16_to_cpu(req->tqp_num); + hdev->pkt_buf_size = __le16_to_cpu(req->buf_size) << HCLGE_BUF_UNIT_S; + + if (hnae_get_bit(hdev->ae_dev->flag, HNAE_DEV_SUPPORT_ROCE_B)) { + hdev->num_roce_msix = + hnae_get_field(__le16_to_cpu(req->pf_intr_vector_number), + HCLGE_PF_VEC_NUM_M, HCLGE_PF_VEC_NUM_S); + + /* PF should have NIC vectors and Roce vectors, + * NIC vectors are queued before Roce vectors. + */ + hdev->num_msi = hdev->num_roce_msix + HCLGE_ROCE_VECTOR_OFFSET; + } else { + hdev->num_msi = + hnae_get_field(__le16_to_cpu(req->pf_intr_vector_number), + HCLGE_PF_VEC_NUM_M, HCLGE_PF_VEC_NUM_S); + } + + return 0; +} + +static int hclge_parse_speed(int speed_cmd, int *speed) +{ + switch (speed_cmd) { + case 6: + *speed = HCLGE_MAC_SPEED_10M; + break; + case 7: + *speed = HCLGE_MAC_SPEED_100M; + break; + case 0: + *speed = HCLGE_MAC_SPEED_1G; + break; + case 1: + *speed = HCLGE_MAC_SPEED_10G; + break; + case 2: + *speed = HCLGE_MAC_SPEED_25G; + break; + case 3: + *speed = HCLGE_MAC_SPEED_40G; + break; + case 4: + *speed = HCLGE_MAC_SPEED_50G; + break; + case 5: + *speed = HCLGE_MAC_SPEED_100G; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void hclge_parse_cfg(struct hclge_cfg *cfg, struct hclge_desc *desc) +{ + struct hclge_cfg_param *req; + u64 mac_addr_tmp_high; + u64 mac_addr_tmp; + int i; + + req = (struct hclge_cfg_param *)desc[0].data; + + /* get the configuration */ + cfg->vmdq_vport_num = hnae_get_field(__le32_to_cpu(req->param[0]), + HCLGE_CFG_VMDQ_M, + HCLGE_CFG_VMDQ_S); + cfg->tc_num = hnae_get_field(__le32_to_cpu(req->param[0]), + HCLGE_CFG_TC_NUM_M, HCLGE_CFG_TC_NUM_S); + cfg->tqp_desc_num = hnae_get_field(__le32_to_cpu(req->param[0]), + HCLGE_CFG_TQP_DESC_N_M, + HCLGE_CFG_TQP_DESC_N_S); + + cfg->phy_addr = hnae_get_field(__le32_to_cpu(req->param[1]), + HCLGE_CFG_PHY_ADDR_M, + HCLGE_CFG_PHY_ADDR_S); + cfg->media_type = hnae_get_field(__le32_to_cpu(req->param[1]), + HCLGE_CFG_MEDIA_TP_M, + HCLGE_CFG_MEDIA_TP_S); + cfg->rx_buf_len = hnae_get_field(__le32_to_cpu(req->param[1]), + HCLGE_CFG_RX_BUF_LEN_M, + HCLGE_CFG_RX_BUF_LEN_S); + /* get mac_address */ + mac_addr_tmp = __le32_to_cpu(req->param[2]); + mac_addr_tmp_high = hnae_get_field(__le32_to_cpu(req->param[3]), + HCLGE_CFG_MAC_ADDR_H_M, + HCLGE_CFG_MAC_ADDR_H_S); + + mac_addr_tmp |= (mac_addr_tmp_high << 31) << 1; + + cfg->default_speed = hnae_get_field(__le32_to_cpu(req->param[3]), + HCLGE_CFG_DEFAULT_SPEED_M, + HCLGE_CFG_DEFAULT_SPEED_S); + for (i = 0; i < ETH_ALEN; i++) + cfg->mac_addr[i] = (mac_addr_tmp >> (8 * i)) & 0xff; + + req = (struct hclge_cfg_param *)desc[1].data; + cfg->numa_node_map = __le32_to_cpu(req->param[0]); +} + +/* hclge_get_cfg: query the static parameter from flash + * @hdev: pointer to struct hclge_dev + * @hcfg: the config structure to be getted + */ +static int hclge_get_cfg(struct hclge_dev *hdev, struct hclge_cfg *hcfg) +{ + struct hclge_desc desc[HCLGE_PF_CFG_DESC_NUM]; + struct hclge_cfg_param *req; + int i, ret; + + for (i = 0; i < HCLGE_PF_CFG_DESC_NUM; i++) { + req = (struct hclge_cfg_param *)desc[i].data; + hclge_cmd_setup_basic_desc(&desc[i], HCLGE_OPC_GET_CFG_PARAM, + true); + hnae_set_field(req->offset, HCLGE_CFG_OFFSET_M, + HCLGE_CFG_OFFSET_S, i * HCLGE_CFG_RD_LEN_BYTES); + /* Len should be united by 4 bytes when send to hardware */ + hnae_set_field(req->offset, HCLGE_CFG_RD_LEN_M, + HCLGE_CFG_RD_LEN_S, + HCLGE_CFG_RD_LEN_BYTES / HCLGE_CFG_RD_LEN_UNIT); + req->offset = cpu_to_le32(req->offset); + } + + ret = hclge_cmd_send(&hdev->hw, desc, HCLGE_PF_CFG_DESC_NUM); + if (ret) { + dev_err(&hdev->pdev->dev, + "get config failed %d.\n", ret); + return ret; + } + + hclge_parse_cfg(hcfg, desc); + return 0; +} + +static int hclge_get_cap(struct hclge_dev *hdev) +{ + int ret; + + ret = hclge_query_function_status(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, + "query function status error %d.\n", ret); + return ret; + } + + /* get pf resource */ + ret = hclge_query_pf_resource(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, + "query pf resource error %d.\n", ret); + return ret; + } + + return 0; +} + +static int hclge_configure(struct hclge_dev *hdev) +{ + struct hclge_cfg cfg; + int ret, i; + + ret = hclge_get_cfg(hdev, &cfg); + if (ret) { + dev_err(&hdev->pdev->dev, "get mac mode error %d.\n", ret); + return ret; + } + + hdev->num_vmdq_vport = cfg.vmdq_vport_num; + hdev->base_tqp_pid = 0; + hdev->rss_size_max = 1; + hdev->rx_buf_len = cfg.rx_buf_len; + for (i = 0; i < ETH_ALEN; i++) + hdev->hw.mac.mac_addr[i] = cfg.mac_addr[i]; + hdev->hw.mac.media_type = cfg.media_type; + hdev->num_desc = cfg.tqp_desc_num; + hdev->tm_info.num_pg = 1; + hdev->tm_info.num_tc = cfg.tc_num; + hdev->tm_info.hw_pfc_map = 0; + + ret = hclge_parse_speed(cfg.default_speed, &hdev->hw.mac.speed); + if (ret) { + dev_err(&hdev->pdev->dev, "Get wrong speed ret=%d.\n", ret); + return ret; + } + + if ((hdev->tm_info.num_tc > HNAE3_MAX_TC) || + (hdev->tm_info.num_tc < 1)) { + dev_warn(&hdev->pdev->dev, "TC num = %d.\n", + hdev->tm_info.num_tc); + hdev->tm_info.num_tc = 1; + } + + /* Currently not support uncontiuous tc */ + for (i = 0; i < cfg.tc_num; i++) + hnae_set_bit(hdev->hw_tc_map, i, 1); + + if (!hdev->num_vmdq_vport && !hdev->num_req_vfs) + hdev->tx_sch_mode = HCLGE_FLAG_TC_BASE_SCH_MODE; + else + hdev->tx_sch_mode = HCLGE_FLAG_VNET_BASE_SCH_MODE; + + return ret; +} + +static int hclge_config_tso(struct hclge_dev *hdev, int tso_mss_min, + int tso_mss_max) +{ + struct hclge_cfg_tso_status *req; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TSO_GENERIC_CONFIG, false); + + req = (struct hclge_cfg_tso_status *)desc.data; + hnae_set_field(req->tso_mss_min, HCLGE_TSO_MSS_MIN_M, + HCLGE_TSO_MSS_MIN_S, tso_mss_min); + hnae_set_field(req->tso_mss_max, HCLGE_TSO_MSS_MIN_M, + HCLGE_TSO_MSS_MIN_S, tso_mss_max); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_alloc_tqps(struct hclge_dev *hdev) +{ + struct hclge_tqp *tqp; + int i; + + hdev->htqp = devm_kcalloc(&hdev->pdev->dev, hdev->num_tqps, + sizeof(struct hclge_tqp), GFP_KERNEL); + if (!hdev->htqp) + return -ENOMEM; + + tqp = hdev->htqp; + + for (i = 0; i < hdev->num_tqps; i++) { + tqp->dev = &hdev->pdev->dev; + tqp->index = i; + + tqp->q.ae_algo = &ae_algo; + tqp->q.buf_size = hdev->rx_buf_len; + tqp->q.desc_num = hdev->num_desc; + tqp->q.io_base = hdev->hw.io_base + HCLGE_TQP_REG_OFFSET + + i * HCLGE_TQP_REG_SIZE; + + tqp++; + } + + return 0; +} + +static int hclge_map_tqps_to_func(struct hclge_dev *hdev, u16 func_id, + u16 tqp_pid, u16 tqp_vid, bool is_pf) +{ + struct hclge_tqp_map *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_SET_TQP_MAP, false); + + req = (struct hclge_tqp_map *)desc.data; + req->tqp_id = cpu_to_le16(tqp_pid); + req->tqp_vf = cpu_to_le16(func_id); + req->tqp_flag = !is_pf << HCLGE_TQP_MAP_TYPE_B | + 1 << HCLGE_TQP_MAP_EN_B; + req->tqp_vid = cpu_to_le16(tqp_vid); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "TQP map failed %d.\n", + ret); + return ret; + } + + return 0; +} + +static int hclge_assign_tqp(struct hclge_vport *vport, + struct hnae3_queue **tqp, u16 num_tqps) +{ + struct hclge_dev *hdev = vport->back; + int i, alloced, func_id, ret; + bool is_pf; + + func_id = vport->vport_id; + is_pf = (vport->vport_id == 0) ? true : false; + + for (i = 0, alloced = 0; i < hdev->num_tqps && + alloced < num_tqps; i++) { + if (!hdev->htqp[i].alloced) { + hdev->htqp[i].q.handle = &vport->nic; + hdev->htqp[i].q.tqp_index = alloced; + tqp[alloced] = &hdev->htqp[i].q; + hdev->htqp[i].alloced = true; + ret = hclge_map_tqps_to_func(hdev, func_id, + hdev->htqp[i].index, + alloced, is_pf); + if (ret) + return ret; + + alloced++; + } + } + vport->alloc_tqps = num_tqps; + + return 0; +} + +static int hclge_knic_setup(struct hclge_vport *vport, u16 num_tqps) +{ + struct hnae3_handle *nic = &vport->nic; + struct hnae3_knic_private_info *kinfo = &nic->kinfo; + struct hclge_dev *hdev = vport->back; + int i, ret; + + kinfo->num_desc = hdev->num_desc; + kinfo->rx_buf_len = hdev->rx_buf_len; + kinfo->num_tc = min_t(u16, num_tqps, hdev->tm_info.num_tc); + kinfo->rss_size + = min_t(u16, hdev->rss_size_max, num_tqps / kinfo->num_tc); + kinfo->num_tqps = kinfo->rss_size * kinfo->num_tc; + + for (i = 0; i < HNAE3_MAX_TC; i++) { + if (hdev->hw_tc_map & BIT(i)) { + kinfo->tc_info[i].enable = true; + kinfo->tc_info[i].tqp_offset = i * kinfo->rss_size; + kinfo->tc_info[i].tqp_count = kinfo->rss_size; + kinfo->tc_info[i].tc = i; + } else { + /* Set to default queue if TC is disable */ + kinfo->tc_info[i].enable = false; + kinfo->tc_info[i].tqp_offset = 0; + kinfo->tc_info[i].tqp_count = 1; + kinfo->tc_info[i].tc = 0; + } + } + + kinfo->tqp = devm_kcalloc(&hdev->pdev->dev, kinfo->num_tqps, + sizeof(struct hnae3_queue *), GFP_KERNEL); + if (!kinfo->tqp) + return -ENOMEM; + + ret = hclge_assign_tqp(vport, kinfo->tqp, kinfo->num_tqps); + if (ret) { + dev_err(&hdev->pdev->dev, "fail to assign TQPs %d.\n", ret); + return -EINVAL; + } + + return 0; +} + +static void hclge_unic_setup(struct hclge_vport *vport, u16 num_tqps) +{ + /* this would be initialized later */ +} + +static int hclge_vport_setup(struct hclge_vport *vport, u16 num_tqps) +{ + struct hnae3_handle *nic = &vport->nic; + struct hclge_dev *hdev = vport->back; + int ret; + + nic->pdev = hdev->pdev; + nic->ae_algo = &ae_algo; + nic->numa_node_mask = hdev->numa_node_mask; + + if (hdev->ae_dev->dev_type == HNAE3_DEV_KNIC) { + ret = hclge_knic_setup(vport, num_tqps); + if (ret) { + dev_err(&hdev->pdev->dev, "knic setup failed %d\n", + ret); + return ret; + } + } else { + hclge_unic_setup(vport, num_tqps); + } + + return 0; +} + +static int hclge_alloc_vport(struct hclge_dev *hdev) +{ + struct pci_dev *pdev = hdev->pdev; + struct hclge_vport *vport; + u32 tqp_main_vport; + u32 tqp_per_vport; + int num_vport, i; + int ret; + + /* We need to alloc a vport for main NIC of PF */ + num_vport = hdev->num_vmdq_vport + hdev->num_req_vfs + 1; + + if (hdev->num_tqps < num_vport) + num_vport = hdev->num_tqps; + + /* Alloc the same number of TQPs for every vport */ + tqp_per_vport = hdev->num_tqps / num_vport; + tqp_main_vport = tqp_per_vport + hdev->num_tqps % num_vport; + + vport = devm_kcalloc(&pdev->dev, num_vport, sizeof(struct hclge_vport), + GFP_KERNEL); + if (!vport) + return -ENOMEM; + + hdev->vport = vport; + hdev->num_alloc_vport = num_vport; + +#ifdef CONFIG_PCI_IOV + /* Enable SRIOV */ + if (hdev->num_req_vfs) { + dev_info(&pdev->dev, "active VFs(%d) found, enabling SRIOV\n", + hdev->num_req_vfs); + ret = pci_enable_sriov(hdev->pdev, hdev->num_req_vfs); + if (ret) { + hdev->num_alloc_vfs = 0; + dev_err(&pdev->dev, "SRIOV enable failed %d\n", + ret); + return ret; + } + } + hdev->num_alloc_vfs = hdev->num_req_vfs; +#endif + + for (i = 0; i < num_vport; i++) { + vport->back = hdev; + vport->vport_id = i; + + if (i == 0) + ret = hclge_vport_setup(vport, tqp_main_vport); + else + ret = hclge_vport_setup(vport, tqp_per_vport); + if (ret) { + dev_err(&pdev->dev, + "vport setup failed for vport %d, %d\n", + i, ret); + return ret; + } + + vport++; + } + + return 0; +} + +static int hclge_cmd_alloc_tx_buff(struct hclge_dev *hdev, u16 buf_size) +{ +/* TX buffer size is unit by 128 byte */ +#define HCLGE_BUF_SIZE_UNIT_SHIFT 7 +#define HCLGE_BUF_SIZE_UPDATE_EN_MSK BIT(15) + struct hclge_tx_buff_alloc *req; + struct hclge_desc desc; + int ret; + u8 i; + + req = (struct hclge_tx_buff_alloc *)desc.data; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TX_BUFF_ALLOC, 0); + for (i = 0; i < HCLGE_TC_NUM; i++) + req->tx_pkt_buff[i] = + cpu_to_le16((buf_size >> HCLGE_BUF_SIZE_UNIT_SHIFT) | + HCLGE_BUF_SIZE_UPDATE_EN_MSK); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "tx buffer alloc cmd failed %d.\n", + ret); + return ret; + } + + return 0; +} + +static int hclge_tx_buffer_alloc(struct hclge_dev *hdev, u32 buf_size) +{ + int ret = hclge_cmd_alloc_tx_buff(hdev, buf_size); + + if (ret) { + dev_err(&hdev->pdev->dev, + "tx buffer alloc failed %d\n", ret); + return ret; + } + + return 0; +} + +static int hclge_get_tc_num(struct hclge_dev *hdev) +{ + int i, cnt = 0; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) + if (hdev->hw_tc_map & BIT(i)) + cnt++; + return cnt; +} + +static int hclge_get_pfc_enalbe_num(struct hclge_dev *hdev) +{ + int i, cnt = 0; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) + if (hdev->hw_tc_map & BIT(i) && + hdev->tm_info.hw_pfc_map & BIT(i)) + cnt++; + return cnt; +} + +/* Get the number of pfc enabled TCs, which have private buffer */ +static int hclge_get_pfc_priv_num(struct hclge_dev *hdev) +{ + struct hclge_priv_buf *priv; + int i, cnt = 0; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + priv = &hdev->priv_buf[i]; + if ((hdev->tm_info.hw_pfc_map & BIT(i)) && + priv->enable) + cnt++; + } + + return cnt; +} + +/* Get the number of pfc disabled TCs, which have private buffer */ +static int hclge_get_no_pfc_priv_num(struct hclge_dev *hdev) +{ + struct hclge_priv_buf *priv; + int i, cnt = 0; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + priv = &hdev->priv_buf[i]; + if (hdev->hw_tc_map & BIT(i) && + !(hdev->tm_info.hw_pfc_map & BIT(i)) && + priv->enable) + cnt++; + } + + return cnt; +} + +static u32 hclge_get_rx_priv_buff_alloced(struct hclge_dev *hdev) +{ + struct hclge_priv_buf *priv; + u32 rx_priv = 0; + int i; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + priv = &hdev->priv_buf[i]; + if (priv->enable) + rx_priv += priv->buf_size; + } + return rx_priv; +} + +static bool hclge_is_rx_buf_ok(struct hclge_dev *hdev, u32 rx_all) +{ + u32 shared_buf_min, shared_buf_tc, shared_std; + int tc_num, pfc_enable_num; + u32 shared_buf; + u32 rx_priv; + int i; + + tc_num = hclge_get_tc_num(hdev); + pfc_enable_num = hclge_get_pfc_enalbe_num(hdev); + + shared_buf_min = 2 * hdev->mps + HCLGE_DEFAULT_DV; + shared_buf_tc = pfc_enable_num * hdev->mps + + (tc_num - pfc_enable_num) * hdev->mps / 2 + + hdev->mps; + shared_std = max_t(u32, shared_buf_min, shared_buf_tc); + + rx_priv = hclge_get_rx_priv_buff_alloced(hdev); + if (rx_all <= rx_priv + shared_std) + return false; + + shared_buf = rx_all - rx_priv; + hdev->s_buf.buf_size = shared_buf; + hdev->s_buf.self.high = shared_buf; + hdev->s_buf.self.low = 2 * hdev->mps; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + if ((hdev->hw_tc_map & BIT(i)) && + (hdev->tm_info.hw_pfc_map & BIT(i))) { + hdev->s_buf.tc_thrd[i].low = hdev->mps; + hdev->s_buf.tc_thrd[i].high = 2 * hdev->mps; + } else { + hdev->s_buf.tc_thrd[i].low = 0; + hdev->s_buf.tc_thrd[i].high = hdev->mps; + } + } + + return true; +} + +/* hclge_rx_buffer_calc: calculate the rx private buffer size for all TCs + * @hdev: pointer to struct hclge_dev + * @tx_size: the allocated tx buffer for all TCs + * @return: 0: calculate sucessful, negative: fail + */ +int hclge_rx_buffer_calc(struct hclge_dev *hdev, u32 tx_size) +{ + u32 rx_all = hdev->pkt_buf_size - tx_size; + int no_pfc_priv_num, pfc_priv_num; + struct hclge_priv_buf *priv; + int i; + + /* step 1, try to alloc private buffer for all enabled tc */ + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + priv = &hdev->priv_buf[i]; + if (hdev->hw_tc_map & BIT(i)) { + priv->enable = 1; + if (hdev->tm_info.hw_pfc_map & BIT(i)) { + priv->wl.low = hdev->mps; + priv->wl.high = priv->wl.low + hdev->mps; + priv->buf_size = priv->wl.high + + HCLGE_DEFAULT_DV; + } else { + priv->wl.low = 0; + priv->wl.high = 2 * hdev->mps; + priv->buf_size = priv->wl.high; + } + } + } + + if (hclge_is_rx_buf_ok(hdev, rx_all)) + return 0; + + /* step 2, try to decrease the buffer size of + * no pfc TC's private buffer + */ + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + priv = &hdev->priv_buf[i]; + + if (hdev->hw_tc_map & BIT(i)) + priv->enable = 1; + + if (hdev->tm_info.hw_pfc_map & BIT(i)) { + priv->wl.low = 128; + priv->wl.high = priv->wl.low + hdev->mps; + priv->buf_size = priv->wl.high + HCLGE_DEFAULT_DV; + } else { + priv->wl.low = 0; + priv->wl.high = hdev->mps; + priv->buf_size = priv->wl.high; + } + } + + if (hclge_is_rx_buf_ok(hdev, rx_all)) + return 0; + + /* step 3, try to reduce the number of pfc disabled TCs, + * which have private buffer + */ + /* get the total no pfc enable TC number, which have private buffer */ + no_pfc_priv_num = hclge_get_no_pfc_priv_num(hdev); + + /* let the last to be cleared first */ + for (i = HCLGE_MAX_TC_NUM - 1; i >= 0; i--) { + priv = &hdev->priv_buf[i]; + + if (hdev->hw_tc_map & BIT(i) && + !(hdev->tm_info.hw_pfc_map & BIT(i))) { + /* Clear the no pfc TC private buffer */ + priv->wl.low = 0; + priv->wl.high = 0; + priv->buf_size = 0; + priv->enable = 0; + no_pfc_priv_num--; + } + + if (hclge_is_rx_buf_ok(hdev, rx_all) || + no_pfc_priv_num == 0) + break; + } + + if (hclge_is_rx_buf_ok(hdev, rx_all)) + return 0; + + /* step 4, try to reduce the number of pfc enabled TCs + * which have private buffer. + */ + pfc_priv_num = hclge_get_pfc_priv_num(hdev); + + /* let the last to be cleared first */ + for (i = HCLGE_MAX_TC_NUM - 1; i >= 0; i--) { + priv = &hdev->priv_buf[i]; + + if (hdev->hw_tc_map & BIT(i) && + hdev->tm_info.hw_pfc_map & BIT(i)) { + /* Reduce the number of pfc TC with private buffer */ + priv->wl.low = 0; + priv->enable = 0; + priv->wl.high = 0; + priv->buf_size = 0; + pfc_priv_num--; + } + + if (hclge_is_rx_buf_ok(hdev, rx_all) || + pfc_priv_num == 0) + break; + } + if (hclge_is_rx_buf_ok(hdev, rx_all)) + return 0; + + return -ENOMEM; +} + +static int hclge_rx_priv_buf_alloc(struct hclge_dev *hdev) +{ + struct hclge_rx_priv_buff *req; + struct hclge_desc desc; + int ret; + int i; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RX_PRIV_BUFF_ALLOC, false); + req = (struct hclge_rx_priv_buff *)desc.data; + + /* Alloc private buffer TCs */ + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + struct hclge_priv_buf *priv = &hdev->priv_buf[i]; + + req->buf_num[i] = + cpu_to_le16(priv->buf_size >> HCLGE_BUF_UNIT_S); + req->buf_num[i] |= + cpu_to_le16(true << HCLGE_TC0_PRI_BUF_EN_B); + } + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "rx private buffer alloc cmd failed %d\n", ret); + return ret; + } + + return 0; +} + +#define HCLGE_PRIV_ENABLE(a) ((a) > 0 ? 1 : 0) + +static int hclge_rx_priv_wl_config(struct hclge_dev *hdev) +{ + struct hclge_rx_priv_wl_buf *req; + struct hclge_priv_buf *priv; + struct hclge_desc desc[2]; + int i, j; + int ret; + + for (i = 0; i < 2; i++) { + hclge_cmd_setup_basic_desc(&desc[i], HCLGE_OPC_RX_PRIV_WL_ALLOC, + false); + req = (struct hclge_rx_priv_wl_buf *)desc[i].data; + + /* The first descriptor set the NEXT bit to 1 */ + if (i == 0) + desc[i].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + else + desc[i].flag &= ~cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + + for (j = 0; j < HCLGE_TC_NUM_ONE_DESC; j++) { + priv = &hdev->priv_buf[i * HCLGE_TC_NUM_ONE_DESC + j]; + req->tc_wl[j].high = + cpu_to_le16(priv->wl.high >> HCLGE_BUF_UNIT_S); + req->tc_wl[j].high |= + cpu_to_le16(HCLGE_PRIV_ENABLE(priv->wl.high) << + HCLGE_RX_PRIV_EN_B); + req->tc_wl[j].low = + cpu_to_le16(priv->wl.low >> HCLGE_BUF_UNIT_S); + req->tc_wl[j].low |= + cpu_to_le16(HCLGE_PRIV_ENABLE(priv->wl.low) << + HCLGE_RX_PRIV_EN_B); + } + } + + /* Send 2 descriptor at one time */ + ret = hclge_cmd_send(&hdev->hw, desc, 2); + if (ret) { + dev_err(&hdev->pdev->dev, + "rx private waterline config cmd failed %d\n", + ret); + return ret; + } + return 0; +} + +static int hclge_common_thrd_config(struct hclge_dev *hdev) +{ + struct hclge_shared_buf *s_buf = &hdev->s_buf; + struct hclge_rx_com_thrd *req; + struct hclge_desc desc[2]; + struct hclge_tc_thrd *tc; + int i, j; + int ret; + + for (i = 0; i < 2; i++) { + hclge_cmd_setup_basic_desc(&desc[i], + HCLGE_OPC_RX_COM_THRD_ALLOC, false); + req = (struct hclge_rx_com_thrd *)&desc[i].data; + + /* The first descriptor set the NEXT bit to 1 */ + if (i == 0) + desc[i].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + else + desc[i].flag &= ~cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + + for (j = 0; j < HCLGE_TC_NUM_ONE_DESC; j++) { + tc = &s_buf->tc_thrd[i * HCLGE_TC_NUM_ONE_DESC + j]; + + req->com_thrd[j].high = + cpu_to_le16(tc->high >> HCLGE_BUF_UNIT_S); + req->com_thrd[j].high |= + cpu_to_le16(HCLGE_PRIV_ENABLE(tc->high) << + HCLGE_RX_PRIV_EN_B); + req->com_thrd[j].low = + cpu_to_le16(tc->low >> HCLGE_BUF_UNIT_S); + req->com_thrd[j].low |= + cpu_to_le16(HCLGE_PRIV_ENABLE(tc->low) << + HCLGE_RX_PRIV_EN_B); + } + } + + /* Send 2 descriptors at one time */ + ret = hclge_cmd_send(&hdev->hw, desc, 2); + if (ret) { + dev_err(&hdev->pdev->dev, + "common threshold config cmd failed %d\n", ret); + return ret; + } + return 0; +} + +static int hclge_common_wl_config(struct hclge_dev *hdev) +{ + struct hclge_shared_buf *buf = &hdev->s_buf; + struct hclge_rx_com_wl *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RX_COM_WL_ALLOC, false); + + req = (struct hclge_rx_com_wl *)desc.data; + req->com_wl.high = cpu_to_le16(buf->self.high >> HCLGE_BUF_UNIT_S); + req->com_wl.high |= + cpu_to_le16(HCLGE_PRIV_ENABLE(buf->self.high) << + HCLGE_RX_PRIV_EN_B); + + req->com_wl.low = cpu_to_le16(buf->self.low >> HCLGE_BUF_UNIT_S); + req->com_wl.low |= + cpu_to_le16(HCLGE_PRIV_ENABLE(buf->self.low) << + HCLGE_RX_PRIV_EN_B); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "common waterline config cmd failed %d\n", ret); + return ret; + } + + return 0; +} + +int hclge_buffer_alloc(struct hclge_dev *hdev) +{ + u32 tx_buf_size = HCLGE_DEFAULT_TX_BUF; + int ret; + + hdev->priv_buf = devm_kmalloc_array(&hdev->pdev->dev, HCLGE_MAX_TC_NUM, + sizeof(struct hclge_priv_buf), + GFP_KERNEL | __GFP_ZERO); + if (!hdev->priv_buf) + return -ENOMEM; + + ret = hclge_tx_buffer_alloc(hdev, tx_buf_size); + if (ret) { + dev_err(&hdev->pdev->dev, + "could not alloc tx buffers %d\n", ret); + return ret; + } + + ret = hclge_rx_buffer_calc(hdev, tx_buf_size); + if (ret) { + dev_err(&hdev->pdev->dev, + "could not calc rx priv buffer size for all TCs %d\n", + ret); + return ret; + } + + ret = hclge_rx_priv_buf_alloc(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, "could not alloc rx priv buffer %d\n", + ret); + return ret; + } + + ret = hclge_rx_priv_wl_config(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, + "could not configure rx private waterline %d\n", ret); + return ret; + } + + ret = hclge_common_thrd_config(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, + "could not configure common threshold %d\n", ret); + return ret; + } + + ret = hclge_common_wl_config(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, + "could not configure common waterline %d\n", ret); + return ret; + } + + return 0; +} + +static int hclge_init_roce_base_info(struct hclge_vport *vport) +{ + struct hnae3_handle *roce = &vport->roce; + struct hnae3_handle *nic = &vport->nic; + + roce->rinfo.num_vectors = vport->back->num_roce_msix; + + if (vport->back->num_msi_left < vport->roce.rinfo.num_vectors || + vport->back->num_msi_left == 0) + return -EINVAL; + + roce->rinfo.base_vector = vport->back->roce_base_vector; + + roce->rinfo.netdev = nic->kinfo.netdev; + roce->rinfo.roce_io_base = vport->back->hw.io_base; + + roce->pdev = nic->pdev; + roce->ae_algo = nic->ae_algo; + roce->numa_node_mask = nic->numa_node_mask; + + return 0; +} + +static int hclge_init_msix(struct hclge_dev *hdev) +{ + struct pci_dev *pdev = hdev->pdev; + int ret, i; + + hdev->msix_entries = devm_kcalloc(&pdev->dev, hdev->num_msi, + sizeof(struct msix_entry), + GFP_KERNEL); + if (!hdev->msix_entries) + return -ENOMEM; + + hdev->vector_status = devm_kcalloc(&pdev->dev, hdev->num_msi, + sizeof(u16), GFP_KERNEL); + if (!hdev->vector_status) + return -ENOMEM; + + for (i = 0; i < hdev->num_msi; i++) { + hdev->msix_entries[i].entry = i; + hdev->vector_status[i] = HCLGE_INVALID_VPORT; + } + + hdev->num_msi_left = hdev->num_msi; + hdev->base_msi_vector = hdev->pdev->irq; + hdev->roce_base_vector = hdev->base_msi_vector + + HCLGE_ROCE_VECTOR_OFFSET; + + ret = pci_enable_msix_range(hdev->pdev, hdev->msix_entries, + hdev->num_msi, hdev->num_msi); + if (ret < 0) { + dev_info(&hdev->pdev->dev, + "MSI-X vector alloc failed: %d\n", ret); + return ret; + } + + return 0; +} + +static int hclge_init_msi(struct hclge_dev *hdev) +{ + struct pci_dev *pdev = hdev->pdev; + int vectors; + int i; + + hdev->vector_status = devm_kcalloc(&pdev->dev, hdev->num_msi, + sizeof(u16), GFP_KERNEL); + if (!hdev->vector_status) + return -ENOMEM; + + for (i = 0; i < hdev->num_msi; i++) + hdev->vector_status[i] = HCLGE_INVALID_VPORT; + + vectors = pci_alloc_irq_vectors(pdev, 1, hdev->num_msi, PCI_IRQ_MSI); + if (vectors < 0) { + dev_err(&pdev->dev, "MSI vectors enable failed %d\n", vectors); + return -EINVAL; + } + hdev->num_msi = vectors; + hdev->num_msi_left = vectors; + hdev->base_msi_vector = pdev->irq; + hdev->roce_base_vector = hdev->base_msi_vector + + HCLGE_ROCE_VECTOR_OFFSET; + + return 0; +} + +static void hclge_check_speed_dup(struct hclge_dev *hdev, int duplex, int speed) +{ + struct hclge_mac *mac = &hdev->hw.mac; + + if ((speed == HCLGE_MAC_SPEED_10M) || (speed == HCLGE_MAC_SPEED_100M)) + mac->duplex = (u8)duplex; + else + mac->duplex = HCLGE_MAC_FULL; + + mac->speed = speed; +} + +int hclge_cfg_mac_speed_dup(struct hclge_dev *hdev, int speed, u8 duplex) +{ + struct hclge_config_mac_speed_dup *req; + struct hclge_desc desc; + int ret; + + req = (struct hclge_config_mac_speed_dup *)desc.data; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_SPEED_DUP, false); + + hnae_set_bit(req->speed_dup, HCLGE_CFG_DUPLEX_B, !!duplex); + + switch (speed) { + case HCLGE_MAC_SPEED_10M: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 6); + break; + case HCLGE_MAC_SPEED_100M: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 7); + break; + case HCLGE_MAC_SPEED_1G: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 0); + break; + case HCLGE_MAC_SPEED_10G: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 1); + break; + case HCLGE_MAC_SPEED_25G: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 2); + break; + case HCLGE_MAC_SPEED_40G: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 3); + break; + case HCLGE_MAC_SPEED_50G: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 4); + break; + case HCLGE_MAC_SPEED_100G: + hnae_set_field(req->speed_dup, HCLGE_CFG_SPEED_M, + HCLGE_CFG_SPEED_S, 5); + break; + default: + dev_err(&hdev->pdev->dev, "invalid speed (%d)\n", speed); + return -EINVAL; + } + + hnae_set_bit(req->mac_change_fec_en, HCLGE_CFG_MAC_SPEED_CHANGE_EN_B, + 1); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "mac speed/duplex config cmd failed %d.\n", ret); + return ret; + } + + hclge_check_speed_dup(hdev, duplex, speed); + + return 0; +} + +static int hclge_cfg_mac_speed_dup_h(struct hnae3_handle *handle, int speed, + u8 duplex) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + return hclge_cfg_mac_speed_dup(hdev, speed, duplex); +} + +static int hclge_query_mac_an_speed_dup(struct hclge_dev *hdev, int *speed, + u8 *duplex) +{ + struct hclge_query_an_speed_dup *req; + struct hclge_desc desc; + int speed_tmp; + int ret; + + req = (struct hclge_query_an_speed_dup *)desc.data; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_AN_RESULT, true); + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "mac speed/autoneg/duplex query cmd failed %d\n", + ret); + return ret; + } + + *duplex = hnae_get_bit(req->an_syn_dup_speed, HCLGE_QUERY_DUPLEX_B); + speed_tmp = hnae_get_field(req->an_syn_dup_speed, HCLGE_QUERY_SPEED_M, + HCLGE_QUERY_SPEED_S); + + ret = hclge_parse_speed(speed_tmp, speed); + if (ret) { + dev_err(&hdev->pdev->dev, + "could not parse speed(=%d), %d\n", speed_tmp, ret); + return -EIO; + } + + return 0; +} + +static int hclge_query_autoneg_result(struct hclge_dev *hdev) +{ + struct hclge_mac *mac = &hdev->hw.mac; + struct hclge_query_an_speed_dup *req; + struct hclge_desc desc; + int ret; + + req = (struct hclge_query_an_speed_dup *)desc.data; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_AN_RESULT, true); + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "autoneg result query cmd failed %d.\n", ret); + return ret; + } + + mac->autoneg = hnae_get_bit(req->an_syn_dup_speed, HCLGE_QUERY_AN_B); + + return 0; +} + +static int hclge_set_autoneg_en(struct hclge_dev *hdev, bool enable) +{ + struct hclge_config_auto_neg *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_AN_MODE, false); + + req = (struct hclge_config_auto_neg *)desc.data; + hnae_set_bit(req->cfg_an_cmd_flag, HCLGE_MAC_CFG_AN_EN_B, !!enable); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "auto neg set cmd failed %d.\n", + ret); + return ret; + } + + return 0; +} + +static int hclge_set_autoneg(struct hnae3_handle *handle, bool enable) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + return hclge_set_autoneg_en(hdev, enable); +} + +static int hclge_get_autoneg(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + hclge_query_autoneg_result(hdev); + + return hdev->hw.mac.autoneg; +} + +static int hclge_mac_init(struct hclge_dev *hdev) +{ + struct hclge_mac *mac = &hdev->hw.mac; + int ret; + + ret = hclge_cfg_mac_speed_dup(hdev, hdev->hw.mac.speed, HCLGE_MAC_FULL); + if (ret) { + dev_err(&hdev->pdev->dev, + "Config mac speed dup fail ret=%d\n", ret); + return ret; + } + + mac->link = 0; + + ret = hclge_mac_mdio_config(hdev); + if (ret) { + dev_warn(&hdev->pdev->dev, + "mdio config fail ret=%d\n", ret); + return ret; + } + + /* Initialize the MTA table work mode */ + hdev->accept_mta_mc = true; + hdev->enable_mta = true; + hdev->mta_mac_sel_type = HCLGE_MAC_ADDR_47_36; + + ret = hclge_set_mta_filter_mode(hdev, + hdev->mta_mac_sel_type, + hdev->enable_mta); + if (ret) { + dev_err(&hdev->pdev->dev, "set mta filter mode failed %d\n", + ret); + return ret; + } + + return hclge_cfg_func_mta_filter(hdev, 0, hdev->accept_mta_mc); +} + +static void hclge_task_schedule(struct hclge_dev *hdev) +{ + if (!test_bit(HCLGE_STATE_DOWN, &hdev->state) && + !test_bit(HCLGE_STATE_REMOVING, &hdev->state) && + !test_and_set_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state)) + (void)schedule_work(&hdev->service_task); +} + +static int hclge_get_mac_link_status(struct hclge_dev *hdev) +{ + struct hclge_link_status *req; + struct hclge_desc desc; + int link_status; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_QUERY_LINK_STATUS, true); + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "get link status cmd failed %d\n", + ret); + return ret; + } + + req = (struct hclge_link_status *)desc.data; + link_status = req->status & HCLGE_LINK_STATUS; + + return !!link_status; +} + +static int hclge_get_mac_phy_link(struct hclge_dev *hdev) +{ + int mac_state; + int link_stat; + + mac_state = hclge_get_mac_link_status(hdev); + + if (hdev->hw.mac.phydev) { + if (!genphy_read_status(hdev->hw.mac.phydev)) + link_stat = mac_state & + hdev->hw.mac.phydev->link; + else + link_stat = 0; + + } else { + link_stat = mac_state; + } + + return !!link_stat; +} + +static void hclge_update_link_status(struct hclge_dev *hdev) +{ + struct hnae3_client *client = hdev->nic_client; + struct hnae3_handle *handle; + int state; + int i; + + if (!client) + return; + state = hclge_get_mac_phy_link(hdev); + if (state != hdev->hw.mac.link) { + for (i = 0; i < hdev->num_vmdq_vport + 1; i++) { + handle = &hdev->vport[i].nic; + client->ops->link_status_change(handle, state); + } + hdev->hw.mac.link = state; + } +} + +static int hclge_update_speed_duplex(struct hclge_dev *hdev) +{ + struct hclge_mac mac = hdev->hw.mac; + u8 duplex; + int speed; + int ret; + + /* get the speed and duplex as autoneg'result from mac cmd when phy + * doesn't exit. + */ + if (mac.phydev) + return 0; + + /* update mac->antoneg. */ + ret = hclge_query_autoneg_result(hdev); + if (ret) { + dev_err(&hdev->pdev->dev, + "autoneg result query failed %d\n", ret); + return ret; + } + + if (!mac.autoneg) + return 0; + + ret = hclge_query_mac_an_speed_dup(hdev, &speed, &duplex); + if (ret) { + dev_err(&hdev->pdev->dev, + "mac autoneg/speed/duplex query failed %d\n", ret); + return ret; + } + + if ((mac.speed != speed) || (mac.duplex != duplex)) { + ret = hclge_cfg_mac_speed_dup(hdev, speed, duplex); + if (ret) { + dev_err(&hdev->pdev->dev, + "mac speed/duplex config failed %d\n", ret); + return ret; + } + } + + return 0; +} + +static int hclge_update_speed_duplex_h(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + return hclge_update_speed_duplex(hdev); +} + +static int hclge_get_status(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + hclge_update_link_status(hdev); + + return hdev->hw.mac.link; +} + +static void hclge_service_timer(unsigned long data) +{ + struct hclge_dev *hdev = (struct hclge_dev *)data; + (void)mod_timer(&hdev->service_timer, jiffies + HZ); + + hclge_task_schedule(hdev); +} + +static void hclge_service_complete(struct hclge_dev *hdev) +{ + WARN_ON(!test_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state)); + + /* Flush memory before next watchdog */ + smp_mb__before_atomic(); + clear_bit(HCLGE_STATE_SERVICE_SCHED, &hdev->state); +} + +static void hclge_service_task(struct work_struct *work) +{ + struct hclge_dev *hdev = + container_of(work, struct hclge_dev, service_task); + + hclge_update_speed_duplex(hdev); + hclge_update_link_status(hdev); + hclge_update_stats_for_all(hdev); + hclge_service_complete(hdev); +} + +static void hclge_disable_sriov(struct hclge_dev *hdev) +{ + /* If our VFs are assigned we cannot shut down SR-IOV + * without causing issues, so just leave the hardware + * available but disabled + */ + if (pci_vfs_assigned(hdev->pdev)) { + dev_warn(&hdev->pdev->dev, + "disabling driver while VFs are assigned\n"); + return; + } + + pci_disable_sriov(hdev->pdev); +} + +struct hclge_vport *hclge_get_vport(struct hnae3_handle *handle) +{ + /* VF handle has no client */ + if (!handle->client) + return container_of(handle, struct hclge_vport, nic); + else if (handle->client->type == HNAE3_CLIENT_ROCE) + return container_of(handle, struct hclge_vport, roce); + else + return container_of(handle, struct hclge_vport, nic); +} + +static int hclge_get_vector(struct hnae3_handle *handle, u16 vector_num, + struct hnae3_vector_info *vector_info) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hnae3_vector_info *vector = vector_info; + struct hclge_dev *hdev = vport->back; + int alloc = 0; + int i, j; + + vector_num = min(hdev->num_msi_left, vector_num); + + for (j = 0; j < vector_num; j++) { + for (i = 1; i < hdev->num_msi; i++) { + if (hdev->vector_status[i] == HCLGE_INVALID_VPORT) { + vector->vector = pci_irq_vector(hdev->pdev, i); + vector->io_addr = hdev->hw.io_base + + HCLGE_VECTOR_REG_BASE + + (i - 1) * HCLGE_VECTOR_REG_OFFSET + + vport->vport_id * + HCLGE_VECTOR_VF_OFFSET; + hdev->vector_status[i] = vport->vport_id; + + vector++; + alloc++; + + break; + } + } + } + hdev->num_msi_left -= alloc; + hdev->num_msi_used += alloc; + + return alloc; +} + +static int hclge_get_vector_index(struct hclge_dev *hdev, int vector) +{ + int i; + + for (i = 0; i < hdev->num_msi; i++) { + if (hdev->msix_entries) { + if (vector == hdev->msix_entries[i].vector) + return i; + } else { + if (vector == (hdev->base_msi_vector + i)) + return i; + } + } + return -EINVAL; +} + +static u32 hclge_get_rss_key_size(struct hnae3_handle *handle) +{ + return HCLGE_RSS_KEY_SIZE; +} + +static u32 hclge_get_rss_indir_size(struct hnae3_handle *handle) +{ + return HCLGE_RSS_IND_TBL_SIZE; +} + +static int hclge_get_rss_algo(struct hclge_dev *hdev) +{ + struct hclge_rss_config *req; + struct hclge_desc desc; + int rss_hash_algo; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RSS_GENERIC_CONFIG, true); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Get link status error, status =%d\n", ret); + return ret; + } + + req = (struct hclge_rss_config *)desc.data; + rss_hash_algo = (req->hash_config & HCLGE_RSS_HASH_ALGO_MASK); + + if (rss_hash_algo == HCLGE_RSS_HASH_ALGO_TOEPLITZ) + return ETH_RSS_HASH_TOP; + + return -EINVAL; +} + +static int hclge_set_rss_algo_key(struct hclge_dev *hdev, + const u8 hfunc, const u8 *key) +{ + struct hclge_rss_config *req; + struct hclge_desc desc; + int key_offset; + int key_size; + int ret; + + req = (struct hclge_rss_config *)desc.data; + + for (key_offset = 0; key_offset < 3; key_offset++) { + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RSS_GENERIC_CONFIG, + false); + + req->hash_config |= (hfunc & HCLGE_RSS_HASH_ALGO_MASK); + req->hash_config |= (key_offset << HCLGE_RSS_HASH_KEY_OFFSET_B); + + if (key_offset == 2) + key_size = + HCLGE_RSS_KEY_SIZE - HCLGE_RSS_HASH_KEY_NUM * 2; + else + key_size = HCLGE_RSS_HASH_KEY_NUM; + + memcpy(req->hash_key, + key + key_offset * HCLGE_RSS_HASH_KEY_NUM, key_size); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Configure RSS config fail, status = %d\n", + ret); + return ret; + } + } + return 0; +} + +static int hclge_set_rss_indir_table(struct hclge_dev *hdev, const u32 *indir) +{ + struct hclge_rss_indirection_table *req; + struct hclge_desc desc; + int i, j; + int ret; + + req = (struct hclge_rss_indirection_table *)desc.data; + + for (i = 0; i < HCLGE_RSS_CFG_TBL_NUM; i++) { + hclge_cmd_setup_basic_desc + (&desc, HCLGE_OPC_RSS_INDIR_TABLE, false); + + req->start_table_index = i * HCLGE_RSS_CFG_TBL_SIZE; + req->rss_set_bitmap = HCLGE_RSS_SET_BITMAP_MSK; + + for (j = 0; j < HCLGE_RSS_CFG_TBL_SIZE; j++) + req->rss_result[j] = + indir[i * HCLGE_RSS_CFG_TBL_SIZE + j]; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Configure rss indir table fail,status = %d\n", + ret); + return ret; + } + } + return 0; +} + +static int hclge_set_rss_tc_mode(struct hclge_dev *hdev, u16 *tc_valid, + u16 *tc_size, u16 *tc_offset) +{ + struct hclge_rss_tc_mode *req; + struct hclge_desc desc; + int ret; + int i; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RSS_TC_MODE, false); + req = (struct hclge_rss_tc_mode *)desc.data; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + hnae_set_bit(req->rss_tc_mode[i], HCLGE_RSS_TC_VALID_B, + (tc_valid[i] & 0x1)); + hnae_set_field(req->rss_tc_mode[i], HCLGE_RSS_TC_SIZE_M, + HCLGE_RSS_TC_SIZE_S, tc_size[i]); + hnae_set_field(req->rss_tc_mode[i], HCLGE_RSS_TC_OFFSET_M, + HCLGE_RSS_TC_OFFSET_S, tc_offset[i]); + } + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Configure rss tc mode fail, status = %d\n", ret); + return ret; + } + + return 0; +} + +static int hclge_set_rss_input_tuple(struct hclge_dev *hdev) +{ +#define HCLGE_RSS_INPUT_TUPLE_OTHER 0xf +#define HCLGE_RSS_INPUT_TUPLE_SCTP 0x1f + struct hclge_rss_input_tuple *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RSS_INPUT_TUPLE, false); + + req = (struct hclge_rss_input_tuple *)desc.data; + req->ipv4_tcp_en = HCLGE_RSS_INPUT_TUPLE_OTHER; + req->ipv4_udp_en = HCLGE_RSS_INPUT_TUPLE_OTHER; + req->ipv4_sctp_en = HCLGE_RSS_INPUT_TUPLE_SCTP; + req->ipv4_fragment_en = HCLGE_RSS_INPUT_TUPLE_OTHER; + req->ipv6_tcp_en = HCLGE_RSS_INPUT_TUPLE_OTHER; + req->ipv6_udp_en = HCLGE_RSS_INPUT_TUPLE_OTHER; + req->ipv6_sctp_en = HCLGE_RSS_INPUT_TUPLE_SCTP; + req->ipv6_fragment_en = HCLGE_RSS_INPUT_TUPLE_OTHER; + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Configure rss input fail, status = %d\n", ret); + return ret; + } + + return 0; +} + +static int hclge_get_rss(struct hnae3_handle *handle, u32 *indir, + u8 *key, u8 *hfunc) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + int i; + + /* Get hash algorithm */ + if (hfunc) + *hfunc = hclge_get_rss_algo(hdev); + + /* Get the RSS Key required by the user */ + if (key) + memcpy(key, vport->rss_hash_key, HCLGE_RSS_KEY_SIZE); + + /* Get indirect table */ + if (indir) + for (i = 0; i < HCLGE_RSS_IND_TBL_SIZE; i++) + indir[i] = vport->rss_indirection_tbl[i]; + + return 0; +} + +static int hclge_set_rss(struct hnae3_handle *handle, const u32 *indir, + const u8 *key, const u8 hfunc) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + u8 hash_algo; + int ret, i; + + /* Set the RSS Hash Key if specififed by the user */ + if (key) { + /* Update the shadow RSS key with user specified qids */ + memcpy(vport->rss_hash_key, key, HCLGE_RSS_KEY_SIZE); + + if (hfunc == ETH_RSS_HASH_TOP || + hfunc == ETH_RSS_HASH_NO_CHANGE) + hash_algo = HCLGE_RSS_HASH_ALGO_TOEPLITZ; + else + return -EINVAL; + ret = hclge_set_rss_algo_key(hdev, hash_algo, key); + if (ret) + return ret; + } + + /* Update the shadow RSS table with user specified qids */ + for (i = 0; i < HCLGE_RSS_IND_TBL_SIZE; i++) + vport->rss_indirection_tbl[i] = indir[i]; + + /* Update the hardware */ + ret = hclge_set_rss_indir_table(hdev, indir); + return ret; +} + +static int hclge_get_tc_size(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + return hdev->rss_size_max; +} + +static int hclge_rss_init_hw(struct hclge_dev *hdev) +{ + const u8 hfunc = HCLGE_RSS_HASH_ALGO_TOEPLITZ; + struct hclge_vport *vport = hdev->vport; + u16 tc_offset[HCLGE_MAX_TC_NUM]; + u8 rss_key[HCLGE_RSS_KEY_SIZE]; + u16 tc_valid[HCLGE_MAX_TC_NUM]; + u16 tc_size[HCLGE_MAX_TC_NUM]; + u32 *rss_indir = NULL; + const u8 *key; + int i, ret, j; + + rss_indir = kcalloc(HCLGE_RSS_IND_TBL_SIZE, sizeof(u32), GFP_KERNEL); + if (!rss_indir) + return -ENOMEM; + + /* Get default RSS key */ + netdev_rss_key_fill(rss_key, HCLGE_RSS_KEY_SIZE); + + /* Initialize RSS indirect table for each vport */ + for (j = 0; j < hdev->num_vmdq_vport + 1; j++) { + for (i = 0; i < HCLGE_RSS_IND_TBL_SIZE; i++) { + vport[j].rss_indirection_tbl[i] = + i % hdev->rss_size_max; + rss_indir[i] = vport[j].rss_indirection_tbl[i]; + } + } + ret = hclge_set_rss_indir_table(hdev, rss_indir); + if (ret) + goto err; + + key = rss_key; + ret = hclge_set_rss_algo_key(hdev, hfunc, key); + if (ret) + goto err; + + ret = hclge_set_rss_input_tuple(hdev); + if (ret) + goto err; + + for (i = 0; i < HCLGE_MAX_TC_NUM; i++) { + if (hdev->hw_tc_map & BIT(i)) + tc_valid[i] = 1; + else + tc_valid[i] = 0; + + switch (hdev->rss_size_max) { + case HCLGE_RSS_TC_SIZE_0: + tc_size[i] = 0; + break; + case HCLGE_RSS_TC_SIZE_1: + tc_size[i] = 1; + break; + case HCLGE_RSS_TC_SIZE_2: + tc_size[i] = 2; + break; + case HCLGE_RSS_TC_SIZE_3: + tc_size[i] = 3; + break; + case HCLGE_RSS_TC_SIZE_4: + tc_size[i] = 4; + break; + case HCLGE_RSS_TC_SIZE_5: + tc_size[i] = 5; + break; + case HCLGE_RSS_TC_SIZE_6: + tc_size[i] = 6; + break; + case HCLGE_RSS_TC_SIZE_7: + tc_size[i] = 7; + break; + default: + break; + } + tc_offset[i] = hdev->rss_size_max * i; + } + ret = hclge_set_rss_tc_mode(hdev, tc_valid, tc_size, tc_offset); + +err: + kfree(rss_indir); + + return ret; +} + +int hclge_map_vport_ring_to_vector(struct hclge_vport *vport, int vector_id, + struct hnae3_ring_chain_node *ring_chain) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_ctrl_vector_chain *req; + struct hnae3_ring_chain_node *node; + struct hclge_desc desc; + int ret; + int i; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_ADD_RING_TO_VECTOR, false); + + req = (struct hclge_ctrl_vector_chain *)desc.data; + req->int_vector_id = vector_id; + + i = 0; + for (node = ring_chain; node; node = node->next) { + hnae_set_field(req->tqp_type_and_id[i], HCLGE_INT_TYPE_M, + HCLGE_INT_TYPE_S, + hnae_get_bit(node->flag, HNAE3_RING_TYPE_B)); + hnae_set_field(req->tqp_type_and_id[i], HCLGE_TQP_ID_M, + HCLGE_TQP_ID_S, node->tqp_index); + req->tqp_type_and_id[i] = cpu_to_le16(req->tqp_type_and_id[i]); + + if (++i >= HCLGE_VECTOR_ELEMENTS_PER_CMD) { + req->int_cause_num = HCLGE_VECTOR_ELEMENTS_PER_CMD; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Map TQP fail, status is %d.\n", + ret); + return ret; + } + i = 0; + + hclge_cmd_setup_basic_desc(&desc, + HCLGE_OPC_ADD_RING_TO_VECTOR, + false); + req->int_vector_id = vector_id; + } + } + + if (i > 0) { + req->int_cause_num = i; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Map TQP fail, status is %d.\n", ret); + return ret; + } + } + + return 0; +} + +int hclge_map_handle_ring_to_vector(struct hnae3_handle *handle, + int vector, + struct hnae3_ring_chain_node *ring_chain) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + int vector_id; + + vector_id = hclge_get_vector_index(hdev, vector); + if (vector_id < 0) { + dev_err(&hdev->pdev->dev, + "Get vector index fail. ret =%d\n", vector_id); + return vector_id; + } + + return hclge_map_vport_ring_to_vector(vport, vector_id, ring_chain); +} + +static int hclge_unmap_ring_from_vector( + struct hnae3_handle *handle, int vector, + struct hnae3_ring_chain_node *ring_chain) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hclge_ctrl_vector_chain *req; + struct hnae3_ring_chain_node *node; + struct hclge_desc desc; + int i, vector_id; + int ret; + + vector_id = hclge_get_vector_index(hdev, vector); + if (vector_id < 0) { + dev_err(&handle->pdev->dev, + "Get vector index fail. ret =%d\n", vector_id); + return vector_id; + } + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_DEL_RING_TO_VECTOR, false); + + req = (struct hclge_ctrl_vector_chain *)desc.data; + req->int_vector_id = vector_id; + + i = 0; + for (node = ring_chain; node; node = node->next) { + hnae_set_field(req->tqp_type_and_id[i], HCLGE_INT_TYPE_M, + HCLGE_INT_TYPE_S, + hnae_get_bit(node->flag, HNAE3_RING_TYPE_B)); + hnae_set_field(req->tqp_type_and_id[i], HCLGE_TQP_ID_M, + HCLGE_TQP_ID_S, node->tqp_index); + + req->tqp_type_and_id[i] = cpu_to_le16(req->tqp_type_and_id[i]); + + if (++i >= HCLGE_VECTOR_ELEMENTS_PER_CMD) { + req->int_cause_num = HCLGE_VECTOR_ELEMENTS_PER_CMD; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Unmap TQP fail, status is %d.\n", + ret); + return ret; + } + i = 0; + hclge_cmd_setup_basic_desc(&desc, + HCLGE_OPC_ADD_RING_TO_VECTOR, + false); + req->int_vector_id = vector_id; + } + } + + if (i > 0) { + req->int_cause_num = i; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Unmap TQP fail, status is %d.\n", ret); + return ret; + } + } + + return 0; +} + +int hclge_cmd_set_promisc_mode(struct hclge_dev *hdev, + struct hclge_promisc_param *param) +{ + struct hclge_promisc_cfg *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_PROMISC_MODE, false); + + req = (struct hclge_promisc_cfg *)desc.data; + req->vf_id = param->vf_id; + req->flag = (param->enable << HCLGE_PROMISC_EN_B); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Set promisc mode fail, status is %d.\n", ret); + return ret; + } + return 0; +} + +void hclge_promisc_param_init(struct hclge_promisc_param *param, bool en_uc, + bool en_mc, bool en_bc, int vport_id) +{ + if (!param) + return; + + memset(param, 0, sizeof(struct hclge_promisc_param)); + if (en_uc) + param->enable = HCLGE_PROMISC_EN_UC; + if (en_mc) + param->enable |= HCLGE_PROMISC_EN_MC; + if (en_bc) + param->enable |= HCLGE_PROMISC_EN_BC; + param->vf_id = vport_id; +} + +static void hclge_set_promisc_mode(struct hnae3_handle *handle, u32 en) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hclge_promisc_param param; + + hclge_promisc_param_init(¶m, en, en, true, vport->vport_id); + hclge_cmd_set_promisc_mode(hdev, ¶m); +} + +static void hclge_cfg_mac_mode(struct hclge_dev *hdev, bool enable) +{ + struct hclge_desc desc; + struct hclge_config_mac_mode *req = + (struct hclge_config_mac_mode *)desc.data; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAC_MODE, false); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_TX_EN_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_RX_EN_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_PAD_TX_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_PAD_RX_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_1588_TX_B, 0); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_1588_RX_B, 0); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_APP_LP_B, 0); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_LINE_LP_B, 0); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_FCS_TX_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, HCLGE_MAC_RX_FCS_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, + HCLGE_MAC_RX_FCS_STRIP_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, + HCLGE_MAC_TX_OVERSIZE_TRUNCATE_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, + HCLGE_MAC_RX_OVERSIZE_TRUNCATE_B, enable); + hnae_set_bit(req->txrx_pad_fcs_loop_en, + HCLGE_MAC_TX_UNDER_MIN_ERR_B, enable); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) + dev_err(&hdev->pdev->dev, + "mac enable fail, ret =%d.\n", ret); +} + +static int hclge_tqp_enable(struct hclge_dev *hdev, int tqp_id, + int stream_id, bool enable) +{ + struct hclge_desc desc; + struct hclge_cfg_com_tqp_queue *req = + (struct hclge_cfg_com_tqp_queue *)desc.data; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_COM_TQP_QUEUE, false); + req->tqp_id = cpu_to_le16(tqp_id & HCLGE_RING_ID_MASK); + req->stream_id = cpu_to_le16(stream_id); + req->enable |= enable << HCLGE_TQP_ENABLE_B; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) + dev_err(&hdev->pdev->dev, + "Tqp enable fail, status =%d.\n", ret); + return ret; +} + +static void hclge_reset_tqp_stats(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hnae3_queue *queue; + struct hclge_tqp *tqp; + int i; + + for (i = 0; i < vport->alloc_tqps; i++) { + queue = handle->kinfo.tqp[i]; + tqp = container_of(queue, struct hclge_tqp, q); + memset(&tqp->tqp_stats, 0, sizeof(tqp->tqp_stats)); + } +} + +static int hclge_ae_start(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + int i, queue_id, ret; + + for (i = 0; i < vport->alloc_tqps; i++) { + /* todo clear interrupt */ + /* ring enable */ + queue_id = hclge_get_queue_id(handle->kinfo.tqp[i]); + if (queue_id < 0) { + dev_warn(&hdev->pdev->dev, + "Get invalid queue id, ignore it\n"); + continue; + } + + hclge_tqp_enable(hdev, queue_id, 0, true); + } + /* mac enable */ + hclge_cfg_mac_mode(hdev, true); + clear_bit(HCLGE_STATE_DOWN, &hdev->state); + (void)mod_timer(&hdev->service_timer, jiffies + HZ); + + ret = hclge_mac_start_phy(hdev); + if (ret) + return ret; + + /* reset tqp stats */ + hclge_reset_tqp_stats(handle); + + return 0; +} + +static void hclge_ae_stop(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + int i, queue_id; + + for (i = 0; i < vport->alloc_tqps; i++) { + /* Ring disable */ + queue_id = hclge_get_queue_id(handle->kinfo.tqp[i]); + if (queue_id < 0) { + dev_warn(&hdev->pdev->dev, + "Get invalid queue id, ignore it\n"); + continue; + } + + hclge_tqp_enable(hdev, queue_id, 0, false); + } + /* Mac disable */ + hclge_cfg_mac_mode(hdev, false); + + hclge_mac_stop_phy(hdev); + + /* reset tqp stats */ + hclge_reset_tqp_stats(handle); +} + +static int hclge_get_mac_vlan_cmd_status(struct hclge_vport *vport, + u16 cmdq_resp, u8 resp_code, + enum hclge_mac_vlan_tbl_opcode op) +{ + struct hclge_dev *hdev = vport->back; + int return_status = -EIO; + + if (cmdq_resp) { + dev_err(&hdev->pdev->dev, + "cmdq execute failed for get_mac_vlan_cmd_status,status=%d.\n", + cmdq_resp); + return -EIO; + } + + if (op == HCLGE_MAC_VLAN_ADD) { + if ((!resp_code) || (resp_code == 1)) { + return_status = 0; + } else if (resp_code == 2) { + return_status = -EIO; + dev_err(&hdev->pdev->dev, + "add mac addr failed for uc_overflow.\n"); + } else if (resp_code == 3) { + return_status = -EIO; + dev_err(&hdev->pdev->dev, + "add mac addr failed for mc_overflow.\n"); + } else { + dev_err(&hdev->pdev->dev, + "add mac addr failed for undefined, code=%d.\n", + resp_code); + } + } else if (op == HCLGE_MAC_VLAN_REMOVE) { + if (!resp_code) { + return_status = 0; + } else if (resp_code == 1) { + return_status = -EIO; + dev_dbg(&hdev->pdev->dev, + "remove mac addr failed for miss.\n"); + } else { + dev_err(&hdev->pdev->dev, + "remove mac addr failed for undefined, code=%d.\n", + resp_code); + } + } else if (op == HCLGE_MAC_VLAN_LKUP) { + if (!resp_code) { + return_status = 0; + } else if (resp_code == 1) { + return_status = -EIO; + dev_dbg(&hdev->pdev->dev, + "lookup mac addr failed for miss.\n"); + } else { + dev_err(&hdev->pdev->dev, + "lookup mac addr failed for undefined, code=%d.\n", + resp_code); + } + } else { + return_status = -EIO; + dev_err(&hdev->pdev->dev, + "unknown opcode for get_mac_vlan_cmd_status,opcode=%d.\n", + op); + } + + return return_status; +} + +static int hclge_update_desc_vfid(struct hclge_desc *desc, int vfid, bool clr) +{ + int word_num; + int bit_num; + + if (vfid > 255 || vfid < 0) + return -EIO; + + if (vfid >= 0 && vfid <= 191) { + word_num = vfid / 32; + bit_num = vfid % 32; + if (clr) + desc[1].data[word_num] &= ~(1 << bit_num); + else + desc[1].data[word_num] |= (1 << bit_num); + } else { + word_num = (vfid - 192) / 32; + bit_num = vfid % 32; + if (clr) + desc[2].data[word_num] &= ~(1 << bit_num); + else + desc[2].data[word_num] |= (1 << bit_num); + } + + return 0; +} + +static bool hclge_is_all_function_id_zero(struct hclge_desc *desc) +{ +#define HCLGE_DESC_NUMBER 3 +#define HCLGE_FUNC_NUMBER_PER_DESC 6 + int i, j; + + for (i = 0; i < HCLGE_DESC_NUMBER; i++) + for (j = 0; j < HCLGE_FUNC_NUMBER_PER_DESC; j++) + if (desc[i].data[j]) + return false; + + return true; +} + +static void hclge_prepare_mac_addr(struct hclge_mac_vlan_tbl_entry *new_req, + const u8 *addr) +{ + const unsigned char *mac_addr = addr; + u32 high_val = mac_addr[2] << 16 | (mac_addr[3] << 24) | + (mac_addr[0]) | (mac_addr[1] << 8); + u32 low_val = mac_addr[4] | (mac_addr[5] << 8); + + new_req->mac_addr_hi32 = cpu_to_le32(high_val); + new_req->mac_addr_lo16 = cpu_to_le16(low_val & 0xffff); +} + +u16 hclge_get_mac_addr_to_mta_index(struct hclge_vport *vport, + const u8 *addr) +{ + u16 high_val = addr[1] | (addr[0] << 8); + struct hclge_dev *hdev = vport->back; + u32 rsh = 4 - hdev->mta_mac_sel_type; + u16 ret_val = (high_val >> rsh) & 0xfff; + + return ret_val; +} + +static int hclge_set_mta_filter_mode(struct hclge_dev *hdev, + enum hclge_mta_dmac_sel_type mta_mac_sel, + bool enable) +{ + struct hclge_mta_filter_mode *req; + struct hclge_desc desc; + int ret; + + req = (struct hclge_mta_filter_mode *)desc.data; + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MTA_MAC_MODE_CFG, false); + + hnae_set_bit(req->dmac_sel_en, HCLGE_CFG_MTA_MAC_EN_B, + enable); + hnae_set_field(req->dmac_sel_en, HCLGE_CFG_MTA_MAC_SEL_M, + HCLGE_CFG_MTA_MAC_SEL_S, mta_mac_sel); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Config mat filter mode failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + + return 0; +} + +int hclge_cfg_func_mta_filter(struct hclge_dev *hdev, + u8 func_id, + bool enable) +{ + struct hclge_cfg_func_mta_filter *req; + struct hclge_desc desc; + int ret; + + req = (struct hclge_cfg_func_mta_filter *)desc.data; + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MTA_MAC_FUNC_CFG, false); + + hnae_set_bit(req->accept, HCLGE_CFG_FUNC_MTA_ACCEPT_B, + enable); + req->function_id = func_id; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Config func_id enable failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + + return 0; +} + +static int hclge_set_mta_table_item(struct hclge_vport *vport, + u16 idx, + bool enable) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_cfg_func_mta_item *req; + struct hclge_desc desc; + int ret; + + req = (struct hclge_cfg_func_mta_item *)desc.data; + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MTA_TBL_ITEM_CFG, false); + hnae_set_bit(req->accept, HCLGE_CFG_MTA_ITEM_ACCEPT_B, enable); + + hnae_set_field(req->item_idx, HCLGE_CFG_MTA_ITEM_IDX_M, + HCLGE_CFG_MTA_ITEM_IDX_S, idx); + req->item_idx = cpu_to_le16(req->item_idx); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Config mta table item failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + + return 0; +} + +static int hclge_remove_mac_vlan_tbl(struct hclge_vport *vport, + struct hclge_mac_vlan_tbl_entry *req) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_desc desc; + u8 resp_code; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MAC_VLAN_REMOVE, false); + + memcpy(desc.data, req, sizeof(struct hclge_mac_vlan_tbl_entry)); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "del mac addr failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + resp_code = (desc.data[0] >> 8) & 0xff; + + return hclge_get_mac_vlan_cmd_status(vport, desc.retval, resp_code, + HCLGE_MAC_VLAN_REMOVE); +} + +static int hclge_lookup_mac_vlan_tbl(struct hclge_vport *vport, + struct hclge_mac_vlan_tbl_entry *req, + struct hclge_desc *desc, + bool is_mc) +{ + struct hclge_dev *hdev = vport->back; + u8 resp_code; + int ret; + + hclge_cmd_setup_basic_desc(&desc[0], HCLGE_OPC_MAC_VLAN_ADD, true); + if (is_mc) { + desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + memcpy(desc[0].data, + req, + sizeof(struct hclge_mac_vlan_tbl_entry)); + hclge_cmd_setup_basic_desc(&desc[1], + HCLGE_OPC_MAC_VLAN_ADD, + true); + desc[1].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + hclge_cmd_setup_basic_desc(&desc[2], + HCLGE_OPC_MAC_VLAN_ADD, + true); + ret = hclge_cmd_send(&hdev->hw, desc, 3); + } else { + memcpy(desc[0].data, + req, + sizeof(struct hclge_mac_vlan_tbl_entry)); + ret = hclge_cmd_send(&hdev->hw, desc, 1); + } + if (ret) { + dev_err(&hdev->pdev->dev, + "lookup mac addr failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + resp_code = (desc[0].data[0] >> 8) & 0xff; + + return hclge_get_mac_vlan_cmd_status(vport, desc[0].retval, resp_code, + HCLGE_MAC_VLAN_LKUP); +} + +static int hclge_add_mac_vlan_tbl(struct hclge_vport *vport, + struct hclge_mac_vlan_tbl_entry *req, + struct hclge_desc *mc_desc) +{ + struct hclge_dev *hdev = vport->back; + int cfg_status; + u8 resp_code; + int ret; + + if (!mc_desc) { + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, + HCLGE_OPC_MAC_VLAN_ADD, + false); + memcpy(desc.data, req, sizeof(struct hclge_mac_vlan_tbl_entry)); + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + resp_code = (desc.data[0] >> 8) & 0xff; + cfg_status = hclge_get_mac_vlan_cmd_status(vport, desc.retval, + resp_code, + HCLGE_MAC_VLAN_ADD); + } else { + mc_desc[0].flag &= cpu_to_le16(~HCLGE_CMD_FLAG_WR); + mc_desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + mc_desc[1].flag &= cpu_to_le16(~HCLGE_CMD_FLAG_WR); + mc_desc[1].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + mc_desc[2].flag &= cpu_to_le16(~HCLGE_CMD_FLAG_WR); + mc_desc[2].flag &= cpu_to_le16(~HCLGE_CMD_FLAG_NEXT); + memcpy(mc_desc[0].data, req, + sizeof(struct hclge_mac_vlan_tbl_entry)); + ret = hclge_cmd_send(&hdev->hw, mc_desc, 3); + resp_code = (mc_desc[0].data[0] >> 8) & 0xff; + cfg_status = hclge_get_mac_vlan_cmd_status(vport, + mc_desc[0].retval, + resp_code, + HCLGE_MAC_VLAN_ADD); + } + + if (ret) { + dev_err(&hdev->pdev->dev, + "add mac addr failed for cmd_send, ret =%d.\n", + ret); + return ret; + } + + return cfg_status; +} + +static int hclge_add_uc_addr(struct hnae3_handle *handle, + const unsigned char *addr) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + + return hclge_add_uc_addr_common(vport, addr); +} + +int hclge_add_uc_addr_common(struct hclge_vport *vport, + const unsigned char *addr) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_mac_vlan_tbl_entry req; + enum hclge_cmd_status status; + + /* mac addr check */ + if (is_zero_ether_addr(addr) || + is_broadcast_ether_addr(addr) || + is_multicast_ether_addr(addr)) { + dev_err(&hdev->pdev->dev, + "Set_uc mac err! invalid mac:%pM. is_zero:%d,is_br=%d,is_mul=%d\n", + addr, + is_zero_ether_addr(addr), + is_broadcast_ether_addr(addr), + is_multicast_ether_addr(addr)); + return -EINVAL; + } + + memset(&req, 0, sizeof(req)); + hnae_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1); + hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0); + hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT1_EN_B, 0); + hnae_set_bit(req.mc_mac_en, HCLGE_MAC_VLAN_BIT0_EN_B, 0); + hnae_set_bit(req.egress_port, + HCLGE_MAC_EPORT_SW_EN_B, 0); + hnae_set_bit(req.egress_port, + HCLGE_MAC_EPORT_TYPE_B, 0); + hnae_set_field(req.egress_port, HCLGE_MAC_EPORT_VFID_M, + HCLGE_MAC_EPORT_VFID_S, vport->vport_id); + hnae_set_field(req.egress_port, HCLGE_MAC_EPORT_PFID_M, + HCLGE_MAC_EPORT_PFID_S, 0); + req.egress_port = cpu_to_le16(req.egress_port); + + hclge_prepare_mac_addr(&req, addr); + + status = hclge_add_mac_vlan_tbl(vport, &req, NULL); + + return status; +} + +static int hclge_rm_uc_addr(struct hnae3_handle *handle, + const unsigned char *addr) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + + return hclge_rm_uc_addr_common(vport, addr); +} + +int hclge_rm_uc_addr_common(struct hclge_vport *vport, + const unsigned char *addr) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_mac_vlan_tbl_entry req; + enum hclge_cmd_status status; + + /* mac addr check */ + if (is_zero_ether_addr(addr) || + is_broadcast_ether_addr(addr) || + is_multicast_ether_addr(addr)) { + dev_dbg(&hdev->pdev->dev, + "Remove mac err! invalid mac:%pM.\n", + addr); + return -EINVAL; + } + + memset(&req, 0, sizeof(req)); + hnae_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1); + hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0); + hclge_prepare_mac_addr(&req, addr); + status = hclge_remove_mac_vlan_tbl(vport, &req); + + return status; +} + +static int hclge_add_mc_addr(struct hnae3_handle *handle, + const unsigned char *addr) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + + return hclge_add_mc_addr_common(vport, addr); +} + +int hclge_add_mc_addr_common(struct hclge_vport *vport, + const unsigned char *addr) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_mac_vlan_tbl_entry req; + struct hclge_desc desc[3]; + u16 tbl_idx; + int status; + + /* mac addr check */ + if (!is_multicast_ether_addr(addr)) { + dev_err(&hdev->pdev->dev, + "Add mc mac err! invalid mac:%pM.\n", + addr); + return -EINVAL; + } + memset(&req, 0, sizeof(req)); + hnae_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1); + hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0); + hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT1_EN_B, 1); + hnae_set_bit(req.mc_mac_en, HCLGE_MAC_VLAN_BIT0_EN_B, 0); + hclge_prepare_mac_addr(&req, addr); + status = hclge_lookup_mac_vlan_tbl(vport, &req, desc, true); + if (!status) { + /* This mac addr exist, update VFID for it */ + hclge_update_desc_vfid(desc, vport->vport_id, false); + status = hclge_add_mac_vlan_tbl(vport, &req, desc); + } else { + /* This mac addr do not exist, add new entry for it */ + memset(desc[0].data, 0, sizeof(desc[0].data)); + memset(desc[1].data, 0, sizeof(desc[0].data)); + memset(desc[2].data, 0, sizeof(desc[0].data)); + hclge_update_desc_vfid(desc, vport->vport_id, false); + status = hclge_add_mac_vlan_tbl(vport, &req, desc); + } + + /* Set MTA table for this MAC address */ + tbl_idx = hclge_get_mac_addr_to_mta_index(vport, addr); + status = hclge_set_mta_table_item(vport, tbl_idx, true); + + return status; +} + +static int hclge_rm_mc_addr(struct hnae3_handle *handle, + const unsigned char *addr) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + + return hclge_rm_mc_addr_common(vport, addr); +} + +int hclge_rm_mc_addr_common(struct hclge_vport *vport, + const unsigned char *addr) +{ + struct hclge_dev *hdev = vport->back; + struct hclge_mac_vlan_tbl_entry req; + enum hclge_cmd_status status; + struct hclge_desc desc[3]; + u16 tbl_idx; + + /* mac addr check */ + if (!is_multicast_ether_addr(addr)) { + dev_dbg(&hdev->pdev->dev, + "Remove mc mac err! invalid mac:%pM.\n", + addr); + return -EINVAL; + } + + memset(&req, 0, sizeof(req)); + hnae_set_bit(req.flags, HCLGE_MAC_VLAN_BIT0_EN_B, 1); + hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT0_EN_B, 0); + hnae_set_bit(req.entry_type, HCLGE_MAC_VLAN_BIT1_EN_B, 1); + hnae_set_bit(req.mc_mac_en, HCLGE_MAC_VLAN_BIT0_EN_B, 0); + hclge_prepare_mac_addr(&req, addr); + status = hclge_lookup_mac_vlan_tbl(vport, &req, desc, true); + if (!status) { + /* This mac addr exist, remove this handle's VFID for it */ + hclge_update_desc_vfid(desc, vport->vport_id, true); + + if (hclge_is_all_function_id_zero(desc)) + /* All the vfid is zero, so need to delete this entry */ + status = hclge_remove_mac_vlan_tbl(vport, &req); + else + /* Not all the vfid is zero, update the vfid */ + status = hclge_add_mac_vlan_tbl(vport, &req, desc); + + } else { + /* This mac addr do not exist, can't delete it */ + dev_err(&hdev->pdev->dev, + "Rm multicast mac addr failed, ret = %d.\n", + status); + return -EIO; + } + + /* Set MTB table for this MAC address */ + tbl_idx = hclge_get_mac_addr_to_mta_index(vport, addr); + status = hclge_set_mta_table_item(vport, tbl_idx, false); + + return status; +} + +static void hclge_get_mac_addr(struct hnae3_handle *handle, u8 *p) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + ether_addr_copy(p, hdev->hw.mac.mac_addr); +} + +static int hclge_set_mac_addr(struct hnae3_handle *handle, void *p) +{ + const unsigned char *new_addr = (const unsigned char *)p; + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + /* mac addr check */ + if (is_zero_ether_addr(new_addr) || + is_broadcast_ether_addr(new_addr) || + is_multicast_ether_addr(new_addr)) { + dev_err(&hdev->pdev->dev, + "Change uc mac err! invalid mac:%p.\n", + new_addr); + return -EINVAL; + } + + hclge_rm_uc_addr(handle, hdev->hw.mac.mac_addr); + + if (!hclge_add_uc_addr(handle, new_addr)) { + ether_addr_copy(hdev->hw.mac.mac_addr, new_addr); + return 0; + } + + return -EIO; +} + +static int hclge_set_vlan_filter_ctrl(struct hclge_dev *hdev, u8 vlan_type, + bool filter_en) +{ + struct hclge_vlan_filter_ctrl *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_VLAN_FILTER_CTRL, false); + + req = (struct hclge_vlan_filter_ctrl *)desc.data; + req->vlan_type = vlan_type; + req->vlan_fe = filter_en; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "set vlan filter fail, ret =%d.\n", + ret); + return ret; + } + + return 0; +} + +int hclge_set_vf_vlan_common(struct hclge_dev *hdev, int vfid, + bool is_kill, u16 vlan, u8 qos, __be16 proto) +{ +#define HCLGE_MAX_VF_BYTES 16 + struct hclge_vlan_filter_vf_cfg *req0; + struct hclge_vlan_filter_vf_cfg *req1; + struct hclge_desc desc[2]; + u8 vf_byte_val; + u8 vf_byte_off; + int ret; + + hclge_cmd_setup_basic_desc(&desc[0], + HCLGE_OPC_VLAN_FILTER_VF_CFG, false); + hclge_cmd_setup_basic_desc(&desc[1], + HCLGE_OPC_VLAN_FILTER_VF_CFG, false); + + desc[0].flag |= cpu_to_le16(HCLGE_CMD_FLAG_NEXT); + + vf_byte_off = vfid / 8; + vf_byte_val = 1 << (vfid % 8); + + req0 = (struct hclge_vlan_filter_vf_cfg *)desc[0].data; + req1 = (struct hclge_vlan_filter_vf_cfg *)desc[1].data; + + req0->vlan_id = vlan; + req0->vlan_cfg = is_kill; + + if (vf_byte_off < HCLGE_MAX_VF_BYTES) + req0->vf_bitmap[vf_byte_off] = vf_byte_val; + else + req1->vf_bitmap[vf_byte_off - HCLGE_MAX_VF_BYTES] = vf_byte_val; + + ret = hclge_cmd_send(&hdev->hw, desc, 2); + if (ret) { + dev_err(&hdev->pdev->dev, + "Send vf vlan command fail, ret =%d.\n", + ret); + return ret; + } + + if (!is_kill) { + if (!req0->resp_code || req0->resp_code == 1) + return 0; + + dev_err(&hdev->pdev->dev, + "Add vf vlan filter fail, ret =%d.\n", + req0->resp_code); + } else { + if (!req0->resp_code) + return 0; + + dev_err(&hdev->pdev->dev, + "Kill vf vlan filter fail, ret =%d.\n", + req0->resp_code); + } + + return -EIO; +} + +static int hclge_set_port_vlan_filter(struct hnae3_handle *handle, + __be16 proto, u16 vlan_id, + bool is_kill) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct hclge_vlan_filter_pf_cfg *req; + struct hclge_desc desc; + u8 vlan_offset_byte_val; + u8 vlan_offset_byte; + u8 vlan_offset_160; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_VLAN_FILTER_PF_CFG, false); + + vlan_offset_160 = vlan_id / 160; + vlan_offset_byte = (vlan_id % 160) / 8; + vlan_offset_byte_val = 1 << (vlan_id % 8); + + req = (struct hclge_vlan_filter_pf_cfg *)desc.data; + req->vlan_offset = vlan_offset_160; + req->vlan_cfg = is_kill; + req->vlan_offset_bitmap[vlan_offset_byte] = vlan_offset_byte_val; + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "port vlan command, send fail, ret =%d.\n", + ret); + return ret; + } + + ret = hclge_set_vf_vlan_common(hdev, 0, is_kill, vlan_id, 0, proto); + if (ret) { + dev_err(&hdev->pdev->dev, + "Set pf vlan filter config fail, ret =%d.\n", + ret); + return -EIO; + } + + return 0; +} + +static int hclge_set_vf_vlan_filter(struct hnae3_handle *handle, int vfid, + u16 vlan, u8 qos, __be16 proto) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + if ((vfid >= hdev->num_alloc_vfs) || (vlan > 4095) || (qos > 7)) + return -EINVAL; + if (proto != htons(ETH_P_8021Q)) + return -EPROTONOSUPPORT; + + return hclge_set_vf_vlan_common(hdev, vfid, false, vlan, qos, proto); +} + +static int hclge_init_vlan_config(struct hclge_dev *hdev) +{ +#define HCLGE_VLAN_TYPE_VF_TABLE 0 +#define HCLGE_VLAN_TYPE_PORT_TABLE 1 + int ret; + + ret = hclge_set_vlan_filter_ctrl(hdev, HCLGE_VLAN_TYPE_VF_TABLE, + true); + if (ret) + return ret; + + ret = hclge_set_vlan_filter_ctrl(hdev, HCLGE_VLAN_TYPE_PORT_TABLE, + true); + + return ret; +} + +static int hclge_set_mtu(struct hnae3_handle *handle, int new_mtu) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_config_max_frm_size *req; + struct hclge_dev *hdev = vport->back; + struct hclge_desc desc; + int ret; + + if ((new_mtu < HCLGE_MAC_MIN_MTU) || (new_mtu > HCLGE_MAC_MAX_MTU)) + return -EINVAL; + + hdev->mps = new_mtu; + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CONFIG_MAX_FRM_SIZE, false); + + req = (struct hclge_config_max_frm_size *)desc.data; + req->max_frm_size = cpu_to_le16(new_mtu); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, "set mtu fail, ret =%d.\n", ret); + return ret; + } + + return 0; +} + +static int hclge_send_reset_tqp_cmd(struct hclge_dev *hdev, u16 queue_id, + bool enable) +{ + struct hclge_reset_tqp_queue *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RESET_TQP_QUEUE, false); + + req = (struct hclge_reset_tqp_queue *)desc.data; + req->tqp_id = cpu_to_le16(queue_id & HCLGE_RING_ID_MASK); + hnae_set_bit(req->reset_req, HCLGE_TQP_RESET_B, enable); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Send tqp reset cmd error, status =%d\n", ret); + return ret; + } + + return 0; +} + +static int hclge_get_reset_status(struct hclge_dev *hdev, u16 queue_id) +{ + struct hclge_reset_tqp_queue *req; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_RESET_TQP_QUEUE, true); + + req = (struct hclge_reset_tqp_queue *)desc.data; + req->tqp_id = cpu_to_le16(queue_id & HCLGE_RING_ID_MASK); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "Get reset status error, status =%d\n", ret); + return ret; + } + + return hnae_get_bit(req->ready_to_reset, HCLGE_TQP_RESET_B); +} + +static void hclge_reset_tqp(struct hnae3_handle *handle, u16 queue_id) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + int reset_try_times = 0; + int reset_status; + int ret; + + ret = hclge_tqp_enable(hdev, queue_id, 0, false); + if (ret) { + dev_warn(&hdev->pdev->dev, "Disable tqp fail, ret = %d\n", ret); + return; + } + + ret = hclge_send_reset_tqp_cmd(hdev, queue_id, true); + if (ret) { + dev_warn(&hdev->pdev->dev, + "Send reset tqp cmd fail, ret = %d\n", ret); + return; + } + + reset_try_times = 0; + while (reset_try_times++ < HCLGE_TQP_RESET_TRY_TIMES) { + /* Wait for tqp hw reset */ + msleep(20); + reset_status = hclge_get_reset_status(hdev, queue_id); + if (reset_status) + break; + } + + if (reset_try_times >= HCLGE_TQP_RESET_TRY_TIMES) { + dev_warn(&hdev->pdev->dev, "Reset TQP fail\n"); + return; + } + + ret = hclge_send_reset_tqp_cmd(hdev, queue_id, false); + if (ret) { + dev_warn(&hdev->pdev->dev, + "Deassert the soft reset fail, ret = %d\n", ret); + return; + } +} + +static u32 hclge_get_fw_version(struct hnae3_handle *handle) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + return hdev->fw_version; +} + +static void hclge_get_pauseparam(struct hnae3_handle *handle, u32 *auto_neg, + u32 *rx_en, u32 *tx_en) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + *auto_neg = hclge_get_autoneg(handle); + + if (hdev->tm_info.fc_mode == HCLGE_FC_PFC) { + *rx_en = 0; + *tx_en = 0; + return; + } + + if (hdev->tm_info.fc_mode == HCLGE_FC_RX_PAUSE) { + *rx_en = 1; + *tx_en = 0; + } else if (hdev->tm_info.fc_mode == HCLGE_FC_TX_PAUSE) { + *tx_en = 1; + *rx_en = 0; + } else if (hdev->tm_info.fc_mode == HCLGE_FC_FULL) { + *rx_en = 1; + *tx_en = 1; + } else { + *rx_en = 0; + *tx_en = 0; + } +} + +static void hclge_get_ksettings_an_result(struct hnae3_handle *handle, + u8 *auto_neg, u32 *speed, u8 *duplex) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + if (speed) + *speed = hdev->hw.mac.speed; + if (duplex) + *duplex = hdev->hw.mac.duplex; + if (auto_neg) + *auto_neg = hdev->hw.mac.autoneg; +} + +static void hclge_get_media_type(struct hnae3_handle *handle, u8 *media_type) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + + if (media_type) + *media_type = hdev->hw.mac.media_type; +} + +static void hclge_get_mdix_mode(struct hnae3_handle *handle, + u8 *tp_mdix_ctrl, u8 *tp_mdix) +{ + struct hclge_vport *vport = hclge_get_vport(handle); + struct hclge_dev *hdev = vport->back; + struct phy_device *phydev = hdev->hw.mac.phydev; + int mdix_ctrl, mdix, retval, is_resolved; + + if (!phydev) { + *tp_mdix_ctrl = ETH_TP_MDI_INVALID; + *tp_mdix = ETH_TP_MDI_INVALID; + return; + } + + phy_write(phydev, HCLGE_PHY_PAGE_REG, HCLGE_PHY_PAGE_MDIX); + + retval = phy_read(phydev, HCLGE_PHY_CSC_REG); + mdix_ctrl = hnae_get_field(retval, HCLGE_PHY_MDIX_CTRL_M, + HCLGE_PHY_MDIX_CTRL_S); + + retval = phy_read(phydev, HCLGE_PHY_CSS_REG); + mdix = hnae_get_bit(retval, HCLGE_PHY_MDIX_STATUS_B); + is_resolved = hnae_get_bit(retval, HCLGE_PHY_SPEED_DUP_RESOLVE_B); + + phy_write(phydev, HCLGE_PHY_PAGE_REG, HCLGE_PHY_PAGE_COPPER); + + switch (mdix_ctrl) { + case 0x0: + *tp_mdix_ctrl = ETH_TP_MDI; + break; + case 0x1: + *tp_mdix_ctrl = ETH_TP_MDI_X; + break; + case 0x3: + *tp_mdix_ctrl = ETH_TP_MDI_AUTO; + break; + default: + *tp_mdix_ctrl = ETH_TP_MDI_INVALID; + break; + } + + if (!is_resolved) + *tp_mdix = ETH_TP_MDI_INVALID; + else if (mdix) + *tp_mdix = ETH_TP_MDI_X; + else + *tp_mdix = ETH_TP_MDI; +} + +static int hclge_init_client_instance(struct hnae3_client *client, + struct hnae3_ae_dev *ae_dev) +{ + struct hclge_dev *hdev = ae_dev->priv; + struct hclge_vport *vport; + int i, ret; + + for (i = 0; i < hdev->num_vmdq_vport + 1; i++) { + vport = &hdev->vport[i]; + + switch (client->type) { + case HNAE3_CLIENT_KNIC: + + hdev->nic_client = client; + vport->nic.client = client; + ret = client->ops->init_instance(&vport->nic); + if (ret) + goto err; + + if (hdev->roce_client && + hnae_get_bit(hdev->ae_dev->flag, + HNAE_DEV_SUPPORT_ROCE_B)) { + struct hnae3_client *rc = hdev->roce_client; + + ret = hclge_init_roce_base_info(vport); + if (ret) + goto err; + + ret = rc->ops->init_instance(&vport->roce); + if (ret) + goto err; + } + + break; + case HNAE3_CLIENT_UNIC: + hdev->nic_client = client; + vport->nic.client = client; + + ret = client->ops->init_instance(&vport->nic); + if (ret) + goto err; + + break; + case HNAE3_CLIENT_ROCE: + if (hnae_get_bit(hdev->ae_dev->flag, + HNAE_DEV_SUPPORT_ROCE_B)) { + hdev->roce_client = client; + vport->roce.client = client; + } + + if (hdev->roce_client) { + ret = hclge_init_roce_base_info(vport); + if (ret) + goto err; + + ret = client->ops->init_instance(&vport->roce); + if (ret) + goto err; + } + } + } + + return 0; +err: + return ret; +} + +static void hclge_uninit_client_instance(struct hnae3_client *client, + struct hnae3_ae_dev *ae_dev) +{ + struct hclge_dev *hdev = ae_dev->priv; + struct hclge_vport *vport; + int i; + + for (i = 0; i < hdev->num_vmdq_vport + 1; i++) { + vport = &hdev->vport[i]; + if (hdev->roce_client) + hdev->roce_client->ops->uninit_instance(&vport->roce, + 0); + if (client->type == HNAE3_CLIENT_ROCE) + return; + if (client->ops->uninit_instance) + client->ops->uninit_instance(&vport->nic, 0); + } +} + +static int hclge_pci_init(struct hclge_dev *hdev) +{ + struct pci_dev *pdev = hdev->pdev; + struct hclge_hw *hw; + int ret; + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "failed to enable PCI device\n"); + goto err_no_drvdata; + } + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) { + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, + "can't set consistent PCI DMA"); + goto err_disable_device; + } + dev_warn(&pdev->dev, "set DMA mask to 32 bits\n"); + } + + ret = pci_request_regions(pdev, HCLGE_DRIVER_NAME); + if (ret) { + dev_err(&pdev->dev, "PCI request regions failed %d\n", ret); + goto err_disable_device; + } + + pci_set_master(pdev); + hw = &hdev->hw; + hw->back = hdev; + hw->io_base = pcim_iomap(pdev, 2, 0); + if (!hw->io_base) { + dev_err(&pdev->dev, "Can't map configuration register space\n"); + ret = -ENOMEM; + goto err_clr_master; + } + + return 0; +err_clr_master: + pci_clear_master(pdev); + pci_release_regions(pdev); +err_disable_device: + pci_disable_device(pdev); +err_no_drvdata: + pci_set_drvdata(pdev, NULL); + + return ret; +} + +static void hclge_pci_uninit(struct hclge_dev *hdev) +{ + struct pci_dev *pdev = hdev->pdev; + + if (hdev->flag & HCLGE_FLAG_USE_MSIX) { + pci_disable_msix(pdev); + devm_kfree(&pdev->dev, hdev->msix_entries); + hdev->msix_entries = NULL; + } else { + pci_disable_msi(pdev); + } + + pci_clear_master(pdev); + pci_release_mem_regions(pdev); + pci_disable_device(pdev); +} + +static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) +{ + struct pci_dev *pdev = ae_dev->pdev; + const struct pci_device_id *id; + struct hclge_dev *hdev; + int ret; + + hdev = devm_kzalloc(&pdev->dev, sizeof(*hdev), GFP_KERNEL); + if (!hdev) { + ret = -ENOMEM; + goto err_hclge_dev; + } + + hdev->flag |= HCLGE_FLAG_USE_MSIX; + hdev->pdev = pdev; + hdev->ae_dev = ae_dev; + ae_dev->priv = hdev; + + id = pci_match_id(roce_pci_tbl, ae_dev->pdev); + if (id) + hnae_set_bit(ae_dev->flag, HNAE_DEV_SUPPORT_ROCE_B, 1); + + ret = hclge_pci_init(hdev); + if (ret) { + dev_err(&pdev->dev, "PCI init failed\n"); + goto err_pci_init; + } + + /* Command queue initialize */ + ret = hclge_cmd_init(hdev); + if (ret) + goto err_cmd_init; + + ret = hclge_get_cap(hdev); + if (ret) { + dev_err(&pdev->dev, "get hw capability error, ret = %d.\n", + ret); + return ret; + } + + ret = hclge_configure(hdev); + if (ret) { + dev_err(&pdev->dev, "Configure dev error, ret = %d.\n", ret); + return ret; + } + + if (hdev->flag & HCLGE_FLAG_USE_MSIX) + ret = hclge_init_msix(hdev); + else + ret = hclge_init_msi(hdev); + if (ret) { + dev_err(&pdev->dev, "Init msix/msi error, ret = %d.\n", ret); + return ret; + } + + ret = hclge_alloc_tqps(hdev); + if (ret) { + dev_err(&pdev->dev, "Allocate TQPs error, ret = %d.\n", ret); + return ret; + } + + ret = hclge_alloc_vport(hdev); + if (ret) { + dev_err(&pdev->dev, "Allocate vport error, ret = %d.\n", ret); + return ret; + } + + ret = hclge_mac_init(hdev); + if (ret) { + dev_err(&pdev->dev, "Mac init error, ret = %d\n", ret); + return ret; + } + ret = hclge_buffer_alloc(hdev); + if (ret) { + dev_err(&pdev->dev, "Buffer allocate fail, ret =%d\n", ret); + return ret; + } + + ret = hclge_config_tso(hdev, HCLGE_TSO_MSS_MIN, HCLGE_TSO_MSS_MAX); + if (ret) { + dev_err(&pdev->dev, "Enable tso fail, ret =%d\n", ret); + return ret; + } + + ret = hclge_rss_init_hw(hdev); + if (ret) { + dev_err(&pdev->dev, "Rss init fail, ret =%d\n", ret); + return ret; + } + + ret = hclge_init_vlan_config(hdev); + if (ret) { + dev_err(&pdev->dev, "VLAN init fail, ret =%d\n", ret); + return ret; + } + + ret = hclge_tm_schd_init(hdev); + if (ret) { + dev_err(&pdev->dev, "tm schd init fail, ret =%d\n", ret); + return ret; + } + + setup_timer(&hdev->service_timer, hclge_service_timer, + (unsigned long)hdev); + INIT_WORK(&hdev->service_task, hclge_service_task); + + set_bit(HCLGE_STATE_SERVICE_INITED, &hdev->state); + set_bit(HCLGE_STATE_DOWN, &hdev->state); + + pr_info("%s driver initialization finished.\n", HCLGE_DRIVER_NAME); + return 0; + +err_cmd_init: + pci_release_regions(pdev); +err_pci_init: + pci_set_drvdata(pdev, NULL); +err_hclge_dev: + return ret; +} + +static void hclge_uninit_ae_dev(struct hnae3_ae_dev *ae_dev) +{ + struct hclge_dev *hdev = ae_dev->priv; + struct hclge_mac *mac = &hdev->hw.mac; + + set_bit(HCLGE_STATE_DOWN, &hdev->state); + + if (IS_ENABLED(CONFIG_PCI_IOV)) + hclge_disable_sriov(hdev); + + if (hdev->service_timer.data) + del_timer_sync(&hdev->service_timer); + if (hdev->service_task.func) + cancel_work_sync(&hdev->service_task); + + if (mac->phydev) + mdiobus_unregister(mac->mdio_bus); + + hclge_destroy_cmd_queue(&hdev->hw); + hclge_pci_uninit(hdev); + ae_dev->priv = NULL; +} + +static const struct hnae3_ae_ops hclge_ops = { + .init_ae_dev = hclge_init_ae_dev, + .uninit_ae_dev = hclge_uninit_ae_dev, + .init_client_instance = hclge_init_client_instance, + .uninit_client_instance = hclge_uninit_client_instance, + .map_ring_to_vector = hclge_map_handle_ring_to_vector, + .unmap_ring_from_vector = hclge_unmap_ring_from_vector, + .get_vector = hclge_get_vector, + .set_promisc_mode = hclge_set_promisc_mode, + .start = hclge_ae_start, + .stop = hclge_ae_stop, + .get_status = hclge_get_status, + .get_ksettings_an_result = hclge_get_ksettings_an_result, + .update_speed_duplex_h = hclge_update_speed_duplex_h, + .cfg_mac_speed_dup_h = hclge_cfg_mac_speed_dup_h, + .get_media_type = hclge_get_media_type, + .get_rss_key_size = hclge_get_rss_key_size, + .get_rss_indir_size = hclge_get_rss_indir_size, + .get_rss = hclge_get_rss, + .set_rss = hclge_set_rss, + .get_tc_size = hclge_get_tc_size, + .get_mac_addr = hclge_get_mac_addr, + .set_mac_addr = hclge_set_mac_addr, + .add_uc_addr = hclge_add_uc_addr, + .rm_uc_addr = hclge_rm_uc_addr, + .add_mc_addr = hclge_add_mc_addr, + .rm_mc_addr = hclge_rm_mc_addr, + .set_autoneg = hclge_set_autoneg, + .get_autoneg = hclge_get_autoneg, + .get_pauseparam = hclge_get_pauseparam, + .set_mtu = hclge_set_mtu, + .reset_queue = hclge_reset_tqp, + .get_stats = hclge_get_stats, + .update_stats = hclge_update_stats, + .get_strings = hclge_get_strings, + .get_sset_count = hclge_get_sset_count, + .get_fw_version = hclge_get_fw_version, + .get_mdix_mode = hclge_get_mdix_mode, + .set_vlan_filter = hclge_set_port_vlan_filter, + .set_vf_vlan_filter = hclge_set_vf_vlan_filter, +}; + +static struct hnae3_ae_algo ae_algo = { + .ops = &hclge_ops, + .name = HCLGE_NAME, + .pdev_id_table = ae_algo_pci_tbl, +}; + +static int hclge_init(void) +{ + pr_info("%s is initializing\n", HCLGE_NAME); + + return hnae3_register_ae_algo(&ae_algo); +} + +static void hclge_exit(void) +{ + hnae3_unregister_ae_algo(&ae_algo); +} +module_init(hclge_init); +module_exit(hclge_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); +MODULE_DESCRIPTION("HCLGE Driver"); +MODULE_VERSION(HCLGE_MOD_VERSION); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h new file mode 100644 index 000000000000..edb10ad075eb --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -0,0 +1,519 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HCLGE_MAIN_H +#define __HCLGE_MAIN_H +#include <linux/fs.h> +#include <linux/types.h> +#include <linux/phy.h> +#include "hclge_cmd.h" +#include "hnae3.h" + +#define HCLGE_MOD_VERSION "v1.0" +#define HCLGE_DRIVER_NAME "hclge" + +#define HCLGE_INVALID_VPORT 0xffff + +#define HCLGE_ROCE_VECTOR_OFFSET 96 + +#define HCLGE_PF_CFG_BLOCK_SIZE 32 +#define HCLGE_PF_CFG_DESC_NUM \ + (HCLGE_PF_CFG_BLOCK_SIZE / HCLGE_CFG_RD_LEN_BYTES) + +#define HCLGE_VECTOR_REG_BASE 0x20000 + +#define HCLGE_VECTOR_REG_OFFSET 0x4 +#define HCLGE_VECTOR_VF_OFFSET 0x100000 + +#define HCLGE_RSS_IND_TBL_SIZE 512 +#define HCLGE_RSS_SET_BITMAP_MSK 0xffff +#define HCLGE_RSS_KEY_SIZE 40 +#define HCLGE_RSS_HASH_ALGO_TOEPLITZ 0 +#define HCLGE_RSS_HASH_ALGO_SIMPLE 1 +#define HCLGE_RSS_HASH_ALGO_SYMMETRIC 2 +#define HCLGE_RSS_HASH_ALGO_MASK 0xf +#define HCLGE_RSS_CFG_TBL_NUM \ + (HCLGE_RSS_IND_TBL_SIZE / HCLGE_RSS_CFG_TBL_SIZE) + +#define HCLGE_RSS_TC_SIZE_0 1 +#define HCLGE_RSS_TC_SIZE_1 2 +#define HCLGE_RSS_TC_SIZE_2 4 +#define HCLGE_RSS_TC_SIZE_3 8 +#define HCLGE_RSS_TC_SIZE_4 16 +#define HCLGE_RSS_TC_SIZE_5 32 +#define HCLGE_RSS_TC_SIZE_6 64 +#define HCLGE_RSS_TC_SIZE_7 128 + +#define HCLGE_TQP_RESET_TRY_TIMES 10 + +#define HCLGE_PHY_PAGE_MDIX 0 +#define HCLGE_PHY_PAGE_COPPER 0 + +/* Page Selection Reg. */ +#define HCLGE_PHY_PAGE_REG 22 + +/* Copper Specific Control Register */ +#define HCLGE_PHY_CSC_REG 16 + +/* Copper Specific Status Register */ +#define HCLGE_PHY_CSS_REG 17 + +#define HCLGE_PHY_MDIX_CTRL_S (5) +#define HCLGE_PHY_MDIX_CTRL_M (3 << HCLGE_PHY_MDIX_CTRL_S) + +#define HCLGE_PHY_MDIX_STATUS_B (6) +#define HCLGE_PHY_SPEED_DUP_RESOLVE_B (11) + +enum HCLGE_DEV_STATE { + HCLGE_STATE_REINITING, + HCLGE_STATE_DOWN, + HCLGE_STATE_DISABLED, + HCLGE_STATE_REMOVING, + HCLGE_STATE_SERVICE_INITED, + HCLGE_STATE_SERVICE_SCHED, + HCLGE_STATE_MBX_HANDLING, + HCLGE_STATE_MBX_IRQ, + HCLGE_STATE_MAX +}; + +#define HCLGE_MPF_ENBALE 1 +struct hclge_caps { + u16 num_tqp; + u16 num_buffer_cell; + u32 flag; + u16 vmdq; +}; + +enum HCLGE_MAC_SPEED { + HCLGE_MAC_SPEED_10M = 10, /* 10 Mbps */ + HCLGE_MAC_SPEED_100M = 100, /* 100 Mbps */ + HCLGE_MAC_SPEED_1G = 1000, /* 1000 Mbps = 1 Gbps */ + HCLGE_MAC_SPEED_10G = 10000, /* 10000 Mbps = 10 Gbps */ + HCLGE_MAC_SPEED_25G = 25000, /* 25000 Mbps = 25 Gbps */ + HCLGE_MAC_SPEED_40G = 40000, /* 40000 Mbps = 40 Gbps */ + HCLGE_MAC_SPEED_50G = 50000, /* 50000 Mbps = 50 Gbps */ + HCLGE_MAC_SPEED_100G = 100000 /* 100000 Mbps = 100 Gbps */ +}; + +enum HCLGE_MAC_DUPLEX { + HCLGE_MAC_HALF, + HCLGE_MAC_FULL +}; + +enum hclge_mta_dmac_sel_type { + HCLGE_MAC_ADDR_47_36, + HCLGE_MAC_ADDR_46_35, + HCLGE_MAC_ADDR_45_34, + HCLGE_MAC_ADDR_44_33, +}; + +struct hclge_mac { + u8 phy_addr; + u8 flag; + u8 media_type; + u8 mac_addr[ETH_ALEN]; + u8 autoneg; + u8 duplex; + u32 speed; + int link; /* store the link status of mac & phy (if phy exit)*/ + struct phy_device *phydev; + struct mii_bus *mdio_bus; + phy_interface_t phy_if; +}; + +struct hclge_hw { + void __iomem *io_base; + struct hclge_mac mac; + int num_vec; + struct hclge_cmq cmq; + struct hclge_caps caps; + void *back; +}; + +/* TQP stats */ +struct hlcge_tqp_stats { + /* query_tqp_tx_queue_statistics ,opcode id: 0x0B03 */ + u64 rcb_tx_ring_pktnum_rcd; /* 32bit */ + /* query_tqp_rx_queue_statistics ,opcode id: 0x0B13 */ + u64 rcb_rx_ring_pktnum_rcd; /* 32bit */ +}; + +struct hclge_tqp { + struct device *dev; /* Device for DMA mapping */ + struct hnae3_queue q; + struct hlcge_tqp_stats tqp_stats; + u16 index; /* Global index in a NIC controller */ + + bool alloced; +}; + +enum hclge_fc_mode { + HCLGE_FC_NONE, + HCLGE_FC_RX_PAUSE, + HCLGE_FC_TX_PAUSE, + HCLGE_FC_FULL, + HCLGE_FC_PFC, + HCLGE_FC_DEFAULT +}; + +#define HCLGE_PG_NUM 4 +#define HCLGE_SCH_MODE_SP 0 +#define HCLGE_SCH_MODE_DWRR 1 +struct hclge_pg_info { + u8 pg_id; + u8 pg_sch_mode; /* 0: sp; 1: dwrr */ + u8 tc_bit_map; + u32 bw_limit; + u8 tc_dwrr[HNAE3_MAX_TC]; +}; + +struct hclge_tc_info { + u8 tc_id; + u8 tc_sch_mode; /* 0: sp; 1: dwrr */ + u8 up; + u8 pgid; + u32 bw_limit; +}; + +struct hclge_cfg { + u8 vmdq_vport_num; + u8 tc_num; + u16 tqp_desc_num; + u16 rx_buf_len; + u8 phy_addr; + u8 media_type; + u8 mac_addr[ETH_ALEN]; + u8 default_speed; + u32 numa_node_map; +}; + +struct hclge_tm_info { + u8 num_tc; + u8 num_pg; /* It must be 1 if vNET-Base schd */ + u8 pg_dwrr[HCLGE_PG_NUM]; + struct hclge_pg_info pg_info[HCLGE_PG_NUM]; + struct hclge_tc_info tc_info[HNAE3_MAX_TC]; + enum hclge_fc_mode fc_mode; + u8 hw_pfc_map; /* Allow for packet drop or not on this TC */ +}; + +struct hclge_comm_stats_str { + char desc[ETH_GSTRING_LEN]; + unsigned long offset; +}; + +/* all 64bit stats, opcode id: 0x0030 */ +struct hclge_64_bit_stats { + /* query_igu_stat */ + u64 igu_rx_oversize_pkt; + u64 igu_rx_undersize_pkt; + u64 igu_rx_out_all_pkt; + u64 igu_rx_uni_pkt; + u64 igu_rx_multi_pkt; + u64 igu_rx_broad_pkt; + u64 rsv0; + + /* query_egu_stat */ + u64 egu_tx_out_all_pkt; + u64 egu_tx_uni_pkt; + u64 egu_tx_multi_pkt; + u64 egu_tx_broad_pkt; + + /* ssu_ppp packet stats */ + u64 ssu_ppp_mac_key_num; + u64 ssu_ppp_host_key_num; + u64 ppp_ssu_mac_rlt_num; + u64 ppp_ssu_host_rlt_num; + + /* ssu_tx_in_out_dfx_stats */ + u64 ssu_tx_in_num; + u64 ssu_tx_out_num; + /* ssu_rx_in_out_dfx_stats */ + u64 ssu_rx_in_num; + u64 ssu_rx_out_num; +}; + +/* all 32bit stats, opcode id: 0x0031 */ +struct hclge_32_bit_stats { + u64 igu_rx_err_pkt; + u64 igu_rx_no_eof_pkt; + u64 igu_rx_no_sof_pkt; + u64 egu_tx_1588_pkt; + u64 egu_tx_err_pkt; + u64 ssu_full_drop_num; + u64 ssu_part_drop_num; + u64 ppp_key_drop_num; + u64 ppp_rlt_drop_num; + u64 ssu_key_drop_num; + u64 pkt_curr_buf_cnt; + u64 qcn_fb_rcv_cnt; + u64 qcn_fb_drop_cnt; + u64 qcn_fb_invaild_cnt; + u64 rsv0; + u64 rx_packet_tc0_in_cnt; + u64 rx_packet_tc1_in_cnt; + u64 rx_packet_tc2_in_cnt; + u64 rx_packet_tc3_in_cnt; + u64 rx_packet_tc4_in_cnt; + u64 rx_packet_tc5_in_cnt; + u64 rx_packet_tc6_in_cnt; + u64 rx_packet_tc7_in_cnt; + u64 rx_packet_tc0_out_cnt; + u64 rx_packet_tc1_out_cnt; + u64 rx_packet_tc2_out_cnt; + u64 rx_packet_tc3_out_cnt; + u64 rx_packet_tc4_out_cnt; + u64 rx_packet_tc5_out_cnt; + u64 rx_packet_tc6_out_cnt; + u64 rx_packet_tc7_out_cnt; + + /* Tx packet level statistics */ + u64 tx_packet_tc0_in_cnt; + u64 tx_packet_tc1_in_cnt; + u64 tx_packet_tc2_in_cnt; + u64 tx_packet_tc3_in_cnt; + u64 tx_packet_tc4_in_cnt; + u64 tx_packet_tc5_in_cnt; + u64 tx_packet_tc6_in_cnt; + u64 tx_packet_tc7_in_cnt; + u64 tx_packet_tc0_out_cnt; + u64 tx_packet_tc1_out_cnt; + u64 tx_packet_tc2_out_cnt; + u64 tx_packet_tc3_out_cnt; + u64 tx_packet_tc4_out_cnt; + u64 tx_packet_tc5_out_cnt; + u64 tx_packet_tc6_out_cnt; + u64 tx_packet_tc7_out_cnt; + + /* packet buffer statistics */ + u64 pkt_curr_buf_tc0_cnt; + u64 pkt_curr_buf_tc1_cnt; + u64 pkt_curr_buf_tc2_cnt; + u64 pkt_curr_buf_tc3_cnt; + u64 pkt_curr_buf_tc4_cnt; + u64 pkt_curr_buf_tc5_cnt; + u64 pkt_curr_buf_tc6_cnt; + u64 pkt_curr_buf_tc7_cnt; + + u64 mb_uncopy_num; + u64 lo_pri_unicast_rlt_drop_num; + u64 hi_pri_multicast_rlt_drop_num; + u64 lo_pri_multicast_rlt_drop_num; + u64 rx_oq_drop_pkt_cnt; + u64 tx_oq_drop_pkt_cnt; + u64 nic_l2_err_drop_pkt_cnt; + u64 roc_l2_err_drop_pkt_cnt; +}; + +/* mac stats ,opcode id: 0x0032 */ +struct hclge_mac_stats { + u64 mac_tx_mac_pause_num; + u64 mac_rx_mac_pause_num; + u64 mac_tx_pfc_pri0_pkt_num; + u64 mac_tx_pfc_pri1_pkt_num; + u64 mac_tx_pfc_pri2_pkt_num; + u64 mac_tx_pfc_pri3_pkt_num; + u64 mac_tx_pfc_pri4_pkt_num; + u64 mac_tx_pfc_pri5_pkt_num; + u64 mac_tx_pfc_pri6_pkt_num; + u64 mac_tx_pfc_pri7_pkt_num; + u64 mac_rx_pfc_pri0_pkt_num; + u64 mac_rx_pfc_pri1_pkt_num; + u64 mac_rx_pfc_pri2_pkt_num; + u64 mac_rx_pfc_pri3_pkt_num; + u64 mac_rx_pfc_pri4_pkt_num; + u64 mac_rx_pfc_pri5_pkt_num; + u64 mac_rx_pfc_pri6_pkt_num; + u64 mac_rx_pfc_pri7_pkt_num; + u64 mac_tx_total_pkt_num; + u64 mac_tx_total_oct_num; + u64 mac_tx_good_pkt_num; + u64 mac_tx_bad_pkt_num; + u64 mac_tx_good_oct_num; + u64 mac_tx_bad_oct_num; + u64 mac_tx_uni_pkt_num; + u64 mac_tx_multi_pkt_num; + u64 mac_tx_broad_pkt_num; + u64 mac_tx_undersize_pkt_num; + u64 mac_tx_overrsize_pkt_num; + u64 mac_tx_64_oct_pkt_num; + u64 mac_tx_65_127_oct_pkt_num; + u64 mac_tx_128_255_oct_pkt_num; + u64 mac_tx_256_511_oct_pkt_num; + u64 mac_tx_512_1023_oct_pkt_num; + u64 mac_tx_1024_1518_oct_pkt_num; + u64 mac_tx_1519_max_oct_pkt_num; + u64 mac_rx_total_pkt_num; + u64 mac_rx_total_oct_num; + u64 mac_rx_good_pkt_num; + u64 mac_rx_bad_pkt_num; + u64 mac_rx_good_oct_num; + u64 mac_rx_bad_oct_num; + u64 mac_rx_uni_pkt_num; + u64 mac_rx_multi_pkt_num; + u64 mac_rx_broad_pkt_num; + u64 mac_rx_undersize_pkt_num; + u64 mac_rx_overrsize_pkt_num; + u64 mac_rx_64_oct_pkt_num; + u64 mac_rx_65_127_oct_pkt_num; + u64 mac_rx_128_255_oct_pkt_num; + u64 mac_rx_256_511_oct_pkt_num; + u64 mac_rx_512_1023_oct_pkt_num; + u64 mac_rx_1024_1518_oct_pkt_num; + u64 mac_rx_1519_max_oct_pkt_num; + + u64 mac_trans_fragment_pkt_num; + u64 mac_trans_undermin_pkt_num; + u64 mac_trans_jabber_pkt_num; + u64 mac_trans_err_all_pkt_num; + u64 mac_trans_from_app_good_pkt_num; + u64 mac_trans_from_app_bad_pkt_num; + u64 mac_rcv_fragment_pkt_num; + u64 mac_rcv_undermin_pkt_num; + u64 mac_rcv_jabber_pkt_num; + u64 mac_rcv_fcs_err_pkt_num; + u64 mac_rcv_send_app_good_pkt_num; + u64 mac_rcv_send_app_bad_pkt_num; +}; + +struct hclge_hw_stats { + struct hclge_mac_stats mac_stats; + struct hclge_64_bit_stats all_64_bit_stats; + struct hclge_32_bit_stats all_32_bit_stats; +}; + +struct hclge_dev { + struct pci_dev *pdev; + struct hnae3_ae_dev *ae_dev; + struct hclge_hw hw; + struct hclge_hw_stats hw_stats; + unsigned long state; + + u32 fw_version; + u16 num_vmdq_vport; /* Num vmdq vport this PF has set up */ + u16 num_tqps; /* Num task queue pairs of this PF */ + u16 num_req_vfs; /* Num VFs requested for this PF */ + + u16 num_roce_msix; /* Num of roce vectors for this PF */ + int roce_base_vector; + + /* Base task tqp physical id of this PF */ + u16 base_tqp_pid; + u16 alloc_rss_size; /* Allocated RSS task queue */ + u16 rss_size_max; /* HW defined max RSS task queue */ + + /* Num of guaranteed filters for this PF */ + u16 fdir_pf_filter_count; + u16 num_alloc_vport; /* Num vports this driver supports */ + u32 numa_node_mask; + u16 rx_buf_len; + u16 num_desc; + u8 hw_tc_map; + u8 tc_num_last_time; + enum hclge_fc_mode fc_mode_last_time; + +#define HCLGE_FLAG_TC_BASE_SCH_MODE 1 +#define HCLGE_FLAG_VNET_BASE_SCH_MODE 2 + u8 tx_sch_mode; + + u8 default_up; + struct hclge_tm_info tm_info; + + u16 num_msi; + u16 num_msi_left; + u16 num_msi_used; + u32 base_msi_vector; + struct msix_entry *msix_entries; + u16 *vector_status; + + u16 pending_udp_bitmap; + + u16 rx_itr_default; + u16 tx_itr_default; + + u16 adminq_work_limit; /* Num of admin receive queue desc to process */ + unsigned long service_timer_period; + unsigned long service_timer_previous; + struct timer_list service_timer; + struct work_struct service_task; + + bool cur_promisc; + int num_alloc_vfs; /* Actual number of VFs allocated */ + + struct hclge_tqp *htqp; + struct hclge_vport *vport; + + struct dentry *hclge_dbgfs; + + struct hnae3_client *nic_client; + struct hnae3_client *roce_client; + +#define HCLGE_FLAG_USE_MSI 0x00000001 +#define HCLGE_FLAG_USE_MSIX 0x00000002 +#define HCLGE_FLAG_MAIN 0x00000004 +#define HCLGE_FLAG_DCB_CAPABLE 0x00000008 +#define HCLGE_FLAG_DCB_ENABLE 0x00000010 + u32 flag; + + u32 pkt_buf_size; /* Total pf buf size for tx/rx */ + u32 mps; /* Max packet size */ + struct hclge_priv_buf *priv_buf; + struct hclge_shared_buf s_buf; + + enum hclge_mta_dmac_sel_type mta_mac_sel_type; + bool enable_mta; /* Mutilcast filter enable */ + bool accept_mta_mc; /* Whether accept mta filter multicast */ +}; + +struct hclge_vport { + u16 alloc_tqps; /* Allocated Tx/Rx queues */ + + u8 rss_hash_key[HCLGE_RSS_KEY_SIZE]; /* User configured hash keys */ + /* User configured lookup table entries */ + u8 rss_indirection_tbl[HCLGE_RSS_IND_TBL_SIZE]; + + u16 qs_offset; + u16 bw_limit; /* VSI BW Limit (0 = disabled) */ + u8 dwrr; + + int vport_id; + struct hclge_dev *back; /* Back reference to associated dev */ + struct hnae3_handle nic; + struct hnae3_handle roce; +}; + +void hclge_promisc_param_init(struct hclge_promisc_param *param, bool en_uc, + bool en_mc, bool en_bc, int vport_id); + +int hclge_add_uc_addr_common(struct hclge_vport *vport, + const unsigned char *addr); +int hclge_rm_uc_addr_common(struct hclge_vport *vport, + const unsigned char *addr); +int hclge_add_mc_addr_common(struct hclge_vport *vport, + const unsigned char *addr); +int hclge_rm_mc_addr_common(struct hclge_vport *vport, + const unsigned char *addr); + +int hclge_cfg_func_mta_filter(struct hclge_dev *hdev, + u8 func_id, + bool enable); +struct hclge_vport *hclge_get_vport(struct hnae3_handle *handle); +int hclge_map_vport_ring_to_vector(struct hclge_vport *vport, int vector, + struct hnae3_ring_chain_node *ring_chain); +static inline int hclge_get_queue_id(struct hnae3_queue *queue) +{ + struct hclge_tqp *tqp = container_of(queue, struct hclge_tqp, q); + + return tqp->index; +} + +int hclge_cfg_mac_speed_dup(struct hclge_dev *hdev, int speed, u8 duplex); +int hclge_set_vf_vlan_common(struct hclge_dev *vport, int vfid, + bool is_kill, u16 vlan, u8 qos, __be16 proto); +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c new file mode 100644 index 000000000000..f32d719c4f77 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/etherdevice.h> +#include <linux/kernel.h> + +#include "hclge_cmd.h" +#include "hclge_main.h" +#include "hclge_mdio.h" + +enum hclge_mdio_c22_op_seq { + HCLGE_MDIO_C22_WRITE = 1, + HCLGE_MDIO_C22_READ = 2 +}; + +#define HCLGE_MDIO_CTRL_START_B 0 +#define HCLGE_MDIO_CTRL_ST_S 1 +#define HCLGE_MDIO_CTRL_ST_M (0x3 << HCLGE_MDIO_CTRL_ST_S) +#define HCLGE_MDIO_CTRL_OP_S 3 +#define HCLGE_MDIO_CTRL_OP_M (0x3 << HCLGE_MDIO_CTRL_OP_S) + +#define HCLGE_MDIO_PHYID_S 0 +#define HCLGE_MDIO_PHYID_M (0x1f << HCLGE_MDIO_PHYID_S) + +#define HCLGE_MDIO_PHYREG_S 0 +#define HCLGE_MDIO_PHYREG_M (0x1f << HCLGE_MDIO_PHYREG_S) + +#define HCLGE_MDIO_STA_B 0 + +struct hclge_mdio_cfg_cmd { + u8 ctrl_bit; + u8 phyid; + u8 phyad; + u8 rsvd; + __le16 reserve; + __le16 data_wr; + __le16 data_rd; + __le16 sta; +}; + +static int hclge_mdio_write(struct mii_bus *bus, int phyid, int regnum, + u16 data) +{ + struct hclge_mdio_cfg_cmd *mdio_cmd; + struct hclge_dev *hdev = bus->priv; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MDIO_CONFIG, false); + + mdio_cmd = (struct hclge_mdio_cfg_cmd *)desc.data; + + hnae_set_field(mdio_cmd->phyid, HCLGE_MDIO_PHYID_M, + HCLGE_MDIO_PHYID_S, phyid); + hnae_set_field(mdio_cmd->phyad, HCLGE_MDIO_PHYREG_M, + HCLGE_MDIO_PHYREG_S, regnum); + + hnae_set_bit(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_START_B, 1); + hnae_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_ST_M, + HCLGE_MDIO_CTRL_ST_S, 1); + hnae_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_OP_M, + HCLGE_MDIO_CTRL_OP_S, HCLGE_MDIO_C22_WRITE); + + mdio_cmd->data_wr = cpu_to_le16(data); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "mdio write fail when sending cmd, status is %d.\n", + ret); + return ret; + } + + return 0; +} + +static int hclge_mdio_read(struct mii_bus *bus, int phyid, int regnum) +{ + struct hclge_mdio_cfg_cmd *mdio_cmd; + struct hclge_dev *hdev = bus->priv; + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_MDIO_CONFIG, true); + + mdio_cmd = (struct hclge_mdio_cfg_cmd *)desc.data; + + hnae_set_field(mdio_cmd->phyid, HCLGE_MDIO_PHYID_M, + HCLGE_MDIO_PHYID_S, phyid); + hnae_set_field(mdio_cmd->phyad, HCLGE_MDIO_PHYREG_M, + HCLGE_MDIO_PHYREG_S, regnum); + + hnae_set_bit(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_START_B, 1); + hnae_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_ST_M, + HCLGE_MDIO_CTRL_ST_S, 1); + hnae_set_field(mdio_cmd->ctrl_bit, HCLGE_MDIO_CTRL_OP_M, + HCLGE_MDIO_CTRL_OP_S, HCLGE_MDIO_C22_READ); + + /* Read out phy data */ + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + if (ret) { + dev_err(&hdev->pdev->dev, + "mdio read fail when get data, status is %d.\n", + ret); + return ret; + } + + if (hnae_get_bit(le16_to_cpu(mdio_cmd->sta), HCLGE_MDIO_STA_B)) { + dev_err(&hdev->pdev->dev, "mdio read data error\n"); + return -EIO; + } + + return le16_to_cpu(mdio_cmd->data_rd); +} + +int hclge_mac_mdio_config(struct hclge_dev *hdev) +{ + struct hclge_mac *mac = &hdev->hw.mac; + struct phy_device *phydev; + struct mii_bus *mdio_bus; + int ret; + + if (hdev->hw.mac.phy_addr >= PHY_MAX_ADDR) + return 0; + + mdio_bus = devm_mdiobus_alloc(&hdev->pdev->dev); + if (!mdio_bus) + return -ENOMEM; + + mdio_bus->name = "hisilicon MII bus"; + mdio_bus->read = hclge_mdio_read; + mdio_bus->write = hclge_mdio_write; + snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s-%s", "mii", + dev_name(&hdev->pdev->dev)); + + mdio_bus->parent = &hdev->pdev->dev; + mdio_bus->priv = hdev; + mdio_bus->phy_mask = ~(1 << mac->phy_addr); + ret = mdiobus_register(mdio_bus); + if (ret) { + dev_err(mdio_bus->parent, + "Failed to register MDIO bus ret = %#x\n", ret); + return ret; + } + + phydev = mdiobus_get_phy(mdio_bus, mac->phy_addr); + if (!phydev) { + dev_err(mdio_bus->parent, "Failed to get phy device\n"); + mdiobus_unregister(mdio_bus); + return -EIO; + } + + mac->phydev = phydev; + mac->mdio_bus = mdio_bus; + + return 0; +} + +static void hclge_mac_adjust_link(struct net_device *netdev) +{ + struct hnae3_handle *h = *((void **)netdev_priv(netdev)); + struct hclge_vport *vport = hclge_get_vport(h); + struct hclge_dev *hdev = vport->back; + int duplex, speed; + int ret; + + speed = netdev->phydev->speed; + duplex = netdev->phydev->duplex; + + ret = hclge_cfg_mac_speed_dup(hdev, speed, duplex); + if (ret) + netdev_err(netdev, "failed to adjust link.\n"); +} + +int hclge_mac_start_phy(struct hclge_dev *hdev) +{ + struct net_device *netdev = hdev->vport[0].nic.netdev; + struct phy_device *phydev = hdev->hw.mac.phydev; + int ret; + + if (!phydev) + return 0; + + ret = phy_connect_direct(netdev, phydev, + hclge_mac_adjust_link, + PHY_INTERFACE_MODE_SGMII); + if (ret) { + netdev_err(netdev, "phy_connect_direct err.\n"); + return ret; + } + + phy_start(phydev); + + return 0; +} + +void hclge_mac_stop_phy(struct hclge_dev *hdev) +{ + struct net_device *netdev = hdev->vport[0].nic.netdev; + struct phy_device *phydev = netdev->phydev; + + if (!phydev) + return; + + phy_stop(phydev); + phy_disconnect(phydev); +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h new file mode 100644 index 000000000000..c5e91cfb8f2c --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_mdio.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2016-2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HCLGE_MDIO_H +#define __HCLGE_MDIO_H + +int hclge_mac_mdio_config(struct hclge_dev *hdev); +int hclge_mac_start_phy(struct hclge_dev *hdev); +void hclge_mac_stop_phy(struct hclge_dev *hdev); + +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c new file mode 100644 index 000000000000..1c577d268f00 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.c @@ -0,0 +1,1015 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/etherdevice.h> + +#include "hclge_cmd.h" +#include "hclge_main.h" +#include "hclge_tm.h" + +enum hclge_shaper_level { + HCLGE_SHAPER_LVL_PRI = 0, + HCLGE_SHAPER_LVL_PG = 1, + HCLGE_SHAPER_LVL_PORT = 2, + HCLGE_SHAPER_LVL_QSET = 3, + HCLGE_SHAPER_LVL_CNT = 4, + HCLGE_SHAPER_LVL_VF = 0, + HCLGE_SHAPER_LVL_PF = 1, +}; + +#define HCLGE_SHAPER_BS_U_DEF 1 +#define HCLGE_SHAPER_BS_S_DEF 4 + +#define HCLGE_ETHER_MAX_RATE 100000 + +/* hclge_shaper_para_calc: calculate ir parameter for the shaper + * @ir: Rate to be config, its unit is Mbps + * @shaper_level: the shaper level. eg: port, pg, priority, queueset + * @ir_b: IR_B parameter of IR shaper + * @ir_u: IR_U parameter of IR shaper + * @ir_s: IR_S parameter of IR shaper + * + * the formula: + * + * IR_b * (2 ^ IR_u) * 8 + * IR(Mbps) = ------------------------- * CLOCK(1000Mbps) + * Tick * (2 ^ IR_s) + * + * @return: 0: calculate sucessful, negative: fail + */ +static int hclge_shaper_para_calc(u32 ir, u8 shaper_level, + u8 *ir_b, u8 *ir_u, u8 *ir_s) +{ + const u16 tick_array[HCLGE_SHAPER_LVL_CNT] = { + 6 * 256, /* Prioriy level */ + 6 * 32, /* Prioriy group level */ + 6 * 8, /* Port level */ + 6 * 256 /* Qset level */ + }; + u8 ir_u_calc = 0, ir_s_calc = 0; + u32 ir_calc; + u32 tick; + + /* Calc tick */ + if (shaper_level >= HCLGE_SHAPER_LVL_CNT) + return -EINVAL; + + tick = tick_array[shaper_level]; + + /** + * Calc the speed if ir_b = 126, ir_u = 0 and ir_s = 0 + * the formula is changed to: + * 126 * 1 * 8 + * ir_calc = ---------------- * 1000 + * tick * 1 + */ + ir_calc = (1008000 + (tick >> 1) - 1) / tick; + + if (ir_calc == ir) { + *ir_b = 126; + *ir_u = 0; + *ir_s = 0; + + return 0; + } else if (ir_calc > ir) { + /* Increasing the denominator to select ir_s value */ + while (ir_calc > ir) { + ir_s_calc++; + ir_calc = 1008000 / (tick * (1 << ir_s_calc)); + } + + if (ir_calc == ir) + *ir_b = 126; + else + *ir_b = (ir * tick * (1 << ir_s_calc) + 4000) / 8000; + } else { + /* Increasing the numerator to select ir_u value */ + u32 numerator; + + while (ir_calc < ir) { + ir_u_calc++; + numerator = 1008000 * (1 << ir_u_calc); + ir_calc = (numerator + (tick >> 1)) / tick; + } + + if (ir_calc == ir) { + *ir_b = 126; + } else { + u32 denominator = (8000 * (1 << --ir_u_calc)); + *ir_b = (ir * tick + (denominator >> 1)) / denominator; + } + } + + *ir_u = ir_u_calc; + *ir_s = ir_s_calc; + + return 0; +} + +static int hclge_mac_pause_en_cfg(struct hclge_dev *hdev, bool tx, bool rx) +{ + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CFG_MAC_PAUSE_EN, false); + + desc.data[0] = cpu_to_le32((tx ? HCLGE_TX_MAC_PAUSE_EN_MSK : 0) | + (rx ? HCLGE_RX_MAC_PAUSE_EN_MSK : 0)); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_fill_pri_array(struct hclge_dev *hdev, u8 *pri, u8 pri_id) +{ + u8 tc; + + for (tc = 0; tc < hdev->tm_info.num_tc; tc++) + if (hdev->tm_info.tc_info[tc].up == pri_id) + break; + + if (tc >= hdev->tm_info.num_tc) + return -EINVAL; + + /** + * the register for priority has four bytes, the first bytes includes + * priority0 and priority1, the higher 4bit stands for priority1 + * while the lower 4bit stands for priority0, as below: + * first byte: | pri_1 | pri_0 | + * second byte: | pri_3 | pri_2 | + * third byte: | pri_5 | pri_4 | + * fourth byte: | pri_7 | pri_6 | + */ + pri[pri_id >> 1] |= tc << ((pri_id & 1) * 4); + + return 0; +} + +static int hclge_up_to_tc_map(struct hclge_dev *hdev) +{ + struct hclge_desc desc; + u8 *pri = (u8 *)desc.data; + u8 pri_id; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_PRI_TO_TC_MAPPING, false); + + for (pri_id = 0; pri_id < hdev->tm_info.num_tc; pri_id++) { + ret = hclge_fill_pri_array(hdev, pri, pri_id); + if (ret) + return ret; + } + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_pg_to_pri_map_cfg(struct hclge_dev *hdev, + u8 pg_id, u8 pri_bit_map) +{ + struct hclge_pg_to_pri_link_cmd *map; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_PG_TO_PRI_LINK, false); + + map = (struct hclge_pg_to_pri_link_cmd *)desc.data; + + map->pg_id = pg_id; + map->pri_bit_map = pri_bit_map; + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_qs_to_pri_map_cfg(struct hclge_dev *hdev, + u16 qs_id, u8 pri) +{ + struct hclge_qs_to_pri_link_cmd *map; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_QS_TO_PRI_LINK, false); + + map = (struct hclge_qs_to_pri_link_cmd *)desc.data; + + map->qs_id = cpu_to_le16(qs_id); + map->priority = pri; + map->link_vld = HCLGE_TM_QS_PRI_LINK_VLD_MSK; + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_q_to_qs_map_cfg(struct hclge_dev *hdev, + u8 q_id, u16 qs_id) +{ + struct hclge_nq_to_qs_link_cmd *map; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_NQ_TO_QS_LINK, false); + + map = (struct hclge_nq_to_qs_link_cmd *)desc.data; + + map->nq_id = cpu_to_le16(q_id); + map->qset_id = cpu_to_le16(qs_id | HCLGE_TM_Q_QS_LINK_VLD_MSK); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_pg_weight_cfg(struct hclge_dev *hdev, u8 pg_id, + u8 dwrr) +{ + struct hclge_pg_weight_cmd *weight; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_PG_WEIGHT, false); + + weight = (struct hclge_pg_weight_cmd *)desc.data; + + weight->pg_id = pg_id; + weight->dwrr = dwrr; + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_pri_weight_cfg(struct hclge_dev *hdev, u8 pri_id, + u8 dwrr) +{ + struct hclge_priority_weight_cmd *weight; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_PRI_WEIGHT, false); + + weight = (struct hclge_priority_weight_cmd *)desc.data; + + weight->pri_id = pri_id; + weight->dwrr = dwrr; + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_qs_weight_cfg(struct hclge_dev *hdev, u16 qs_id, + u8 dwrr) +{ + struct hclge_qs_weight_cmd *weight; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_QS_WEIGHT, false); + + weight = (struct hclge_qs_weight_cmd *)desc.data; + + weight->qs_id = cpu_to_le16(qs_id); + weight->dwrr = dwrr; + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_pg_shapping_cfg(struct hclge_dev *hdev, + enum hclge_shap_bucket bucket, u8 pg_id, + u8 ir_b, u8 ir_u, u8 ir_s, u8 bs_b, u8 bs_s) +{ + struct hclge_pg_shapping_cmd *shap_cfg_cmd; + enum hclge_opcode_type opcode; + struct hclge_desc desc; + + opcode = bucket ? HCLGE_OPC_TM_PG_P_SHAPPING : + HCLGE_OPC_TM_PG_C_SHAPPING; + hclge_cmd_setup_basic_desc(&desc, opcode, false); + + shap_cfg_cmd = (struct hclge_pg_shapping_cmd *)desc.data; + + shap_cfg_cmd->pg_id = pg_id; + + hclge_tm_set_feild(shap_cfg_cmd->pg_shapping_para, IR_B, ir_b); + hclge_tm_set_feild(shap_cfg_cmd->pg_shapping_para, IR_U, ir_u); + hclge_tm_set_feild(shap_cfg_cmd->pg_shapping_para, IR_S, ir_s); + hclge_tm_set_feild(shap_cfg_cmd->pg_shapping_para, BS_B, bs_b); + hclge_tm_set_feild(shap_cfg_cmd->pg_shapping_para, BS_S, bs_s); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_pri_shapping_cfg(struct hclge_dev *hdev, + enum hclge_shap_bucket bucket, u8 pri_id, + u8 ir_b, u8 ir_u, u8 ir_s, + u8 bs_b, u8 bs_s) +{ + struct hclge_pri_shapping_cmd *shap_cfg_cmd; + enum hclge_opcode_type opcode; + struct hclge_desc desc; + + opcode = bucket ? HCLGE_OPC_TM_PRI_P_SHAPPING : + HCLGE_OPC_TM_PRI_C_SHAPPING; + + hclge_cmd_setup_basic_desc(&desc, opcode, false); + + shap_cfg_cmd = (struct hclge_pri_shapping_cmd *)desc.data; + + shap_cfg_cmd->pri_id = pri_id; + + hclge_tm_set_feild(shap_cfg_cmd->pri_shapping_para, IR_B, ir_b); + hclge_tm_set_feild(shap_cfg_cmd->pri_shapping_para, IR_U, ir_u); + hclge_tm_set_feild(shap_cfg_cmd->pri_shapping_para, IR_S, ir_s); + hclge_tm_set_feild(shap_cfg_cmd->pri_shapping_para, BS_B, bs_b); + hclge_tm_set_feild(shap_cfg_cmd->pri_shapping_para, BS_S, bs_s); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_pg_schd_mode_cfg(struct hclge_dev *hdev, u8 pg_id) +{ + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_PG_SCH_MODE_CFG, false); + + if (hdev->tm_info.pg_info[pg_id].pg_sch_mode == HCLGE_SCH_MODE_DWRR) + desc.data[1] = cpu_to_le32(HCLGE_TM_TX_SCHD_DWRR_MSK); + else + desc.data[1] = 0; + + desc.data[0] = cpu_to_le32(pg_id); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_pri_schd_mode_cfg(struct hclge_dev *hdev, u8 pri_id) +{ + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_PRI_SCH_MODE_CFG, false); + + if (hdev->tm_info.tc_info[pri_id].tc_sch_mode == HCLGE_SCH_MODE_DWRR) + desc.data[1] = cpu_to_le32(HCLGE_TM_TX_SCHD_DWRR_MSK); + else + desc.data[1] = 0; + + desc.data[0] = cpu_to_le32(pri_id); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_qs_schd_mode_cfg(struct hclge_dev *hdev, u16 qs_id) +{ + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_QS_SCH_MODE_CFG, false); + + if (hdev->tm_info.tc_info[qs_id].tc_sch_mode == HCLGE_SCH_MODE_DWRR) + desc.data[1] = cpu_to_le32(HCLGE_TM_TX_SCHD_DWRR_MSK); + else + desc.data[1] = 0; + + desc.data[0] = cpu_to_le32(qs_id); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static int hclge_tm_qs_bp_cfg(struct hclge_dev *hdev, u8 tc) +{ + struct hclge_bp_to_qs_map_cmd *bp_to_qs_map_cmd; + struct hclge_desc desc; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_TM_BP_TO_QSET_MAPPING, + false); + + bp_to_qs_map_cmd = (struct hclge_bp_to_qs_map_cmd *)desc.data; + + bp_to_qs_map_cmd->tc_id = tc; + + /* Qset and tc is one by one mapping */ + bp_to_qs_map_cmd->qs_bit_map = cpu_to_le32(1 << tc); + + return hclge_cmd_send(&hdev->hw, &desc, 1); +} + +static void hclge_tm_vport_tc_info_update(struct hclge_vport *vport) +{ + struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo; + struct hclge_dev *hdev = vport->back; + u8 i; + + kinfo = &vport->nic.kinfo; + vport->bw_limit = hdev->tm_info.pg_info[0].bw_limit; + kinfo->num_tc = + min_t(u16, kinfo->num_tqps, hdev->tm_info.num_tc); + kinfo->rss_size + = min_t(u16, hdev->rss_size_max, + kinfo->num_tqps / kinfo->num_tc); + vport->qs_offset = hdev->tm_info.num_tc * vport->vport_id; + vport->dwrr = 100; /* 100 percent as init */ + + for (i = 0; i < kinfo->num_tc; i++) { + if (hdev->hw_tc_map & BIT(i)) { + kinfo->tc_info[i].enable = true; + kinfo->tc_info[i].tqp_offset = i * kinfo->rss_size; + kinfo->tc_info[i].tqp_count = kinfo->rss_size; + kinfo->tc_info[i].tc = i; + kinfo->tc_info[i].up = hdev->tm_info.tc_info[i].up; + } else { + /* Set to default queue if TC is disable */ + kinfo->tc_info[i].enable = false; + kinfo->tc_info[i].tqp_offset = 0; + kinfo->tc_info[i].tqp_count = 1; + kinfo->tc_info[i].tc = 0; + kinfo->tc_info[i].up = 0; + } + } +} + +static void hclge_tm_vport_info_update(struct hclge_dev *hdev) +{ + struct hclge_vport *vport = hdev->vport; + u32 i; + + for (i = 0; i < hdev->num_alloc_vport; i++) { + hclge_tm_vport_tc_info_update(vport); + + vport++; + } +} + +static void hclge_tm_tc_info_init(struct hclge_dev *hdev) +{ + u8 i; + + for (i = 0; i < hdev->tm_info.num_tc; i++) { + hdev->tm_info.tc_info[i].tc_id = i; + hdev->tm_info.tc_info[i].tc_sch_mode = HCLGE_SCH_MODE_DWRR; + hdev->tm_info.tc_info[i].up = i; + hdev->tm_info.tc_info[i].pgid = 0; + hdev->tm_info.tc_info[i].bw_limit = + hdev->tm_info.pg_info[0].bw_limit; + } + + hdev->flag &= ~HCLGE_FLAG_DCB_ENABLE; +} + +static void hclge_tm_pg_info_init(struct hclge_dev *hdev) +{ + u8 i; + + for (i = 0; i < hdev->tm_info.num_pg; i++) { + int k; + + hdev->tm_info.pg_dwrr[i] = i ? 0 : 100; + + hdev->tm_info.pg_info[i].pg_id = i; + hdev->tm_info.pg_info[i].pg_sch_mode = HCLGE_SCH_MODE_DWRR; + + hdev->tm_info.pg_info[i].bw_limit = HCLGE_ETHER_MAX_RATE; + + if (i != 0) + continue; + + hdev->tm_info.pg_info[i].tc_bit_map = hdev->hw_tc_map; + for (k = 0; k < hdev->tm_info.num_tc; k++) + hdev->tm_info.pg_info[i].tc_dwrr[k] = 100; + } +} + +static int hclge_tm_schd_info_init(struct hclge_dev *hdev) +{ + if ((hdev->tx_sch_mode != HCLGE_FLAG_TC_BASE_SCH_MODE) && + (hdev->tm_info.num_pg != 1)) + return -EINVAL; + + hclge_tm_pg_info_init(hdev); + + hclge_tm_tc_info_init(hdev); + + hclge_tm_vport_info_update(hdev); + + hdev->tm_info.fc_mode = HCLGE_FC_NONE; + hdev->fc_mode_last_time = hdev->tm_info.fc_mode; + + return 0; +} + +static int hclge_tm_pg_to_pri_map(struct hclge_dev *hdev) +{ + int ret; + u32 i; + + if (hdev->tx_sch_mode != HCLGE_FLAG_TC_BASE_SCH_MODE) + return 0; + + for (i = 0; i < hdev->tm_info.num_pg; i++) { + /* Cfg mapping */ + ret = hclge_tm_pg_to_pri_map_cfg( + hdev, i, hdev->tm_info.pg_info[i].tc_bit_map); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_pg_shaper_cfg(struct hclge_dev *hdev) +{ + u8 ir_u, ir_b, ir_s; + int ret; + u32 i; + + /* Cfg pg schd */ + if (hdev->tx_sch_mode != HCLGE_FLAG_TC_BASE_SCH_MODE) + return 0; + + /* Pg to pri */ + for (i = 0; i < hdev->tm_info.num_pg; i++) { + /* Calc shaper para */ + ret = hclge_shaper_para_calc( + hdev->tm_info.pg_info[i].bw_limit, + HCLGE_SHAPER_LVL_PG, + &ir_b, &ir_u, &ir_s); + if (ret) + return ret; + + ret = hclge_tm_pg_shapping_cfg(hdev, + HCLGE_TM_SHAP_C_BUCKET, i, + 0, 0, 0, HCLGE_SHAPER_BS_U_DEF, + HCLGE_SHAPER_BS_S_DEF); + if (ret) + return ret; + + ret = hclge_tm_pg_shapping_cfg(hdev, + HCLGE_TM_SHAP_P_BUCKET, i, + ir_b, ir_u, ir_s, + HCLGE_SHAPER_BS_U_DEF, + HCLGE_SHAPER_BS_S_DEF); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_pg_dwrr_cfg(struct hclge_dev *hdev) +{ + int ret; + u32 i; + + /* cfg pg schd */ + if (hdev->tx_sch_mode != HCLGE_FLAG_TC_BASE_SCH_MODE) + return 0; + + /* pg to prio */ + for (i = 0; i < hdev->tm_info.num_pg; i++) { + /* Cfg dwrr */ + ret = hclge_tm_pg_weight_cfg(hdev, i, + hdev->tm_info.pg_dwrr[i]); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_vport_q_to_qs_map(struct hclge_dev *hdev, + struct hclge_vport *vport) +{ + struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo; + struct hnae3_queue **tqp = kinfo->tqp; + struct hnae3_tc_info *v_tc_info; + u32 i, j; + int ret; + + for (i = 0; i < kinfo->num_tc; i++) { + v_tc_info = &kinfo->tc_info[i]; + for (j = 0; j < v_tc_info->tqp_count; j++) { + struct hnae3_queue *q = tqp[v_tc_info->tqp_offset + j]; + + ret = hclge_tm_q_to_qs_map_cfg(hdev, + hclge_get_queue_id(q), + vport->qs_offset + i); + if (ret) + return ret; + } + } + + return 0; +} + +static int hclge_tm_pri_q_qs_cfg(struct hclge_dev *hdev) +{ + struct hclge_vport *vport = hdev->vport; + int ret; + u32 i; + + if (hdev->tx_sch_mode == HCLGE_FLAG_TC_BASE_SCH_MODE) { + /* Cfg qs -> pri mapping, one by one mapping */ + for (i = 0; i < hdev->tm_info.num_tc; i++) { + ret = hclge_tm_qs_to_pri_map_cfg(hdev, i, i); + if (ret) + return ret; + } + } else if (hdev->tx_sch_mode == HCLGE_FLAG_VNET_BASE_SCH_MODE) { + int k; + /* Cfg qs -> pri mapping, qs = tc, pri = vf, 8 qs -> 1 pri */ + for (k = 0; k < hdev->num_alloc_vport; k++) + for (i = 0; i < HNAE3_MAX_TC; i++) { + ret = hclge_tm_qs_to_pri_map_cfg( + hdev, vport[k].qs_offset + i, k); + if (ret) + return ret; + } + } else { + return -EINVAL; + } + + /* Cfg q -> qs mapping */ + for (i = 0; i < hdev->num_alloc_vport; i++) { + ret = hclge_vport_q_to_qs_map(hdev, vport); + if (ret) + return ret; + + vport++; + } + + return 0; +} + +static int hclge_tm_pri_tc_base_shaper_cfg(struct hclge_dev *hdev) +{ + u8 ir_u, ir_b, ir_s; + int ret; + u32 i; + + for (i = 0; i < hdev->tm_info.num_tc; i++) { + ret = hclge_shaper_para_calc( + hdev->tm_info.tc_info[i].bw_limit, + HCLGE_SHAPER_LVL_PRI, + &ir_b, &ir_u, &ir_s); + if (ret) + return ret; + + ret = hclge_tm_pri_shapping_cfg( + hdev, HCLGE_TM_SHAP_C_BUCKET, i, + 0, 0, 0, HCLGE_SHAPER_BS_U_DEF, + HCLGE_SHAPER_BS_S_DEF); + if (ret) + return ret; + + ret = hclge_tm_pri_shapping_cfg( + hdev, HCLGE_TM_SHAP_P_BUCKET, i, + ir_b, ir_u, ir_s, HCLGE_SHAPER_BS_U_DEF, + HCLGE_SHAPER_BS_S_DEF); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_pri_vnet_base_shaper_pri_cfg(struct hclge_vport *vport) +{ + struct hclge_dev *hdev = vport->back; + u8 ir_u, ir_b, ir_s; + int ret; + + ret = hclge_shaper_para_calc(vport->bw_limit, HCLGE_SHAPER_LVL_VF, + &ir_b, &ir_u, &ir_s); + if (ret) + return ret; + + ret = hclge_tm_pri_shapping_cfg(hdev, HCLGE_TM_SHAP_C_BUCKET, + vport->vport_id, + 0, 0, 0, HCLGE_SHAPER_BS_U_DEF, + HCLGE_SHAPER_BS_S_DEF); + if (ret) + return ret; + + ret = hclge_tm_pri_shapping_cfg(hdev, HCLGE_TM_SHAP_P_BUCKET, + vport->vport_id, + ir_b, ir_u, ir_s, + HCLGE_SHAPER_BS_U_DEF, + HCLGE_SHAPER_BS_S_DEF); + if (ret) + return ret; + + return 0; +} + +static int hclge_tm_pri_vnet_base_shaper_qs_cfg(struct hclge_vport *vport) +{ + struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo; + struct hclge_dev *hdev = vport->back; + struct hnae3_tc_info *v_tc_info; + u8 ir_u, ir_b, ir_s; + u32 i; + int ret; + + for (i = 0; i < kinfo->num_tc; i++) { + v_tc_info = &kinfo->tc_info[i]; + ret = hclge_shaper_para_calc( + hdev->tm_info.tc_info[i].bw_limit, + HCLGE_SHAPER_LVL_QSET, + &ir_b, &ir_u, &ir_s); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_pri_vnet_base_shaper_cfg(struct hclge_dev *hdev) +{ + struct hclge_vport *vport = hdev->vport; + int ret; + u32 i; + + /* Need config vport shaper */ + for (i = 0; i < hdev->num_alloc_vport; i++) { + ret = hclge_tm_pri_vnet_base_shaper_pri_cfg(vport); + if (ret) + return ret; + + ret = hclge_tm_pri_vnet_base_shaper_qs_cfg(vport); + if (ret) + return ret; + + vport++; + } + + return 0; +} + +static int hclge_tm_pri_shaper_cfg(struct hclge_dev *hdev) +{ + int ret; + + if (hdev->tx_sch_mode == HCLGE_FLAG_TC_BASE_SCH_MODE) { + ret = hclge_tm_pri_tc_base_shaper_cfg(hdev); + if (ret) + return ret; + } else { + ret = hclge_tm_pri_vnet_base_shaper_cfg(hdev); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_pri_tc_base_dwrr_cfg(struct hclge_dev *hdev) +{ + struct hclge_pg_info *pg_info; + u8 dwrr; + int ret; + u32 i; + + for (i = 0; i < hdev->tm_info.num_tc; i++) { + pg_info = + &hdev->tm_info.pg_info[hdev->tm_info.tc_info[i].pgid]; + dwrr = pg_info->tc_dwrr[i]; + + ret = hclge_tm_pri_weight_cfg(hdev, i, dwrr); + if (ret) + return ret; + + ret = hclge_tm_qs_weight_cfg(hdev, i, dwrr); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_pri_vnet_base_dwrr_pri_cfg(struct hclge_vport *vport) +{ + struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo; + struct hclge_dev *hdev = vport->back; + int ret; + u8 i; + + /* Vf dwrr */ + ret = hclge_tm_pri_weight_cfg(hdev, vport->vport_id, vport->dwrr); + if (ret) + return ret; + + /* Qset dwrr */ + for (i = 0; i < kinfo->num_tc; i++) { + ret = hclge_tm_qs_weight_cfg( + hdev, vport->qs_offset + i, + hdev->tm_info.pg_info[0].tc_dwrr[i]); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_pri_vnet_base_dwrr_cfg(struct hclge_dev *hdev) +{ + struct hclge_vport *vport = hdev->vport; + int ret; + u32 i; + + for (i = 0; i < hdev->num_alloc_vport; i++) { + ret = hclge_tm_pri_vnet_base_dwrr_pri_cfg(vport); + if (ret) + return ret; + + vport++; + } + + return 0; +} + +static int hclge_tm_pri_dwrr_cfg(struct hclge_dev *hdev) +{ + int ret; + + if (hdev->tx_sch_mode == HCLGE_FLAG_TC_BASE_SCH_MODE) { + ret = hclge_tm_pri_tc_base_dwrr_cfg(hdev); + if (ret) + return ret; + } else { + ret = hclge_tm_pri_vnet_base_dwrr_cfg(hdev); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_map_cfg(struct hclge_dev *hdev) +{ + int ret; + + ret = hclge_tm_pg_to_pri_map(hdev); + if (ret) + return ret; + + return hclge_tm_pri_q_qs_cfg(hdev); +} + +static int hclge_tm_shaper_cfg(struct hclge_dev *hdev) +{ + int ret; + + ret = hclge_tm_pg_shaper_cfg(hdev); + if (ret) + return ret; + + return hclge_tm_pri_shaper_cfg(hdev); +} + +int hclge_tm_dwrr_cfg(struct hclge_dev *hdev) +{ + int ret; + + ret = hclge_tm_pg_dwrr_cfg(hdev); + if (ret) + return ret; + + return hclge_tm_pri_dwrr_cfg(hdev); +} + +static int hclge_tm_lvl2_schd_mode_cfg(struct hclge_dev *hdev) +{ + int ret; + u8 i; + + /* Only being config on TC-Based scheduler mode */ + if (hdev->tx_sch_mode == HCLGE_FLAG_VNET_BASE_SCH_MODE) + return 0; + + for (i = 0; i < hdev->tm_info.num_pg; i++) { + ret = hclge_tm_pg_schd_mode_cfg(hdev, i); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_schd_mode_vnet_base_cfg(struct hclge_vport *vport) +{ + struct hnae3_knic_private_info *kinfo = &vport->nic.kinfo; + struct hclge_dev *hdev = vport->back; + int ret; + u8 i; + + ret = hclge_tm_pri_schd_mode_cfg(hdev, vport->vport_id); + if (ret) + return ret; + + for (i = 0; i < kinfo->num_tc; i++) { + ret = hclge_tm_qs_schd_mode_cfg(hdev, vport->qs_offset + i); + if (ret) + return ret; + } + + return 0; +} + +static int hclge_tm_lvl34_schd_mode_cfg(struct hclge_dev *hdev) +{ + struct hclge_vport *vport = hdev->vport; + int ret; + u8 i; + + if (hdev->tx_sch_mode == HCLGE_FLAG_TC_BASE_SCH_MODE) { + for (i = 0; i < hdev->tm_info.num_tc; i++) { + ret = hclge_tm_pri_schd_mode_cfg(hdev, i); + if (ret) + return ret; + + ret = hclge_tm_qs_schd_mode_cfg(hdev, i); + if (ret) + return ret; + } + } else { + for (i = 0; i < hdev->num_alloc_vport; i++) { + ret = hclge_tm_schd_mode_vnet_base_cfg(vport); + if (ret) + return ret; + + vport++; + } + } + + return 0; +} + +static int hclge_tm_schd_mode_hw(struct hclge_dev *hdev) +{ + int ret; + + ret = hclge_tm_lvl2_schd_mode_cfg(hdev); + if (ret) + return ret; + + return hclge_tm_lvl34_schd_mode_cfg(hdev); +} + +static int hclge_tm_schd_setup_hw(struct hclge_dev *hdev) +{ + int ret; + + /* Cfg tm mapping */ + ret = hclge_tm_map_cfg(hdev); + if (ret) + return ret; + + /* Cfg tm shaper */ + ret = hclge_tm_shaper_cfg(hdev); + if (ret) + return ret; + + /* Cfg dwrr */ + ret = hclge_tm_dwrr_cfg(hdev); + if (ret) + return ret; + + /* Cfg schd mode for each level schd */ + return hclge_tm_schd_mode_hw(hdev); +} + +int hclge_pause_setup_hw(struct hclge_dev *hdev) +{ + bool en = hdev->tm_info.fc_mode != HCLGE_FC_PFC; + int ret; + u8 i; + + ret = hclge_mac_pause_en_cfg(hdev, en, en); + if (ret) + return ret; + + for (i = 0; i < hdev->tm_info.num_tc; i++) { + ret = hclge_tm_qs_bp_cfg(hdev, i); + if (ret) + return ret; + } + + return hclge_up_to_tc_map(hdev); +} + +int hclge_tm_init_hw(struct hclge_dev *hdev) +{ + int ret; + + if ((hdev->tx_sch_mode != HCLGE_FLAG_TC_BASE_SCH_MODE) && + (hdev->tx_sch_mode != HCLGE_FLAG_VNET_BASE_SCH_MODE)) + return -ENOTSUPP; + + ret = hclge_tm_schd_setup_hw(hdev); + if (ret) + return ret; + + ret = hclge_pause_setup_hw(hdev); + if (ret) + return ret; + + return 0; +} + +int hclge_tm_schd_init(struct hclge_dev *hdev) +{ + int ret = hclge_tm_schd_info_init(hdev); + + if (ret) + return ret; + + return hclge_tm_init_hw(hdev); +} diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h new file mode 100644 index 000000000000..7e67337dfaf2 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_tm.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HCLGE_TM_H +#define __HCLGE_TM_H + +#include <linux/types.h> + +/* MAC Pause */ +#define HCLGE_TX_MAC_PAUSE_EN_MSK BIT(0) +#define HCLGE_RX_MAC_PAUSE_EN_MSK BIT(1) + +#define HCLGE_TM_PORT_BASE_MODE_MSK BIT(0) + +/* SP or DWRR */ +#define HCLGE_TM_TX_SCHD_DWRR_MSK BIT(0) +#define HCLGE_TM_TX_SCHD_SP_MSK (0xFE) + +struct hclge_pg_to_pri_link_cmd { + u8 pg_id; + u8 rsvd1[3]; + u8 pri_bit_map; +}; + +struct hclge_qs_to_pri_link_cmd { + __le16 qs_id; + __le16 rsvd; + u8 priority; +#define HCLGE_TM_QS_PRI_LINK_VLD_MSK BIT(0) + u8 link_vld; +}; + +struct hclge_nq_to_qs_link_cmd { + __le16 nq_id; + __le16 rsvd; +#define HCLGE_TM_Q_QS_LINK_VLD_MSK BIT(10) + __le16 qset_id; +}; + +struct hclge_pg_weight_cmd { + u8 pg_id; + u8 dwrr; +}; + +struct hclge_priority_weight_cmd { + u8 pri_id; + u8 dwrr; +}; + +struct hclge_qs_weight_cmd { + __le16 qs_id; + u8 dwrr; +}; + +#define HCLGE_TM_SHAP_IR_B_MSK GENMASK(7, 0) +#define HCLGE_TM_SHAP_IR_B_LSH 0 +#define HCLGE_TM_SHAP_IR_U_MSK GENMASK(11, 8) +#define HCLGE_TM_SHAP_IR_U_LSH 8 +#define HCLGE_TM_SHAP_IR_S_MSK GENMASK(15, 12) +#define HCLGE_TM_SHAP_IR_S_LSH 12 +#define HCLGE_TM_SHAP_BS_B_MSK GENMASK(20, 16) +#define HCLGE_TM_SHAP_BS_B_LSH 16 +#define HCLGE_TM_SHAP_BS_S_MSK GENMASK(25, 21) +#define HCLGE_TM_SHAP_BS_S_LSH 21 + +enum hclge_shap_bucket { + HCLGE_TM_SHAP_C_BUCKET = 0, + HCLGE_TM_SHAP_P_BUCKET, +}; + +struct hclge_pri_shapping_cmd { + u8 pri_id; + u8 rsvd[3]; + __le32 pri_shapping_para; +}; + +struct hclge_pg_shapping_cmd { + u8 pg_id; + u8 rsvd[3]; + __le32 pg_shapping_para; +}; + +struct hclge_bp_to_qs_map_cmd { + u8 tc_id; + u8 rsvd[2]; + u8 qs_group_id; + __le32 qs_bit_map; + u32 rsvd1; +}; + +#define hclge_tm_set_feild(dest, string, val) \ + hnae_set_field((dest), (HCLGE_TM_SHAP_##string##_MSK), \ + (HCLGE_TM_SHAP_##string##_LSH), val) +#define hclge_tm_get_feild(src, string) \ + hnae_get_field((src), (HCLGE_TM_SHAP_##string##_MSK), \ + (HCLGE_TM_SHAP_##string##_LSH)) + +int hclge_tm_schd_init(struct hclge_dev *hdev); +int hclge_pause_setup_hw(struct hclge_dev *hdev); +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c new file mode 100644 index 000000000000..1c3e29447891 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -0,0 +1,2891 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/dma-mapping.h> +#include <linux/etherdevice.h> +#include <linux/interrupt.h> +#include <linux/if_vlan.h> +#include <linux/ip.h> +#include <linux/ipv6.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/skbuff.h> +#include <linux/sctp.h> +#include <linux/vermagic.h> +#include <net/gre.h> +#include <net/vxlan.h> + +#include "hnae3.h" +#include "hns3_enet.h" + +const char hns3_driver_name[] = "hns3"; +const char hns3_driver_version[] = VERMAGIC_STRING; +static const char hns3_driver_string[] = + "Hisilicon Ethernet Network Driver for Hip08 Family"; +static const char hns3_copyright[] = "Copyright (c) 2017 Huawei Corporation."; +static struct hnae3_client client; + +/* hns3_pci_tbl - PCI Device ID Table + * + * Last entry must be all 0s + * + * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, + * Class, Class Mask, private data (not used) } + */ +static const struct pci_device_id hns3_pci_tbl[] = { + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_GE), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_25GE_RDMA_MACSEC), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_50GE_RDMA_MACSEC), 0}, + {PCI_VDEVICE(HUAWEI, HNAE3_DEV_ID_100G_RDMA_MACSEC), 0}, + /* required last entry */ + {0, } +}; +MODULE_DEVICE_TABLE(pci, hns3_pci_tbl); + +static irqreturn_t hns3_irq_handle(int irq, void *dev) +{ + struct hns3_enet_tqp_vector *tqp_vector = dev; + + napi_schedule(&tqp_vector->napi); + + return IRQ_HANDLED; +} + +static void hns3_nic_uninit_irq(struct hns3_nic_priv *priv) +{ + struct hns3_enet_tqp_vector *tqp_vectors; + unsigned int i; + + for (i = 0; i < priv->vector_num; i++) { + tqp_vectors = &priv->tqp_vector[i]; + + if (tqp_vectors->irq_init_flag != HNS3_VECTOR_INITED) + continue; + + /* release the irq resource */ + free_irq(tqp_vectors->vector_irq, tqp_vectors); + tqp_vectors->irq_init_flag = HNS3_VECTOR_NOT_INITED; + } +} + +static int hns3_nic_init_irq(struct hns3_nic_priv *priv) +{ + struct hns3_enet_tqp_vector *tqp_vectors; + int txrx_int_idx = 0; + int rx_int_idx = 0; + int tx_int_idx = 0; + unsigned int i; + int ret; + + for (i = 0; i < priv->vector_num; i++) { + tqp_vectors = &priv->tqp_vector[i]; + + if (tqp_vectors->irq_init_flag == HNS3_VECTOR_INITED) + continue; + + if (tqp_vectors->tx_group.ring && tqp_vectors->rx_group.ring) { + snprintf(tqp_vectors->name, HNAE3_INT_NAME_LEN - 1, + "%s-%s-%d", priv->netdev->name, "TxRx", + txrx_int_idx++); + txrx_int_idx++; + } else if (tqp_vectors->rx_group.ring) { + snprintf(tqp_vectors->name, HNAE3_INT_NAME_LEN - 1, + "%s-%s-%d", priv->netdev->name, "Rx", + rx_int_idx++); + } else if (tqp_vectors->tx_group.ring) { + snprintf(tqp_vectors->name, HNAE3_INT_NAME_LEN - 1, + "%s-%s-%d", priv->netdev->name, "Tx", + tx_int_idx++); + } else { + /* Skip this unused q_vector */ + continue; + } + + tqp_vectors->name[HNAE3_INT_NAME_LEN - 1] = '\0'; + + ret = request_irq(tqp_vectors->vector_irq, hns3_irq_handle, 0, + tqp_vectors->name, + tqp_vectors); + if (ret) { + netdev_err(priv->netdev, "request irq(%d) fail\n", + tqp_vectors->vector_irq); + return ret; + } + + tqp_vectors->irq_init_flag = HNS3_VECTOR_INITED; + } + + return 0; +} + +static void hns3_mask_vector_irq(struct hns3_enet_tqp_vector *tqp_vector, + u32 mask_en) +{ + writel(mask_en, tqp_vector->mask_addr); +} + +static void hns3_vector_enable(struct hns3_enet_tqp_vector *tqp_vector) +{ + napi_enable(&tqp_vector->napi); + + /* enable vector */ + hns3_mask_vector_irq(tqp_vector, 1); +} + +static void hns3_vector_disable(struct hns3_enet_tqp_vector *tqp_vector) +{ + /* disable vector */ + hns3_mask_vector_irq(tqp_vector, 0); + + disable_irq(tqp_vector->vector_irq); + napi_disable(&tqp_vector->napi); +} + +static void hns3_set_vector_coalesc_gl(struct hns3_enet_tqp_vector *tqp_vector, + u32 gl_value) +{ + /* this defines the configuration for GL (Interrupt Gap Limiter) + * GL defines inter interrupt gap. + * GL and RL(Rate Limiter) are 2 ways to acheive interrupt coalescing + */ + writel(gl_value, tqp_vector->mask_addr + HNS3_VECTOR_GL0_OFFSET); + writel(gl_value, tqp_vector->mask_addr + HNS3_VECTOR_GL1_OFFSET); + writel(gl_value, tqp_vector->mask_addr + HNS3_VECTOR_GL2_OFFSET); +} + +static void hns3_set_vector_coalesc_rl(struct hns3_enet_tqp_vector *tqp_vector, + u32 rl_value) +{ + /* this defines the configuration for RL (Interrupt Rate Limiter). + * Rl defines rate of interrupts i.e. number of interrupts-per-second + * GL and RL(Rate Limiter) are 2 ways to acheive interrupt coalescing + */ + writel(rl_value, tqp_vector->mask_addr + HNS3_VECTOR_RL_OFFSET); +} + +static void hns3_vector_gl_rl_init(struct hns3_enet_tqp_vector *tqp_vector) +{ + /* initialize the configuration for interrupt coalescing. + * 1. GL (Interrupt Gap Limiter) + * 2. RL (Interrupt Rate Limiter) + */ + + /* Default :enable interrupt coalesce */ + tqp_vector->rx_group.int_gl = HNS3_INT_GL_50K; + tqp_vector->tx_group.int_gl = HNS3_INT_GL_50K; + hns3_set_vector_coalesc_gl(tqp_vector, HNS3_INT_GL_50K); + /* for now we are disabling Interrupt RL - we + * will re-enable later + */ + hns3_set_vector_coalesc_rl(tqp_vector, 0); + tqp_vector->rx_group.flow_level = HNS3_FLOW_LOW; + tqp_vector->tx_group.flow_level = HNS3_FLOW_LOW; +} + +static int hns3_nic_net_up(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + int i, j; + int ret; + + /* get irq resource for all vectors */ + ret = hns3_nic_init_irq(priv); + if (ret) { + netdev_err(netdev, "hns init irq failed! ret=%d\n", ret); + return ret; + } + + /* enable the vectors */ + for (i = 0; i < priv->vector_num; i++) + hns3_vector_enable(&priv->tqp_vector[i]); + + /* start the ae_dev */ + ret = h->ae_algo->ops->start ? h->ae_algo->ops->start(h) : 0; + if (ret) + goto out_start_err; + + return 0; + +out_start_err: + for (j = i - 1; j >= 0; j--) + hns3_vector_disable(&priv->tqp_vector[j]); + + hns3_nic_uninit_irq(priv); + + return ret; +} + +static int hns3_nic_net_open(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + int ret; + + netif_carrier_off(netdev); + + ret = netif_set_real_num_tx_queues(netdev, h->kinfo.num_tqps); + if (ret) { + netdev_err(netdev, + "netif_set_real_num_tx_queues fail, ret=%d!\n", + ret); + return ret; + } + + ret = netif_set_real_num_rx_queues(netdev, h->kinfo.num_tqps); + if (ret) { + netdev_err(netdev, + "netif_set_real_num_rx_queues fail, ret=%d!\n", ret); + return ret; + } + + ret = hns3_nic_net_up(netdev); + if (ret) { + netdev_err(netdev, + "hns net up fail, ret=%d!\n", ret); + return ret; + } + + return 0; +} + +static void hns3_nic_net_down(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + const struct hnae3_ae_ops *ops; + int i; + + /* stop ae_dev */ + ops = priv->ae_handle->ae_algo->ops; + if (ops->stop) + ops->stop(priv->ae_handle); + + /* disable vectors */ + for (i = 0; i < priv->vector_num; i++) + hns3_vector_disable(&priv->tqp_vector[i]); + + /* free irq resources */ + hns3_nic_uninit_irq(priv); +} + +static int hns3_nic_net_stop(struct net_device *netdev) +{ + netif_tx_stop_all_queues(netdev); + netif_carrier_off(netdev); + + hns3_nic_net_down(netdev); + + return 0; +} + +void hns3_set_multicast_list(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + struct netdev_hw_addr *ha = NULL; + + if (h->ae_algo->ops->set_mc_addr) { + netdev_for_each_mc_addr(ha, netdev) + if (h->ae_algo->ops->set_mc_addr(h, ha->addr)) + netdev_err(netdev, "set multicast fail\n"); + } +} + +static int hns3_nic_uc_sync(struct net_device *netdev, + const unsigned char *addr) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (h->ae_algo->ops->add_uc_addr) + return h->ae_algo->ops->add_uc_addr(h, addr); + + return 0; +} + +static int hns3_nic_uc_unsync(struct net_device *netdev, + const unsigned char *addr) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (h->ae_algo->ops->rm_uc_addr) + return h->ae_algo->ops->rm_uc_addr(h, addr); + + return 0; +} + +static int hns3_nic_mc_sync(struct net_device *netdev, + const unsigned char *addr) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (h->ae_algo->ops->add_mc_addr) + return h->ae_algo->ops->add_mc_addr(h, addr); + + return 0; +} + +static int hns3_nic_mc_unsync(struct net_device *netdev, + const unsigned char *addr) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (h->ae_algo->ops->rm_mc_addr) + return h->ae_algo->ops->rm_mc_addr(h, addr); + + return 0; +} + +void hns3_nic_set_rx_mode(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (h->ae_algo->ops->set_promisc_mode) { + if (netdev->flags & IFF_PROMISC) + h->ae_algo->ops->set_promisc_mode(h, 1); + else + h->ae_algo->ops->set_promisc_mode(h, 0); + } + if (__dev_uc_sync(netdev, hns3_nic_uc_sync, hns3_nic_uc_unsync)) + netdev_err(netdev, "sync uc address fail\n"); + if (netdev->flags & IFF_MULTICAST) + if (__dev_mc_sync(netdev, hns3_nic_mc_sync, hns3_nic_mc_unsync)) + netdev_err(netdev, "sync mc address fail\n"); +} + +static int hns3_set_tso(struct sk_buff *skb, u32 *paylen, + u16 *mss, u32 *type_cs_vlan_tso) +{ + u32 l4_offset, hdr_len; + union l3_hdr_info l3; + union l4_hdr_info l4; + u32 l4_paylen; + int ret; + + if (!skb_is_gso(skb)) + return 0; + + ret = skb_cow_head(skb, 0); + if (ret) + return ret; + + l3.hdr = skb_network_header(skb); + l4.hdr = skb_transport_header(skb); + + /* Software should clear the IPv4's checksum field when tso is + * needed. + */ + if (l3.v4->version == 4) + l3.v4->check = 0; + + /* tunnel packet.*/ + if (skb_shinfo(skb)->gso_type & (SKB_GSO_GRE | + SKB_GSO_GRE_CSUM | + SKB_GSO_UDP_TUNNEL | + SKB_GSO_UDP_TUNNEL_CSUM)) { + if ((!(skb_shinfo(skb)->gso_type & + SKB_GSO_PARTIAL)) && + (skb_shinfo(skb)->gso_type & + SKB_GSO_UDP_TUNNEL_CSUM)) { + /* Software should clear the udp's checksum + * field when tso is needed. + */ + l4.udp->check = 0; + } + /* reset l3&l4 pointers from outer to inner headers */ + l3.hdr = skb_inner_network_header(skb); + l4.hdr = skb_inner_transport_header(skb); + + /* Software should clear the IPv4's checksum field when + * tso is needed. + */ + if (l3.v4->version == 4) + l3.v4->check = 0; + } + + /* normal or tunnel packet*/ + l4_offset = l4.hdr - skb->data; + hdr_len = (l4.tcp->doff * 4) + l4_offset; + + /* remove payload length from inner pseudo checksum when tso*/ + l4_paylen = skb->len - l4_offset; + csum_replace_by_diff(&l4.tcp->check, + (__force __wsum)htonl(l4_paylen)); + + /* find the txbd field values */ + *paylen = skb->len - hdr_len; + hnae_set_bit(*type_cs_vlan_tso, + HNS3_TXD_TSO_B, 1); + + /* get MSS for TSO */ + *mss = skb_shinfo(skb)->gso_size; + + return 0; +} + +static int hns3_get_l4_protocol(struct sk_buff *skb, u8 *ol4_proto, + u8 *il4_proto) +{ + union { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; + } l3; + unsigned char *l4_hdr; + unsigned char *exthdr; + u8 l4_proto_tmp; + __be16 frag_off; + + /* find outer header point */ + l3.hdr = skb_network_header(skb); + l4_hdr = skb_inner_transport_header(skb); + + if (skb->protocol == htons(ETH_P_IPV6)) { + exthdr = l3.hdr + sizeof(*l3.v6); + l4_proto_tmp = l3.v6->nexthdr; + if (l4_hdr != exthdr) + ipv6_skip_exthdr(skb, exthdr - skb->data, + &l4_proto_tmp, &frag_off); + } else if (skb->protocol == htons(ETH_P_IP)) { + l4_proto_tmp = l3.v4->protocol; + } else { + return -EINVAL; + } + + *ol4_proto = l4_proto_tmp; + + /* tunnel packet */ + if (!skb->encapsulation) { + *il4_proto = 0; + return 0; + } + + /* find inner header point */ + l3.hdr = skb_inner_network_header(skb); + l4_hdr = skb_inner_transport_header(skb); + + if (l3.v6->version == 6) { + exthdr = l3.hdr + sizeof(*l3.v6); + l4_proto_tmp = l3.v6->nexthdr; + if (l4_hdr != exthdr) + ipv6_skip_exthdr(skb, exthdr - skb->data, + &l4_proto_tmp, &frag_off); + } else if (l3.v4->version == 4) { + l4_proto_tmp = l3.v4->protocol; + } + + *il4_proto = l4_proto_tmp; + + return 0; +} + +static void hns3_set_l2l3l4_len(struct sk_buff *skb, u8 ol4_proto, + u8 il4_proto, u32 *type_cs_vlan_tso, + u32 *ol_type_vlan_len_msec) +{ + union { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; + } l3; + union { + struct tcphdr *tcp; + struct udphdr *udp; + struct gre_base_hdr *gre; + unsigned char *hdr; + } l4; + unsigned char *l2_hdr; + u8 l4_proto = ol4_proto; + u32 ol2_len; + u32 ol3_len; + u32 ol4_len; + u32 l2_len; + u32 l3_len; + + l3.hdr = skb_network_header(skb); + l4.hdr = skb_transport_header(skb); + + /* compute L2 header size for normal packet, defined in 2 Bytes */ + l2_len = l3.hdr - skb->data; + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L2LEN_M, + HNS3_TXD_L2LEN_S, l2_len >> 1); + + /* tunnel packet*/ + if (skb->encapsulation) { + /* compute OL2 header size, defined in 2 Bytes */ + ol2_len = l2_len; + hnae_set_field(*ol_type_vlan_len_msec, + HNS3_TXD_L2LEN_M, + HNS3_TXD_L2LEN_S, ol2_len >> 1); + + /* compute OL3 header size, defined in 4 Bytes */ + ol3_len = l4.hdr - l3.hdr; + hnae_set_field(*ol_type_vlan_len_msec, HNS3_TXD_L3LEN_M, + HNS3_TXD_L3LEN_S, ol3_len >> 2); + + /* MAC in UDP, MAC in GRE (0x6558)*/ + if ((ol4_proto == IPPROTO_UDP) || (ol4_proto == IPPROTO_GRE)) { + /* switch MAC header ptr from outer to inner header.*/ + l2_hdr = skb_inner_mac_header(skb); + + /* compute OL4 header size, defined in 4 Bytes. */ + ol4_len = l2_hdr - l4.hdr; + hnae_set_field(*ol_type_vlan_len_msec, HNS3_TXD_L4LEN_M, + HNS3_TXD_L4LEN_S, ol4_len >> 2); + + /* switch IP header ptr from outer to inner header */ + l3.hdr = skb_inner_network_header(skb); + + /* compute inner l2 header size, defined in 2 Bytes. */ + l2_len = l3.hdr - l2_hdr; + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L2LEN_M, + HNS3_TXD_L2LEN_S, l2_len >> 1); + } else { + /* skb packet types not supported by hardware, + * txbd len fild doesn't be filled. + */ + return; + } + + /* switch L4 header pointer from outer to inner */ + l4.hdr = skb_inner_transport_header(skb); + + l4_proto = il4_proto; + } + + /* compute inner(/normal) L3 header size, defined in 4 Bytes */ + l3_len = l4.hdr - l3.hdr; + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L3LEN_M, + HNS3_TXD_L3LEN_S, l3_len >> 2); + + /* compute inner(/normal) L4 header size, defined in 4 Bytes */ + switch (l4_proto) { + case IPPROTO_TCP: + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L4LEN_M, + HNS3_TXD_L4LEN_S, l4.tcp->doff); + break; + case IPPROTO_SCTP: + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L4LEN_M, + HNS3_TXD_L4LEN_S, (sizeof(struct sctphdr) >> 2)); + break; + case IPPROTO_UDP: + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L4LEN_M, + HNS3_TXD_L4LEN_S, (sizeof(struct udphdr) >> 2)); + break; + default: + /* skb packet types not supported by hardware, + * txbd len fild doesn't be filled. + */ + return; + } +} + +static int hns3_set_l3l4_type_csum(struct sk_buff *skb, u8 ol4_proto, + u8 il4_proto, u32 *type_cs_vlan_tso, + u32 *ol_type_vlan_len_msec) +{ + union { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; + } l3; + u32 l4_proto = ol4_proto; + + l3.hdr = skb_network_header(skb); + + /* define OL3 type and tunnel type(OL4).*/ + if (skb->encapsulation) { + /* define outer network header type.*/ + if (skb->protocol == htons(ETH_P_IP)) { + if (skb_is_gso(skb)) + hnae_set_field(*ol_type_vlan_len_msec, + HNS3_TXD_OL3T_M, HNS3_TXD_OL3T_S, + HNS3_OL3T_IPV4_CSUM); + else + hnae_set_field(*ol_type_vlan_len_msec, + HNS3_TXD_OL3T_M, HNS3_TXD_OL3T_S, + HNS3_OL3T_IPV4_NO_CSUM); + + } else if (skb->protocol == htons(ETH_P_IPV6)) { + hnae_set_field(*ol_type_vlan_len_msec, HNS3_TXD_OL3T_M, + HNS3_TXD_OL3T_S, HNS3_OL3T_IPV6); + } + + /* define tunnel type(OL4).*/ + switch (l4_proto) { + case IPPROTO_UDP: + hnae_set_field(*ol_type_vlan_len_msec, + HNS3_TXD_TUNTYPE_M, + HNS3_TXD_TUNTYPE_S, + HNS3_TUN_MAC_IN_UDP); + break; + case IPPROTO_GRE: + hnae_set_field(*ol_type_vlan_len_msec, + HNS3_TXD_TUNTYPE_M, + HNS3_TXD_TUNTYPE_S, + HNS3_TUN_NVGRE); + break; + default: + /* drop the skb tunnel packet if hardware don't support, + * because hardware can't calculate csum when TSO. + */ + if (skb_is_gso(skb)) + return -EDOM; + + /* the stack computes the IP header already, + * driver calculate l4 checksum when not TSO. + */ + skb_checksum_help(skb); + return 0; + } + + l3.hdr = skb_inner_network_header(skb); + l4_proto = il4_proto; + } + + if (l3.v4->version == 4) { + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L3T_M, + HNS3_TXD_L3T_S, HNS3_L3T_IPV4); + + /* the stack computes the IP header already, the only time we + * need the hardware to recompute it is in the case of TSO. + */ + if (skb_is_gso(skb)) + hnae_set_bit(*type_cs_vlan_tso, HNS3_TXD_L3CS_B, 1); + + hnae_set_bit(*type_cs_vlan_tso, HNS3_TXD_L4CS_B, 1); + } else if (l3.v6->version == 6) { + hnae_set_field(*type_cs_vlan_tso, HNS3_TXD_L3T_M, + HNS3_TXD_L3T_S, HNS3_L3T_IPV6); + hnae_set_bit(*type_cs_vlan_tso, HNS3_TXD_L4CS_B, 1); + } + + switch (l4_proto) { + case IPPROTO_TCP: + hnae_set_field(*type_cs_vlan_tso, + HNS3_TXD_L4T_M, + HNS3_TXD_L4T_S, + HNS3_L4T_TCP); + break; + case IPPROTO_UDP: + hnae_set_field(*type_cs_vlan_tso, + HNS3_TXD_L4T_M, + HNS3_TXD_L4T_S, + HNS3_L4T_UDP); + break; + case IPPROTO_SCTP: + hnae_set_field(*type_cs_vlan_tso, + HNS3_TXD_L4T_M, + HNS3_TXD_L4T_S, + HNS3_L4T_SCTP); + break; + default: + /* drop the skb tunnel packet if hardware don't support, + * because hardware can't calculate csum when TSO. + */ + if (skb_is_gso(skb)) + return -EDOM; + + /* the stack computes the IP header already, + * driver calculate l4 checksum when not TSO. + */ + skb_checksum_help(skb); + return 0; + } + + return 0; +} + +static void hns3_set_txbd_baseinfo(u16 *bdtp_fe_sc_vld_ra_ri, int frag_end) +{ + /* Config bd buffer end */ + hnae_set_field(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_BDTYPE_M, + HNS3_TXD_BDTYPE_M, 0); + hnae_set_bit(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_FE_B, !!frag_end); + hnae_set_bit(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_VLD_B, 1); + hnae_set_field(*bdtp_fe_sc_vld_ra_ri, HNS3_TXD_SC_M, HNS3_TXD_SC_S, 1); +} + +static int hns3_fill_desc(struct hns3_enet_ring *ring, void *priv, + int size, dma_addr_t dma, int frag_end, + enum hns_desc_type type) +{ + struct hns3_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_use]; + struct hns3_desc *desc = &ring->desc[ring->next_to_use]; + u32 ol_type_vlan_len_msec = 0; + u16 bdtp_fe_sc_vld_ra_ri = 0; + u32 type_cs_vlan_tso = 0; + struct sk_buff *skb; + u32 paylen = 0; + u16 mss = 0; + __be16 protocol; + u8 ol4_proto; + u8 il4_proto; + int ret; + + /* The txbd's baseinfo of DESC_TYPE_PAGE & DESC_TYPE_SKB */ + desc_cb->priv = priv; + desc_cb->length = size; + desc_cb->dma = dma; + desc_cb->type = type; + + /* now, fill the descriptor */ + desc->addr = cpu_to_le64(dma); + desc->tx.send_size = cpu_to_le16((u16)size); + hns3_set_txbd_baseinfo(&bdtp_fe_sc_vld_ra_ri, frag_end); + desc->tx.bdtp_fe_sc_vld_ra_ri = cpu_to_le16(bdtp_fe_sc_vld_ra_ri); + + if (type == DESC_TYPE_SKB) { + skb = (struct sk_buff *)priv; + paylen = cpu_to_le16(skb->len); + + if (skb->ip_summed == CHECKSUM_PARTIAL) { + skb_reset_mac_len(skb); + protocol = skb->protocol; + + /* vlan packet*/ + if (protocol == htons(ETH_P_8021Q)) { + protocol = vlan_get_protocol(skb); + skb->protocol = protocol; + } + ret = hns3_get_l4_protocol(skb, &ol4_proto, &il4_proto); + if (ret) + return ret; + hns3_set_l2l3l4_len(skb, ol4_proto, il4_proto, + &type_cs_vlan_tso, + &ol_type_vlan_len_msec); + ret = hns3_set_l3l4_type_csum(skb, ol4_proto, il4_proto, + &type_cs_vlan_tso, + &ol_type_vlan_len_msec); + if (ret) + return ret; + + ret = hns3_set_tso(skb, &paylen, &mss, + &type_cs_vlan_tso); + if (ret) + return ret; + } + + /* Set txbd */ + desc->tx.ol_type_vlan_len_msec = + cpu_to_le32(ol_type_vlan_len_msec); + desc->tx.type_cs_vlan_tso_len = + cpu_to_le32(type_cs_vlan_tso); + desc->tx.paylen = cpu_to_le16(paylen); + desc->tx.mss = cpu_to_le16(mss); + } + + /* move ring pointer to next.*/ + ring_ptr_move_fw(ring, next_to_use); + + return 0; +} + +static int hns3_fill_desc_tso(struct hns3_enet_ring *ring, void *priv, + int size, dma_addr_t dma, int frag_end, + enum hns_desc_type type) +{ + unsigned int frag_buf_num; + unsigned int k; + int sizeoflast; + int ret; + + frag_buf_num = (size + HNS3_MAX_BD_SIZE - 1) / HNS3_MAX_BD_SIZE; + sizeoflast = size % HNS3_MAX_BD_SIZE; + sizeoflast = sizeoflast ? sizeoflast : HNS3_MAX_BD_SIZE; + + /* When the frag size is bigger than hardware, split this frag */ + for (k = 0; k < frag_buf_num; k++) { + ret = hns3_fill_desc(ring, priv, + (k == frag_buf_num - 1) ? + sizeoflast : HNS3_MAX_BD_SIZE, + dma + HNS3_MAX_BD_SIZE * k, + frag_end && (k == frag_buf_num - 1) ? 1 : 0, + (type == DESC_TYPE_SKB && !k) ? + DESC_TYPE_SKB : DESC_TYPE_PAGE); + if (ret) + return ret; + } + + return 0; +} + +static int hns3_nic_maybe_stop_tso(struct sk_buff **out_skb, int *bnum, + struct hns3_enet_ring *ring) +{ + struct sk_buff *skb = *out_skb; + struct skb_frag_struct *frag; + int bdnum_for_frag; + int frag_num; + int buf_num; + int size; + int i; + + size = skb_headlen(skb); + buf_num = (size + HNS3_MAX_BD_SIZE - 1) / HNS3_MAX_BD_SIZE; + + frag_num = skb_shinfo(skb)->nr_frags; + for (i = 0; i < frag_num; i++) { + frag = &skb_shinfo(skb)->frags[i]; + size = skb_frag_size(frag); + bdnum_for_frag = + (size + HNS3_MAX_BD_SIZE - 1) / HNS3_MAX_BD_SIZE; + if (bdnum_for_frag > HNS3_MAX_BD_PER_FRAG) + return -ENOMEM; + + buf_num += bdnum_for_frag; + } + + if (buf_num > ring_space(ring)) + return -EBUSY; + + *bnum = buf_num; + return 0; +} + +static int hns3_nic_maybe_stop_tx(struct sk_buff **out_skb, int *bnum, + struct hns3_enet_ring *ring) +{ + struct sk_buff *skb = *out_skb; + int buf_num; + + /* No. of segments (plus a header) */ + buf_num = skb_shinfo(skb)->nr_frags + 1; + + if (buf_num > ring_space(ring)) + return -EBUSY; + + *bnum = buf_num; + + return 0; +} + +static void hns_nic_dma_unmap(struct hns3_enet_ring *ring, int next_to_use_orig) +{ + struct device *dev = ring_to_dev(ring); + unsigned int i; + + for (i = 0; i < ring->desc_num; i++) { + /* check if this is where we started */ + if (ring->next_to_use == next_to_use_orig) + break; + + /* unmap the descriptor dma address */ + if (ring->desc_cb[ring->next_to_use].type == DESC_TYPE_SKB) + dma_unmap_single(dev, + ring->desc_cb[ring->next_to_use].dma, + ring->desc_cb[ring->next_to_use].length, + DMA_TO_DEVICE); + else + dma_unmap_page(dev, + ring->desc_cb[ring->next_to_use].dma, + ring->desc_cb[ring->next_to_use].length, + DMA_TO_DEVICE); + + /* rollback one */ + ring_ptr_move_bw(ring, next_to_use); + } +} + +static netdev_tx_t hns3_nic_net_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hns3_nic_ring_data *ring_data = + &tx_ring_data(priv, skb->queue_mapping); + struct hns3_enet_ring *ring = ring_data->ring; + struct device *dev = priv->dev; + struct netdev_queue *dev_queue; + struct skb_frag_struct *frag; + int next_to_use_head; + int next_to_use_frag; + dma_addr_t dma; + int buf_num; + int seg_num; + int size; + int ret; + int i; + + /* Prefetch the data used later */ + prefetch(skb->data); + + switch (priv->ops.maybe_stop_tx(&skb, &buf_num, ring)) { + case -EBUSY: + u64_stats_update_begin(&ring->syncp); + ring->stats.tx_busy++; + u64_stats_update_end(&ring->syncp); + + goto out_net_tx_busy; + case -ENOMEM: + u64_stats_update_begin(&ring->syncp); + ring->stats.sw_err_cnt++; + u64_stats_update_end(&ring->syncp); + netdev_err(netdev, "no memory to xmit!\n"); + + goto out_err_tx_ok; + default: + break; + } + + /* No. of segments (plus a header) */ + seg_num = skb_shinfo(skb)->nr_frags + 1; + /* Fill the first part */ + size = skb_headlen(skb); + + next_to_use_head = ring->next_to_use; + + dma = dma_map_single(dev, skb->data, size, DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma)) { + netdev_err(netdev, "TX head DMA map failed\n"); + ring->stats.sw_err_cnt++; + goto out_err_tx_ok; + } + + ret = priv->ops.fill_desc(ring, skb, size, dma, seg_num == 1 ? 1 : 0, + DESC_TYPE_SKB); + if (ret) + goto head_dma_map_err; + + next_to_use_frag = ring->next_to_use; + /* Fill the fragments */ + for (i = 1; i < seg_num; i++) { + frag = &skb_shinfo(skb)->frags[i - 1]; + size = skb_frag_size(frag); + dma = skb_frag_dma_map(dev, frag, 0, size, DMA_TO_DEVICE); + if (dma_mapping_error(dev, dma)) { + netdev_err(netdev, "TX frag(%d) DMA map failed\n", i); + ring->stats.sw_err_cnt++; + goto frag_dma_map_err; + } + ret = priv->ops.fill_desc(ring, skb_frag_page(frag), size, dma, + seg_num - 1 == i ? 1 : 0, + DESC_TYPE_PAGE); + + if (ret) + goto frag_dma_map_err; + } + + /* Complete translate all packets */ + dev_queue = netdev_get_tx_queue(netdev, ring_data->queue_index); + netdev_tx_sent_queue(dev_queue, skb->len); + + wmb(); /* Commit all data before submit */ + + hnae_queue_xmit(ring->tqp, buf_num); + + return NETDEV_TX_OK; + +frag_dma_map_err: + hns_nic_dma_unmap(ring, next_to_use_frag); + +head_dma_map_err: + hns_nic_dma_unmap(ring, next_to_use_head); + +out_err_tx_ok: + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; + +out_net_tx_busy: + netif_stop_subqueue(netdev, ring_data->queue_index); + smp_mb(); /* Commit all data before submit */ + + return NETDEV_TX_BUSY; +} + +static int hns3_nic_net_set_mac_address(struct net_device *netdev, void *p) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + struct sockaddr *mac_addr = p; + int ret; + + if (!mac_addr || !is_valid_ether_addr((const u8 *)mac_addr->sa_data)) + return -EADDRNOTAVAIL; + + ret = h->ae_algo->ops->set_mac_addr(h, mac_addr->sa_data); + if (ret) { + netdev_err(netdev, "set_mac_address fail, ret=%d!\n", ret); + return ret; + } + + ether_addr_copy(netdev->dev_addr, mac_addr->sa_data); + + return 0; +} + +static int hns3_nic_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + + if (features & (NETIF_F_TSO | NETIF_F_TSO6)) { + priv->ops.fill_desc = hns3_fill_desc_tso; + priv->ops.maybe_stop_tx = hns3_nic_maybe_stop_tso; + } else { + priv->ops.fill_desc = hns3_fill_desc; + priv->ops.maybe_stop_tx = hns3_nic_maybe_stop_tx; + } + + netdev->features = features; + return 0; +} + +static void +hns3_nic_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + int queue_num = priv->ae_handle->kinfo.num_tqps; + struct hns3_enet_ring *ring; + unsigned int start; + unsigned int idx; + u64 tx_bytes = 0; + u64 rx_bytes = 0; + u64 tx_pkts = 0; + u64 rx_pkts = 0; + + for (idx = 0; idx < queue_num; idx++) { + /* fetch the tx stats */ + ring = priv->ring_data[idx].ring; + do { + start = u64_stats_fetch_begin_irq(&ring->syncp); + tx_bytes += ring->stats.tx_bytes; + tx_pkts += ring->stats.tx_pkts; + } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + + /* fetch the rx stats */ + ring = priv->ring_data[idx + queue_num].ring; + do { + start = u64_stats_fetch_begin_irq(&ring->syncp); + rx_bytes += ring->stats.rx_bytes; + rx_pkts += ring->stats.rx_pkts; + } while (u64_stats_fetch_retry_irq(&ring->syncp, start)); + } + + stats->tx_bytes = tx_bytes; + stats->tx_packets = tx_pkts; + stats->rx_bytes = rx_bytes; + stats->rx_packets = rx_pkts; + + stats->rx_errors = netdev->stats.rx_errors; + stats->multicast = netdev->stats.multicast; + stats->rx_length_errors = netdev->stats.rx_length_errors; + stats->rx_crc_errors = netdev->stats.rx_crc_errors; + stats->rx_missed_errors = netdev->stats.rx_missed_errors; + + stats->tx_errors = netdev->stats.tx_errors; + stats->rx_dropped = netdev->stats.rx_dropped; + stats->tx_dropped = netdev->stats.tx_dropped; + stats->collisions = netdev->stats.collisions; + stats->rx_over_errors = netdev->stats.rx_over_errors; + stats->rx_frame_errors = netdev->stats.rx_frame_errors; + stats->rx_fifo_errors = netdev->stats.rx_fifo_errors; + stats->tx_aborted_errors = netdev->stats.tx_aborted_errors; + stats->tx_carrier_errors = netdev->stats.tx_carrier_errors; + stats->tx_fifo_errors = netdev->stats.tx_fifo_errors; + stats->tx_heartbeat_errors = netdev->stats.tx_heartbeat_errors; + stats->tx_window_errors = netdev->stats.tx_window_errors; + stats->rx_compressed = netdev->stats.rx_compressed; + stats->tx_compressed = netdev->stats.tx_compressed; +} + +static void hns3_add_tunnel_port(struct net_device *netdev, u16 port, + enum hns3_udp_tnl_type type) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hns3_udp_tunnel *udp_tnl = &priv->udp_tnl[type]; + struct hnae3_handle *h = priv->ae_handle; + + if (udp_tnl->used && udp_tnl->dst_port == port) { + udp_tnl->used++; + return; + } + + if (udp_tnl->used) { + netdev_warn(netdev, + "UDP tunnel [%d], port [%d] offload\n", type, port); + return; + } + + udp_tnl->dst_port = port; + udp_tnl->used = 1; + /* TBD send command to hardware to add port */ + if (h->ae_algo->ops->add_tunnel_udp) + h->ae_algo->ops->add_tunnel_udp(h, port); +} + +static void hns3_del_tunnel_port(struct net_device *netdev, u16 port, + enum hns3_udp_tnl_type type) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hns3_udp_tunnel *udp_tnl = &priv->udp_tnl[type]; + struct hnae3_handle *h = priv->ae_handle; + + if (!udp_tnl->used || udp_tnl->dst_port != port) { + netdev_warn(netdev, + "Invalid UDP tunnel port %d\n", port); + return; + } + + udp_tnl->used--; + if (udp_tnl->used) + return; + + udp_tnl->dst_port = 0; + /* TBD send command to hardware to del port */ + if (h->ae_algo->ops->del_tunnel_udp) + h->ae_algo->ops->del_tunnel_udp(h, port); +} + +/* hns3_nic_udp_tunnel_add - Get notifiacetion about UDP tunnel ports + * @netdev: This physical ports's netdev + * @ti: Tunnel information + */ +static void hns3_nic_udp_tunnel_add(struct net_device *netdev, + struct udp_tunnel_info *ti) +{ + u16 port_n = ntohs(ti->port); + + switch (ti->type) { + case UDP_TUNNEL_TYPE_VXLAN: + hns3_add_tunnel_port(netdev, port_n, HNS3_UDP_TNL_VXLAN); + break; + case UDP_TUNNEL_TYPE_GENEVE: + hns3_add_tunnel_port(netdev, port_n, HNS3_UDP_TNL_GENEVE); + break; + default: + netdev_err(netdev, "unsupported tunnel type %d\n", ti->type); + break; + } +} + +static void hns3_nic_udp_tunnel_del(struct net_device *netdev, + struct udp_tunnel_info *ti) +{ + u16 port_n = ntohs(ti->port); + + switch (ti->type) { + case UDP_TUNNEL_TYPE_VXLAN: + hns3_del_tunnel_port(netdev, port_n, HNS3_UDP_TNL_VXLAN); + break; + case UDP_TUNNEL_TYPE_GENEVE: + hns3_del_tunnel_port(netdev, port_n, HNS3_UDP_TNL_GENEVE); + break; + default: + break; + } +} + +static int hns3_setup_tc(struct net_device *netdev, u8 tc) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + struct hnae3_knic_private_info *kinfo = &h->kinfo; + unsigned int i; + int ret; + + if (tc > HNAE3_MAX_TC) + return -EINVAL; + + if (kinfo->num_tc == tc) + return 0; + + if (!netdev) + return -EINVAL; + + if (!tc) { + netdev_reset_tc(netdev); + return 0; + } + + /* Set num_tc for netdev */ + ret = netdev_set_num_tc(netdev, tc); + if (ret) + return ret; + + /* Set per TC queues for the VSI */ + for (i = 0; i < HNAE3_MAX_TC; i++) { + if (kinfo->tc_info[i].enable) + netdev_set_tc_queue(netdev, + kinfo->tc_info[i].tc, + kinfo->tc_info[i].tqp_count, + kinfo->tc_info[i].tqp_offset); + } + + return 0; +} + +static int hns3_nic_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + struct tc_mqprio_qopt *mqprio = type_data; + + if (type != TC_SETUP_MQPRIO) + return -EOPNOTSUPP; + + return hns3_setup_tc(dev, mqprio->num_tc); +} + +static int hns3_vlan_rx_add_vid(struct net_device *netdev, + __be16 proto, u16 vid) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + int ret = -EIO; + + if (h->ae_algo->ops->set_vlan_filter) + ret = h->ae_algo->ops->set_vlan_filter(h, proto, vid, false); + + return ret; +} + +static int hns3_vlan_rx_kill_vid(struct net_device *netdev, + __be16 proto, u16 vid) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + int ret = -EIO; + + if (h->ae_algo->ops->set_vlan_filter) + ret = h->ae_algo->ops->set_vlan_filter(h, proto, vid, true); + + return ret; +} + +static int hns3_ndo_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, + u8 qos, __be16 vlan_proto) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + int ret = -EIO; + + if (h->ae_algo->ops->set_vf_vlan_filter) + ret = h->ae_algo->ops->set_vf_vlan_filter(h, vf, vlan, + qos, vlan_proto); + + return ret; +} + +static int hns3_nic_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + bool if_running = netif_running(netdev); + int ret; + + if (!h->ae_algo->ops->set_mtu) + return -EOPNOTSUPP; + + /* if this was called with netdev up then bring netdevice down */ + if (if_running) { + (void)hns3_nic_net_stop(netdev); + msleep(100); + } + + ret = h->ae_algo->ops->set_mtu(h, new_mtu); + if (ret) { + netdev_err(netdev, "failed to change MTU in hardware %d\n", + ret); + return ret; + } + + /* if the netdev was running earlier, bring it up again */ + if (if_running && hns3_nic_net_open(netdev)) + ret = -EINVAL; + + return ret; +} + +static const struct net_device_ops hns3_nic_netdev_ops = { + .ndo_open = hns3_nic_net_open, + .ndo_stop = hns3_nic_net_stop, + .ndo_start_xmit = hns3_nic_net_xmit, + .ndo_set_mac_address = hns3_nic_net_set_mac_address, + .ndo_change_mtu = hns3_nic_change_mtu, + .ndo_set_features = hns3_nic_set_features, + .ndo_get_stats64 = hns3_nic_get_stats64, + .ndo_setup_tc = hns3_nic_setup_tc, + .ndo_set_rx_mode = hns3_nic_set_rx_mode, + .ndo_udp_tunnel_add = hns3_nic_udp_tunnel_add, + .ndo_udp_tunnel_del = hns3_nic_udp_tunnel_del, + .ndo_vlan_rx_add_vid = hns3_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = hns3_vlan_rx_kill_vid, + .ndo_set_vf_vlan = hns3_ndo_set_vf_vlan, +}; + +/* hns3_probe - Device initialization routine + * @pdev: PCI device information struct + * @ent: entry in hns3_pci_tbl + * + * hns3_probe initializes a PF identified by a pci_dev structure. + * The OS initialization, configuring of the PF private structure, + * and a hardware reset occur. + * + * Returns 0 on success, negative on failure + */ +static int hns3_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct hnae3_ae_dev *ae_dev; + int ret; + + ae_dev = devm_kzalloc(&pdev->dev, sizeof(*ae_dev), + GFP_KERNEL); + if (!ae_dev) { + ret = -ENOMEM; + return ret; + } + + ae_dev->pdev = pdev; + ae_dev->dev_type = HNAE3_DEV_KNIC; + pci_set_drvdata(pdev, ae_dev); + + return hnae3_register_ae_dev(ae_dev); +} + +/* hns3_remove - Device removal routine + * @pdev: PCI device information struct + */ +static void hns3_remove(struct pci_dev *pdev) +{ + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev); + + hnae3_unregister_ae_dev(ae_dev); + + devm_kfree(&pdev->dev, ae_dev); + + pci_set_drvdata(pdev, NULL); +} + +static struct pci_driver hns3_driver = { + .name = hns3_driver_name, + .id_table = hns3_pci_tbl, + .probe = hns3_probe, + .remove = hns3_remove, +}; + +/* set default feature to hns3 */ +static void hns3_set_default_feature(struct net_device *netdev) +{ + netdev->priv_flags |= IFF_UNICAST_FLT; + + netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO | + NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; + + netdev->hw_enc_features |= NETIF_F_TSO_MANGLEID; + + netdev->gso_partial_features |= NETIF_F_GSO_GRE_CSUM; + + netdev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_HW_VLAN_CTAG_FILTER | + NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO | + NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; + + netdev->vlan_features |= + NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | + NETIF_F_SG | NETIF_F_GSO | NETIF_F_GRO | + NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; + + netdev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_HW_VLAN_CTAG_FILTER | + NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_GSO | + NETIF_F_GRO | NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM | NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; +} + +static int hns3_alloc_buffer(struct hns3_enet_ring *ring, + struct hns3_desc_cb *cb) +{ + unsigned int order = hnae_page_order(ring); + struct page *p; + + p = dev_alloc_pages(order); + if (!p) + return -ENOMEM; + + cb->priv = p; + cb->page_offset = 0; + cb->reuse_flag = 0; + cb->buf = page_address(p); + cb->length = hnae_page_size(ring); + cb->type = DESC_TYPE_PAGE; + + memset(cb->buf, 0, cb->length); + + return 0; +} + +static void hns3_free_buffer(struct hns3_enet_ring *ring, + struct hns3_desc_cb *cb) +{ + if (cb->type == DESC_TYPE_SKB) + dev_kfree_skb_any((struct sk_buff *)cb->priv); + else if (!HNAE3_IS_TX_RING(ring)) + put_page((struct page *)cb->priv); + memset(cb, 0, sizeof(*cb)); +} + +static int hns3_map_buffer(struct hns3_enet_ring *ring, struct hns3_desc_cb *cb) +{ + cb->dma = dma_map_page(ring_to_dev(ring), cb->priv, 0, + cb->length, ring_to_dma_dir(ring)); + + if (dma_mapping_error(ring_to_dev(ring), cb->dma)) + return -EIO; + + return 0; +} + +static void hns3_unmap_buffer(struct hns3_enet_ring *ring, + struct hns3_desc_cb *cb) +{ + if (cb->type == DESC_TYPE_SKB) + dma_unmap_single(ring_to_dev(ring), cb->dma, cb->length, + ring_to_dma_dir(ring)); + else + dma_unmap_page(ring_to_dev(ring), cb->dma, cb->length, + ring_to_dma_dir(ring)); +} + +static void hns3_buffer_detach(struct hns3_enet_ring *ring, int i) +{ + hns3_unmap_buffer(ring, &ring->desc_cb[i]); + ring->desc[i].addr = 0; +} + +static void hns3_free_buffer_detach(struct hns3_enet_ring *ring, int i) +{ + struct hns3_desc_cb *cb = &ring->desc_cb[i]; + + if (!ring->desc_cb[i].dma) + return; + + hns3_buffer_detach(ring, i); + hns3_free_buffer(ring, cb); +} + +static void hns3_free_buffers(struct hns3_enet_ring *ring) +{ + int i; + + for (i = 0; i < ring->desc_num; i++) + hns3_free_buffer_detach(ring, i); +} + +/* free desc along with its attached buffer */ +static void hns3_free_desc(struct hns3_enet_ring *ring) +{ + hns3_free_buffers(ring); + + dma_unmap_single(ring_to_dev(ring), ring->desc_dma_addr, + ring->desc_num * sizeof(ring->desc[0]), + DMA_BIDIRECTIONAL); + ring->desc_dma_addr = 0; + kfree(ring->desc); + ring->desc = NULL; +} + +static int hns3_alloc_desc(struct hns3_enet_ring *ring) +{ + int size = ring->desc_num * sizeof(ring->desc[0]); + + ring->desc = kzalloc(size, GFP_KERNEL); + if (!ring->desc) + return -ENOMEM; + + ring->desc_dma_addr = dma_map_single(ring_to_dev(ring), ring->desc, + size, DMA_BIDIRECTIONAL); + if (dma_mapping_error(ring_to_dev(ring), ring->desc_dma_addr)) { + ring->desc_dma_addr = 0; + kfree(ring->desc); + ring->desc = NULL; + return -ENOMEM; + } + + return 0; +} + +static int hns3_reserve_buffer_map(struct hns3_enet_ring *ring, + struct hns3_desc_cb *cb) +{ + int ret; + + ret = hns3_alloc_buffer(ring, cb); + if (ret) + goto out; + + ret = hns3_map_buffer(ring, cb); + if (ret) + goto out_with_buf; + + return 0; + +out_with_buf: + hns3_free_buffers(ring); +out: + return ret; +} + +static int hns3_alloc_buffer_attach(struct hns3_enet_ring *ring, int i) +{ + int ret = hns3_reserve_buffer_map(ring, &ring->desc_cb[i]); + + if (ret) + return ret; + + ring->desc[i].addr = cpu_to_le64(ring->desc_cb[i].dma); + + return 0; +} + +/* Allocate memory for raw pkg, and map with dma */ +static int hns3_alloc_ring_buffers(struct hns3_enet_ring *ring) +{ + int i, j, ret; + + for (i = 0; i < ring->desc_num; i++) { + ret = hns3_alloc_buffer_attach(ring, i); + if (ret) + goto out_buffer_fail; + } + + return 0; + +out_buffer_fail: + for (j = i - 1; j >= 0; j--) + hns3_free_buffer_detach(ring, j); + return ret; +} + +/* detach a in-used buffer and replace with a reserved one */ +static void hns3_replace_buffer(struct hns3_enet_ring *ring, int i, + struct hns3_desc_cb *res_cb) +{ + hns3_map_buffer(ring, &ring->desc_cb[i]); + ring->desc_cb[i] = *res_cb; + ring->desc[i].addr = cpu_to_le64(ring->desc_cb[i].dma); +} + +static void hns3_reuse_buffer(struct hns3_enet_ring *ring, int i) +{ + ring->desc_cb[i].reuse_flag = 0; + ring->desc[i].addr = cpu_to_le64(ring->desc_cb[i].dma + + ring->desc_cb[i].page_offset); +} + +static void hns3_nic_reclaim_one_desc(struct hns3_enet_ring *ring, int *bytes, + int *pkts) +{ + struct hns3_desc_cb *desc_cb = &ring->desc_cb[ring->next_to_clean]; + + (*pkts) += (desc_cb->type == DESC_TYPE_SKB); + (*bytes) += desc_cb->length; + /* desc_cb will be cleaned, after hnae_free_buffer_detach*/ + hns3_free_buffer_detach(ring, ring->next_to_clean); + + ring_ptr_move_fw(ring, next_to_clean); +} + +static int is_valid_clean_head(struct hns3_enet_ring *ring, int h) +{ + int u = ring->next_to_use; + int c = ring->next_to_clean; + + if (unlikely(h > ring->desc_num)) + return 0; + + return u > c ? (h > c && h <= u) : (h > c || h <= u); +} + +int hns3_clean_tx_ring(struct hns3_enet_ring *ring, int budget) +{ + struct net_device *netdev = ring->tqp->handle->kinfo.netdev; + struct netdev_queue *dev_queue; + int bytes, pkts; + int head; + + head = readl_relaxed(ring->tqp->io_base + HNS3_RING_TX_RING_HEAD_REG); + rmb(); /* Make sure head is ready before touch any data */ + + if (is_ring_empty(ring) || head == ring->next_to_clean) + return 0; /* no data to poll */ + + if (!is_valid_clean_head(ring, head)) { + netdev_err(netdev, "wrong head (%d, %d-%d)\n", head, + ring->next_to_use, ring->next_to_clean); + + u64_stats_update_begin(&ring->syncp); + ring->stats.io_err_cnt++; + u64_stats_update_end(&ring->syncp); + return -EIO; + } + + bytes = 0; + pkts = 0; + while (head != ring->next_to_clean && budget) { + hns3_nic_reclaim_one_desc(ring, &bytes, &pkts); + /* Issue prefetch for next Tx descriptor */ + prefetch(&ring->desc_cb[ring->next_to_clean]); + budget--; + } + + ring->tqp_vector->tx_group.total_bytes += bytes; + ring->tqp_vector->tx_group.total_packets += pkts; + + u64_stats_update_begin(&ring->syncp); + ring->stats.tx_bytes += bytes; + ring->stats.tx_pkts += pkts; + u64_stats_update_end(&ring->syncp); + + dev_queue = netdev_get_tx_queue(netdev, ring->tqp->tqp_index); + netdev_tx_completed_queue(dev_queue, pkts, bytes); + + if (unlikely(pkts && netif_carrier_ok(netdev) && + (ring_space(ring) > HNS3_MAX_BD_PER_PKT))) { + /* Make sure that anybody stopping the queue after this + * sees the new next_to_clean. + */ + smp_mb(); + if (netif_tx_queue_stopped(dev_queue)) { + netif_tx_wake_queue(dev_queue); + ring->stats.restart_queue++; + } + } + + return !!budget; +} + +static int hns3_desc_unused(struct hns3_enet_ring *ring) +{ + int ntc = ring->next_to_clean; + int ntu = ring->next_to_use; + + return ((ntc >= ntu) ? 0 : ring->desc_num) + ntc - ntu; +} + +static void +hns3_nic_alloc_rx_buffers(struct hns3_enet_ring *ring, int cleand_count) +{ + struct hns3_desc_cb *desc_cb; + struct hns3_desc_cb res_cbs; + int i, ret; + + for (i = 0; i < cleand_count; i++) { + desc_cb = &ring->desc_cb[ring->next_to_use]; + if (desc_cb->reuse_flag) { + u64_stats_update_begin(&ring->syncp); + ring->stats.reuse_pg_cnt++; + u64_stats_update_end(&ring->syncp); + + hns3_reuse_buffer(ring, ring->next_to_use); + } else { + ret = hns3_reserve_buffer_map(ring, &res_cbs); + if (ret) { + u64_stats_update_begin(&ring->syncp); + ring->stats.sw_err_cnt++; + u64_stats_update_end(&ring->syncp); + + netdev_err(ring->tqp->handle->kinfo.netdev, + "hnae reserve buffer map failed.\n"); + break; + } + hns3_replace_buffer(ring, ring->next_to_use, &res_cbs); + } + + ring_ptr_move_fw(ring, next_to_use); + } + + wmb(); /* Make all data has been write before submit */ + writel_relaxed(i, ring->tqp->io_base + HNS3_RING_RX_RING_HEAD_REG); +} + +/* hns3_nic_get_headlen - determine size of header for LRO/GRO + * @data: pointer to the start of the headers + * @max: total length of section to find headers in + * + * This function is meant to determine the length of headers that will + * be recognized by hardware for LRO, GRO, and RSC offloads. The main + * motivation of doing this is to only perform one pull for IPv4 TCP + * packets so that we can do basic things like calculating the gso_size + * based on the average data per packet. + */ +static unsigned int hns3_nic_get_headlen(unsigned char *data, u32 flag, + unsigned int max_size) +{ + unsigned char *network; + u8 hlen; + + /* This should never happen, but better safe than sorry */ + if (max_size < ETH_HLEN) + return max_size; + + /* Initialize network frame pointer */ + network = data; + + /* Set first protocol and move network header forward */ + network += ETH_HLEN; + + /* Handle any vlan tag if present */ + if (hnae_get_field(flag, HNS3_RXD_VLAN_M, HNS3_RXD_VLAN_S) + == HNS3_RX_FLAG_VLAN_PRESENT) { + if ((typeof(max_size))(network - data) > (max_size - VLAN_HLEN)) + return max_size; + + network += VLAN_HLEN; + } + + /* Handle L3 protocols */ + if (hnae_get_field(flag, HNS3_RXD_L3ID_M, HNS3_RXD_L3ID_S) + == HNS3_RX_FLAG_L3ID_IPV4) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct iphdr))) + return max_size; + + /* Access ihl as a u8 to avoid unaligned access on ia64 */ + hlen = (network[0] & 0x0F) << 2; + + /* Verify hlen meets minimum size requirements */ + if (hlen < sizeof(struct iphdr)) + return network - data; + + /* Record next protocol if header is present */ + } else if (hnae_get_field(flag, HNS3_RXD_L3ID_M, HNS3_RXD_L3ID_S) + == HNS3_RX_FLAG_L3ID_IPV6) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct ipv6hdr))) + return max_size; + + /* Record next protocol */ + hlen = sizeof(struct ipv6hdr); + } else { + return network - data; + } + + /* Relocate pointer to start of L4 header */ + network += hlen; + + /* Finally sort out TCP/UDP */ + if (hnae_get_field(flag, HNS3_RXD_L4ID_M, HNS3_RXD_L4ID_S) + == HNS3_RX_FLAG_L4ID_TCP) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct tcphdr))) + return max_size; + + /* Access doff as a u8 to avoid unaligned access on ia64 */ + hlen = (network[12] & 0xF0) >> 2; + + /* Verify hlen meets minimum size requirements */ + if (hlen < sizeof(struct tcphdr)) + return network - data; + + network += hlen; + } else if (hnae_get_field(flag, HNS3_RXD_L4ID_M, HNS3_RXD_L4ID_S) + == HNS3_RX_FLAG_L4ID_UDP) { + if ((typeof(max_size))(network - data) > + (max_size - sizeof(struct udphdr))) + return max_size; + + network += sizeof(struct udphdr); + } + + /* If everything has gone correctly network should be the + * data section of the packet and will be the end of the header. + * If not then it probably represents the end of the last recognized + * header. + */ + if ((typeof(max_size))(network - data) < max_size) + return network - data; + else + return max_size; +} + +static void hns3_nic_reuse_page(struct sk_buff *skb, int i, + struct hns3_enet_ring *ring, int pull_len, + struct hns3_desc_cb *desc_cb) +{ + struct hns3_desc *desc; + int truesize, size; + int last_offset; + bool twobufs; + + twobufs = ((PAGE_SIZE < 8192) && + hnae_buf_size(ring) == HNS3_BUFFER_SIZE_2048); + + desc = &ring->desc[ring->next_to_clean]; + size = le16_to_cpu(desc->rx.size); + + if (twobufs) { + truesize = hnae_buf_size(ring); + } else { + truesize = ALIGN(size, L1_CACHE_BYTES); + last_offset = hnae_page_size(ring) - hnae_buf_size(ring); + } + + skb_add_rx_frag(skb, i, desc_cb->priv, desc_cb->page_offset + pull_len, + size - pull_len, truesize - pull_len); + + /* Avoid re-using remote pages,flag default unreuse */ + if (unlikely(page_to_nid(desc_cb->priv) != numa_node_id())) + return; + + if (twobufs) { + /* If we are only owner of page we can reuse it */ + if (likely(page_count(desc_cb->priv) == 1)) { + /* Flip page offset to other buffer */ + desc_cb->page_offset ^= truesize; + + desc_cb->reuse_flag = 1; + /* bump ref count on page before it is given*/ + get_page(desc_cb->priv); + } + return; + } + + /* Move offset up to the next cache line */ + desc_cb->page_offset += truesize; + + if (desc_cb->page_offset <= last_offset) { + desc_cb->reuse_flag = 1; + /* Bump ref count on page before it is given*/ + get_page(desc_cb->priv); + } +} + +static void hns3_rx_checksum(struct hns3_enet_ring *ring, struct sk_buff *skb, + struct hns3_desc *desc) +{ + struct net_device *netdev = ring->tqp->handle->kinfo.netdev; + int l3_type, l4_type; + u32 bd_base_info; + int ol4_type; + u32 l234info; + + bd_base_info = le32_to_cpu(desc->rx.bd_base_info); + l234info = le32_to_cpu(desc->rx.l234_info); + + skb->ip_summed = CHECKSUM_NONE; + + skb_checksum_none_assert(skb); + + if (!(netdev->features & NETIF_F_RXCSUM)) + return; + + /* check if hardware has done checksum */ + if (!hnae_get_bit(bd_base_info, HNS3_RXD_L3L4P_B)) + return; + + if (unlikely(hnae_get_bit(l234info, HNS3_RXD_L3E_B) || + hnae_get_bit(l234info, HNS3_RXD_L4E_B) || + hnae_get_bit(l234info, HNS3_RXD_OL3E_B) || + hnae_get_bit(l234info, HNS3_RXD_OL4E_B))) { + netdev_err(netdev, "L3/L4 error pkt\n"); + u64_stats_update_begin(&ring->syncp); + ring->stats.l3l4_csum_err++; + u64_stats_update_end(&ring->syncp); + + return; + } + + l3_type = hnae_get_field(l234info, HNS3_RXD_L3ID_M, + HNS3_RXD_L3ID_S); + l4_type = hnae_get_field(l234info, HNS3_RXD_L4ID_M, + HNS3_RXD_L4ID_S); + + ol4_type = hnae_get_field(l234info, HNS3_RXD_OL4ID_M, HNS3_RXD_OL4ID_S); + switch (ol4_type) { + case HNS3_OL4_TYPE_MAC_IN_UDP: + case HNS3_OL4_TYPE_NVGRE: + skb->csum_level = 1; + case HNS3_OL4_TYPE_NO_TUN: + /* Can checksum ipv4 or ipv6 + UDP/TCP/SCTP packets */ + if (l3_type == HNS3_L3_TYPE_IPV4 || + (l3_type == HNS3_L3_TYPE_IPV6 && + (l4_type == HNS3_L4_TYPE_UDP || + l4_type == HNS3_L4_TYPE_TCP || + l4_type == HNS3_L4_TYPE_SCTP))) + skb->ip_summed = CHECKSUM_UNNECESSARY; + break; + } +} + +static int hns3_handle_rx_bd(struct hns3_enet_ring *ring, + struct sk_buff **out_skb, int *out_bnum) +{ + struct net_device *netdev = ring->tqp->handle->kinfo.netdev; + struct hns3_desc_cb *desc_cb; + struct hns3_desc *desc; + struct sk_buff *skb; + unsigned char *va; + u32 bd_base_info; + int pull_len; + u32 l234info; + int length; + int bnum; + + desc = &ring->desc[ring->next_to_clean]; + desc_cb = &ring->desc_cb[ring->next_to_clean]; + + prefetch(desc); + + length = le16_to_cpu(desc->rx.pkt_len); + bd_base_info = le32_to_cpu(desc->rx.bd_base_info); + l234info = le32_to_cpu(desc->rx.l234_info); + + /* Check valid BD */ + if (!hnae_get_bit(bd_base_info, HNS3_RXD_VLD_B)) + return -EFAULT; + + va = (unsigned char *)desc_cb->buf + desc_cb->page_offset; + + /* Prefetch first cache line of first page + * Idea is to cache few bytes of the header of the packet. Our L1 Cache + * line size is 64B so need to prefetch twice to make it 128B. But in + * actual we can have greater size of caches with 128B Level 1 cache + * lines. In such a case, single fetch would suffice to cache in the + * relevant part of the header. + */ + prefetch(va); +#if L1_CACHE_BYTES < 128 + prefetch(va + L1_CACHE_BYTES); +#endif + + skb = *out_skb = napi_alloc_skb(&ring->tqp_vector->napi, + HNS3_RX_HEAD_SIZE); + if (unlikely(!skb)) { + netdev_err(netdev, "alloc rx skb fail\n"); + + u64_stats_update_begin(&ring->syncp); + ring->stats.sw_err_cnt++; + u64_stats_update_end(&ring->syncp); + + return -ENOMEM; + } + + prefetchw(skb->data); + + bnum = 1; + if (length <= HNS3_RX_HEAD_SIZE) { + memcpy(__skb_put(skb, length), va, ALIGN(length, sizeof(long))); + + /* We can reuse buffer as-is, just make sure it is local */ + if (likely(page_to_nid(desc_cb->priv) == numa_node_id())) + desc_cb->reuse_flag = 1; + else /* This page cannot be reused so discard it */ + put_page(desc_cb->priv); + + ring_ptr_move_fw(ring, next_to_clean); + } else { + u64_stats_update_begin(&ring->syncp); + ring->stats.seg_pkt_cnt++; + u64_stats_update_end(&ring->syncp); + + pull_len = hns3_nic_get_headlen(va, l234info, + HNS3_RX_HEAD_SIZE); + memcpy(__skb_put(skb, pull_len), va, + ALIGN(pull_len, sizeof(long))); + + hns3_nic_reuse_page(skb, 0, ring, pull_len, desc_cb); + ring_ptr_move_fw(ring, next_to_clean); + + while (!hnae_get_bit(bd_base_info, HNS3_RXD_FE_B)) { + desc = &ring->desc[ring->next_to_clean]; + desc_cb = &ring->desc_cb[ring->next_to_clean]; + bd_base_info = le32_to_cpu(desc->rx.bd_base_info); + hns3_nic_reuse_page(skb, bnum, ring, 0, desc_cb); + ring_ptr_move_fw(ring, next_to_clean); + bnum++; + } + } + + *out_bnum = bnum; + + if (unlikely(!hnae_get_bit(bd_base_info, HNS3_RXD_VLD_B))) { + netdev_err(netdev, "no valid bd,%016llx,%016llx\n", + ((u64 *)desc)[0], ((u64 *)desc)[1]); + u64_stats_update_begin(&ring->syncp); + ring->stats.non_vld_descs++; + u64_stats_update_end(&ring->syncp); + + dev_kfree_skb_any(skb); + return -EINVAL; + } + + if (unlikely((!desc->rx.pkt_len) || + hnae_get_bit(l234info, HNS3_RXD_TRUNCAT_B))) { + netdev_err(netdev, "truncated pkt\n"); + u64_stats_update_begin(&ring->syncp); + ring->stats.err_pkt_len++; + u64_stats_update_end(&ring->syncp); + + dev_kfree_skb_any(skb); + return -EFAULT; + } + + if (unlikely(hnae_get_bit(l234info, HNS3_RXD_L2E_B))) { + netdev_err(netdev, "L2 error pkt\n"); + u64_stats_update_begin(&ring->syncp); + ring->stats.l2_err++; + u64_stats_update_end(&ring->syncp); + + dev_kfree_skb_any(skb); + return -EFAULT; + } + + u64_stats_update_begin(&ring->syncp); + ring->stats.rx_pkts++; + ring->stats.rx_bytes += skb->len; + u64_stats_update_end(&ring->syncp); + + ring->tqp_vector->rx_group.total_bytes += skb->len; + + hns3_rx_checksum(ring, skb, desc); + return 0; +} + +static int hns3_clean_rx_ring(struct hns3_enet_ring *ring, int budget) +{ +#define RCB_NOF_ALLOC_RX_BUFF_ONCE 16 + struct net_device *netdev = ring->tqp->handle->kinfo.netdev; + int recv_pkts, recv_bds, clean_count, err; + int unused_count = hns3_desc_unused(ring); + struct sk_buff *skb = NULL; + int num, bnum = 0; + + num = readl_relaxed(ring->tqp->io_base + HNS3_RING_RX_RING_FBDNUM_REG); + rmb(); /* Make sure num taken effect before the other data is touched */ + + recv_pkts = 0, recv_bds = 0, clean_count = 0; + num -= unused_count; + + while (recv_pkts < budget && recv_bds < num) { + /* Reuse or realloc buffers */ + if (clean_count + unused_count >= RCB_NOF_ALLOC_RX_BUFF_ONCE) { + hns3_nic_alloc_rx_buffers(ring, + clean_count + unused_count); + clean_count = 0; + unused_count = hns3_desc_unused(ring); + } + + /* Poll one pkt */ + err = hns3_handle_rx_bd(ring, &skb, &bnum); + if (unlikely(!skb)) /* This fault cannot be repaired */ + goto out; + + recv_bds += bnum; + clean_count += bnum; + if (unlikely(err)) { /* Do jump the err */ + recv_pkts++; + continue; + } + + /* Do update ip stack process */ + skb->protocol = eth_type_trans(skb, netdev); + (void)napi_gro_receive(&ring->tqp_vector->napi, skb); + + recv_pkts++; + } + +out: + /* Make all data has been write before submit */ + if (clean_count + unused_count > 0) + hns3_nic_alloc_rx_buffers(ring, + clean_count + unused_count); + + return recv_pkts; +} + +static bool hns3_get_new_int_gl(struct hns3_enet_ring_group *ring_group) +{ +#define HNS3_RX_ULTRA_PACKET_RATE 40000 + enum hns3_flow_level_range new_flow_level; + struct hns3_enet_tqp_vector *tqp_vector; + int packets_per_secs; + int bytes_per_usecs; + u16 new_int_gl; + int usecs; + + if (!ring_group->int_gl) + return false; + + if (ring_group->total_packets == 0) { + ring_group->int_gl = HNS3_INT_GL_50K; + ring_group->flow_level = HNS3_FLOW_LOW; + return true; + } + + /* Simple throttlerate management + * 0-10MB/s lower (50000 ints/s) + * 10-20MB/s middle (20000 ints/s) + * 20-1249MB/s high (18000 ints/s) + * > 40000pps ultra (8000 ints/s) + */ + new_flow_level = ring_group->flow_level; + new_int_gl = ring_group->int_gl; + tqp_vector = ring_group->ring->tqp_vector; + usecs = (ring_group->int_gl << 1); + bytes_per_usecs = ring_group->total_bytes / usecs; + /* 1000000 microseconds */ + packets_per_secs = ring_group->total_packets * 1000000 / usecs; + + switch (new_flow_level) { + case HNS3_FLOW_LOW: + if (bytes_per_usecs > 10) + new_flow_level = HNS3_FLOW_MID; + break; + case HNS3_FLOW_MID: + if (bytes_per_usecs > 20) + new_flow_level = HNS3_FLOW_HIGH; + else if (bytes_per_usecs <= 10) + new_flow_level = HNS3_FLOW_LOW; + break; + case HNS3_FLOW_HIGH: + case HNS3_FLOW_ULTRA: + default: + if (bytes_per_usecs <= 20) + new_flow_level = HNS3_FLOW_MID; + break; + } + + if ((packets_per_secs > HNS3_RX_ULTRA_PACKET_RATE) && + (&tqp_vector->rx_group == ring_group)) + new_flow_level = HNS3_FLOW_ULTRA; + + switch (new_flow_level) { + case HNS3_FLOW_LOW: + new_int_gl = HNS3_INT_GL_50K; + break; + case HNS3_FLOW_MID: + new_int_gl = HNS3_INT_GL_20K; + break; + case HNS3_FLOW_HIGH: + new_int_gl = HNS3_INT_GL_18K; + break; + case HNS3_FLOW_ULTRA: + new_int_gl = HNS3_INT_GL_8K; + break; + default: + break; + } + + ring_group->total_bytes = 0; + ring_group->total_packets = 0; + ring_group->flow_level = new_flow_level; + if (new_int_gl != ring_group->int_gl) { + ring_group->int_gl = new_int_gl; + return true; + } + return false; +} + +static void hns3_update_new_int_gl(struct hns3_enet_tqp_vector *tqp_vector) +{ + u16 rx_int_gl, tx_int_gl; + bool rx, tx; + + rx = hns3_get_new_int_gl(&tqp_vector->rx_group); + tx = hns3_get_new_int_gl(&tqp_vector->tx_group); + rx_int_gl = tqp_vector->rx_group.int_gl; + tx_int_gl = tqp_vector->tx_group.int_gl; + if (rx && tx) { + if (rx_int_gl > tx_int_gl) { + tqp_vector->tx_group.int_gl = rx_int_gl; + tqp_vector->tx_group.flow_level = + tqp_vector->rx_group.flow_level; + hns3_set_vector_coalesc_gl(tqp_vector, rx_int_gl); + } else { + tqp_vector->rx_group.int_gl = tx_int_gl; + tqp_vector->rx_group.flow_level = + tqp_vector->tx_group.flow_level; + hns3_set_vector_coalesc_gl(tqp_vector, tx_int_gl); + } + } +} + +static int hns3_nic_common_poll(struct napi_struct *napi, int budget) +{ + struct hns3_enet_ring *ring; + int rx_pkt_total = 0; + + struct hns3_enet_tqp_vector *tqp_vector = + container_of(napi, struct hns3_enet_tqp_vector, napi); + bool clean_complete = true; + int rx_budget; + + /* Since the actual Tx work is minimal, we can give the Tx a larger + * budget and be more aggressive about cleaning up the Tx descriptors. + */ + hns3_for_each_ring(ring, tqp_vector->tx_group) { + if (!hns3_clean_tx_ring(ring, budget)) + clean_complete = false; + } + + /* make sure rx ring budget not smaller than 1 */ + rx_budget = max(budget / tqp_vector->num_tqps, 1); + + hns3_for_each_ring(ring, tqp_vector->rx_group) { + int rx_cleaned = hns3_clean_rx_ring(ring, rx_budget); + + if (rx_cleaned >= rx_budget) + clean_complete = false; + + rx_pkt_total += rx_cleaned; + } + + tqp_vector->rx_group.total_packets += rx_pkt_total; + + if (!clean_complete) + return budget; + + napi_complete(napi); + hns3_update_new_int_gl(tqp_vector); + hns3_mask_vector_irq(tqp_vector, 1); + + return rx_pkt_total; +} + +static int hns3_get_vector_ring_chain(struct hns3_enet_tqp_vector *tqp_vector, + struct hnae3_ring_chain_node *head) +{ + struct pci_dev *pdev = tqp_vector->handle->pdev; + struct hnae3_ring_chain_node *cur_chain = head; + struct hnae3_ring_chain_node *chain; + struct hns3_enet_ring *tx_ring; + struct hns3_enet_ring *rx_ring; + + tx_ring = tqp_vector->tx_group.ring; + if (tx_ring) { + cur_chain->tqp_index = tx_ring->tqp->tqp_index; + hnae_set_bit(cur_chain->flag, HNAE3_RING_TYPE_B, + HNAE3_RING_TYPE_TX); + + cur_chain->next = NULL; + + while (tx_ring->next) { + tx_ring = tx_ring->next; + + chain = devm_kzalloc(&pdev->dev, sizeof(*chain), + GFP_KERNEL); + if (!chain) + return -ENOMEM; + + cur_chain->next = chain; + chain->tqp_index = tx_ring->tqp->tqp_index; + hnae_set_bit(chain->flag, HNAE3_RING_TYPE_B, + HNAE3_RING_TYPE_TX); + + cur_chain = chain; + } + } + + rx_ring = tqp_vector->rx_group.ring; + if (!tx_ring && rx_ring) { + cur_chain->next = NULL; + cur_chain->tqp_index = rx_ring->tqp->tqp_index; + hnae_set_bit(cur_chain->flag, HNAE3_RING_TYPE_B, + HNAE3_RING_TYPE_RX); + + rx_ring = rx_ring->next; + } + + while (rx_ring) { + chain = devm_kzalloc(&pdev->dev, sizeof(*chain), GFP_KERNEL); + if (!chain) + return -ENOMEM; + + cur_chain->next = chain; + chain->tqp_index = rx_ring->tqp->tqp_index; + hnae_set_bit(chain->flag, HNAE3_RING_TYPE_B, + HNAE3_RING_TYPE_RX); + cur_chain = chain; + + rx_ring = rx_ring->next; + } + + return 0; +} + +static void hns3_free_vector_ring_chain(struct hns3_enet_tqp_vector *tqp_vector, + struct hnae3_ring_chain_node *head) +{ + struct pci_dev *pdev = tqp_vector->handle->pdev; + struct hnae3_ring_chain_node *chain_tmp, *chain; + + chain = head->next; + + while (chain) { + chain_tmp = chain->next; + devm_kfree(&pdev->dev, chain); + chain = chain_tmp; + } +} + +static void hns3_add_ring_to_group(struct hns3_enet_ring_group *group, + struct hns3_enet_ring *ring) +{ + ring->next = group->ring; + group->ring = ring; + + group->count++; +} + +static int hns3_nic_init_vector_data(struct hns3_nic_priv *priv) +{ + struct hnae3_ring_chain_node vector_ring_chain; + struct hnae3_handle *h = priv->ae_handle; + struct hns3_enet_tqp_vector *tqp_vector; + struct hnae3_vector_info *vector; + struct pci_dev *pdev = h->pdev; + u16 tqp_num = h->kinfo.num_tqps; + u16 vector_num; + int ret = 0; + u16 i; + + /* RSS size, cpu online and vector_num should be the same */ + /* Should consider 2p/4p later */ + vector_num = min_t(u16, num_online_cpus(), tqp_num); + vector = devm_kcalloc(&pdev->dev, vector_num, sizeof(*vector), + GFP_KERNEL); + if (!vector) + return -ENOMEM; + + vector_num = h->ae_algo->ops->get_vector(h, vector_num, vector); + + priv->vector_num = vector_num; + priv->tqp_vector = (struct hns3_enet_tqp_vector *) + devm_kcalloc(&pdev->dev, vector_num, sizeof(*priv->tqp_vector), + GFP_KERNEL); + if (!priv->tqp_vector) + return -ENOMEM; + + for (i = 0; i < tqp_num; i++) { + u16 vector_i = i % vector_num; + + tqp_vector = &priv->tqp_vector[vector_i]; + + hns3_add_ring_to_group(&tqp_vector->tx_group, + priv->ring_data[i].ring); + + hns3_add_ring_to_group(&tqp_vector->rx_group, + priv->ring_data[i + tqp_num].ring); + + tqp_vector->idx = vector_i; + tqp_vector->mask_addr = vector[vector_i].io_addr; + tqp_vector->vector_irq = vector[vector_i].vector; + tqp_vector->num_tqps++; + + priv->ring_data[i].ring->tqp_vector = tqp_vector; + priv->ring_data[i + tqp_num].ring->tqp_vector = tqp_vector; + } + + for (i = 0; i < vector_num; i++) { + tqp_vector = &priv->tqp_vector[i]; + + tqp_vector->rx_group.total_bytes = 0; + tqp_vector->rx_group.total_packets = 0; + tqp_vector->tx_group.total_bytes = 0; + tqp_vector->tx_group.total_packets = 0; + hns3_vector_gl_rl_init(tqp_vector); + tqp_vector->handle = h; + + ret = hns3_get_vector_ring_chain(tqp_vector, + &vector_ring_chain); + if (ret) + goto out; + + ret = h->ae_algo->ops->map_ring_to_vector(h, + tqp_vector->vector_irq, &vector_ring_chain); + if (ret) + goto out; + + hns3_free_vector_ring_chain(tqp_vector, &vector_ring_chain); + + netif_napi_add(priv->netdev, &tqp_vector->napi, + hns3_nic_common_poll, NAPI_POLL_WEIGHT); + } + +out: + devm_kfree(&pdev->dev, vector); + return ret; +} + +static int hns3_nic_uninit_vector_data(struct hns3_nic_priv *priv) +{ + struct hnae3_ring_chain_node vector_ring_chain; + struct hnae3_handle *h = priv->ae_handle; + struct hns3_enet_tqp_vector *tqp_vector; + struct pci_dev *pdev = h->pdev; + int i, ret; + + for (i = 0; i < priv->vector_num; i++) { + tqp_vector = &priv->tqp_vector[i]; + + ret = hns3_get_vector_ring_chain(tqp_vector, + &vector_ring_chain); + if (ret) + return ret; + + ret = h->ae_algo->ops->unmap_ring_from_vector(h, + tqp_vector->vector_irq, &vector_ring_chain); + if (ret) + return ret; + + hns3_free_vector_ring_chain(tqp_vector, &vector_ring_chain); + + if (priv->tqp_vector[i].irq_init_flag == HNS3_VECTOR_INITED) { + (void)irq_set_affinity_hint( + priv->tqp_vector[i].vector_irq, + NULL); + devm_free_irq(&pdev->dev, + priv->tqp_vector[i].vector_irq, + &priv->tqp_vector[i]); + } + + priv->ring_data[i].ring->irq_init_flag = HNS3_VECTOR_NOT_INITED; + + netif_napi_del(&priv->tqp_vector[i].napi); + } + + devm_kfree(&pdev->dev, priv->tqp_vector); + + return 0; +} + +static int hns3_ring_get_cfg(struct hnae3_queue *q, struct hns3_nic_priv *priv, + int ring_type) +{ + struct hns3_nic_ring_data *ring_data = priv->ring_data; + int queue_num = priv->ae_handle->kinfo.num_tqps; + struct pci_dev *pdev = priv->ae_handle->pdev; + struct hns3_enet_ring *ring; + + ring = devm_kzalloc(&pdev->dev, sizeof(*ring), GFP_KERNEL); + if (!ring) + return -ENOMEM; + + if (ring_type == HNAE3_RING_TYPE_TX) { + ring_data[q->tqp_index].ring = ring; + ring->io_base = (u8 __iomem *)q->io_base + HNS3_TX_REG_OFFSET; + } else { + ring_data[q->tqp_index + queue_num].ring = ring; + ring->io_base = q->io_base; + } + + hnae_set_bit(ring->flag, HNAE3_RING_TYPE_B, ring_type); + + ring_data[q->tqp_index].queue_index = q->tqp_index; + + ring->tqp = q; + ring->desc = NULL; + ring->desc_cb = NULL; + ring->dev = priv->dev; + ring->desc_dma_addr = 0; + ring->buf_size = q->buf_size; + ring->desc_num = q->desc_num; + ring->next_to_use = 0; + ring->next_to_clean = 0; + + return 0; +} + +static int hns3_queue_to_ring(struct hnae3_queue *tqp, + struct hns3_nic_priv *priv) +{ + int ret; + + ret = hns3_ring_get_cfg(tqp, priv, HNAE3_RING_TYPE_TX); + if (ret) + return ret; + + ret = hns3_ring_get_cfg(tqp, priv, HNAE3_RING_TYPE_RX); + if (ret) + return ret; + + return 0; +} + +static int hns3_get_ring_config(struct hns3_nic_priv *priv) +{ + struct hnae3_handle *h = priv->ae_handle; + struct pci_dev *pdev = h->pdev; + int i, ret; + + priv->ring_data = devm_kzalloc(&pdev->dev, h->kinfo.num_tqps * + sizeof(*priv->ring_data) * 2, + GFP_KERNEL); + if (!priv->ring_data) + return -ENOMEM; + + for (i = 0; i < h->kinfo.num_tqps; i++) { + ret = hns3_queue_to_ring(h->kinfo.tqp[i], priv); + if (ret) + goto err; + } + + return 0; +err: + devm_kfree(&pdev->dev, priv->ring_data); + return ret; +} + +static int hns3_alloc_ring_memory(struct hns3_enet_ring *ring) +{ + int ret; + + if (ring->desc_num <= 0 || ring->buf_size <= 0) + return -EINVAL; + + ring->desc_cb = kcalloc(ring->desc_num, sizeof(ring->desc_cb[0]), + GFP_KERNEL); + if (!ring->desc_cb) { + ret = -ENOMEM; + goto out; + } + + ret = hns3_alloc_desc(ring); + if (ret) + goto out_with_desc_cb; + + if (!HNAE3_IS_TX_RING(ring)) { + ret = hns3_alloc_ring_buffers(ring); + if (ret) + goto out_with_desc; + } + + return 0; + +out_with_desc: + hns3_free_desc(ring); +out_with_desc_cb: + kfree(ring->desc_cb); + ring->desc_cb = NULL; +out: + return ret; +} + +static void hns3_fini_ring(struct hns3_enet_ring *ring) +{ + hns3_free_desc(ring); + kfree(ring->desc_cb); + ring->desc_cb = NULL; + ring->next_to_clean = 0; + ring->next_to_use = 0; +} + +int hns3_buf_size2type(u32 buf_size) +{ + int bd_size_type; + + switch (buf_size) { + case 512: + bd_size_type = HNS3_BD_SIZE_512_TYPE; + break; + case 1024: + bd_size_type = HNS3_BD_SIZE_1024_TYPE; + break; + case 2048: + bd_size_type = HNS3_BD_SIZE_2048_TYPE; + break; + case 4096: + bd_size_type = HNS3_BD_SIZE_4096_TYPE; + break; + default: + bd_size_type = HNS3_BD_SIZE_2048_TYPE; + } + + return bd_size_type; +} + +static void hns3_init_ring_hw(struct hns3_enet_ring *ring) +{ + dma_addr_t dma = ring->desc_dma_addr; + struct hnae3_queue *q = ring->tqp; + + if (!HNAE3_IS_TX_RING(ring)) { + hns3_write_dev(q, HNS3_RING_RX_RING_BASEADDR_L_REG, + (u32)dma); + hns3_write_dev(q, HNS3_RING_RX_RING_BASEADDR_H_REG, + (u32)((dma >> 31) >> 1)); + + hns3_write_dev(q, HNS3_RING_RX_RING_BD_LEN_REG, + hns3_buf_size2type(ring->buf_size)); + hns3_write_dev(q, HNS3_RING_RX_RING_BD_NUM_REG, + ring->desc_num / 8 - 1); + + } else { + hns3_write_dev(q, HNS3_RING_TX_RING_BASEADDR_L_REG, + (u32)dma); + hns3_write_dev(q, HNS3_RING_TX_RING_BASEADDR_H_REG, + (u32)((dma >> 31) >> 1)); + + hns3_write_dev(q, HNS3_RING_TX_RING_BD_LEN_REG, + hns3_buf_size2type(ring->buf_size)); + hns3_write_dev(q, HNS3_RING_TX_RING_BD_NUM_REG, + ring->desc_num / 8 - 1); + } +} + +static int hns3_init_all_ring(struct hns3_nic_priv *priv) +{ + struct hnae3_handle *h = priv->ae_handle; + int ring_num = h->kinfo.num_tqps * 2; + int i, j; + int ret; + + for (i = 0; i < ring_num; i++) { + ret = hns3_alloc_ring_memory(priv->ring_data[i].ring); + if (ret) { + dev_err(priv->dev, + "Alloc ring memory fail! ret=%d\n", ret); + goto out_when_alloc_ring_memory; + } + + hns3_init_ring_hw(priv->ring_data[i].ring); + + u64_stats_init(&priv->ring_data[i].ring->syncp); + } + + return 0; + +out_when_alloc_ring_memory: + for (j = i - 1; j >= 0; j--) + hns3_fini_ring(priv->ring_data[i].ring); + + return -ENOMEM; +} + +static int hns3_uninit_all_ring(struct hns3_nic_priv *priv) +{ + struct hnae3_handle *h = priv->ae_handle; + int i; + + for (i = 0; i < h->kinfo.num_tqps; i++) { + if (h->ae_algo->ops->reset_queue) + h->ae_algo->ops->reset_queue(h, i); + + hns3_fini_ring(priv->ring_data[i].ring); + hns3_fini_ring(priv->ring_data[i + h->kinfo.num_tqps].ring); + } + + return 0; +} + +/* Set mac addr if it is configured. or leave it to the AE driver */ +static void hns3_init_mac_addr(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + u8 mac_addr_temp[ETH_ALEN]; + + if (h->ae_algo->ops->get_mac_addr) { + h->ae_algo->ops->get_mac_addr(h, mac_addr_temp); + ether_addr_copy(netdev->dev_addr, mac_addr_temp); + } + + /* Check if the MAC address is valid, if not get a random one */ + if (!is_valid_ether_addr(netdev->dev_addr)) { + eth_hw_addr_random(netdev); + dev_warn(priv->dev, "using random MAC address %pM\n", + netdev->dev_addr); + /* Also copy this new MAC address into hdev */ + if (h->ae_algo->ops->set_mac_addr) + h->ae_algo->ops->set_mac_addr(h, netdev->dev_addr); + } +} + +static void hns3_nic_set_priv_ops(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + + if ((netdev->features & NETIF_F_TSO) || + (netdev->features & NETIF_F_TSO6)) { + priv->ops.fill_desc = hns3_fill_desc_tso; + priv->ops.maybe_stop_tx = hns3_nic_maybe_stop_tso; + } else { + priv->ops.fill_desc = hns3_fill_desc; + priv->ops.maybe_stop_tx = hns3_nic_maybe_stop_tx; + } +} + +static int hns3_client_init(struct hnae3_handle *handle) +{ + struct pci_dev *pdev = handle->pdev; + struct hns3_nic_priv *priv; + struct net_device *netdev; + int ret; + + netdev = alloc_etherdev_mq(sizeof(struct hns3_nic_priv), + handle->kinfo.num_tqps); + if (!netdev) + return -ENOMEM; + + priv = netdev_priv(netdev); + priv->dev = &pdev->dev; + priv->netdev = netdev; + priv->ae_handle = handle; + + handle->kinfo.netdev = netdev; + handle->priv = (void *)priv; + + hns3_init_mac_addr(netdev); + + hns3_set_default_feature(netdev); + + netdev->watchdog_timeo = HNS3_TX_TIMEOUT; + netdev->priv_flags |= IFF_UNICAST_FLT; + netdev->netdev_ops = &hns3_nic_netdev_ops; + SET_NETDEV_DEV(netdev, &pdev->dev); + hns3_ethtool_set_ops(netdev); + hns3_nic_set_priv_ops(netdev); + + /* Carrier off reporting is important to ethtool even BEFORE open */ + netif_carrier_off(netdev); + + ret = hns3_get_ring_config(priv); + if (ret) { + ret = -ENOMEM; + goto out_get_ring_cfg; + } + + ret = hns3_nic_init_vector_data(priv); + if (ret) { + ret = -ENOMEM; + goto out_init_vector_data; + } + + ret = hns3_init_all_ring(priv); + if (ret) { + ret = -ENOMEM; + goto out_init_ring_data; + } + + ret = register_netdev(netdev); + if (ret) { + dev_err(priv->dev, "probe register netdev fail!\n"); + goto out_reg_netdev_fail; + } + + /* MTU range: (ETH_MIN_MTU(kernel default) - 9706) */ + netdev->max_mtu = HNS3_MAX_MTU - (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); + + return ret; + +out_reg_netdev_fail: +out_init_ring_data: + (void)hns3_nic_uninit_vector_data(priv); + priv->ring_data = NULL; +out_init_vector_data: +out_get_ring_cfg: + priv->ae_handle = NULL; + free_netdev(netdev); + return ret; +} + +static void hns3_client_uninit(struct hnae3_handle *handle, bool reset) +{ + struct net_device *netdev = handle->kinfo.netdev; + struct hns3_nic_priv *priv = netdev_priv(netdev); + int ret; + + if (netdev->reg_state != NETREG_UNINITIALIZED) + unregister_netdev(netdev); + + ret = hns3_nic_uninit_vector_data(priv); + if (ret) + netdev_err(netdev, "uninit vector error\n"); + + ret = hns3_uninit_all_ring(priv); + if (ret) + netdev_err(netdev, "uninit ring error\n"); + + priv->ring_data = NULL; + + free_netdev(netdev); +} + +static void hns3_link_status_change(struct hnae3_handle *handle, bool linkup) +{ + struct net_device *netdev = handle->kinfo.netdev; + + if (!netdev) + return; + + if (linkup) { + netif_carrier_on(netdev); + netif_tx_wake_all_queues(netdev); + netdev_info(netdev, "link up\n"); + } else { + netif_carrier_off(netdev); + netif_tx_stop_all_queues(netdev); + netdev_info(netdev, "link down\n"); + } +} + +const struct hnae3_client_ops client_ops = { + .init_instance = hns3_client_init, + .uninit_instance = hns3_client_uninit, + .link_status_change = hns3_link_status_change, +}; + +/* hns3_init_module - Driver registration routine + * hns3_init_module is the first routine called when the driver is + * loaded. All it does is register with the PCI subsystem. + */ +static int __init hns3_init_module(void) +{ + int ret; + + pr_info("%s: %s - version\n", hns3_driver_name, hns3_driver_string); + pr_info("%s: %s\n", hns3_driver_name, hns3_copyright); + + client.type = HNAE3_CLIENT_KNIC; + snprintf(client.name, HNAE3_CLIENT_NAME_LENGTH - 1, "%s", + hns3_driver_name); + + client.ops = &client_ops; + + ret = hnae3_register_client(&client); + if (ret) + return ret; + + ret = pci_register_driver(&hns3_driver); + if (ret) + hnae3_unregister_client(&client); + + return ret; +} +module_init(hns3_init_module); + +/* hns3_exit_module - Driver exit cleanup routine + * hns3_exit_module is called just before the driver is removed + * from memory. + */ +static void __exit hns3_exit_module(void) +{ + pci_unregister_driver(&hns3_driver); + hnae3_unregister_client(&client); +} +module_exit(hns3_exit_module); + +MODULE_DESCRIPTION("HNS3: Hisilicon Ethernet Driver"); +MODULE_AUTHOR("Huawei Tech. Co., Ltd."); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("pci:hns-nic"); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.h new file mode 100644 index 000000000000..7e8746189747 --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.h @@ -0,0 +1,593 @@ +/* + * Copyright (c) 2016 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __HNS3_ENET_H +#define __HNS3_ENET_H + +#include "hnae3.h" + +extern const char hns3_driver_version[]; + +enum hns3_nic_state { + HNS3_NIC_STATE_TESTING, + HNS3_NIC_STATE_RESETTING, + HNS3_NIC_STATE_REINITING, + HNS3_NIC_STATE_DOWN, + HNS3_NIC_STATE_DISABLED, + HNS3_NIC_STATE_REMOVING, + HNS3_NIC_STATE_SERVICE_INITED, + HNS3_NIC_STATE_SERVICE_SCHED, + HNS3_NIC_STATE2_RESET_REQUESTED, + HNS3_NIC_STATE_MAX +}; + +#define HNS3_RING_RX_RING_BASEADDR_L_REG 0x00000 +#define HNS3_RING_RX_RING_BASEADDR_H_REG 0x00004 +#define HNS3_RING_RX_RING_BD_NUM_REG 0x00008 +#define HNS3_RING_RX_RING_BD_LEN_REG 0x0000C +#define HNS3_RING_RX_RING_TAIL_REG 0x00018 +#define HNS3_RING_RX_RING_HEAD_REG 0x0001C +#define HNS3_RING_RX_RING_FBDNUM_REG 0x00020 +#define HNS3_RING_RX_RING_PKTNUM_RECORD_REG 0x0002C + +#define HNS3_RING_TX_RING_BASEADDR_L_REG 0x00040 +#define HNS3_RING_TX_RING_BASEADDR_H_REG 0x00044 +#define HNS3_RING_TX_RING_BD_NUM_REG 0x00048 +#define HNS3_RING_TX_RING_BD_LEN_REG 0x0004C +#define HNS3_RING_TX_RING_TAIL_REG 0x00058 +#define HNS3_RING_TX_RING_HEAD_REG 0x0005C +#define HNS3_RING_TX_RING_FBDNUM_REG 0x00060 +#define HNS3_RING_TX_RING_OFFSET_REG 0x00064 +#define HNS3_RING_TX_RING_PKTNUM_RECORD_REG 0x0006C + +#define HNS3_RING_PREFETCH_EN_REG 0x0007C +#define HNS3_RING_CFG_VF_NUM_REG 0x00080 +#define HNS3_RING_ASID_REG 0x0008C +#define HNS3_RING_RX_VM_REG 0x00090 +#define HNS3_RING_T0_BE_RST 0x00094 +#define HNS3_RING_COULD_BE_RST 0x00098 +#define HNS3_RING_WRR_WEIGHT_REG 0x0009c + +#define HNS3_RING_INTMSK_RXWL_REG 0x000A0 +#define HNS3_RING_INTSTS_RX_RING_REG 0x000A4 +#define HNS3_RX_RING_INT_STS_REG 0x000A8 +#define HNS3_RING_INTMSK_TXWL_REG 0x000AC +#define HNS3_RING_INTSTS_TX_RING_REG 0x000B0 +#define HNS3_TX_RING_INT_STS_REG 0x000B4 +#define HNS3_RING_INTMSK_RX_OVERTIME_REG 0x000B8 +#define HNS3_RING_INTSTS_RX_OVERTIME_REG 0x000BC +#define HNS3_RING_INTMSK_TX_OVERTIME_REG 0x000C4 +#define HNS3_RING_INTSTS_TX_OVERTIME_REG 0x000C8 + +#define HNS3_RING_MB_CTRL_REG 0x00100 +#define HNS3_RING_MB_DATA_BASE_REG 0x00200 + +#define HNS3_TX_REG_OFFSET 0x40 + +#define HNS3_RX_HEAD_SIZE 256 + +#define HNS3_TX_TIMEOUT (5 * HZ) +#define HNS3_RING_NAME_LEN 16 +#define HNS3_BUFFER_SIZE_2048 2048 +#define HNS3_RING_MAX_PENDING 32768 +#define HNS3_MAX_MTU 9728 + +#define HNS3_BD_SIZE_512_TYPE 0 +#define HNS3_BD_SIZE_1024_TYPE 1 +#define HNS3_BD_SIZE_2048_TYPE 2 +#define HNS3_BD_SIZE_4096_TYPE 3 + +#define HNS3_RX_FLAG_VLAN_PRESENT 0x1 +#define HNS3_RX_FLAG_L3ID_IPV4 0x0 +#define HNS3_RX_FLAG_L3ID_IPV6 0x1 +#define HNS3_RX_FLAG_L4ID_UDP 0x0 +#define HNS3_RX_FLAG_L4ID_TCP 0x1 + +#define HNS3_RXD_DMAC_S 0 +#define HNS3_RXD_DMAC_M (0x3 << HNS3_RXD_DMAC_S) +#define HNS3_RXD_VLAN_S 2 +#define HNS3_RXD_VLAN_M (0x3 << HNS3_RXD_VLAN_S) +#define HNS3_RXD_L3ID_S 4 +#define HNS3_RXD_L3ID_M (0xf << HNS3_RXD_L3ID_S) +#define HNS3_RXD_L4ID_S 8 +#define HNS3_RXD_L4ID_M (0xf << HNS3_RXD_L4ID_S) +#define HNS3_RXD_FRAG_B 12 +#define HNS3_RXD_L2E_B 16 +#define HNS3_RXD_L3E_B 17 +#define HNS3_RXD_L4E_B 18 +#define HNS3_RXD_TRUNCAT_B 19 +#define HNS3_RXD_HOI_B 20 +#define HNS3_RXD_DOI_B 21 +#define HNS3_RXD_OL3E_B 22 +#define HNS3_RXD_OL4E_B 23 + +#define HNS3_RXD_ODMAC_S 0 +#define HNS3_RXD_ODMAC_M (0x3 << HNS3_RXD_ODMAC_S) +#define HNS3_RXD_OVLAN_S 2 +#define HNS3_RXD_OVLAN_M (0x3 << HNS3_RXD_OVLAN_S) +#define HNS3_RXD_OL3ID_S 4 +#define HNS3_RXD_OL3ID_M (0xf << HNS3_RXD_OL3ID_S) +#define HNS3_RXD_OL4ID_S 8 +#define HNS3_RXD_OL4ID_M (0xf << HNS3_RXD_OL4ID_S) +#define HNS3_RXD_FBHI_S 12 +#define HNS3_RXD_FBHI_M (0x3 << HNS3_RXD_FBHI_S) +#define HNS3_RXD_FBLI_S 14 +#define HNS3_RXD_FBLI_M (0x3 << HNS3_RXD_FBLI_S) + +#define HNS3_RXD_BDTYPE_S 0 +#define HNS3_RXD_BDTYPE_M (0xf << HNS3_RXD_BDTYPE_S) +#define HNS3_RXD_VLD_B 4 +#define HNS3_RXD_UDP0_B 5 +#define HNS3_RXD_EXTEND_B 7 +#define HNS3_RXD_FE_B 8 +#define HNS3_RXD_LUM_B 9 +#define HNS3_RXD_CRCP_B 10 +#define HNS3_RXD_L3L4P_B 11 +#define HNS3_RXD_TSIND_S 12 +#define HNS3_RXD_TSIND_M (0x7 << HNS3_RXD_TSIND_S) +#define HNS3_RXD_LKBK_B 15 +#define HNS3_RXD_HDL_S 16 +#define HNS3_RXD_HDL_M (0x7ff << HNS3_RXD_HDL_S) +#define HNS3_RXD_HSIND_B 31 + +#define HNS3_TXD_L3T_S 0 +#define HNS3_TXD_L3T_M (0x3 << HNS3_TXD_L3T_S) +#define HNS3_TXD_L4T_S 2 +#define HNS3_TXD_L4T_M (0x3 << HNS3_TXD_L4T_S) +#define HNS3_TXD_L3CS_B 4 +#define HNS3_TXD_L4CS_B 5 +#define HNS3_TXD_VLAN_B 6 +#define HNS3_TXD_TSO_B 7 + +#define HNS3_TXD_L2LEN_S 8 +#define HNS3_TXD_L2LEN_M (0xff << HNS3_TXD_L2LEN_S) +#define HNS3_TXD_L3LEN_S 16 +#define HNS3_TXD_L3LEN_M (0xff << HNS3_TXD_L3LEN_S) +#define HNS3_TXD_L4LEN_S 24 +#define HNS3_TXD_L4LEN_M (0xff << HNS3_TXD_L4LEN_S) + +#define HNS3_TXD_OL3T_S 0 +#define HNS3_TXD_OL3T_M (0x3 << HNS3_TXD_OL3T_S) +#define HNS3_TXD_OVLAN_B 2 +#define HNS3_TXD_MACSEC_B 3 +#define HNS3_TXD_TUNTYPE_S 4 +#define HNS3_TXD_TUNTYPE_M (0xf << HNS3_TXD_TUNTYPE_S) + +#define HNS3_TXD_BDTYPE_S 0 +#define HNS3_TXD_BDTYPE_M (0xf << HNS3_TXD_BDTYPE_S) +#define HNS3_TXD_FE_B 4 +#define HNS3_TXD_SC_S 5 +#define HNS3_TXD_SC_M (0x3 << HNS3_TXD_SC_S) +#define HNS3_TXD_EXTEND_B 7 +#define HNS3_TXD_VLD_B 8 +#define HNS3_TXD_RI_B 9 +#define HNS3_TXD_RA_B 10 +#define HNS3_TXD_TSYN_B 11 +#define HNS3_TXD_DECTTL_S 12 +#define HNS3_TXD_DECTTL_M (0xf << HNS3_TXD_DECTTL_S) + +#define HNS3_TXD_MSS_S 0 +#define HNS3_TXD_MSS_M (0x3fff << HNS3_TXD_MSS_S) + +#define HNS3_VECTOR_TX_IRQ BIT_ULL(0) +#define HNS3_VECTOR_RX_IRQ BIT_ULL(1) + +#define HNS3_VECTOR_NOT_INITED 0 +#define HNS3_VECTOR_INITED 1 + +#define HNS3_MAX_BD_SIZE 65535 +#define HNS3_MAX_BD_PER_FRAG 8 +#define HNS3_MAX_BD_PER_PKT MAX_SKB_FRAGS + +#define HNS3_VECTOR_GL0_OFFSET 0x100 +#define HNS3_VECTOR_GL1_OFFSET 0x200 +#define HNS3_VECTOR_GL2_OFFSET 0x300 +#define HNS3_VECTOR_RL_OFFSET 0x900 +#define HNS3_VECTOR_RL_EN_B 6 + +enum hns3_pkt_l3t_type { + HNS3_L3T_NONE, + HNS3_L3T_IPV6, + HNS3_L3T_IPV4, + HNS3_L3T_RESERVED +}; + +enum hns3_pkt_l4t_type { + HNS3_L4T_UNKNOWN, + HNS3_L4T_TCP, + HNS3_L4T_UDP, + HNS3_L4T_SCTP +}; + +enum hns3_pkt_ol3t_type { + HNS3_OL3T_NONE, + HNS3_OL3T_IPV6, + HNS3_OL3T_IPV4_NO_CSUM, + HNS3_OL3T_IPV4_CSUM +}; + +enum hns3_pkt_tun_type { + HNS3_TUN_NONE, + HNS3_TUN_MAC_IN_UDP, + HNS3_TUN_NVGRE, + HNS3_TUN_OTHER +}; + +/* hardware spec ring buffer format */ +struct __packed hns3_desc { + __le64 addr; + union { + struct { + __le16 vlan_tag; + __le16 send_size; + union { + __le32 type_cs_vlan_tso_len; + struct { + __u8 type_cs_vlan_tso; + __u8 l2_len; + __u8 l3_len; + __u8 l4_len; + }; + }; + __le16 outer_vlan_tag; + __le16 tv; + + union { + __le32 ol_type_vlan_len_msec; + struct { + __u8 ol_type_vlan_msec; + __u8 ol2_len; + __u8 ol3_len; + __u8 ol4_len; + }; + }; + + __le32 paylen; + __le16 bdtp_fe_sc_vld_ra_ri; + __le16 mss; + } tx; + + struct { + __le32 l234_info; + __le16 pkt_len; + __le16 size; + + __le32 rss_hash; + __le16 fd_id; + __le16 vlan_tag; + + union { + __le32 ol_info; + struct { + __le16 o_dm_vlan_id_fb; + __le16 ot_vlan_tag; + }; + }; + + __le32 bd_base_info; + } rx; + }; +}; + +struct hns3_desc_cb { + dma_addr_t dma; /* dma address of this desc */ + void *buf; /* cpu addr for a desc */ + + /* priv data for the desc, e.g. skb when use with ip stack*/ + void *priv; + u16 page_offset; + u16 reuse_flag; + + u16 length; /* length of the buffer */ + + /* desc type, used by the ring user to mark the type of the priv data */ + u16 type; +}; + +enum hns3_pkt_l3type { + HNS3_L3_TYPE_IPV4, + HNS3_L3_TYPE_IPV6, + HNS3_L3_TYPE_ARP, + HNS3_L3_TYPE_RARP, + HNS3_L3_TYPE_IPV4_OPT, + HNS3_L3_TYPE_IPV6_EXT, + HNS3_L3_TYPE_LLDP, + HNS3_L3_TYPE_BPDU, + HNS3_L3_TYPE_MAC_PAUSE, + HNS3_L3_TYPE_PFC_PAUSE,/* 0x9*/ + + /* reserved for 0xA~0xB*/ + + HNS3_L3_TYPE_CNM = 0xc, + + /* reserved for 0xD~0xE*/ + + HNS3_L3_TYPE_PARSE_FAIL = 0xf /* must be last */ +}; + +enum hns3_pkt_l4type { + HNS3_L4_TYPE_UDP, + HNS3_L4_TYPE_TCP, + HNS3_L4_TYPE_GRE, + HNS3_L4_TYPE_SCTP, + HNS3_L4_TYPE_IGMP, + HNS3_L4_TYPE_ICMP, + + /* reserved for 0x6~0xE */ + + HNS3_L4_TYPE_PARSE_FAIL = 0xf /* must be last */ +}; + +enum hns3_pkt_ol3type { + HNS3_OL3_TYPE_IPV4 = 0, + HNS3_OL3_TYPE_IPV6, + /* reserved for 0x2~0x3 */ + HNS3_OL3_TYPE_IPV4_OPT = 4, + HNS3_OL3_TYPE_IPV6_EXT, + + /* reserved for 0x6~0xE*/ + + HNS3_OL3_TYPE_PARSE_FAIL = 0xf /* must be last */ +}; + +enum hns3_pkt_ol4type { + HNS3_OL4_TYPE_NO_TUN, + HNS3_OL4_TYPE_MAC_IN_UDP, + HNS3_OL4_TYPE_NVGRE, + HNS3_OL4_TYPE_UNKNOWN +}; + +struct ring_stats { + u64 io_err_cnt; + u64 sw_err_cnt; + u64 seg_pkt_cnt; + union { + struct { + u64 tx_pkts; + u64 tx_bytes; + u64 tx_err_cnt; + u64 restart_queue; + u64 tx_busy; + }; + struct { + u64 rx_pkts; + u64 rx_bytes; + u64 rx_err_cnt; + u64 reuse_pg_cnt; + u64 err_pkt_len; + u64 non_vld_descs; + u64 err_bd_num; + u64 l2_err; + u64 l3l4_csum_err; + }; + }; +}; + +struct hns3_enet_ring { + u8 __iomem *io_base; /* base io address for the ring */ + struct hns3_desc *desc; /* dma map address space */ + struct hns3_desc_cb *desc_cb; + struct hns3_enet_ring *next; + struct hns3_enet_tqp_vector *tqp_vector; + struct hnae3_queue *tqp; + char ring_name[HNS3_RING_NAME_LEN]; + struct device *dev; /* will be used for DMA mapping of descriptors */ + + /* statistic */ + struct ring_stats stats; + struct u64_stats_sync syncp; + + dma_addr_t desc_dma_addr; + u32 buf_size; /* size for hnae_desc->addr, preset by AE */ + u16 desc_num; /* total number of desc */ + u16 max_desc_num_per_pkt; + u16 max_raw_data_sz_per_desc; + u16 max_pkt_size; + int next_to_use; /* idx of next spare desc */ + + /* idx of lastest sent desc, the ring is empty when equal to + * next_to_use + */ + int next_to_clean; + + u32 flag; /* ring attribute */ + int irq_init_flag; + + int numa_node; + cpumask_t affinity_mask; +}; + +struct hns_queue; + +struct hns3_nic_ring_data { + struct hns3_enet_ring *ring; + struct napi_struct napi; + int queue_index; + int (*poll_one)(struct hns3_nic_ring_data *, int, void *); + void (*ex_process)(struct hns3_nic_ring_data *, struct sk_buff *); + void (*fini_process)(struct hns3_nic_ring_data *); +}; + +struct hns3_nic_ops { + int (*fill_desc)(struct hns3_enet_ring *ring, void *priv, + int size, dma_addr_t dma, int frag_end, + enum hns_desc_type type); + int (*maybe_stop_tx)(struct sk_buff **out_skb, + int *bnum, struct hns3_enet_ring *ring); + void (*get_rxd_bnum)(u32 bnum_flag, int *out_bnum); +}; + +enum hns3_flow_level_range { + HNS3_FLOW_LOW = 0, + HNS3_FLOW_MID = 1, + HNS3_FLOW_HIGH = 2, + HNS3_FLOW_ULTRA = 3, +}; + +enum hns3_link_mode_bits { + HNS3_LM_FIBRE_BIT = BIT(0), + HNS3_LM_AUTONEG_BIT = BIT(1), + HNS3_LM_TP_BIT = BIT(2), + HNS3_LM_PAUSE_BIT = BIT(3), + HNS3_LM_BACKPLANE_BIT = BIT(4), + HNS3_LM_10BASET_HALF_BIT = BIT(5), + HNS3_LM_10BASET_FULL_BIT = BIT(6), + HNS3_LM_100BASET_HALF_BIT = BIT(7), + HNS3_LM_100BASET_FULL_BIT = BIT(8), + HNS3_LM_1000BASET_FULL_BIT = BIT(9), + HNS3_LM_10000BASEKR_FULL_BIT = BIT(10), + HNS3_LM_25000BASEKR_FULL_BIT = BIT(11), + HNS3_LM_40000BASELR4_FULL_BIT = BIT(12), + HNS3_LM_50000BASEKR2_FULL_BIT = BIT(13), + HNS3_LM_100000BASEKR4_FULL_BIT = BIT(14), + HNS3_LM_COUNT = 15 +}; + +#define HNS3_INT_GL_50K 0x000A +#define HNS3_INT_GL_20K 0x0019 +#define HNS3_INT_GL_18K 0x001B +#define HNS3_INT_GL_8K 0x003E + +struct hns3_enet_ring_group { + /* array of pointers to rings */ + struct hns3_enet_ring *ring; + u64 total_bytes; /* total bytes processed this group */ + u64 total_packets; /* total packets processed this group */ + u16 count; + enum hns3_flow_level_range flow_level; + u16 int_gl; +}; + +struct hns3_enet_tqp_vector { + struct hnae3_handle *handle; + u8 __iomem *mask_addr; + int vector_irq; + int irq_init_flag; + + u16 idx; /* index in the TQP vector array per handle. */ + + struct napi_struct napi; + + struct hns3_enet_ring_group rx_group; + struct hns3_enet_ring_group tx_group; + + u16 num_tqps; /* total number of tqps in TQP vector */ + + cpumask_t affinity_mask; + char name[HNAE3_INT_NAME_LEN]; + + /* when 0 should adjust interrupt coalesce parameter */ + u8 int_adapt_down; +} ____cacheline_internodealigned_in_smp; + +enum hns3_udp_tnl_type { + HNS3_UDP_TNL_VXLAN, + HNS3_UDP_TNL_GENEVE, + HNS3_UDP_TNL_MAX, +}; + +struct hns3_udp_tunnel { + u16 dst_port; + int used; +}; + +struct hns3_nic_priv { + struct hnae3_handle *ae_handle; + u32 enet_ver; + u32 port_id; + struct net_device *netdev; + struct device *dev; + struct hns3_nic_ops ops; + + /** + * the cb for nic to manage the ring buffer, the first half of the + * array is for tx_ring and vice versa for the second half + */ + struct hns3_nic_ring_data *ring_data; + struct hns3_enet_tqp_vector *tqp_vector; + u16 vector_num; + + /* The most recently read link state */ + int link; + u64 tx_timeout_count; + + unsigned long state; + + struct timer_list service_timer; + + struct work_struct service_task; + + struct notifier_block notifier_block; + /* Vxlan/Geneve information */ + struct hns3_udp_tunnel udp_tnl[HNS3_UDP_TNL_MAX]; +}; + +union l3_hdr_info { + struct iphdr *v4; + struct ipv6hdr *v6; + unsigned char *hdr; +}; + +union l4_hdr_info { + struct tcphdr *tcp; + struct udphdr *udp; + unsigned char *hdr; +}; + +/* the distance between [begin, end) in a ring buffer + * note: there is a unuse slot between the begin and the end + */ +static inline int ring_dist(struct hns3_enet_ring *ring, int begin, int end) +{ + return (end - begin + ring->desc_num) % ring->desc_num; +} + +static inline int ring_space(struct hns3_enet_ring *ring) +{ + return ring->desc_num - + ring_dist(ring, ring->next_to_clean, ring->next_to_use) - 1; +} + +static inline int is_ring_empty(struct hns3_enet_ring *ring) +{ + return ring->next_to_use == ring->next_to_clean; +} + +static inline void hns3_write_reg(void __iomem *base, u32 reg, u32 value) +{ + u8 __iomem *reg_addr = READ_ONCE(base); + + writel(value, reg_addr + reg); +} + +#define hns3_write_dev(a, reg, value) \ + hns3_write_reg((a)->io_base, (reg), (value)) + +#define hnae_queue_xmit(tqp, buf_num) writel_relaxed(buf_num, \ + (tqp)->io_base + HNS3_RING_TX_RING_TAIL_REG) + +#define ring_to_dev(ring) (&(ring)->tqp->handle->pdev->dev) + +#define ring_to_dma_dir(ring) (HNAE3_IS_TX_RING(ring) ? \ + DMA_TO_DEVICE : DMA_FROM_DEVICE) + +#define tx_ring_data(priv, idx) ((priv)->ring_data[idx]) + +#define hnae_buf_size(_ring) ((_ring)->buf_size) +#define hnae_page_order(_ring) (get_order(hnae_buf_size(_ring))) +#define hnae_page_size(_ring) (PAGE_SIZE << hnae_page_order(_ring)) + +/* iterator for handling rings in ring group */ +#define hns3_for_each_ring(pos, head) \ + for (pos = (head).ring; pos; pos = pos->next) + +void hns3_ethtool_set_ops(struct net_device *netdev); + +int hns3_clean_tx_ring(struct hns3_enet_ring *ring, int budget); +#endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c new file mode 100644 index 000000000000..d636399232fb --- /dev/null +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_ethtool.c @@ -0,0 +1,493 @@ +/* + * Copyright (c) 2016~2017 Hisilicon Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include <linux/etherdevice.h> +#include <linux/string.h> + +#include "hns3_enet.h" + +struct hns3_stats { + char stats_string[ETH_GSTRING_LEN]; + int stats_size; + int stats_offset; +}; + +/* tqp related stats */ +#define HNS3_TQP_STAT(_string, _member) { \ + .stats_string = _string, \ + .stats_size = FIELD_SIZEOF(struct ring_stats, _member), \ + .stats_offset = offsetof(struct hns3_enet_ring, stats), \ +} \ + +static const struct hns3_stats hns3_txq_stats[] = { + /* Tx per-queue statistics */ + HNS3_TQP_STAT("tx_io_err_cnt", io_err_cnt), + HNS3_TQP_STAT("tx_sw_err_cnt", sw_err_cnt), + HNS3_TQP_STAT("tx_seg_pkt_cnt", seg_pkt_cnt), + HNS3_TQP_STAT("tx_pkts", tx_pkts), + HNS3_TQP_STAT("tx_bytes", tx_bytes), + HNS3_TQP_STAT("tx_err_cnt", tx_err_cnt), + HNS3_TQP_STAT("tx_restart_queue", restart_queue), + HNS3_TQP_STAT("tx_busy", tx_busy), +}; + +#define HNS3_TXQ_STATS_COUNT ARRAY_SIZE(hns3_txq_stats) + +static const struct hns3_stats hns3_rxq_stats[] = { + /* Rx per-queue statistics */ + HNS3_TQP_STAT("rx_io_err_cnt", io_err_cnt), + HNS3_TQP_STAT("rx_sw_err_cnt", sw_err_cnt), + HNS3_TQP_STAT("rx_seg_pkt_cnt", seg_pkt_cnt), + HNS3_TQP_STAT("rx_pkts", rx_pkts), + HNS3_TQP_STAT("rx_bytes", rx_bytes), + HNS3_TQP_STAT("rx_err_cnt", rx_err_cnt), + HNS3_TQP_STAT("rx_reuse_pg_cnt", reuse_pg_cnt), + HNS3_TQP_STAT("rx_err_pkt_len", err_pkt_len), + HNS3_TQP_STAT("rx_non_vld_descs", non_vld_descs), + HNS3_TQP_STAT("rx_err_bd_num", err_bd_num), + HNS3_TQP_STAT("rx_l2_err", l2_err), + HNS3_TQP_STAT("rx_l3l4_csum_err", l3l4_csum_err), +}; + +#define HNS3_RXQ_STATS_COUNT ARRAY_SIZE(hns3_rxq_stats) + +#define HNS3_TQP_STATS_COUNT (HNS3_TXQ_STATS_COUNT + HNS3_RXQ_STATS_COUNT) + +struct hns3_link_mode_mapping { + u32 hns3_link_mode; + u32 ethtool_link_mode; +}; + +static const struct hns3_link_mode_mapping hns3_lm_map[] = { + {HNS3_LM_FIBRE_BIT, ETHTOOL_LINK_MODE_FIBRE_BIT}, + {HNS3_LM_AUTONEG_BIT, ETHTOOL_LINK_MODE_Autoneg_BIT}, + {HNS3_LM_TP_BIT, ETHTOOL_LINK_MODE_TP_BIT}, + {HNS3_LM_PAUSE_BIT, ETHTOOL_LINK_MODE_Pause_BIT}, + {HNS3_LM_BACKPLANE_BIT, ETHTOOL_LINK_MODE_Backplane_BIT}, + {HNS3_LM_10BASET_HALF_BIT, ETHTOOL_LINK_MODE_10baseT_Half_BIT}, + {HNS3_LM_10BASET_FULL_BIT, ETHTOOL_LINK_MODE_10baseT_Full_BIT}, + {HNS3_LM_100BASET_HALF_BIT, ETHTOOL_LINK_MODE_100baseT_Half_BIT}, + {HNS3_LM_100BASET_FULL_BIT, ETHTOOL_LINK_MODE_100baseT_Full_BIT}, + {HNS3_LM_1000BASET_FULL_BIT, ETHTOOL_LINK_MODE_1000baseT_Full_BIT}, +}; + +static void hns3_driv_to_eth_caps(u32 caps, struct ethtool_link_ksettings *cmd, + bool is_advertised) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hns3_lm_map); i++) { + if (!(caps & hns3_lm_map[i].hns3_link_mode)) + continue; + + if (is_advertised) { + ethtool_link_ksettings_zero_link_mode(cmd, + advertising); + __set_bit(hns3_lm_map[i].ethtool_link_mode, + cmd->link_modes.advertising); + } else { + ethtool_link_ksettings_zero_link_mode(cmd, + supported); + __set_bit(hns3_lm_map[i].ethtool_link_mode, + cmd->link_modes.supported); + } + } +} + +static int hns3_get_sset_count(struct net_device *netdev, int stringset) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + const struct hnae3_ae_ops *ops = h->ae_algo->ops; + + if (!ops->get_sset_count) + return -EOPNOTSUPP; + + switch (stringset) { + case ETH_SS_STATS: + return ((HNS3_TQP_STATS_COUNT * h->kinfo.num_tqps) + + ops->get_sset_count(h, stringset)); + + case ETH_SS_TEST: + return ops->get_sset_count(h, stringset); + } + + return 0; +} + +static void *hns3_update_strings(u8 *data, const struct hns3_stats *stats, + u32 stat_count, u32 num_tqps) +{ +#define MAX_PREFIX_SIZE (8 + 4) + u32 size_left; + u32 i, j; + u32 n1; + + for (i = 0; i < num_tqps; i++) { + for (j = 0; j < stat_count; j++) { + data[ETH_GSTRING_LEN - 1] = '\0'; + + /* first, prepend the prefix string */ + n1 = snprintf(data, MAX_PREFIX_SIZE, "rcb_q%d_", i); + n1 = min_t(uint, n1, MAX_PREFIX_SIZE - 1); + size_left = (ETH_GSTRING_LEN - 1) - n1; + + /* now, concatenate the stats string to it */ + strncat(data, stats[j].stats_string, size_left); + data += ETH_GSTRING_LEN; + } + } + + return data; +} + +static u8 *hns3_get_strings_tqps(struct hnae3_handle *handle, u8 *data) +{ + struct hnae3_knic_private_info *kinfo = &handle->kinfo; + + /* get strings for Tx */ + data = hns3_update_strings(data, hns3_txq_stats, HNS3_TXQ_STATS_COUNT, + kinfo->num_tqps); + + /* get strings for Rx */ + data = hns3_update_strings(data, hns3_rxq_stats, HNS3_RXQ_STATS_COUNT, + kinfo->num_tqps); + + return data; +} + +static void hns3_get_strings(struct net_device *netdev, u32 stringset, u8 *data) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + const struct hnae3_ae_ops *ops = h->ae_algo->ops; + char *buff = (char *)data; + + if (!ops->get_strings) + return; + + switch (stringset) { + case ETH_SS_STATS: + buff = hns3_get_strings_tqps(h, buff); + h->ae_algo->ops->get_strings(h, stringset, (u8 *)buff); + break; + case ETH_SS_TEST: + ops->get_strings(h, stringset, data); + break; + } +} + +static u64 *hns3_get_stats_tqps(struct hnae3_handle *handle, u64 *data) +{ + struct hns3_nic_priv *nic_priv = (struct hns3_nic_priv *)handle->priv; + struct hnae3_knic_private_info *kinfo = &handle->kinfo; + struct hns3_enet_ring *ring; + u8 *stat; + u32 i; + + /* get stats for Tx */ + for (i = 0; i < kinfo->num_tqps; i++) { + ring = nic_priv->ring_data[i].ring; + for (i = 0; i < HNS3_TXQ_STATS_COUNT; i++) { + stat = (u8 *)ring + hns3_txq_stats[i].stats_offset; + *data++ = *(u64 *)stat; + } + } + + /* get stats for Rx */ + for (i = 0; i < kinfo->num_tqps; i++) { + ring = nic_priv->ring_data[i + kinfo->num_tqps].ring; + for (i = 0; i < HNS3_RXQ_STATS_COUNT; i++) { + stat = (u8 *)ring + hns3_rxq_stats[i].stats_offset; + *data++ = *(u64 *)stat; + } + } + + return data; +} + +/* hns3_get_stats - get detail statistics. + * @netdev: net device + * @stats: statistics info. + * @data: statistics data. + */ +void hns3_get_stats(struct net_device *netdev, struct ethtool_stats *stats, + u64 *data) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + u64 *p = data; + + if (!h->ae_algo->ops->get_stats || !h->ae_algo->ops->update_stats) { + netdev_err(netdev, "could not get any statistics\n"); + return; + } + + h->ae_algo->ops->update_stats(h, &netdev->stats); + + /* get per-queue stats */ + p = hns3_get_stats_tqps(h, p); + + /* get MAC & other misc hardware stats */ + h->ae_algo->ops->get_stats(h, p); +} + +static void hns3_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *drvinfo) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + strncpy(drvinfo->version, hns3_driver_version, + sizeof(drvinfo->version)); + drvinfo->version[sizeof(drvinfo->version) - 1] = '\0'; + + strncpy(drvinfo->driver, h->pdev->driver->name, + sizeof(drvinfo->driver)); + drvinfo->driver[sizeof(drvinfo->driver) - 1] = '\0'; + + strncpy(drvinfo->bus_info, pci_name(h->pdev), + sizeof(drvinfo->bus_info)); + drvinfo->bus_info[ETHTOOL_BUSINFO_LEN - 1] = '\0'; + + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), "0x%08x", + priv->ae_handle->ae_algo->ops->get_fw_version(h)); +} + +static u32 hns3_get_link(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h; + + h = priv->ae_handle; + + if (h->ae_algo && h->ae_algo->ops && h->ae_algo->ops->get_status) + return h->ae_algo->ops->get_status(h); + else + return 0; +} + +static void hns3_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *param) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + int queue_num = priv->ae_handle->kinfo.num_tqps; + + param->tx_max_pending = HNS3_RING_MAX_PENDING; + param->rx_max_pending = HNS3_RING_MAX_PENDING; + + param->tx_pending = priv->ring_data[0].ring->desc_num; + param->rx_pending = priv->ring_data[queue_num].ring->desc_num; +} + +static void hns3_get_pauseparam(struct net_device *netdev, + struct ethtool_pauseparam *param) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (h->ae_algo && h->ae_algo->ops && h->ae_algo->ops->get_pauseparam) + h->ae_algo->ops->get_pauseparam(h, ¶m->autoneg, + ¶m->rx_pause, ¶m->tx_pause); +} + +static int hns3_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *cmd) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + u32 supported_caps; + u32 advertised_caps; + u8 media_type = HNAE3_MEDIA_TYPE_UNKNOWN; + u8 link_stat; + u8 auto_neg; + u8 duplex; + u32 speed; + + if (!h->ae_algo || !h->ae_algo->ops) + return -EOPNOTSUPP; + + /* 1.auto_neg & speed & duplex from cmd */ + if (h->ae_algo->ops->get_ksettings_an_result) { + h->ae_algo->ops->get_ksettings_an_result(h, &auto_neg, + &speed, &duplex); + cmd->base.autoneg = auto_neg; + cmd->base.speed = speed; + cmd->base.duplex = duplex; + + link_stat = hns3_get_link(netdev); + if (!link_stat) { + cmd->base.speed = (u32)SPEED_UNKNOWN; + cmd->base.duplex = DUPLEX_UNKNOWN; + } + } + + /* 2.media_type get from bios parameter block */ + if (h->ae_algo->ops->get_media_type) { + h->ae_algo->ops->get_media_type(h, &media_type); + + switch (media_type) { + case HNAE3_MEDIA_TYPE_FIBER: + cmd->base.port = PORT_FIBRE; + supported_caps = HNS3_LM_FIBRE_BIT | + HNS3_LM_AUTONEG_BIT | + HNS3_LM_PAUSE_BIT | + HNS3_LM_1000BASET_FULL_BIT; + + advertised_caps = supported_caps; + break; + case HNAE3_MEDIA_TYPE_COPPER: + cmd->base.port = PORT_TP; + supported_caps = HNS3_LM_TP_BIT | + HNS3_LM_AUTONEG_BIT | + HNS3_LM_PAUSE_BIT | + HNS3_LM_1000BASET_FULL_BIT | + HNS3_LM_100BASET_FULL_BIT | + HNS3_LM_100BASET_HALF_BIT | + HNS3_LM_10BASET_FULL_BIT | + HNS3_LM_10BASET_HALF_BIT; + advertised_caps = supported_caps; + break; + case HNAE3_MEDIA_TYPE_BACKPLANE: + cmd->base.port = PORT_NONE; + supported_caps = HNS3_LM_BACKPLANE_BIT | + HNS3_LM_PAUSE_BIT | + HNS3_LM_AUTONEG_BIT | + HNS3_LM_1000BASET_FULL_BIT | + HNS3_LM_100BASET_FULL_BIT | + HNS3_LM_100BASET_HALF_BIT | + HNS3_LM_10BASET_FULL_BIT | + HNS3_LM_10BASET_HALF_BIT; + + advertised_caps = supported_caps; + break; + case HNAE3_MEDIA_TYPE_UNKNOWN: + default: + cmd->base.port = PORT_OTHER; + supported_caps = 0; + advertised_caps = 0; + break; + } + + /* now, map driver link modes to ethtool link modes */ + hns3_driv_to_eth_caps(supported_caps, cmd, false); + hns3_driv_to_eth_caps(advertised_caps, cmd, true); + } + + /* 3.mdix_ctrl&mdix get from phy reg */ + if (h->ae_algo->ops->get_mdix_mode) + h->ae_algo->ops->get_mdix_mode(h, &cmd->base.eth_tp_mdix_ctrl, + &cmd->base.eth_tp_mdix); + /* 4.mdio_support */ + cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22; + + return 0; +} + +static u32 hns3_get_rss_key_size(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || + !h->ae_algo->ops->get_rss_key_size) + return -EOPNOTSUPP; + + return h->ae_algo->ops->get_rss_key_size(h); +} + +static u32 hns3_get_rss_indir_size(struct net_device *netdev) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || + !h->ae_algo->ops->get_rss_indir_size) + return -EOPNOTSUPP; + + return h->ae_algo->ops->get_rss_indir_size(h); +} + +static int hns3_get_rss(struct net_device *netdev, u32 *indir, u8 *key, + u8 *hfunc) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || !h->ae_algo->ops->get_rss) + return -EOPNOTSUPP; + + return h->ae_algo->ops->get_rss(h, indir, key, hfunc); +} + +static int hns3_set_rss(struct net_device *netdev, const u32 *indir, + const u8 *key, const u8 hfunc) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || !h->ae_algo->ops->set_rss) + return -EOPNOTSUPP; + + /* currently we only support Toeplitz hash */ + if ((hfunc != ETH_RSS_HASH_NO_CHANGE) && (hfunc != ETH_RSS_HASH_TOP)) { + netdev_err(netdev, + "hash func not supported (only Toeplitz hash)\n"); + return -EOPNOTSUPP; + } + if (!indir) { + netdev_err(netdev, + "set rss failed for indir is empty\n"); + return -EOPNOTSUPP; + } + + return h->ae_algo->ops->set_rss(h, indir, key, hfunc); +} + +static int hns3_get_rxnfc(struct net_device *netdev, + struct ethtool_rxnfc *cmd, + u32 *rule_locs) +{ + struct hns3_nic_priv *priv = netdev_priv(netdev); + struct hnae3_handle *h = priv->ae_handle; + + if (!h->ae_algo || !h->ae_algo->ops || !h->ae_algo->ops->get_tc_size) + return -EOPNOTSUPP; + + switch (cmd->cmd) { + case ETHTOOL_GRXRINGS: + cmd->data = h->ae_algo->ops->get_tc_size(h); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct ethtool_ops hns3_ethtool_ops = { + .get_drvinfo = hns3_get_drvinfo, + .get_link = hns3_get_link, + .get_ringparam = hns3_get_ringparam, + .get_pauseparam = hns3_get_pauseparam, + .get_strings = hns3_get_strings, + .get_ethtool_stats = hns3_get_stats, + .get_sset_count = hns3_get_sset_count, + .get_rxnfc = hns3_get_rxnfc, + .get_rxfh_key_size = hns3_get_rss_key_size, + .get_rxfh_indir_size = hns3_get_rss_indir_size, + .get_rxfh = hns3_get_rss, + .set_rxfh = hns3_set_rss, + .get_link_ksettings = hns3_get_link_ksettings, +}; + +void hns3_ethtool_set_ops(struct net_device *netdev) +{ + netdev->ethtool_ops = &hns3_ethtool_ops; +} diff --git a/drivers/net/ethernet/hp/hp100.c b/drivers/net/ethernet/hp/hp100.c index c6164a98f257..c8c7ad2eff77 100644 --- a/drivers/net/ethernet/hp/hp100.c +++ b/drivers/net/ethernet/hp/hp100.c @@ -194,7 +194,7 @@ static const char *hp100_isa_tbl[] = { }; #endif -static struct eisa_device_id hp100_eisa_tbl[] = { +static const struct eisa_device_id hp100_eisa_tbl[] = { { "HWPF180" }, /* HP J2577 rev A */ { "HWP1920" }, /* HP 27248B */ { "HWP1940" }, /* HP J2577 */ diff --git a/drivers/net/ethernet/huawei/Kconfig b/drivers/net/ethernet/huawei/Kconfig new file mode 100644 index 000000000000..c1a95ae4058b --- /dev/null +++ b/drivers/net/ethernet/huawei/Kconfig @@ -0,0 +1,19 @@ +# +# Huawei driver configuration +# + +config NET_VENDOR_HUAWEI + bool "Huawei devices" + default y + ---help--- + If you have a network (Ethernet) card belonging to this class, say Y. + Note that the answer to this question doesn't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about Huawei cards. If you say Y, you will be asked + for your specific card in the following questions. + +if NET_VENDOR_HUAWEI + +source "drivers/net/ethernet/huawei/hinic/Kconfig" + +endif # NET_VENDOR_HUAWEI diff --git a/drivers/net/ethernet/huawei/Makefile b/drivers/net/ethernet/huawei/Makefile new file mode 100644 index 000000000000..5c37cc8fc1bc --- /dev/null +++ b/drivers/net/ethernet/huawei/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Huawei device drivers. +# + +obj-$(CONFIG_HINIC) += hinic/ diff --git a/drivers/net/ethernet/huawei/hinic/Kconfig b/drivers/net/ethernet/huawei/hinic/Kconfig new file mode 100644 index 000000000000..08db24954f7e --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/Kconfig @@ -0,0 +1,12 @@ +# +# Huawei driver configuration +# + +config HINIC + tristate "Huawei Intelligent PCIE Network Interface Card" + depends on (PCI_MSI && X86) + ---help--- + This driver supports HiNIC PCIE Ethernet cards. + To compile this driver as part of the kernel, choose Y here. + If unsure, choose N. + The default is compiled as module. diff --git a/drivers/net/ethernet/huawei/hinic/Makefile b/drivers/net/ethernet/huawei/hinic/Makefile new file mode 100644 index 000000000000..289ce88bb2d0 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/Makefile @@ -0,0 +1,6 @@ +obj-$(CONFIG_HINIC) += hinic.o + +hinic-y := hinic_main.o hinic_tx.o hinic_rx.o hinic_port.o hinic_hw_dev.o \ + hinic_hw_io.o hinic_hw_qp.o hinic_hw_cmdq.o hinic_hw_wq.o \ + hinic_hw_mgmt.o hinic_hw_api_cmd.o hinic_hw_eqs.o hinic_hw_if.o \ + hinic_common.o diff --git a/drivers/net/ethernet/huawei/hinic/hinic_common.c b/drivers/net/ethernet/huawei/hinic/hinic_common.c new file mode 100644 index 000000000000..02c74fd8380e --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_common.c @@ -0,0 +1,80 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <asm/byteorder.h> + +#include "hinic_common.h" + +/** + * hinic_cpu_to_be32 - convert data to big endian 32 bit format + * @data: the data to convert + * @len: length of data to convert + **/ +void hinic_cpu_to_be32(void *data, int len) +{ + u32 *mem = data; + int i; + + len = len / sizeof(u32); + + for (i = 0; i < len; i++) { + *mem = cpu_to_be32(*mem); + mem++; + } +} + +/** + * hinic_be32_to_cpu - convert data from big endian 32 bit format + * @data: the data to convert + * @len: length of data to convert + **/ +void hinic_be32_to_cpu(void *data, int len) +{ + u32 *mem = data; + int i; + + len = len / sizeof(u32); + + for (i = 0; i < len; i++) { + *mem = be32_to_cpu(*mem); + mem++; + } +} + +/** + * hinic_set_sge - set dma area in scatter gather entry + * @sge: scatter gather entry + * @addr: dma address + * @len: length of relevant data in the dma address + **/ +void hinic_set_sge(struct hinic_sge *sge, dma_addr_t addr, int len) +{ + sge->hi_addr = upper_32_bits(addr); + sge->lo_addr = lower_32_bits(addr); + sge->len = len; +} + +/** + * hinic_sge_to_dma - get dma address from scatter gather entry + * @sge: scatter gather entry + * + * Return dma address of sg entry + **/ +dma_addr_t hinic_sge_to_dma(struct hinic_sge *sge) +{ + return (dma_addr_t)((((u64)sge->hi_addr) << 32) | sge->lo_addr); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_common.h b/drivers/net/ethernet/huawei/hinic/hinic_common.h new file mode 100644 index 000000000000..2c06b76e94a1 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_common.h @@ -0,0 +1,38 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_COMMON_H +#define HINIC_COMMON_H + +#include <linux/types.h> + +#define UPPER_8_BITS(data) (((data) >> 8) & 0xFF) +#define LOWER_8_BITS(data) ((data) & 0xFF) + +struct hinic_sge { + u32 hi_addr; + u32 lo_addr; + u32 len; +}; + +void hinic_cpu_to_be32(void *data, int len); + +void hinic_be32_to_cpu(void *data, int len); + +void hinic_set_sge(struct hinic_sge *sge, dma_addr_t addr, int len); + +dma_addr_t hinic_sge_to_dma(struct hinic_sge *sge); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_dev.h new file mode 100644 index 000000000000..5186cc9023aa --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_dev.h @@ -0,0 +1,64 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_DEV_H +#define HINIC_DEV_H + +#include <linux/netdevice.h> +#include <linux/types.h> +#include <linux/semaphore.h> +#include <linux/workqueue.h> +#include <linux/bitops.h> + +#include "hinic_hw_dev.h" +#include "hinic_tx.h" +#include "hinic_rx.h" + +#define HINIC_DRV_NAME "hinic" + +enum hinic_flags { + HINIC_LINK_UP = BIT(0), + HINIC_INTF_UP = BIT(1), +}; + +struct hinic_rx_mode_work { + struct work_struct work; + u32 rx_mode; +}; + +struct hinic_dev { + struct net_device *netdev; + struct hinic_hwdev *hwdev; + + u32 msg_enable; + unsigned int tx_weight; + unsigned int rx_weight; + + unsigned int flags; + + struct semaphore mgmt_lock; + unsigned long *vlan_bitmap; + + struct hinic_rx_mode_work rx_mode_work; + struct workqueue_struct *workq; + + struct hinic_txq *txqs; + struct hinic_rxq *rxqs; + + struct hinic_txq_stats tx_stats; + struct hinic_rxq_stats rx_stats; +}; + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c new file mode 100644 index 000000000000..c40603a183df --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.c @@ -0,0 +1,978 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/jiffies.h> +#include <linux/delay.h> +#include <linux/log2.h> +#include <linux/semaphore.h> +#include <asm/byteorder.h> +#include <asm/barrier.h> + +#include "hinic_hw_csr.h" +#include "hinic_hw_if.h" +#include "hinic_hw_api_cmd.h" + +#define API_CHAIN_NUM_CELLS 32 + +#define API_CMD_CELL_SIZE_SHIFT 6 +#define API_CMD_CELL_SIZE_MIN (BIT(API_CMD_CELL_SIZE_SHIFT)) + +#define API_CMD_CELL_SIZE(cell_size) \ + (((cell_size) >= API_CMD_CELL_SIZE_MIN) ? \ + (1 << (fls(cell_size - 1))) : API_CMD_CELL_SIZE_MIN) + +#define API_CMD_CELL_SIZE_VAL(size) \ + ilog2((size) >> API_CMD_CELL_SIZE_SHIFT) + +#define API_CMD_BUF_SIZE 2048 + +/* Sizes of the members in hinic_api_cmd_cell */ +#define API_CMD_CELL_DESC_SIZE 8 +#define API_CMD_CELL_DATA_ADDR_SIZE 8 + +#define API_CMD_CELL_ALIGNMENT 8 + +#define API_CMD_TIMEOUT 1000 + +#define MASKED_IDX(chain, idx) ((idx) & ((chain)->num_cells - 1)) + +#define SIZE_8BYTES(size) (ALIGN((size), 8) >> 3) +#define SIZE_4BYTES(size) (ALIGN((size), 4) >> 2) + +#define RD_DMA_ATTR_DEFAULT 0 +#define WR_DMA_ATTR_DEFAULT 0 + +enum api_cmd_data_format { + SGE_DATA = 1, /* cell data is passed by hw address */ +}; + +enum api_cmd_type { + API_CMD_WRITE = 0, +}; + +enum api_cmd_bypass { + NO_BYPASS = 0, + BYPASS = 1, +}; + +enum api_cmd_xor_chk_level { + XOR_CHK_DIS = 0, + + XOR_CHK_ALL = 3, +}; + +static u8 xor_chksum_set(void *data) +{ + int idx; + u8 *val, checksum = 0; + + val = data; + + for (idx = 0; idx < 7; idx++) + checksum ^= val[idx]; + + return checksum; +} + +static void set_prod_idx(struct hinic_api_cmd_chain *chain) +{ + enum hinic_api_cmd_chain_type chain_type = chain->chain_type; + struct hinic_hwif *hwif = chain->hwif; + u32 addr, prod_idx; + + addr = HINIC_CSR_API_CMD_CHAIN_PI_ADDR(chain_type); + prod_idx = hinic_hwif_read_reg(hwif, addr); + + prod_idx = HINIC_API_CMD_PI_CLEAR(prod_idx, IDX); + + prod_idx |= HINIC_API_CMD_PI_SET(chain->prod_idx, IDX); + + hinic_hwif_write_reg(hwif, addr, prod_idx); +} + +static u32 get_hw_cons_idx(struct hinic_api_cmd_chain *chain) +{ + u32 addr, val; + + addr = HINIC_CSR_API_CMD_STATUS_ADDR(chain->chain_type); + val = hinic_hwif_read_reg(chain->hwif, addr); + + return HINIC_API_CMD_STATUS_GET(val, CONS_IDX); +} + +/** + * chain_busy - check if the chain is still processing last requests + * @chain: chain to check + * + * Return 0 - Success, negative - Failure + **/ +static int chain_busy(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + u32 prod_idx; + + switch (chain->chain_type) { + case HINIC_API_CMD_WRITE_TO_MGMT_CPU: + chain->cons_idx = get_hw_cons_idx(chain); + prod_idx = chain->prod_idx; + + /* check for a space for a new command */ + if (chain->cons_idx == MASKED_IDX(chain, prod_idx + 1)) { + dev_err(&pdev->dev, "API CMD chain %d is busy\n", + chain->chain_type); + return -EBUSY; + } + break; + + default: + dev_err(&pdev->dev, "Unknown API CMD Chain type\n"); + break; + } + + return 0; +} + +/** + * get_cell_data_size - get the data size of a specific cell type + * @type: chain type + * + * Return the data(Desc + Address) size in the cell + **/ +static u8 get_cell_data_size(enum hinic_api_cmd_chain_type type) +{ + u8 cell_data_size = 0; + + switch (type) { + case HINIC_API_CMD_WRITE_TO_MGMT_CPU: + cell_data_size = ALIGN(API_CMD_CELL_DESC_SIZE + + API_CMD_CELL_DATA_ADDR_SIZE, + API_CMD_CELL_ALIGNMENT); + break; + default: + break; + } + + return cell_data_size; +} + +/** + * prepare_cell_ctrl - prepare the ctrl of the cell for the command + * @cell_ctrl: the control of the cell to set the control value into it + * @data_size: the size of the data in the cell + **/ +static void prepare_cell_ctrl(u64 *cell_ctrl, u16 data_size) +{ + u8 chksum; + u64 ctrl; + + ctrl = HINIC_API_CMD_CELL_CTRL_SET(SIZE_8BYTES(data_size), DATA_SZ) | + HINIC_API_CMD_CELL_CTRL_SET(RD_DMA_ATTR_DEFAULT, RD_DMA_ATTR) | + HINIC_API_CMD_CELL_CTRL_SET(WR_DMA_ATTR_DEFAULT, WR_DMA_ATTR); + + chksum = xor_chksum_set(&ctrl); + + ctrl |= HINIC_API_CMD_CELL_CTRL_SET(chksum, XOR_CHKSUM); + + /* The data in the HW should be in Big Endian Format */ + *cell_ctrl = cpu_to_be64(ctrl); +} + +/** + * prepare_api_cmd - prepare API CMD command + * @chain: chain for the command + * @dest: destination node on the card that will receive the command + * @cmd: command data + * @cmd_size: the command size + **/ +static void prepare_api_cmd(struct hinic_api_cmd_chain *chain, + enum hinic_node_id dest, + void *cmd, u16 cmd_size) +{ + struct hinic_api_cmd_cell *cell = chain->curr_node; + struct hinic_api_cmd_cell_ctxt *cell_ctxt; + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + + cell_ctxt = &chain->cell_ctxt[chain->prod_idx]; + + switch (chain->chain_type) { + case HINIC_API_CMD_WRITE_TO_MGMT_CPU: + cell->desc = HINIC_API_CMD_DESC_SET(SGE_DATA, API_TYPE) | + HINIC_API_CMD_DESC_SET(API_CMD_WRITE, RD_WR) | + HINIC_API_CMD_DESC_SET(NO_BYPASS, MGMT_BYPASS); + break; + + default: + dev_err(&pdev->dev, "unknown Chain type\n"); + return; + } + + cell->desc |= HINIC_API_CMD_DESC_SET(dest, DEST) | + HINIC_API_CMD_DESC_SET(SIZE_4BYTES(cmd_size), SIZE); + + cell->desc |= HINIC_API_CMD_DESC_SET(xor_chksum_set(&cell->desc), + XOR_CHKSUM); + + /* The data in the HW should be in Big Endian Format */ + cell->desc = cpu_to_be64(cell->desc); + + memcpy(cell_ctxt->api_cmd_vaddr, cmd, cmd_size); +} + +/** + * prepare_cell - prepare cell ctrl and cmd in the current cell + * @chain: chain for the command + * @dest: destination node on the card that will receive the command + * @cmd: command data + * @cmd_size: the command size + * + * Return 0 - Success, negative - Failure + **/ +static void prepare_cell(struct hinic_api_cmd_chain *chain, + enum hinic_node_id dest, + void *cmd, u16 cmd_size) +{ + struct hinic_api_cmd_cell *curr_node = chain->curr_node; + u16 data_size = get_cell_data_size(chain->chain_type); + + prepare_cell_ctrl(&curr_node->ctrl, data_size); + prepare_api_cmd(chain, dest, cmd, cmd_size); +} + +static inline void cmd_chain_prod_idx_inc(struct hinic_api_cmd_chain *chain) +{ + chain->prod_idx = MASKED_IDX(chain, chain->prod_idx + 1); +} + +/** + * api_cmd_status_update - update the status in the chain struct + * @chain: chain to update + **/ +static void api_cmd_status_update(struct hinic_api_cmd_chain *chain) +{ + enum hinic_api_cmd_chain_type chain_type; + struct hinic_api_cmd_status *wb_status; + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + u64 status_header; + u32 status; + + wb_status = chain->wb_status; + status_header = be64_to_cpu(wb_status->header); + + status = be32_to_cpu(wb_status->status); + if (HINIC_API_CMD_STATUS_GET(status, CHKSUM_ERR)) { + dev_err(&pdev->dev, "API CMD status: Xor check error\n"); + return; + } + + chain_type = HINIC_API_CMD_STATUS_HEADER_GET(status_header, CHAIN_ID); + if (chain_type >= HINIC_API_CMD_MAX) { + dev_err(&pdev->dev, "unknown API CMD Chain %d\n", chain_type); + return; + } + + chain->cons_idx = HINIC_API_CMD_STATUS_GET(status, CONS_IDX); +} + +/** + * wait_for_status_poll - wait for write to api cmd command to complete + * @chain: the chain of the command + * + * Return 0 - Success, negative - Failure + **/ +static int wait_for_status_poll(struct hinic_api_cmd_chain *chain) +{ + int err = -ETIMEDOUT; + unsigned long end; + + end = jiffies + msecs_to_jiffies(API_CMD_TIMEOUT); + do { + api_cmd_status_update(chain); + + /* wait for CI to be updated - sign for completion */ + if (chain->cons_idx == chain->prod_idx) { + err = 0; + break; + } + + msleep(20); + } while (time_before(jiffies, end)); + + return err; +} + +/** + * wait_for_api_cmd_completion - wait for command to complete + * @chain: chain for the command + * + * Return 0 - Success, negative - Failure + **/ +static int wait_for_api_cmd_completion(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + int err; + + switch (chain->chain_type) { + case HINIC_API_CMD_WRITE_TO_MGMT_CPU: + err = wait_for_status_poll(chain); + if (err) { + dev_err(&pdev->dev, "API CMD Poll status timeout\n"); + break; + } + break; + + default: + dev_err(&pdev->dev, "unknown API CMD Chain type\n"); + err = -EINVAL; + break; + } + + return err; +} + +/** + * api_cmd - API CMD command + * @chain: chain for the command + * @dest: destination node on the card that will receive the command + * @cmd: command data + * @size: the command size + * + * Return 0 - Success, negative - Failure + **/ +static int api_cmd(struct hinic_api_cmd_chain *chain, + enum hinic_node_id dest, u8 *cmd, u16 cmd_size) +{ + struct hinic_api_cmd_cell_ctxt *ctxt; + int err; + + down(&chain->sem); + if (chain_busy(chain)) { + up(&chain->sem); + return -EBUSY; + } + + prepare_cell(chain, dest, cmd, cmd_size); + cmd_chain_prod_idx_inc(chain); + + wmb(); /* inc pi before issue the command */ + + set_prod_idx(chain); /* issue the command */ + + ctxt = &chain->cell_ctxt[chain->prod_idx]; + + chain->curr_node = ctxt->cell_vaddr; + + err = wait_for_api_cmd_completion(chain); + + up(&chain->sem); + return err; +} + +/** + * hinic_api_cmd_write - Write API CMD command + * @chain: chain for write command + * @dest: destination node on the card that will receive the command + * @cmd: command data + * @size: the command size + * + * Return 0 - Success, negative - Failure + **/ +int hinic_api_cmd_write(struct hinic_api_cmd_chain *chain, + enum hinic_node_id dest, u8 *cmd, u16 size) +{ + /* Verify the chain type */ + if (chain->chain_type == HINIC_API_CMD_WRITE_TO_MGMT_CPU) + return api_cmd(chain, dest, cmd, size); + + return -EINVAL; +} + +/** + * api_cmd_hw_restart - restart the chain in the HW + * @chain: the API CMD specific chain to restart + * + * Return 0 - Success, negative - Failure + **/ +static int api_cmd_hw_restart(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + int err = -ETIMEDOUT; + unsigned long end; + u32 reg_addr, val; + + /* Read Modify Write */ + reg_addr = HINIC_CSR_API_CMD_CHAIN_REQ_ADDR(chain->chain_type); + val = hinic_hwif_read_reg(hwif, reg_addr); + + val = HINIC_API_CMD_CHAIN_REQ_CLEAR(val, RESTART); + val |= HINIC_API_CMD_CHAIN_REQ_SET(1, RESTART); + + hinic_hwif_write_reg(hwif, reg_addr, val); + + end = jiffies + msecs_to_jiffies(API_CMD_TIMEOUT); + do { + val = hinic_hwif_read_reg(hwif, reg_addr); + + if (!HINIC_API_CMD_CHAIN_REQ_GET(val, RESTART)) { + err = 0; + break; + } + + msleep(20); + } while (time_before(jiffies, end)); + + return err; +} + +/** + * api_cmd_ctrl_init - set the control register of a chain + * @chain: the API CMD specific chain to set control register for + **/ +static void api_cmd_ctrl_init(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + u32 addr, ctrl; + u16 cell_size; + + /* Read Modify Write */ + addr = HINIC_CSR_API_CMD_CHAIN_CTRL_ADDR(chain->chain_type); + + cell_size = API_CMD_CELL_SIZE_VAL(chain->cell_size); + + ctrl = hinic_hwif_read_reg(hwif, addr); + + ctrl = HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, RESTART_WB_STAT) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, XOR_ERR) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, AEQE_EN) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, XOR_CHK_EN) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, CELL_SIZE); + + ctrl |= HINIC_API_CMD_CHAIN_CTRL_SET(1, XOR_ERR) | + HINIC_API_CMD_CHAIN_CTRL_SET(XOR_CHK_ALL, XOR_CHK_EN) | + HINIC_API_CMD_CHAIN_CTRL_SET(cell_size, CELL_SIZE); + + hinic_hwif_write_reg(hwif, addr, ctrl); +} + +/** + * api_cmd_set_status_addr - set the status address of a chain in the HW + * @chain: the API CMD specific chain to set in HW status address for + **/ +static void api_cmd_set_status_addr(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + u32 addr, val; + + addr = HINIC_CSR_API_CMD_STATUS_HI_ADDR(chain->chain_type); + val = upper_32_bits(chain->wb_status_paddr); + hinic_hwif_write_reg(hwif, addr, val); + + addr = HINIC_CSR_API_CMD_STATUS_LO_ADDR(chain->chain_type); + val = lower_32_bits(chain->wb_status_paddr); + hinic_hwif_write_reg(hwif, addr, val); +} + +/** + * api_cmd_set_num_cells - set the number cells of a chain in the HW + * @chain: the API CMD specific chain to set in HW the number of cells for + **/ +static void api_cmd_set_num_cells(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + u32 addr, val; + + addr = HINIC_CSR_API_CMD_CHAIN_NUM_CELLS_ADDR(chain->chain_type); + val = chain->num_cells; + hinic_hwif_write_reg(hwif, addr, val); +} + +/** + * api_cmd_head_init - set the head of a chain in the HW + * @chain: the API CMD specific chain to set in HW the head for + **/ +static void api_cmd_head_init(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + u32 addr, val; + + addr = HINIC_CSR_API_CMD_CHAIN_HEAD_HI_ADDR(chain->chain_type); + val = upper_32_bits(chain->head_cell_paddr); + hinic_hwif_write_reg(hwif, addr, val); + + addr = HINIC_CSR_API_CMD_CHAIN_HEAD_LO_ADDR(chain->chain_type); + val = lower_32_bits(chain->head_cell_paddr); + hinic_hwif_write_reg(hwif, addr, val); +} + +/** + * api_cmd_chain_hw_clean - clean the HW + * @chain: the API CMD specific chain + **/ +static void api_cmd_chain_hw_clean(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + u32 addr, ctrl; + + addr = HINIC_CSR_API_CMD_CHAIN_CTRL_ADDR(chain->chain_type); + + ctrl = hinic_hwif_read_reg(hwif, addr); + ctrl = HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, RESTART_WB_STAT) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, XOR_ERR) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, AEQE_EN) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, XOR_CHK_EN) & + HINIC_API_CMD_CHAIN_CTRL_CLEAR(ctrl, CELL_SIZE); + + hinic_hwif_write_reg(hwif, addr, ctrl); +} + +/** + * api_cmd_chain_hw_init - initialize the chain in the HW + * @chain: the API CMD specific chain to initialize in HW + * + * Return 0 - Success, negative - Failure + **/ +static int api_cmd_chain_hw_init(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + int err; + + api_cmd_chain_hw_clean(chain); + + api_cmd_set_status_addr(chain); + + err = api_cmd_hw_restart(chain); + if (err) { + dev_err(&pdev->dev, "Failed to restart API CMD HW\n"); + return err; + } + + api_cmd_ctrl_init(chain); + api_cmd_set_num_cells(chain); + api_cmd_head_init(chain); + return 0; +} + +/** + * free_cmd_buf - free the dma buffer of API CMD command + * @chain: the API CMD specific chain of the cmd + * @cell_idx: the cell index of the cmd + **/ +static void free_cmd_buf(struct hinic_api_cmd_chain *chain, int cell_idx) +{ + struct hinic_api_cmd_cell_ctxt *cell_ctxt; + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + + cell_ctxt = &chain->cell_ctxt[cell_idx]; + + dma_free_coherent(&pdev->dev, API_CMD_BUF_SIZE, + cell_ctxt->api_cmd_vaddr, + cell_ctxt->api_cmd_paddr); +} + +/** + * alloc_cmd_buf - allocate a dma buffer for API CMD command + * @chain: the API CMD specific chain for the cmd + * @cell: the cell in the HW for the cmd + * @cell_idx: the index of the cell + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_cmd_buf(struct hinic_api_cmd_chain *chain, + struct hinic_api_cmd_cell *cell, int cell_idx) +{ + struct hinic_api_cmd_cell_ctxt *cell_ctxt; + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + dma_addr_t cmd_paddr; + u8 *cmd_vaddr; + int err = 0; + + cmd_vaddr = dma_zalloc_coherent(&pdev->dev, API_CMD_BUF_SIZE, + &cmd_paddr, GFP_KERNEL); + if (!cmd_vaddr) { + dev_err(&pdev->dev, "Failed to allocate API CMD DMA memory\n"); + return -ENOMEM; + } + + cell_ctxt = &chain->cell_ctxt[cell_idx]; + + cell_ctxt->api_cmd_vaddr = cmd_vaddr; + cell_ctxt->api_cmd_paddr = cmd_paddr; + + /* set the cmd DMA address in the cell */ + switch (chain->chain_type) { + case HINIC_API_CMD_WRITE_TO_MGMT_CPU: + /* The data in the HW should be in Big Endian Format */ + cell->write.hw_cmd_paddr = cpu_to_be64(cmd_paddr); + break; + + default: + dev_err(&pdev->dev, "Unsupported API CMD chain type\n"); + free_cmd_buf(chain, cell_idx); + err = -EINVAL; + break; + } + + return err; +} + +/** + * api_cmd_create_cell - create API CMD cell for specific chain + * @chain: the API CMD specific chain to create its cell + * @cell_idx: the index of the cell to create + * @pre_node: previous cell + * @node_vaddr: the returned virt addr of the cell + * + * Return 0 - Success, negative - Failure + **/ +static int api_cmd_create_cell(struct hinic_api_cmd_chain *chain, + int cell_idx, + struct hinic_api_cmd_cell *pre_node, + struct hinic_api_cmd_cell **node_vaddr) +{ + struct hinic_api_cmd_cell_ctxt *cell_ctxt; + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_api_cmd_cell *node; + dma_addr_t node_paddr; + int err; + + node = dma_zalloc_coherent(&pdev->dev, chain->cell_size, + &node_paddr, GFP_KERNEL); + if (!node) { + dev_err(&pdev->dev, "Failed to allocate dma API CMD cell\n"); + return -ENOMEM; + } + + node->read.hw_wb_resp_paddr = 0; + + cell_ctxt = &chain->cell_ctxt[cell_idx]; + cell_ctxt->cell_vaddr = node; + cell_ctxt->cell_paddr = node_paddr; + + if (!pre_node) { + chain->head_cell_paddr = node_paddr; + chain->head_node = node; + } else { + /* The data in the HW should be in Big Endian Format */ + pre_node->next_cell_paddr = cpu_to_be64(node_paddr); + } + + switch (chain->chain_type) { + case HINIC_API_CMD_WRITE_TO_MGMT_CPU: + err = alloc_cmd_buf(chain, node, cell_idx); + if (err) { + dev_err(&pdev->dev, "Failed to allocate cmd buffer\n"); + goto err_alloc_cmd_buf; + } + break; + + default: + dev_err(&pdev->dev, "Unsupported API CMD chain type\n"); + err = -EINVAL; + goto err_alloc_cmd_buf; + } + + *node_vaddr = node; + return 0; + +err_alloc_cmd_buf: + dma_free_coherent(&pdev->dev, chain->cell_size, node, node_paddr); + return err; +} + +/** + * api_cmd_destroy_cell - destroy API CMD cell of specific chain + * @chain: the API CMD specific chain to destroy its cell + * @cell_idx: the cell to destroy + **/ +static void api_cmd_destroy_cell(struct hinic_api_cmd_chain *chain, + int cell_idx) +{ + struct hinic_api_cmd_cell_ctxt *cell_ctxt; + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_api_cmd_cell *node; + dma_addr_t node_paddr; + size_t node_size; + + cell_ctxt = &chain->cell_ctxt[cell_idx]; + + node = cell_ctxt->cell_vaddr; + node_paddr = cell_ctxt->cell_paddr; + node_size = chain->cell_size; + + if (cell_ctxt->api_cmd_vaddr) { + switch (chain->chain_type) { + case HINIC_API_CMD_WRITE_TO_MGMT_CPU: + free_cmd_buf(chain, cell_idx); + break; + default: + dev_err(&pdev->dev, "Unsupported API CMD chain type\n"); + break; + } + + dma_free_coherent(&pdev->dev, node_size, node, + node_paddr); + } +} + +/** + * api_cmd_destroy_cells - destroy API CMD cells of specific chain + * @chain: the API CMD specific chain to destroy its cells + * @num_cells: number of cells to destroy + **/ +static void api_cmd_destroy_cells(struct hinic_api_cmd_chain *chain, + int num_cells) +{ + int cell_idx; + + for (cell_idx = 0; cell_idx < num_cells; cell_idx++) + api_cmd_destroy_cell(chain, cell_idx); +} + +/** + * api_cmd_create_cells - create API CMD cells for specific chain + * @chain: the API CMD specific chain + * + * Return 0 - Success, negative - Failure + **/ +static int api_cmd_create_cells(struct hinic_api_cmd_chain *chain) +{ + struct hinic_api_cmd_cell *node = NULL, *pre_node = NULL; + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + int err, cell_idx; + + for (cell_idx = 0; cell_idx < chain->num_cells; cell_idx++) { + err = api_cmd_create_cell(chain, cell_idx, pre_node, &node); + if (err) { + dev_err(&pdev->dev, "Failed to create API CMD cell\n"); + goto err_create_cell; + } + + pre_node = node; + } + + /* set the Final node to point on the start */ + node->next_cell_paddr = cpu_to_be64(chain->head_cell_paddr); + + /* set the current node to be the head */ + chain->curr_node = chain->head_node; + return 0; + +err_create_cell: + api_cmd_destroy_cells(chain, cell_idx); + return err; +} + +/** + * api_chain_init - initialize API CMD specific chain + * @chain: the API CMD specific chain to initialize + * @attr: attributes to set in the chain + * + * Return 0 - Success, negative - Failure + **/ +static int api_chain_init(struct hinic_api_cmd_chain *chain, + struct hinic_api_cmd_chain_attr *attr) +{ + struct hinic_hwif *hwif = attr->hwif; + struct pci_dev *pdev = hwif->pdev; + size_t cell_ctxt_size; + + chain->hwif = hwif; + chain->chain_type = attr->chain_type; + chain->num_cells = attr->num_cells; + chain->cell_size = attr->cell_size; + + chain->prod_idx = 0; + chain->cons_idx = 0; + + sema_init(&chain->sem, 1); + + cell_ctxt_size = chain->num_cells * sizeof(*chain->cell_ctxt); + chain->cell_ctxt = devm_kzalloc(&pdev->dev, cell_ctxt_size, GFP_KERNEL); + if (!chain->cell_ctxt) + return -ENOMEM; + + chain->wb_status = dma_zalloc_coherent(&pdev->dev, + sizeof(*chain->wb_status), + &chain->wb_status_paddr, + GFP_KERNEL); + if (!chain->wb_status) { + dev_err(&pdev->dev, "Failed to allocate DMA wb status\n"); + return -ENOMEM; + } + + return 0; +} + +/** + * api_chain_free - free API CMD specific chain + * @chain: the API CMD specific chain to free + **/ +static void api_chain_free(struct hinic_api_cmd_chain *chain) +{ + struct hinic_hwif *hwif = chain->hwif; + struct pci_dev *pdev = hwif->pdev; + + dma_free_coherent(&pdev->dev, sizeof(*chain->wb_status), + chain->wb_status, chain->wb_status_paddr); +} + +/** + * api_cmd_create_chain - create API CMD specific chain + * @attr: attributes to set the chain + * + * Return the created chain + **/ +static struct hinic_api_cmd_chain * + api_cmd_create_chain(struct hinic_api_cmd_chain_attr *attr) +{ + struct hinic_hwif *hwif = attr->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_api_cmd_chain *chain; + int err; + + if (attr->num_cells & (attr->num_cells - 1)) { + dev_err(&pdev->dev, "Invalid number of cells, must be power of 2\n"); + return ERR_PTR(-EINVAL); + } + + chain = devm_kzalloc(&pdev->dev, sizeof(*chain), GFP_KERNEL); + if (!chain) + return ERR_PTR(-ENOMEM); + + err = api_chain_init(chain, attr); + if (err) { + dev_err(&pdev->dev, "Failed to initialize chain\n"); + return ERR_PTR(err); + } + + err = api_cmd_create_cells(chain); + if (err) { + dev_err(&pdev->dev, "Failed to create cells for API CMD chain\n"); + goto err_create_cells; + } + + err = api_cmd_chain_hw_init(chain); + if (err) { + dev_err(&pdev->dev, "Failed to initialize chain HW\n"); + goto err_chain_hw_init; + } + + return chain; + +err_chain_hw_init: + api_cmd_destroy_cells(chain, chain->num_cells); + +err_create_cells: + api_chain_free(chain); + return ERR_PTR(err); +} + +/** + * api_cmd_destroy_chain - destroy API CMD specific chain + * @chain: the API CMD specific chain to destroy + **/ +static void api_cmd_destroy_chain(struct hinic_api_cmd_chain *chain) +{ + api_cmd_chain_hw_clean(chain); + api_cmd_destroy_cells(chain, chain->num_cells); + api_chain_free(chain); +} + +/** + * hinic_api_cmd_init - Initialize all the API CMD chains + * @chain: the API CMD chains that are initialized + * @hwif: the hardware interface of a pci function device + * + * Return 0 - Success, negative - Failure + **/ +int hinic_api_cmd_init(struct hinic_api_cmd_chain **chain, + struct hinic_hwif *hwif) +{ + enum hinic_api_cmd_chain_type type, chain_type; + struct hinic_api_cmd_chain_attr attr; + struct pci_dev *pdev = hwif->pdev; + size_t hw_cell_sz; + int err; + + hw_cell_sz = sizeof(struct hinic_api_cmd_cell); + + attr.hwif = hwif; + attr.num_cells = API_CHAIN_NUM_CELLS; + attr.cell_size = API_CMD_CELL_SIZE(hw_cell_sz); + + chain_type = HINIC_API_CMD_WRITE_TO_MGMT_CPU; + for ( ; chain_type < HINIC_API_CMD_MAX; chain_type++) { + attr.chain_type = chain_type; + + if (chain_type != HINIC_API_CMD_WRITE_TO_MGMT_CPU) + continue; + + chain[chain_type] = api_cmd_create_chain(&attr); + if (IS_ERR(chain[chain_type])) { + dev_err(&pdev->dev, "Failed to create chain %d\n", + chain_type); + err = PTR_ERR(chain[chain_type]); + goto err_create_chain; + } + } + + return 0; + +err_create_chain: + type = HINIC_API_CMD_WRITE_TO_MGMT_CPU; + for ( ; type < chain_type; type++) { + if (type != HINIC_API_CMD_WRITE_TO_MGMT_CPU) + continue; + + api_cmd_destroy_chain(chain[type]); + } + + return err; +} + +/** + * hinic_api_cmd_free - free the API CMD chains + * @chain: the API CMD chains that are freed + **/ +void hinic_api_cmd_free(struct hinic_api_cmd_chain **chain) +{ + enum hinic_api_cmd_chain_type chain_type; + + chain_type = HINIC_API_CMD_WRITE_TO_MGMT_CPU; + for ( ; chain_type < HINIC_API_CMD_MAX; chain_type++) { + if (chain_type != HINIC_API_CMD_WRITE_TO_MGMT_CPU) + continue; + + api_cmd_destroy_chain(chain[chain_type]); + } +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h new file mode 100644 index 000000000000..31b94d5d47f7 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_api_cmd.h @@ -0,0 +1,208 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_API_CMD_H +#define HINIC_HW_API_CMD_H + +#include <linux/types.h> +#include <linux/semaphore.h> + +#include "hinic_hw_if.h" + +#define HINIC_API_CMD_PI_IDX_SHIFT 0 + +#define HINIC_API_CMD_PI_IDX_MASK 0xFFFFFF + +#define HINIC_API_CMD_PI_SET(val, member) \ + (((u32)(val) & HINIC_API_CMD_PI_##member##_MASK) << \ + HINIC_API_CMD_PI_##member##_SHIFT) + +#define HINIC_API_CMD_PI_CLEAR(val, member) \ + ((val) & (~(HINIC_API_CMD_PI_##member##_MASK \ + << HINIC_API_CMD_PI_##member##_SHIFT))) + +#define HINIC_API_CMD_CHAIN_REQ_RESTART_SHIFT 1 + +#define HINIC_API_CMD_CHAIN_REQ_RESTART_MASK 0x1 + +#define HINIC_API_CMD_CHAIN_REQ_SET(val, member) \ + (((u32)(val) & HINIC_API_CMD_CHAIN_REQ_##member##_MASK) << \ + HINIC_API_CMD_CHAIN_REQ_##member##_SHIFT) + +#define HINIC_API_CMD_CHAIN_REQ_GET(val, member) \ + (((val) >> HINIC_API_CMD_CHAIN_REQ_##member##_SHIFT) & \ + HINIC_API_CMD_CHAIN_REQ_##member##_MASK) + +#define HINIC_API_CMD_CHAIN_REQ_CLEAR(val, member) \ + ((val) & (~(HINIC_API_CMD_CHAIN_REQ_##member##_MASK \ + << HINIC_API_CMD_CHAIN_REQ_##member##_SHIFT))) + +#define HINIC_API_CMD_CHAIN_CTRL_RESTART_WB_STAT_SHIFT 1 +#define HINIC_API_CMD_CHAIN_CTRL_XOR_ERR_SHIFT 2 +#define HINIC_API_CMD_CHAIN_CTRL_AEQE_EN_SHIFT 4 +#define HINIC_API_CMD_CHAIN_CTRL_AEQ_ID_SHIFT 8 +#define HINIC_API_CMD_CHAIN_CTRL_XOR_CHK_EN_SHIFT 28 +#define HINIC_API_CMD_CHAIN_CTRL_CELL_SIZE_SHIFT 30 + +#define HINIC_API_CMD_CHAIN_CTRL_RESTART_WB_STAT_MASK 0x1 +#define HINIC_API_CMD_CHAIN_CTRL_XOR_ERR_MASK 0x1 +#define HINIC_API_CMD_CHAIN_CTRL_AEQE_EN_MASK 0x1 +#define HINIC_API_CMD_CHAIN_CTRL_AEQ_ID_MASK 0x3 +#define HINIC_API_CMD_CHAIN_CTRL_XOR_CHK_EN_MASK 0x3 +#define HINIC_API_CMD_CHAIN_CTRL_CELL_SIZE_MASK 0x3 + +#define HINIC_API_CMD_CHAIN_CTRL_SET(val, member) \ + (((u32)(val) & HINIC_API_CMD_CHAIN_CTRL_##member##_MASK) << \ + HINIC_API_CMD_CHAIN_CTRL_##member##_SHIFT) + +#define HINIC_API_CMD_CHAIN_CTRL_CLEAR(val, member) \ + ((val) & (~(HINIC_API_CMD_CHAIN_CTRL_##member##_MASK \ + << HINIC_API_CMD_CHAIN_CTRL_##member##_SHIFT))) + +#define HINIC_API_CMD_CELL_CTRL_DATA_SZ_SHIFT 0 +#define HINIC_API_CMD_CELL_CTRL_RD_DMA_ATTR_SHIFT 16 +#define HINIC_API_CMD_CELL_CTRL_WR_DMA_ATTR_SHIFT 24 +#define HINIC_API_CMD_CELL_CTRL_XOR_CHKSUM_SHIFT 56 + +#define HINIC_API_CMD_CELL_CTRL_DATA_SZ_MASK 0x3F +#define HINIC_API_CMD_CELL_CTRL_RD_DMA_ATTR_MASK 0x3F +#define HINIC_API_CMD_CELL_CTRL_WR_DMA_ATTR_MASK 0x3F +#define HINIC_API_CMD_CELL_CTRL_XOR_CHKSUM_MASK 0xFF + +#define HINIC_API_CMD_CELL_CTRL_SET(val, member) \ + ((((u64)val) & HINIC_API_CMD_CELL_CTRL_##member##_MASK) << \ + HINIC_API_CMD_CELL_CTRL_##member##_SHIFT) + +#define HINIC_API_CMD_DESC_API_TYPE_SHIFT 0 +#define HINIC_API_CMD_DESC_RD_WR_SHIFT 1 +#define HINIC_API_CMD_DESC_MGMT_BYPASS_SHIFT 2 +#define HINIC_API_CMD_DESC_DEST_SHIFT 32 +#define HINIC_API_CMD_DESC_SIZE_SHIFT 40 +#define HINIC_API_CMD_DESC_XOR_CHKSUM_SHIFT 56 + +#define HINIC_API_CMD_DESC_API_TYPE_MASK 0x1 +#define HINIC_API_CMD_DESC_RD_WR_MASK 0x1 +#define HINIC_API_CMD_DESC_MGMT_BYPASS_MASK 0x1 +#define HINIC_API_CMD_DESC_DEST_MASK 0x1F +#define HINIC_API_CMD_DESC_SIZE_MASK 0x7FF +#define HINIC_API_CMD_DESC_XOR_CHKSUM_MASK 0xFF + +#define HINIC_API_CMD_DESC_SET(val, member) \ + ((((u64)val) & HINIC_API_CMD_DESC_##member##_MASK) << \ + HINIC_API_CMD_DESC_##member##_SHIFT) + +#define HINIC_API_CMD_STATUS_HEADER_CHAIN_ID_SHIFT 16 + +#define HINIC_API_CMD_STATUS_HEADER_CHAIN_ID_MASK 0xFF + +#define HINIC_API_CMD_STATUS_HEADER_GET(val, member) \ + (((val) >> HINIC_API_CMD_STATUS_HEADER_##member##_SHIFT) & \ + HINIC_API_CMD_STATUS_HEADER_##member##_MASK) + +#define HINIC_API_CMD_STATUS_CONS_IDX_SHIFT 0 +#define HINIC_API_CMD_STATUS_CHKSUM_ERR_SHIFT 28 + +#define HINIC_API_CMD_STATUS_CONS_IDX_MASK 0xFFFFFF +#define HINIC_API_CMD_STATUS_CHKSUM_ERR_MASK 0x3 + +#define HINIC_API_CMD_STATUS_GET(val, member) \ + (((val) >> HINIC_API_CMD_STATUS_##member##_SHIFT) & \ + HINIC_API_CMD_STATUS_##member##_MASK) + +enum hinic_api_cmd_chain_type { + HINIC_API_CMD_WRITE_TO_MGMT_CPU = 2, + + HINIC_API_CMD_MAX, +}; + +struct hinic_api_cmd_chain_attr { + struct hinic_hwif *hwif; + enum hinic_api_cmd_chain_type chain_type; + + u32 num_cells; + u16 cell_size; +}; + +struct hinic_api_cmd_status { + u64 header; + u32 status; + u32 rsvd0; + u32 rsvd1; + u32 rsvd2; + u64 rsvd3; +}; + +/* HW struct */ +struct hinic_api_cmd_cell { + u64 ctrl; + + /* address is 64 bit in HW struct */ + u64 next_cell_paddr; + + u64 desc; + + /* HW struct */ + union { + struct { + u64 hw_cmd_paddr; + } write; + + struct { + u64 hw_wb_resp_paddr; + u64 hw_cmd_paddr; + } read; + }; +}; + +struct hinic_api_cmd_cell_ctxt { + dma_addr_t cell_paddr; + struct hinic_api_cmd_cell *cell_vaddr; + + dma_addr_t api_cmd_paddr; + u8 *api_cmd_vaddr; +}; + +struct hinic_api_cmd_chain { + struct hinic_hwif *hwif; + enum hinic_api_cmd_chain_type chain_type; + + u32 num_cells; + u16 cell_size; + + /* HW members in 24 bit format */ + u32 prod_idx; + u32 cons_idx; + + struct semaphore sem; + + struct hinic_api_cmd_cell_ctxt *cell_ctxt; + + dma_addr_t wb_status_paddr; + struct hinic_api_cmd_status *wb_status; + + dma_addr_t head_cell_paddr; + struct hinic_api_cmd_cell *head_node; + struct hinic_api_cmd_cell *curr_node; +}; + +int hinic_api_cmd_write(struct hinic_api_cmd_chain *chain, + enum hinic_node_id dest, u8 *cmd, u16 size); + +int hinic_api_cmd_init(struct hinic_api_cmd_chain **chain, + struct hinic_hwif *hwif); + +void hinic_api_cmd_free(struct hinic_api_cmd_chain **chain); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c new file mode 100644 index 000000000000..7d95f0866fb0 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.c @@ -0,0 +1,946 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/spinlock.h> +#include <linux/sizes.h> +#include <linux/atomic.h> +#include <linux/log2.h> +#include <linux/io.h> +#include <linux/completion.h> +#include <linux/err.h> +#include <asm/byteorder.h> +#include <asm/barrier.h> + +#include "hinic_common.h" +#include "hinic_hw_if.h" +#include "hinic_hw_eqs.h" +#include "hinic_hw_mgmt.h" +#include "hinic_hw_wqe.h" +#include "hinic_hw_wq.h" +#include "hinic_hw_cmdq.h" +#include "hinic_hw_io.h" +#include "hinic_hw_dev.h" + +#define CMDQ_CEQE_TYPE_SHIFT 0 + +#define CMDQ_CEQE_TYPE_MASK 0x7 + +#define CMDQ_CEQE_GET(val, member) \ + (((val) >> CMDQ_CEQE_##member##_SHIFT) \ + & CMDQ_CEQE_##member##_MASK) + +#define CMDQ_WQE_ERRCODE_VAL_SHIFT 20 + +#define CMDQ_WQE_ERRCODE_VAL_MASK 0xF + +#define CMDQ_WQE_ERRCODE_GET(val, member) \ + (((val) >> CMDQ_WQE_ERRCODE_##member##_SHIFT) \ + & CMDQ_WQE_ERRCODE_##member##_MASK) + +#define CMDQ_DB_PI_OFF(pi) (((u16)LOWER_8_BITS(pi)) << 3) + +#define CMDQ_DB_ADDR(db_base, pi) ((db_base) + CMDQ_DB_PI_OFF(pi)) + +#define CMDQ_WQE_HEADER(wqe) ((struct hinic_cmdq_header *)(wqe)) + +#define CMDQ_WQE_COMPLETED(ctrl_info) \ + HINIC_CMDQ_CTRL_GET(ctrl_info, HW_BUSY_BIT) + +#define FIRST_DATA_TO_WRITE_LAST sizeof(u64) + +#define CMDQ_DB_OFF SZ_2K + +#define CMDQ_WQEBB_SIZE 64 +#define CMDQ_WQE_SIZE 64 +#define CMDQ_DEPTH SZ_4K + +#define CMDQ_WQ_PAGE_SIZE SZ_4K + +#define WQE_LCMD_SIZE 64 +#define WQE_SCMD_SIZE 64 + +#define COMPLETE_LEN 3 + +#define CMDQ_TIMEOUT 1000 + +#define CMDQ_PFN(addr, page_size) ((addr) >> (ilog2(page_size))) + +#define cmdq_to_cmdqs(cmdq) container_of((cmdq) - (cmdq)->cmdq_type, \ + struct hinic_cmdqs, cmdq[0]) + +#define cmdqs_to_func_to_io(cmdqs) container_of(cmdqs, \ + struct hinic_func_to_io, \ + cmdqs) + +enum cmdq_wqe_type { + WQE_LCMD_TYPE = 0, + WQE_SCMD_TYPE = 1, +}; + +enum completion_format { + COMPLETE_DIRECT = 0, + COMPLETE_SGE = 1, +}; + +enum data_format { + DATA_SGE = 0, + DATA_DIRECT = 1, +}; + +enum bufdesc_len { + BUFDESC_LCMD_LEN = 2, /* 16 bytes - 2(8 byte unit) */ + BUFDESC_SCMD_LEN = 3, /* 24 bytes - 3(8 byte unit) */ +}; + +enum ctrl_sect_len { + CTRL_SECT_LEN = 1, /* 4 bytes (ctrl) - 1(8 byte unit) */ + CTRL_DIRECT_SECT_LEN = 2, /* 12 bytes (ctrl + rsvd) - 2(8 byte unit) */ +}; + +enum cmdq_scmd_type { + CMDQ_SET_ARM_CMD = 2, +}; + +enum cmdq_cmd_type { + CMDQ_CMD_SYNC_DIRECT_RESP = 0, + CMDQ_CMD_SYNC_SGE_RESP = 1, +}; + +enum completion_request { + NO_CEQ = 0, + CEQ_SET = 1, +}; + +/** + * hinic_alloc_cmdq_buf - alloc buffer for sending command + * @cmdqs: the cmdqs + * @cmdq_buf: the buffer returned in this struct + * + * Return 0 - Success, negative - Failure + **/ +int hinic_alloc_cmdq_buf(struct hinic_cmdqs *cmdqs, + struct hinic_cmdq_buf *cmdq_buf) +{ + struct hinic_hwif *hwif = cmdqs->hwif; + struct pci_dev *pdev = hwif->pdev; + + cmdq_buf->buf = pci_pool_alloc(cmdqs->cmdq_buf_pool, GFP_KERNEL, + &cmdq_buf->dma_addr); + if (!cmdq_buf->buf) { + dev_err(&pdev->dev, "Failed to allocate cmd from the pool\n"); + return -ENOMEM; + } + + return 0; +} + +/** + * hinic_free_cmdq_buf - free buffer + * @cmdqs: the cmdqs + * @cmdq_buf: the buffer to free that is in this struct + **/ +void hinic_free_cmdq_buf(struct hinic_cmdqs *cmdqs, + struct hinic_cmdq_buf *cmdq_buf) +{ + pci_pool_free(cmdqs->cmdq_buf_pool, cmdq_buf->buf, cmdq_buf->dma_addr); +} + +static unsigned int cmdq_wqe_size_from_bdlen(enum bufdesc_len len) +{ + unsigned int wqe_size = 0; + + switch (len) { + case BUFDESC_LCMD_LEN: + wqe_size = WQE_LCMD_SIZE; + break; + case BUFDESC_SCMD_LEN: + wqe_size = WQE_SCMD_SIZE; + break; + } + + return wqe_size; +} + +static void cmdq_set_sge_completion(struct hinic_cmdq_completion *completion, + struct hinic_cmdq_buf *buf_out) +{ + struct hinic_sge_resp *sge_resp = &completion->sge_resp; + + hinic_set_sge(&sge_resp->sge, buf_out->dma_addr, buf_out->size); +} + +static void cmdq_prepare_wqe_ctrl(struct hinic_cmdq_wqe *wqe, int wrapped, + enum hinic_cmd_ack_type ack_type, + enum hinic_mod_type mod, u8 cmd, u16 prod_idx, + enum completion_format complete_format, + enum data_format data_format, + enum bufdesc_len buf_len) +{ + struct hinic_cmdq_wqe_lcmd *wqe_lcmd; + struct hinic_cmdq_wqe_scmd *wqe_scmd; + enum ctrl_sect_len ctrl_len; + struct hinic_ctrl *ctrl; + u32 saved_data; + + if (data_format == DATA_SGE) { + wqe_lcmd = &wqe->wqe_lcmd; + + wqe_lcmd->status.status_info = 0; + ctrl = &wqe_lcmd->ctrl; + ctrl_len = CTRL_SECT_LEN; + } else { + wqe_scmd = &wqe->direct_wqe.wqe_scmd; + + wqe_scmd->status.status_info = 0; + ctrl = &wqe_scmd->ctrl; + ctrl_len = CTRL_DIRECT_SECT_LEN; + } + + ctrl->ctrl_info = HINIC_CMDQ_CTRL_SET(prod_idx, PI) | + HINIC_CMDQ_CTRL_SET(cmd, CMD) | + HINIC_CMDQ_CTRL_SET(mod, MOD) | + HINIC_CMDQ_CTRL_SET(ack_type, ACK_TYPE); + + CMDQ_WQE_HEADER(wqe)->header_info = + HINIC_CMDQ_WQE_HEADER_SET(buf_len, BUFDESC_LEN) | + HINIC_CMDQ_WQE_HEADER_SET(complete_format, COMPLETE_FMT) | + HINIC_CMDQ_WQE_HEADER_SET(data_format, DATA_FMT) | + HINIC_CMDQ_WQE_HEADER_SET(CEQ_SET, COMPLETE_REQ) | + HINIC_CMDQ_WQE_HEADER_SET(COMPLETE_LEN, COMPLETE_SECT_LEN) | + HINIC_CMDQ_WQE_HEADER_SET(ctrl_len, CTRL_LEN) | + HINIC_CMDQ_WQE_HEADER_SET(wrapped, TOGGLED_WRAPPED); + + saved_data = CMDQ_WQE_HEADER(wqe)->saved_data; + saved_data = HINIC_SAVED_DATA_CLEAR(saved_data, ARM); + + if ((cmd == CMDQ_SET_ARM_CMD) && (mod == HINIC_MOD_COMM)) + CMDQ_WQE_HEADER(wqe)->saved_data |= + HINIC_SAVED_DATA_SET(1, ARM); + else + CMDQ_WQE_HEADER(wqe)->saved_data = saved_data; +} + +static void cmdq_set_lcmd_bufdesc(struct hinic_cmdq_wqe_lcmd *wqe_lcmd, + struct hinic_cmdq_buf *buf_in) +{ + hinic_set_sge(&wqe_lcmd->buf_desc.sge, buf_in->dma_addr, buf_in->size); +} + +static void cmdq_set_direct_wqe_data(struct hinic_cmdq_direct_wqe *wqe, + void *buf_in, u32 in_size) +{ + struct hinic_cmdq_wqe_scmd *wqe_scmd = &wqe->wqe_scmd; + + wqe_scmd->buf_desc.buf_len = in_size; + memcpy(wqe_scmd->buf_desc.data, buf_in, in_size); +} + +static void cmdq_set_lcmd_wqe(struct hinic_cmdq_wqe *wqe, + enum cmdq_cmd_type cmd_type, + struct hinic_cmdq_buf *buf_in, + struct hinic_cmdq_buf *buf_out, int wrapped, + enum hinic_cmd_ack_type ack_type, + enum hinic_mod_type mod, u8 cmd, u16 prod_idx) +{ + struct hinic_cmdq_wqe_lcmd *wqe_lcmd = &wqe->wqe_lcmd; + enum completion_format complete_format; + + switch (cmd_type) { + case CMDQ_CMD_SYNC_SGE_RESP: + complete_format = COMPLETE_SGE; + cmdq_set_sge_completion(&wqe_lcmd->completion, buf_out); + break; + case CMDQ_CMD_SYNC_DIRECT_RESP: + complete_format = COMPLETE_DIRECT; + wqe_lcmd->completion.direct_resp = 0; + break; + } + + cmdq_prepare_wqe_ctrl(wqe, wrapped, ack_type, mod, cmd, + prod_idx, complete_format, DATA_SGE, + BUFDESC_LCMD_LEN); + + cmdq_set_lcmd_bufdesc(wqe_lcmd, buf_in); +} + +static void cmdq_set_direct_wqe(struct hinic_cmdq_wqe *wqe, + enum cmdq_cmd_type cmd_type, + void *buf_in, u16 in_size, + struct hinic_cmdq_buf *buf_out, int wrapped, + enum hinic_cmd_ack_type ack_type, + enum hinic_mod_type mod, u8 cmd, u16 prod_idx) +{ + struct hinic_cmdq_direct_wqe *direct_wqe = &wqe->direct_wqe; + enum completion_format complete_format; + struct hinic_cmdq_wqe_scmd *wqe_scmd; + + wqe_scmd = &direct_wqe->wqe_scmd; + + switch (cmd_type) { + case CMDQ_CMD_SYNC_SGE_RESP: + complete_format = COMPLETE_SGE; + cmdq_set_sge_completion(&wqe_scmd->completion, buf_out); + break; + case CMDQ_CMD_SYNC_DIRECT_RESP: + complete_format = COMPLETE_DIRECT; + wqe_scmd->completion.direct_resp = 0; + break; + } + + cmdq_prepare_wqe_ctrl(wqe, wrapped, ack_type, mod, cmd, prod_idx, + complete_format, DATA_DIRECT, BUFDESC_SCMD_LEN); + + cmdq_set_direct_wqe_data(direct_wqe, buf_in, in_size); +} + +static void cmdq_wqe_fill(void *dst, void *src) +{ + memcpy(dst + FIRST_DATA_TO_WRITE_LAST, src + FIRST_DATA_TO_WRITE_LAST, + CMDQ_WQE_SIZE - FIRST_DATA_TO_WRITE_LAST); + + wmb(); /* The first 8 bytes should be written last */ + + *(u64 *)dst = *(u64 *)src; +} + +static void cmdq_fill_db(u32 *db_info, + enum hinic_cmdq_type cmdq_type, u16 prod_idx) +{ + *db_info = HINIC_CMDQ_DB_INFO_SET(UPPER_8_BITS(prod_idx), HI_PROD_IDX) | + HINIC_CMDQ_DB_INFO_SET(HINIC_CTRL_PATH, PATH) | + HINIC_CMDQ_DB_INFO_SET(cmdq_type, CMDQ_TYPE) | + HINIC_CMDQ_DB_INFO_SET(HINIC_DB_CMDQ_TYPE, DB_TYPE); +} + +static void cmdq_set_db(struct hinic_cmdq *cmdq, + enum hinic_cmdq_type cmdq_type, u16 prod_idx) +{ + u32 db_info; + + cmdq_fill_db(&db_info, cmdq_type, prod_idx); + + /* The data that is written to HW should be in Big Endian Format */ + db_info = cpu_to_be32(db_info); + + wmb(); /* write all before the doorbell */ + + writel(db_info, CMDQ_DB_ADDR(cmdq->db_base, prod_idx)); +} + +static int cmdq_sync_cmd_direct_resp(struct hinic_cmdq *cmdq, + enum hinic_mod_type mod, u8 cmd, + struct hinic_cmdq_buf *buf_in, + u64 *resp) +{ + struct hinic_cmdq_wqe *curr_cmdq_wqe, cmdq_wqe; + u16 curr_prod_idx, next_prod_idx; + int errcode, wrapped, num_wqebbs; + struct hinic_wq *wq = cmdq->wq; + struct hinic_hw_wqe *hw_wqe; + struct completion done; + + /* Keep doorbell index correct. bh - for tasklet(ceq). */ + spin_lock_bh(&cmdq->cmdq_lock); + + /* WQE_SIZE = WQEBB_SIZE, we will get the wq element and not shadow*/ + hw_wqe = hinic_get_wqe(wq, WQE_LCMD_SIZE, &curr_prod_idx); + if (IS_ERR(hw_wqe)) { + spin_unlock_bh(&cmdq->cmdq_lock); + return -EBUSY; + } + + curr_cmdq_wqe = &hw_wqe->cmdq_wqe; + + wrapped = cmdq->wrapped; + + num_wqebbs = ALIGN(WQE_LCMD_SIZE, wq->wqebb_size) / wq->wqebb_size; + next_prod_idx = curr_prod_idx + num_wqebbs; + if (next_prod_idx >= wq->q_depth) { + cmdq->wrapped = !cmdq->wrapped; + next_prod_idx -= wq->q_depth; + } + + cmdq->errcode[curr_prod_idx] = &errcode; + + init_completion(&done); + cmdq->done[curr_prod_idx] = &done; + + cmdq_set_lcmd_wqe(&cmdq_wqe, CMDQ_CMD_SYNC_DIRECT_RESP, buf_in, NULL, + wrapped, HINIC_CMD_ACK_TYPE_CMDQ, mod, cmd, + curr_prod_idx); + + /* The data that is written to HW should be in Big Endian Format */ + hinic_cpu_to_be32(&cmdq_wqe, WQE_LCMD_SIZE); + + /* CMDQ WQE is not shadow, therefore wqe will be written to wq */ + cmdq_wqe_fill(curr_cmdq_wqe, &cmdq_wqe); + + cmdq_set_db(cmdq, HINIC_CMDQ_SYNC, next_prod_idx); + + spin_unlock_bh(&cmdq->cmdq_lock); + + if (!wait_for_completion_timeout(&done, CMDQ_TIMEOUT)) { + spin_lock_bh(&cmdq->cmdq_lock); + + if (cmdq->errcode[curr_prod_idx] == &errcode) + cmdq->errcode[curr_prod_idx] = NULL; + + if (cmdq->done[curr_prod_idx] == &done) + cmdq->done[curr_prod_idx] = NULL; + + spin_unlock_bh(&cmdq->cmdq_lock); + + return -ETIMEDOUT; + } + + smp_rmb(); /* read error code after completion */ + + if (resp) { + struct hinic_cmdq_wqe_lcmd *wqe_lcmd = &curr_cmdq_wqe->wqe_lcmd; + + *resp = cpu_to_be64(wqe_lcmd->completion.direct_resp); + } + + if (errcode != 0) + return -EFAULT; + + return 0; +} + +static int cmdq_set_arm_bit(struct hinic_cmdq *cmdq, void *buf_in, + u16 in_size) +{ + struct hinic_cmdq_wqe *curr_cmdq_wqe, cmdq_wqe; + u16 curr_prod_idx, next_prod_idx; + struct hinic_wq *wq = cmdq->wq; + struct hinic_hw_wqe *hw_wqe; + int wrapped, num_wqebbs; + + /* Keep doorbell index correct */ + spin_lock(&cmdq->cmdq_lock); + + /* WQE_SIZE = WQEBB_SIZE, we will get the wq element and not shadow*/ + hw_wqe = hinic_get_wqe(wq, WQE_SCMD_SIZE, &curr_prod_idx); + if (IS_ERR(hw_wqe)) { + spin_unlock(&cmdq->cmdq_lock); + return -EBUSY; + } + + curr_cmdq_wqe = &hw_wqe->cmdq_wqe; + + wrapped = cmdq->wrapped; + + num_wqebbs = ALIGN(WQE_SCMD_SIZE, wq->wqebb_size) / wq->wqebb_size; + next_prod_idx = curr_prod_idx + num_wqebbs; + if (next_prod_idx >= wq->q_depth) { + cmdq->wrapped = !cmdq->wrapped; + next_prod_idx -= wq->q_depth; + } + + cmdq_set_direct_wqe(&cmdq_wqe, CMDQ_CMD_SYNC_DIRECT_RESP, buf_in, + in_size, NULL, wrapped, HINIC_CMD_ACK_TYPE_CMDQ, + HINIC_MOD_COMM, CMDQ_SET_ARM_CMD, curr_prod_idx); + + /* The data that is written to HW should be in Big Endian Format */ + hinic_cpu_to_be32(&cmdq_wqe, WQE_SCMD_SIZE); + + /* cmdq wqe is not shadow, therefore wqe will be written to wq */ + cmdq_wqe_fill(curr_cmdq_wqe, &cmdq_wqe); + + cmdq_set_db(cmdq, HINIC_CMDQ_SYNC, next_prod_idx); + + spin_unlock(&cmdq->cmdq_lock); + return 0; +} + +static int cmdq_params_valid(struct hinic_cmdq_buf *buf_in) +{ + if (buf_in->size > HINIC_CMDQ_MAX_DATA_SIZE) + return -EINVAL; + + return 0; +} + +/** + * hinic_cmdq_direct_resp - send command with direct data as resp + * @cmdqs: the cmdqs + * @mod: module on the card that will handle the command + * @cmd: the command + * @buf_in: the buffer for the command + * @resp: the response to return + * + * Return 0 - Success, negative - Failure + **/ +int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs, + enum hinic_mod_type mod, u8 cmd, + struct hinic_cmdq_buf *buf_in, u64 *resp) +{ + struct hinic_hwif *hwif = cmdqs->hwif; + struct pci_dev *pdev = hwif->pdev; + int err; + + err = cmdq_params_valid(buf_in); + if (err) { + dev_err(&pdev->dev, "Invalid CMDQ parameters\n"); + return err; + } + + return cmdq_sync_cmd_direct_resp(&cmdqs->cmdq[HINIC_CMDQ_SYNC], + mod, cmd, buf_in, resp); +} + +/** + * hinic_set_arm_bit - set arm bit for enable interrupt again + * @cmdqs: the cmdqs + * @q_type: type of queue to set the arm bit for + * @q_id: the queue number + * + * Return 0 - Success, negative - Failure + **/ +int hinic_set_arm_bit(struct hinic_cmdqs *cmdqs, + enum hinic_set_arm_qtype q_type, u32 q_id) +{ + struct hinic_cmdq *cmdq = &cmdqs->cmdq[HINIC_CMDQ_SYNC]; + struct hinic_hwif *hwif = cmdqs->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_cmdq_arm_bit arm_bit; + int err; + + arm_bit.q_type = q_type; + arm_bit.q_id = q_id; + + err = cmdq_set_arm_bit(cmdq, &arm_bit, sizeof(arm_bit)); + if (err) { + dev_err(&pdev->dev, "Failed to set arm for qid %d\n", q_id); + return err; + } + + return 0; +} + +static void clear_wqe_complete_bit(struct hinic_cmdq *cmdq, + struct hinic_cmdq_wqe *wqe) +{ + u32 header_info = be32_to_cpu(CMDQ_WQE_HEADER(wqe)->header_info); + unsigned int bufdesc_len, wqe_size; + struct hinic_ctrl *ctrl; + + bufdesc_len = HINIC_CMDQ_WQE_HEADER_GET(header_info, BUFDESC_LEN); + wqe_size = cmdq_wqe_size_from_bdlen(bufdesc_len); + if (wqe_size == WQE_LCMD_SIZE) { + struct hinic_cmdq_wqe_lcmd *wqe_lcmd = &wqe->wqe_lcmd; + + ctrl = &wqe_lcmd->ctrl; + } else { + struct hinic_cmdq_direct_wqe *direct_wqe = &wqe->direct_wqe; + struct hinic_cmdq_wqe_scmd *wqe_scmd; + + wqe_scmd = &direct_wqe->wqe_scmd; + ctrl = &wqe_scmd->ctrl; + } + + /* clear HW busy bit */ + ctrl->ctrl_info = 0; + + wmb(); /* verify wqe is clear */ +} + +/** + * cmdq_arm_ceq_handler - cmdq completion event handler for arm command + * @cmdq: the cmdq of the arm command + * @wqe: the wqe of the arm command + * + * Return 0 - Success, negative - Failure + **/ +static int cmdq_arm_ceq_handler(struct hinic_cmdq *cmdq, + struct hinic_cmdq_wqe *wqe) +{ + struct hinic_cmdq_direct_wqe *direct_wqe = &wqe->direct_wqe; + struct hinic_cmdq_wqe_scmd *wqe_scmd; + struct hinic_ctrl *ctrl; + u32 ctrl_info; + + wqe_scmd = &direct_wqe->wqe_scmd; + ctrl = &wqe_scmd->ctrl; + ctrl_info = be32_to_cpu(ctrl->ctrl_info); + + /* HW should toggle the HW BUSY BIT */ + if (!CMDQ_WQE_COMPLETED(ctrl_info)) + return -EBUSY; + + clear_wqe_complete_bit(cmdq, wqe); + + hinic_put_wqe(cmdq->wq, WQE_SCMD_SIZE); + return 0; +} + +static void cmdq_update_errcode(struct hinic_cmdq *cmdq, u16 prod_idx, + int errcode) +{ + if (cmdq->errcode[prod_idx]) + *cmdq->errcode[prod_idx] = errcode; +} + +/** + * cmdq_arm_ceq_handler - cmdq completion event handler for sync command + * @cmdq: the cmdq of the command + * @cons_idx: the consumer index to update the error code for + * @errcode: the error code + **/ +static void cmdq_sync_cmd_handler(struct hinic_cmdq *cmdq, u16 cons_idx, + int errcode) +{ + u16 prod_idx = cons_idx; + + spin_lock(&cmdq->cmdq_lock); + cmdq_update_errcode(cmdq, prod_idx, errcode); + + wmb(); /* write all before update for the command request */ + + if (cmdq->done[prod_idx]) + complete(cmdq->done[prod_idx]); + spin_unlock(&cmdq->cmdq_lock); +} + +static int cmdq_cmd_ceq_handler(struct hinic_cmdq *cmdq, u16 ci, + struct hinic_cmdq_wqe *cmdq_wqe) +{ + struct hinic_cmdq_wqe_lcmd *wqe_lcmd = &cmdq_wqe->wqe_lcmd; + struct hinic_status *status = &wqe_lcmd->status; + struct hinic_ctrl *ctrl = &wqe_lcmd->ctrl; + int errcode; + + if (!CMDQ_WQE_COMPLETED(be32_to_cpu(ctrl->ctrl_info))) + return -EBUSY; + + errcode = CMDQ_WQE_ERRCODE_GET(be32_to_cpu(status->status_info), VAL); + + cmdq_sync_cmd_handler(cmdq, ci, errcode); + + clear_wqe_complete_bit(cmdq, cmdq_wqe); + hinic_put_wqe(cmdq->wq, WQE_LCMD_SIZE); + return 0; +} + +/** + * cmdq_ceq_handler - cmdq completion event handler + * @handle: private data for the handler(cmdqs) + * @ceqe_data: ceq element data + **/ +static void cmdq_ceq_handler(void *handle, u32 ceqe_data) +{ + enum hinic_cmdq_type cmdq_type = CMDQ_CEQE_GET(ceqe_data, TYPE); + struct hinic_cmdqs *cmdqs = (struct hinic_cmdqs *)handle; + struct hinic_cmdq *cmdq = &cmdqs->cmdq[cmdq_type]; + struct hinic_cmdq_header *header; + struct hinic_hw_wqe *hw_wqe; + int err, set_arm = 0; + u32 saved_data; + u16 ci; + + /* Read the smallest wqe size for getting wqe size */ + while ((hw_wqe = hinic_read_wqe(cmdq->wq, WQE_SCMD_SIZE, &ci))) { + if (IS_ERR(hw_wqe)) + break; + + header = CMDQ_WQE_HEADER(&hw_wqe->cmdq_wqe); + saved_data = be32_to_cpu(header->saved_data); + + if (HINIC_SAVED_DATA_GET(saved_data, ARM)) { + /* arm_bit was set until here */ + set_arm = 0; + + if (cmdq_arm_ceq_handler(cmdq, &hw_wqe->cmdq_wqe)) + break; + } else { + set_arm = 1; + + hw_wqe = hinic_read_wqe(cmdq->wq, WQE_LCMD_SIZE, &ci); + if (IS_ERR(hw_wqe)) + break; + + if (cmdq_cmd_ceq_handler(cmdq, ci, &hw_wqe->cmdq_wqe)) + break; + } + } + + if (set_arm) { + struct hinic_hwif *hwif = cmdqs->hwif; + struct pci_dev *pdev = hwif->pdev; + + err = hinic_set_arm_bit(cmdqs, HINIC_SET_ARM_CMDQ, cmdq_type); + if (err) + dev_err(&pdev->dev, "Failed to set arm for CMDQ\n"); + } +} + +/** + * cmdq_init_queue_ctxt - init the queue ctxt of a cmdq + * @cmdq_ctxt: cmdq ctxt to initialize + * @cmdq: the cmdq + * @cmdq_pages: the memory of the queue + **/ +static void cmdq_init_queue_ctxt(struct hinic_cmdq_ctxt *cmdq_ctxt, + struct hinic_cmdq *cmdq, + struct hinic_cmdq_pages *cmdq_pages) +{ + struct hinic_cmdq_ctxt_info *ctxt_info = &cmdq_ctxt->ctxt_info; + u64 wq_first_page_paddr, cmdq_first_block_paddr, pfn; + struct hinic_cmdqs *cmdqs = cmdq_to_cmdqs(cmdq); + struct hinic_wq *wq = cmdq->wq; + + /* The data in the HW is in Big Endian Format */ + wq_first_page_paddr = be64_to_cpu(*wq->block_vaddr); + + pfn = CMDQ_PFN(wq_first_page_paddr, wq->wq_page_size); + + ctxt_info->curr_wqe_page_pfn = + HINIC_CMDQ_CTXT_PAGE_INFO_SET(pfn, CURR_WQE_PAGE_PFN) | + HINIC_CMDQ_CTXT_PAGE_INFO_SET(HINIC_CEQ_ID_CMDQ, EQ_ID) | + HINIC_CMDQ_CTXT_PAGE_INFO_SET(1, CEQ_ARM) | + HINIC_CMDQ_CTXT_PAGE_INFO_SET(1, CEQ_EN) | + HINIC_CMDQ_CTXT_PAGE_INFO_SET(cmdq->wrapped, WRAPPED); + + /* block PFN - Read Modify Write */ + cmdq_first_block_paddr = cmdq_pages->page_paddr; + + pfn = CMDQ_PFN(cmdq_first_block_paddr, wq->wq_page_size); + + ctxt_info->wq_block_pfn = + HINIC_CMDQ_CTXT_BLOCK_INFO_SET(pfn, WQ_BLOCK_PFN) | + HINIC_CMDQ_CTXT_BLOCK_INFO_SET(atomic_read(&wq->cons_idx), CI); + + cmdq_ctxt->func_idx = HINIC_HWIF_FUNC_IDX(cmdqs->hwif); + cmdq_ctxt->cmdq_type = cmdq->cmdq_type; +} + +/** + * init_cmdq - initialize cmdq + * @cmdq: the cmdq + * @wq: the wq attaced to the cmdq + * @q_type: the cmdq type of the cmdq + * @db_area: doorbell area for the cmdq + * + * Return 0 - Success, negative - Failure + **/ +static int init_cmdq(struct hinic_cmdq *cmdq, struct hinic_wq *wq, + enum hinic_cmdq_type q_type, void __iomem *db_area) +{ + int err; + + cmdq->wq = wq; + cmdq->cmdq_type = q_type; + cmdq->wrapped = 1; + + spin_lock_init(&cmdq->cmdq_lock); + + cmdq->done = vzalloc(wq->q_depth * sizeof(*cmdq->done)); + if (!cmdq->done) + return -ENOMEM; + + cmdq->errcode = vzalloc(wq->q_depth * sizeof(*cmdq->errcode)); + if (!cmdq->errcode) { + err = -ENOMEM; + goto err_errcode; + } + + cmdq->db_base = db_area + CMDQ_DB_OFF; + return 0; + +err_errcode: + vfree(cmdq->done); + return err; +} + +/** + * free_cmdq - Free cmdq + * @cmdq: the cmdq to free + **/ +static void free_cmdq(struct hinic_cmdq *cmdq) +{ + vfree(cmdq->errcode); + vfree(cmdq->done); +} + +/** + * init_cmdqs_ctxt - write the cmdq ctxt to HW after init all cmdq + * @hwdev: the NIC HW device + * @cmdqs: cmdqs to write the ctxts for + * &db_area: db_area for all the cmdqs + * + * Return 0 - Success, negative - Failure + **/ +static int init_cmdqs_ctxt(struct hinic_hwdev *hwdev, + struct hinic_cmdqs *cmdqs, void __iomem **db_area) +{ + struct hinic_hwif *hwif = hwdev->hwif; + enum hinic_cmdq_type type, cmdq_type; + struct hinic_cmdq_ctxt *cmdq_ctxts; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + size_t cmdq_ctxts_size; + int err; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI function type\n"); + return -EINVAL; + } + + cmdq_ctxts_size = HINIC_MAX_CMDQ_TYPES * sizeof(*cmdq_ctxts); + cmdq_ctxts = devm_kzalloc(&pdev->dev, cmdq_ctxts_size, GFP_KERNEL); + if (!cmdq_ctxts) + return -ENOMEM; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + cmdq_type = HINIC_CMDQ_SYNC; + for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) { + err = init_cmdq(&cmdqs->cmdq[cmdq_type], + &cmdqs->saved_wqs[cmdq_type], cmdq_type, + db_area[cmdq_type]); + if (err) { + dev_err(&pdev->dev, "Failed to initialize cmdq\n"); + goto err_init_cmdq; + } + + cmdq_init_queue_ctxt(&cmdq_ctxts[cmdq_type], + &cmdqs->cmdq[cmdq_type], + &cmdqs->cmdq_pages); + } + + /* Write the CMDQ ctxts */ + cmdq_type = HINIC_CMDQ_SYNC; + for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) { + err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_CMDQ_CTXT_SET, + &cmdq_ctxts[cmdq_type], + sizeof(cmdq_ctxts[cmdq_type]), + NULL, NULL, HINIC_MGMT_MSG_SYNC); + if (err) { + dev_err(&pdev->dev, "Failed to set CMDQ CTXT type = %d\n", + cmdq_type); + goto err_write_cmdq_ctxt; + } + } + + devm_kfree(&pdev->dev, cmdq_ctxts); + return 0; + +err_write_cmdq_ctxt: + cmdq_type = HINIC_MAX_CMDQ_TYPES; + +err_init_cmdq: + for (type = HINIC_CMDQ_SYNC; type < cmdq_type; type++) + free_cmdq(&cmdqs->cmdq[type]); + + devm_kfree(&pdev->dev, cmdq_ctxts); + return err; +} + +/** + * hinic_init_cmdqs - init all cmdqs + * @cmdqs: cmdqs to init + * @hwif: HW interface for accessing cmdqs + * @db_area: doorbell areas for all the cmdqs + * + * Return 0 - Success, negative - Failure + **/ +int hinic_init_cmdqs(struct hinic_cmdqs *cmdqs, struct hinic_hwif *hwif, + void __iomem **db_area) +{ + struct hinic_func_to_io *func_to_io = cmdqs_to_func_to_io(cmdqs); + struct pci_dev *pdev = hwif->pdev; + struct hinic_hwdev *hwdev; + size_t saved_wqs_size; + u16 max_wqe_size; + int err; + + cmdqs->hwif = hwif; + cmdqs->cmdq_buf_pool = pci_pool_create("hinic_cmdq", pdev, + HINIC_CMDQ_BUF_SIZE, + HINIC_CMDQ_BUF_SIZE, 0); + if (!cmdqs->cmdq_buf_pool) + return -ENOMEM; + + saved_wqs_size = HINIC_MAX_CMDQ_TYPES * sizeof(struct hinic_wq); + cmdqs->saved_wqs = devm_kzalloc(&pdev->dev, saved_wqs_size, GFP_KERNEL); + if (!cmdqs->saved_wqs) { + err = -ENOMEM; + goto err_saved_wqs; + } + + max_wqe_size = WQE_LCMD_SIZE; + err = hinic_wqs_cmdq_alloc(&cmdqs->cmdq_pages, cmdqs->saved_wqs, hwif, + HINIC_MAX_CMDQ_TYPES, CMDQ_WQEBB_SIZE, + CMDQ_WQ_PAGE_SIZE, CMDQ_DEPTH, max_wqe_size); + if (err) { + dev_err(&pdev->dev, "Failed to allocate CMDQ wqs\n"); + goto err_cmdq_wqs; + } + + hwdev = container_of(func_to_io, struct hinic_hwdev, func_to_io); + err = init_cmdqs_ctxt(hwdev, cmdqs, db_area); + if (err) { + dev_err(&pdev->dev, "Failed to write cmdq ctxt\n"); + goto err_cmdq_ctxt; + } + + hinic_ceq_register_cb(&func_to_io->ceqs, HINIC_CEQ_CMDQ, cmdqs, + cmdq_ceq_handler); + return 0; + +err_cmdq_ctxt: + hinic_wqs_cmdq_free(&cmdqs->cmdq_pages, cmdqs->saved_wqs, + HINIC_MAX_CMDQ_TYPES); + +err_cmdq_wqs: + devm_kfree(&pdev->dev, cmdqs->saved_wqs); + +err_saved_wqs: + pci_pool_destroy(cmdqs->cmdq_buf_pool); + return err; +} + +/** + * hinic_free_cmdqs - free all cmdqs + * @cmdqs: cmdqs to free + **/ +void hinic_free_cmdqs(struct hinic_cmdqs *cmdqs) +{ + struct hinic_func_to_io *func_to_io = cmdqs_to_func_to_io(cmdqs); + struct hinic_hwif *hwif = cmdqs->hwif; + struct pci_dev *pdev = hwif->pdev; + enum hinic_cmdq_type cmdq_type; + + hinic_ceq_unregister_cb(&func_to_io->ceqs, HINIC_CEQ_CMDQ); + + cmdq_type = HINIC_CMDQ_SYNC; + for (; cmdq_type < HINIC_MAX_CMDQ_TYPES; cmdq_type++) + free_cmdq(&cmdqs->cmdq[cmdq_type]); + + hinic_wqs_cmdq_free(&cmdqs->cmdq_pages, cmdqs->saved_wqs, + HINIC_MAX_CMDQ_TYPES); + + devm_kfree(&pdev->dev, cmdqs->saved_wqs); + + pci_pool_destroy(cmdqs->cmdq_buf_pool); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h new file mode 100644 index 000000000000..b35583400cb6 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_cmdq.h @@ -0,0 +1,187 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_CMDQ_H +#define HINIC_CMDQ_H + +#include <linux/types.h> +#include <linux/spinlock.h> +#include <linux/completion.h> +#include <linux/pci.h> + +#include "hinic_hw_if.h" +#include "hinic_hw_wq.h" + +#define HINIC_CMDQ_CTXT_CURR_WQE_PAGE_PFN_SHIFT 0 +#define HINIC_CMDQ_CTXT_EQ_ID_SHIFT 56 +#define HINIC_CMDQ_CTXT_CEQ_ARM_SHIFT 61 +#define HINIC_CMDQ_CTXT_CEQ_EN_SHIFT 62 +#define HINIC_CMDQ_CTXT_WRAPPED_SHIFT 63 + +#define HINIC_CMDQ_CTXT_CURR_WQE_PAGE_PFN_MASK 0xFFFFFFFFFFFFF +#define HINIC_CMDQ_CTXT_EQ_ID_MASK 0x1F +#define HINIC_CMDQ_CTXT_CEQ_ARM_MASK 0x1 +#define HINIC_CMDQ_CTXT_CEQ_EN_MASK 0x1 +#define HINIC_CMDQ_CTXT_WRAPPED_MASK 0x1 + +#define HINIC_CMDQ_CTXT_PAGE_INFO_SET(val, member) \ + (((u64)(val) & HINIC_CMDQ_CTXT_##member##_MASK) \ + << HINIC_CMDQ_CTXT_##member##_SHIFT) + +#define HINIC_CMDQ_CTXT_PAGE_INFO_CLEAR(val, member) \ + ((val) & (~((u64)HINIC_CMDQ_CTXT_##member##_MASK \ + << HINIC_CMDQ_CTXT_##member##_SHIFT))) + +#define HINIC_CMDQ_CTXT_WQ_BLOCK_PFN_SHIFT 0 +#define HINIC_CMDQ_CTXT_CI_SHIFT 52 + +#define HINIC_CMDQ_CTXT_WQ_BLOCK_PFN_MASK 0xFFFFFFFFFFFFF +#define HINIC_CMDQ_CTXT_CI_MASK 0xFFF + +#define HINIC_CMDQ_CTXT_BLOCK_INFO_SET(val, member) \ + (((u64)(val) & HINIC_CMDQ_CTXT_##member##_MASK) \ + << HINIC_CMDQ_CTXT_##member##_SHIFT) + +#define HINIC_CMDQ_CTXT_BLOCK_INFO_CLEAR(val, member) \ + ((val) & (~((u64)HINIC_CMDQ_CTXT_##member##_MASK \ + << HINIC_CMDQ_CTXT_##member##_SHIFT))) + +#define HINIC_SAVED_DATA_ARM_SHIFT 31 + +#define HINIC_SAVED_DATA_ARM_MASK 0x1 + +#define HINIC_SAVED_DATA_SET(val, member) \ + (((u32)(val) & HINIC_SAVED_DATA_##member##_MASK) \ + << HINIC_SAVED_DATA_##member##_SHIFT) + +#define HINIC_SAVED_DATA_GET(val, member) \ + (((val) >> HINIC_SAVED_DATA_##member##_SHIFT) \ + & HINIC_SAVED_DATA_##member##_MASK) + +#define HINIC_SAVED_DATA_CLEAR(val, member) \ + ((val) & (~(HINIC_SAVED_DATA_##member##_MASK \ + << HINIC_SAVED_DATA_##member##_SHIFT))) + +#define HINIC_CMDQ_DB_INFO_HI_PROD_IDX_SHIFT 0 +#define HINIC_CMDQ_DB_INFO_PATH_SHIFT 23 +#define HINIC_CMDQ_DB_INFO_CMDQ_TYPE_SHIFT 24 +#define HINIC_CMDQ_DB_INFO_DB_TYPE_SHIFT 27 + +#define HINIC_CMDQ_DB_INFO_HI_PROD_IDX_MASK 0xFF +#define HINIC_CMDQ_DB_INFO_PATH_MASK 0x1 +#define HINIC_CMDQ_DB_INFO_CMDQ_TYPE_MASK 0x7 +#define HINIC_CMDQ_DB_INFO_DB_TYPE_MASK 0x1F + +#define HINIC_CMDQ_DB_INFO_SET(val, member) \ + (((u32)(val) & HINIC_CMDQ_DB_INFO_##member##_MASK) \ + << HINIC_CMDQ_DB_INFO_##member##_SHIFT) + +#define HINIC_CMDQ_BUF_SIZE 2048 + +#define HINIC_CMDQ_BUF_HW_RSVD 8 +#define HINIC_CMDQ_MAX_DATA_SIZE (HINIC_CMDQ_BUF_SIZE - \ + HINIC_CMDQ_BUF_HW_RSVD) + +enum hinic_cmdq_type { + HINIC_CMDQ_SYNC, + + HINIC_MAX_CMDQ_TYPES, +}; + +enum hinic_set_arm_qtype { + HINIC_SET_ARM_CMDQ, +}; + +enum hinic_cmd_ack_type { + HINIC_CMD_ACK_TYPE_CMDQ, +}; + +struct hinic_cmdq_buf { + void *buf; + dma_addr_t dma_addr; + size_t size; +}; + +struct hinic_cmdq_arm_bit { + u32 q_type; + u32 q_id; +}; + +struct hinic_cmdq_ctxt_info { + u64 curr_wqe_page_pfn; + u64 wq_block_pfn; +}; + +struct hinic_cmdq_ctxt { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u8 cmdq_type; + u8 rsvd1[1]; + + u8 rsvd2[4]; + + struct hinic_cmdq_ctxt_info ctxt_info; +}; + +struct hinic_cmdq { + struct hinic_wq *wq; + + enum hinic_cmdq_type cmdq_type; + int wrapped; + + /* Lock for keeping the doorbell order */ + spinlock_t cmdq_lock; + + struct completion **done; + int **errcode; + + /* doorbell area */ + void __iomem *db_base; +}; + +struct hinic_cmdqs { + struct hinic_hwif *hwif; + + struct pci_pool *cmdq_buf_pool; + + struct hinic_wq *saved_wqs; + + struct hinic_cmdq_pages cmdq_pages; + + struct hinic_cmdq cmdq[HINIC_MAX_CMDQ_TYPES]; +}; + +int hinic_alloc_cmdq_buf(struct hinic_cmdqs *cmdqs, + struct hinic_cmdq_buf *cmdq_buf); + +void hinic_free_cmdq_buf(struct hinic_cmdqs *cmdqs, + struct hinic_cmdq_buf *cmdq_buf); + +int hinic_cmdq_direct_resp(struct hinic_cmdqs *cmdqs, + enum hinic_mod_type mod, u8 cmd, + struct hinic_cmdq_buf *buf_in, u64 *out_param); + +int hinic_set_arm_bit(struct hinic_cmdqs *cmdqs, + enum hinic_set_arm_qtype q_type, u32 q_id); + +int hinic_init_cmdqs(struct hinic_cmdqs *cmdqs, struct hinic_hwif *hwif, + void __iomem **db_area); + +void hinic_free_cmdqs(struct hinic_cmdqs *cmdqs); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h new file mode 100644 index 000000000000..f39b184f674d --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_csr.h @@ -0,0 +1,149 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_CSR_H +#define HINIC_HW_CSR_H + +/* HW interface registers */ +#define HINIC_CSR_FUNC_ATTR0_ADDR 0x0 +#define HINIC_CSR_FUNC_ATTR1_ADDR 0x4 + +#define HINIC_CSR_FUNC_ATTR4_ADDR 0x10 +#define HINIC_CSR_FUNC_ATTR5_ADDR 0x14 + +#define HINIC_DMA_ATTR_BASE 0xC80 +#define HINIC_ELECTION_BASE 0x4200 + +#define HINIC_DMA_ATTR_STRIDE 0x4 +#define HINIC_CSR_DMA_ATTR_ADDR(idx) \ + (HINIC_DMA_ATTR_BASE + (idx) * HINIC_DMA_ATTR_STRIDE) + +#define HINIC_PPF_ELECTION_STRIDE 0x4 +#define HINIC_CSR_MAX_PORTS 4 + +#define HINIC_CSR_PPF_ELECTION_ADDR(idx) \ + (HINIC_ELECTION_BASE + (idx) * HINIC_PPF_ELECTION_STRIDE) + +/* API CMD registers */ +#define HINIC_CSR_API_CMD_BASE 0xF000 + +#define HINIC_CSR_API_CMD_STRIDE 0x100 + +#define HINIC_CSR_API_CMD_CHAIN_HEAD_HI_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x0 + (idx) * HINIC_CSR_API_CMD_STRIDE) + +#define HINIC_CSR_API_CMD_CHAIN_HEAD_LO_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x4 + (idx) * HINIC_CSR_API_CMD_STRIDE) + +#define HINIC_CSR_API_CMD_STATUS_HI_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x8 + (idx) * HINIC_CSR_API_CMD_STRIDE) + +#define HINIC_CSR_API_CMD_STATUS_LO_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0xC + (idx) * HINIC_CSR_API_CMD_STRIDE) + +#define HINIC_CSR_API_CMD_CHAIN_NUM_CELLS_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x10 + (idx) * HINIC_CSR_API_CMD_STRIDE) + +#define HINIC_CSR_API_CMD_CHAIN_CTRL_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x14 + (idx) * HINIC_CSR_API_CMD_STRIDE) + +#define HINIC_CSR_API_CMD_CHAIN_PI_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x1C + (idx) * HINIC_CSR_API_CMD_STRIDE) + +#define HINIC_CSR_API_CMD_CHAIN_REQ_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x20 + (idx) * HINIC_CSR_API_CMD_STRIDE) + +#define HINIC_CSR_API_CMD_STATUS_ADDR(idx) \ + (HINIC_CSR_API_CMD_BASE + 0x30 + (idx) * HINIC_CSR_API_CMD_STRIDE) + +/* MSI-X registers */ +#define HINIC_CSR_MSIX_CTRL_BASE 0x2000 +#define HINIC_CSR_MSIX_CNT_BASE 0x2004 + +#define HINIC_CSR_MSIX_STRIDE 0x8 + +#define HINIC_CSR_MSIX_CTRL_ADDR(idx) \ + (HINIC_CSR_MSIX_CTRL_BASE + (idx) * HINIC_CSR_MSIX_STRIDE) + +#define HINIC_CSR_MSIX_CNT_ADDR(idx) \ + (HINIC_CSR_MSIX_CNT_BASE + (idx) * HINIC_CSR_MSIX_STRIDE) + +/* EQ registers */ +#define HINIC_AEQ_MTT_OFF_BASE_ADDR 0x200 +#define HINIC_CEQ_MTT_OFF_BASE_ADDR 0x400 + +#define HINIC_EQ_MTT_OFF_STRIDE 0x40 + +#define HINIC_CSR_AEQ_MTT_OFF(id) \ + (HINIC_AEQ_MTT_OFF_BASE_ADDR + (id) * HINIC_EQ_MTT_OFF_STRIDE) + +#define HINIC_CSR_CEQ_MTT_OFF(id) \ + (HINIC_CEQ_MTT_OFF_BASE_ADDR + (id) * HINIC_EQ_MTT_OFF_STRIDE) + +#define HINIC_CSR_EQ_PAGE_OFF_STRIDE 8 + +#define HINIC_CSR_AEQ_HI_PHYS_ADDR_REG(q_id, pg_num) \ + (HINIC_CSR_AEQ_MTT_OFF(q_id) + \ + (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE) + +#define HINIC_CSR_CEQ_HI_PHYS_ADDR_REG(q_id, pg_num) \ + (HINIC_CSR_CEQ_MTT_OFF(q_id) + \ + (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE) + +#define HINIC_CSR_AEQ_LO_PHYS_ADDR_REG(q_id, pg_num) \ + (HINIC_CSR_AEQ_MTT_OFF(q_id) + \ + (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE + 4) + +#define HINIC_CSR_CEQ_LO_PHYS_ADDR_REG(q_id, pg_num) \ + (HINIC_CSR_CEQ_MTT_OFF(q_id) + \ + (pg_num) * HINIC_CSR_EQ_PAGE_OFF_STRIDE + 4) + +#define HINIC_AEQ_CTRL_0_ADDR_BASE 0xE00 +#define HINIC_AEQ_CTRL_1_ADDR_BASE 0xE04 +#define HINIC_AEQ_CONS_IDX_ADDR_BASE 0xE08 +#define HINIC_AEQ_PROD_IDX_ADDR_BASE 0xE0C + +#define HINIC_CEQ_CTRL_0_ADDR_BASE 0x1000 +#define HINIC_CEQ_CTRL_1_ADDR_BASE 0x1004 +#define HINIC_CEQ_CONS_IDX_ADDR_BASE 0x1008 +#define HINIC_CEQ_PROD_IDX_ADDR_BASE 0x100C + +#define HINIC_EQ_OFF_STRIDE 0x80 + +#define HINIC_CSR_AEQ_CTRL_0_ADDR(idx) \ + (HINIC_AEQ_CTRL_0_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + +#define HINIC_CSR_AEQ_CTRL_1_ADDR(idx) \ + (HINIC_AEQ_CTRL_1_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + +#define HINIC_CSR_AEQ_CONS_IDX_ADDR(idx) \ + (HINIC_AEQ_CONS_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + +#define HINIC_CSR_AEQ_PROD_IDX_ADDR(idx) \ + (HINIC_AEQ_PROD_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + +#define HINIC_CSR_CEQ_CTRL_0_ADDR(idx) \ + (HINIC_CEQ_CTRL_0_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + +#define HINIC_CSR_CEQ_CTRL_1_ADDR(idx) \ + (HINIC_CEQ_CTRL_1_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + +#define HINIC_CSR_CEQ_CONS_IDX_ADDR(idx) \ + (HINIC_CEQ_CONS_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + +#define HINIC_CSR_CEQ_PROD_IDX_ADDR(idx) \ + (HINIC_CEQ_PROD_IDX_ADDR_BASE + (idx) * HINIC_EQ_OFF_STRIDE) + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c new file mode 100644 index 000000000000..79b567447084 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.c @@ -0,0 +1,1013 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/log2.h> +#include <linux/err.h> + +#include "hinic_hw_if.h" +#include "hinic_hw_eqs.h" +#include "hinic_hw_mgmt.h" +#include "hinic_hw_qp_ctxt.h" +#include "hinic_hw_qp.h" +#include "hinic_hw_io.h" +#include "hinic_hw_dev.h" + +#define IO_STATUS_TIMEOUT 100 +#define OUTBOUND_STATE_TIMEOUT 100 +#define DB_STATE_TIMEOUT 100 + +#define MAX_IRQS(max_qps, num_aeqs, num_ceqs) \ + (2 * (max_qps) + (num_aeqs) + (num_ceqs)) + +#define ADDR_IN_4BYTES(addr) ((addr) >> 2) + +enum intr_type { + INTR_MSIX_TYPE, +}; + +enum io_status { + IO_STOPPED = 0, + IO_RUNNING = 1, +}; + +enum hw_ioctxt_set_cmdq_depth { + HW_IOCTXT_SET_CMDQ_DEPTH_DEFAULT, +}; + +/* HW struct */ +struct hinic_dev_cap { + u8 status; + u8 version; + u8 rsvd0[6]; + + u8 rsvd1[5]; + u8 intr_type; + u8 rsvd2[66]; + u16 max_sqs; + u16 max_rqs; + u8 rsvd3[208]; +}; + +/** + * get_capability - convert device capabilities to NIC capabilities + * @hwdev: the HW device to set and convert device capabilities for + * @dev_cap: device capabilities from FW + * + * Return 0 - Success, negative - Failure + **/ +static int get_capability(struct hinic_hwdev *hwdev, + struct hinic_dev_cap *dev_cap) +{ + struct hinic_cap *nic_cap = &hwdev->nic_cap; + int num_aeqs, num_ceqs, num_irqs; + + if (!HINIC_IS_PF(hwdev->hwif) && !HINIC_IS_PPF(hwdev->hwif)) + return -EINVAL; + + if (dev_cap->intr_type != INTR_MSIX_TYPE) + return -EFAULT; + + num_aeqs = HINIC_HWIF_NUM_AEQS(hwdev->hwif); + num_ceqs = HINIC_HWIF_NUM_CEQS(hwdev->hwif); + num_irqs = HINIC_HWIF_NUM_IRQS(hwdev->hwif); + + /* Each QP has its own (SQ + RQ) interrupts */ + nic_cap->num_qps = (num_irqs - (num_aeqs + num_ceqs)) / 2; + + if (nic_cap->num_qps > HINIC_Q_CTXT_MAX) + nic_cap->num_qps = HINIC_Q_CTXT_MAX; + + /* num_qps must be power of 2 */ + nic_cap->num_qps = BIT(fls(nic_cap->num_qps) - 1); + + nic_cap->max_qps = dev_cap->max_sqs + 1; + if (nic_cap->max_qps != (dev_cap->max_rqs + 1)) + return -EFAULT; + + if (nic_cap->num_qps > nic_cap->max_qps) + nic_cap->num_qps = nic_cap->max_qps; + + return 0; +} + +/** + * get_cap_from_fw - get device capabilities from FW + * @pfhwdev: the PF HW device to get capabilities for + * + * Return 0 - Success, negative - Failure + **/ +static int get_cap_from_fw(struct hinic_pfhwdev *pfhwdev) +{ + struct hinic_hwdev *hwdev = &pfhwdev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_dev_cap dev_cap; + u16 in_len, out_len; + int err; + + in_len = 0; + out_len = sizeof(dev_cap); + + err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_CFGM, + HINIC_CFG_NIC_CAP, &dev_cap, in_len, &dev_cap, + &out_len, HINIC_MGMT_MSG_SYNC); + if (err) { + dev_err(&pdev->dev, "Failed to get capability from FW\n"); + return err; + } + + return get_capability(hwdev, &dev_cap); +} + +/** + * get_dev_cap - get device capabilities + * @hwdev: the NIC HW device to get capabilities for + * + * Return 0 - Success, negative - Failure + **/ +static int get_dev_cap(struct hinic_hwdev *hwdev) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + int err; + + switch (HINIC_FUNC_TYPE(hwif)) { + case HINIC_PPF: + case HINIC_PF: + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + err = get_cap_from_fw(pfhwdev); + if (err) { + dev_err(&pdev->dev, "Failed to get capability from FW\n"); + return err; + } + break; + + default: + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + return -EINVAL; + } + + return 0; +} + +/** + * init_msix - enable the msix and save the entries + * @hwdev: the NIC HW device + * + * Return 0 - Success, negative - Failure + **/ +static int init_msix(struct hinic_hwdev *hwdev) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + int nr_irqs, num_aeqs, num_ceqs; + size_t msix_entries_size; + int i, err; + + num_aeqs = HINIC_HWIF_NUM_AEQS(hwif); + num_ceqs = HINIC_HWIF_NUM_CEQS(hwif); + nr_irqs = MAX_IRQS(HINIC_MAX_QPS, num_aeqs, num_ceqs); + if (nr_irqs > HINIC_HWIF_NUM_IRQS(hwif)) + nr_irqs = HINIC_HWIF_NUM_IRQS(hwif); + + msix_entries_size = nr_irqs * sizeof(*hwdev->msix_entries); + hwdev->msix_entries = devm_kzalloc(&pdev->dev, msix_entries_size, + GFP_KERNEL); + if (!hwdev->msix_entries) + return -ENOMEM; + + for (i = 0; i < nr_irqs; i++) + hwdev->msix_entries[i].entry = i; + + err = pci_enable_msix_exact(pdev, hwdev->msix_entries, nr_irqs); + if (err) { + dev_err(&pdev->dev, "Failed to enable pci msix\n"); + return err; + } + + return 0; +} + +/** + * disable_msix - disable the msix + * @hwdev: the NIC HW device + **/ +static void disable_msix(struct hinic_hwdev *hwdev) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + + pci_disable_msix(pdev); +} + +/** + * hinic_port_msg_cmd - send port msg to mgmt + * @hwdev: the NIC HW device + * @cmd: the port command + * @buf_in: input buffer + * @in_size: input size + * @buf_out: output buffer + * @out_size: returned output size + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd, + void *buf_in, u16 in_size, void *buf_out, u16 *out_size) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "unsupported PCI Function type\n"); + return -EINVAL; + } + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC, cmd, + buf_in, in_size, buf_out, out_size, + HINIC_MGMT_MSG_SYNC); +} + +/** + * init_fw_ctxt- Init Firmware tables before network mgmt and io operations + * @hwdev: the NIC HW device + * + * Return 0 - Success, negative - Failure + **/ +static int init_fw_ctxt(struct hinic_hwdev *hwdev) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_cmd_fw_ctxt fw_ctxt; + struct hinic_pfhwdev *pfhwdev; + u16 out_size; + int err; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + return -EINVAL; + } + + fw_ctxt.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + fw_ctxt.rx_buf_sz = HINIC_RX_BUF_SZ; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_FWCTXT_INIT, + &fw_ctxt, sizeof(fw_ctxt), + &fw_ctxt, &out_size); + if (err || (out_size != sizeof(fw_ctxt)) || fw_ctxt.status) { + dev_err(&pdev->dev, "Failed to init FW ctxt, ret = %d\n", + fw_ctxt.status); + return -EFAULT; + } + + return 0; +} + +/** + * set_hw_ioctxt - set the shape of the IO queues in FW + * @hwdev: the NIC HW device + * @rq_depth: rq depth + * @sq_depth: sq depth + * + * Return 0 - Success, negative - Failure + **/ +static int set_hw_ioctxt(struct hinic_hwdev *hwdev, unsigned int rq_depth, + unsigned int sq_depth) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_cmd_hw_ioctxt hw_ioctxt; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + return -EINVAL; + } + + hw_ioctxt.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + hw_ioctxt.set_cmdq_depth = HW_IOCTXT_SET_CMDQ_DEPTH_DEFAULT; + hw_ioctxt.cmdq_depth = 0; + + hw_ioctxt.rq_depth = ilog2(rq_depth); + + hw_ioctxt.rx_buf_sz_idx = HINIC_RX_BUF_SZ_IDX; + + hw_ioctxt.sq_depth = ilog2(sq_depth); + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_HWCTXT_SET, + &hw_ioctxt, sizeof(hw_ioctxt), NULL, + NULL, HINIC_MGMT_MSG_SYNC); +} + +static int wait_for_outbound_state(struct hinic_hwdev *hwdev) +{ + enum hinic_outbound_state outbound_state; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + unsigned long end; + + end = jiffies + msecs_to_jiffies(OUTBOUND_STATE_TIMEOUT); + do { + outbound_state = hinic_outbound_state_get(hwif); + + if (outbound_state == HINIC_OUTBOUND_ENABLE) + return 0; + + msleep(20); + } while (time_before(jiffies, end)); + + dev_err(&pdev->dev, "Wait for OUTBOUND - Timeout\n"); + return -EFAULT; +} + +static int wait_for_db_state(struct hinic_hwdev *hwdev) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + enum hinic_db_state db_state; + unsigned long end; + + end = jiffies + msecs_to_jiffies(DB_STATE_TIMEOUT); + do { + db_state = hinic_db_state_get(hwif); + + if (db_state == HINIC_DB_ENABLE) + return 0; + + msleep(20); + } while (time_before(jiffies, end)); + + dev_err(&pdev->dev, "Wait for DB - Timeout\n"); + return -EFAULT; +} + +static int wait_for_io_stopped(struct hinic_hwdev *hwdev) +{ + struct hinic_cmd_io_status cmd_io_status; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + unsigned long end; + u16 out_size; + int err; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + return -EINVAL; + } + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + cmd_io_status.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + end = jiffies + msecs_to_jiffies(IO_STATUS_TIMEOUT); + do { + err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_IO_STATUS_GET, + &cmd_io_status, sizeof(cmd_io_status), + &cmd_io_status, &out_size, + HINIC_MGMT_MSG_SYNC); + if ((err) || (out_size != sizeof(cmd_io_status))) { + dev_err(&pdev->dev, "Failed to get IO status, ret = %d\n", + err); + return err; + } + + if (cmd_io_status.status == IO_STOPPED) { + dev_info(&pdev->dev, "IO stopped\n"); + return 0; + } + + msleep(20); + } while (time_before(jiffies, end)); + + dev_err(&pdev->dev, "Wait for IO stopped - Timeout\n"); + return -ETIMEDOUT; +} + +/** + * clear_io_resource - set the IO resources as not active in the NIC + * @hwdev: the NIC HW device + * + * Return 0 - Success, negative - Failure + **/ +static int clear_io_resources(struct hinic_hwdev *hwdev) +{ + struct hinic_cmd_clear_io_res cmd_clear_io_res; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + int err; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + return -EINVAL; + } + + err = wait_for_io_stopped(hwdev); + if (err) { + dev_err(&pdev->dev, "IO has not stopped yet\n"); + return err; + } + + cmd_clear_io_res.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + err = hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, HINIC_MOD_COMM, + HINIC_COMM_CMD_IO_RES_CLEAR, &cmd_clear_io_res, + sizeof(cmd_clear_io_res), NULL, NULL, + HINIC_MGMT_MSG_SYNC); + if (err) { + dev_err(&pdev->dev, "Failed to clear IO resources\n"); + return err; + } + + return 0; +} + +/** + * set_resources_state - set the state of the resources in the NIC + * @hwdev: the NIC HW device + * @state: the state to set + * + * Return 0 - Success, negative - Failure + **/ +static int set_resources_state(struct hinic_hwdev *hwdev, + enum hinic_res_state state) +{ + struct hinic_cmd_set_res_state res_state; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + return -EINVAL; + } + + res_state.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + res_state.state = state; + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, + HINIC_MOD_COMM, + HINIC_COMM_CMD_RES_STATE_SET, + &res_state, sizeof(res_state), NULL, + NULL, HINIC_MGMT_MSG_SYNC); +} + +/** + * get_base_qpn - get the first qp number + * @hwdev: the NIC HW device + * @base_qpn: returned qp number + * + * Return 0 - Success, negative - Failure + **/ +static int get_base_qpn(struct hinic_hwdev *hwdev, u16 *base_qpn) +{ + struct hinic_cmd_base_qpn cmd_base_qpn; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + cmd_base_qpn.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_GLOBAL_QPN, + &cmd_base_qpn, sizeof(cmd_base_qpn), + &cmd_base_qpn, &out_size); + if (err || (out_size != sizeof(cmd_base_qpn)) || cmd_base_qpn.status) { + dev_err(&pdev->dev, "Failed to get base qpn, status = %d\n", + cmd_base_qpn.status); + return -EFAULT; + } + + *base_qpn = cmd_base_qpn.qpn; + return 0; +} + +/** + * hinic_hwdev_ifup - Preparing the HW for passing IO + * @hwdev: the NIC HW device + * + * Return 0 - Success, negative - Failure + **/ +int hinic_hwdev_ifup(struct hinic_hwdev *hwdev) +{ + struct hinic_func_to_io *func_to_io = &hwdev->func_to_io; + struct hinic_cap *nic_cap = &hwdev->nic_cap; + struct hinic_hwif *hwif = hwdev->hwif; + int err, num_aeqs, num_ceqs, num_qps; + struct msix_entry *ceq_msix_entries; + struct msix_entry *sq_msix_entries; + struct msix_entry *rq_msix_entries; + struct pci_dev *pdev = hwif->pdev; + u16 base_qpn; + + err = get_base_qpn(hwdev, &base_qpn); + if (err) { + dev_err(&pdev->dev, "Failed to get global base qp number\n"); + return err; + } + + num_aeqs = HINIC_HWIF_NUM_AEQS(hwif); + num_ceqs = HINIC_HWIF_NUM_CEQS(hwif); + + ceq_msix_entries = &hwdev->msix_entries[num_aeqs]; + + err = hinic_io_init(func_to_io, hwif, nic_cap->max_qps, num_ceqs, + ceq_msix_entries); + if (err) { + dev_err(&pdev->dev, "Failed to init IO channel\n"); + return err; + } + + num_qps = nic_cap->num_qps; + sq_msix_entries = &hwdev->msix_entries[num_aeqs + num_ceqs]; + rq_msix_entries = &hwdev->msix_entries[num_aeqs + num_ceqs + num_qps]; + + err = hinic_io_create_qps(func_to_io, base_qpn, num_qps, + sq_msix_entries, rq_msix_entries); + if (err) { + dev_err(&pdev->dev, "Failed to create QPs\n"); + goto err_create_qps; + } + + err = wait_for_db_state(hwdev); + if (err) { + dev_warn(&pdev->dev, "db - disabled, try again\n"); + hinic_db_state_set(hwif, HINIC_DB_ENABLE); + } + + err = set_hw_ioctxt(hwdev, HINIC_SQ_DEPTH, HINIC_RQ_DEPTH); + if (err) { + dev_err(&pdev->dev, "Failed to set HW IO ctxt\n"); + goto err_hw_ioctxt; + } + + return 0; + +err_hw_ioctxt: + hinic_io_destroy_qps(func_to_io, num_qps); + +err_create_qps: + hinic_io_free(func_to_io); + return err; +} + +/** + * hinic_hwdev_ifdown - Closing the HW for passing IO + * @hwdev: the NIC HW device + * + **/ +void hinic_hwdev_ifdown(struct hinic_hwdev *hwdev) +{ + struct hinic_func_to_io *func_to_io = &hwdev->func_to_io; + struct hinic_cap *nic_cap = &hwdev->nic_cap; + + clear_io_resources(hwdev); + + hinic_io_destroy_qps(func_to_io, nic_cap->num_qps); + hinic_io_free(func_to_io); +} + +/** + * hinic_hwdev_cb_register - register callback handler for MGMT events + * @hwdev: the NIC HW device + * @cmd: the mgmt event + * @handle: private data for the handler + * @handler: event handler + **/ +void hinic_hwdev_cb_register(struct hinic_hwdev *hwdev, + enum hinic_mgmt_msg_cmd cmd, void *handle, + void (*handler)(void *handle, void *buf_in, + u16 in_size, void *buf_out, + u16 *out_size)) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + struct hinic_nic_cb *nic_cb; + u8 cmd_cb; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "unsupported PCI Function type\n"); + return; + } + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + cmd_cb = cmd - HINIC_MGMT_MSG_CMD_BASE; + nic_cb = &pfhwdev->nic_cb[cmd_cb]; + + nic_cb->handler = handler; + nic_cb->handle = handle; + nic_cb->cb_state = HINIC_CB_ENABLED; +} + +/** + * hinic_hwdev_cb_unregister - unregister callback handler for MGMT events + * @hwdev: the NIC HW device + * @cmd: the mgmt event + **/ +void hinic_hwdev_cb_unregister(struct hinic_hwdev *hwdev, + enum hinic_mgmt_msg_cmd cmd) +{ + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + struct hinic_nic_cb *nic_cb; + u8 cmd_cb; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "unsupported PCI Function type\n"); + return; + } + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + + cmd_cb = cmd - HINIC_MGMT_MSG_CMD_BASE; + nic_cb = &pfhwdev->nic_cb[cmd_cb]; + + nic_cb->cb_state &= ~HINIC_CB_ENABLED; + + while (nic_cb->cb_state & HINIC_CB_RUNNING) + schedule(); + + nic_cb->handler = NULL; +} + +/** + * nic_mgmt_msg_handler - nic mgmt event handler + * @handle: private data for the handler + * @buf_in: input buffer + * @in_size: input size + * @buf_out: output buffer + * @out_size: returned output size + **/ +static void nic_mgmt_msg_handler(void *handle, u8 cmd, void *buf_in, + u16 in_size, void *buf_out, u16 *out_size) +{ + struct hinic_pfhwdev *pfhwdev = handle; + enum hinic_cb_state cb_state; + struct hinic_nic_cb *nic_cb; + struct hinic_hwdev *hwdev; + struct hinic_hwif *hwif; + struct pci_dev *pdev; + u8 cmd_cb; + + hwdev = &pfhwdev->hwdev; + hwif = hwdev->hwif; + pdev = hwif->pdev; + + if ((cmd < HINIC_MGMT_MSG_CMD_BASE) || + (cmd >= HINIC_MGMT_MSG_CMD_MAX)) { + dev_err(&pdev->dev, "unknown L2NIC event, cmd = %d\n", cmd); + return; + } + + cmd_cb = cmd - HINIC_MGMT_MSG_CMD_BASE; + + nic_cb = &pfhwdev->nic_cb[cmd_cb]; + + cb_state = cmpxchg(&nic_cb->cb_state, + HINIC_CB_ENABLED, + HINIC_CB_ENABLED | HINIC_CB_RUNNING); + + if ((cb_state == HINIC_CB_ENABLED) && (nic_cb->handler)) + nic_cb->handler(nic_cb->handle, buf_in, + in_size, buf_out, out_size); + else + dev_err(&pdev->dev, "Unhandled NIC Event %d\n", cmd); + + nic_cb->cb_state &= ~HINIC_CB_RUNNING; +} + +/** + * init_pfhwdev - Initialize the extended components of PF + * @pfhwdev: the HW device for PF + * + * Return 0 - success, negative - failure + **/ +static int init_pfhwdev(struct hinic_pfhwdev *pfhwdev) +{ + struct hinic_hwdev *hwdev = &pfhwdev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + int err; + + err = hinic_pf_to_mgmt_init(&pfhwdev->pf_to_mgmt, hwif); + if (err) { + dev_err(&pdev->dev, "Failed to initialize PF to MGMT channel\n"); + return err; + } + + hinic_register_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC, + pfhwdev, nic_mgmt_msg_handler); + + hinic_set_pf_action(hwif, HINIC_PF_MGMT_ACTIVE); + return 0; +} + +/** + * free_pfhwdev - Free the extended components of PF + * @pfhwdev: the HW device for PF + **/ +static void free_pfhwdev(struct hinic_pfhwdev *pfhwdev) +{ + struct hinic_hwdev *hwdev = &pfhwdev->hwdev; + + hinic_set_pf_action(hwdev->hwif, HINIC_PF_MGMT_INIT); + + hinic_unregister_mgmt_msg_cb(&pfhwdev->pf_to_mgmt, HINIC_MOD_L2NIC); + + hinic_pf_to_mgmt_free(&pfhwdev->pf_to_mgmt); +} + +/** + * hinic_init_hwdev - Initialize the NIC HW + * @pdev: the NIC pci device + * + * Return initialized NIC HW device + * + * Initialize the NIC HW device and return a pointer to it + **/ +struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev) +{ + struct hinic_pfhwdev *pfhwdev; + struct hinic_hwdev *hwdev; + struct hinic_hwif *hwif; + int err, num_aeqs; + + hwif = devm_kzalloc(&pdev->dev, sizeof(*hwif), GFP_KERNEL); + if (!hwif) + return ERR_PTR(-ENOMEM); + + err = hinic_init_hwif(hwif, pdev); + if (err) { + dev_err(&pdev->dev, "Failed to init HW interface\n"); + return ERR_PTR(err); + } + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + err = -EFAULT; + goto err_func_type; + } + + pfhwdev = devm_kzalloc(&pdev->dev, sizeof(*pfhwdev), GFP_KERNEL); + if (!pfhwdev) { + err = -ENOMEM; + goto err_pfhwdev_alloc; + } + + hwdev = &pfhwdev->hwdev; + hwdev->hwif = hwif; + + err = init_msix(hwdev); + if (err) { + dev_err(&pdev->dev, "Failed to init msix\n"); + goto err_init_msix; + } + + err = wait_for_outbound_state(hwdev); + if (err) { + dev_warn(&pdev->dev, "outbound - disabled, try again\n"); + hinic_outbound_state_set(hwif, HINIC_OUTBOUND_ENABLE); + } + + num_aeqs = HINIC_HWIF_NUM_AEQS(hwif); + + err = hinic_aeqs_init(&hwdev->aeqs, hwif, num_aeqs, + HINIC_DEFAULT_AEQ_LEN, HINIC_EQ_PAGE_SIZE, + hwdev->msix_entries); + if (err) { + dev_err(&pdev->dev, "Failed to init async event queues\n"); + goto err_aeqs_init; + } + + err = init_pfhwdev(pfhwdev); + if (err) { + dev_err(&pdev->dev, "Failed to init PF HW device\n"); + goto err_init_pfhwdev; + } + + err = get_dev_cap(hwdev); + if (err) { + dev_err(&pdev->dev, "Failed to get device capabilities\n"); + goto err_dev_cap; + } + + err = init_fw_ctxt(hwdev); + if (err) { + dev_err(&pdev->dev, "Failed to init function table\n"); + goto err_init_fw_ctxt; + } + + err = set_resources_state(hwdev, HINIC_RES_ACTIVE); + if (err) { + dev_err(&pdev->dev, "Failed to set resources state\n"); + goto err_resources_state; + } + + return hwdev; + +err_resources_state: +err_init_fw_ctxt: +err_dev_cap: + free_pfhwdev(pfhwdev); + +err_init_pfhwdev: + hinic_aeqs_free(&hwdev->aeqs); + +err_aeqs_init: + disable_msix(hwdev); + +err_init_msix: +err_pfhwdev_alloc: +err_func_type: + hinic_free_hwif(hwif); + return ERR_PTR(err); +} + +/** + * hinic_free_hwdev - Free the NIC HW device + * @hwdev: the NIC HW device + **/ +void hinic_free_hwdev(struct hinic_hwdev *hwdev) +{ + struct hinic_pfhwdev *pfhwdev = container_of(hwdev, + struct hinic_pfhwdev, + hwdev); + + set_resources_state(hwdev, HINIC_RES_CLEAN); + + free_pfhwdev(pfhwdev); + + hinic_aeqs_free(&hwdev->aeqs); + + disable_msix(hwdev); + + hinic_free_hwif(hwdev->hwif); +} + +/** + * hinic_hwdev_num_qps - return the number QPs available for use + * @hwdev: the NIC HW device + * + * Return number QPs available for use + **/ +int hinic_hwdev_num_qps(struct hinic_hwdev *hwdev) +{ + struct hinic_cap *nic_cap = &hwdev->nic_cap; + + return nic_cap->num_qps; +} + +/** + * hinic_hwdev_get_sq - get SQ + * @hwdev: the NIC HW device + * @i: the position of the SQ + * + * Return: the SQ in the i position + **/ +struct hinic_sq *hinic_hwdev_get_sq(struct hinic_hwdev *hwdev, int i) +{ + struct hinic_func_to_io *func_to_io = &hwdev->func_to_io; + struct hinic_qp *qp = &func_to_io->qps[i]; + + if (i >= hinic_hwdev_num_qps(hwdev)) + return NULL; + + return &qp->sq; +} + +/** + * hinic_hwdev_get_sq - get RQ + * @hwdev: the NIC HW device + * @i: the position of the RQ + * + * Return: the RQ in the i position + **/ +struct hinic_rq *hinic_hwdev_get_rq(struct hinic_hwdev *hwdev, int i) +{ + struct hinic_func_to_io *func_to_io = &hwdev->func_to_io; + struct hinic_qp *qp = &func_to_io->qps[i]; + + if (i >= hinic_hwdev_num_qps(hwdev)) + return NULL; + + return &qp->rq; +} + +/** + * hinic_hwdev_msix_cnt_set - clear message attribute counters for msix entry + * @hwdev: the NIC HW device + * @msix_index: msix_index + * + * Return 0 - Success, negative - Failure + **/ +int hinic_hwdev_msix_cnt_set(struct hinic_hwdev *hwdev, u16 msix_index) +{ + return hinic_msix_attr_cnt_clear(hwdev->hwif, msix_index); +} + +/** + * hinic_hwdev_msix_set - set message attribute for msix entry + * @hwdev: the NIC HW device + * @msix_index: msix_index + * @pending_limit: the maximum pending interrupt events (unit 8) + * @coalesc_timer: coalesc period for interrupt (unit 8 us) + * @lli_timer: replenishing period for low latency credit (unit 8 us) + * @lli_credit_limit: maximum credits for low latency msix messages (unit 8) + * @resend_timer: maximum wait for resending msix (unit coalesc period) + * + * Return 0 - Success, negative - Failure + **/ +int hinic_hwdev_msix_set(struct hinic_hwdev *hwdev, u16 msix_index, + u8 pending_limit, u8 coalesc_timer, + u8 lli_timer_cfg, u8 lli_credit_limit, + u8 resend_timer) +{ + return hinic_msix_attr_set(hwdev->hwif, msix_index, + pending_limit, coalesc_timer, + lli_timer_cfg, lli_credit_limit, + resend_timer); +} + +/** + * hinic_hwdev_hw_ci_addr_set - set cons idx addr and attributes in HW for sq + * @hwdev: the NIC HW device + * @sq: send queue + * @pending_limit: the maximum pending update ci events (unit 8) + * @coalesc_timer: coalesc period for update ci (unit 8 us) + * + * Return 0 - Success, negative - Failure + **/ +int hinic_hwdev_hw_ci_addr_set(struct hinic_hwdev *hwdev, struct hinic_sq *sq, + u8 pending_limit, u8 coalesc_timer) +{ + struct hinic_qp *qp = container_of(sq, struct hinic_qp, sq); + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_pfhwdev *pfhwdev; + struct hinic_cmd_hw_ci hw_ci; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "Unsupported PCI Function type\n"); + return -EINVAL; + } + + hw_ci.dma_attr_off = 0; + hw_ci.pending_limit = pending_limit; + hw_ci.coalesc_timer = coalesc_timer; + + hw_ci.msix_en = 1; + hw_ci.msix_entry_idx = sq->msix_entry; + + hw_ci.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + hw_ci.sq_id = qp->q_id; + + hw_ci.ci_addr = ADDR_IN_4BYTES(sq->hw_ci_dma_addr); + + pfhwdev = container_of(hwdev, struct hinic_pfhwdev, hwdev); + return hinic_msg_to_mgmt(&pfhwdev->pf_to_mgmt, + HINIC_MOD_COMM, + HINIC_COMM_CMD_SQ_HI_CI_SET, + &hw_ci, sizeof(hw_ci), NULL, + NULL, HINIC_MGMT_MSG_SYNC); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h new file mode 100644 index 000000000000..0f5563f3b779 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_dev.h @@ -0,0 +1,239 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_DEV_H +#define HINIC_HW_DEV_H + +#include <linux/pci.h> +#include <linux/types.h> +#include <linux/bitops.h> + +#include "hinic_hw_if.h" +#include "hinic_hw_eqs.h" +#include "hinic_hw_mgmt.h" +#include "hinic_hw_qp.h" +#include "hinic_hw_io.h" + +#define HINIC_MAX_QPS 32 + +#define HINIC_MGMT_NUM_MSG_CMD (HINIC_MGMT_MSG_CMD_MAX - \ + HINIC_MGMT_MSG_CMD_BASE) + +struct hinic_cap { + u16 max_qps; + u16 num_qps; +}; + +enum hinic_port_cmd { + HINIC_PORT_CMD_CHANGE_MTU = 2, + + HINIC_PORT_CMD_ADD_VLAN = 3, + HINIC_PORT_CMD_DEL_VLAN = 4, + + HINIC_PORT_CMD_SET_MAC = 9, + HINIC_PORT_CMD_GET_MAC = 10, + HINIC_PORT_CMD_DEL_MAC = 11, + + HINIC_PORT_CMD_SET_RX_MODE = 12, + + HINIC_PORT_CMD_GET_LINK_STATE = 24, + + HINIC_PORT_CMD_SET_PORT_STATE = 41, + + HINIC_PORT_CMD_FWCTXT_INIT = 69, + + HINIC_PORT_CMD_SET_FUNC_STATE = 93, + + HINIC_PORT_CMD_GET_GLOBAL_QPN = 102, + + HINIC_PORT_CMD_GET_CAP = 170, +}; + +enum hinic_mgmt_msg_cmd { + HINIC_MGMT_MSG_CMD_BASE = 160, + + HINIC_MGMT_MSG_CMD_LINK_STATUS = 160, + + HINIC_MGMT_MSG_CMD_MAX, +}; + +enum hinic_cb_state { + HINIC_CB_ENABLED = BIT(0), + HINIC_CB_RUNNING = BIT(1), +}; + +enum hinic_res_state { + HINIC_RES_CLEAN = 0, + HINIC_RES_ACTIVE = 1, +}; + +struct hinic_cmd_fw_ctxt { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 rx_buf_sz; + + u32 rsvd1; +}; + +struct hinic_cmd_hw_ioctxt { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + + u16 rsvd1; + + u8 set_cmdq_depth; + u8 cmdq_depth; + + u8 rsvd2; + u8 rsvd3; + u8 rsvd4; + u8 rsvd5; + + u16 rq_depth; + u16 rx_buf_sz_idx; + u16 sq_depth; +}; + +struct hinic_cmd_io_status { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u8 rsvd1; + u8 rsvd2; + u32 io_status; +}; + +struct hinic_cmd_clear_io_res { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u8 rsvd1; + u8 rsvd2; +}; + +struct hinic_cmd_set_res_state { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u8 state; + u8 rsvd1; + u32 rsvd2; +}; + +struct hinic_cmd_base_qpn { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 qpn; +}; + +struct hinic_cmd_hw_ci { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + + u8 dma_attr_off; + u8 pending_limit; + u8 coalesc_timer; + + u8 msix_en; + u16 msix_entry_idx; + + u32 sq_id; + u32 rsvd1; + u64 ci_addr; +}; + +struct hinic_hwdev { + struct hinic_hwif *hwif; + struct msix_entry *msix_entries; + + struct hinic_aeqs aeqs; + struct hinic_func_to_io func_to_io; + + struct hinic_cap nic_cap; +}; + +struct hinic_nic_cb { + void (*handler)(void *handle, void *buf_in, + u16 in_size, void *buf_out, + u16 *out_size); + + void *handle; + unsigned long cb_state; +}; + +struct hinic_pfhwdev { + struct hinic_hwdev hwdev; + + struct hinic_pf_to_mgmt pf_to_mgmt; + + struct hinic_nic_cb nic_cb[HINIC_MGMT_NUM_MSG_CMD]; +}; + +void hinic_hwdev_cb_register(struct hinic_hwdev *hwdev, + enum hinic_mgmt_msg_cmd cmd, void *handle, + void (*handler)(void *handle, void *buf_in, + u16 in_size, void *buf_out, + u16 *out_size)); + +void hinic_hwdev_cb_unregister(struct hinic_hwdev *hwdev, + enum hinic_mgmt_msg_cmd cmd); + +int hinic_port_msg_cmd(struct hinic_hwdev *hwdev, enum hinic_port_cmd cmd, + void *buf_in, u16 in_size, void *buf_out, + u16 *out_size); + +int hinic_hwdev_ifup(struct hinic_hwdev *hwdev); + +void hinic_hwdev_ifdown(struct hinic_hwdev *hwdev); + +struct hinic_hwdev *hinic_init_hwdev(struct pci_dev *pdev); + +void hinic_free_hwdev(struct hinic_hwdev *hwdev); + +int hinic_hwdev_num_qps(struct hinic_hwdev *hwdev); + +struct hinic_sq *hinic_hwdev_get_sq(struct hinic_hwdev *hwdev, int i); + +struct hinic_rq *hinic_hwdev_get_rq(struct hinic_hwdev *hwdev, int i); + +int hinic_hwdev_msix_cnt_set(struct hinic_hwdev *hwdev, u16 msix_index); + +int hinic_hwdev_msix_set(struct hinic_hwdev *hwdev, u16 msix_index, + u8 pending_limit, u8 coalesc_timer, + u8 lli_timer_cfg, u8 lli_credit_limit, + u8 resend_timer); + +int hinic_hwdev_hw_ci_addr_set(struct hinic_hwdev *hwdev, struct hinic_sq *sq, + u8 pending_limit, u8 coalesc_timer); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c new file mode 100644 index 000000000000..7cb8b9b94726 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.c @@ -0,0 +1,886 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/workqueue.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/log2.h> +#include <asm/byteorder.h> +#include <asm/barrier.h> + +#include "hinic_hw_csr.h" +#include "hinic_hw_if.h" +#include "hinic_hw_eqs.h" + +#define HINIC_EQS_WQ_NAME "hinic_eqs" + +#define GET_EQ_NUM_PAGES(eq, pg_size) \ + (ALIGN((eq)->q_len * (eq)->elem_size, pg_size) / (pg_size)) + +#define GET_EQ_NUM_ELEMS_IN_PG(eq, pg_size) ((pg_size) / (eq)->elem_size) + +#define EQ_CONS_IDX_REG_ADDR(eq) (((eq)->type == HINIC_AEQ) ? \ + HINIC_CSR_AEQ_CONS_IDX_ADDR((eq)->q_id) : \ + HINIC_CSR_CEQ_CONS_IDX_ADDR((eq)->q_id)) + +#define EQ_PROD_IDX_REG_ADDR(eq) (((eq)->type == HINIC_AEQ) ? \ + HINIC_CSR_AEQ_PROD_IDX_ADDR((eq)->q_id) : \ + HINIC_CSR_CEQ_PROD_IDX_ADDR((eq)->q_id)) + +#define EQ_HI_PHYS_ADDR_REG(eq, pg_num) (((eq)->type == HINIC_AEQ) ? \ + HINIC_CSR_AEQ_HI_PHYS_ADDR_REG((eq)->q_id, pg_num) : \ + HINIC_CSR_CEQ_HI_PHYS_ADDR_REG((eq)->q_id, pg_num)) + +#define EQ_LO_PHYS_ADDR_REG(eq, pg_num) (((eq)->type == HINIC_AEQ) ? \ + HINIC_CSR_AEQ_LO_PHYS_ADDR_REG((eq)->q_id, pg_num) : \ + HINIC_CSR_CEQ_LO_PHYS_ADDR_REG((eq)->q_id, pg_num)) + +#define GET_EQ_ELEMENT(eq, idx) \ + ((eq)->virt_addr[(idx) / (eq)->num_elem_in_pg] + \ + (((idx) & ((eq)->num_elem_in_pg - 1)) * (eq)->elem_size)) + +#define GET_AEQ_ELEM(eq, idx) ((struct hinic_aeq_elem *) \ + GET_EQ_ELEMENT(eq, idx)) + +#define GET_CEQ_ELEM(eq, idx) ((u32 *) \ + GET_EQ_ELEMENT(eq, idx)) + +#define GET_CURR_AEQ_ELEM(eq) GET_AEQ_ELEM(eq, (eq)->cons_idx) + +#define GET_CURR_CEQ_ELEM(eq) GET_CEQ_ELEM(eq, (eq)->cons_idx) + +#define PAGE_IN_4K(page_size) ((page_size) >> 12) +#define EQ_SET_HW_PAGE_SIZE_VAL(eq) (ilog2(PAGE_IN_4K((eq)->page_size))) + +#define ELEMENT_SIZE_IN_32B(eq) (((eq)->elem_size) >> 5) +#define EQ_SET_HW_ELEM_SIZE_VAL(eq) (ilog2(ELEMENT_SIZE_IN_32B(eq))) + +#define EQ_MAX_PAGES 8 + +#define CEQE_TYPE_SHIFT 23 +#define CEQE_TYPE_MASK 0x7 + +#define CEQE_TYPE(ceqe) (((ceqe) >> CEQE_TYPE_SHIFT) & \ + CEQE_TYPE_MASK) + +#define CEQE_DATA_MASK 0x3FFFFFF +#define CEQE_DATA(ceqe) ((ceqe) & CEQE_DATA_MASK) + +#define aeq_to_aeqs(eq) \ + container_of((eq) - (eq)->q_id, struct hinic_aeqs, aeq[0]) + +#define ceq_to_ceqs(eq) \ + container_of((eq) - (eq)->q_id, struct hinic_ceqs, ceq[0]) + +#define work_to_aeq_work(work) \ + container_of(work, struct hinic_eq_work, work) + +#define DMA_ATTR_AEQ_DEFAULT 0 +#define DMA_ATTR_CEQ_DEFAULT 0 + +/* No coalescence */ +#define THRESH_CEQ_DEFAULT 0 + +enum eq_int_mode { + EQ_INT_MODE_ARMED, + EQ_INT_MODE_ALWAYS +}; + +enum eq_arm_state { + EQ_NOT_ARMED, + EQ_ARMED +}; + +/** + * hinic_aeq_register_hw_cb - register AEQ callback for specific event + * @aeqs: pointer to Async eqs of the chip + * @event: aeq event to register callback for it + * @handle: private data will be used by the callback + * @hw_handler: callback function + **/ +void hinic_aeq_register_hw_cb(struct hinic_aeqs *aeqs, + enum hinic_aeq_type event, void *handle, + void (*hwe_handler)(void *handle, void *data, + u8 size)) +{ + struct hinic_hw_event_cb *hwe_cb = &aeqs->hwe_cb[event]; + + hwe_cb->hwe_handler = hwe_handler; + hwe_cb->handle = handle; + hwe_cb->hwe_state = HINIC_EQE_ENABLED; +} + +/** + * hinic_aeq_unregister_hw_cb - unregister the AEQ callback for specific event + * @aeqs: pointer to Async eqs of the chip + * @event: aeq event to unregister callback for it + **/ +void hinic_aeq_unregister_hw_cb(struct hinic_aeqs *aeqs, + enum hinic_aeq_type event) +{ + struct hinic_hw_event_cb *hwe_cb = &aeqs->hwe_cb[event]; + + hwe_cb->hwe_state &= ~HINIC_EQE_ENABLED; + + while (hwe_cb->hwe_state & HINIC_EQE_RUNNING) + schedule(); + + hwe_cb->hwe_handler = NULL; +} + +/** + * hinic_ceq_register_cb - register CEQ callback for specific event + * @ceqs: pointer to Completion eqs part of the chip + * @event: ceq event to register callback for it + * @handle: private data will be used by the callback + * @handler: callback function + **/ +void hinic_ceq_register_cb(struct hinic_ceqs *ceqs, + enum hinic_ceq_type event, void *handle, + void (*handler)(void *handle, u32 ceqe_data)) +{ + struct hinic_ceq_cb *ceq_cb = &ceqs->ceq_cb[event]; + + ceq_cb->handler = handler; + ceq_cb->handle = handle; + ceq_cb->ceqe_state = HINIC_EQE_ENABLED; +} + +/** + * hinic_ceq_unregister_cb - unregister the CEQ callback for specific event + * @ceqs: pointer to Completion eqs part of the chip + * @event: ceq event to unregister callback for it + **/ +void hinic_ceq_unregister_cb(struct hinic_ceqs *ceqs, + enum hinic_ceq_type event) +{ + struct hinic_ceq_cb *ceq_cb = &ceqs->ceq_cb[event]; + + ceq_cb->ceqe_state &= ~HINIC_EQE_ENABLED; + + while (ceq_cb->ceqe_state & HINIC_EQE_RUNNING) + schedule(); + + ceq_cb->handler = NULL; +} + +static u8 eq_cons_idx_checksum_set(u32 val) +{ + u8 checksum = 0; + int idx; + + for (idx = 0; idx < 32; idx += 4) + checksum ^= ((val >> idx) & 0xF); + + return (checksum & 0xF); +} + +/** + * eq_update_ci - update the HW cons idx of event queue + * @eq: the event queue to update the cons idx for + **/ +static void eq_update_ci(struct hinic_eq *eq) +{ + u32 val, addr = EQ_CONS_IDX_REG_ADDR(eq); + + /* Read Modify Write */ + val = hinic_hwif_read_reg(eq->hwif, addr); + + val = HINIC_EQ_CI_CLEAR(val, IDX) & + HINIC_EQ_CI_CLEAR(val, WRAPPED) & + HINIC_EQ_CI_CLEAR(val, INT_ARMED) & + HINIC_EQ_CI_CLEAR(val, XOR_CHKSUM); + + val |= HINIC_EQ_CI_SET(eq->cons_idx, IDX) | + HINIC_EQ_CI_SET(eq->wrapped, WRAPPED) | + HINIC_EQ_CI_SET(EQ_ARMED, INT_ARMED); + + val |= HINIC_EQ_CI_SET(eq_cons_idx_checksum_set(val), XOR_CHKSUM); + + hinic_hwif_write_reg(eq->hwif, addr, val); +} + +/** + * aeq_irq_handler - handler for the AEQ event + * @eq: the Async Event Queue that received the event + **/ +static void aeq_irq_handler(struct hinic_eq *eq) +{ + struct hinic_aeqs *aeqs = aeq_to_aeqs(eq); + struct hinic_hwif *hwif = aeqs->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_aeq_elem *aeqe_curr; + struct hinic_hw_event_cb *hwe_cb; + enum hinic_aeq_type event; + unsigned long eqe_state; + u32 aeqe_desc; + int i, size; + + for (i = 0; i < eq->q_len; i++) { + aeqe_curr = GET_CURR_AEQ_ELEM(eq); + + /* Data in HW is in Big endian Format */ + aeqe_desc = be32_to_cpu(aeqe_curr->desc); + + /* HW toggles the wrapped bit, when it adds eq element */ + if (HINIC_EQ_ELEM_DESC_GET(aeqe_desc, WRAPPED) == eq->wrapped) + break; + + event = HINIC_EQ_ELEM_DESC_GET(aeqe_desc, TYPE); + if (event >= HINIC_MAX_AEQ_EVENTS) { + dev_err(&pdev->dev, "Unknown AEQ Event %d\n", event); + return; + } + + if (!HINIC_EQ_ELEM_DESC_GET(aeqe_desc, SRC)) { + hwe_cb = &aeqs->hwe_cb[event]; + + size = HINIC_EQ_ELEM_DESC_GET(aeqe_desc, SIZE); + + eqe_state = cmpxchg(&hwe_cb->hwe_state, + HINIC_EQE_ENABLED, + HINIC_EQE_ENABLED | + HINIC_EQE_RUNNING); + if ((eqe_state == HINIC_EQE_ENABLED) && + (hwe_cb->hwe_handler)) + hwe_cb->hwe_handler(hwe_cb->handle, + aeqe_curr->data, size); + else + dev_err(&pdev->dev, "Unhandled AEQ Event %d\n", + event); + + hwe_cb->hwe_state &= ~HINIC_EQE_RUNNING; + } + + eq->cons_idx++; + + if (eq->cons_idx == eq->q_len) { + eq->cons_idx = 0; + eq->wrapped = !eq->wrapped; + } + } +} + +/** + * ceq_event_handler - handler for the ceq events + * @ceqs: ceqs part of the chip + * @ceqe: ceq element that describes the event + **/ +static void ceq_event_handler(struct hinic_ceqs *ceqs, u32 ceqe) +{ + struct hinic_hwif *hwif = ceqs->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_ceq_cb *ceq_cb; + enum hinic_ceq_type event; + unsigned long eqe_state; + + event = CEQE_TYPE(ceqe); + if (event >= HINIC_MAX_CEQ_EVENTS) { + dev_err(&pdev->dev, "Unknown CEQ event, event = %d\n", event); + return; + } + + ceq_cb = &ceqs->ceq_cb[event]; + + eqe_state = cmpxchg(&ceq_cb->ceqe_state, + HINIC_EQE_ENABLED, + HINIC_EQE_ENABLED | HINIC_EQE_RUNNING); + + if ((eqe_state == HINIC_EQE_ENABLED) && (ceq_cb->handler)) + ceq_cb->handler(ceq_cb->handle, CEQE_DATA(ceqe)); + else + dev_err(&pdev->dev, "Unhandled CEQ Event %d\n", event); + + ceq_cb->ceqe_state &= ~HINIC_EQE_RUNNING; +} + +/** + * ceq_irq_handler - handler for the CEQ event + * @eq: the Completion Event Queue that received the event + **/ +static void ceq_irq_handler(struct hinic_eq *eq) +{ + struct hinic_ceqs *ceqs = ceq_to_ceqs(eq); + u32 ceqe; + int i; + + for (i = 0; i < eq->q_len; i++) { + ceqe = *(GET_CURR_CEQ_ELEM(eq)); + + /* Data in HW is in Big endian Format */ + ceqe = be32_to_cpu(ceqe); + + /* HW toggles the wrapped bit, when it adds eq element event */ + if (HINIC_EQ_ELEM_DESC_GET(ceqe, WRAPPED) == eq->wrapped) + break; + + ceq_event_handler(ceqs, ceqe); + + eq->cons_idx++; + + if (eq->cons_idx == eq->q_len) { + eq->cons_idx = 0; + eq->wrapped = !eq->wrapped; + } + } +} + +/** + * eq_irq_handler - handler for the EQ event + * @data: the Event Queue that received the event + **/ +static void eq_irq_handler(void *data) +{ + struct hinic_eq *eq = data; + + if (eq->type == HINIC_AEQ) + aeq_irq_handler(eq); + else if (eq->type == HINIC_CEQ) + ceq_irq_handler(eq); + + eq_update_ci(eq); +} + +/** + * eq_irq_work - the work of the EQ that received the event + * @work: the work struct that is associated with the EQ + **/ +static void eq_irq_work(struct work_struct *work) +{ + struct hinic_eq_work *aeq_work = work_to_aeq_work(work); + struct hinic_eq *aeq; + + aeq = aeq_work->data; + eq_irq_handler(aeq); +} + +/** + * ceq_tasklet - the tasklet of the EQ that received the event + * @ceq_data: the eq + **/ +static void ceq_tasklet(unsigned long ceq_data) +{ + struct hinic_eq *ceq = (struct hinic_eq *)ceq_data; + + eq_irq_handler(ceq); +} + +/** + * aeq_interrupt - aeq interrupt handler + * @irq: irq number + * @data: the Async Event Queue that collected the event + **/ +static irqreturn_t aeq_interrupt(int irq, void *data) +{ + struct hinic_eq_work *aeq_work; + struct hinic_eq *aeq = data; + struct hinic_aeqs *aeqs; + + /* clear resend timer cnt register */ + hinic_msix_attr_cnt_clear(aeq->hwif, aeq->msix_entry.entry); + + aeq_work = &aeq->aeq_work; + aeq_work->data = aeq; + + aeqs = aeq_to_aeqs(aeq); + queue_work(aeqs->workq, &aeq_work->work); + + return IRQ_HANDLED; +} + +/** + * ceq_interrupt - ceq interrupt handler + * @irq: irq number + * @data: the Completion Event Queue that collected the event + **/ +static irqreturn_t ceq_interrupt(int irq, void *data) +{ + struct hinic_eq *ceq = data; + + /* clear resend timer cnt register */ + hinic_msix_attr_cnt_clear(ceq->hwif, ceq->msix_entry.entry); + + tasklet_schedule(&ceq->ceq_tasklet); + + return IRQ_HANDLED; +} + +static void set_ctrl0(struct hinic_eq *eq) +{ + struct msix_entry *msix_entry = &eq->msix_entry; + enum hinic_eq_type type = eq->type; + u32 addr, val, ctrl0; + + if (type == HINIC_AEQ) { + /* RMW Ctrl0 */ + addr = HINIC_CSR_AEQ_CTRL_0_ADDR(eq->q_id); + + val = hinic_hwif_read_reg(eq->hwif, addr); + + val = HINIC_AEQ_CTRL_0_CLEAR(val, INT_IDX) & + HINIC_AEQ_CTRL_0_CLEAR(val, DMA_ATTR) & + HINIC_AEQ_CTRL_0_CLEAR(val, PCI_INTF_IDX) & + HINIC_AEQ_CTRL_0_CLEAR(val, INT_MODE); + + ctrl0 = HINIC_AEQ_CTRL_0_SET(msix_entry->entry, INT_IDX) | + HINIC_AEQ_CTRL_0_SET(DMA_ATTR_AEQ_DEFAULT, DMA_ATTR) | + HINIC_AEQ_CTRL_0_SET(HINIC_HWIF_PCI_INTF(eq->hwif), + PCI_INTF_IDX) | + HINIC_AEQ_CTRL_0_SET(EQ_INT_MODE_ARMED, INT_MODE); + + val |= ctrl0; + + hinic_hwif_write_reg(eq->hwif, addr, val); + } else if (type == HINIC_CEQ) { + /* RMW Ctrl0 */ + addr = HINIC_CSR_CEQ_CTRL_0_ADDR(eq->q_id); + + val = hinic_hwif_read_reg(eq->hwif, addr); + + val = HINIC_CEQ_CTRL_0_CLEAR(val, INTR_IDX) & + HINIC_CEQ_CTRL_0_CLEAR(val, DMA_ATTR) & + HINIC_CEQ_CTRL_0_CLEAR(val, KICK_THRESH) & + HINIC_CEQ_CTRL_0_CLEAR(val, PCI_INTF_IDX) & + HINIC_CEQ_CTRL_0_CLEAR(val, INTR_MODE); + + ctrl0 = HINIC_CEQ_CTRL_0_SET(msix_entry->entry, INTR_IDX) | + HINIC_CEQ_CTRL_0_SET(DMA_ATTR_CEQ_DEFAULT, DMA_ATTR) | + HINIC_CEQ_CTRL_0_SET(THRESH_CEQ_DEFAULT, KICK_THRESH) | + HINIC_CEQ_CTRL_0_SET(HINIC_HWIF_PCI_INTF(eq->hwif), + PCI_INTF_IDX) | + HINIC_CEQ_CTRL_0_SET(EQ_INT_MODE_ARMED, INTR_MODE); + + val |= ctrl0; + + hinic_hwif_write_reg(eq->hwif, addr, val); + } +} + +static void set_ctrl1(struct hinic_eq *eq) +{ + enum hinic_eq_type type = eq->type; + u32 page_size_val, elem_size; + u32 addr, val, ctrl1; + + if (type == HINIC_AEQ) { + /* RMW Ctrl1 */ + addr = HINIC_CSR_AEQ_CTRL_1_ADDR(eq->q_id); + + page_size_val = EQ_SET_HW_PAGE_SIZE_VAL(eq); + elem_size = EQ_SET_HW_ELEM_SIZE_VAL(eq); + + val = hinic_hwif_read_reg(eq->hwif, addr); + + val = HINIC_AEQ_CTRL_1_CLEAR(val, LEN) & + HINIC_AEQ_CTRL_1_CLEAR(val, ELEM_SIZE) & + HINIC_AEQ_CTRL_1_CLEAR(val, PAGE_SIZE); + + ctrl1 = HINIC_AEQ_CTRL_1_SET(eq->q_len, LEN) | + HINIC_AEQ_CTRL_1_SET(elem_size, ELEM_SIZE) | + HINIC_AEQ_CTRL_1_SET(page_size_val, PAGE_SIZE); + + val |= ctrl1; + + hinic_hwif_write_reg(eq->hwif, addr, val); + } else if (type == HINIC_CEQ) { + /* RMW Ctrl1 */ + addr = HINIC_CSR_CEQ_CTRL_1_ADDR(eq->q_id); + + page_size_val = EQ_SET_HW_PAGE_SIZE_VAL(eq); + + val = hinic_hwif_read_reg(eq->hwif, addr); + + val = HINIC_CEQ_CTRL_1_CLEAR(val, LEN) & + HINIC_CEQ_CTRL_1_CLEAR(val, PAGE_SIZE); + + ctrl1 = HINIC_CEQ_CTRL_1_SET(eq->q_len, LEN) | + HINIC_CEQ_CTRL_1_SET(page_size_val, PAGE_SIZE); + + val |= ctrl1; + + hinic_hwif_write_reg(eq->hwif, addr, val); + } +} + +/** + * set_eq_ctrls - setting eq's ctrl registers + * @eq: the Event Queue for setting + **/ +static void set_eq_ctrls(struct hinic_eq *eq) +{ + set_ctrl0(eq); + set_ctrl1(eq); +} + +/** + * aeq_elements_init - initialize all the elements in the aeq + * @eq: the Async Event Queue + * @init_val: value to initialize the elements with it + **/ +static void aeq_elements_init(struct hinic_eq *eq, u32 init_val) +{ + struct hinic_aeq_elem *aeqe; + int i; + + for (i = 0; i < eq->q_len; i++) { + aeqe = GET_AEQ_ELEM(eq, i); + aeqe->desc = cpu_to_be32(init_val); + } + + wmb(); /* Write the initilzation values */ +} + +/** + * ceq_elements_init - Initialize all the elements in the ceq + * @eq: the event queue + * @init_val: value to init with it the elements + **/ +static void ceq_elements_init(struct hinic_eq *eq, u32 init_val) +{ + u32 *ceqe; + int i; + + for (i = 0; i < eq->q_len; i++) { + ceqe = GET_CEQ_ELEM(eq, i); + *(ceqe) = cpu_to_be32(init_val); + } + + wmb(); /* Write the initilzation values */ +} + +/** + * alloc_eq_pages - allocate the pages for the queue + * @eq: the event queue + * + * Return 0 - Success, Negative - Failure + **/ +static int alloc_eq_pages(struct hinic_eq *eq) +{ + struct hinic_hwif *hwif = eq->hwif; + struct pci_dev *pdev = hwif->pdev; + u32 init_val, addr, val; + size_t addr_size; + int err, pg; + + addr_size = eq->num_pages * sizeof(*eq->dma_addr); + eq->dma_addr = devm_kzalloc(&pdev->dev, addr_size, GFP_KERNEL); + if (!eq->dma_addr) + return -ENOMEM; + + addr_size = eq->num_pages * sizeof(*eq->virt_addr); + eq->virt_addr = devm_kzalloc(&pdev->dev, addr_size, GFP_KERNEL); + if (!eq->virt_addr) { + err = -ENOMEM; + goto err_virt_addr_alloc; + } + + for (pg = 0; pg < eq->num_pages; pg++) { + eq->virt_addr[pg] = dma_zalloc_coherent(&pdev->dev, + eq->page_size, + &eq->dma_addr[pg], + GFP_KERNEL); + if (!eq->virt_addr[pg]) { + err = -ENOMEM; + goto err_dma_alloc; + } + + addr = EQ_HI_PHYS_ADDR_REG(eq, pg); + val = upper_32_bits(eq->dma_addr[pg]); + + hinic_hwif_write_reg(hwif, addr, val); + + addr = EQ_LO_PHYS_ADDR_REG(eq, pg); + val = lower_32_bits(eq->dma_addr[pg]); + + hinic_hwif_write_reg(hwif, addr, val); + } + + init_val = HINIC_EQ_ELEM_DESC_SET(eq->wrapped, WRAPPED); + + if (eq->type == HINIC_AEQ) + aeq_elements_init(eq, init_val); + else if (eq->type == HINIC_CEQ) + ceq_elements_init(eq, init_val); + + return 0; + +err_dma_alloc: + while (--pg >= 0) + dma_free_coherent(&pdev->dev, eq->page_size, + eq->virt_addr[pg], + eq->dma_addr[pg]); + + devm_kfree(&pdev->dev, eq->virt_addr); + +err_virt_addr_alloc: + devm_kfree(&pdev->dev, eq->dma_addr); + return err; +} + +/** + * free_eq_pages - free the pages of the queue + * @eq: the Event Queue + **/ +static void free_eq_pages(struct hinic_eq *eq) +{ + struct hinic_hwif *hwif = eq->hwif; + struct pci_dev *pdev = hwif->pdev; + int pg; + + for (pg = 0; pg < eq->num_pages; pg++) + dma_free_coherent(&pdev->dev, eq->page_size, + eq->virt_addr[pg], + eq->dma_addr[pg]); + + devm_kfree(&pdev->dev, eq->virt_addr); + devm_kfree(&pdev->dev, eq->dma_addr); +} + +/** + * init_eq - initialize Event Queue + * @eq: the event queue + * @hwif: the HW interface of a PCI function device + * @type: the type of the event queue, aeq or ceq + * @q_id: Queue id number + * @q_len: the number of EQ elements + * @page_size: the page size of the pages in the event queue + * @entry: msix entry associated with the event queue + * + * Return 0 - Success, Negative - Failure + **/ +static int init_eq(struct hinic_eq *eq, struct hinic_hwif *hwif, + enum hinic_eq_type type, int q_id, u32 q_len, u32 page_size, + struct msix_entry entry) +{ + struct pci_dev *pdev = hwif->pdev; + int err; + + eq->hwif = hwif; + eq->type = type; + eq->q_id = q_id; + eq->q_len = q_len; + eq->page_size = page_size; + + /* Clear PI and CI, also clear the ARM bit */ + hinic_hwif_write_reg(eq->hwif, EQ_CONS_IDX_REG_ADDR(eq), 0); + hinic_hwif_write_reg(eq->hwif, EQ_PROD_IDX_REG_ADDR(eq), 0); + + eq->cons_idx = 0; + eq->wrapped = 0; + + if (type == HINIC_AEQ) { + eq->elem_size = HINIC_AEQE_SIZE; + } else if (type == HINIC_CEQ) { + eq->elem_size = HINIC_CEQE_SIZE; + } else { + dev_err(&pdev->dev, "Invalid EQ type\n"); + return -EINVAL; + } + + eq->num_pages = GET_EQ_NUM_PAGES(eq, page_size); + eq->num_elem_in_pg = GET_EQ_NUM_ELEMS_IN_PG(eq, page_size); + + eq->msix_entry = entry; + + if (eq->num_elem_in_pg & (eq->num_elem_in_pg - 1)) { + dev_err(&pdev->dev, "num elements in eq page != power of 2\n"); + return -EINVAL; + } + + if (eq->num_pages > EQ_MAX_PAGES) { + dev_err(&pdev->dev, "too many pages for eq\n"); + return -EINVAL; + } + + set_eq_ctrls(eq); + eq_update_ci(eq); + + err = alloc_eq_pages(eq); + if (err) { + dev_err(&pdev->dev, "Failed to allocate pages for eq\n"); + return err; + } + + if (type == HINIC_AEQ) { + struct hinic_eq_work *aeq_work = &eq->aeq_work; + + INIT_WORK(&aeq_work->work, eq_irq_work); + } else if (type == HINIC_CEQ) { + tasklet_init(&eq->ceq_tasklet, ceq_tasklet, + (unsigned long)eq); + } + + /* set the attributes of the msix entry */ + hinic_msix_attr_set(eq->hwif, eq->msix_entry.entry, + HINIC_EQ_MSIX_PENDING_LIMIT_DEFAULT, + HINIC_EQ_MSIX_COALESC_TIMER_DEFAULT, + HINIC_EQ_MSIX_LLI_TIMER_DEFAULT, + HINIC_EQ_MSIX_LLI_CREDIT_LIMIT_DEFAULT, + HINIC_EQ_MSIX_RESEND_TIMER_DEFAULT); + + if (type == HINIC_AEQ) + err = request_irq(entry.vector, aeq_interrupt, 0, + "hinic_aeq", eq); + else if (type == HINIC_CEQ) + err = request_irq(entry.vector, ceq_interrupt, 0, + "hinic_ceq", eq); + + if (err) { + dev_err(&pdev->dev, "Failed to request irq for the EQ\n"); + goto err_req_irq; + } + + return 0; + +err_req_irq: + free_eq_pages(eq); + return err; +} + +/** + * remove_eq - remove Event Queue + * @eq: the event queue + **/ +static void remove_eq(struct hinic_eq *eq) +{ + struct msix_entry *entry = &eq->msix_entry; + + free_irq(entry->vector, eq); + + if (eq->type == HINIC_AEQ) { + struct hinic_eq_work *aeq_work = &eq->aeq_work; + + cancel_work_sync(&aeq_work->work); + } else if (eq->type == HINIC_CEQ) { + tasklet_kill(&eq->ceq_tasklet); + } + + free_eq_pages(eq); +} + +/** + * hinic_aeqs_init - initialize all the aeqs + * @aeqs: pointer to Async eqs of the chip + * @hwif: the HW interface of a PCI function device + * @num_aeqs: number of AEQs + * @q_len: number of EQ elements + * @page_size: the page size of the pages in the event queue + * @msix_entries: msix entries associated with the event queues + * + * Return 0 - Success, negative - Failure + **/ +int hinic_aeqs_init(struct hinic_aeqs *aeqs, struct hinic_hwif *hwif, + int num_aeqs, u32 q_len, u32 page_size, + struct msix_entry *msix_entries) +{ + struct pci_dev *pdev = hwif->pdev; + int err, i, q_id; + + aeqs->workq = create_singlethread_workqueue(HINIC_EQS_WQ_NAME); + if (!aeqs->workq) + return -ENOMEM; + + aeqs->hwif = hwif; + aeqs->num_aeqs = num_aeqs; + + for (q_id = 0; q_id < num_aeqs; q_id++) { + err = init_eq(&aeqs->aeq[q_id], hwif, HINIC_AEQ, q_id, q_len, + page_size, msix_entries[q_id]); + if (err) { + dev_err(&pdev->dev, "Failed to init aeq %d\n", q_id); + goto err_init_aeq; + } + } + + return 0; + +err_init_aeq: + for (i = 0; i < q_id; i++) + remove_eq(&aeqs->aeq[i]); + + destroy_workqueue(aeqs->workq); + return err; +} + +/** + * hinic_aeqs_free - free all the aeqs + * @aeqs: pointer to Async eqs of the chip + **/ +void hinic_aeqs_free(struct hinic_aeqs *aeqs) +{ + int q_id; + + for (q_id = 0; q_id < aeqs->num_aeqs ; q_id++) + remove_eq(&aeqs->aeq[q_id]); + + destroy_workqueue(aeqs->workq); +} + +/** + * hinic_ceqs_init - init all the ceqs + * @ceqs: ceqs part of the chip + * @hwif: the hardware interface of a pci function device + * @num_ceqs: number of CEQs + * @q_len: number of EQ elements + * @page_size: the page size of the event queue + * @msix_entries: msix entries associated with the event queues + * + * Return 0 - Success, Negative - Failure + **/ +int hinic_ceqs_init(struct hinic_ceqs *ceqs, struct hinic_hwif *hwif, + int num_ceqs, u32 q_len, u32 page_size, + struct msix_entry *msix_entries) +{ + struct pci_dev *pdev = hwif->pdev; + int i, q_id, err; + + ceqs->hwif = hwif; + ceqs->num_ceqs = num_ceqs; + + for (q_id = 0; q_id < num_ceqs; q_id++) { + err = init_eq(&ceqs->ceq[q_id], hwif, HINIC_CEQ, q_id, q_len, + page_size, msix_entries[q_id]); + if (err) { + dev_err(&pdev->dev, "Failed to init ceq %d\n", q_id); + goto err_init_ceq; + } + } + + return 0; + +err_init_ceq: + for (i = 0; i < q_id; i++) + remove_eq(&ceqs->ceq[i]); + + return err; +} + +/** + * hinic_ceqs_free - free all the ceqs + * @ceqs: ceqs part of the chip + **/ +void hinic_ceqs_free(struct hinic_ceqs *ceqs) +{ + int q_id; + + for (q_id = 0; q_id < ceqs->num_ceqs; q_id++) + remove_eq(&ceqs->ceq[q_id]); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h new file mode 100644 index 000000000000..ecb9c2bc6dc8 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_eqs.h @@ -0,0 +1,265 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_EQS_H +#define HINIC_HW_EQS_H + +#include <linux/types.h> +#include <linux/workqueue.h> +#include <linux/pci.h> +#include <linux/sizes.h> +#include <linux/bitops.h> +#include <linux/interrupt.h> + +#include "hinic_hw_if.h" + +#define HINIC_AEQ_CTRL_0_INT_IDX_SHIFT 0 +#define HINIC_AEQ_CTRL_0_DMA_ATTR_SHIFT 12 +#define HINIC_AEQ_CTRL_0_PCI_INTF_IDX_SHIFT 20 +#define HINIC_AEQ_CTRL_0_INT_MODE_SHIFT 31 + +#define HINIC_AEQ_CTRL_0_INT_IDX_MASK 0x3FF +#define HINIC_AEQ_CTRL_0_DMA_ATTR_MASK 0x3F +#define HINIC_AEQ_CTRL_0_PCI_INTF_IDX_MASK 0x3 +#define HINIC_AEQ_CTRL_0_INT_MODE_MASK 0x1 + +#define HINIC_AEQ_CTRL_0_SET(val, member) \ + (((u32)(val) & HINIC_AEQ_CTRL_0_##member##_MASK) << \ + HINIC_AEQ_CTRL_0_##member##_SHIFT) + +#define HINIC_AEQ_CTRL_0_CLEAR(val, member) \ + ((val) & (~(HINIC_AEQ_CTRL_0_##member##_MASK \ + << HINIC_AEQ_CTRL_0_##member##_SHIFT))) + +#define HINIC_AEQ_CTRL_1_LEN_SHIFT 0 +#define HINIC_AEQ_CTRL_1_ELEM_SIZE_SHIFT 24 +#define HINIC_AEQ_CTRL_1_PAGE_SIZE_SHIFT 28 + +#define HINIC_AEQ_CTRL_1_LEN_MASK 0x1FFFFF +#define HINIC_AEQ_CTRL_1_ELEM_SIZE_MASK 0x3 +#define HINIC_AEQ_CTRL_1_PAGE_SIZE_MASK 0xF + +#define HINIC_AEQ_CTRL_1_SET(val, member) \ + (((u32)(val) & HINIC_AEQ_CTRL_1_##member##_MASK) << \ + HINIC_AEQ_CTRL_1_##member##_SHIFT) + +#define HINIC_AEQ_CTRL_1_CLEAR(val, member) \ + ((val) & (~(HINIC_AEQ_CTRL_1_##member##_MASK \ + << HINIC_AEQ_CTRL_1_##member##_SHIFT))) + +#define HINIC_CEQ_CTRL_0_INTR_IDX_SHIFT 0 +#define HINIC_CEQ_CTRL_0_DMA_ATTR_SHIFT 12 +#define HINIC_CEQ_CTRL_0_KICK_THRESH_SHIFT 20 +#define HINIC_CEQ_CTRL_0_PCI_INTF_IDX_SHIFT 24 +#define HINIC_CEQ_CTRL_0_INTR_MODE_SHIFT 31 + +#define HINIC_CEQ_CTRL_0_INTR_IDX_MASK 0x3FF +#define HINIC_CEQ_CTRL_0_DMA_ATTR_MASK 0x3F +#define HINIC_CEQ_CTRL_0_KICK_THRESH_MASK 0xF +#define HINIC_CEQ_CTRL_0_PCI_INTF_IDX_MASK 0x3 +#define HINIC_CEQ_CTRL_0_INTR_MODE_MASK 0x1 + +#define HINIC_CEQ_CTRL_0_SET(val, member) \ + (((u32)(val) & HINIC_CEQ_CTRL_0_##member##_MASK) << \ + HINIC_CEQ_CTRL_0_##member##_SHIFT) + +#define HINIC_CEQ_CTRL_0_CLEAR(val, member) \ + ((val) & (~(HINIC_CEQ_CTRL_0_##member##_MASK \ + << HINIC_CEQ_CTRL_0_##member##_SHIFT))) + +#define HINIC_CEQ_CTRL_1_LEN_SHIFT 0 +#define HINIC_CEQ_CTRL_1_PAGE_SIZE_SHIFT 28 + +#define HINIC_CEQ_CTRL_1_LEN_MASK 0x1FFFFF +#define HINIC_CEQ_CTRL_1_PAGE_SIZE_MASK 0xF + +#define HINIC_CEQ_CTRL_1_SET(val, member) \ + (((u32)(val) & HINIC_CEQ_CTRL_1_##member##_MASK) << \ + HINIC_CEQ_CTRL_1_##member##_SHIFT) + +#define HINIC_CEQ_CTRL_1_CLEAR(val, member) \ + ((val) & (~(HINIC_CEQ_CTRL_1_##member##_MASK \ + << HINIC_CEQ_CTRL_1_##member##_SHIFT))) + +#define HINIC_EQ_ELEM_DESC_TYPE_SHIFT 0 +#define HINIC_EQ_ELEM_DESC_SRC_SHIFT 7 +#define HINIC_EQ_ELEM_DESC_SIZE_SHIFT 8 +#define HINIC_EQ_ELEM_DESC_WRAPPED_SHIFT 31 + +#define HINIC_EQ_ELEM_DESC_TYPE_MASK 0x7F +#define HINIC_EQ_ELEM_DESC_SRC_MASK 0x1 +#define HINIC_EQ_ELEM_DESC_SIZE_MASK 0xFF +#define HINIC_EQ_ELEM_DESC_WRAPPED_MASK 0x1 + +#define HINIC_EQ_ELEM_DESC_SET(val, member) \ + (((u32)(val) & HINIC_EQ_ELEM_DESC_##member##_MASK) << \ + HINIC_EQ_ELEM_DESC_##member##_SHIFT) + +#define HINIC_EQ_ELEM_DESC_GET(val, member) \ + (((val) >> HINIC_EQ_ELEM_DESC_##member##_SHIFT) & \ + HINIC_EQ_ELEM_DESC_##member##_MASK) + +#define HINIC_EQ_CI_IDX_SHIFT 0 +#define HINIC_EQ_CI_WRAPPED_SHIFT 20 +#define HINIC_EQ_CI_XOR_CHKSUM_SHIFT 24 +#define HINIC_EQ_CI_INT_ARMED_SHIFT 31 + +#define HINIC_EQ_CI_IDX_MASK 0xFFFFF +#define HINIC_EQ_CI_WRAPPED_MASK 0x1 +#define HINIC_EQ_CI_XOR_CHKSUM_MASK 0xF +#define HINIC_EQ_CI_INT_ARMED_MASK 0x1 + +#define HINIC_EQ_CI_SET(val, member) \ + (((u32)(val) & HINIC_EQ_CI_##member##_MASK) << \ + HINIC_EQ_CI_##member##_SHIFT) + +#define HINIC_EQ_CI_CLEAR(val, member) \ + ((val) & (~(HINIC_EQ_CI_##member##_MASK \ + << HINIC_EQ_CI_##member##_SHIFT))) + +#define HINIC_MAX_AEQS 4 +#define HINIC_MAX_CEQS 32 + +#define HINIC_AEQE_SIZE 64 +#define HINIC_CEQE_SIZE 4 + +#define HINIC_AEQE_DESC_SIZE 4 +#define HINIC_AEQE_DATA_SIZE \ + (HINIC_AEQE_SIZE - HINIC_AEQE_DESC_SIZE) + +#define HINIC_DEFAULT_AEQ_LEN 64 +#define HINIC_DEFAULT_CEQ_LEN 1024 + +#define HINIC_EQ_PAGE_SIZE SZ_4K + +#define HINIC_CEQ_ID_CMDQ 0 + +enum hinic_eq_type { + HINIC_AEQ, + HINIC_CEQ, +}; + +enum hinic_aeq_type { + HINIC_MSG_FROM_MGMT_CPU = 2, + + HINIC_MAX_AEQ_EVENTS, +}; + +enum hinic_ceq_type { + HINIC_CEQ_CMDQ = 3, + + HINIC_MAX_CEQ_EVENTS, +}; + +enum hinic_eqe_state { + HINIC_EQE_ENABLED = BIT(0), + HINIC_EQE_RUNNING = BIT(1), +}; + +struct hinic_aeq_elem { + u8 data[HINIC_AEQE_DATA_SIZE]; + u32 desc; +}; + +struct hinic_eq_work { + struct work_struct work; + void *data; +}; + +struct hinic_eq { + struct hinic_hwif *hwif; + + enum hinic_eq_type type; + int q_id; + u32 q_len; + u32 page_size; + + u32 cons_idx; + int wrapped; + + size_t elem_size; + int num_pages; + int num_elem_in_pg; + + struct msix_entry msix_entry; + + dma_addr_t *dma_addr; + void **virt_addr; + + struct hinic_eq_work aeq_work; + + struct tasklet_struct ceq_tasklet; +}; + +struct hinic_hw_event_cb { + void (*hwe_handler)(void *handle, void *data, u8 size); + void *handle; + unsigned long hwe_state; +}; + +struct hinic_aeqs { + struct hinic_hwif *hwif; + + struct hinic_eq aeq[HINIC_MAX_AEQS]; + int num_aeqs; + + struct hinic_hw_event_cb hwe_cb[HINIC_MAX_AEQ_EVENTS]; + + struct workqueue_struct *workq; +}; + +struct hinic_ceq_cb { + void (*handler)(void *handle, u32 ceqe_data); + void *handle; + enum hinic_eqe_state ceqe_state; +}; + +struct hinic_ceqs { + struct hinic_hwif *hwif; + + struct hinic_eq ceq[HINIC_MAX_CEQS]; + int num_ceqs; + + struct hinic_ceq_cb ceq_cb[HINIC_MAX_CEQ_EVENTS]; +}; + +void hinic_aeq_register_hw_cb(struct hinic_aeqs *aeqs, + enum hinic_aeq_type event, void *handle, + void (*hwe_handler)(void *handle, void *data, + u8 size)); + +void hinic_aeq_unregister_hw_cb(struct hinic_aeqs *aeqs, + enum hinic_aeq_type event); + +void hinic_ceq_register_cb(struct hinic_ceqs *ceqs, + enum hinic_ceq_type event, void *handle, + void (*ceq_cb)(void *handle, u32 ceqe_data)); + +void hinic_ceq_unregister_cb(struct hinic_ceqs *ceqs, + enum hinic_ceq_type event); + +int hinic_aeqs_init(struct hinic_aeqs *aeqs, struct hinic_hwif *hwif, + int num_aeqs, u32 q_len, u32 page_size, + struct msix_entry *msix_entries); + +void hinic_aeqs_free(struct hinic_aeqs *aeqs); + +int hinic_ceqs_init(struct hinic_ceqs *ceqs, struct hinic_hwif *hwif, + int num_ceqs, u32 q_len, u32 page_size, + struct msix_entry *msix_entries); + +void hinic_ceqs_free(struct hinic_ceqs *ceqs); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c new file mode 100644 index 000000000000..823a17061a97 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.c @@ -0,0 +1,351 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <linux/types.h> +#include <linux/bitops.h> + +#include "hinic_hw_csr.h" +#include "hinic_hw_if.h" + +#define PCIE_ATTR_ENTRY 0 + +#define VALID_MSIX_IDX(attr, msix_index) ((msix_index) < (attr)->num_irqs) + +/** + * hinic_msix_attr_set - set message attribute for msix entry + * @hwif: the HW interface of a pci function device + * @msix_index: msix_index + * @pending_limit: the maximum pending interrupt events (unit 8) + * @coalesc_timer: coalesc period for interrupt (unit 8 us) + * @lli_timer: replenishing period for low latency credit (unit 8 us) + * @lli_credit_limit: maximum credits for low latency msix messages (unit 8) + * @resend_timer: maximum wait for resending msix (unit coalesc period) + * + * Return 0 - Success, negative - Failure + **/ +int hinic_msix_attr_set(struct hinic_hwif *hwif, u16 msix_index, + u8 pending_limit, u8 coalesc_timer, + u8 lli_timer, u8 lli_credit_limit, + u8 resend_timer) +{ + u32 msix_ctrl, addr; + + if (!VALID_MSIX_IDX(&hwif->attr, msix_index)) + return -EINVAL; + + msix_ctrl = HINIC_MSIX_ATTR_SET(pending_limit, PENDING_LIMIT) | + HINIC_MSIX_ATTR_SET(coalesc_timer, COALESC_TIMER) | + HINIC_MSIX_ATTR_SET(lli_timer, LLI_TIMER) | + HINIC_MSIX_ATTR_SET(lli_credit_limit, LLI_CREDIT) | + HINIC_MSIX_ATTR_SET(resend_timer, RESEND_TIMER); + + addr = HINIC_CSR_MSIX_CTRL_ADDR(msix_index); + + hinic_hwif_write_reg(hwif, addr, msix_ctrl); + return 0; +} + +/** + * hinic_msix_attr_get - get message attribute of msix entry + * @hwif: the HW interface of a pci function device + * @msix_index: msix_index + * @pending_limit: the maximum pending interrupt events (unit 8) + * @coalesc_timer: coalesc period for interrupt (unit 8 us) + * @lli_timer: replenishing period for low latency credit (unit 8 us) + * @lli_credit_limit: maximum credits for low latency msix messages (unit 8) + * @resend_timer: maximum wait for resending msix (unit coalesc period) + * + * Return 0 - Success, negative - Failure + **/ +int hinic_msix_attr_get(struct hinic_hwif *hwif, u16 msix_index, + u8 *pending_limit, u8 *coalesc_timer, + u8 *lli_timer, u8 *lli_credit_limit, + u8 *resend_timer) +{ + u32 addr, val; + + if (!VALID_MSIX_IDX(&hwif->attr, msix_index)) + return -EINVAL; + + addr = HINIC_CSR_MSIX_CTRL_ADDR(msix_index); + val = hinic_hwif_read_reg(hwif, addr); + + *pending_limit = HINIC_MSIX_ATTR_GET(val, PENDING_LIMIT); + *coalesc_timer = HINIC_MSIX_ATTR_GET(val, COALESC_TIMER); + *lli_timer = HINIC_MSIX_ATTR_GET(val, LLI_TIMER); + *lli_credit_limit = HINIC_MSIX_ATTR_GET(val, LLI_CREDIT); + *resend_timer = HINIC_MSIX_ATTR_GET(val, RESEND_TIMER); + return 0; +} + +/** + * hinic_msix_attr_cnt_clear - clear message attribute counters for msix entry + * @hwif: the HW interface of a pci function device + * @msix_index: msix_index + * + * Return 0 - Success, negative - Failure + **/ +int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index) +{ + u32 msix_ctrl, addr; + + if (!VALID_MSIX_IDX(&hwif->attr, msix_index)) + return -EINVAL; + + msix_ctrl = HINIC_MSIX_CNT_SET(1, RESEND_TIMER); + addr = HINIC_CSR_MSIX_CNT_ADDR(msix_index); + + hinic_hwif_write_reg(hwif, addr, msix_ctrl); + return 0; +} + +/** + * hinic_set_pf_action - set action on pf channel + * @hwif: the HW interface of a pci function device + * @action: action on pf channel + * + * Return 0 - Success, negative - Failure + **/ +void hinic_set_pf_action(struct hinic_hwif *hwif, enum hinic_pf_action action) +{ + u32 attr5 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR5_ADDR); + + attr5 = HINIC_FA5_CLEAR(attr5, PF_ACTION); + attr5 |= HINIC_FA5_SET(action, PF_ACTION); + + hinic_hwif_write_reg(hwif, HINIC_CSR_FUNC_ATTR5_ADDR, attr5); +} + +enum hinic_outbound_state hinic_outbound_state_get(struct hinic_hwif *hwif) +{ + u32 attr4 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR4_ADDR); + + return HINIC_FA4_GET(attr4, OUTBOUND_STATE); +} + +void hinic_outbound_state_set(struct hinic_hwif *hwif, + enum hinic_outbound_state outbound_state) +{ + u32 attr4 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR4_ADDR); + + attr4 = HINIC_FA4_CLEAR(attr4, OUTBOUND_STATE); + attr4 |= HINIC_FA4_SET(outbound_state, OUTBOUND_STATE); + + hinic_hwif_write_reg(hwif, HINIC_CSR_FUNC_ATTR4_ADDR, attr4); +} + +enum hinic_db_state hinic_db_state_get(struct hinic_hwif *hwif) +{ + u32 attr4 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR4_ADDR); + + return HINIC_FA4_GET(attr4, DB_STATE); +} + +void hinic_db_state_set(struct hinic_hwif *hwif, + enum hinic_db_state db_state) +{ + u32 attr4 = hinic_hwif_read_reg(hwif, HINIC_CSR_FUNC_ATTR4_ADDR); + + attr4 = HINIC_FA4_CLEAR(attr4, DB_STATE); + attr4 |= HINIC_FA4_SET(db_state, DB_STATE); + + hinic_hwif_write_reg(hwif, HINIC_CSR_FUNC_ATTR4_ADDR, attr4); +} + +/** + * hwif_ready - test if the HW is ready for use + * @hwif: the HW interface of a pci function device + * + * Return 0 - Success, negative - Failure + **/ +static int hwif_ready(struct hinic_hwif *hwif) +{ + struct pci_dev *pdev = hwif->pdev; + u32 addr, attr1; + + addr = HINIC_CSR_FUNC_ATTR1_ADDR; + attr1 = hinic_hwif_read_reg(hwif, addr); + + if (!HINIC_FA1_GET(attr1, INIT_STATUS)) { + dev_err(&pdev->dev, "hwif status is not ready\n"); + return -EFAULT; + } + + return 0; +} + +/** + * set_hwif_attr - set the attributes in the relevant members in hwif + * @hwif: the HW interface of a pci function device + * @attr0: the first attribute that was read from the hw + * @attr1: the second attribute that was read from the hw + **/ +static void set_hwif_attr(struct hinic_hwif *hwif, u32 attr0, u32 attr1) +{ + hwif->attr.func_idx = HINIC_FA0_GET(attr0, FUNC_IDX); + hwif->attr.pf_idx = HINIC_FA0_GET(attr0, PF_IDX); + hwif->attr.pci_intf_idx = HINIC_FA0_GET(attr0, PCI_INTF_IDX); + hwif->attr.func_type = HINIC_FA0_GET(attr0, FUNC_TYPE); + + hwif->attr.num_aeqs = BIT(HINIC_FA1_GET(attr1, AEQS_PER_FUNC)); + hwif->attr.num_ceqs = BIT(HINIC_FA1_GET(attr1, CEQS_PER_FUNC)); + hwif->attr.num_irqs = BIT(HINIC_FA1_GET(attr1, IRQS_PER_FUNC)); + hwif->attr.num_dma_attr = BIT(HINIC_FA1_GET(attr1, DMA_ATTR_PER_FUNC)); +} + +/** + * read_hwif_attr - read the attributes and set members in hwif + * @hwif: the HW interface of a pci function device + **/ +static void read_hwif_attr(struct hinic_hwif *hwif) +{ + u32 addr, attr0, attr1; + + addr = HINIC_CSR_FUNC_ATTR0_ADDR; + attr0 = hinic_hwif_read_reg(hwif, addr); + + addr = HINIC_CSR_FUNC_ATTR1_ADDR; + attr1 = hinic_hwif_read_reg(hwif, addr); + + set_hwif_attr(hwif, attr0, attr1); +} + +/** + * set_ppf - try to set hwif as ppf and set the type of hwif in this case + * @hwif: the HW interface of a pci function device + **/ +static void set_ppf(struct hinic_hwif *hwif) +{ + struct hinic_func_attr *attr = &hwif->attr; + u32 addr, val, ppf_election; + + /* Read Modify Write */ + addr = HINIC_CSR_PPF_ELECTION_ADDR(HINIC_HWIF_PCI_INTF(hwif)); + + val = hinic_hwif_read_reg(hwif, addr); + val = HINIC_PPF_ELECTION_CLEAR(val, IDX); + + ppf_election = HINIC_PPF_ELECTION_SET(HINIC_HWIF_FUNC_IDX(hwif), IDX); + + val |= ppf_election; + hinic_hwif_write_reg(hwif, addr, val); + + /* check PPF */ + val = hinic_hwif_read_reg(hwif, addr); + + attr->ppf_idx = HINIC_PPF_ELECTION_GET(val, IDX); + if (attr->ppf_idx == HINIC_HWIF_FUNC_IDX(hwif)) + attr->func_type = HINIC_PPF; +} + +/** + * set_dma_attr - set the dma attributes in the HW + * @hwif: the HW interface of a pci function device + * @entry_idx: the entry index in the dma table + * @st: PCIE TLP steering tag + * @at: PCIE TLP AT field + * @ph: PCIE TLP Processing Hint field + * @no_snooping: PCIE TLP No snooping + * @tph_en: PCIE TLP Processing Hint Enable + **/ +static void set_dma_attr(struct hinic_hwif *hwif, u32 entry_idx, + u8 st, u8 at, u8 ph, + enum hinic_pcie_nosnoop no_snooping, + enum hinic_pcie_tph tph_en) +{ + u32 addr, val, dma_attr_entry; + + /* Read Modify Write */ + addr = HINIC_CSR_DMA_ATTR_ADDR(entry_idx); + + val = hinic_hwif_read_reg(hwif, addr); + val = HINIC_DMA_ATTR_CLEAR(val, ST) & + HINIC_DMA_ATTR_CLEAR(val, AT) & + HINIC_DMA_ATTR_CLEAR(val, PH) & + HINIC_DMA_ATTR_CLEAR(val, NO_SNOOPING) & + HINIC_DMA_ATTR_CLEAR(val, TPH_EN); + + dma_attr_entry = HINIC_DMA_ATTR_SET(st, ST) | + HINIC_DMA_ATTR_SET(at, AT) | + HINIC_DMA_ATTR_SET(ph, PH) | + HINIC_DMA_ATTR_SET(no_snooping, NO_SNOOPING) | + HINIC_DMA_ATTR_SET(tph_en, TPH_EN); + + val |= dma_attr_entry; + hinic_hwif_write_reg(hwif, addr, val); +} + +/** + * dma_attr_table_init - initialize the the default dma attributes + * @hwif: the HW interface of a pci function device + **/ +static void dma_attr_init(struct hinic_hwif *hwif) +{ + set_dma_attr(hwif, PCIE_ATTR_ENTRY, HINIC_PCIE_ST_DISABLE, + HINIC_PCIE_AT_DISABLE, HINIC_PCIE_PH_DISABLE, + HINIC_PCIE_SNOOP, HINIC_PCIE_TPH_DISABLE); +} + +/** + * hinic_init_hwif - initialize the hw interface + * @hwif: the HW interface of a pci function device + * @pdev: the pci device for acessing PCI resources + * + * Return 0 - Success, negative - Failure + **/ +int hinic_init_hwif(struct hinic_hwif *hwif, struct pci_dev *pdev) +{ + int err; + + hwif->pdev = pdev; + + hwif->cfg_regs_bar = pci_ioremap_bar(pdev, HINIC_PCI_CFG_REGS_BAR); + if (!hwif->cfg_regs_bar) { + dev_err(&pdev->dev, "Failed to map configuration regs\n"); + return -ENOMEM; + } + + err = hwif_ready(hwif); + if (err) { + dev_err(&pdev->dev, "HW interface is not ready\n"); + goto err_hwif_ready; + } + + read_hwif_attr(hwif); + + if (HINIC_IS_PF(hwif)) + set_ppf(hwif); + + /* No transactionss before DMA is initialized */ + dma_attr_init(hwif); + return 0; + +err_hwif_ready: + iounmap(hwif->cfg_regs_bar); + return err; +} + +/** + * hinic_free_hwif - free the HW interface + * @hwif: the HW interface of a pci function device + **/ +void hinic_free_hwif(struct hinic_hwif *hwif) +{ + iounmap(hwif->cfg_regs_bar); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h new file mode 100644 index 000000000000..5b4760c0e9f5 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_if.h @@ -0,0 +1,272 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_IF_H +#define HINIC_HW_IF_H + +#include <linux/pci.h> +#include <linux/io.h> +#include <linux/types.h> +#include <asm/byteorder.h> + +#define HINIC_DMA_ATTR_ST_SHIFT 0 +#define HINIC_DMA_ATTR_AT_SHIFT 8 +#define HINIC_DMA_ATTR_PH_SHIFT 10 +#define HINIC_DMA_ATTR_NO_SNOOPING_SHIFT 12 +#define HINIC_DMA_ATTR_TPH_EN_SHIFT 13 + +#define HINIC_DMA_ATTR_ST_MASK 0xFF +#define HINIC_DMA_ATTR_AT_MASK 0x3 +#define HINIC_DMA_ATTR_PH_MASK 0x3 +#define HINIC_DMA_ATTR_NO_SNOOPING_MASK 0x1 +#define HINIC_DMA_ATTR_TPH_EN_MASK 0x1 + +#define HINIC_DMA_ATTR_SET(val, member) \ + (((u32)(val) & HINIC_DMA_ATTR_##member##_MASK) << \ + HINIC_DMA_ATTR_##member##_SHIFT) + +#define HINIC_DMA_ATTR_CLEAR(val, member) \ + ((val) & (~(HINIC_DMA_ATTR_##member##_MASK \ + << HINIC_DMA_ATTR_##member##_SHIFT))) + +#define HINIC_FA0_FUNC_IDX_SHIFT 0 +#define HINIC_FA0_PF_IDX_SHIFT 10 +#define HINIC_FA0_PCI_INTF_IDX_SHIFT 14 +/* reserved members - off 16 */ +#define HINIC_FA0_FUNC_TYPE_SHIFT 24 + +#define HINIC_FA0_FUNC_IDX_MASK 0x3FF +#define HINIC_FA0_PF_IDX_MASK 0xF +#define HINIC_FA0_PCI_INTF_IDX_MASK 0x3 +#define HINIC_FA0_FUNC_TYPE_MASK 0x1 + +#define HINIC_FA0_GET(val, member) \ + (((val) >> HINIC_FA0_##member##_SHIFT) & HINIC_FA0_##member##_MASK) + +#define HINIC_FA1_AEQS_PER_FUNC_SHIFT 8 +/* reserved members - off 10 */ +#define HINIC_FA1_CEQS_PER_FUNC_SHIFT 12 +/* reserved members - off 15 */ +#define HINIC_FA1_IRQS_PER_FUNC_SHIFT 20 +#define HINIC_FA1_DMA_ATTR_PER_FUNC_SHIFT 24 +/* reserved members - off 27 */ +#define HINIC_FA1_INIT_STATUS_SHIFT 30 + +#define HINIC_FA1_AEQS_PER_FUNC_MASK 0x3 +#define HINIC_FA1_CEQS_PER_FUNC_MASK 0x7 +#define HINIC_FA1_IRQS_PER_FUNC_MASK 0xF +#define HINIC_FA1_DMA_ATTR_PER_FUNC_MASK 0x7 +#define HINIC_FA1_INIT_STATUS_MASK 0x1 + +#define HINIC_FA1_GET(val, member) \ + (((val) >> HINIC_FA1_##member##_SHIFT) & HINIC_FA1_##member##_MASK) + +#define HINIC_FA4_OUTBOUND_STATE_SHIFT 0 +#define HINIC_FA4_DB_STATE_SHIFT 1 + +#define HINIC_FA4_OUTBOUND_STATE_MASK 0x1 +#define HINIC_FA4_DB_STATE_MASK 0x1 + +#define HINIC_FA4_GET(val, member) \ + (((val) >> HINIC_FA4_##member##_SHIFT) & HINIC_FA4_##member##_MASK) + +#define HINIC_FA4_SET(val, member) \ + ((((u32)val) & HINIC_FA4_##member##_MASK) << HINIC_FA4_##member##_SHIFT) + +#define HINIC_FA4_CLEAR(val, member) \ + ((val) & (~(HINIC_FA4_##member##_MASK << HINIC_FA4_##member##_SHIFT))) + +#define HINIC_FA5_PF_ACTION_SHIFT 0 +#define HINIC_FA5_PF_ACTION_MASK 0xFFFF + +#define HINIC_FA5_SET(val, member) \ + (((u32)(val) & HINIC_FA5_##member##_MASK) << HINIC_FA5_##member##_SHIFT) + +#define HINIC_FA5_CLEAR(val, member) \ + ((val) & (~(HINIC_FA5_##member##_MASK << HINIC_FA5_##member##_SHIFT))) + +#define HINIC_PPF_ELECTION_IDX_SHIFT 0 +#define HINIC_PPF_ELECTION_IDX_MASK 0x1F + +#define HINIC_PPF_ELECTION_SET(val, member) \ + (((u32)(val) & HINIC_PPF_ELECTION_##member##_MASK) << \ + HINIC_PPF_ELECTION_##member##_SHIFT) + +#define HINIC_PPF_ELECTION_GET(val, member) \ + (((val) >> HINIC_PPF_ELECTION_##member##_SHIFT) & \ + HINIC_PPF_ELECTION_##member##_MASK) + +#define HINIC_PPF_ELECTION_CLEAR(val, member) \ + ((val) & (~(HINIC_PPF_ELECTION_##member##_MASK \ + << HINIC_PPF_ELECTION_##member##_SHIFT))) + +#define HINIC_MSIX_PENDING_LIMIT_SHIFT 0 +#define HINIC_MSIX_COALESC_TIMER_SHIFT 8 +#define HINIC_MSIX_LLI_TIMER_SHIFT 16 +#define HINIC_MSIX_LLI_CREDIT_SHIFT 24 +#define HINIC_MSIX_RESEND_TIMER_SHIFT 29 + +#define HINIC_MSIX_PENDING_LIMIT_MASK 0xFF +#define HINIC_MSIX_COALESC_TIMER_MASK 0xFF +#define HINIC_MSIX_LLI_TIMER_MASK 0xFF +#define HINIC_MSIX_LLI_CREDIT_MASK 0x1F +#define HINIC_MSIX_RESEND_TIMER_MASK 0x7 + +#define HINIC_MSIX_ATTR_SET(val, member) \ + (((u32)(val) & HINIC_MSIX_##member##_MASK) << \ + HINIC_MSIX_##member##_SHIFT) + +#define HINIC_MSIX_ATTR_GET(val, member) \ + (((val) >> HINIC_MSIX_##member##_SHIFT) & \ + HINIC_MSIX_##member##_MASK) + +#define HINIC_MSIX_CNT_RESEND_TIMER_SHIFT 29 + +#define HINIC_MSIX_CNT_RESEND_TIMER_MASK 0x1 + +#define HINIC_MSIX_CNT_SET(val, member) \ + (((u32)(val) & HINIC_MSIX_CNT_##member##_MASK) << \ + HINIC_MSIX_CNT_##member##_SHIFT) + +#define HINIC_HWIF_NUM_AEQS(hwif) ((hwif)->attr.num_aeqs) +#define HINIC_HWIF_NUM_CEQS(hwif) ((hwif)->attr.num_ceqs) +#define HINIC_HWIF_NUM_IRQS(hwif) ((hwif)->attr.num_irqs) +#define HINIC_HWIF_FUNC_IDX(hwif) ((hwif)->attr.func_idx) +#define HINIC_HWIF_PCI_INTF(hwif) ((hwif)->attr.pci_intf_idx) +#define HINIC_HWIF_PF_IDX(hwif) ((hwif)->attr.pf_idx) + +#define HINIC_FUNC_TYPE(hwif) ((hwif)->attr.func_type) +#define HINIC_IS_PF(hwif) (HINIC_FUNC_TYPE(hwif) == HINIC_PF) +#define HINIC_IS_PPF(hwif) (HINIC_FUNC_TYPE(hwif) == HINIC_PPF) + +#define HINIC_PCI_CFG_REGS_BAR 0 +#define HINIC_PCI_DB_BAR 4 + +#define HINIC_PCIE_ST_DISABLE 0 +#define HINIC_PCIE_AT_DISABLE 0 +#define HINIC_PCIE_PH_DISABLE 0 + +#define HINIC_EQ_MSIX_PENDING_LIMIT_DEFAULT 0 /* Disabled */ +#define HINIC_EQ_MSIX_COALESC_TIMER_DEFAULT 0xFF /* max */ +#define HINIC_EQ_MSIX_LLI_TIMER_DEFAULT 0 /* Disabled */ +#define HINIC_EQ_MSIX_LLI_CREDIT_LIMIT_DEFAULT 0 /* Disabled */ +#define HINIC_EQ_MSIX_RESEND_TIMER_DEFAULT 7 /* max */ + +enum hinic_pcie_nosnoop { + HINIC_PCIE_SNOOP = 0, + HINIC_PCIE_NO_SNOOP = 1, +}; + +enum hinic_pcie_tph { + HINIC_PCIE_TPH_DISABLE = 0, + HINIC_PCIE_TPH_ENABLE = 1, +}; + +enum hinic_func_type { + HINIC_PF = 0, + HINIC_PPF = 2, +}; + +enum hinic_mod_type { + HINIC_MOD_COMM = 0, /* HW communication module */ + HINIC_MOD_L2NIC = 1, /* L2NIC module */ + HINIC_MOD_CFGM = 7, /* Configuration module */ + + HINIC_MOD_MAX = 15 +}; + +enum hinic_node_id { + HINIC_NODE_ID_MGMT = 21, +}; + +enum hinic_pf_action { + HINIC_PF_MGMT_INIT = 0x0, + + HINIC_PF_MGMT_ACTIVE = 0x11, +}; + +enum hinic_outbound_state { + HINIC_OUTBOUND_ENABLE = 0, + HINIC_OUTBOUND_DISABLE = 1, +}; + +enum hinic_db_state { + HINIC_DB_ENABLE = 0, + HINIC_DB_DISABLE = 1, +}; + +struct hinic_func_attr { + u16 func_idx; + u8 pf_idx; + u8 pci_intf_idx; + + enum hinic_func_type func_type; + + u8 ppf_idx; + + u16 num_irqs; + u8 num_aeqs; + u8 num_ceqs; + + u8 num_dma_attr; +}; + +struct hinic_hwif { + struct pci_dev *pdev; + void __iomem *cfg_regs_bar; + + struct hinic_func_attr attr; +}; + +static inline u32 hinic_hwif_read_reg(struct hinic_hwif *hwif, u32 reg) +{ + return be32_to_cpu(readl(hwif->cfg_regs_bar + reg)); +} + +static inline void hinic_hwif_write_reg(struct hinic_hwif *hwif, u32 reg, + u32 val) +{ + writel(cpu_to_be32(val), hwif->cfg_regs_bar + reg); +} + +int hinic_msix_attr_set(struct hinic_hwif *hwif, u16 msix_index, + u8 pending_limit, u8 coalesc_timer, + u8 lli_timer_cfg, u8 lli_credit_limit, + u8 resend_timer); + +int hinic_msix_attr_get(struct hinic_hwif *hwif, u16 msix_index, + u8 *pending_limit, u8 *coalesc_timer_cfg, + u8 *lli_timer, u8 *lli_credit_limit, + u8 *resend_timer); + +int hinic_msix_attr_cnt_clear(struct hinic_hwif *hwif, u16 msix_index); + +void hinic_set_pf_action(struct hinic_hwif *hwif, enum hinic_pf_action action); + +enum hinic_outbound_state hinic_outbound_state_get(struct hinic_hwif *hwif); + +void hinic_outbound_state_set(struct hinic_hwif *hwif, + enum hinic_outbound_state outbound_state); + +enum hinic_db_state hinic_db_state_get(struct hinic_hwif *hwif); + +void hinic_db_state_set(struct hinic_hwif *hwif, + enum hinic_db_state db_state); + +int hinic_init_hwif(struct hinic_hwif *hwif, struct pci_dev *pdev); + +void hinic_free_hwif(struct hinic_hwif *hwif); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c new file mode 100644 index 000000000000..8e5897669a3a --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.c @@ -0,0 +1,533 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/semaphore.h> +#include <linux/dma-mapping.h> +#include <linux/io.h> +#include <linux/err.h> + +#include "hinic_hw_if.h" +#include "hinic_hw_eqs.h" +#include "hinic_hw_wqe.h" +#include "hinic_hw_wq.h" +#include "hinic_hw_cmdq.h" +#include "hinic_hw_qp_ctxt.h" +#include "hinic_hw_qp.h" +#include "hinic_hw_io.h" + +#define CI_Q_ADDR_SIZE sizeof(u32) + +#define CI_ADDR(base_addr, q_id) ((base_addr) + \ + (q_id) * CI_Q_ADDR_SIZE) + +#define CI_TABLE_SIZE(num_qps) ((num_qps) * CI_Q_ADDR_SIZE) + +#define DB_IDX(db, db_base) \ + (((unsigned long)(db) - (unsigned long)(db_base)) / HINIC_DB_PAGE_SIZE) + +enum io_cmd { + IO_CMD_MODIFY_QUEUE_CTXT = 0, +}; + +static void init_db_area_idx(struct hinic_free_db_area *free_db_area) +{ + int i; + + for (i = 0; i < HINIC_DB_MAX_AREAS; i++) + free_db_area->db_idx[i] = i; + + free_db_area->alloc_pos = 0; + free_db_area->return_pos = HINIC_DB_MAX_AREAS; + + free_db_area->num_free = HINIC_DB_MAX_AREAS; + + sema_init(&free_db_area->idx_lock, 1); +} + +static void __iomem *get_db_area(struct hinic_func_to_io *func_to_io) +{ + struct hinic_free_db_area *free_db_area = &func_to_io->free_db_area; + int pos, idx; + + down(&free_db_area->idx_lock); + + free_db_area->num_free--; + + if (free_db_area->num_free < 0) { + free_db_area->num_free++; + up(&free_db_area->idx_lock); + return ERR_PTR(-ENOMEM); + } + + pos = free_db_area->alloc_pos++; + pos &= HINIC_DB_MAX_AREAS - 1; + + idx = free_db_area->db_idx[pos]; + + free_db_area->db_idx[pos] = -1; + + up(&free_db_area->idx_lock); + + return func_to_io->db_base + idx * HINIC_DB_PAGE_SIZE; +} + +static void return_db_area(struct hinic_func_to_io *func_to_io, + void __iomem *db_base) +{ + struct hinic_free_db_area *free_db_area = &func_to_io->free_db_area; + int pos, idx = DB_IDX(db_base, func_to_io->db_base); + + down(&free_db_area->idx_lock); + + pos = free_db_area->return_pos++; + pos &= HINIC_DB_MAX_AREAS - 1; + + free_db_area->db_idx[pos] = idx; + + free_db_area->num_free++; + + up(&free_db_area->idx_lock); +} + +static int write_sq_ctxts(struct hinic_func_to_io *func_to_io, u16 base_qpn, + u16 num_sqs) +{ + struct hinic_hwif *hwif = func_to_io->hwif; + struct hinic_sq_ctxt_block *sq_ctxt_block; + struct pci_dev *pdev = hwif->pdev; + struct hinic_cmdq_buf cmdq_buf; + struct hinic_sq_ctxt *sq_ctxt; + struct hinic_qp *qp; + u64 out_param; + int err, i; + + err = hinic_alloc_cmdq_buf(&func_to_io->cmdqs, &cmdq_buf); + if (err) { + dev_err(&pdev->dev, "Failed to allocate cmdq buf\n"); + return err; + } + + sq_ctxt_block = cmdq_buf.buf; + sq_ctxt = sq_ctxt_block->sq_ctxt; + + hinic_qp_prepare_header(&sq_ctxt_block->hdr, HINIC_QP_CTXT_TYPE_SQ, + num_sqs, func_to_io->max_qps); + for (i = 0; i < num_sqs; i++) { + qp = &func_to_io->qps[i]; + + hinic_sq_prepare_ctxt(&sq_ctxt[i], &qp->sq, + base_qpn + qp->q_id); + } + + cmdq_buf.size = HINIC_SQ_CTXT_SIZE(num_sqs); + + err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC, + IO_CMD_MODIFY_QUEUE_CTXT, &cmdq_buf, + &out_param); + if ((err) || (out_param != 0)) { + dev_err(&pdev->dev, "Failed to set SQ ctxts\n"); + err = -EFAULT; + } + + hinic_free_cmdq_buf(&func_to_io->cmdqs, &cmdq_buf); + return err; +} + +static int write_rq_ctxts(struct hinic_func_to_io *func_to_io, u16 base_qpn, + u16 num_rqs) +{ + struct hinic_hwif *hwif = func_to_io->hwif; + struct hinic_rq_ctxt_block *rq_ctxt_block; + struct pci_dev *pdev = hwif->pdev; + struct hinic_cmdq_buf cmdq_buf; + struct hinic_rq_ctxt *rq_ctxt; + struct hinic_qp *qp; + u64 out_param; + int err, i; + + err = hinic_alloc_cmdq_buf(&func_to_io->cmdqs, &cmdq_buf); + if (err) { + dev_err(&pdev->dev, "Failed to allocate cmdq buf\n"); + return err; + } + + rq_ctxt_block = cmdq_buf.buf; + rq_ctxt = rq_ctxt_block->rq_ctxt; + + hinic_qp_prepare_header(&rq_ctxt_block->hdr, HINIC_QP_CTXT_TYPE_RQ, + num_rqs, func_to_io->max_qps); + for (i = 0; i < num_rqs; i++) { + qp = &func_to_io->qps[i]; + + hinic_rq_prepare_ctxt(&rq_ctxt[i], &qp->rq, + base_qpn + qp->q_id); + } + + cmdq_buf.size = HINIC_RQ_CTXT_SIZE(num_rqs); + + err = hinic_cmdq_direct_resp(&func_to_io->cmdqs, HINIC_MOD_L2NIC, + IO_CMD_MODIFY_QUEUE_CTXT, &cmdq_buf, + &out_param); + if ((err) || (out_param != 0)) { + dev_err(&pdev->dev, "Failed to set RQ ctxts\n"); + err = -EFAULT; + } + + hinic_free_cmdq_buf(&func_to_io->cmdqs, &cmdq_buf); + return err; +} + +/** + * write_qp_ctxts - write the qp ctxt to HW + * @func_to_io: func to io channel that holds the IO components + * @base_qpn: first qp number + * @num_qps: number of qps to write + * + * Return 0 - Success, negative - Failure + **/ +static int write_qp_ctxts(struct hinic_func_to_io *func_to_io, u16 base_qpn, + u16 num_qps) +{ + return (write_sq_ctxts(func_to_io, base_qpn, num_qps) || + write_rq_ctxts(func_to_io, base_qpn, num_qps)); +} + +/** + * init_qp - Initialize a Queue Pair + * @func_to_io: func to io channel that holds the IO components + * @qp: pointer to the qp to initialize + * @q_id: the id of the qp + * @sq_msix_entry: msix entry for sq + * @rq_msix_entry: msix entry for rq + * + * Return 0 - Success, negative - Failure + **/ +static int init_qp(struct hinic_func_to_io *func_to_io, + struct hinic_qp *qp, int q_id, + struct msix_entry *sq_msix_entry, + struct msix_entry *rq_msix_entry) +{ + struct hinic_hwif *hwif = func_to_io->hwif; + struct pci_dev *pdev = hwif->pdev; + void __iomem *db_base; + int err; + + qp->q_id = q_id; + + err = hinic_wq_allocate(&func_to_io->wqs, &func_to_io->sq_wq[q_id], + HINIC_SQ_WQEBB_SIZE, HINIC_SQ_PAGE_SIZE, + HINIC_SQ_DEPTH, HINIC_SQ_WQE_MAX_SIZE); + if (err) { + dev_err(&pdev->dev, "Failed to allocate WQ for SQ\n"); + return err; + } + + err = hinic_wq_allocate(&func_to_io->wqs, &func_to_io->rq_wq[q_id], + HINIC_RQ_WQEBB_SIZE, HINIC_RQ_PAGE_SIZE, + HINIC_RQ_DEPTH, HINIC_RQ_WQE_SIZE); + if (err) { + dev_err(&pdev->dev, "Failed to allocate WQ for RQ\n"); + goto err_rq_alloc; + } + + db_base = get_db_area(func_to_io); + if (IS_ERR(db_base)) { + dev_err(&pdev->dev, "Failed to get DB area for SQ\n"); + err = PTR_ERR(db_base); + goto err_get_db; + } + + func_to_io->sq_db[q_id] = db_base; + + err = hinic_init_sq(&qp->sq, hwif, &func_to_io->sq_wq[q_id], + sq_msix_entry, + CI_ADDR(func_to_io->ci_addr_base, q_id), + CI_ADDR(func_to_io->ci_dma_base, q_id), db_base); + if (err) { + dev_err(&pdev->dev, "Failed to init SQ\n"); + goto err_sq_init; + } + + err = hinic_init_rq(&qp->rq, hwif, &func_to_io->rq_wq[q_id], + rq_msix_entry); + if (err) { + dev_err(&pdev->dev, "Failed to init RQ\n"); + goto err_rq_init; + } + + return 0; + +err_rq_init: + hinic_clean_sq(&qp->sq); + +err_sq_init: + return_db_area(func_to_io, db_base); + +err_get_db: + hinic_wq_free(&func_to_io->wqs, &func_to_io->rq_wq[q_id]); + +err_rq_alloc: + hinic_wq_free(&func_to_io->wqs, &func_to_io->sq_wq[q_id]); + return err; +} + +/** + * destroy_qp - Clean the resources of a Queue Pair + * @func_to_io: func to io channel that holds the IO components + * @qp: pointer to the qp to clean + **/ +static void destroy_qp(struct hinic_func_to_io *func_to_io, + struct hinic_qp *qp) +{ + int q_id = qp->q_id; + + hinic_clean_rq(&qp->rq); + hinic_clean_sq(&qp->sq); + + return_db_area(func_to_io, func_to_io->sq_db[q_id]); + + hinic_wq_free(&func_to_io->wqs, &func_to_io->rq_wq[q_id]); + hinic_wq_free(&func_to_io->wqs, &func_to_io->sq_wq[q_id]); +} + +/** + * hinic_io_create_qps - Create Queue Pairs + * @func_to_io: func to io channel that holds the IO components + * @base_qpn: base qp number + * @num_qps: number queue pairs to create + * @sq_msix_entry: msix entries for sq + * @rq_msix_entry: msix entries for rq + * + * Return 0 - Success, negative - Failure + **/ +int hinic_io_create_qps(struct hinic_func_to_io *func_to_io, + u16 base_qpn, int num_qps, + struct msix_entry *sq_msix_entries, + struct msix_entry *rq_msix_entries) +{ + struct hinic_hwif *hwif = func_to_io->hwif; + struct pci_dev *pdev = hwif->pdev; + size_t qps_size, wq_size, db_size; + void *ci_addr_base; + int i, j, err; + + qps_size = num_qps * sizeof(*func_to_io->qps); + func_to_io->qps = devm_kzalloc(&pdev->dev, qps_size, GFP_KERNEL); + if (!func_to_io->qps) + return -ENOMEM; + + wq_size = num_qps * sizeof(*func_to_io->sq_wq); + func_to_io->sq_wq = devm_kzalloc(&pdev->dev, wq_size, GFP_KERNEL); + if (!func_to_io->sq_wq) { + err = -ENOMEM; + goto err_sq_wq; + } + + wq_size = num_qps * sizeof(*func_to_io->rq_wq); + func_to_io->rq_wq = devm_kzalloc(&pdev->dev, wq_size, GFP_KERNEL); + if (!func_to_io->rq_wq) { + err = -ENOMEM; + goto err_rq_wq; + } + + db_size = num_qps * sizeof(*func_to_io->sq_db); + func_to_io->sq_db = devm_kzalloc(&pdev->dev, db_size, GFP_KERNEL); + if (!func_to_io->sq_db) { + err = -ENOMEM; + goto err_sq_db; + } + + ci_addr_base = dma_zalloc_coherent(&pdev->dev, CI_TABLE_SIZE(num_qps), + &func_to_io->ci_dma_base, + GFP_KERNEL); + if (!ci_addr_base) { + dev_err(&pdev->dev, "Failed to allocate CI area\n"); + err = -ENOMEM; + goto err_ci_base; + } + + func_to_io->ci_addr_base = ci_addr_base; + + for (i = 0; i < num_qps; i++) { + err = init_qp(func_to_io, &func_to_io->qps[i], i, + &sq_msix_entries[i], &rq_msix_entries[i]); + if (err) { + dev_err(&pdev->dev, "Failed to create QP %d\n", i); + goto err_init_qp; + } + } + + err = write_qp_ctxts(func_to_io, base_qpn, num_qps); + if (err) { + dev_err(&pdev->dev, "Failed to init QP ctxts\n"); + goto err_write_qp_ctxts; + } + + return 0; + +err_write_qp_ctxts: +err_init_qp: + for (j = 0; j < i; j++) + destroy_qp(func_to_io, &func_to_io->qps[j]); + + dma_free_coherent(&pdev->dev, CI_TABLE_SIZE(num_qps), + func_to_io->ci_addr_base, func_to_io->ci_dma_base); + +err_ci_base: + devm_kfree(&pdev->dev, func_to_io->sq_db); + +err_sq_db: + devm_kfree(&pdev->dev, func_to_io->rq_wq); + +err_rq_wq: + devm_kfree(&pdev->dev, func_to_io->sq_wq); + +err_sq_wq: + devm_kfree(&pdev->dev, func_to_io->qps); + return err; +} + +/** + * hinic_io_destroy_qps - Destroy the IO Queue Pairs + * @func_to_io: func to io channel that holds the IO components + * @num_qps: number queue pairs to destroy + **/ +void hinic_io_destroy_qps(struct hinic_func_to_io *func_to_io, int num_qps) +{ + struct hinic_hwif *hwif = func_to_io->hwif; + struct pci_dev *pdev = hwif->pdev; + size_t ci_table_size; + int i; + + ci_table_size = CI_TABLE_SIZE(num_qps); + + for (i = 0; i < num_qps; i++) + destroy_qp(func_to_io, &func_to_io->qps[i]); + + dma_free_coherent(&pdev->dev, ci_table_size, func_to_io->ci_addr_base, + func_to_io->ci_dma_base); + + devm_kfree(&pdev->dev, func_to_io->sq_db); + + devm_kfree(&pdev->dev, func_to_io->rq_wq); + devm_kfree(&pdev->dev, func_to_io->sq_wq); + + devm_kfree(&pdev->dev, func_to_io->qps); +} + +/** + * hinic_io_init - Initialize the IO components + * @func_to_io: func to io channel that holds the IO components + * @hwif: HW interface for accessing IO + * @max_qps: maximum QPs in HW + * @num_ceqs: number completion event queues + * @ceq_msix_entries: msix entries for ceqs + * + * Return 0 - Success, negative - Failure + **/ +int hinic_io_init(struct hinic_func_to_io *func_to_io, + struct hinic_hwif *hwif, u16 max_qps, int num_ceqs, + struct msix_entry *ceq_msix_entries) +{ + struct pci_dev *pdev = hwif->pdev; + enum hinic_cmdq_type cmdq, type; + void __iomem *db_area; + int err; + + func_to_io->hwif = hwif; + func_to_io->qps = NULL; + func_to_io->max_qps = max_qps; + + err = hinic_ceqs_init(&func_to_io->ceqs, hwif, num_ceqs, + HINIC_DEFAULT_CEQ_LEN, HINIC_EQ_PAGE_SIZE, + ceq_msix_entries); + if (err) { + dev_err(&pdev->dev, "Failed to init CEQs\n"); + return err; + } + + err = hinic_wqs_alloc(&func_to_io->wqs, 2 * max_qps, hwif); + if (err) { + dev_err(&pdev->dev, "Failed to allocate WQS for IO\n"); + goto err_wqs_alloc; + } + + func_to_io->db_base = pci_ioremap_bar(pdev, HINIC_PCI_DB_BAR); + if (!func_to_io->db_base) { + dev_err(&pdev->dev, "Failed to remap IO DB area\n"); + err = -ENOMEM; + goto err_db_ioremap; + } + + init_db_area_idx(&func_to_io->free_db_area); + + for (cmdq = HINIC_CMDQ_SYNC; cmdq < HINIC_MAX_CMDQ_TYPES; cmdq++) { + db_area = get_db_area(func_to_io); + if (IS_ERR(db_area)) { + dev_err(&pdev->dev, "Failed to get cmdq db area\n"); + err = PTR_ERR(db_area); + goto err_db_area; + } + + func_to_io->cmdq_db_area[cmdq] = db_area; + } + + err = hinic_init_cmdqs(&func_to_io->cmdqs, hwif, + func_to_io->cmdq_db_area); + if (err) { + dev_err(&pdev->dev, "Failed to initialize cmdqs\n"); + goto err_init_cmdqs; + } + + return 0; + +err_init_cmdqs: +err_db_area: + for (type = HINIC_CMDQ_SYNC; type < cmdq; type++) + return_db_area(func_to_io, func_to_io->cmdq_db_area[type]); + + iounmap(func_to_io->db_base); + +err_db_ioremap: + hinic_wqs_free(&func_to_io->wqs); + +err_wqs_alloc: + hinic_ceqs_free(&func_to_io->ceqs); + return err; +} + +/** + * hinic_io_free - Free the IO components + * @func_to_io: func to io channel that holds the IO components + **/ +void hinic_io_free(struct hinic_func_to_io *func_to_io) +{ + enum hinic_cmdq_type cmdq; + + hinic_free_cmdqs(&func_to_io->cmdqs); + + for (cmdq = HINIC_CMDQ_SYNC; cmdq < HINIC_MAX_CMDQ_TYPES; cmdq++) + return_db_area(func_to_io, func_to_io->cmdq_db_area[cmdq]); + + iounmap(func_to_io->db_base); + hinic_wqs_free(&func_to_io->wqs); + hinic_ceqs_free(&func_to_io->ceqs); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h new file mode 100644 index 000000000000..adb64179d47d --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_io.h @@ -0,0 +1,97 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_IO_H +#define HINIC_HW_IO_H + +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/semaphore.h> +#include <linux/sizes.h> + +#include "hinic_hw_if.h" +#include "hinic_hw_eqs.h" +#include "hinic_hw_wq.h" +#include "hinic_hw_cmdq.h" +#include "hinic_hw_qp.h" + +#define HINIC_DB_PAGE_SIZE SZ_4K +#define HINIC_DB_SIZE SZ_4M + +#define HINIC_DB_MAX_AREAS (HINIC_DB_SIZE / HINIC_DB_PAGE_SIZE) + +enum hinic_db_type { + HINIC_DB_CMDQ_TYPE, + HINIC_DB_SQ_TYPE, +}; + +enum hinic_io_path { + HINIC_CTRL_PATH, + HINIC_DATA_PATH, +}; + +struct hinic_free_db_area { + int db_idx[HINIC_DB_MAX_AREAS]; + + int alloc_pos; + int return_pos; + + int num_free; + + /* Lock for getting db area */ + struct semaphore idx_lock; +}; + +struct hinic_func_to_io { + struct hinic_hwif *hwif; + + struct hinic_ceqs ceqs; + + struct hinic_wqs wqs; + + struct hinic_wq *sq_wq; + struct hinic_wq *rq_wq; + + struct hinic_qp *qps; + u16 max_qps; + + void __iomem **sq_db; + void __iomem *db_base; + + void *ci_addr_base; + dma_addr_t ci_dma_base; + + struct hinic_free_db_area free_db_area; + + void __iomem *cmdq_db_area[HINIC_MAX_CMDQ_TYPES]; + + struct hinic_cmdqs cmdqs; +}; + +int hinic_io_create_qps(struct hinic_func_to_io *func_to_io, + u16 base_qpn, int num_qps, + struct msix_entry *sq_msix_entries, + struct msix_entry *rq_msix_entries); + +void hinic_io_destroy_qps(struct hinic_func_to_io *func_to_io, + int num_qps); + +int hinic_io_init(struct hinic_func_to_io *func_to_io, + struct hinic_hwif *hwif, u16 max_qps, int num_ceqs, + struct msix_entry *ceq_msix_entries); + +void hinic_io_free(struct hinic_func_to_io *func_to_io); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c new file mode 100644 index 000000000000..278dc13f3dae --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.c @@ -0,0 +1,597 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/semaphore.h> +#include <linux/completion.h> +#include <linux/slab.h> +#include <asm/barrier.h> + +#include "hinic_hw_if.h" +#include "hinic_hw_eqs.h" +#include "hinic_hw_api_cmd.h" +#include "hinic_hw_mgmt.h" +#include "hinic_hw_dev.h" + +#define SYNC_MSG_ID_MASK 0x1FF + +#define SYNC_MSG_ID(pf_to_mgmt) ((pf_to_mgmt)->sync_msg_id) + +#define SYNC_MSG_ID_INC(pf_to_mgmt) (SYNC_MSG_ID(pf_to_mgmt) = \ + ((SYNC_MSG_ID(pf_to_mgmt) + 1) & \ + SYNC_MSG_ID_MASK)) + +#define MSG_SZ_IS_VALID(in_size) ((in_size) <= MAX_MSG_LEN) + +#define MGMT_MSG_LEN_MIN 20 +#define MGMT_MSG_LEN_STEP 16 +#define MGMT_MSG_RSVD_FOR_DEV 8 + +#define SEGMENT_LEN 48 + +#define MAX_PF_MGMT_BUF_SIZE 2048 + +/* Data should be SEG LEN size aligned */ +#define MAX_MSG_LEN 2016 + +#define MSG_NOT_RESP 0xFFFF + +#define MGMT_MSG_TIMEOUT 1000 + +#define mgmt_to_pfhwdev(pf_mgmt) \ + container_of(pf_mgmt, struct hinic_pfhwdev, pf_to_mgmt) + +enum msg_segment_type { + NOT_LAST_SEGMENT = 0, + LAST_SEGMENT = 1, +}; + +enum mgmt_direction_type { + MGMT_DIRECT_SEND = 0, + MGMT_RESP = 1, +}; + +enum msg_ack_type { + MSG_ACK = 0, + MSG_NO_ACK = 1, +}; + +/** + * hinic_register_mgmt_msg_cb - register msg handler for a msg from a module + * @pf_to_mgmt: PF to MGMT channel + * @mod: module in the chip that this handler will handle its messages + * @handle: private data for the callback + * @callback: the handler that will handle messages + **/ +void hinic_register_mgmt_msg_cb(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod, + void *handle, + void (*callback)(void *handle, + u8 cmd, void *buf_in, + u16 in_size, void *buf_out, + u16 *out_size)) +{ + struct hinic_mgmt_cb *mgmt_cb = &pf_to_mgmt->mgmt_cb[mod]; + + mgmt_cb->cb = callback; + mgmt_cb->handle = handle; + mgmt_cb->state = HINIC_MGMT_CB_ENABLED; +} + +/** + * hinic_unregister_mgmt_msg_cb - unregister msg handler for a msg from a module + * @pf_to_mgmt: PF to MGMT channel + * @mod: module in the chip that this handler handles its messages + **/ +void hinic_unregister_mgmt_msg_cb(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod) +{ + struct hinic_mgmt_cb *mgmt_cb = &pf_to_mgmt->mgmt_cb[mod]; + + mgmt_cb->state &= ~HINIC_MGMT_CB_ENABLED; + + while (mgmt_cb->state & HINIC_MGMT_CB_RUNNING) + schedule(); + + mgmt_cb->cb = NULL; +} + +/** + * prepare_header - prepare the header of the message + * @pf_to_mgmt: PF to MGMT channel + * @msg_len: the length of the message + * @mod: module in the chip that will get the message + * @ack_type: ask for response + * @direction: the direction of the message + * @cmd: command of the message + * @msg_id: message id + * + * Return the prepared header value + **/ +static u64 prepare_header(struct hinic_pf_to_mgmt *pf_to_mgmt, + u16 msg_len, enum hinic_mod_type mod, + enum msg_ack_type ack_type, + enum mgmt_direction_type direction, + u16 cmd, u16 msg_id) +{ + struct hinic_hwif *hwif = pf_to_mgmt->hwif; + + return HINIC_MSG_HEADER_SET(msg_len, MSG_LEN) | + HINIC_MSG_HEADER_SET(mod, MODULE) | + HINIC_MSG_HEADER_SET(SEGMENT_LEN, SEG_LEN) | + HINIC_MSG_HEADER_SET(ack_type, NO_ACK) | + HINIC_MSG_HEADER_SET(0, ASYNC_MGMT_TO_PF) | + HINIC_MSG_HEADER_SET(0, SEQID) | + HINIC_MSG_HEADER_SET(LAST_SEGMENT, LAST) | + HINIC_MSG_HEADER_SET(direction, DIRECTION) | + HINIC_MSG_HEADER_SET(cmd, CMD) | + HINIC_MSG_HEADER_SET(HINIC_HWIF_PCI_INTF(hwif), PCI_INTF) | + HINIC_MSG_HEADER_SET(HINIC_HWIF_PF_IDX(hwif), PF_IDX) | + HINIC_MSG_HEADER_SET(msg_id, MSG_ID); +} + +/** + * prepare_mgmt_cmd - prepare the mgmt command + * @mgmt_cmd: pointer to the command to prepare + * @header: pointer of the header for the message + * @msg: the data of the message + * @msg_len: the length of the message + **/ +static void prepare_mgmt_cmd(u8 *mgmt_cmd, u64 *header, u8 *msg, u16 msg_len) +{ + memset(mgmt_cmd, 0, MGMT_MSG_RSVD_FOR_DEV); + + mgmt_cmd += MGMT_MSG_RSVD_FOR_DEV; + memcpy(mgmt_cmd, header, sizeof(*header)); + + mgmt_cmd += sizeof(*header); + memcpy(mgmt_cmd, msg, msg_len); +} + +/** + * mgmt_msg_len - calculate the total message length + * @msg_data_len: the length of the message data + * + * Return the total message length + **/ +static u16 mgmt_msg_len(u16 msg_data_len) +{ + /* RSVD + HEADER_SIZE + DATA_LEN */ + u16 msg_len = MGMT_MSG_RSVD_FOR_DEV + sizeof(u64) + msg_data_len; + + if (msg_len > MGMT_MSG_LEN_MIN) + msg_len = MGMT_MSG_LEN_MIN + + ALIGN((msg_len - MGMT_MSG_LEN_MIN), + MGMT_MSG_LEN_STEP); + else + msg_len = MGMT_MSG_LEN_MIN; + + return msg_len; +} + +/** + * send_msg_to_mgmt - send message to mgmt by API CMD + * @pf_to_mgmt: PF to MGMT channel + * @mod: module in the chip that will get the message + * @cmd: command of the message + * @data: the msg data + * @data_len: the msg data length + * @ack_type: ask for response + * @direction: the direction of the original message + * @resp_msg_id: msg id to response for + * + * Return 0 - Success, negative - Failure + **/ +static int send_msg_to_mgmt(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod, u8 cmd, + u8 *data, u16 data_len, + enum msg_ack_type ack_type, + enum mgmt_direction_type direction, + u16 resp_msg_id) +{ + struct hinic_api_cmd_chain *chain; + u64 header; + u16 msg_id; + + msg_id = SYNC_MSG_ID(pf_to_mgmt); + + if (direction == MGMT_RESP) { + header = prepare_header(pf_to_mgmt, data_len, mod, ack_type, + direction, cmd, resp_msg_id); + } else { + SYNC_MSG_ID_INC(pf_to_mgmt); + header = prepare_header(pf_to_mgmt, data_len, mod, ack_type, + direction, cmd, msg_id); + } + + prepare_mgmt_cmd(pf_to_mgmt->sync_msg_buf, &header, data, data_len); + + chain = pf_to_mgmt->cmd_chain[HINIC_API_CMD_WRITE_TO_MGMT_CPU]; + return hinic_api_cmd_write(chain, HINIC_NODE_ID_MGMT, + pf_to_mgmt->sync_msg_buf, + mgmt_msg_len(data_len)); +} + +/** + * msg_to_mgmt_sync - send sync message to mgmt + * @pf_to_mgmt: PF to MGMT channel + * @mod: module in the chip that will get the message + * @cmd: command of the message + * @buf_in: the msg data + * @in_size: the msg data length + * @buf_out: response + * @out_size: response length + * @direction: the direction of the original message + * @resp_msg_id: msg id to response for + * + * Return 0 - Success, negative - Failure + **/ +static int msg_to_mgmt_sync(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod, u8 cmd, + u8 *buf_in, u16 in_size, + u8 *buf_out, u16 *out_size, + enum mgmt_direction_type direction, + u16 resp_msg_id) +{ + struct hinic_hwif *hwif = pf_to_mgmt->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_recv_msg *recv_msg; + struct completion *recv_done; + u16 msg_id; + int err; + + /* Lock the sync_msg_buf */ + down(&pf_to_mgmt->sync_msg_lock); + + recv_msg = &pf_to_mgmt->recv_resp_msg_from_mgmt; + recv_done = &recv_msg->recv_done; + + if (resp_msg_id == MSG_NOT_RESP) + msg_id = SYNC_MSG_ID(pf_to_mgmt); + else + msg_id = resp_msg_id; + + init_completion(recv_done); + + err = send_msg_to_mgmt(pf_to_mgmt, mod, cmd, buf_in, in_size, + MSG_ACK, direction, resp_msg_id); + if (err) { + dev_err(&pdev->dev, "Failed to send sync msg to mgmt\n"); + goto unlock_sync_msg; + } + + if (!wait_for_completion_timeout(recv_done, MGMT_MSG_TIMEOUT)) { + dev_err(&pdev->dev, "MGMT timeout, MSG id = %d\n", msg_id); + err = -ETIMEDOUT; + goto unlock_sync_msg; + } + + smp_rmb(); /* verify reading after completion */ + + if (recv_msg->msg_id != msg_id) { + dev_err(&pdev->dev, "incorrect MSG for id = %d\n", msg_id); + err = -EFAULT; + goto unlock_sync_msg; + } + + if ((buf_out) && (recv_msg->msg_len <= MAX_PF_MGMT_BUF_SIZE)) { + memcpy(buf_out, recv_msg->msg, recv_msg->msg_len); + *out_size = recv_msg->msg_len; + } + +unlock_sync_msg: + up(&pf_to_mgmt->sync_msg_lock); + return err; +} + +/** + * msg_to_mgmt_async - send message to mgmt without response + * @pf_to_mgmt: PF to MGMT channel + * @mod: module in the chip that will get the message + * @cmd: command of the message + * @buf_in: the msg data + * @in_size: the msg data length + * @direction: the direction of the original message + * @resp_msg_id: msg id to response for + * + * Return 0 - Success, negative - Failure + **/ +static int msg_to_mgmt_async(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod, u8 cmd, + u8 *buf_in, u16 in_size, + enum mgmt_direction_type direction, + u16 resp_msg_id) +{ + int err; + + /* Lock the sync_msg_buf */ + down(&pf_to_mgmt->sync_msg_lock); + + err = send_msg_to_mgmt(pf_to_mgmt, mod, cmd, buf_in, in_size, + MSG_NO_ACK, direction, resp_msg_id); + + up(&pf_to_mgmt->sync_msg_lock); + return err; +} + +/** + * hinic_msg_to_mgmt - send message to mgmt + * @pf_to_mgmt: PF to MGMT channel + * @mod: module in the chip that will get the message + * @cmd: command of the message + * @buf_in: the msg data + * @in_size: the msg data length + * @buf_out: response + * @out_size: returned response length + * @sync: sync msg or async msg + * + * Return 0 - Success, negative - Failure + **/ +int hinic_msg_to_mgmt(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod, u8 cmd, + void *buf_in, u16 in_size, void *buf_out, u16 *out_size, + enum hinic_mgmt_msg_type sync) +{ + struct hinic_hwif *hwif = pf_to_mgmt->hwif; + struct pci_dev *pdev = hwif->pdev; + + if (sync != HINIC_MGMT_MSG_SYNC) { + dev_err(&pdev->dev, "Invalid MGMT msg type\n"); + return -EINVAL; + } + + if (!MSG_SZ_IS_VALID(in_size)) { + dev_err(&pdev->dev, "Invalid MGMT msg buffer size\n"); + return -EINVAL; + } + + return msg_to_mgmt_sync(pf_to_mgmt, mod, cmd, buf_in, in_size, + buf_out, out_size, MGMT_DIRECT_SEND, + MSG_NOT_RESP); +} + +/** + * mgmt_recv_msg_handler - handler for message from mgmt cpu + * @pf_to_mgmt: PF to MGMT channel + * @recv_msg: received message details + **/ +static void mgmt_recv_msg_handler(struct hinic_pf_to_mgmt *pf_to_mgmt, + struct hinic_recv_msg *recv_msg) +{ + struct hinic_hwif *hwif = pf_to_mgmt->hwif; + struct pci_dev *pdev = hwif->pdev; + u8 *buf_out = recv_msg->buf_out; + struct hinic_mgmt_cb *mgmt_cb; + unsigned long cb_state; + u16 out_size = 0; + + if (recv_msg->mod >= HINIC_MOD_MAX) { + dev_err(&pdev->dev, "Unknown MGMT MSG module = %d\n", + recv_msg->mod); + return; + } + + mgmt_cb = &pf_to_mgmt->mgmt_cb[recv_msg->mod]; + + cb_state = cmpxchg(&mgmt_cb->state, + HINIC_MGMT_CB_ENABLED, + HINIC_MGMT_CB_ENABLED | HINIC_MGMT_CB_RUNNING); + + if ((cb_state == HINIC_MGMT_CB_ENABLED) && (mgmt_cb->cb)) + mgmt_cb->cb(mgmt_cb->handle, recv_msg->cmd, + recv_msg->msg, recv_msg->msg_len, + buf_out, &out_size); + else + dev_err(&pdev->dev, "No MGMT msg handler, mod = %d\n", + recv_msg->mod); + + mgmt_cb->state &= ~HINIC_MGMT_CB_RUNNING; + + if (!recv_msg->async_mgmt_to_pf) + /* MGMT sent sync msg, send the response */ + msg_to_mgmt_async(pf_to_mgmt, recv_msg->mod, recv_msg->cmd, + buf_out, out_size, MGMT_RESP, + recv_msg->msg_id); +} + +/** + * mgmt_resp_msg_handler - handler for a response message from mgmt cpu + * @pf_to_mgmt: PF to MGMT channel + * @recv_msg: received message details + **/ +static void mgmt_resp_msg_handler(struct hinic_pf_to_mgmt *pf_to_mgmt, + struct hinic_recv_msg *recv_msg) +{ + wmb(); /* verify writing all, before reading */ + + complete(&recv_msg->recv_done); +} + +/** + * recv_mgmt_msg_handler - handler for a message from mgmt cpu + * @pf_to_mgmt: PF to MGMT channel + * @header: the header of the message + * @recv_msg: received message details + **/ +static void recv_mgmt_msg_handler(struct hinic_pf_to_mgmt *pf_to_mgmt, + u64 *header, struct hinic_recv_msg *recv_msg) +{ + struct hinic_hwif *hwif = pf_to_mgmt->hwif; + struct pci_dev *pdev = hwif->pdev; + int seq_id, seg_len; + u8 *msg_body; + + seq_id = HINIC_MSG_HEADER_GET(*header, SEQID); + seg_len = HINIC_MSG_HEADER_GET(*header, SEG_LEN); + + if (seq_id >= (MAX_MSG_LEN / SEGMENT_LEN)) { + dev_err(&pdev->dev, "recv big mgmt msg\n"); + return; + } + + msg_body = (u8 *)header + sizeof(*header); + memcpy(recv_msg->msg + seq_id * SEGMENT_LEN, msg_body, seg_len); + + if (!HINIC_MSG_HEADER_GET(*header, LAST)) + return; + + recv_msg->cmd = HINIC_MSG_HEADER_GET(*header, CMD); + recv_msg->mod = HINIC_MSG_HEADER_GET(*header, MODULE); + recv_msg->async_mgmt_to_pf = HINIC_MSG_HEADER_GET(*header, + ASYNC_MGMT_TO_PF); + recv_msg->msg_len = HINIC_MSG_HEADER_GET(*header, MSG_LEN); + recv_msg->msg_id = HINIC_MSG_HEADER_GET(*header, MSG_ID); + + if (HINIC_MSG_HEADER_GET(*header, DIRECTION) == MGMT_RESP) + mgmt_resp_msg_handler(pf_to_mgmt, recv_msg); + else + mgmt_recv_msg_handler(pf_to_mgmt, recv_msg); +} + +/** + * mgmt_msg_aeqe_handler - handler for a mgmt message event + * @handle: PF to MGMT channel + * @data: the header of the message + * @size: unused + **/ +static void mgmt_msg_aeqe_handler(void *handle, void *data, u8 size) +{ + struct hinic_pf_to_mgmt *pf_to_mgmt = handle; + struct hinic_recv_msg *recv_msg; + u64 *header = (u64 *)data; + + recv_msg = HINIC_MSG_HEADER_GET(*header, DIRECTION) == + MGMT_DIRECT_SEND ? + &pf_to_mgmt->recv_msg_from_mgmt : + &pf_to_mgmt->recv_resp_msg_from_mgmt; + + recv_mgmt_msg_handler(pf_to_mgmt, header, recv_msg); +} + +/** + * alloc_recv_msg - allocate receive message memory + * @pf_to_mgmt: PF to MGMT channel + * @recv_msg: pointer that will hold the allocated data + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_recv_msg(struct hinic_pf_to_mgmt *pf_to_mgmt, + struct hinic_recv_msg *recv_msg) +{ + struct hinic_hwif *hwif = pf_to_mgmt->hwif; + struct pci_dev *pdev = hwif->pdev; + + recv_msg->msg = devm_kzalloc(&pdev->dev, MAX_PF_MGMT_BUF_SIZE, + GFP_KERNEL); + if (!recv_msg->msg) + return -ENOMEM; + + recv_msg->buf_out = devm_kzalloc(&pdev->dev, MAX_PF_MGMT_BUF_SIZE, + GFP_KERNEL); + if (!recv_msg->buf_out) + return -ENOMEM; + + return 0; +} + +/** + * alloc_msg_buf - allocate all the message buffers of PF to MGMT channel + * @pf_to_mgmt: PF to MGMT channel + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_msg_buf(struct hinic_pf_to_mgmt *pf_to_mgmt) +{ + struct hinic_hwif *hwif = pf_to_mgmt->hwif; + struct pci_dev *pdev = hwif->pdev; + int err; + + err = alloc_recv_msg(pf_to_mgmt, + &pf_to_mgmt->recv_msg_from_mgmt); + if (err) { + dev_err(&pdev->dev, "Failed to allocate recv msg\n"); + return err; + } + + err = alloc_recv_msg(pf_to_mgmt, + &pf_to_mgmt->recv_resp_msg_from_mgmt); + if (err) { + dev_err(&pdev->dev, "Failed to allocate resp recv msg\n"); + return err; + } + + pf_to_mgmt->sync_msg_buf = devm_kzalloc(&pdev->dev, + MAX_PF_MGMT_BUF_SIZE, + GFP_KERNEL); + if (!pf_to_mgmt->sync_msg_buf) + return -ENOMEM; + + return 0; +} + +/** + * hinic_pf_to_mgmt_init - initialize PF to MGMT channel + * @pf_to_mgmt: PF to MGMT channel + * @hwif: HW interface the PF to MGMT will use for accessing HW + * + * Return 0 - Success, negative - Failure + **/ +int hinic_pf_to_mgmt_init(struct hinic_pf_to_mgmt *pf_to_mgmt, + struct hinic_hwif *hwif) +{ + struct hinic_pfhwdev *pfhwdev = mgmt_to_pfhwdev(pf_to_mgmt); + struct hinic_hwdev *hwdev = &pfhwdev->hwdev; + struct pci_dev *pdev = hwif->pdev; + int err; + + pf_to_mgmt->hwif = hwif; + + sema_init(&pf_to_mgmt->sync_msg_lock, 1); + pf_to_mgmt->sync_msg_id = 0; + + err = alloc_msg_buf(pf_to_mgmt); + if (err) { + dev_err(&pdev->dev, "Failed to allocate msg buffers\n"); + return err; + } + + err = hinic_api_cmd_init(pf_to_mgmt->cmd_chain, hwif); + if (err) { + dev_err(&pdev->dev, "Failed to initialize cmd chains\n"); + return err; + } + + hinic_aeq_register_hw_cb(&hwdev->aeqs, HINIC_MSG_FROM_MGMT_CPU, + pf_to_mgmt, + mgmt_msg_aeqe_handler); + return 0; +} + +/** + * hinic_pf_to_mgmt_free - free PF to MGMT channel + * @pf_to_mgmt: PF to MGMT channel + **/ +void hinic_pf_to_mgmt_free(struct hinic_pf_to_mgmt *pf_to_mgmt) +{ + struct hinic_pfhwdev *pfhwdev = mgmt_to_pfhwdev(pf_to_mgmt); + struct hinic_hwdev *hwdev = &pfhwdev->hwdev; + + hinic_aeq_unregister_hw_cb(&hwdev->aeqs, HINIC_MSG_FROM_MGMT_CPU); + hinic_api_cmd_free(pf_to_mgmt->cmd_chain); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h new file mode 100644 index 000000000000..320711e8dee6 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_mgmt.h @@ -0,0 +1,153 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_MGMT_H +#define HINIC_HW_MGMT_H + +#include <linux/types.h> +#include <linux/semaphore.h> +#include <linux/completion.h> +#include <linux/bitops.h> + +#include "hinic_hw_if.h" +#include "hinic_hw_api_cmd.h" + +#define HINIC_MSG_HEADER_MSG_LEN_SHIFT 0 +#define HINIC_MSG_HEADER_MODULE_SHIFT 11 +#define HINIC_MSG_HEADER_SEG_LEN_SHIFT 16 +#define HINIC_MSG_HEADER_NO_ACK_SHIFT 22 +#define HINIC_MSG_HEADER_ASYNC_MGMT_TO_PF_SHIFT 23 +#define HINIC_MSG_HEADER_SEQID_SHIFT 24 +#define HINIC_MSG_HEADER_LAST_SHIFT 30 +#define HINIC_MSG_HEADER_DIRECTION_SHIFT 31 +#define HINIC_MSG_HEADER_CMD_SHIFT 32 +#define HINIC_MSG_HEADER_ZEROS_SHIFT 40 +#define HINIC_MSG_HEADER_PCI_INTF_SHIFT 48 +#define HINIC_MSG_HEADER_PF_IDX_SHIFT 50 +#define HINIC_MSG_HEADER_MSG_ID_SHIFT 54 + +#define HINIC_MSG_HEADER_MSG_LEN_MASK 0x7FF +#define HINIC_MSG_HEADER_MODULE_MASK 0x1F +#define HINIC_MSG_HEADER_SEG_LEN_MASK 0x3F +#define HINIC_MSG_HEADER_NO_ACK_MASK 0x1 +#define HINIC_MSG_HEADER_ASYNC_MGMT_TO_PF_MASK 0x1 +#define HINIC_MSG_HEADER_SEQID_MASK 0x3F +#define HINIC_MSG_HEADER_LAST_MASK 0x1 +#define HINIC_MSG_HEADER_DIRECTION_MASK 0x1 +#define HINIC_MSG_HEADER_CMD_MASK 0xFF +#define HINIC_MSG_HEADER_ZEROS_MASK 0xFF +#define HINIC_MSG_HEADER_PCI_INTF_MASK 0x3 +#define HINIC_MSG_HEADER_PF_IDX_MASK 0xF +#define HINIC_MSG_HEADER_MSG_ID_MASK 0x3FF + +#define HINIC_MSG_HEADER_SET(val, member) \ + ((u64)((val) & HINIC_MSG_HEADER_##member##_MASK) << \ + HINIC_MSG_HEADER_##member##_SHIFT) + +#define HINIC_MSG_HEADER_GET(val, member) \ + (((val) >> HINIC_MSG_HEADER_##member##_SHIFT) & \ + HINIC_MSG_HEADER_##member##_MASK) + +enum hinic_mgmt_msg_type { + HINIC_MGMT_MSG_SYNC = 1, +}; + +enum hinic_cfg_cmd { + HINIC_CFG_NIC_CAP = 0, +}; + +enum hinic_comm_cmd { + HINIC_COMM_CMD_IO_STATUS_GET = 0x3, + + HINIC_COMM_CMD_CMDQ_CTXT_SET = 0x10, + HINIC_COMM_CMD_CMDQ_CTXT_GET = 0x11, + + HINIC_COMM_CMD_HWCTXT_SET = 0x12, + HINIC_COMM_CMD_HWCTXT_GET = 0x13, + + HINIC_COMM_CMD_SQ_HI_CI_SET = 0x14, + + HINIC_COMM_CMD_RES_STATE_SET = 0x24, + + HINIC_COMM_CMD_IO_RES_CLEAR = 0x29, + + HINIC_COMM_CMD_MAX = 0x32, +}; + +enum hinic_mgmt_cb_state { + HINIC_MGMT_CB_ENABLED = BIT(0), + HINIC_MGMT_CB_RUNNING = BIT(1), +}; + +struct hinic_recv_msg { + u8 *msg; + u8 *buf_out; + + struct completion recv_done; + + u16 cmd; + enum hinic_mod_type mod; + int async_mgmt_to_pf; + + u16 msg_len; + u16 msg_id; +}; + +struct hinic_mgmt_cb { + void (*cb)(void *handle, u8 cmd, + void *buf_in, u16 in_size, + void *buf_out, u16 *out_size); + + void *handle; + unsigned long state; +}; + +struct hinic_pf_to_mgmt { + struct hinic_hwif *hwif; + + struct semaphore sync_msg_lock; + u16 sync_msg_id; + u8 *sync_msg_buf; + + struct hinic_recv_msg recv_resp_msg_from_mgmt; + struct hinic_recv_msg recv_msg_from_mgmt; + + struct hinic_api_cmd_chain *cmd_chain[HINIC_API_CMD_MAX]; + + struct hinic_mgmt_cb mgmt_cb[HINIC_MOD_MAX]; +}; + +void hinic_register_mgmt_msg_cb(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod, + void *handle, + void (*callback)(void *handle, + u8 cmd, void *buf_in, + u16 in_size, void *buf_out, + u16 *out_size)); + +void hinic_unregister_mgmt_msg_cb(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod); + +int hinic_msg_to_mgmt(struct hinic_pf_to_mgmt *pf_to_mgmt, + enum hinic_mod_type mod, u8 cmd, + void *buf_in, u16 in_size, void *buf_out, u16 *out_size, + enum hinic_mgmt_msg_type sync); + +int hinic_pf_to_mgmt_init(struct hinic_pf_to_mgmt *pf_to_mgmt, + struct hinic_hwif *hwif); + +void hinic_pf_to_mgmt_free(struct hinic_pf_to_mgmt *pf_to_mgmt); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c new file mode 100644 index 000000000000..b9db6d649743 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.c @@ -0,0 +1,887 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/vmalloc.h> +#include <linux/errno.h> +#include <linux/sizes.h> +#include <linux/atomic.h> +#include <linux/skbuff.h> +#include <linux/io.h> +#include <asm/barrier.h> +#include <asm/byteorder.h> + +#include "hinic_common.h" +#include "hinic_hw_if.h" +#include "hinic_hw_wqe.h" +#include "hinic_hw_wq.h" +#include "hinic_hw_qp_ctxt.h" +#include "hinic_hw_qp.h" +#include "hinic_hw_io.h" + +#define SQ_DB_OFF SZ_2K + +/* The number of cache line to prefetch Until threshold state */ +#define WQ_PREFETCH_MAX 2 +/* The number of cache line to prefetch After threshold state */ +#define WQ_PREFETCH_MIN 1 +/* Threshold state */ +#define WQ_PREFETCH_THRESHOLD 256 + +/* sizes of the SQ/RQ ctxt */ +#define Q_CTXT_SIZE 48 +#define CTXT_RSVD 240 + +#define SQ_CTXT_OFFSET(max_sqs, max_rqs, q_id) \ + (((max_rqs) + (max_sqs)) * CTXT_RSVD + (q_id) * Q_CTXT_SIZE) + +#define RQ_CTXT_OFFSET(max_sqs, max_rqs, q_id) \ + (((max_rqs) + (max_sqs)) * CTXT_RSVD + \ + (max_sqs + (q_id)) * Q_CTXT_SIZE) + +#define SIZE_16BYTES(size) (ALIGN(size, 16) >> 4) +#define SIZE_8BYTES(size) (ALIGN(size, 8) >> 3) +#define SECT_SIZE_FROM_8BYTES(size) ((size) << 3) + +#define SQ_DB_PI_HI_SHIFT 8 +#define SQ_DB_PI_HI(prod_idx) ((prod_idx) >> SQ_DB_PI_HI_SHIFT) + +#define SQ_DB_PI_LOW_MASK 0xFF +#define SQ_DB_PI_LOW(prod_idx) ((prod_idx) & SQ_DB_PI_LOW_MASK) + +#define SQ_DB_ADDR(sq, pi) ((u64 *)((sq)->db_base) + SQ_DB_PI_LOW(pi)) + +#define SQ_MASKED_IDX(sq, idx) ((idx) & (sq)->wq->mask) +#define RQ_MASKED_IDX(rq, idx) ((idx) & (rq)->wq->mask) + +#define TX_MAX_MSS_DEFAULT 0x3E00 + +enum sq_wqe_type { + SQ_NORMAL_WQE = 0, +}; + +enum rq_completion_fmt { + RQ_COMPLETE_SGE = 1 +}; + +void hinic_qp_prepare_header(struct hinic_qp_ctxt_header *qp_ctxt_hdr, + enum hinic_qp_ctxt_type ctxt_type, + u16 num_queues, u16 max_queues) +{ + u16 max_sqs = max_queues; + u16 max_rqs = max_queues; + + qp_ctxt_hdr->num_queues = num_queues; + qp_ctxt_hdr->queue_type = ctxt_type; + + if (ctxt_type == HINIC_QP_CTXT_TYPE_SQ) + qp_ctxt_hdr->addr_offset = SQ_CTXT_OFFSET(max_sqs, max_rqs, 0); + else + qp_ctxt_hdr->addr_offset = RQ_CTXT_OFFSET(max_sqs, max_rqs, 0); + + qp_ctxt_hdr->addr_offset = SIZE_16BYTES(qp_ctxt_hdr->addr_offset); + + hinic_cpu_to_be32(qp_ctxt_hdr, sizeof(*qp_ctxt_hdr)); +} + +void hinic_sq_prepare_ctxt(struct hinic_sq_ctxt *sq_ctxt, + struct hinic_sq *sq, u16 global_qid) +{ + u32 wq_page_pfn_hi, wq_page_pfn_lo, wq_block_pfn_hi, wq_block_pfn_lo; + u64 wq_page_addr, wq_page_pfn, wq_block_pfn; + u16 pi_start, ci_start; + struct hinic_wq *wq; + + wq = sq->wq; + ci_start = atomic_read(&wq->cons_idx); + pi_start = atomic_read(&wq->prod_idx); + + /* Read the first page paddr from the WQ page paddr ptrs */ + wq_page_addr = be64_to_cpu(*wq->block_vaddr); + + wq_page_pfn = HINIC_WQ_PAGE_PFN(wq_page_addr); + wq_page_pfn_hi = upper_32_bits(wq_page_pfn); + wq_page_pfn_lo = lower_32_bits(wq_page_pfn); + + wq_block_pfn = HINIC_WQ_BLOCK_PFN(wq->block_paddr); + wq_block_pfn_hi = upper_32_bits(wq_block_pfn); + wq_block_pfn_lo = lower_32_bits(wq_block_pfn); + + sq_ctxt->ceq_attr = HINIC_SQ_CTXT_CEQ_ATTR_SET(global_qid, + GLOBAL_SQ_ID) | + HINIC_SQ_CTXT_CEQ_ATTR_SET(0, EN); + + sq_ctxt->ci_wrapped = HINIC_SQ_CTXT_CI_SET(ci_start, IDX) | + HINIC_SQ_CTXT_CI_SET(1, WRAPPED); + + sq_ctxt->wq_hi_pfn_pi = + HINIC_SQ_CTXT_WQ_PAGE_SET(wq_page_pfn_hi, HI_PFN) | + HINIC_SQ_CTXT_WQ_PAGE_SET(pi_start, PI); + + sq_ctxt->wq_lo_pfn = wq_page_pfn_lo; + + sq_ctxt->pref_cache = + HINIC_SQ_CTXT_PREF_SET(WQ_PREFETCH_MIN, CACHE_MIN) | + HINIC_SQ_CTXT_PREF_SET(WQ_PREFETCH_MAX, CACHE_MAX) | + HINIC_SQ_CTXT_PREF_SET(WQ_PREFETCH_THRESHOLD, CACHE_THRESHOLD); + + sq_ctxt->pref_wrapped = 1; + + sq_ctxt->pref_wq_hi_pfn_ci = + HINIC_SQ_CTXT_PREF_SET(ci_start, CI) | + HINIC_SQ_CTXT_PREF_SET(wq_page_pfn_hi, WQ_HI_PFN); + + sq_ctxt->pref_wq_lo_pfn = wq_page_pfn_lo; + + sq_ctxt->wq_block_hi_pfn = + HINIC_SQ_CTXT_WQ_BLOCK_SET(wq_block_pfn_hi, HI_PFN); + + sq_ctxt->wq_block_lo_pfn = wq_block_pfn_lo; + + hinic_cpu_to_be32(sq_ctxt, sizeof(*sq_ctxt)); +} + +void hinic_rq_prepare_ctxt(struct hinic_rq_ctxt *rq_ctxt, + struct hinic_rq *rq, u16 global_qid) +{ + u32 wq_page_pfn_hi, wq_page_pfn_lo, wq_block_pfn_hi, wq_block_pfn_lo; + u64 wq_page_addr, wq_page_pfn, wq_block_pfn; + u16 pi_start, ci_start; + struct hinic_wq *wq; + + wq = rq->wq; + ci_start = atomic_read(&wq->cons_idx); + pi_start = atomic_read(&wq->prod_idx); + + /* Read the first page paddr from the WQ page paddr ptrs */ + wq_page_addr = be64_to_cpu(*wq->block_vaddr); + + wq_page_pfn = HINIC_WQ_PAGE_PFN(wq_page_addr); + wq_page_pfn_hi = upper_32_bits(wq_page_pfn); + wq_page_pfn_lo = lower_32_bits(wq_page_pfn); + + wq_block_pfn = HINIC_WQ_BLOCK_PFN(wq->block_paddr); + wq_block_pfn_hi = upper_32_bits(wq_block_pfn); + wq_block_pfn_lo = lower_32_bits(wq_block_pfn); + + rq_ctxt->ceq_attr = HINIC_RQ_CTXT_CEQ_ATTR_SET(0, EN) | + HINIC_RQ_CTXT_CEQ_ATTR_SET(1, WRAPPED); + + rq_ctxt->pi_intr_attr = HINIC_RQ_CTXT_PI_SET(pi_start, IDX) | + HINIC_RQ_CTXT_PI_SET(rq->msix_entry, INTR); + + rq_ctxt->wq_hi_pfn_ci = HINIC_RQ_CTXT_WQ_PAGE_SET(wq_page_pfn_hi, + HI_PFN) | + HINIC_RQ_CTXT_WQ_PAGE_SET(ci_start, CI); + + rq_ctxt->wq_lo_pfn = wq_page_pfn_lo; + + rq_ctxt->pref_cache = + HINIC_RQ_CTXT_PREF_SET(WQ_PREFETCH_MIN, CACHE_MIN) | + HINIC_RQ_CTXT_PREF_SET(WQ_PREFETCH_MAX, CACHE_MAX) | + HINIC_RQ_CTXT_PREF_SET(WQ_PREFETCH_THRESHOLD, CACHE_THRESHOLD); + + rq_ctxt->pref_wrapped = 1; + + rq_ctxt->pref_wq_hi_pfn_ci = + HINIC_RQ_CTXT_PREF_SET(wq_page_pfn_hi, WQ_HI_PFN) | + HINIC_RQ_CTXT_PREF_SET(ci_start, CI); + + rq_ctxt->pref_wq_lo_pfn = wq_page_pfn_lo; + + rq_ctxt->pi_paddr_hi = upper_32_bits(rq->pi_dma_addr); + rq_ctxt->pi_paddr_lo = lower_32_bits(rq->pi_dma_addr); + + rq_ctxt->wq_block_hi_pfn = + HINIC_RQ_CTXT_WQ_BLOCK_SET(wq_block_pfn_hi, HI_PFN); + + rq_ctxt->wq_block_lo_pfn = wq_block_pfn_lo; + + hinic_cpu_to_be32(rq_ctxt, sizeof(*rq_ctxt)); +} + +/** + * alloc_sq_skb_arr - allocate sq array for saved skb + * @sq: HW Send Queue + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_sq_skb_arr(struct hinic_sq *sq) +{ + struct hinic_wq *wq = sq->wq; + size_t skb_arr_size; + + skb_arr_size = wq->q_depth * sizeof(*sq->saved_skb); + sq->saved_skb = vzalloc(skb_arr_size); + if (!sq->saved_skb) + return -ENOMEM; + + return 0; +} + +/** + * free_sq_skb_arr - free sq array for saved skb + * @sq: HW Send Queue + **/ +static void free_sq_skb_arr(struct hinic_sq *sq) +{ + vfree(sq->saved_skb); +} + +/** + * alloc_rq_skb_arr - allocate rq array for saved skb + * @rq: HW Receive Queue + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_rq_skb_arr(struct hinic_rq *rq) +{ + struct hinic_wq *wq = rq->wq; + size_t skb_arr_size; + + skb_arr_size = wq->q_depth * sizeof(*rq->saved_skb); + rq->saved_skb = vzalloc(skb_arr_size); + if (!rq->saved_skb) + return -ENOMEM; + + return 0; +} + +/** + * free_rq_skb_arr - free rq array for saved skb + * @rq: HW Receive Queue + **/ +static void free_rq_skb_arr(struct hinic_rq *rq) +{ + vfree(rq->saved_skb); +} + +/** + * hinic_init_sq - Initialize HW Send Queue + * @sq: HW Send Queue + * @hwif: HW Interface for accessing HW + * @wq: Work Queue for the data of the SQ + * @entry: msix entry for sq + * @ci_addr: address for reading the current HW consumer index + * @ci_dma_addr: dma address for reading the current HW consumer index + * @db_base: doorbell base address + * + * Return 0 - Success, negative - Failure + **/ +int hinic_init_sq(struct hinic_sq *sq, struct hinic_hwif *hwif, + struct hinic_wq *wq, struct msix_entry *entry, + void *ci_addr, dma_addr_t ci_dma_addr, + void __iomem *db_base) +{ + sq->hwif = hwif; + + sq->wq = wq; + + sq->irq = entry->vector; + sq->msix_entry = entry->entry; + + sq->hw_ci_addr = ci_addr; + sq->hw_ci_dma_addr = ci_dma_addr; + + sq->db_base = db_base + SQ_DB_OFF; + + return alloc_sq_skb_arr(sq); +} + +/** + * hinic_clean_sq - Clean HW Send Queue's Resources + * @sq: Send Queue + **/ +void hinic_clean_sq(struct hinic_sq *sq) +{ + free_sq_skb_arr(sq); +} + +/** + * alloc_rq_cqe - allocate rq completion queue elements + * @rq: HW Receive Queue + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_rq_cqe(struct hinic_rq *rq) +{ + struct hinic_hwif *hwif = rq->hwif; + struct pci_dev *pdev = hwif->pdev; + size_t cqe_dma_size, cqe_size; + struct hinic_wq *wq = rq->wq; + int j, i; + + cqe_size = wq->q_depth * sizeof(*rq->cqe); + rq->cqe = vzalloc(cqe_size); + if (!rq->cqe) + return -ENOMEM; + + cqe_dma_size = wq->q_depth * sizeof(*rq->cqe_dma); + rq->cqe_dma = vzalloc(cqe_dma_size); + if (!rq->cqe_dma) + goto err_cqe_dma_arr_alloc; + + for (i = 0; i < wq->q_depth; i++) { + rq->cqe[i] = dma_zalloc_coherent(&pdev->dev, + sizeof(*rq->cqe[i]), + &rq->cqe_dma[i], GFP_KERNEL); + if (!rq->cqe[i]) + goto err_cqe_alloc; + } + + return 0; + +err_cqe_alloc: + for (j = 0; j < i; j++) + dma_free_coherent(&pdev->dev, sizeof(*rq->cqe[j]), rq->cqe[j], + rq->cqe_dma[j]); + + vfree(rq->cqe_dma); + +err_cqe_dma_arr_alloc: + vfree(rq->cqe); + return -ENOMEM; +} + +/** + * free_rq_cqe - free rq completion queue elements + * @rq: HW Receive Queue + **/ +static void free_rq_cqe(struct hinic_rq *rq) +{ + struct hinic_hwif *hwif = rq->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_wq *wq = rq->wq; + int i; + + for (i = 0; i < wq->q_depth; i++) + dma_free_coherent(&pdev->dev, sizeof(*rq->cqe[i]), rq->cqe[i], + rq->cqe_dma[i]); + + vfree(rq->cqe_dma); + vfree(rq->cqe); +} + +/** + * hinic_init_rq - Initialize HW Receive Queue + * @rq: HW Receive Queue + * @hwif: HW Interface for accessing HW + * @wq: Work Queue for the data of the RQ + * @entry: msix entry for rq + * + * Return 0 - Success, negative - Failure + **/ +int hinic_init_rq(struct hinic_rq *rq, struct hinic_hwif *hwif, + struct hinic_wq *wq, struct msix_entry *entry) +{ + struct pci_dev *pdev = hwif->pdev; + size_t pi_size; + int err; + + rq->hwif = hwif; + + rq->wq = wq; + + rq->irq = entry->vector; + rq->msix_entry = entry->entry; + + rq->buf_sz = HINIC_RX_BUF_SZ; + + err = alloc_rq_skb_arr(rq); + if (err) { + dev_err(&pdev->dev, "Failed to allocate rq priv data\n"); + return err; + } + + err = alloc_rq_cqe(rq); + if (err) { + dev_err(&pdev->dev, "Failed to allocate rq cqe\n"); + goto err_alloc_rq_cqe; + } + + /* HW requirements: Must be at least 32 bit */ + pi_size = ALIGN(sizeof(*rq->pi_virt_addr), sizeof(u32)); + rq->pi_virt_addr = dma_zalloc_coherent(&pdev->dev, pi_size, + &rq->pi_dma_addr, GFP_KERNEL); + if (!rq->pi_virt_addr) { + dev_err(&pdev->dev, "Failed to allocate PI address\n"); + err = -ENOMEM; + goto err_pi_virt; + } + + return 0; + +err_pi_virt: + free_rq_cqe(rq); + +err_alloc_rq_cqe: + free_rq_skb_arr(rq); + return err; +} + +/** + * hinic_clean_rq - Clean HW Receive Queue's Resources + * @rq: HW Receive Queue + **/ +void hinic_clean_rq(struct hinic_rq *rq) +{ + struct hinic_hwif *hwif = rq->hwif; + struct pci_dev *pdev = hwif->pdev; + size_t pi_size; + + pi_size = ALIGN(sizeof(*rq->pi_virt_addr), sizeof(u32)); + dma_free_coherent(&pdev->dev, pi_size, rq->pi_virt_addr, + rq->pi_dma_addr); + + free_rq_cqe(rq); + free_rq_skb_arr(rq); +} + +/** + * hinic_get_sq_free_wqebbs - return number of free wqebbs for use + * @sq: send queue + * + * Return number of free wqebbs + **/ +int hinic_get_sq_free_wqebbs(struct hinic_sq *sq) +{ + struct hinic_wq *wq = sq->wq; + + return atomic_read(&wq->delta) - 1; +} + +/** + * hinic_get_rq_free_wqebbs - return number of free wqebbs for use + * @rq: recv queue + * + * Return number of free wqebbs + **/ +int hinic_get_rq_free_wqebbs(struct hinic_rq *rq) +{ + struct hinic_wq *wq = rq->wq; + + return atomic_read(&wq->delta) - 1; +} + +static void sq_prepare_ctrl(struct hinic_sq_ctrl *ctrl, u16 prod_idx, + int nr_descs) +{ + u32 ctrl_size, task_size, bufdesc_size; + + ctrl_size = SIZE_8BYTES(sizeof(struct hinic_sq_ctrl)); + task_size = SIZE_8BYTES(sizeof(struct hinic_sq_task)); + bufdesc_size = nr_descs * sizeof(struct hinic_sq_bufdesc); + bufdesc_size = SIZE_8BYTES(bufdesc_size); + + ctrl->ctrl_info = HINIC_SQ_CTRL_SET(bufdesc_size, BUFDESC_SECT_LEN) | + HINIC_SQ_CTRL_SET(task_size, TASKSECT_LEN) | + HINIC_SQ_CTRL_SET(SQ_NORMAL_WQE, DATA_FORMAT) | + HINIC_SQ_CTRL_SET(ctrl_size, LEN); + + ctrl->queue_info = HINIC_SQ_CTRL_SET(TX_MAX_MSS_DEFAULT, + QUEUE_INFO_MSS); +} + +static void sq_prepare_task(struct hinic_sq_task *task) +{ + task->pkt_info0 = + HINIC_SQ_TASK_INFO0_SET(0, L2HDR_LEN) | + HINIC_SQ_TASK_INFO0_SET(HINIC_L4_OFF_DISABLE, L4_OFFLOAD) | + HINIC_SQ_TASK_INFO0_SET(HINIC_OUTER_L3TYPE_UNKNOWN, + INNER_L3TYPE) | + HINIC_SQ_TASK_INFO0_SET(HINIC_VLAN_OFF_DISABLE, + VLAN_OFFLOAD) | + HINIC_SQ_TASK_INFO0_SET(HINIC_PKT_NOT_PARSED, PARSE_FLAG); + + task->pkt_info1 = + HINIC_SQ_TASK_INFO1_SET(HINIC_MEDIA_UNKNOWN, MEDIA_TYPE) | + HINIC_SQ_TASK_INFO1_SET(0, INNER_L4_LEN) | + HINIC_SQ_TASK_INFO1_SET(0, INNER_L3_LEN); + + task->pkt_info2 = + HINIC_SQ_TASK_INFO2_SET(0, TUNNEL_L4_LEN) | + HINIC_SQ_TASK_INFO2_SET(0, OUTER_L3_LEN) | + HINIC_SQ_TASK_INFO2_SET(HINIC_TUNNEL_L4TYPE_UNKNOWN, + TUNNEL_L4TYPE) | + HINIC_SQ_TASK_INFO2_SET(HINIC_OUTER_L3TYPE_UNKNOWN, + OUTER_L3TYPE); + + task->ufo_v6_identify = 0; + + task->pkt_info4 = HINIC_SQ_TASK_INFO4_SET(HINIC_L2TYPE_ETH, L2TYPE); + + task->zero_pad = 0; +} + +/** + * hinic_sq_prepare_wqe - prepare wqe before insert to the queue + * @sq: send queue + * @prod_idx: pi value + * @sq_wqe: wqe to prepare + * @sges: sges for use by the wqe for send for buf addresses + * @nr_sges: number of sges + **/ +void hinic_sq_prepare_wqe(struct hinic_sq *sq, u16 prod_idx, + struct hinic_sq_wqe *sq_wqe, struct hinic_sge *sges, + int nr_sges) +{ + int i; + + sq_prepare_ctrl(&sq_wqe->ctrl, prod_idx, nr_sges); + + sq_prepare_task(&sq_wqe->task); + + for (i = 0; i < nr_sges; i++) + sq_wqe->buf_descs[i].sge = sges[i]; +} + +/** + * sq_prepare_db - prepare doorbell to write + * @sq: send queue + * @prod_idx: pi value for the doorbell + * @cos: cos of the doorbell + * + * Return db value + **/ +static u32 sq_prepare_db(struct hinic_sq *sq, u16 prod_idx, unsigned int cos) +{ + struct hinic_qp *qp = container_of(sq, struct hinic_qp, sq); + u8 hi_prod_idx = SQ_DB_PI_HI(SQ_MASKED_IDX(sq, prod_idx)); + + /* Data should be written to HW in Big Endian Format */ + return cpu_to_be32(HINIC_SQ_DB_INFO_SET(hi_prod_idx, PI_HI) | + HINIC_SQ_DB_INFO_SET(HINIC_DB_SQ_TYPE, TYPE) | + HINIC_SQ_DB_INFO_SET(HINIC_DATA_PATH, PATH) | + HINIC_SQ_DB_INFO_SET(cos, COS) | + HINIC_SQ_DB_INFO_SET(qp->q_id, QID)); +} + +/** + * hinic_sq_write_db- write doorbell + * @sq: send queue + * @prod_idx: pi value for the doorbell + * @wqe_size: wqe size + * @cos: cos of the wqe + **/ +void hinic_sq_write_db(struct hinic_sq *sq, u16 prod_idx, unsigned int wqe_size, + unsigned int cos) +{ + struct hinic_wq *wq = sq->wq; + + /* increment prod_idx to the next */ + prod_idx += ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size; + + wmb(); /* Write all before the doorbell */ + + writel(sq_prepare_db(sq, prod_idx, cos), SQ_DB_ADDR(sq, prod_idx)); +} + +/** + * hinic_sq_get_wqe - get wqe ptr in the current pi and update the pi + * @sq: sq to get wqe from + * @wqe_size: wqe size + * @prod_idx: returned pi + * + * Return wqe pointer + **/ +struct hinic_sq_wqe *hinic_sq_get_wqe(struct hinic_sq *sq, + unsigned int wqe_size, u16 *prod_idx) +{ + struct hinic_hw_wqe *hw_wqe = hinic_get_wqe(sq->wq, wqe_size, + prod_idx); + + if (IS_ERR(hw_wqe)) + return NULL; + + return &hw_wqe->sq_wqe; +} + +/** + * hinic_sq_write_wqe - write the wqe to the sq + * @sq: send queue + * @prod_idx: pi of the wqe + * @sq_wqe: the wqe to write + * @skb: skb to save + * @wqe_size: the size of the wqe + **/ +void hinic_sq_write_wqe(struct hinic_sq *sq, u16 prod_idx, + struct hinic_sq_wqe *sq_wqe, + struct sk_buff *skb, unsigned int wqe_size) +{ + struct hinic_hw_wqe *hw_wqe = (struct hinic_hw_wqe *)sq_wqe; + + sq->saved_skb[prod_idx] = skb; + + /* The data in the HW should be in Big Endian Format */ + hinic_cpu_to_be32(sq_wqe, wqe_size); + + hinic_write_wqe(sq->wq, hw_wqe, wqe_size); +} + +/** + * hinic_sq_read_wqe - read wqe ptr in the current ci and update the ci + * @sq: send queue + * @skb: return skb that was saved + * @wqe_size: the size of the wqe + * @cons_idx: consumer index of the wqe + * + * Return wqe in ci position + **/ +struct hinic_sq_wqe *hinic_sq_read_wqe(struct hinic_sq *sq, + struct sk_buff **skb, + unsigned int *wqe_size, u16 *cons_idx) +{ + struct hinic_hw_wqe *hw_wqe; + struct hinic_sq_wqe *sq_wqe; + struct hinic_sq_ctrl *ctrl; + unsigned int buf_sect_len; + u32 ctrl_info; + + /* read the ctrl section for getting wqe size */ + hw_wqe = hinic_read_wqe(sq->wq, sizeof(*ctrl), cons_idx); + if (IS_ERR(hw_wqe)) + return NULL; + + sq_wqe = &hw_wqe->sq_wqe; + ctrl = &sq_wqe->ctrl; + ctrl_info = be32_to_cpu(ctrl->ctrl_info); + buf_sect_len = HINIC_SQ_CTRL_GET(ctrl_info, BUFDESC_SECT_LEN); + + *wqe_size = sizeof(*ctrl) + sizeof(sq_wqe->task); + *wqe_size += SECT_SIZE_FROM_8BYTES(buf_sect_len); + + *skb = sq->saved_skb[*cons_idx]; + + /* using the real wqe size to read wqe again */ + hw_wqe = hinic_read_wqe(sq->wq, *wqe_size, cons_idx); + + return &hw_wqe->sq_wqe; +} + +/** + * hinic_sq_put_wqe - release the ci for new wqes + * @sq: send queue + * @wqe_size: the size of the wqe + **/ +void hinic_sq_put_wqe(struct hinic_sq *sq, unsigned int wqe_size) +{ + hinic_put_wqe(sq->wq, wqe_size); +} + +/** + * hinic_sq_get_sges - get sges from the wqe + * @sq_wqe: wqe to get the sges from its buffer addresses + * @sges: returned sges + * @nr_sges: number sges to return + **/ +void hinic_sq_get_sges(struct hinic_sq_wqe *sq_wqe, struct hinic_sge *sges, + int nr_sges) +{ + int i; + + for (i = 0; i < nr_sges && i < HINIC_MAX_SQ_BUFDESCS; i++) { + sges[i] = sq_wqe->buf_descs[i].sge; + hinic_be32_to_cpu(&sges[i], sizeof(sges[i])); + } +} + +/** + * hinic_rq_get_wqe - get wqe ptr in the current pi and update the pi + * @rq: rq to get wqe from + * @wqe_size: wqe size + * @prod_idx: returned pi + * + * Return wqe pointer + **/ +struct hinic_rq_wqe *hinic_rq_get_wqe(struct hinic_rq *rq, + unsigned int wqe_size, u16 *prod_idx) +{ + struct hinic_hw_wqe *hw_wqe = hinic_get_wqe(rq->wq, wqe_size, + prod_idx); + + if (IS_ERR(hw_wqe)) + return NULL; + + return &hw_wqe->rq_wqe; +} + +/** + * hinic_rq_write_wqe - write the wqe to the rq + * @rq: recv queue + * @prod_idx: pi of the wqe + * @rq_wqe: the wqe to write + * @skb: skb to save + **/ +void hinic_rq_write_wqe(struct hinic_rq *rq, u16 prod_idx, + struct hinic_rq_wqe *rq_wqe, struct sk_buff *skb) +{ + struct hinic_hw_wqe *hw_wqe = (struct hinic_hw_wqe *)rq_wqe; + + rq->saved_skb[prod_idx] = skb; + + /* The data in the HW should be in Big Endian Format */ + hinic_cpu_to_be32(rq_wqe, sizeof(*rq_wqe)); + + hinic_write_wqe(rq->wq, hw_wqe, sizeof(*rq_wqe)); +} + +/** + * hinic_rq_read_wqe - read wqe ptr in the current ci and update the ci + * @rq: recv queue + * @wqe_size: the size of the wqe + * @skb: return saved skb + * @cons_idx: consumer index of the wqe + * + * Return wqe in ci position + **/ +struct hinic_rq_wqe *hinic_rq_read_wqe(struct hinic_rq *rq, + unsigned int wqe_size, + struct sk_buff **skb, u16 *cons_idx) +{ + struct hinic_hw_wqe *hw_wqe; + struct hinic_rq_cqe *cqe; + int rx_done; + u32 status; + + hw_wqe = hinic_read_wqe(rq->wq, wqe_size, cons_idx); + if (IS_ERR(hw_wqe)) + return NULL; + + cqe = rq->cqe[*cons_idx]; + + status = be32_to_cpu(cqe->status); + + rx_done = HINIC_RQ_CQE_STATUS_GET(status, RXDONE); + if (!rx_done) + return NULL; + + *skb = rq->saved_skb[*cons_idx]; + + return &hw_wqe->rq_wqe; +} + +/** + * hinic_rq_read_next_wqe - increment ci and read the wqe in ci position + * @rq: recv queue + * @wqe_size: the size of the wqe + * @skb: return saved skb + * @cons_idx: consumer index in the wq + * + * Return wqe in incremented ci position + **/ +struct hinic_rq_wqe *hinic_rq_read_next_wqe(struct hinic_rq *rq, + unsigned int wqe_size, + struct sk_buff **skb, + u16 *cons_idx) +{ + struct hinic_wq *wq = rq->wq; + struct hinic_hw_wqe *hw_wqe; + unsigned int num_wqebbs; + + wqe_size = ALIGN(wqe_size, wq->wqebb_size); + num_wqebbs = wqe_size / wq->wqebb_size; + + *cons_idx = RQ_MASKED_IDX(rq, *cons_idx + num_wqebbs); + + *skb = rq->saved_skb[*cons_idx]; + + hw_wqe = hinic_read_wqe_direct(wq, *cons_idx); + + return &hw_wqe->rq_wqe; +} + +/** + * hinic_put_wqe - release the ci for new wqes + * @rq: recv queue + * @cons_idx: consumer index of the wqe + * @wqe_size: the size of the wqe + **/ +void hinic_rq_put_wqe(struct hinic_rq *rq, u16 cons_idx, + unsigned int wqe_size) +{ + struct hinic_rq_cqe *cqe = rq->cqe[cons_idx]; + u32 status = be32_to_cpu(cqe->status); + + status = HINIC_RQ_CQE_STATUS_CLEAR(status, RXDONE); + + /* Rx WQE size is 1 WQEBB, no wq shadow*/ + cqe->status = cpu_to_be32(status); + + wmb(); /* clear done flag */ + + hinic_put_wqe(rq->wq, wqe_size); +} + +/** + * hinic_rq_get_sge - get sge from the wqe + * @rq: recv queue + * @rq_wqe: wqe to get the sge from its buf address + * @cons_idx: consumer index + * @sge: returned sge + **/ +void hinic_rq_get_sge(struct hinic_rq *rq, struct hinic_rq_wqe *rq_wqe, + u16 cons_idx, struct hinic_sge *sge) +{ + struct hinic_rq_cqe *cqe = rq->cqe[cons_idx]; + u32 len = be32_to_cpu(cqe->len); + + sge->hi_addr = be32_to_cpu(rq_wqe->buf_desc.hi_addr); + sge->lo_addr = be32_to_cpu(rq_wqe->buf_desc.lo_addr); + sge->len = HINIC_RQ_CQE_SGE_GET(len, LEN); +} + +/** + * hinic_rq_prepare_wqe - prepare wqe before insert to the queue + * @rq: recv queue + * @prod_idx: pi value + * @rq_wqe: the wqe + * @sge: sge for use by the wqe for recv buf address + **/ +void hinic_rq_prepare_wqe(struct hinic_rq *rq, u16 prod_idx, + struct hinic_rq_wqe *rq_wqe, struct hinic_sge *sge) +{ + struct hinic_rq_cqe_sect *cqe_sect = &rq_wqe->cqe_sect; + struct hinic_rq_bufdesc *buf_desc = &rq_wqe->buf_desc; + struct hinic_rq_cqe *cqe = rq->cqe[prod_idx]; + struct hinic_rq_ctrl *ctrl = &rq_wqe->ctrl; + dma_addr_t cqe_dma = rq->cqe_dma[prod_idx]; + + ctrl->ctrl_info = + HINIC_RQ_CTRL_SET(SIZE_8BYTES(sizeof(*ctrl)), LEN) | + HINIC_RQ_CTRL_SET(SIZE_8BYTES(sizeof(*cqe_sect)), + COMPLETE_LEN) | + HINIC_RQ_CTRL_SET(SIZE_8BYTES(sizeof(*buf_desc)), + BUFDESC_SECT_LEN) | + HINIC_RQ_CTRL_SET(RQ_COMPLETE_SGE, COMPLETE_FORMAT); + + hinic_set_sge(&cqe_sect->sge, cqe_dma, sizeof(*cqe)); + + buf_desc->hi_addr = sge->hi_addr; + buf_desc->lo_addr = sge->lo_addr; +} + +/** + * hinic_rq_update - update pi of the rq + * @rq: recv queue + * @prod_idx: pi value + **/ +void hinic_rq_update(struct hinic_rq *rq, u16 prod_idx) +{ + *rq->pi_virt_addr = cpu_to_be16(RQ_MASKED_IDX(rq, prod_idx + 1)); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h new file mode 100644 index 000000000000..df729a1587e9 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp.h @@ -0,0 +1,201 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_QP_H +#define HINIC_HW_QP_H + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/sizes.h> +#include <linux/pci.h> +#include <linux/skbuff.h> + +#include "hinic_common.h" +#include "hinic_hw_if.h" +#include "hinic_hw_wqe.h" +#include "hinic_hw_wq.h" +#include "hinic_hw_qp_ctxt.h" + +#define HINIC_SQ_DB_INFO_PI_HI_SHIFT 0 +#define HINIC_SQ_DB_INFO_QID_SHIFT 8 +#define HINIC_SQ_DB_INFO_PATH_SHIFT 23 +#define HINIC_SQ_DB_INFO_COS_SHIFT 24 +#define HINIC_SQ_DB_INFO_TYPE_SHIFT 27 + +#define HINIC_SQ_DB_INFO_PI_HI_MASK 0xFF +#define HINIC_SQ_DB_INFO_QID_MASK 0x3FF +#define HINIC_SQ_DB_INFO_PATH_MASK 0x1 +#define HINIC_SQ_DB_INFO_COS_MASK 0x7 +#define HINIC_SQ_DB_INFO_TYPE_MASK 0x1F + +#define HINIC_SQ_DB_INFO_SET(val, member) \ + (((u32)(val) & HINIC_SQ_DB_INFO_##member##_MASK) \ + << HINIC_SQ_DB_INFO_##member##_SHIFT) + +#define HINIC_SQ_WQEBB_SIZE 64 +#define HINIC_RQ_WQEBB_SIZE 32 + +#define HINIC_SQ_PAGE_SIZE SZ_4K +#define HINIC_RQ_PAGE_SIZE SZ_4K + +#define HINIC_SQ_DEPTH SZ_4K +#define HINIC_RQ_DEPTH SZ_4K + +/* In any change to HINIC_RX_BUF_SZ, HINIC_RX_BUF_SZ_IDX must be changed */ +#define HINIC_RX_BUF_SZ 2048 +#define HINIC_RX_BUF_SZ_IDX HINIC_RX_BUF_SZ_2048_IDX + +#define HINIC_MIN_TX_WQE_SIZE(wq) \ + ALIGN(HINIC_SQ_WQE_SIZE(1), (wq)->wqebb_size) + +#define HINIC_MIN_TX_NUM_WQEBBS(sq) \ + (HINIC_MIN_TX_WQE_SIZE((sq)->wq) / (sq)->wq->wqebb_size) + +enum hinic_rx_buf_sz_idx { + HINIC_RX_BUF_SZ_32_IDX, + HINIC_RX_BUF_SZ_64_IDX, + HINIC_RX_BUF_SZ_96_IDX, + HINIC_RX_BUF_SZ_128_IDX, + HINIC_RX_BUF_SZ_192_IDX, + HINIC_RX_BUF_SZ_256_IDX, + HINIC_RX_BUF_SZ_384_IDX, + HINIC_RX_BUF_SZ_512_IDX, + HINIC_RX_BUF_SZ_768_IDX, + HINIC_RX_BUF_SZ_1024_IDX, + HINIC_RX_BUF_SZ_1536_IDX, + HINIC_RX_BUF_SZ_2048_IDX, + HINIC_RX_BUF_SZ_3072_IDX, + HINIC_RX_BUF_SZ_4096_IDX, + HINIC_RX_BUF_SZ_8192_IDX, + HINIC_RX_BUF_SZ_16384_IDX, +}; + +struct hinic_sq { + struct hinic_hwif *hwif; + + struct hinic_wq *wq; + + u32 irq; + u16 msix_entry; + + void *hw_ci_addr; + dma_addr_t hw_ci_dma_addr; + + void __iomem *db_base; + + struct sk_buff **saved_skb; +}; + +struct hinic_rq { + struct hinic_hwif *hwif; + + struct hinic_wq *wq; + + u32 irq; + u16 msix_entry; + + size_t buf_sz; + + struct sk_buff **saved_skb; + + struct hinic_rq_cqe **cqe; + dma_addr_t *cqe_dma; + + u16 *pi_virt_addr; + dma_addr_t pi_dma_addr; +}; + +struct hinic_qp { + struct hinic_sq sq; + struct hinic_rq rq; + + u16 q_id; +}; + +void hinic_qp_prepare_header(struct hinic_qp_ctxt_header *qp_ctxt_hdr, + enum hinic_qp_ctxt_type ctxt_type, + u16 num_queues, u16 max_queues); + +void hinic_sq_prepare_ctxt(struct hinic_sq_ctxt *sq_ctxt, + struct hinic_sq *sq, u16 global_qid); + +void hinic_rq_prepare_ctxt(struct hinic_rq_ctxt *rq_ctxt, + struct hinic_rq *rq, u16 global_qid); + +int hinic_init_sq(struct hinic_sq *sq, struct hinic_hwif *hwif, + struct hinic_wq *wq, struct msix_entry *entry, void *ci_addr, + dma_addr_t ci_dma_addr, void __iomem *db_base); + +void hinic_clean_sq(struct hinic_sq *sq); + +int hinic_init_rq(struct hinic_rq *rq, struct hinic_hwif *hwif, + struct hinic_wq *wq, struct msix_entry *entry); + +void hinic_clean_rq(struct hinic_rq *rq); + +int hinic_get_sq_free_wqebbs(struct hinic_sq *sq); + +int hinic_get_rq_free_wqebbs(struct hinic_rq *rq); + +void hinic_sq_prepare_wqe(struct hinic_sq *sq, u16 prod_idx, + struct hinic_sq_wqe *wqe, struct hinic_sge *sges, + int nr_sges); + +void hinic_sq_write_db(struct hinic_sq *sq, u16 prod_idx, unsigned int wqe_size, + unsigned int cos); + +struct hinic_sq_wqe *hinic_sq_get_wqe(struct hinic_sq *sq, + unsigned int wqe_size, u16 *prod_idx); + +void hinic_sq_write_wqe(struct hinic_sq *sq, u16 prod_idx, + struct hinic_sq_wqe *wqe, struct sk_buff *skb, + unsigned int wqe_size); + +struct hinic_sq_wqe *hinic_sq_read_wqe(struct hinic_sq *sq, + struct sk_buff **skb, + unsigned int *wqe_size, u16 *cons_idx); + +void hinic_sq_put_wqe(struct hinic_sq *sq, unsigned int wqe_size); + +void hinic_sq_get_sges(struct hinic_sq_wqe *wqe, struct hinic_sge *sges, + int nr_sges); + +struct hinic_rq_wqe *hinic_rq_get_wqe(struct hinic_rq *rq, + unsigned int wqe_size, u16 *prod_idx); + +void hinic_rq_write_wqe(struct hinic_rq *rq, u16 prod_idx, + struct hinic_rq_wqe *wqe, struct sk_buff *skb); + +struct hinic_rq_wqe *hinic_rq_read_wqe(struct hinic_rq *rq, + unsigned int wqe_size, + struct sk_buff **skb, u16 *cons_idx); + +struct hinic_rq_wqe *hinic_rq_read_next_wqe(struct hinic_rq *rq, + unsigned int wqe_size, + struct sk_buff **skb, + u16 *cons_idx); + +void hinic_rq_put_wqe(struct hinic_rq *rq, u16 cons_idx, + unsigned int wqe_size); + +void hinic_rq_get_sge(struct hinic_rq *rq, struct hinic_rq_wqe *wqe, + u16 cons_idx, struct hinic_sge *sge); + +void hinic_rq_prepare_wqe(struct hinic_rq *rq, u16 prod_idx, + struct hinic_rq_wqe *wqe, struct hinic_sge *sge); + +void hinic_rq_update(struct hinic_rq *rq, u16 prod_idx); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h new file mode 100644 index 000000000000..376abf00762b --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_qp_ctxt.h @@ -0,0 +1,214 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_QP_CTXT_H +#define HINIC_HW_QP_CTXT_H + +#include <linux/types.h> + +#include "hinic_hw_cmdq.h" + +#define HINIC_SQ_CTXT_CEQ_ATTR_GLOBAL_SQ_ID_SHIFT 13 +#define HINIC_SQ_CTXT_CEQ_ATTR_EN_SHIFT 23 + +#define HINIC_SQ_CTXT_CEQ_ATTR_GLOBAL_SQ_ID_MASK 0x3FF +#define HINIC_SQ_CTXT_CEQ_ATTR_EN_MASK 0x1 + +#define HINIC_SQ_CTXT_CEQ_ATTR_SET(val, member) \ + (((u32)(val) & HINIC_SQ_CTXT_CEQ_ATTR_##member##_MASK) \ + << HINIC_SQ_CTXT_CEQ_ATTR_##member##_SHIFT) + +#define HINIC_SQ_CTXT_CI_IDX_SHIFT 11 +#define HINIC_SQ_CTXT_CI_WRAPPED_SHIFT 23 + +#define HINIC_SQ_CTXT_CI_IDX_MASK 0xFFF +#define HINIC_SQ_CTXT_CI_WRAPPED_MASK 0x1 + +#define HINIC_SQ_CTXT_CI_SET(val, member) \ + (((u32)(val) & HINIC_SQ_CTXT_CI_##member##_MASK) \ + << HINIC_SQ_CTXT_CI_##member##_SHIFT) + +#define HINIC_SQ_CTXT_WQ_PAGE_HI_PFN_SHIFT 0 +#define HINIC_SQ_CTXT_WQ_PAGE_PI_SHIFT 20 + +#define HINIC_SQ_CTXT_WQ_PAGE_HI_PFN_MASK 0xFFFFF +#define HINIC_SQ_CTXT_WQ_PAGE_PI_MASK 0xFFF + +#define HINIC_SQ_CTXT_WQ_PAGE_SET(val, member) \ + (((u32)(val) & HINIC_SQ_CTXT_WQ_PAGE_##member##_MASK) \ + << HINIC_SQ_CTXT_WQ_PAGE_##member##_SHIFT) + +#define HINIC_SQ_CTXT_PREF_CACHE_THRESHOLD_SHIFT 0 +#define HINIC_SQ_CTXT_PREF_CACHE_MAX_SHIFT 14 +#define HINIC_SQ_CTXT_PREF_CACHE_MIN_SHIFT 25 + +#define HINIC_SQ_CTXT_PREF_CACHE_THRESHOLD_MASK 0x3FFF +#define HINIC_SQ_CTXT_PREF_CACHE_MAX_MASK 0x7FF +#define HINIC_SQ_CTXT_PREF_CACHE_MIN_MASK 0x7F + +#define HINIC_SQ_CTXT_PREF_WQ_HI_PFN_SHIFT 0 +#define HINIC_SQ_CTXT_PREF_CI_SHIFT 20 + +#define HINIC_SQ_CTXT_PREF_WQ_HI_PFN_MASK 0xFFFFF +#define HINIC_SQ_CTXT_PREF_CI_MASK 0xFFF + +#define HINIC_SQ_CTXT_PREF_SET(val, member) \ + (((u32)(val) & HINIC_SQ_CTXT_PREF_##member##_MASK) \ + << HINIC_SQ_CTXT_PREF_##member##_SHIFT) + +#define HINIC_SQ_CTXT_WQ_BLOCK_HI_PFN_SHIFT 0 + +#define HINIC_SQ_CTXT_WQ_BLOCK_HI_PFN_MASK 0x7FFFFF + +#define HINIC_SQ_CTXT_WQ_BLOCK_SET(val, member) \ + (((u32)(val) & HINIC_SQ_CTXT_WQ_BLOCK_##member##_MASK) \ + << HINIC_SQ_CTXT_WQ_BLOCK_##member##_SHIFT) + +#define HINIC_RQ_CTXT_CEQ_ATTR_EN_SHIFT 0 +#define HINIC_RQ_CTXT_CEQ_ATTR_WRAPPED_SHIFT 1 + +#define HINIC_RQ_CTXT_CEQ_ATTR_EN_MASK 0x1 +#define HINIC_RQ_CTXT_CEQ_ATTR_WRAPPED_MASK 0x1 + +#define HINIC_RQ_CTXT_CEQ_ATTR_SET(val, member) \ + (((u32)(val) & HINIC_RQ_CTXT_CEQ_ATTR_##member##_MASK) \ + << HINIC_RQ_CTXT_CEQ_ATTR_##member##_SHIFT) + +#define HINIC_RQ_CTXT_PI_IDX_SHIFT 0 +#define HINIC_RQ_CTXT_PI_INTR_SHIFT 22 + +#define HINIC_RQ_CTXT_PI_IDX_MASK 0xFFF +#define HINIC_RQ_CTXT_PI_INTR_MASK 0x3FF + +#define HINIC_RQ_CTXT_PI_SET(val, member) \ + (((u32)(val) & HINIC_RQ_CTXT_PI_##member##_MASK) << \ + HINIC_RQ_CTXT_PI_##member##_SHIFT) + +#define HINIC_RQ_CTXT_WQ_PAGE_HI_PFN_SHIFT 0 +#define HINIC_RQ_CTXT_WQ_PAGE_CI_SHIFT 20 + +#define HINIC_RQ_CTXT_WQ_PAGE_HI_PFN_MASK 0xFFFFF +#define HINIC_RQ_CTXT_WQ_PAGE_CI_MASK 0xFFF + +#define HINIC_RQ_CTXT_WQ_PAGE_SET(val, member) \ + (((u32)(val) & HINIC_RQ_CTXT_WQ_PAGE_##member##_MASK) << \ + HINIC_RQ_CTXT_WQ_PAGE_##member##_SHIFT) + +#define HINIC_RQ_CTXT_PREF_CACHE_THRESHOLD_SHIFT 0 +#define HINIC_RQ_CTXT_PREF_CACHE_MAX_SHIFT 14 +#define HINIC_RQ_CTXT_PREF_CACHE_MIN_SHIFT 25 + +#define HINIC_RQ_CTXT_PREF_CACHE_THRESHOLD_MASK 0x3FFF +#define HINIC_RQ_CTXT_PREF_CACHE_MAX_MASK 0x7FF +#define HINIC_RQ_CTXT_PREF_CACHE_MIN_MASK 0x7F + +#define HINIC_RQ_CTXT_PREF_WQ_HI_PFN_SHIFT 0 +#define HINIC_RQ_CTXT_PREF_CI_SHIFT 20 + +#define HINIC_RQ_CTXT_PREF_WQ_HI_PFN_MASK 0xFFFFF +#define HINIC_RQ_CTXT_PREF_CI_MASK 0xFFF + +#define HINIC_RQ_CTXT_PREF_SET(val, member) \ + (((u32)(val) & HINIC_RQ_CTXT_PREF_##member##_MASK) << \ + HINIC_RQ_CTXT_PREF_##member##_SHIFT) + +#define HINIC_RQ_CTXT_WQ_BLOCK_HI_PFN_SHIFT 0 + +#define HINIC_RQ_CTXT_WQ_BLOCK_HI_PFN_MASK 0x7FFFFF + +#define HINIC_RQ_CTXT_WQ_BLOCK_SET(val, member) \ + (((u32)(val) & HINIC_RQ_CTXT_WQ_BLOCK_##member##_MASK) << \ + HINIC_RQ_CTXT_WQ_BLOCK_##member##_SHIFT) + +#define HINIC_SQ_CTXT_SIZE(num_sqs) (sizeof(struct hinic_qp_ctxt_header) \ + + (num_sqs) * sizeof(struct hinic_sq_ctxt)) + +#define HINIC_RQ_CTXT_SIZE(num_rqs) (sizeof(struct hinic_qp_ctxt_header) \ + + (num_rqs) * sizeof(struct hinic_rq_ctxt)) + +#define HINIC_WQ_PAGE_PFN_SHIFT 12 +#define HINIC_WQ_BLOCK_PFN_SHIFT 9 + +#define HINIC_WQ_PAGE_PFN(page_addr) ((page_addr) >> HINIC_WQ_PAGE_PFN_SHIFT) +#define HINIC_WQ_BLOCK_PFN(page_addr) ((page_addr) >> \ + HINIC_WQ_BLOCK_PFN_SHIFT) + +#define HINIC_Q_CTXT_MAX \ + ((HINIC_CMDQ_BUF_SIZE - sizeof(struct hinic_qp_ctxt_header)) \ + / sizeof(struct hinic_sq_ctxt)) + +enum hinic_qp_ctxt_type { + HINIC_QP_CTXT_TYPE_SQ, + HINIC_QP_CTXT_TYPE_RQ +}; + +struct hinic_qp_ctxt_header { + u16 num_queues; + u16 queue_type; + u32 addr_offset; +}; + +struct hinic_sq_ctxt { + u32 ceq_attr; + + u32 ci_wrapped; + + u32 wq_hi_pfn_pi; + u32 wq_lo_pfn; + + u32 pref_cache; + u32 pref_wrapped; + u32 pref_wq_hi_pfn_ci; + u32 pref_wq_lo_pfn; + + u32 rsvd0; + u32 rsvd1; + + u32 wq_block_hi_pfn; + u32 wq_block_lo_pfn; +}; + +struct hinic_rq_ctxt { + u32 ceq_attr; + + u32 pi_intr_attr; + + u32 wq_hi_pfn_ci; + u32 wq_lo_pfn; + + u32 pref_cache; + u32 pref_wrapped; + + u32 pref_wq_hi_pfn_ci; + u32 pref_wq_lo_pfn; + + u32 pi_paddr_hi; + u32 pi_paddr_lo; + + u32 wq_block_hi_pfn; + u32 wq_block_lo_pfn; +}; + +struct hinic_sq_ctxt_block { + struct hinic_qp_ctxt_header hdr; + struct hinic_sq_ctxt sq_ctxt[HINIC_Q_CTXT_MAX]; +}; + +struct hinic_rq_ctxt_block { + struct hinic_qp_ctxt_header hdr; + struct hinic_rq_ctxt rq_ctxt[HINIC_Q_CTXT_MAX]; +}; + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c new file mode 100644 index 000000000000..3e3181c089bd --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.c @@ -0,0 +1,878 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/atomic.h> +#include <linux/semaphore.h> +#include <linux/errno.h> +#include <linux/vmalloc.h> +#include <linux/err.h> +#include <asm/byteorder.h> + +#include "hinic_hw_if.h" +#include "hinic_hw_wqe.h" +#include "hinic_hw_wq.h" +#include "hinic_hw_cmdq.h" + +#define WQS_BLOCKS_PER_PAGE 4 + +#define WQ_BLOCK_SIZE 4096 +#define WQS_PAGE_SIZE (WQS_BLOCKS_PER_PAGE * WQ_BLOCK_SIZE) + +#define WQS_MAX_NUM_BLOCKS 128 +#define WQS_FREE_BLOCKS_SIZE(wqs) (WQS_MAX_NUM_BLOCKS * \ + sizeof((wqs)->free_blocks[0])) + +#define WQ_SIZE(wq) ((wq)->q_depth * (wq)->wqebb_size) + +#define WQ_PAGE_ADDR_SIZE sizeof(u64) +#define WQ_MAX_PAGES (WQ_BLOCK_SIZE / WQ_PAGE_ADDR_SIZE) + +#define CMDQ_BLOCK_SIZE 512 +#define CMDQ_PAGE_SIZE 4096 + +#define CMDQ_WQ_MAX_PAGES (CMDQ_BLOCK_SIZE / WQ_PAGE_ADDR_SIZE) + +#define WQ_BASE_VADDR(wqs, wq) \ + ((void *)((wqs)->page_vaddr[(wq)->page_idx]) \ + + (wq)->block_idx * WQ_BLOCK_SIZE) + +#define WQ_BASE_PADDR(wqs, wq) \ + ((wqs)->page_paddr[(wq)->page_idx] \ + + (wq)->block_idx * WQ_BLOCK_SIZE) + +#define WQ_BASE_ADDR(wqs, wq) \ + ((void *)((wqs)->shadow_page_vaddr[(wq)->page_idx]) \ + + (wq)->block_idx * WQ_BLOCK_SIZE) + +#define CMDQ_BASE_VADDR(cmdq_pages, wq) \ + ((void *)((cmdq_pages)->page_vaddr) \ + + (wq)->block_idx * CMDQ_BLOCK_SIZE) + +#define CMDQ_BASE_PADDR(cmdq_pages, wq) \ + ((cmdq_pages)->page_paddr \ + + (wq)->block_idx * CMDQ_BLOCK_SIZE) + +#define CMDQ_BASE_ADDR(cmdq_pages, wq) \ + ((void *)((cmdq_pages)->shadow_page_vaddr) \ + + (wq)->block_idx * CMDQ_BLOCK_SIZE) + +#define WQE_PAGE_OFF(wq, idx) (((idx) & ((wq)->num_wqebbs_per_page - 1)) * \ + (wq)->wqebb_size) + +#define WQE_PAGE_NUM(wq, idx) (((idx) / ((wq)->num_wqebbs_per_page)) \ + & ((wq)->num_q_pages - 1)) + +#define WQ_PAGE_ADDR(wq, idx) \ + ((wq)->shadow_block_vaddr[WQE_PAGE_NUM(wq, idx)]) + +#define MASKED_WQE_IDX(wq, idx) ((idx) & (wq)->mask) + +#define WQE_IN_RANGE(wqe, start, end) \ + (((unsigned long)(wqe) >= (unsigned long)(start)) && \ + ((unsigned long)(wqe) < (unsigned long)(end))) + +#define WQE_SHADOW_PAGE(wq, wqe) \ + (((unsigned long)(wqe) - (unsigned long)(wq)->shadow_wqe) \ + / (wq)->max_wqe_size) + +/** + * queue_alloc_page - allocate page for Queue + * @hwif: HW interface for allocating DMA + * @vaddr: virtual address will be returned in this address + * @paddr: physical address will be returned in this address + * @shadow_vaddr: VM area will be return here for holding WQ page addresses + * @page_sz: page size of each WQ page + * + * Return 0 - Success, negative - Failure + **/ +static int queue_alloc_page(struct hinic_hwif *hwif, u64 **vaddr, u64 *paddr, + void ***shadow_vaddr, size_t page_sz) +{ + struct pci_dev *pdev = hwif->pdev; + dma_addr_t dma_addr; + + *vaddr = dma_zalloc_coherent(&pdev->dev, page_sz, &dma_addr, + GFP_KERNEL); + if (!*vaddr) { + dev_err(&pdev->dev, "Failed to allocate dma for wqs page\n"); + return -ENOMEM; + } + + *paddr = (u64)dma_addr; + + /* use vzalloc for big mem */ + *shadow_vaddr = vzalloc(page_sz); + if (!*shadow_vaddr) + goto err_shadow_vaddr; + + return 0; + +err_shadow_vaddr: + dma_free_coherent(&pdev->dev, page_sz, *vaddr, dma_addr); + return -ENOMEM; +} + +/** + * wqs_allocate_page - allocate page for WQ set + * @wqs: Work Queue Set + * @page_idx: the page index of the page will be allocated + * + * Return 0 - Success, negative - Failure + **/ +static int wqs_allocate_page(struct hinic_wqs *wqs, int page_idx) +{ + return queue_alloc_page(wqs->hwif, &wqs->page_vaddr[page_idx], + &wqs->page_paddr[page_idx], + &wqs->shadow_page_vaddr[page_idx], + WQS_PAGE_SIZE); +} + +/** + * wqs_free_page - free page of WQ set + * @wqs: Work Queue Set + * @page_idx: the page index of the page will be freed + **/ +static void wqs_free_page(struct hinic_wqs *wqs, int page_idx) +{ + struct hinic_hwif *hwif = wqs->hwif; + struct pci_dev *pdev = hwif->pdev; + + dma_free_coherent(&pdev->dev, WQS_PAGE_SIZE, + wqs->page_vaddr[page_idx], + (dma_addr_t)wqs->page_paddr[page_idx]); + vfree(wqs->shadow_page_vaddr[page_idx]); +} + +/** + * cmdq_allocate_page - allocate page for cmdq + * @cmdq_pages: the pages of the cmdq queue struct to hold the page + * + * Return 0 - Success, negative - Failure + **/ +static int cmdq_allocate_page(struct hinic_cmdq_pages *cmdq_pages) +{ + return queue_alloc_page(cmdq_pages->hwif, &cmdq_pages->page_vaddr, + &cmdq_pages->page_paddr, + &cmdq_pages->shadow_page_vaddr, + CMDQ_PAGE_SIZE); +} + +/** + * cmdq_free_page - free page from cmdq + * @cmdq_pages: the pages of the cmdq queue struct that hold the page + * + * Return 0 - Success, negative - Failure + **/ +static void cmdq_free_page(struct hinic_cmdq_pages *cmdq_pages) +{ + struct hinic_hwif *hwif = cmdq_pages->hwif; + struct pci_dev *pdev = hwif->pdev; + + dma_free_coherent(&pdev->dev, CMDQ_PAGE_SIZE, + cmdq_pages->page_vaddr, + (dma_addr_t)cmdq_pages->page_paddr); + vfree(cmdq_pages->shadow_page_vaddr); +} + +static int alloc_page_arrays(struct hinic_wqs *wqs) +{ + struct hinic_hwif *hwif = wqs->hwif; + struct pci_dev *pdev = hwif->pdev; + size_t size; + + size = wqs->num_pages * sizeof(*wqs->page_paddr); + wqs->page_paddr = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!wqs->page_paddr) + return -ENOMEM; + + size = wqs->num_pages * sizeof(*wqs->page_vaddr); + wqs->page_vaddr = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!wqs->page_vaddr) + goto err_page_vaddr; + + size = wqs->num_pages * sizeof(*wqs->shadow_page_vaddr); + wqs->shadow_page_vaddr = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!wqs->shadow_page_vaddr) + goto err_page_shadow_vaddr; + + return 0; + +err_page_shadow_vaddr: + devm_kfree(&pdev->dev, wqs->page_vaddr); + +err_page_vaddr: + devm_kfree(&pdev->dev, wqs->page_paddr); + return -ENOMEM; +} + +static void free_page_arrays(struct hinic_wqs *wqs) +{ + struct hinic_hwif *hwif = wqs->hwif; + struct pci_dev *pdev = hwif->pdev; + + devm_kfree(&pdev->dev, wqs->shadow_page_vaddr); + devm_kfree(&pdev->dev, wqs->page_vaddr); + devm_kfree(&pdev->dev, wqs->page_paddr); +} + +static int wqs_next_block(struct hinic_wqs *wqs, int *page_idx, + int *block_idx) +{ + int pos; + + down(&wqs->alloc_blocks_lock); + + wqs->num_free_blks--; + + if (wqs->num_free_blks < 0) { + wqs->num_free_blks++; + up(&wqs->alloc_blocks_lock); + return -ENOMEM; + } + + pos = wqs->alloc_blk_pos++; + pos &= WQS_MAX_NUM_BLOCKS - 1; + + *page_idx = wqs->free_blocks[pos].page_idx; + *block_idx = wqs->free_blocks[pos].block_idx; + + wqs->free_blocks[pos].page_idx = -1; + wqs->free_blocks[pos].block_idx = -1; + + up(&wqs->alloc_blocks_lock); + return 0; +} + +static void wqs_return_block(struct hinic_wqs *wqs, int page_idx, + int block_idx) +{ + int pos; + + down(&wqs->alloc_blocks_lock); + + pos = wqs->return_blk_pos++; + pos &= WQS_MAX_NUM_BLOCKS - 1; + + wqs->free_blocks[pos].page_idx = page_idx; + wqs->free_blocks[pos].block_idx = block_idx; + + wqs->num_free_blks++; + + up(&wqs->alloc_blocks_lock); +} + +static void init_wqs_blocks_arr(struct hinic_wqs *wqs) +{ + int page_idx, blk_idx, pos = 0; + + for (page_idx = 0; page_idx < wqs->num_pages; page_idx++) { + for (blk_idx = 0; blk_idx < WQS_BLOCKS_PER_PAGE; blk_idx++) { + wqs->free_blocks[pos].page_idx = page_idx; + wqs->free_blocks[pos].block_idx = blk_idx; + pos++; + } + } + + wqs->alloc_blk_pos = 0; + wqs->return_blk_pos = pos; + wqs->num_free_blks = pos; + + sema_init(&wqs->alloc_blocks_lock, 1); +} + +/** + * hinic_wqs_alloc - allocate Work Queues set + * @wqs: Work Queue Set + * @max_wqs: maximum wqs to allocate + * @hwif: HW interface for use for the allocation + * + * Return 0 - Success, negative - Failure + **/ +int hinic_wqs_alloc(struct hinic_wqs *wqs, int max_wqs, + struct hinic_hwif *hwif) +{ + struct pci_dev *pdev = hwif->pdev; + int err, i, page_idx; + + max_wqs = ALIGN(max_wqs, WQS_BLOCKS_PER_PAGE); + if (max_wqs > WQS_MAX_NUM_BLOCKS) { + dev_err(&pdev->dev, "Invalid max_wqs = %d\n", max_wqs); + return -EINVAL; + } + + wqs->hwif = hwif; + wqs->num_pages = max_wqs / WQS_BLOCKS_PER_PAGE; + + if (alloc_page_arrays(wqs)) { + dev_err(&pdev->dev, + "Failed to allocate mem for page addresses\n"); + return -ENOMEM; + } + + for (page_idx = 0; page_idx < wqs->num_pages; page_idx++) { + err = wqs_allocate_page(wqs, page_idx); + if (err) { + dev_err(&pdev->dev, "Failed wq page allocation\n"); + goto err_wq_allocate_page; + } + } + + wqs->free_blocks = devm_kzalloc(&pdev->dev, WQS_FREE_BLOCKS_SIZE(wqs), + GFP_KERNEL); + if (!wqs->free_blocks) { + err = -ENOMEM; + goto err_alloc_blocks; + } + + init_wqs_blocks_arr(wqs); + return 0; + +err_alloc_blocks: +err_wq_allocate_page: + for (i = 0; i < page_idx; i++) + wqs_free_page(wqs, i); + + free_page_arrays(wqs); + return err; +} + +/** + * hinic_wqs_free - free Work Queues set + * @wqs: Work Queue Set + **/ +void hinic_wqs_free(struct hinic_wqs *wqs) +{ + struct hinic_hwif *hwif = wqs->hwif; + struct pci_dev *pdev = hwif->pdev; + int page_idx; + + devm_kfree(&pdev->dev, wqs->free_blocks); + + for (page_idx = 0; page_idx < wqs->num_pages; page_idx++) + wqs_free_page(wqs, page_idx); + + free_page_arrays(wqs); +} + +/** + * alloc_wqes_shadow - allocate WQE shadows for WQ + * @wq: WQ to allocate shadows for + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_wqes_shadow(struct hinic_wq *wq) +{ + struct hinic_hwif *hwif = wq->hwif; + struct pci_dev *pdev = hwif->pdev; + size_t size; + + size = wq->num_q_pages * wq->max_wqe_size; + wq->shadow_wqe = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!wq->shadow_wqe) + return -ENOMEM; + + size = wq->num_q_pages * sizeof(wq->prod_idx); + wq->shadow_idx = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); + if (!wq->shadow_idx) + goto err_shadow_idx; + + return 0; + +err_shadow_idx: + devm_kfree(&pdev->dev, wq->shadow_wqe); + return -ENOMEM; +} + +/** + * free_wqes_shadow - free WQE shadows of WQ + * @wq: WQ to free shadows from + **/ +static void free_wqes_shadow(struct hinic_wq *wq) +{ + struct hinic_hwif *hwif = wq->hwif; + struct pci_dev *pdev = hwif->pdev; + + devm_kfree(&pdev->dev, wq->shadow_idx); + devm_kfree(&pdev->dev, wq->shadow_wqe); +} + +/** + * free_wq_pages - free pages of WQ + * @hwif: HW interface for releasing dma addresses + * @wq: WQ to free pages from + * @num_q_pages: number pages to free + **/ +static void free_wq_pages(struct hinic_wq *wq, struct hinic_hwif *hwif, + int num_q_pages) +{ + struct pci_dev *pdev = hwif->pdev; + int i; + + for (i = 0; i < num_q_pages; i++) { + void **vaddr = &wq->shadow_block_vaddr[i]; + u64 *paddr = &wq->block_vaddr[i]; + dma_addr_t dma_addr; + + dma_addr = (dma_addr_t)be64_to_cpu(*paddr); + dma_free_coherent(&pdev->dev, wq->wq_page_size, *vaddr, + dma_addr); + } + + free_wqes_shadow(wq); +} + +/** + * alloc_wq_pages - alloc pages for WQ + * @hwif: HW interface for allocating dma addresses + * @wq: WQ to allocate pages for + * @max_pages: maximum pages allowed + * + * Return 0 - Success, negative - Failure + **/ +static int alloc_wq_pages(struct hinic_wq *wq, struct hinic_hwif *hwif, + int max_pages) +{ + struct pci_dev *pdev = hwif->pdev; + int i, err, num_q_pages; + + num_q_pages = ALIGN(WQ_SIZE(wq), wq->wq_page_size) / wq->wq_page_size; + if (num_q_pages > max_pages) { + dev_err(&pdev->dev, "Number wq pages exceeds the limit\n"); + return -EINVAL; + } + + if (num_q_pages & (num_q_pages - 1)) { + dev_err(&pdev->dev, "Number wq pages must be power of 2\n"); + return -EINVAL; + } + + wq->num_q_pages = num_q_pages; + + err = alloc_wqes_shadow(wq); + if (err) { + dev_err(&pdev->dev, "Failed to allocate wqe shadow\n"); + return err; + } + + for (i = 0; i < num_q_pages; i++) { + void **vaddr = &wq->shadow_block_vaddr[i]; + u64 *paddr = &wq->block_vaddr[i]; + dma_addr_t dma_addr; + + *vaddr = dma_zalloc_coherent(&pdev->dev, wq->wq_page_size, + &dma_addr, GFP_KERNEL); + if (!*vaddr) { + dev_err(&pdev->dev, "Failed to allocate wq page\n"); + goto err_alloc_wq_pages; + } + + /* HW uses Big Endian Format */ + *paddr = cpu_to_be64(dma_addr); + } + + return 0; + +err_alloc_wq_pages: + free_wq_pages(wq, hwif, i); + return -ENOMEM; +} + +/** + * hinic_wq_allocate - Allocate the WQ resources from the WQS + * @wqs: WQ set from which to allocate the WQ resources + * @wq: WQ to allocate resources for it from the WQ set + * @wqebb_size: Work Queue Block Byte Size + * @wq_page_size: the page size in the Work Queue + * @q_depth: number of wqebbs in WQ + * @max_wqe_size: maximum WQE size that will be used in the WQ + * + * Return 0 - Success, negative - Failure + **/ +int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq, + u16 wqebb_size, u16 wq_page_size, u16 q_depth, + u16 max_wqe_size) +{ + struct hinic_hwif *hwif = wqs->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 num_wqebbs_per_page; + int err; + + if (wqebb_size == 0) { + dev_err(&pdev->dev, "wqebb_size must be > 0\n"); + return -EINVAL; + } + + if (wq_page_size == 0) { + dev_err(&pdev->dev, "wq_page_size must be > 0\n"); + return -EINVAL; + } + + if (q_depth & (q_depth - 1)) { + dev_err(&pdev->dev, "WQ q_depth must be power of 2\n"); + return -EINVAL; + } + + num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size) / wqebb_size; + + if (num_wqebbs_per_page & (num_wqebbs_per_page - 1)) { + dev_err(&pdev->dev, "num wqebbs per page must be power of 2\n"); + return -EINVAL; + } + + wq->hwif = hwif; + + err = wqs_next_block(wqs, &wq->page_idx, &wq->block_idx); + if (err) { + dev_err(&pdev->dev, "Failed to get free wqs next block\n"); + return err; + } + + wq->wqebb_size = wqebb_size; + wq->wq_page_size = wq_page_size; + wq->q_depth = q_depth; + wq->max_wqe_size = max_wqe_size; + wq->num_wqebbs_per_page = num_wqebbs_per_page; + + wq->block_vaddr = WQ_BASE_VADDR(wqs, wq); + wq->shadow_block_vaddr = WQ_BASE_ADDR(wqs, wq); + wq->block_paddr = WQ_BASE_PADDR(wqs, wq); + + err = alloc_wq_pages(wq, wqs->hwif, WQ_MAX_PAGES); + if (err) { + dev_err(&pdev->dev, "Failed to allocate wq pages\n"); + goto err_alloc_wq_pages; + } + + atomic_set(&wq->cons_idx, 0); + atomic_set(&wq->prod_idx, 0); + atomic_set(&wq->delta, q_depth); + wq->mask = q_depth - 1; + + return 0; + +err_alloc_wq_pages: + wqs_return_block(wqs, wq->page_idx, wq->block_idx); + return err; +} + +/** + * hinic_wq_free - Free the WQ resources to the WQS + * @wqs: WQ set to free the WQ resources to it + * @wq: WQ to free its resources to the WQ set resources + **/ +void hinic_wq_free(struct hinic_wqs *wqs, struct hinic_wq *wq) +{ + free_wq_pages(wq, wqs->hwif, wq->num_q_pages); + + wqs_return_block(wqs, wq->page_idx, wq->block_idx); +} + +/** + * hinic_wqs_cmdq_alloc - Allocate wqs for cmdqs + * @cmdq_pages: will hold the pages of the cmdq + * @wq: returned wqs + * @hwif: HW interface + * @cmdq_blocks: number of cmdq blocks/wq to allocate + * @wqebb_size: Work Queue Block Byte Size + * @wq_page_size: the page size in the Work Queue + * @q_depth: number of wqebbs in WQ + * @max_wqe_size: maximum WQE size that will be used in the WQ + * + * Return 0 - Success, negative - Failure + **/ +int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages, + struct hinic_wq *wq, struct hinic_hwif *hwif, + int cmdq_blocks, u16 wqebb_size, u16 wq_page_size, + u16 q_depth, u16 max_wqe_size) +{ + struct pci_dev *pdev = hwif->pdev; + u16 num_wqebbs_per_page; + int i, j, err = -ENOMEM; + + if (wqebb_size == 0) { + dev_err(&pdev->dev, "wqebb_size must be > 0\n"); + return -EINVAL; + } + + if (wq_page_size == 0) { + dev_err(&pdev->dev, "wq_page_size must be > 0\n"); + return -EINVAL; + } + + if (q_depth & (q_depth - 1)) { + dev_err(&pdev->dev, "WQ q_depth must be power of 2\n"); + return -EINVAL; + } + + num_wqebbs_per_page = ALIGN(wq_page_size, wqebb_size) / wqebb_size; + + if (num_wqebbs_per_page & (num_wqebbs_per_page - 1)) { + dev_err(&pdev->dev, "num wqebbs per page must be power of 2\n"); + return -EINVAL; + } + + cmdq_pages->hwif = hwif; + + err = cmdq_allocate_page(cmdq_pages); + if (err) { + dev_err(&pdev->dev, "Failed to allocate CMDQ page\n"); + return err; + } + + for (i = 0; i < cmdq_blocks; i++) { + wq[i].hwif = hwif; + wq[i].page_idx = 0; + wq[i].block_idx = i; + + wq[i].wqebb_size = wqebb_size; + wq[i].wq_page_size = wq_page_size; + wq[i].q_depth = q_depth; + wq[i].max_wqe_size = max_wqe_size; + wq[i].num_wqebbs_per_page = num_wqebbs_per_page; + + wq[i].block_vaddr = CMDQ_BASE_VADDR(cmdq_pages, &wq[i]); + wq[i].shadow_block_vaddr = CMDQ_BASE_ADDR(cmdq_pages, &wq[i]); + wq[i].block_paddr = CMDQ_BASE_PADDR(cmdq_pages, &wq[i]); + + err = alloc_wq_pages(&wq[i], cmdq_pages->hwif, + CMDQ_WQ_MAX_PAGES); + if (err) { + dev_err(&pdev->dev, "Failed to alloc CMDQ blocks\n"); + goto err_cmdq_block; + } + + atomic_set(&wq[i].cons_idx, 0); + atomic_set(&wq[i].prod_idx, 0); + atomic_set(&wq[i].delta, q_depth); + wq[i].mask = q_depth - 1; + } + + return 0; + +err_cmdq_block: + for (j = 0; j < i; j++) + free_wq_pages(&wq[j], cmdq_pages->hwif, wq[j].num_q_pages); + + cmdq_free_page(cmdq_pages); + return err; +} + +/** + * hinic_wqs_cmdq_free - Free wqs from cmdqs + * @cmdq_pages: hold the pages of the cmdq + * @wq: wqs to free + * @cmdq_blocks: number of wqs to free + **/ +void hinic_wqs_cmdq_free(struct hinic_cmdq_pages *cmdq_pages, + struct hinic_wq *wq, int cmdq_blocks) +{ + int i; + + for (i = 0; i < cmdq_blocks; i++) + free_wq_pages(&wq[i], cmdq_pages->hwif, wq[i].num_q_pages); + + cmdq_free_page(cmdq_pages); +} + +static void copy_wqe_to_shadow(struct hinic_wq *wq, void *shadow_addr, + int num_wqebbs, u16 idx) +{ + void *wqebb_addr; + int i; + + for (i = 0; i < num_wqebbs; i++, idx++) { + idx = MASKED_WQE_IDX(wq, idx); + wqebb_addr = WQ_PAGE_ADDR(wq, idx) + + WQE_PAGE_OFF(wq, idx); + + memcpy(shadow_addr, wqebb_addr, wq->wqebb_size); + + shadow_addr += wq->wqebb_size; + } +} + +static void copy_wqe_from_shadow(struct hinic_wq *wq, void *shadow_addr, + int num_wqebbs, u16 idx) +{ + void *wqebb_addr; + int i; + + for (i = 0; i < num_wqebbs; i++, idx++) { + idx = MASKED_WQE_IDX(wq, idx); + wqebb_addr = WQ_PAGE_ADDR(wq, idx) + + WQE_PAGE_OFF(wq, idx); + + memcpy(wqebb_addr, shadow_addr, wq->wqebb_size); + shadow_addr += wq->wqebb_size; + } +} + +/** + * hinic_get_wqe - get wqe ptr in the current pi and update the pi + * @wq: wq to get wqe from + * @wqe_size: wqe size + * @prod_idx: returned pi + * + * Return wqe pointer + **/ +struct hinic_hw_wqe *hinic_get_wqe(struct hinic_wq *wq, unsigned int wqe_size, + u16 *prod_idx) +{ + int curr_pg, end_pg, num_wqebbs; + u16 curr_prod_idx, end_prod_idx; + + *prod_idx = MASKED_WQE_IDX(wq, atomic_read(&wq->prod_idx)); + + num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size; + + if (atomic_sub_return(num_wqebbs, &wq->delta) <= 0) { + atomic_add(num_wqebbs, &wq->delta); + return ERR_PTR(-EBUSY); + } + + end_prod_idx = atomic_add_return(num_wqebbs, &wq->prod_idx); + + end_prod_idx = MASKED_WQE_IDX(wq, end_prod_idx); + curr_prod_idx = end_prod_idx - num_wqebbs; + curr_prod_idx = MASKED_WQE_IDX(wq, curr_prod_idx); + + /* end prod index points to the next wqebb, therefore minus 1 */ + end_prod_idx = MASKED_WQE_IDX(wq, end_prod_idx - 1); + + curr_pg = WQE_PAGE_NUM(wq, curr_prod_idx); + end_pg = WQE_PAGE_NUM(wq, end_prod_idx); + + *prod_idx = curr_prod_idx; + + if (curr_pg != end_pg) { + void *shadow_addr = &wq->shadow_wqe[curr_pg * wq->max_wqe_size]; + + copy_wqe_to_shadow(wq, shadow_addr, num_wqebbs, *prod_idx); + + wq->shadow_idx[curr_pg] = *prod_idx; + return shadow_addr; + } + + return WQ_PAGE_ADDR(wq, *prod_idx) + WQE_PAGE_OFF(wq, *prod_idx); +} + +/** + * hinic_put_wqe - return the wqe place to use for a new wqe + * @wq: wq to return wqe + * @wqe_size: wqe size + **/ +void hinic_put_wqe(struct hinic_wq *wq, unsigned int wqe_size) +{ + int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size; + + atomic_add(num_wqebbs, &wq->cons_idx); + + atomic_add(num_wqebbs, &wq->delta); +} + +/** + * hinic_read_wqe - read wqe ptr in the current ci + * @wq: wq to get read from + * @wqe_size: wqe size + * @cons_idx: returned ci + * + * Return wqe pointer + **/ +struct hinic_hw_wqe *hinic_read_wqe(struct hinic_wq *wq, unsigned int wqe_size, + u16 *cons_idx) +{ + int num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size; + u16 curr_cons_idx, end_cons_idx; + int curr_pg, end_pg; + + if ((atomic_read(&wq->delta) + num_wqebbs) > wq->q_depth) + return ERR_PTR(-EBUSY); + + curr_cons_idx = atomic_read(&wq->cons_idx); + + curr_cons_idx = MASKED_WQE_IDX(wq, curr_cons_idx); + end_cons_idx = MASKED_WQE_IDX(wq, curr_cons_idx + num_wqebbs - 1); + + curr_pg = WQE_PAGE_NUM(wq, curr_cons_idx); + end_pg = WQE_PAGE_NUM(wq, end_cons_idx); + + *cons_idx = curr_cons_idx; + + if (curr_pg != end_pg) { + void *shadow_addr = &wq->shadow_wqe[curr_pg * wq->max_wqe_size]; + + copy_wqe_to_shadow(wq, shadow_addr, num_wqebbs, *cons_idx); + return shadow_addr; + } + + return WQ_PAGE_ADDR(wq, *cons_idx) + WQE_PAGE_OFF(wq, *cons_idx); +} + +/** + * hinic_read_wqe_direct - read wqe directly from ci position + * @wq: wq + * @cons_idx: ci position + * + * Return wqe + **/ +struct hinic_hw_wqe *hinic_read_wqe_direct(struct hinic_wq *wq, u16 cons_idx) +{ + return WQ_PAGE_ADDR(wq, cons_idx) + WQE_PAGE_OFF(wq, cons_idx); +} + +/** + * wqe_shadow - check if a wqe is shadow + * @wq: wq of the wqe + * @wqe: the wqe for shadow checking + * + * Return true - shadow, false - Not shadow + **/ +static inline bool wqe_shadow(struct hinic_wq *wq, struct hinic_hw_wqe *wqe) +{ + size_t wqe_shadow_size = wq->num_q_pages * wq->max_wqe_size; + + return WQE_IN_RANGE(wqe, wq->shadow_wqe, + &wq->shadow_wqe[wqe_shadow_size]); +} + +/** + * hinic_write_wqe - write the wqe to the wq + * @wq: wq to write wqe to + * @wqe: wqe to write + * @wqe_size: wqe size + **/ +void hinic_write_wqe(struct hinic_wq *wq, struct hinic_hw_wqe *wqe, + unsigned int wqe_size) +{ + int curr_pg, num_wqebbs; + void *shadow_addr; + u16 prod_idx; + + if (wqe_shadow(wq, wqe)) { + curr_pg = WQE_SHADOW_PAGE(wq, wqe); + + prod_idx = wq->shadow_idx[curr_pg]; + num_wqebbs = ALIGN(wqe_size, wq->wqebb_size) / wq->wqebb_size; + shadow_addr = &wq->shadow_wqe[curr_pg * wq->max_wqe_size]; + + copy_wqe_from_shadow(wq, shadow_addr, num_wqebbs, prod_idx); + } +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h new file mode 100644 index 000000000000..9c030a0f035e --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wq.h @@ -0,0 +1,117 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_WQ_H +#define HINIC_HW_WQ_H + +#include <linux/types.h> +#include <linux/semaphore.h> +#include <linux/atomic.h> + +#include "hinic_hw_if.h" +#include "hinic_hw_wqe.h" + +struct hinic_free_block { + int page_idx; + int block_idx; +}; + +struct hinic_wq { + struct hinic_hwif *hwif; + + int page_idx; + int block_idx; + + u16 wqebb_size; + u16 wq_page_size; + u16 q_depth; + u16 max_wqe_size; + u16 num_wqebbs_per_page; + + /* The addresses are 64 bit in the HW */ + u64 block_paddr; + void **shadow_block_vaddr; + u64 *block_vaddr; + + int num_q_pages; + u8 *shadow_wqe; + u16 *shadow_idx; + + atomic_t cons_idx; + atomic_t prod_idx; + atomic_t delta; + u16 mask; +}; + +struct hinic_wqs { + struct hinic_hwif *hwif; + int num_pages; + + /* The addresses are 64 bit in the HW */ + u64 *page_paddr; + u64 **page_vaddr; + void ***shadow_page_vaddr; + + struct hinic_free_block *free_blocks; + int alloc_blk_pos; + int return_blk_pos; + int num_free_blks; + + /* Lock for getting a free block from the WQ set */ + struct semaphore alloc_blocks_lock; +}; + +struct hinic_cmdq_pages { + /* The addresses are 64 bit in the HW */ + u64 page_paddr; + u64 *page_vaddr; + void **shadow_page_vaddr; + + struct hinic_hwif *hwif; +}; + +int hinic_wqs_cmdq_alloc(struct hinic_cmdq_pages *cmdq_pages, + struct hinic_wq *wq, struct hinic_hwif *hwif, + int cmdq_blocks, u16 wqebb_size, u16 wq_page_size, + u16 q_depth, u16 max_wqe_size); + +void hinic_wqs_cmdq_free(struct hinic_cmdq_pages *cmdq_pages, + struct hinic_wq *wq, int cmdq_blocks); + +int hinic_wqs_alloc(struct hinic_wqs *wqs, int num_wqs, + struct hinic_hwif *hwif); + +void hinic_wqs_free(struct hinic_wqs *wqs); + +int hinic_wq_allocate(struct hinic_wqs *wqs, struct hinic_wq *wq, + u16 wqebb_size, u16 wq_page_size, u16 q_depth, + u16 max_wqe_size); + +void hinic_wq_free(struct hinic_wqs *wqs, struct hinic_wq *wq); + +struct hinic_hw_wqe *hinic_get_wqe(struct hinic_wq *wq, unsigned int wqe_size, + u16 *prod_idx); + +void hinic_put_wqe(struct hinic_wq *wq, unsigned int wqe_size); + +struct hinic_hw_wqe *hinic_read_wqe(struct hinic_wq *wq, unsigned int wqe_size, + u16 *cons_idx); + +struct hinic_hw_wqe *hinic_read_wqe_direct(struct hinic_wq *wq, u16 cons_idx); + +void hinic_write_wqe(struct hinic_wq *wq, struct hinic_hw_wqe *wqe, + unsigned int wqe_size); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h b/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h new file mode 100644 index 000000000000..bc73485483c5 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_hw_wqe.h @@ -0,0 +1,368 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_HW_WQE_H +#define HINIC_HW_WQE_H + +#include "hinic_common.h" + +#define HINIC_CMDQ_CTRL_PI_SHIFT 0 +#define HINIC_CMDQ_CTRL_CMD_SHIFT 16 +#define HINIC_CMDQ_CTRL_MOD_SHIFT 24 +#define HINIC_CMDQ_CTRL_ACK_TYPE_SHIFT 29 +#define HINIC_CMDQ_CTRL_HW_BUSY_BIT_SHIFT 31 + +#define HINIC_CMDQ_CTRL_PI_MASK 0xFFFF +#define HINIC_CMDQ_CTRL_CMD_MASK 0xFF +#define HINIC_CMDQ_CTRL_MOD_MASK 0x1F +#define HINIC_CMDQ_CTRL_ACK_TYPE_MASK 0x3 +#define HINIC_CMDQ_CTRL_HW_BUSY_BIT_MASK 0x1 + +#define HINIC_CMDQ_CTRL_SET(val, member) \ + (((u32)(val) & HINIC_CMDQ_CTRL_##member##_MASK) \ + << HINIC_CMDQ_CTRL_##member##_SHIFT) + +#define HINIC_CMDQ_CTRL_GET(val, member) \ + (((val) >> HINIC_CMDQ_CTRL_##member##_SHIFT) \ + & HINIC_CMDQ_CTRL_##member##_MASK) + +#define HINIC_CMDQ_WQE_HEADER_BUFDESC_LEN_SHIFT 0 +#define HINIC_CMDQ_WQE_HEADER_COMPLETE_FMT_SHIFT 15 +#define HINIC_CMDQ_WQE_HEADER_DATA_FMT_SHIFT 22 +#define HINIC_CMDQ_WQE_HEADER_COMPLETE_REQ_SHIFT 23 +#define HINIC_CMDQ_WQE_HEADER_COMPLETE_SECT_LEN_SHIFT 27 +#define HINIC_CMDQ_WQE_HEADER_CTRL_LEN_SHIFT 29 +#define HINIC_CMDQ_WQE_HEADER_TOGGLED_WRAPPED_SHIFT 31 + +#define HINIC_CMDQ_WQE_HEADER_BUFDESC_LEN_MASK 0xFF +#define HINIC_CMDQ_WQE_HEADER_COMPLETE_FMT_MASK 0x1 +#define HINIC_CMDQ_WQE_HEADER_DATA_FMT_MASK 0x1 +#define HINIC_CMDQ_WQE_HEADER_COMPLETE_REQ_MASK 0x1 +#define HINIC_CMDQ_WQE_HEADER_COMPLETE_SECT_LEN_MASK 0x3 +#define HINIC_CMDQ_WQE_HEADER_CTRL_LEN_MASK 0x3 +#define HINIC_CMDQ_WQE_HEADER_TOGGLED_WRAPPED_MASK 0x1 + +#define HINIC_CMDQ_WQE_HEADER_SET(val, member) \ + (((u32)(val) & HINIC_CMDQ_WQE_HEADER_##member##_MASK) \ + << HINIC_CMDQ_WQE_HEADER_##member##_SHIFT) + +#define HINIC_CMDQ_WQE_HEADER_GET(val, member) \ + (((val) >> HINIC_CMDQ_WQE_HEADER_##member##_SHIFT) \ + & HINIC_CMDQ_WQE_HEADER_##member##_MASK) + +#define HINIC_SQ_CTRL_BUFDESC_SECT_LEN_SHIFT 0 +#define HINIC_SQ_CTRL_TASKSECT_LEN_SHIFT 16 +#define HINIC_SQ_CTRL_DATA_FORMAT_SHIFT 22 +#define HINIC_SQ_CTRL_LEN_SHIFT 29 + +#define HINIC_SQ_CTRL_BUFDESC_SECT_LEN_MASK 0xFF +#define HINIC_SQ_CTRL_TASKSECT_LEN_MASK 0x1F +#define HINIC_SQ_CTRL_DATA_FORMAT_MASK 0x1 +#define HINIC_SQ_CTRL_LEN_MASK 0x3 + +#define HINIC_SQ_CTRL_QUEUE_INFO_MSS_SHIFT 13 + +#define HINIC_SQ_CTRL_QUEUE_INFO_MSS_MASK 0x3FFF + +#define HINIC_SQ_CTRL_SET(val, member) \ + (((u32)(val) & HINIC_SQ_CTRL_##member##_MASK) \ + << HINIC_SQ_CTRL_##member##_SHIFT) + +#define HINIC_SQ_CTRL_GET(val, member) \ + (((val) >> HINIC_SQ_CTRL_##member##_SHIFT) \ + & HINIC_SQ_CTRL_##member##_MASK) + +#define HINIC_SQ_TASK_INFO0_L2HDR_LEN_SHIFT 0 +#define HINIC_SQ_TASK_INFO0_L4_OFFLOAD_SHIFT 8 +#define HINIC_SQ_TASK_INFO0_INNER_L3TYPE_SHIFT 10 +#define HINIC_SQ_TASK_INFO0_VLAN_OFFLOAD_SHIFT 12 +#define HINIC_SQ_TASK_INFO0_PARSE_FLAG_SHIFT 13 +/* 1 bit reserved */ +#define HINIC_SQ_TASK_INFO0_TSO_FLAG_SHIFT 15 +#define HINIC_SQ_TASK_INFO0_VLAN_TAG_SHIFT 16 + +#define HINIC_SQ_TASK_INFO0_L2HDR_LEN_MASK 0xFF +#define HINIC_SQ_TASK_INFO0_L4_OFFLOAD_MASK 0x3 +#define HINIC_SQ_TASK_INFO0_INNER_L3TYPE_MASK 0x3 +#define HINIC_SQ_TASK_INFO0_VLAN_OFFLOAD_MASK 0x1 +#define HINIC_SQ_TASK_INFO0_PARSE_FLAG_MASK 0x1 +/* 1 bit reserved */ +#define HINIC_SQ_TASK_INFO0_TSO_FLAG_MASK 0x1 +#define HINIC_SQ_TASK_INFO0_VLAN_TAG_MASK 0xFFFF + +#define HINIC_SQ_TASK_INFO0_SET(val, member) \ + (((u32)(val) & HINIC_SQ_TASK_INFO0_##member##_MASK) << \ + HINIC_SQ_TASK_INFO0_##member##_SHIFT) + +/* 8 bits reserved */ +#define HINIC_SQ_TASK_INFO1_MEDIA_TYPE_SHIFT 8 +#define HINIC_SQ_TASK_INFO1_INNER_L4_LEN_SHIFT 16 +#define HINIC_SQ_TASK_INFO1_INNER_L3_LEN_SHIFT 24 + +/* 8 bits reserved */ +#define HINIC_SQ_TASK_INFO1_MEDIA_TYPE_MASK 0xFF +#define HINIC_SQ_TASK_INFO1_INNER_L4_LEN_MASK 0xFF +#define HINIC_SQ_TASK_INFO1_INNER_L3_LEN_MASK 0xFF + +#define HINIC_SQ_TASK_INFO1_SET(val, member) \ + (((u32)(val) & HINIC_SQ_TASK_INFO1_##member##_MASK) << \ + HINIC_SQ_TASK_INFO1_##member##_SHIFT) + +#define HINIC_SQ_TASK_INFO2_TUNNEL_L4_LEN_SHIFT 0 +#define HINIC_SQ_TASK_INFO2_OUTER_L3_LEN_SHIFT 12 +#define HINIC_SQ_TASK_INFO2_TUNNEL_L4TYPE_SHIFT 19 +/* 1 bit reserved */ +#define HINIC_SQ_TASK_INFO2_OUTER_L3TYPE_SHIFT 22 +/* 8 bits reserved */ + +#define HINIC_SQ_TASK_INFO2_TUNNEL_L4_LEN_MASK 0xFFF +#define HINIC_SQ_TASK_INFO2_OUTER_L3_LEN_MASK 0x7F +#define HINIC_SQ_TASK_INFO2_TUNNEL_L4TYPE_MASK 0x3 +/* 1 bit reserved */ +#define HINIC_SQ_TASK_INFO2_OUTER_L3TYPE_MASK 0x3 +/* 8 bits reserved */ + +#define HINIC_SQ_TASK_INFO2_SET(val, member) \ + (((u32)(val) & HINIC_SQ_TASK_INFO2_##member##_MASK) << \ + HINIC_SQ_TASK_INFO2_##member##_SHIFT) + +/* 31 bits reserved */ +#define HINIC_SQ_TASK_INFO4_L2TYPE_SHIFT 31 + +/* 31 bits reserved */ +#define HINIC_SQ_TASK_INFO4_L2TYPE_MASK 0x1 + +#define HINIC_SQ_TASK_INFO4_SET(val, member) \ + (((u32)(val) & HINIC_SQ_TASK_INFO4_##member##_MASK) << \ + HINIC_SQ_TASK_INFO4_##member##_SHIFT) + +#define HINIC_RQ_CQE_STATUS_RXDONE_SHIFT 31 + +#define HINIC_RQ_CQE_STATUS_RXDONE_MASK 0x1 + +#define HINIC_RQ_CQE_STATUS_GET(val, member) \ + (((val) >> HINIC_RQ_CQE_STATUS_##member##_SHIFT) & \ + HINIC_RQ_CQE_STATUS_##member##_MASK) + +#define HINIC_RQ_CQE_STATUS_CLEAR(val, member) \ + ((val) & (~(HINIC_RQ_CQE_STATUS_##member##_MASK << \ + HINIC_RQ_CQE_STATUS_##member##_SHIFT))) + +#define HINIC_RQ_CQE_SGE_LEN_SHIFT 16 + +#define HINIC_RQ_CQE_SGE_LEN_MASK 0xFFFF + +#define HINIC_RQ_CQE_SGE_GET(val, member) \ + (((val) >> HINIC_RQ_CQE_SGE_##member##_SHIFT) & \ + HINIC_RQ_CQE_SGE_##member##_MASK) + +#define HINIC_RQ_CTRL_BUFDESC_SECT_LEN_SHIFT 0 +#define HINIC_RQ_CTRL_COMPLETE_FORMAT_SHIFT 15 +#define HINIC_RQ_CTRL_COMPLETE_LEN_SHIFT 27 +#define HINIC_RQ_CTRL_LEN_SHIFT 29 + +#define HINIC_RQ_CTRL_BUFDESC_SECT_LEN_MASK 0xFF +#define HINIC_RQ_CTRL_COMPLETE_FORMAT_MASK 0x1 +#define HINIC_RQ_CTRL_COMPLETE_LEN_MASK 0x3 +#define HINIC_RQ_CTRL_LEN_MASK 0x3 + +#define HINIC_RQ_CTRL_SET(val, member) \ + (((u32)(val) & HINIC_RQ_CTRL_##member##_MASK) << \ + HINIC_RQ_CTRL_##member##_SHIFT) + +#define HINIC_SQ_WQE_SIZE(nr_sges) \ + (sizeof(struct hinic_sq_ctrl) + \ + sizeof(struct hinic_sq_task) + \ + (nr_sges) * sizeof(struct hinic_sq_bufdesc)) + +#define HINIC_SCMD_DATA_LEN 16 + +#define HINIC_MAX_SQ_BUFDESCS 17 + +#define HINIC_SQ_WQE_MAX_SIZE 320 +#define HINIC_RQ_WQE_SIZE 32 + +enum hinic_l4offload_type { + HINIC_L4_OFF_DISABLE = 0, + HINIC_TCP_OFFLOAD_ENABLE = 1, + HINIC_SCTP_OFFLOAD_ENABLE = 2, + HINIC_UDP_OFFLOAD_ENABLE = 3, +}; + +enum hinic_vlan_offload { + HINIC_VLAN_OFF_DISABLE = 0, + HINIC_VLAN_OFF_ENABLE = 1, +}; + +enum hinic_pkt_parsed { + HINIC_PKT_NOT_PARSED = 0, + HINIC_PKT_PARSED = 1, +}; + +enum hinic_outer_l3type { + HINIC_OUTER_L3TYPE_UNKNOWN = 0, + HINIC_OUTER_L3TYPE_IPV6 = 1, + HINIC_OUTER_L3TYPE_IPV4_NO_CHKSUM = 2, + HINIC_OUTER_L3TYPE_IPV4_CHKSUM = 3, +}; + +enum hinic_media_type { + HINIC_MEDIA_UNKNOWN = 0, +}; + +enum hinic_l2type { + HINIC_L2TYPE_ETH = 0, +}; + +enum hinc_tunnel_l4type { + HINIC_TUNNEL_L4TYPE_UNKNOWN = 0, +}; + +struct hinic_cmdq_header { + u32 header_info; + u32 saved_data; +}; + +struct hinic_status { + u32 status_info; +}; + +struct hinic_ctrl { + u32 ctrl_info; +}; + +struct hinic_sge_resp { + struct hinic_sge sge; + u32 rsvd; +}; + +struct hinic_cmdq_completion { + /* HW Format */ + union { + struct hinic_sge_resp sge_resp; + u64 direct_resp; + }; +}; + +struct hinic_scmd_bufdesc { + u32 buf_len; + u32 rsvd; + u8 data[HINIC_SCMD_DATA_LEN]; +}; + +struct hinic_lcmd_bufdesc { + struct hinic_sge sge; + u32 rsvd1; + u64 rsvd2; + u64 rsvd3; +}; + +struct hinic_cmdq_wqe_scmd { + struct hinic_cmdq_header header; + u64 rsvd; + struct hinic_status status; + struct hinic_ctrl ctrl; + struct hinic_cmdq_completion completion; + struct hinic_scmd_bufdesc buf_desc; +}; + +struct hinic_cmdq_wqe_lcmd { + struct hinic_cmdq_header header; + struct hinic_status status; + struct hinic_ctrl ctrl; + struct hinic_cmdq_completion completion; + struct hinic_lcmd_bufdesc buf_desc; +}; + +struct hinic_cmdq_direct_wqe { + struct hinic_cmdq_wqe_scmd wqe_scmd; +}; + +struct hinic_cmdq_wqe { + /* HW Format */ + union { + struct hinic_cmdq_direct_wqe direct_wqe; + struct hinic_cmdq_wqe_lcmd wqe_lcmd; + }; +}; + +struct hinic_sq_ctrl { + u32 ctrl_info; + u32 queue_info; +}; + +struct hinic_sq_task { + u32 pkt_info0; + u32 pkt_info1; + u32 pkt_info2; + u32 ufo_v6_identify; + u32 pkt_info4; + u32 zero_pad; +}; + +struct hinic_sq_bufdesc { + struct hinic_sge sge; + u32 rsvd; +}; + +struct hinic_sq_wqe { + struct hinic_sq_ctrl ctrl; + struct hinic_sq_task task; + struct hinic_sq_bufdesc buf_descs[HINIC_MAX_SQ_BUFDESCS]; +}; + +struct hinic_rq_cqe { + u32 status; + u32 len; + + u32 rsvd2; + u32 rsvd3; + u32 rsvd4; + u32 rsvd5; + u32 rsvd6; + u32 rsvd7; +}; + +struct hinic_rq_ctrl { + u32 ctrl_info; +}; + +struct hinic_rq_cqe_sect { + struct hinic_sge sge; + u32 rsvd; +}; + +struct hinic_rq_bufdesc { + u32 hi_addr; + u32 lo_addr; +}; + +struct hinic_rq_wqe { + struct hinic_rq_ctrl ctrl; + u32 rsvd; + struct hinic_rq_cqe_sect cqe_sect; + struct hinic_rq_bufdesc buf_desc; +}; + +struct hinic_hw_wqe { + /* HW Format */ + union { + struct hinic_cmdq_wqe cmdq_wqe; + struct hinic_sq_wqe sq_wqe; + struct hinic_rq_wqe rq_wqe; + }; +}; + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_main.c b/drivers/net/ethernet/huawei/hinic/hinic_main.c new file mode 100644 index 000000000000..eb53bd93065e --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_main.c @@ -0,0 +1,1112 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/etherdevice.h> +#include <linux/netdevice.h> +#include <linux/slab.h> +#include <linux/if_vlan.h> +#include <linux/semaphore.h> +#include <linux/workqueue.h> +#include <net/ip.h> +#include <linux/bitops.h> +#include <linux/bitmap.h> +#include <linux/delay.h> +#include <linux/err.h> + +#include "hinic_hw_qp.h" +#include "hinic_hw_dev.h" +#include "hinic_port.h" +#include "hinic_tx.h" +#include "hinic_rx.h" +#include "hinic_dev.h" + +MODULE_AUTHOR("Huawei Technologies CO., Ltd"); +MODULE_DESCRIPTION("Huawei Intelligent NIC driver"); +MODULE_LICENSE("GPL"); + +static unsigned int tx_weight = 64; +module_param(tx_weight, uint, 0644); +MODULE_PARM_DESC(tx_weight, "Number Tx packets for NAPI budget (default=64)"); + +static unsigned int rx_weight = 64; +module_param(rx_weight, uint, 0644); +MODULE_PARM_DESC(rx_weight, "Number Rx packets for NAPI budget (default=64)"); + +#define PCI_DEVICE_ID_HI1822_PF 0x1822 + +#define HINIC_WQ_NAME "hinic_dev" + +#define MSG_ENABLE_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | \ + NETIF_MSG_IFUP | \ + NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR) + +#define VLAN_BITMAP_SIZE(nic_dev) (ALIGN(VLAN_N_VID, 8) / 8) + +#define work_to_rx_mode_work(work) \ + container_of(work, struct hinic_rx_mode_work, work) + +#define rx_mode_work_to_nic_dev(rx_mode_work) \ + container_of(rx_mode_work, struct hinic_dev, rx_mode_work) + +static int change_mac_addr(struct net_device *netdev, const u8 *addr); + +static void set_link_speed(struct ethtool_link_ksettings *link_ksettings, + enum hinic_speed speed) +{ + switch (speed) { + case HINIC_SPEED_10MB_LINK: + link_ksettings->base.speed = SPEED_10; + break; + + case HINIC_SPEED_100MB_LINK: + link_ksettings->base.speed = SPEED_100; + break; + + case HINIC_SPEED_1000MB_LINK: + link_ksettings->base.speed = SPEED_1000; + break; + + case HINIC_SPEED_10GB_LINK: + link_ksettings->base.speed = SPEED_10000; + break; + + case HINIC_SPEED_25GB_LINK: + link_ksettings->base.speed = SPEED_25000; + break; + + case HINIC_SPEED_40GB_LINK: + link_ksettings->base.speed = SPEED_40000; + break; + + case HINIC_SPEED_100GB_LINK: + link_ksettings->base.speed = SPEED_100000; + break; + + default: + link_ksettings->base.speed = SPEED_UNKNOWN; + break; + } +} + +static int hinic_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings + *link_ksettings) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + enum hinic_port_link_state link_state; + struct hinic_port_cap port_cap; + int err; + + ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); + ethtool_link_ksettings_add_link_mode(link_ksettings, supported, + Autoneg); + + link_ksettings->base.speed = SPEED_UNKNOWN; + link_ksettings->base.autoneg = AUTONEG_DISABLE; + link_ksettings->base.duplex = DUPLEX_UNKNOWN; + + err = hinic_port_get_cap(nic_dev, &port_cap); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to get port capabilities\n"); + return err; + } + + err = hinic_port_link_state(nic_dev, &link_state); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to get port link state\n"); + return err; + } + + if (link_state != HINIC_LINK_STATE_UP) { + netif_info(nic_dev, drv, netdev, "No link\n"); + return err; + } + + set_link_speed(link_ksettings, port_cap.speed); + + if (!!(port_cap.autoneg_cap & HINIC_AUTONEG_SUPPORTED)) + ethtool_link_ksettings_add_link_mode(link_ksettings, + advertising, Autoneg); + + if (port_cap.autoneg_state == HINIC_AUTONEG_ACTIVE) + link_ksettings->base.autoneg = AUTONEG_ENABLE; + + link_ksettings->base.duplex = (port_cap.duplex == HINIC_DUPLEX_FULL) ? + DUPLEX_FULL : DUPLEX_HALF; + return 0; +} + +static void hinic_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + + strlcpy(info->driver, HINIC_DRV_NAME, sizeof(info->driver)); + strlcpy(info->bus_info, pci_name(hwif->pdev), sizeof(info->bus_info)); +} + +static void hinic_get_ringparam(struct net_device *netdev, + struct ethtool_ringparam *ring) +{ + ring->rx_max_pending = HINIC_RQ_DEPTH; + ring->tx_max_pending = HINIC_SQ_DEPTH; + ring->rx_pending = HINIC_RQ_DEPTH; + ring->tx_pending = HINIC_SQ_DEPTH; +} + +static void hinic_get_channels(struct net_device *netdev, + struct ethtool_channels *channels) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + + channels->max_rx = hwdev->nic_cap.max_qps; + channels->max_tx = hwdev->nic_cap.max_qps; + channels->max_other = 0; + channels->max_combined = 0; + channels->rx_count = hinic_hwdev_num_qps(hwdev); + channels->tx_count = hinic_hwdev_num_qps(hwdev); + channels->other_count = 0; + channels->combined_count = 0; +} + +static const struct ethtool_ops hinic_ethtool_ops = { + .get_link_ksettings = hinic_get_link_ksettings, + .get_drvinfo = hinic_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_ringparam = hinic_get_ringparam, + .get_channels = hinic_get_channels, +}; + +static void update_rx_stats(struct hinic_dev *nic_dev, struct hinic_rxq *rxq) +{ + struct hinic_rxq_stats *nic_rx_stats = &nic_dev->rx_stats; + struct hinic_rxq_stats rx_stats; + + u64_stats_init(&rx_stats.syncp); + + hinic_rxq_get_stats(rxq, &rx_stats); + + u64_stats_update_begin(&nic_rx_stats->syncp); + nic_rx_stats->bytes += rx_stats.bytes; + nic_rx_stats->pkts += rx_stats.pkts; + u64_stats_update_end(&nic_rx_stats->syncp); + + hinic_rxq_clean_stats(rxq); +} + +static void update_tx_stats(struct hinic_dev *nic_dev, struct hinic_txq *txq) +{ + struct hinic_txq_stats *nic_tx_stats = &nic_dev->tx_stats; + struct hinic_txq_stats tx_stats; + + u64_stats_init(&tx_stats.syncp); + + hinic_txq_get_stats(txq, &tx_stats); + + u64_stats_update_begin(&nic_tx_stats->syncp); + nic_tx_stats->bytes += tx_stats.bytes; + nic_tx_stats->pkts += tx_stats.pkts; + nic_tx_stats->tx_busy += tx_stats.tx_busy; + nic_tx_stats->tx_wake += tx_stats.tx_wake; + nic_tx_stats->tx_dropped += tx_stats.tx_dropped; + u64_stats_update_end(&nic_tx_stats->syncp); + + hinic_txq_clean_stats(txq); +} + +static void update_nic_stats(struct hinic_dev *nic_dev) +{ + int i, num_qps = hinic_hwdev_num_qps(nic_dev->hwdev); + + for (i = 0; i < num_qps; i++) + update_rx_stats(nic_dev, &nic_dev->rxqs[i]); + + for (i = 0; i < num_qps; i++) + update_tx_stats(nic_dev, &nic_dev->txqs[i]); +} + +/** + * create_txqs - Create the Logical Tx Queues of specific NIC device + * @nic_dev: the specific NIC device + * + * Return 0 - Success, negative - Failure + **/ +static int create_txqs(struct hinic_dev *nic_dev) +{ + int err, i, j, num_txqs = hinic_hwdev_num_qps(nic_dev->hwdev); + struct net_device *netdev = nic_dev->netdev; + size_t txq_size; + + if (nic_dev->txqs) + return -EINVAL; + + txq_size = num_txqs * sizeof(*nic_dev->txqs); + nic_dev->txqs = devm_kzalloc(&netdev->dev, txq_size, GFP_KERNEL); + if (!nic_dev->txqs) + return -ENOMEM; + + for (i = 0; i < num_txqs; i++) { + struct hinic_sq *sq = hinic_hwdev_get_sq(nic_dev->hwdev, i); + + err = hinic_init_txq(&nic_dev->txqs[i], sq, netdev); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to init Txq\n"); + goto err_init_txq; + } + } + + return 0; + +err_init_txq: + for (j = 0; j < i; j++) + hinic_clean_txq(&nic_dev->txqs[j]); + + devm_kfree(&netdev->dev, nic_dev->txqs); + return err; +} + +/** + * free_txqs - Free the Logical Tx Queues of specific NIC device + * @nic_dev: the specific NIC device + **/ +static void free_txqs(struct hinic_dev *nic_dev) +{ + int i, num_txqs = hinic_hwdev_num_qps(nic_dev->hwdev); + struct net_device *netdev = nic_dev->netdev; + + if (!nic_dev->txqs) + return; + + for (i = 0; i < num_txqs; i++) + hinic_clean_txq(&nic_dev->txqs[i]); + + devm_kfree(&netdev->dev, nic_dev->txqs); + nic_dev->txqs = NULL; +} + +/** + * create_txqs - Create the Logical Rx Queues of specific NIC device + * @nic_dev: the specific NIC device + * + * Return 0 - Success, negative - Failure + **/ +static int create_rxqs(struct hinic_dev *nic_dev) +{ + int err, i, j, num_rxqs = hinic_hwdev_num_qps(nic_dev->hwdev); + struct net_device *netdev = nic_dev->netdev; + size_t rxq_size; + + if (nic_dev->rxqs) + return -EINVAL; + + rxq_size = num_rxqs * sizeof(*nic_dev->rxqs); + nic_dev->rxqs = devm_kzalloc(&netdev->dev, rxq_size, GFP_KERNEL); + if (!nic_dev->rxqs) + return -ENOMEM; + + for (i = 0; i < num_rxqs; i++) { + struct hinic_rq *rq = hinic_hwdev_get_rq(nic_dev->hwdev, i); + + err = hinic_init_rxq(&nic_dev->rxqs[i], rq, netdev); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to init rxq\n"); + goto err_init_rxq; + } + } + + return 0; + +err_init_rxq: + for (j = 0; j < i; j++) + hinic_clean_rxq(&nic_dev->rxqs[j]); + + devm_kfree(&netdev->dev, nic_dev->rxqs); + return err; +} + +/** + * free_txqs - Free the Logical Rx Queues of specific NIC device + * @nic_dev: the specific NIC device + **/ +static void free_rxqs(struct hinic_dev *nic_dev) +{ + int i, num_rxqs = hinic_hwdev_num_qps(nic_dev->hwdev); + struct net_device *netdev = nic_dev->netdev; + + if (!nic_dev->rxqs) + return; + + for (i = 0; i < num_rxqs; i++) + hinic_clean_rxq(&nic_dev->rxqs[i]); + + devm_kfree(&netdev->dev, nic_dev->rxqs); + nic_dev->rxqs = NULL; +} + +static int hinic_open(struct net_device *netdev) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + enum hinic_port_link_state link_state; + int err, ret, num_qps; + + if (!(nic_dev->flags & HINIC_INTF_UP)) { + err = hinic_hwdev_ifup(nic_dev->hwdev); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed - HW interface up\n"); + return err; + } + } + + err = create_txqs(nic_dev); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to create Tx queues\n"); + goto err_create_txqs; + } + + err = create_rxqs(nic_dev); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to create Rx queues\n"); + goto err_create_rxqs; + } + + num_qps = hinic_hwdev_num_qps(nic_dev->hwdev); + netif_set_real_num_tx_queues(netdev, num_qps); + netif_set_real_num_rx_queues(netdev, num_qps); + + err = hinic_port_set_state(nic_dev, HINIC_PORT_ENABLE); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to set port state\n"); + goto err_port_state; + } + + err = hinic_port_set_func_state(nic_dev, HINIC_FUNC_PORT_ENABLE); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to set func port state\n"); + goto err_func_port_state; + } + + /* Wait up to 3 sec between port enable to link state */ + msleep(3000); + + down(&nic_dev->mgmt_lock); + + err = hinic_port_link_state(nic_dev, &link_state); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to get link state\n"); + goto err_port_link; + } + + if (link_state == HINIC_LINK_STATE_UP) + nic_dev->flags |= HINIC_LINK_UP; + + nic_dev->flags |= HINIC_INTF_UP; + + if ((nic_dev->flags & (HINIC_LINK_UP | HINIC_INTF_UP)) == + (HINIC_LINK_UP | HINIC_INTF_UP)) { + netif_info(nic_dev, drv, netdev, "link + intf UP\n"); + netif_carrier_on(netdev); + netif_tx_wake_all_queues(netdev); + } + + up(&nic_dev->mgmt_lock); + + netif_info(nic_dev, drv, netdev, "HINIC_INTF is UP\n"); + return 0; + +err_port_link: + up(&nic_dev->mgmt_lock); + ret = hinic_port_set_func_state(nic_dev, HINIC_FUNC_PORT_DISABLE); + if (ret) + netif_warn(nic_dev, drv, netdev, + "Failed to revert func port state\n"); + +err_func_port_state: + ret = hinic_port_set_state(nic_dev, HINIC_PORT_DISABLE); + if (ret) + netif_warn(nic_dev, drv, netdev, + "Failed to revert port state\n"); + +err_port_state: + free_rxqs(nic_dev); + +err_create_rxqs: + free_txqs(nic_dev); + +err_create_txqs: + if (!(nic_dev->flags & HINIC_INTF_UP)) + hinic_hwdev_ifdown(nic_dev->hwdev); + return err; +} + +static int hinic_close(struct net_device *netdev) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + unsigned int flags; + int err; + + down(&nic_dev->mgmt_lock); + + flags = nic_dev->flags; + nic_dev->flags &= ~HINIC_INTF_UP; + + netif_carrier_off(netdev); + netif_tx_disable(netdev); + + update_nic_stats(nic_dev); + + up(&nic_dev->mgmt_lock); + + err = hinic_port_set_func_state(nic_dev, HINIC_FUNC_PORT_DISABLE); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to set func port state\n"); + nic_dev->flags |= (flags & HINIC_INTF_UP); + return err; + } + + err = hinic_port_set_state(nic_dev, HINIC_PORT_DISABLE); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to set port state\n"); + nic_dev->flags |= (flags & HINIC_INTF_UP); + return err; + } + + free_rxqs(nic_dev); + free_txqs(nic_dev); + + if (flags & HINIC_INTF_UP) + hinic_hwdev_ifdown(nic_dev->hwdev); + + netif_info(nic_dev, drv, netdev, "HINIC_INTF is DOWN\n"); + return 0; +} + +static int hinic_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + int err; + + netif_info(nic_dev, drv, netdev, "set_mtu = %d\n", new_mtu); + + err = hinic_port_set_mtu(nic_dev, new_mtu); + if (err) + netif_err(nic_dev, drv, netdev, "Failed to set port mtu\n"); + else + netdev->mtu = new_mtu; + + return err; +} + +/** + * change_mac_addr - change the main mac address of network device + * @netdev: network device + * @addr: mac address to set + * + * Return 0 - Success, negative - Failure + **/ +static int change_mac_addr(struct net_device *netdev, const u8 *addr) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + u16 vid = 0; + int err; + + if (!is_valid_ether_addr(addr)) + return -EADDRNOTAVAIL; + + netif_info(nic_dev, drv, netdev, "change mac addr = %02x %02x %02x %02x %02x %02x\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + down(&nic_dev->mgmt_lock); + + do { + err = hinic_port_del_mac(nic_dev, netdev->dev_addr, vid); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to delete mac\n"); + break; + } + + err = hinic_port_add_mac(nic_dev, addr, vid); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to add mac\n"); + break; + } + + vid = find_next_bit(nic_dev->vlan_bitmap, VLAN_N_VID, vid + 1); + } while (vid != VLAN_N_VID); + + up(&nic_dev->mgmt_lock); + return err; +} + +static int hinic_set_mac_addr(struct net_device *netdev, void *addr) +{ + unsigned char new_mac[ETH_ALEN]; + struct sockaddr *saddr = addr; + int err; + + memcpy(new_mac, saddr->sa_data, ETH_ALEN); + + err = change_mac_addr(netdev, new_mac); + if (!err) + memcpy(netdev->dev_addr, new_mac, ETH_ALEN); + + return err; +} + +/** + * add_mac_addr - add mac address to network device + * @netdev: network device + * @addr: mac address to add + * + * Return 0 - Success, negative - Failure + **/ +static int add_mac_addr(struct net_device *netdev, const u8 *addr) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + u16 vid = 0; + int err; + + if (!is_valid_ether_addr(addr)) + return -EADDRNOTAVAIL; + + netif_info(nic_dev, drv, netdev, "set mac addr = %02x %02x %02x %02x %02x %02x\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + down(&nic_dev->mgmt_lock); + + do { + err = hinic_port_add_mac(nic_dev, addr, vid); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to add mac\n"); + break; + } + + vid = find_next_bit(nic_dev->vlan_bitmap, VLAN_N_VID, vid + 1); + } while (vid != VLAN_N_VID); + + up(&nic_dev->mgmt_lock); + return err; +} + +/** + * remove_mac_addr - remove mac address from network device + * @netdev: network device + * @addr: mac address to remove + * + * Return 0 - Success, negative - Failure + **/ +static int remove_mac_addr(struct net_device *netdev, const u8 *addr) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + u16 vid = 0; + int err; + + if (!is_valid_ether_addr(addr)) + return -EADDRNOTAVAIL; + + netif_info(nic_dev, drv, netdev, "remove mac addr = %02x %02x %02x %02x %02x %02x\n", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + + down(&nic_dev->mgmt_lock); + + do { + err = hinic_port_del_mac(nic_dev, addr, vid); + if (err) { + netif_err(nic_dev, drv, netdev, + "Failed to delete mac\n"); + break; + } + + vid = find_next_bit(nic_dev->vlan_bitmap, VLAN_N_VID, vid + 1); + } while (vid != VLAN_N_VID); + + up(&nic_dev->mgmt_lock); + return err; +} + +static int hinic_vlan_rx_add_vid(struct net_device *netdev, + __always_unused __be16 proto, u16 vid) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + int ret, err; + + netif_info(nic_dev, drv, netdev, "add vid = %d\n", vid); + + down(&nic_dev->mgmt_lock); + + err = hinic_port_add_vlan(nic_dev, vid); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to add vlan\n"); + goto err_vlan_add; + } + + err = hinic_port_add_mac(nic_dev, netdev->dev_addr, vid); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to set mac\n"); + goto err_add_mac; + } + + bitmap_set(nic_dev->vlan_bitmap, vid, 1); + + up(&nic_dev->mgmt_lock); + return 0; + +err_add_mac: + ret = hinic_port_del_vlan(nic_dev, vid); + if (ret) + netif_err(nic_dev, drv, netdev, + "Failed to revert by removing vlan\n"); + +err_vlan_add: + up(&nic_dev->mgmt_lock); + return err; +} + +static int hinic_vlan_rx_kill_vid(struct net_device *netdev, + __always_unused __be16 proto, u16 vid) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + int err; + + netif_info(nic_dev, drv, netdev, "remove vid = %d\n", vid); + + down(&nic_dev->mgmt_lock); + + err = hinic_port_del_vlan(nic_dev, vid); + if (err) { + netif_err(nic_dev, drv, netdev, "Failed to delete vlan\n"); + goto err_del_vlan; + } + + bitmap_clear(nic_dev->vlan_bitmap, vid, 1); + + up(&nic_dev->mgmt_lock); + return 0; + +err_del_vlan: + up(&nic_dev->mgmt_lock); + return err; +} + +static void set_rx_mode(struct work_struct *work) +{ + struct hinic_rx_mode_work *rx_mode_work = work_to_rx_mode_work(work); + struct hinic_dev *nic_dev = rx_mode_work_to_nic_dev(rx_mode_work); + + netif_info(nic_dev, drv, nic_dev->netdev, "set rx mode work\n"); + + hinic_port_set_rx_mode(nic_dev, rx_mode_work->rx_mode); + + __dev_uc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr); + __dev_mc_sync(nic_dev->netdev, add_mac_addr, remove_mac_addr); +} + +static void hinic_set_rx_mode(struct net_device *netdev) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_rx_mode_work *rx_mode_work; + u32 rx_mode; + + rx_mode_work = &nic_dev->rx_mode_work; + + rx_mode = HINIC_RX_MODE_UC | + HINIC_RX_MODE_MC | + HINIC_RX_MODE_BC; + + if (netdev->flags & IFF_PROMISC) + rx_mode |= HINIC_RX_MODE_PROMISC; + else if (netdev->flags & IFF_ALLMULTI) + rx_mode |= HINIC_RX_MODE_MC_ALL; + + rx_mode_work->rx_mode = rx_mode; + + queue_work(nic_dev->workq, &rx_mode_work->work); +} + +static void hinic_tx_timeout(struct net_device *netdev) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + + netif_err(nic_dev, drv, netdev, "Tx timeout\n"); +} + +static void hinic_get_stats64(struct net_device *netdev, + struct rtnl_link_stats64 *stats) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_rxq_stats *nic_rx_stats; + struct hinic_txq_stats *nic_tx_stats; + + nic_rx_stats = &nic_dev->rx_stats; + nic_tx_stats = &nic_dev->tx_stats; + + down(&nic_dev->mgmt_lock); + + if (nic_dev->flags & HINIC_INTF_UP) + update_nic_stats(nic_dev); + + up(&nic_dev->mgmt_lock); + + stats->rx_bytes = nic_rx_stats->bytes; + stats->rx_packets = nic_rx_stats->pkts; + + stats->tx_bytes = nic_tx_stats->bytes; + stats->tx_packets = nic_tx_stats->pkts; + stats->tx_errors = nic_tx_stats->tx_dropped; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void hinic_netpoll(struct net_device *netdev) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + int i, num_qps; + + num_qps = hinic_hwdev_num_qps(nic_dev->hwdev); + for (i = 0; i < num_qps; i++) { + struct hinic_txq *txq = &nic_dev->txqs[i]; + struct hinic_rxq *rxq = &nic_dev->rxqs[i]; + + napi_schedule(&txq->napi); + napi_schedule(&rxq->napi); + } +} +#endif + +static const struct net_device_ops hinic_netdev_ops = { + .ndo_open = hinic_open, + .ndo_stop = hinic_close, + .ndo_change_mtu = hinic_change_mtu, + .ndo_set_mac_address = hinic_set_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_vlan_rx_add_vid = hinic_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = hinic_vlan_rx_kill_vid, + .ndo_set_rx_mode = hinic_set_rx_mode, + .ndo_start_xmit = hinic_xmit_frame, + .ndo_tx_timeout = hinic_tx_timeout, + .ndo_get_stats64 = hinic_get_stats64, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = hinic_netpoll, +#endif +}; + +static void netdev_features_init(struct net_device *netdev) +{ + netdev->hw_features = NETIF_F_SG | NETIF_F_HIGHDMA; + + netdev->vlan_features = netdev->hw_features; + + netdev->features = netdev->hw_features | NETIF_F_HW_VLAN_CTAG_FILTER; +} + +/** + * link_status_event_handler - link event handler + * @handle: nic device for the handler + * @buf_in: input buffer + * @in_size: input size + * @buf_in: output buffer + * @out_size: returned output size + * + * Return 0 - Success, negative - Failure + **/ +static void link_status_event_handler(void *handle, void *buf_in, u16 in_size, + void *buf_out, u16 *out_size) +{ + struct hinic_port_link_status *link_status, *ret_link_status; + struct hinic_dev *nic_dev = handle; + + link_status = buf_in; + + if (link_status->link == HINIC_LINK_STATE_UP) { + down(&nic_dev->mgmt_lock); + + nic_dev->flags |= HINIC_LINK_UP; + + if ((nic_dev->flags & (HINIC_LINK_UP | HINIC_INTF_UP)) == + (HINIC_LINK_UP | HINIC_INTF_UP)) { + netif_carrier_on(nic_dev->netdev); + netif_tx_wake_all_queues(nic_dev->netdev); + } + + up(&nic_dev->mgmt_lock); + + netif_info(nic_dev, drv, nic_dev->netdev, "HINIC_Link is UP\n"); + } else { + down(&nic_dev->mgmt_lock); + + nic_dev->flags &= ~HINIC_LINK_UP; + + netif_carrier_off(nic_dev->netdev); + netif_tx_disable(nic_dev->netdev); + + up(&nic_dev->mgmt_lock); + + netif_info(nic_dev, drv, nic_dev->netdev, "HINIC_Link is DOWN\n"); + } + + ret_link_status = buf_out; + ret_link_status->status = 0; + + *out_size = sizeof(*ret_link_status); +} + +/** + * nic_dev_init - Initialize the NIC device + * @pdev: the NIC pci device + * + * Return 0 - Success, negative - Failure + **/ +static int nic_dev_init(struct pci_dev *pdev) +{ + struct hinic_rx_mode_work *rx_mode_work; + struct hinic_txq_stats *tx_stats; + struct hinic_rxq_stats *rx_stats; + struct hinic_dev *nic_dev; + struct net_device *netdev; + struct hinic_hwdev *hwdev; + int err, num_qps; + + hwdev = hinic_init_hwdev(pdev); + if (IS_ERR(hwdev)) { + dev_err(&pdev->dev, "Failed to initialize HW device\n"); + return PTR_ERR(hwdev); + } + + num_qps = hinic_hwdev_num_qps(hwdev); + if (num_qps <= 0) { + dev_err(&pdev->dev, "Invalid number of QPS\n"); + err = -EINVAL; + goto err_num_qps; + } + + netdev = alloc_etherdev_mq(sizeof(*nic_dev), num_qps); + if (!netdev) { + dev_err(&pdev->dev, "Failed to allocate Ethernet device\n"); + err = -ENOMEM; + goto err_alloc_etherdev; + } + + netdev->netdev_ops = &hinic_netdev_ops; + netdev->ethtool_ops = &hinic_ethtool_ops; + netdev->max_mtu = ETH_MAX_MTU; + + nic_dev = netdev_priv(netdev); + nic_dev->netdev = netdev; + nic_dev->hwdev = hwdev; + nic_dev->msg_enable = MSG_ENABLE_DEFAULT; + nic_dev->flags = 0; + nic_dev->txqs = NULL; + nic_dev->rxqs = NULL; + nic_dev->tx_weight = tx_weight; + nic_dev->rx_weight = rx_weight; + + sema_init(&nic_dev->mgmt_lock, 1); + + tx_stats = &nic_dev->tx_stats; + rx_stats = &nic_dev->rx_stats; + + u64_stats_init(&tx_stats->syncp); + u64_stats_init(&rx_stats->syncp); + + nic_dev->vlan_bitmap = devm_kzalloc(&pdev->dev, + VLAN_BITMAP_SIZE(nic_dev), + GFP_KERNEL); + if (!nic_dev->vlan_bitmap) { + err = -ENOMEM; + goto err_vlan_bitmap; + } + + nic_dev->workq = create_singlethread_workqueue(HINIC_WQ_NAME); + if (!nic_dev->workq) { + err = -ENOMEM; + goto err_workq; + } + + pci_set_drvdata(pdev, netdev); + + err = hinic_port_get_mac(nic_dev, netdev->dev_addr); + if (err) + dev_warn(&pdev->dev, "Failed to get mac address\n"); + + err = hinic_port_add_mac(nic_dev, netdev->dev_addr, 0); + if (err) { + dev_err(&pdev->dev, "Failed to add mac\n"); + goto err_add_mac; + } + + err = hinic_port_set_mtu(nic_dev, netdev->mtu); + if (err) { + dev_err(&pdev->dev, "Failed to set mtu\n"); + goto err_set_mtu; + } + + rx_mode_work = &nic_dev->rx_mode_work; + INIT_WORK(&rx_mode_work->work, set_rx_mode); + + netdev_features_init(netdev); + + netif_carrier_off(netdev); + + hinic_hwdev_cb_register(nic_dev->hwdev, HINIC_MGMT_MSG_CMD_LINK_STATUS, + nic_dev, link_status_event_handler); + + err = register_netdev(netdev); + if (err) { + dev_err(&pdev->dev, "Failed to register netdev\n"); + goto err_reg_netdev; + } + + return 0; + +err_reg_netdev: + hinic_hwdev_cb_unregister(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_LINK_STATUS); + cancel_work_sync(&rx_mode_work->work); + +err_set_mtu: +err_add_mac: + pci_set_drvdata(pdev, NULL); + destroy_workqueue(nic_dev->workq); + +err_workq: +err_vlan_bitmap: + free_netdev(netdev); + +err_alloc_etherdev: +err_num_qps: + hinic_free_hwdev(hwdev); + return err; +} + +static int hinic_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int err = pci_enable_device(pdev); + + if (err) { + dev_err(&pdev->dev, "Failed to enable PCI device\n"); + return err; + } + + err = pci_request_regions(pdev, HINIC_DRV_NAME); + if (err) { + dev_err(&pdev->dev, "Failed to request PCI regions\n"); + goto err_pci_regions; + } + + pci_set_master(pdev); + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + if (err) { + dev_warn(&pdev->dev, "Couldn't set 64-bit DMA mask\n"); + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err) { + dev_err(&pdev->dev, "Failed to set DMA mask\n"); + goto err_dma_mask; + } + } + + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); + if (err) { + dev_warn(&pdev->dev, + "Couldn't set 64-bit consistent DMA mask\n"); + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err) { + dev_err(&pdev->dev, + "Failed to set consistent DMA mask\n"); + goto err_dma_consistent_mask; + } + } + + err = nic_dev_init(pdev); + if (err) { + dev_err(&pdev->dev, "Failed to initialize NIC device\n"); + goto err_nic_dev_init; + } + + dev_info(&pdev->dev, "HiNIC driver - probed\n"); + return 0; + +err_nic_dev_init: +err_dma_consistent_mask: +err_dma_mask: + pci_release_regions(pdev); + +err_pci_regions: + pci_disable_device(pdev); + return err; +} + +static void hinic_remove(struct pci_dev *pdev) +{ + struct net_device *netdev = pci_get_drvdata(pdev); + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_rx_mode_work *rx_mode_work; + + unregister_netdev(netdev); + + hinic_hwdev_cb_unregister(nic_dev->hwdev, + HINIC_MGMT_MSG_CMD_LINK_STATUS); + + rx_mode_work = &nic_dev->rx_mode_work; + cancel_work_sync(&rx_mode_work->work); + + pci_set_drvdata(pdev, NULL); + + destroy_workqueue(nic_dev->workq); + + hinic_free_hwdev(nic_dev->hwdev); + + free_netdev(netdev); + + pci_release_regions(pdev); + pci_disable_device(pdev); + + dev_info(&pdev->dev, "HiNIC driver - removed\n"); +} + +static const struct pci_device_id hinic_pci_table[] = { + { PCI_VDEVICE(HUAWEI, PCI_DEVICE_ID_HI1822_PF), 0}, + { 0, 0} +}; +MODULE_DEVICE_TABLE(pci, hinic_pci_table); + +static struct pci_driver hinic_driver = { + .name = HINIC_DRV_NAME, + .id_table = hinic_pci_table, + .probe = hinic_probe, + .remove = hinic_remove, +}; + +module_pci_driver(hinic_driver); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.c b/drivers/net/ethernet/huawei/hinic/hinic_port.c new file mode 100644 index 000000000000..4d4e3f05fb5f --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.c @@ -0,0 +1,379 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/types.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/if_vlan.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/errno.h> + +#include "hinic_hw_if.h" +#include "hinic_hw_dev.h" +#include "hinic_port.h" +#include "hinic_dev.h" + +#define HINIC_MIN_MTU_SIZE 256 +#define HINIC_MAX_JUMBO_FRAME_SIZE 15872 + +enum mac_op { + MAC_DEL, + MAC_SET, +}; + +/** + * change_mac - change(add or delete) mac address + * @nic_dev: nic device + * @addr: mac address + * @vlan_id: vlan number to set with the mac + * @op: add or delete the mac + * + * Return 0 - Success, negative - Failure + **/ +static int change_mac(struct hinic_dev *nic_dev, const u8 *addr, + u16 vlan_id, enum mac_op op) +{ + struct net_device *netdev = nic_dev->netdev; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_port_mac_cmd port_mac_cmd; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + enum hinic_port_cmd cmd; + u16 out_size; + int err; + + if (vlan_id >= VLAN_N_VID) { + netif_err(nic_dev, drv, netdev, "Invalid VLAN number\n"); + return -EINVAL; + } + + if (op == MAC_SET) + cmd = HINIC_PORT_CMD_SET_MAC; + else + cmd = HINIC_PORT_CMD_DEL_MAC; + + port_mac_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + port_mac_cmd.vlan_id = vlan_id; + memcpy(port_mac_cmd.mac, addr, ETH_ALEN); + + err = hinic_port_msg_cmd(hwdev, cmd, &port_mac_cmd, + sizeof(port_mac_cmd), + &port_mac_cmd, &out_size); + if (err || (out_size != sizeof(port_mac_cmd)) || port_mac_cmd.status) { + dev_err(&pdev->dev, "Failed to change MAC, ret = %d\n", + port_mac_cmd.status); + return -EFAULT; + } + + return 0; +} + +/** + * hinic_port_add_mac - add mac address + * @nic_dev: nic device + * @addr: mac address + * @vlan_id: vlan number to set with the mac + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_add_mac(struct hinic_dev *nic_dev, + const u8 *addr, u16 vlan_id) +{ + return change_mac(nic_dev, addr, vlan_id, MAC_SET); +} + +/** + * hinic_port_del_mac - remove mac address + * @nic_dev: nic device + * @addr: mac address + * @vlan_id: vlan number that is connected to the mac + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_del_mac(struct hinic_dev *nic_dev, const u8 *addr, + u16 vlan_id) +{ + return change_mac(nic_dev, addr, vlan_id, MAC_DEL); +} + +/** + * hinic_port_get_mac - get the mac address of the nic device + * @nic_dev: nic device + * @addr: returned mac address + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_get_mac(struct hinic_dev *nic_dev, u8 *addr) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_port_mac_cmd port_mac_cmd; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + port_mac_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_MAC, + &port_mac_cmd, sizeof(port_mac_cmd), + &port_mac_cmd, &out_size); + if (err || (out_size != sizeof(port_mac_cmd)) || port_mac_cmd.status) { + dev_err(&pdev->dev, "Failed to get mac, ret = %d\n", + port_mac_cmd.status); + return -EFAULT; + } + + memcpy(addr, port_mac_cmd.mac, ETH_ALEN); + return 0; +} + +/** + * hinic_port_set_mtu - set mtu + * @nic_dev: nic device + * @new_mtu: new mtu + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_set_mtu(struct hinic_dev *nic_dev, int new_mtu) +{ + struct net_device *netdev = nic_dev->netdev; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_port_mtu_cmd port_mtu_cmd; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + int err, max_frame; + u16 out_size; + + if (new_mtu < HINIC_MIN_MTU_SIZE) { + netif_err(nic_dev, drv, netdev, "mtu < MIN MTU size"); + return -EINVAL; + } + + max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN; + if (max_frame > HINIC_MAX_JUMBO_FRAME_SIZE) { + netif_err(nic_dev, drv, netdev, "mtu > MAX MTU size"); + return -EINVAL; + } + + port_mtu_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + port_mtu_cmd.mtu = new_mtu; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_CHANGE_MTU, + &port_mtu_cmd, sizeof(port_mtu_cmd), + &port_mtu_cmd, &out_size); + if (err || (out_size != sizeof(port_mtu_cmd)) || port_mtu_cmd.status) { + dev_err(&pdev->dev, "Failed to set mtu, ret = %d\n", + port_mtu_cmd.status); + return -EFAULT; + } + + return 0; +} + +/** + * hinic_port_add_vlan - add vlan to the nic device + * @nic_dev: nic device + * @vlan_id: the vlan number to add + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_add_vlan(struct hinic_dev *nic_dev, u16 vlan_id) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_port_vlan_cmd port_vlan_cmd; + + port_vlan_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + port_vlan_cmd.vlan_id = vlan_id; + + return hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_ADD_VLAN, + &port_vlan_cmd, sizeof(port_vlan_cmd), + NULL, NULL); +} + +/** + * hinic_port_del_vlan - delete vlan from the nic device + * @nic_dev: nic device + * @vlan_id: the vlan number to delete + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_del_vlan(struct hinic_dev *nic_dev, u16 vlan_id) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_port_vlan_cmd port_vlan_cmd; + + port_vlan_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + port_vlan_cmd.vlan_id = vlan_id; + + return hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_DEL_VLAN, + &port_vlan_cmd, sizeof(port_vlan_cmd), + NULL, NULL); +} + +/** + * hinic_port_set_rx_mode - set rx mode in the nic device + * @nic_dev: nic device + * @rx_mode: the rx mode to set + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_set_rx_mode(struct hinic_dev *nic_dev, u32 rx_mode) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_port_rx_mode_cmd rx_mode_cmd; + + rx_mode_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwdev->hwif); + rx_mode_cmd.rx_mode = rx_mode; + + return hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_RX_MODE, + &rx_mode_cmd, sizeof(rx_mode_cmd), + NULL, NULL); +} + +/** + * hinic_port_link_state - get the link state + * @nic_dev: nic device + * @link_state: the returned link state + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_link_state(struct hinic_dev *nic_dev, + enum hinic_port_link_state *link_state) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct hinic_port_link_cmd link_cmd; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "unsupported PCI Function type\n"); + return -EINVAL; + } + + link_cmd.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_LINK_STATE, + &link_cmd, sizeof(link_cmd), + &link_cmd, &out_size); + if (err || (out_size != sizeof(link_cmd)) || link_cmd.status) { + dev_err(&pdev->dev, "Failed to get link state, ret = %d\n", + link_cmd.status); + return -EINVAL; + } + + *link_state = link_cmd.state; + return 0; +} + +/** + * hinic_port_set_state - set port state + * @nic_dev: nic device + * @state: the state to set + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_set_state(struct hinic_dev *nic_dev, enum hinic_port_state state) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_port_state_cmd port_state; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + if (!HINIC_IS_PF(hwif) && !HINIC_IS_PPF(hwif)) { + dev_err(&pdev->dev, "unsupported PCI Function type\n"); + return -EINVAL; + } + + port_state.state = state; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_PORT_STATE, + &port_state, sizeof(port_state), + &port_state, &out_size); + if (err || (out_size != sizeof(port_state)) || port_state.status) { + dev_err(&pdev->dev, "Failed to set port state, ret = %d\n", + port_state.status); + return -EFAULT; + } + + return 0; +} + +/** + * hinic_port_set_func_state- set func device state + * @nic_dev: nic device + * @state: the state to set + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_set_func_state(struct hinic_dev *nic_dev, + enum hinic_func_port_state state) +{ + struct hinic_port_func_state_cmd func_state; + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + func_state.func_idx = HINIC_HWIF_FUNC_IDX(hwif); + func_state.state = state; + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_SET_FUNC_STATE, + &func_state, sizeof(func_state), + &func_state, &out_size); + if (err || (out_size != sizeof(func_state)) || func_state.status) { + dev_err(&pdev->dev, "Failed to set port func state, ret = %d\n", + func_state.status); + return -EFAULT; + } + + return 0; +} + +/** + * hinic_port_get_cap - get port capabilities + * @nic_dev: nic device + * @port_cap: returned port capabilities + * + * Return 0 - Success, negative - Failure + **/ +int hinic_port_get_cap(struct hinic_dev *nic_dev, + struct hinic_port_cap *port_cap) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + u16 out_size; + int err; + + port_cap->func_idx = HINIC_HWIF_FUNC_IDX(hwif); + + err = hinic_port_msg_cmd(hwdev, HINIC_PORT_CMD_GET_CAP, + port_cap, sizeof(*port_cap), + port_cap, &out_size); + if (err || (out_size != sizeof(*port_cap)) || port_cap->status) { + dev_err(&pdev->dev, + "Failed to get port capabilities, ret = %d\n", + port_cap->status); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_port.h b/drivers/net/ethernet/huawei/hinic/hinic_port.h new file mode 100644 index 000000000000..9404365195dd --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_port.h @@ -0,0 +1,198 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_PORT_H +#define HINIC_PORT_H + +#include <linux/types.h> +#include <linux/etherdevice.h> +#include <linux/bitops.h> + +#include "hinic_dev.h" + +enum hinic_rx_mode { + HINIC_RX_MODE_UC = BIT(0), + HINIC_RX_MODE_MC = BIT(1), + HINIC_RX_MODE_BC = BIT(2), + HINIC_RX_MODE_MC_ALL = BIT(3), + HINIC_RX_MODE_PROMISC = BIT(4), +}; + +enum hinic_port_link_state { + HINIC_LINK_STATE_DOWN, + HINIC_LINK_STATE_UP, +}; + +enum hinic_port_state { + HINIC_PORT_DISABLE = 0, + HINIC_PORT_ENABLE = 3, +}; + +enum hinic_func_port_state { + HINIC_FUNC_PORT_DISABLE = 0, + HINIC_FUNC_PORT_ENABLE = 2, +}; + +enum hinic_autoneg_cap { + HINIC_AUTONEG_UNSUPPORTED, + HINIC_AUTONEG_SUPPORTED, +}; + +enum hinic_autoneg_state { + HINIC_AUTONEG_DISABLED, + HINIC_AUTONEG_ACTIVE, +}; + +enum hinic_duplex { + HINIC_DUPLEX_HALF, + HINIC_DUPLEX_FULL, +}; + +enum hinic_speed { + HINIC_SPEED_10MB_LINK = 0, + HINIC_SPEED_100MB_LINK, + HINIC_SPEED_1000MB_LINK, + HINIC_SPEED_10GB_LINK, + HINIC_SPEED_25GB_LINK, + HINIC_SPEED_40GB_LINK, + HINIC_SPEED_100GB_LINK, + + HINIC_SPEED_UNKNOWN = 0xFF, +}; + +struct hinic_port_mac_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 vlan_id; + u16 rsvd1; + unsigned char mac[ETH_ALEN]; +}; + +struct hinic_port_mtu_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 rsvd1; + u32 mtu; +}; + +struct hinic_port_vlan_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 vlan_id; +}; + +struct hinic_port_rx_mode_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 rsvd; + u32 rx_mode; +}; + +struct hinic_port_link_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u8 state; + u8 rsvd1; +}; + +struct hinic_port_state_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u8 state; + u8 rsvd1[3]; +}; + +struct hinic_port_link_status { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 rsvd1; + u8 link; + u8 rsvd2; +}; + +struct hinic_port_func_state_cmd { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 rsvd1; + u8 state; + u8 rsvd2[3]; +}; + +struct hinic_port_cap { + u8 status; + u8 version; + u8 rsvd0[6]; + + u16 func_idx; + u16 rsvd1; + u8 port_type; + u8 autoneg_cap; + u8 autoneg_state; + u8 duplex; + u8 speed; + u8 rsvd2[3]; +}; + +int hinic_port_add_mac(struct hinic_dev *nic_dev, const u8 *addr, + u16 vlan_id); + +int hinic_port_del_mac(struct hinic_dev *nic_dev, const u8 *addr, + u16 vlan_id); + +int hinic_port_get_mac(struct hinic_dev *nic_dev, u8 *addr); + +int hinic_port_set_mtu(struct hinic_dev *nic_dev, int new_mtu); + +int hinic_port_add_vlan(struct hinic_dev *nic_dev, u16 vlan_id); + +int hinic_port_del_vlan(struct hinic_dev *nic_dev, u16 vlan_id); + +int hinic_port_set_rx_mode(struct hinic_dev *nic_dev, u32 rx_mode); + +int hinic_port_link_state(struct hinic_dev *nic_dev, + enum hinic_port_link_state *link_state); + +int hinic_port_set_state(struct hinic_dev *nic_dev, + enum hinic_port_state state); + +int hinic_port_set_func_state(struct hinic_dev *nic_dev, + enum hinic_func_port_state state); + +int hinic_port_get_cap(struct hinic_dev *nic_dev, + struct hinic_port_cap *port_cap); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.c b/drivers/net/ethernet/huawei/hinic/hinic_rx.c new file mode 100644 index 000000000000..1d4f712b15a8 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.c @@ -0,0 +1,509 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/u64_stats_sync.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/skbuff.h> +#include <linux/dma-mapping.h> +#include <linux/prefetch.h> +#include <asm/barrier.h> + +#include "hinic_common.h" +#include "hinic_hw_if.h" +#include "hinic_hw_wqe.h" +#include "hinic_hw_wq.h" +#include "hinic_hw_qp.h" +#include "hinic_hw_dev.h" +#include "hinic_rx.h" +#include "hinic_dev.h" + +#define RX_IRQ_NO_PENDING 0 +#define RX_IRQ_NO_COALESC 0 +#define RX_IRQ_NO_LLI_TIMER 0 +#define RX_IRQ_NO_CREDIT 0 +#define RX_IRQ_NO_RESEND_TIMER 0 + +/** + * hinic_rxq_clean_stats - Clean the statistics of specific queue + * @rxq: Logical Rx Queue + **/ +void hinic_rxq_clean_stats(struct hinic_rxq *rxq) +{ + struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats; + + u64_stats_update_begin(&rxq_stats->syncp); + rxq_stats->pkts = 0; + rxq_stats->bytes = 0; + u64_stats_update_end(&rxq_stats->syncp); +} + +/** + * hinic_rxq_get_stats - get statistics of Rx Queue + * @rxq: Logical Rx Queue + * @stats: return updated stats here + **/ +void hinic_rxq_get_stats(struct hinic_rxq *rxq, struct hinic_rxq_stats *stats) +{ + struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats; + unsigned int start; + + u64_stats_update_begin(&stats->syncp); + do { + start = u64_stats_fetch_begin(&rxq_stats->syncp); + stats->pkts = rxq_stats->pkts; + stats->bytes = rxq_stats->bytes; + } while (u64_stats_fetch_retry(&rxq_stats->syncp, start)); + u64_stats_update_end(&stats->syncp); +} + +/** + * rxq_stats_init - Initialize the statistics of specific queue + * @rxq: Logical Rx Queue + **/ +static void rxq_stats_init(struct hinic_rxq *rxq) +{ + struct hinic_rxq_stats *rxq_stats = &rxq->rxq_stats; + + u64_stats_init(&rxq_stats->syncp); + hinic_rxq_clean_stats(rxq); +} + +/** + * rx_alloc_skb - allocate skb and map it to dma address + * @rxq: rx queue + * @dma_addr: returned dma address for the skb + * + * Return skb + **/ +static struct sk_buff *rx_alloc_skb(struct hinic_rxq *rxq, + dma_addr_t *dma_addr) +{ + struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct sk_buff *skb; + dma_addr_t addr; + int err; + + skb = netdev_alloc_skb_ip_align(rxq->netdev, rxq->rq->buf_sz); + if (!skb) { + netdev_err(rxq->netdev, "Failed to allocate Rx SKB\n"); + return NULL; + } + + addr = dma_map_single(&pdev->dev, skb->data, rxq->rq->buf_sz, + DMA_FROM_DEVICE); + err = dma_mapping_error(&pdev->dev, addr); + if (err) { + dev_err(&pdev->dev, "Failed to map Rx DMA, err = %d\n", err); + goto err_rx_map; + } + + *dma_addr = addr; + return skb; + +err_rx_map: + dev_kfree_skb_any(skb); + return NULL; +} + +/** + * rx_unmap_skb - unmap the dma address of the skb + * @rxq: rx queue + * @dma_addr: dma address of the skb + **/ +static void rx_unmap_skb(struct hinic_rxq *rxq, dma_addr_t dma_addr) +{ + struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + + dma_unmap_single(&pdev->dev, dma_addr, rxq->rq->buf_sz, + DMA_FROM_DEVICE); +} + +/** + * rx_free_skb - unmap and free skb + * @rxq: rx queue + * @skb: skb to free + * @dma_addr: dma address of the skb + **/ +static void rx_free_skb(struct hinic_rxq *rxq, struct sk_buff *skb, + dma_addr_t dma_addr) +{ + rx_unmap_skb(rxq, dma_addr); + dev_kfree_skb_any(skb); +} + +/** + * rx_alloc_pkts - allocate pkts in rx queue + * @rxq: rx queue + * + * Return number of skbs allocated + **/ +static int rx_alloc_pkts(struct hinic_rxq *rxq) +{ + struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); + struct hinic_rq_wqe *rq_wqe; + unsigned int free_wqebbs; + struct hinic_sge sge; + dma_addr_t dma_addr; + struct sk_buff *skb; + int i, alloc_more; + u16 prod_idx; + + free_wqebbs = hinic_get_rq_free_wqebbs(rxq->rq); + alloc_more = 0; + + /* Limit the allocation chunks */ + if (free_wqebbs > nic_dev->rx_weight) + free_wqebbs = nic_dev->rx_weight; + + for (i = 0; i < free_wqebbs; i++) { + skb = rx_alloc_skb(rxq, &dma_addr); + if (!skb) { + netdev_err(rxq->netdev, "Failed to alloc Rx skb\n"); + alloc_more = 1; + goto skb_out; + } + + hinic_set_sge(&sge, dma_addr, skb->len); + + rq_wqe = hinic_rq_get_wqe(rxq->rq, HINIC_RQ_WQE_SIZE, + &prod_idx); + if (!rq_wqe) { + rx_free_skb(rxq, skb, dma_addr); + alloc_more = 1; + goto skb_out; + } + + hinic_rq_prepare_wqe(rxq->rq, prod_idx, rq_wqe, &sge); + + hinic_rq_write_wqe(rxq->rq, prod_idx, rq_wqe, skb); + } + +skb_out: + if (i) { + wmb(); /* write all the wqes before update PI */ + + hinic_rq_update(rxq->rq, prod_idx); + } + + if (alloc_more) + tasklet_schedule(&rxq->rx_task); + + return i; +} + +/** + * free_all_rx_skbs - free all skbs in rx queue + * @rxq: rx queue + **/ +static void free_all_rx_skbs(struct hinic_rxq *rxq) +{ + struct hinic_rq *rq = rxq->rq; + struct hinic_hw_wqe *hw_wqe; + struct hinic_sge sge; + u16 ci; + + while ((hw_wqe = hinic_read_wqe(rq->wq, HINIC_RQ_WQE_SIZE, &ci))) { + if (IS_ERR(hw_wqe)) + break; + + hinic_rq_get_sge(rq, &hw_wqe->rq_wqe, ci, &sge); + + hinic_put_wqe(rq->wq, HINIC_RQ_WQE_SIZE); + + rx_free_skb(rxq, rq->saved_skb[ci], hinic_sge_to_dma(&sge)); + } +} + +/** + * rx_alloc_task - tasklet for queue allocation + * @data: rx queue + **/ +static void rx_alloc_task(unsigned long data) +{ + struct hinic_rxq *rxq = (struct hinic_rxq *)data; + + (void)rx_alloc_pkts(rxq); +} + +/** + * rx_recv_jumbo_pkt - Rx handler for jumbo pkt + * @rxq: rx queue + * @head_skb: the first skb in the list + * @left_pkt_len: left size of the pkt exclude head skb + * @ci: consumer index + * + * Return number of wqes that used for the left of the pkt + **/ +static int rx_recv_jumbo_pkt(struct hinic_rxq *rxq, struct sk_buff *head_skb, + unsigned int left_pkt_len, u16 ci) +{ + struct sk_buff *skb, *curr_skb = head_skb; + struct hinic_rq_wqe *rq_wqe; + unsigned int curr_len; + struct hinic_sge sge; + int num_wqes = 0; + + while (left_pkt_len > 0) { + rq_wqe = hinic_rq_read_next_wqe(rxq->rq, HINIC_RQ_WQE_SIZE, + &skb, &ci); + + num_wqes++; + + hinic_rq_get_sge(rxq->rq, rq_wqe, ci, &sge); + + rx_unmap_skb(rxq, hinic_sge_to_dma(&sge)); + + prefetch(skb->data); + + curr_len = (left_pkt_len > HINIC_RX_BUF_SZ) ? HINIC_RX_BUF_SZ : + left_pkt_len; + + left_pkt_len -= curr_len; + + __skb_put(skb, curr_len); + + if (curr_skb == head_skb) + skb_shinfo(head_skb)->frag_list = skb; + else + curr_skb->next = skb; + + head_skb->len += skb->len; + head_skb->data_len += skb->len; + head_skb->truesize += skb->truesize; + + curr_skb = skb; + } + + return num_wqes; +} + +/** + * rxq_recv - Rx handler + * @rxq: rx queue + * @budget: maximum pkts to process + * + * Return number of pkts received + **/ +static int rxq_recv(struct hinic_rxq *rxq, int budget) +{ + struct hinic_qp *qp = container_of(rxq->rq, struct hinic_qp, rq); + u64 pkt_len = 0, rx_bytes = 0; + struct hinic_rq_wqe *rq_wqe; + int num_wqes, pkts = 0; + struct hinic_sge sge; + struct sk_buff *skb; + u16 ci; + + while (pkts < budget) { + num_wqes = 0; + + rq_wqe = hinic_rq_read_wqe(rxq->rq, HINIC_RQ_WQE_SIZE, &skb, + &ci); + if (!rq_wqe) + break; + + hinic_rq_get_sge(rxq->rq, rq_wqe, ci, &sge); + + rx_unmap_skb(rxq, hinic_sge_to_dma(&sge)); + + prefetch(skb->data); + + pkt_len = sge.len; + + if (pkt_len <= HINIC_RX_BUF_SZ) { + __skb_put(skb, pkt_len); + } else { + __skb_put(skb, HINIC_RX_BUF_SZ); + num_wqes = rx_recv_jumbo_pkt(rxq, skb, pkt_len - + HINIC_RX_BUF_SZ, ci); + } + + hinic_rq_put_wqe(rxq->rq, ci, + (num_wqes + 1) * HINIC_RQ_WQE_SIZE); + + skb_record_rx_queue(skb, qp->q_id); + skb->protocol = eth_type_trans(skb, rxq->netdev); + + napi_gro_receive(&rxq->napi, skb); + + pkts++; + rx_bytes += pkt_len; + } + + if (pkts) + tasklet_schedule(&rxq->rx_task); /* hinic_rx_alloc_pkts */ + + u64_stats_update_begin(&rxq->rxq_stats.syncp); + rxq->rxq_stats.pkts += pkts; + rxq->rxq_stats.bytes += rx_bytes; + u64_stats_update_end(&rxq->rxq_stats.syncp); + + return pkts; +} + +static int rx_poll(struct napi_struct *napi, int budget) +{ + struct hinic_rxq *rxq = container_of(napi, struct hinic_rxq, napi); + struct hinic_rq *rq = rxq->rq; + int pkts; + + pkts = rxq_recv(rxq, budget); + if (pkts >= budget) + return budget; + + napi_complete(napi); + enable_irq(rq->irq); + return pkts; +} + +static void rx_add_napi(struct hinic_rxq *rxq) +{ + struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); + + netif_napi_add(rxq->netdev, &rxq->napi, rx_poll, nic_dev->rx_weight); + napi_enable(&rxq->napi); +} + +static void rx_del_napi(struct hinic_rxq *rxq) +{ + napi_disable(&rxq->napi); + netif_napi_del(&rxq->napi); +} + +static irqreturn_t rx_irq(int irq, void *data) +{ + struct hinic_rxq *rxq = (struct hinic_rxq *)data; + struct hinic_rq *rq = rxq->rq; + struct hinic_dev *nic_dev; + + /* Disable the interrupt until napi will be completed */ + disable_irq_nosync(rq->irq); + + nic_dev = netdev_priv(rxq->netdev); + hinic_hwdev_msix_cnt_set(nic_dev->hwdev, rq->msix_entry); + + napi_schedule(&rxq->napi); + return IRQ_HANDLED; +} + +static int rx_request_irq(struct hinic_rxq *rxq) +{ + struct hinic_dev *nic_dev = netdev_priv(rxq->netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_rq *rq = rxq->rq; + int err; + + rx_add_napi(rxq); + + hinic_hwdev_msix_set(hwdev, rq->msix_entry, + RX_IRQ_NO_PENDING, RX_IRQ_NO_COALESC, + RX_IRQ_NO_LLI_TIMER, RX_IRQ_NO_CREDIT, + RX_IRQ_NO_RESEND_TIMER); + + err = request_irq(rq->irq, rx_irq, 0, rxq->irq_name, rxq); + if (err) { + rx_del_napi(rxq); + return err; + } + + return 0; +} + +static void rx_free_irq(struct hinic_rxq *rxq) +{ + struct hinic_rq *rq = rxq->rq; + + free_irq(rq->irq, rxq); + rx_del_napi(rxq); +} + +/** + * hinic_init_rxq - Initialize the Rx Queue + * @rxq: Logical Rx Queue + * @rq: Hardware Rx Queue to connect the Logical queue with + * @netdev: network device to connect the Logical queue with + * + * Return 0 - Success, negative - Failure + **/ +int hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq, + struct net_device *netdev) +{ + struct hinic_qp *qp = container_of(rq, struct hinic_qp, rq); + int err, pkts, irqname_len; + + rxq->netdev = netdev; + rxq->rq = rq; + + rxq_stats_init(rxq); + + irqname_len = snprintf(NULL, 0, "hinic_rxq%d", qp->q_id) + 1; + rxq->irq_name = devm_kzalloc(&netdev->dev, irqname_len, GFP_KERNEL); + if (!rxq->irq_name) + return -ENOMEM; + + sprintf(rxq->irq_name, "hinic_rxq%d", qp->q_id); + + tasklet_init(&rxq->rx_task, rx_alloc_task, (unsigned long)rxq); + + pkts = rx_alloc_pkts(rxq); + if (!pkts) { + err = -ENOMEM; + goto err_rx_pkts; + } + + err = rx_request_irq(rxq); + if (err) { + netdev_err(netdev, "Failed to request Rx irq\n"); + goto err_req_rx_irq; + } + + return 0; + +err_req_rx_irq: +err_rx_pkts: + tasklet_kill(&rxq->rx_task); + free_all_rx_skbs(rxq); + devm_kfree(&netdev->dev, rxq->irq_name); + return err; +} + +/** + * hinic_clean_rxq - Clean the Rx Queue + * @rxq: Logical Rx Queue + **/ +void hinic_clean_rxq(struct hinic_rxq *rxq) +{ + struct net_device *netdev = rxq->netdev; + + rx_free_irq(rxq); + + tasklet_kill(&rxq->rx_task); + free_all_rx_skbs(rxq); + devm_kfree(&netdev->dev, rxq->irq_name); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_rx.h b/drivers/net/ethernet/huawei/hinic/hinic_rx.h new file mode 100644 index 000000000000..27c9af4b1c12 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_rx.h @@ -0,0 +1,55 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_RX_H +#define HINIC_RX_H + +#include <linux/types.h> +#include <linux/netdevice.h> +#include <linux/u64_stats_sync.h> +#include <linux/interrupt.h> + +#include "hinic_hw_qp.h" + +struct hinic_rxq_stats { + u64 pkts; + u64 bytes; + + struct u64_stats_sync syncp; +}; + +struct hinic_rxq { + struct net_device *netdev; + struct hinic_rq *rq; + + struct hinic_rxq_stats rxq_stats; + + char *irq_name; + + struct tasklet_struct rx_task; + + struct napi_struct napi; +}; + +void hinic_rxq_clean_stats(struct hinic_rxq *rxq); + +void hinic_rxq_get_stats(struct hinic_rxq *rxq, struct hinic_rxq_stats *stats); + +int hinic_init_rxq(struct hinic_rxq *rxq, struct hinic_rq *rq, + struct net_device *netdev); + +void hinic_clean_rxq(struct hinic_rxq *rxq); + +#endif diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.c b/drivers/net/ethernet/huawei/hinic/hinic_tx.c new file mode 100644 index 000000000000..abe3e38cd342 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.c @@ -0,0 +1,504 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/u64_stats_sync.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/skbuff.h> +#include <linux/smp.h> +#include <asm/byteorder.h> + +#include "hinic_common.h" +#include "hinic_hw_if.h" +#include "hinic_hw_wqe.h" +#include "hinic_hw_wq.h" +#include "hinic_hw_qp.h" +#include "hinic_hw_dev.h" +#include "hinic_dev.h" +#include "hinic_tx.h" + +#define TX_IRQ_NO_PENDING 0 +#define TX_IRQ_NO_COALESC 0 +#define TX_IRQ_NO_LLI_TIMER 0 +#define TX_IRQ_NO_CREDIT 0 +#define TX_IRQ_NO_RESEND_TIMER 0 + +#define CI_UPDATE_NO_PENDING 0 +#define CI_UPDATE_NO_COALESC 0 + +#define HW_CONS_IDX(sq) be16_to_cpu(*(u16 *)((sq)->hw_ci_addr)) + +#define MIN_SKB_LEN 64 + +/** + * hinic_txq_clean_stats - Clean the statistics of specific queue + * @txq: Logical Tx Queue + **/ +void hinic_txq_clean_stats(struct hinic_txq *txq) +{ + struct hinic_txq_stats *txq_stats = &txq->txq_stats; + + u64_stats_update_begin(&txq_stats->syncp); + txq_stats->pkts = 0; + txq_stats->bytes = 0; + txq_stats->tx_busy = 0; + txq_stats->tx_wake = 0; + txq_stats->tx_dropped = 0; + u64_stats_update_end(&txq_stats->syncp); +} + +/** + * hinic_txq_get_stats - get statistics of Tx Queue + * @txq: Logical Tx Queue + * @stats: return updated stats here + **/ +void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats) +{ + struct hinic_txq_stats *txq_stats = &txq->txq_stats; + unsigned int start; + + u64_stats_update_begin(&stats->syncp); + do { + start = u64_stats_fetch_begin(&txq_stats->syncp); + stats->pkts = txq_stats->pkts; + stats->bytes = txq_stats->bytes; + stats->tx_busy = txq_stats->tx_busy; + stats->tx_wake = txq_stats->tx_wake; + stats->tx_dropped = txq_stats->tx_dropped; + } while (u64_stats_fetch_retry(&txq_stats->syncp, start)); + u64_stats_update_end(&stats->syncp); +} + +/** + * txq_stats_init - Initialize the statistics of specific queue + * @txq: Logical Tx Queue + **/ +static void txq_stats_init(struct hinic_txq *txq) +{ + struct hinic_txq_stats *txq_stats = &txq->txq_stats; + + u64_stats_init(&txq_stats->syncp); + hinic_txq_clean_stats(txq); +} + +/** + * tx_map_skb - dma mapping for skb and return sges + * @nic_dev: nic device + * @skb: the skb + * @sges: returned sges + * + * Return 0 - Success, negative - Failure + **/ +static int tx_map_skb(struct hinic_dev *nic_dev, struct sk_buff *skb, + struct hinic_sge *sges) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct skb_frag_struct *frag; + dma_addr_t dma_addr; + int i, j; + + dma_addr = dma_map_single(&pdev->dev, skb->data, skb_headlen(skb), + DMA_TO_DEVICE); + if (dma_mapping_error(&pdev->dev, dma_addr)) { + dev_err(&pdev->dev, "Failed to map Tx skb data\n"); + return -EFAULT; + } + + hinic_set_sge(&sges[0], dma_addr, skb_headlen(skb)); + + for (i = 0 ; i < skb_shinfo(skb)->nr_frags; i++) { + frag = &skb_shinfo(skb)->frags[i]; + + dma_addr = skb_frag_dma_map(&pdev->dev, frag, 0, + skb_frag_size(frag), + DMA_TO_DEVICE); + if (dma_mapping_error(&pdev->dev, dma_addr)) { + dev_err(&pdev->dev, "Failed to map Tx skb frag\n"); + goto err_tx_map; + } + + hinic_set_sge(&sges[i + 1], dma_addr, skb_frag_size(frag)); + } + + return 0; + +err_tx_map: + for (j = 0; j < i; j++) + dma_unmap_page(&pdev->dev, hinic_sge_to_dma(&sges[j + 1]), + sges[j + 1].len, DMA_TO_DEVICE); + + dma_unmap_single(&pdev->dev, hinic_sge_to_dma(&sges[0]), sges[0].len, + DMA_TO_DEVICE); + return -EFAULT; +} + +/** + * tx_unmap_skb - unmap the dma address of the skb + * @nic_dev: nic device + * @skb: the skb + * @sges: the sges that are connected to the skb + **/ +static void tx_unmap_skb(struct hinic_dev *nic_dev, struct sk_buff *skb, + struct hinic_sge *sges) +{ + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + int i; + + for (i = 0; i < skb_shinfo(skb)->nr_frags ; i++) + dma_unmap_page(&pdev->dev, hinic_sge_to_dma(&sges[i + 1]), + sges[i + 1].len, DMA_TO_DEVICE); + + dma_unmap_single(&pdev->dev, hinic_sge_to_dma(&sges[0]), sges[0].len, + DMA_TO_DEVICE); +} + +netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct netdev_queue *netdev_txq; + int nr_sges, err = NETDEV_TX_OK; + struct hinic_sq_wqe *sq_wqe; + unsigned int wqe_size; + struct hinic_txq *txq; + struct hinic_qp *qp; + u16 prod_idx; + + txq = &nic_dev->txqs[skb->queue_mapping]; + qp = container_of(txq->sq, struct hinic_qp, sq); + + if (skb->len < MIN_SKB_LEN) { + if (skb_pad(skb, MIN_SKB_LEN - skb->len)) { + netdev_err(netdev, "Failed to pad skb\n"); + goto update_error_stats; + } + + skb->len = MIN_SKB_LEN; + } + + nr_sges = skb_shinfo(skb)->nr_frags + 1; + if (nr_sges > txq->max_sges) { + netdev_err(netdev, "Too many Tx sges\n"); + goto skb_error; + } + + err = tx_map_skb(nic_dev, skb, txq->sges); + if (err) + goto skb_error; + + wqe_size = HINIC_SQ_WQE_SIZE(nr_sges); + + sq_wqe = hinic_sq_get_wqe(txq->sq, wqe_size, &prod_idx); + if (!sq_wqe) { + tx_unmap_skb(nic_dev, skb, txq->sges); + + netif_stop_subqueue(netdev, qp->q_id); + + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.tx_busy++; + u64_stats_update_end(&txq->txq_stats.syncp); + err = NETDEV_TX_BUSY; + goto flush_skbs; + } + + hinic_sq_prepare_wqe(txq->sq, prod_idx, sq_wqe, txq->sges, nr_sges); + + hinic_sq_write_wqe(txq->sq, prod_idx, sq_wqe, skb, wqe_size); + +flush_skbs: + netdev_txq = netdev_get_tx_queue(netdev, skb->queue_mapping); + if ((!skb->xmit_more) || (netif_xmit_stopped(netdev_txq))) + hinic_sq_write_db(txq->sq, prod_idx, wqe_size, 0); + + return err; + +skb_error: + dev_kfree_skb_any(skb); + +update_error_stats: + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.tx_dropped++; + u64_stats_update_end(&txq->txq_stats.syncp); + return err; +} + +/** + * tx_free_skb - unmap and free skb + * @nic_dev: nic device + * @skb: the skb + * @sges: the sges that are connected to the skb + **/ +static void tx_free_skb(struct hinic_dev *nic_dev, struct sk_buff *skb, + struct hinic_sge *sges) +{ + tx_unmap_skb(nic_dev, skb, sges); + + dev_kfree_skb_any(skb); +} + +/** + * free_all_rx_skbs - free all skbs in tx queue + * @txq: tx queue + **/ +static void free_all_tx_skbs(struct hinic_txq *txq) +{ + struct hinic_dev *nic_dev = netdev_priv(txq->netdev); + struct hinic_sq *sq = txq->sq; + struct hinic_sq_wqe *sq_wqe; + unsigned int wqe_size; + struct sk_buff *skb; + int nr_sges; + u16 ci; + + while ((sq_wqe = hinic_sq_read_wqe(sq, &skb, &wqe_size, &ci))) { + nr_sges = skb_shinfo(skb)->nr_frags + 1; + + hinic_sq_get_sges(sq_wqe, txq->free_sges, nr_sges); + + hinic_sq_put_wqe(sq, wqe_size); + + tx_free_skb(nic_dev, skb, txq->free_sges); + } +} + +/** + * free_tx_poll - free finished tx skbs in tx queue that connected to napi + * @napi: napi + * @budget: number of tx + * + * Return 0 - Success, negative - Failure + **/ +static int free_tx_poll(struct napi_struct *napi, int budget) +{ + struct hinic_txq *txq = container_of(napi, struct hinic_txq, napi); + struct hinic_qp *qp = container_of(txq->sq, struct hinic_qp, sq); + struct hinic_dev *nic_dev = netdev_priv(txq->netdev); + struct netdev_queue *netdev_txq; + struct hinic_sq *sq = txq->sq; + struct hinic_wq *wq = sq->wq; + struct hinic_sq_wqe *sq_wqe; + unsigned int wqe_size; + int nr_sges, pkts = 0; + struct sk_buff *skb; + u64 tx_bytes = 0; + u16 hw_ci, sw_ci; + + do { + hw_ci = HW_CONS_IDX(sq) & wq->mask; + + sq_wqe = hinic_sq_read_wqe(sq, &skb, &wqe_size, &sw_ci); + if ((!sq_wqe) || + (((hw_ci - sw_ci) & wq->mask) * wq->wqebb_size < wqe_size)) + break; + + tx_bytes += skb->len; + pkts++; + + nr_sges = skb_shinfo(skb)->nr_frags + 1; + + hinic_sq_get_sges(sq_wqe, txq->free_sges, nr_sges); + + hinic_sq_put_wqe(sq, wqe_size); + + tx_free_skb(nic_dev, skb, txq->free_sges); + } while (pkts < budget); + + if (__netif_subqueue_stopped(nic_dev->netdev, qp->q_id) && + hinic_get_sq_free_wqebbs(sq) >= HINIC_MIN_TX_NUM_WQEBBS(sq)) { + netdev_txq = netdev_get_tx_queue(txq->netdev, qp->q_id); + + __netif_tx_lock(netdev_txq, smp_processor_id()); + + netif_wake_subqueue(nic_dev->netdev, qp->q_id); + + __netif_tx_unlock(netdev_txq); + + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.tx_wake++; + u64_stats_update_end(&txq->txq_stats.syncp); + } + + u64_stats_update_begin(&txq->txq_stats.syncp); + txq->txq_stats.bytes += tx_bytes; + txq->txq_stats.pkts += pkts; + u64_stats_update_end(&txq->txq_stats.syncp); + + if (pkts < budget) { + napi_complete(napi); + enable_irq(sq->irq); + return pkts; + } + + return budget; +} + +static void tx_napi_add(struct hinic_txq *txq, int weight) +{ + netif_napi_add(txq->netdev, &txq->napi, free_tx_poll, weight); + napi_enable(&txq->napi); +} + +static void tx_napi_del(struct hinic_txq *txq) +{ + napi_disable(&txq->napi); + netif_napi_del(&txq->napi); +} + +static irqreturn_t tx_irq(int irq, void *data) +{ + struct hinic_txq *txq = data; + struct hinic_dev *nic_dev; + + nic_dev = netdev_priv(txq->netdev); + + /* Disable the interrupt until napi will be completed */ + disable_irq_nosync(txq->sq->irq); + + hinic_hwdev_msix_cnt_set(nic_dev->hwdev, txq->sq->msix_entry); + + napi_schedule(&txq->napi); + return IRQ_HANDLED; +} + +static int tx_request_irq(struct hinic_txq *txq) +{ + struct hinic_dev *nic_dev = netdev_priv(txq->netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + struct hinic_hwif *hwif = hwdev->hwif; + struct pci_dev *pdev = hwif->pdev; + struct hinic_sq *sq = txq->sq; + int err; + + tx_napi_add(txq, nic_dev->tx_weight); + + hinic_hwdev_msix_set(nic_dev->hwdev, sq->msix_entry, + TX_IRQ_NO_PENDING, TX_IRQ_NO_COALESC, + TX_IRQ_NO_LLI_TIMER, TX_IRQ_NO_CREDIT, + TX_IRQ_NO_RESEND_TIMER); + + err = request_irq(sq->irq, tx_irq, 0, txq->irq_name, txq); + if (err) { + dev_err(&pdev->dev, "Failed to request Tx irq\n"); + tx_napi_del(txq); + return err; + } + + return 0; +} + +static void tx_free_irq(struct hinic_txq *txq) +{ + struct hinic_sq *sq = txq->sq; + + free_irq(sq->irq, txq); + tx_napi_del(txq); +} + +/** + * hinic_init_txq - Initialize the Tx Queue + * @txq: Logical Tx Queue + * @sq: Hardware Tx Queue to connect the Logical queue with + * @netdev: network device to connect the Logical queue with + * + * Return 0 - Success, negative - Failure + **/ +int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq, + struct net_device *netdev) +{ + struct hinic_qp *qp = container_of(sq, struct hinic_qp, sq); + struct hinic_dev *nic_dev = netdev_priv(netdev); + struct hinic_hwdev *hwdev = nic_dev->hwdev; + int err, irqname_len; + size_t sges_size; + + txq->netdev = netdev; + txq->sq = sq; + + txq_stats_init(txq); + + txq->max_sges = HINIC_MAX_SQ_BUFDESCS; + + sges_size = txq->max_sges * sizeof(*txq->sges); + txq->sges = devm_kzalloc(&netdev->dev, sges_size, GFP_KERNEL); + if (!txq->sges) + return -ENOMEM; + + sges_size = txq->max_sges * sizeof(*txq->free_sges); + txq->free_sges = devm_kzalloc(&netdev->dev, sges_size, GFP_KERNEL); + if (!txq->free_sges) { + err = -ENOMEM; + goto err_alloc_free_sges; + } + + irqname_len = snprintf(NULL, 0, "hinic_txq%d", qp->q_id) + 1; + txq->irq_name = devm_kzalloc(&netdev->dev, irqname_len, GFP_KERNEL); + if (!txq->irq_name) { + err = -ENOMEM; + goto err_alloc_irqname; + } + + sprintf(txq->irq_name, "hinic_txq%d", qp->q_id); + + err = hinic_hwdev_hw_ci_addr_set(hwdev, sq, CI_UPDATE_NO_PENDING, + CI_UPDATE_NO_COALESC); + if (err) + goto err_hw_ci; + + err = tx_request_irq(txq); + if (err) { + netdev_err(netdev, "Failed to request Tx irq\n"); + goto err_req_tx_irq; + } + + return 0; + +err_req_tx_irq: +err_hw_ci: + devm_kfree(&netdev->dev, txq->irq_name); + +err_alloc_irqname: + devm_kfree(&netdev->dev, txq->free_sges); + +err_alloc_free_sges: + devm_kfree(&netdev->dev, txq->sges); + return err; +} + +/** + * hinic_clean_txq - Clean the Tx Queue + * @txq: Logical Tx Queue + **/ +void hinic_clean_txq(struct hinic_txq *txq) +{ + struct net_device *netdev = txq->netdev; + + tx_free_irq(txq); + + free_all_tx_skbs(txq); + + devm_kfree(&netdev->dev, txq->irq_name); + devm_kfree(&netdev->dev, txq->free_sges); + devm_kfree(&netdev->dev, txq->sges); +} diff --git a/drivers/net/ethernet/huawei/hinic/hinic_tx.h b/drivers/net/ethernet/huawei/hinic/hinic_tx.h new file mode 100644 index 000000000000..1fa55dce5aa7 --- /dev/null +++ b/drivers/net/ethernet/huawei/hinic/hinic_tx.h @@ -0,0 +1,62 @@ +/* + * Huawei HiNIC PCI Express Linux driver + * Copyright(c) 2017 Huawei Technologies Co., Ltd + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + */ + +#ifndef HINIC_TX_H +#define HINIC_TX_H + +#include <linux/types.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/u64_stats_sync.h> + +#include "hinic_common.h" +#include "hinic_hw_qp.h" + +struct hinic_txq_stats { + u64 pkts; + u64 bytes; + u64 tx_busy; + u64 tx_wake; + u64 tx_dropped; + + struct u64_stats_sync syncp; +}; + +struct hinic_txq { + struct net_device *netdev; + struct hinic_sq *sq; + + struct hinic_txq_stats txq_stats; + + int max_sges; + struct hinic_sge *sges; + struct hinic_sge *free_sges; + + char *irq_name; + struct napi_struct napi; +}; + +void hinic_txq_clean_stats(struct hinic_txq *txq); + +void hinic_txq_get_stats(struct hinic_txq *txq, struct hinic_txq_stats *stats); + +netdev_tx_t hinic_xmit_frame(struct sk_buff *skb, struct net_device *netdev); + +int hinic_init_txq(struct hinic_txq *txq, struct hinic_sq *sq, + struct net_device *netdev); + +void hinic_clean_txq(struct hinic_txq *txq); + +#endif diff --git a/drivers/net/ethernet/ibm/ehea/ehea_main.c b/drivers/net/ethernet/ibm/ehea/ehea_main.c index b9d310f20bcc..4878b7169e0f 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_main.c +++ b/drivers/net/ethernet/ibm/ehea/ehea_main.c @@ -3102,8 +3102,7 @@ static int ehea_setup_ports(struct ehea_adapter *adapter) dn_log_port_id = of_get_property(eth_dn, "ibm,hea-port-no", NULL); if (!dn_log_port_id) { - pr_err("bad device node: eth_dn name=%s\n", - eth_dn->full_name); + pr_err("bad device node: eth_dn name=%pOF\n", eth_dn); continue; } @@ -3425,7 +3424,7 @@ static int ehea_probe_adapter(struct platform_device *dev) if (!adapter->handle) { dev_err(&dev->dev, "failed getting handle for adapter" - " '%s'\n", dev->dev.of_node->full_name); + " '%pOF'\n", dev->dev.of_node); ret = -ENODEV; goto out_free_ad; } diff --git a/drivers/net/ethernet/ibm/emac/core.c b/drivers/net/ethernet/ibm/emac/core.c index 259e69a52ec5..7feff2450ed6 100644 --- a/drivers/net/ethernet/ibm/emac/core.c +++ b/drivers/net/ethernet/ibm/emac/core.c @@ -133,8 +133,7 @@ static inline void emac_report_timeout_error(struct emac_instance *dev, EMAC_FTR_440EP_PHY_CLK_FIX)) DBG(dev, "%s" NL, error); else if (net_ratelimit()) - printk(KERN_ERR "%s: %s\n", dev->ofdev->dev.of_node->full_name, - error); + printk(KERN_ERR "%pOF: %s\n", dev->ofdev->dev.of_node, error); } /* EMAC PHY clock workaround: @@ -2258,8 +2257,8 @@ static void emac_ethtool_get_drvinfo(struct net_device *ndev, strlcpy(info->driver, "ibm_emac", sizeof(info->driver)); strlcpy(info->version, DRV_VERSION, sizeof(info->version)); - snprintf(info->bus_info, sizeof(info->bus_info), "PPC 4xx EMAC-%d %s", - dev->cell_index, dev->ofdev->dev.of_node->full_name); + snprintf(info->bus_info, sizeof(info->bus_info), "PPC 4xx EMAC-%d %pOF", + dev->cell_index, dev->ofdev->dev.of_node); } static const struct ethtool_ops emac_ethtool_ops = { @@ -2431,8 +2430,8 @@ static int emac_read_uint_prop(struct device_node *np, const char *name, const u32 *prop = of_get_property(np, name, &len); if (prop == NULL || len < sizeof(u32)) { if (fatal) - printk(KERN_ERR "%s: missing %s property\n", - np->full_name, name); + printk(KERN_ERR "%pOF: missing %s property\n", + np, name); return -ENODEV; } *val = *prop; @@ -2768,7 +2767,7 @@ static int emac_init_phy(struct emac_instance *dev) #endif mutex_unlock(&emac_phy_map_lock); if (i == 0x20) { - printk(KERN_WARNING "%s: can't find PHY!\n", np->full_name); + printk(KERN_WARNING "%pOF: can't find PHY!\n", np); return -ENXIO; } @@ -2894,8 +2893,8 @@ static int emac_init_config(struct emac_instance *dev) #ifdef CONFIG_IBM_EMAC_NO_FLOW_CTRL dev->features |= EMAC_FTR_NO_FLOW_CONTROL_40x; #else - printk(KERN_ERR "%s: Flow control not disabled!\n", - np->full_name); + printk(KERN_ERR "%pOF: Flow control not disabled!\n", + np); return -ENXIO; #endif } @@ -2918,8 +2917,7 @@ static int emac_init_config(struct emac_instance *dev) #ifdef CONFIG_IBM_EMAC_TAH dev->features |= EMAC_FTR_HAS_TAH; #else - printk(KERN_ERR "%s: TAH support not enabled !\n", - np->full_name); + printk(KERN_ERR "%pOF: TAH support not enabled !\n", np); return -ENXIO; #endif } @@ -2928,8 +2926,7 @@ static int emac_init_config(struct emac_instance *dev) #ifdef CONFIG_IBM_EMAC_ZMII dev->features |= EMAC_FTR_HAS_ZMII; #else - printk(KERN_ERR "%s: ZMII support not enabled !\n", - np->full_name); + printk(KERN_ERR "%pOF: ZMII support not enabled !\n", np); return -ENXIO; #endif } @@ -2938,8 +2935,7 @@ static int emac_init_config(struct emac_instance *dev) #ifdef CONFIG_IBM_EMAC_RGMII dev->features |= EMAC_FTR_HAS_RGMII; #else - printk(KERN_ERR "%s: RGMII support not enabled !\n", - np->full_name); + printk(KERN_ERR "%pOF: RGMII support not enabled !\n", np); return -ENXIO; #endif } @@ -2947,8 +2943,8 @@ static int emac_init_config(struct emac_instance *dev) /* Read MAC-address */ p = of_get_property(np, "local-mac-address", NULL); if (p == NULL) { - printk(KERN_ERR "%s: Can't find local-mac-address property\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't find local-mac-address property\n", + np); return -ENXIO; } memcpy(dev->ndev->dev_addr, p, ETH_ALEN); @@ -3036,30 +3032,24 @@ static int emac_probe(struct platform_device *ofdev) /* Init various config data based on device-tree */ err = emac_init_config(dev); - if (err != 0) + if (err) goto err_free; /* Get interrupts. EMAC irq is mandatory, WOL irq is optional */ dev->emac_irq = irq_of_parse_and_map(np, 0); dev->wol_irq = irq_of_parse_and_map(np, 1); if (!dev->emac_irq) { - printk(KERN_ERR "%s: Can't map main interrupt\n", np->full_name); + printk(KERN_ERR "%pOF: Can't map main interrupt\n", np); + err = -ENODEV; goto err_free; } ndev->irq = dev->emac_irq; /* Map EMAC regs */ - if (of_address_to_resource(np, 0, &dev->rsrc_regs)) { - printk(KERN_ERR "%s: Can't get registers address\n", - np->full_name); - goto err_irq_unmap; - } - // TODO : request_mem_region - dev->emacp = ioremap(dev->rsrc_regs.start, - resource_size(&dev->rsrc_regs)); + // TODO : platform_get_resource() and devm_ioremap_resource() + dev->emacp = of_iomap(np, 0); if (dev->emacp == NULL) { - printk(KERN_ERR "%s: Can't map device registers!\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't map device registers!\n", np); err = -ENOMEM; goto err_irq_unmap; } @@ -3068,8 +3058,7 @@ static int emac_probe(struct platform_device *ofdev) err = emac_wait_deps(dev); if (err) { printk(KERN_ERR - "%s: Timeout waiting for dependent devices\n", - np->full_name); + "%pOF: Timeout waiting for dependent devices\n", np); /* display more info about what's missing ? */ goto err_reg_unmap; } @@ -3084,8 +3073,8 @@ static int emac_probe(struct platform_device *ofdev) dev->commac.rx_chan_mask = MAL_CHAN_MASK(dev->mal_rx_chan); err = mal_register_commac(dev->mal, &dev->commac); if (err) { - printk(KERN_ERR "%s: failed to register with mal %s!\n", - np->full_name, dev->mal_dev->dev.of_node->full_name); + printk(KERN_ERR "%pOF: failed to register with mal %pOF!\n", + np, dev->mal_dev->dev.of_node); goto err_rel_deps; } dev->rx_skb_size = emac_rx_skb_size(ndev->mtu); @@ -3161,8 +3150,8 @@ static int emac_probe(struct platform_device *ofdev) err = register_netdev(ndev); if (err) { - printk(KERN_ERR "%s: failed to register net device (%d)!\n", - np->full_name, err); + printk(KERN_ERR "%pOF: failed to register net device (%d)!\n", + np, err); goto err_detach_tah; } @@ -3176,8 +3165,8 @@ static int emac_probe(struct platform_device *ofdev) wake_up_all(&emac_probe_wait); - printk(KERN_INFO "%s: EMAC-%d %s, MAC %pM\n", - ndev->name, dev->cell_index, np->full_name, ndev->dev_addr); + printk(KERN_INFO "%s: EMAC-%d %pOF, MAC %pM\n", + ndev->name, dev->cell_index, np, ndev->dev_addr); if (dev->phy_mode == PHY_MODE_SGMII) printk(KERN_NOTICE "%s: in SGMII mode\n", ndev->name); diff --git a/drivers/net/ethernet/ibm/emac/core.h b/drivers/net/ethernet/ibm/emac/core.h index f10e156641d5..369de2cfb15b 100644 --- a/drivers/net/ethernet/ibm/emac/core.h +++ b/drivers/net/ethernet/ibm/emac/core.h @@ -167,7 +167,6 @@ struct emac_error_stats { struct emac_instance { struct net_device *ndev; - struct resource rsrc_regs; struct emac_regs __iomem *emacp; struct platform_device *ofdev; struct device_node **blist; /* bootlist entry */ diff --git a/drivers/net/ethernet/ibm/emac/debug.h b/drivers/net/ethernet/ibm/emac/debug.h index 5bdfc174a07e..9d06d3be3161 100644 --- a/drivers/net/ethernet/ibm/emac/debug.h +++ b/drivers/net/ethernet/ibm/emac/debug.h @@ -31,7 +31,7 @@ #endif #define EMAC_DBG(d, name, fmt, arg...) \ - printk(KERN_DEBUG #name "%s: " fmt, d->ofdev->dev.of_node->full_name, ## arg) + printk(KERN_DEBUG #name "%pOF: " fmt, d->ofdev->dev.of_node, ## arg) #if DBG_LEVEL > 0 # define DBG(d,f,x...) EMAC_DBG(d, emac, f, ##x) diff --git a/drivers/net/ethernet/ibm/emac/mal.c b/drivers/net/ethernet/ibm/emac/mal.c index 91b1a558f37d..2c74baa2398a 100644 --- a/drivers/net/ethernet/ibm/emac/mal.c +++ b/drivers/net/ethernet/ibm/emac/mal.c @@ -579,8 +579,8 @@ static int mal_probe(struct platform_device *ofdev) mal->features |= (MAL_FTR_CLEAR_ICINTSTAT | MAL_FTR_COMMON_ERR_INT); #else - printk(KERN_ERR "%s: Support for 405EZ not enabled!\n", - ofdev->dev.of_node->full_name); + printk(KERN_ERR "%pOF: Support for 405EZ not enabled!\n", + ofdev->dev.of_node); err = -ENODEV; goto fail; #endif @@ -687,8 +687,8 @@ static int mal_probe(struct platform_device *ofdev) mal_enable_eob_irq(mal); printk(KERN_INFO - "MAL v%d %s, %d TX channels, %d RX channels\n", - mal->version, ofdev->dev.of_node->full_name, + "MAL v%d %pOF, %d TX channels, %d RX channels\n", + mal->version, ofdev->dev.of_node, mal->num_tx_chans, mal->num_rx_chans); /* Advertise this instance to the rest of the world */ diff --git a/drivers/net/ethernet/ibm/emac/rgmii.c b/drivers/net/ethernet/ibm/emac/rgmii.c index 206ccbbae7bb..c4a1ac38bba8 100644 --- a/drivers/net/ethernet/ibm/emac/rgmii.c +++ b/drivers/net/ethernet/ibm/emac/rgmii.c @@ -104,8 +104,8 @@ int rgmii_attach(struct platform_device *ofdev, int input, int mode) /* Check if we need to attach to a RGMII */ if (input < 0 || !rgmii_valid_mode(mode)) { - printk(KERN_ERR "%s: unsupported settings !\n", - ofdev->dev.of_node->full_name); + printk(KERN_ERR "%pOF: unsupported settings !\n", + ofdev->dev.of_node); return -ENODEV; } @@ -114,8 +114,8 @@ int rgmii_attach(struct platform_device *ofdev, int input, int mode) /* Enable this input */ out_be32(&p->fer, in_be32(&p->fer) | rgmii_mode_mask(mode, input)); - printk(KERN_NOTICE "%s: input %d in %s mode\n", - ofdev->dev.of_node->full_name, input, rgmii_mode_name(mode)); + printk(KERN_NOTICE "%pOF: input %d in %s mode\n", + ofdev->dev.of_node, input, rgmii_mode_name(mode)); ++dev->users; @@ -249,8 +249,7 @@ static int rgmii_probe(struct platform_device *ofdev) rc = -ENXIO; if (of_address_to_resource(np, 0, ®s)) { - printk(KERN_ERR "%s: Can't get registers address\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't get registers address\n", np); goto err_free; } @@ -258,8 +257,7 @@ static int rgmii_probe(struct platform_device *ofdev) dev->base = (struct rgmii_regs __iomem *)ioremap(regs.start, sizeof(struct rgmii_regs)); if (dev->base == NULL) { - printk(KERN_ERR "%s: Can't map device registers!\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't map device registers!\n", np); goto err_free; } @@ -278,8 +276,8 @@ static int rgmii_probe(struct platform_device *ofdev) out_be32(&dev->base->fer, 0); printk(KERN_INFO - "RGMII %s initialized with%s MDIO support\n", - ofdev->dev.of_node->full_name, + "RGMII %pOF initialized with%s MDIO support\n", + ofdev->dev.of_node, (dev->flags & EMAC_RGMII_FLAG_HAS_MDIO) ? "" : "out"); wmb(); diff --git a/drivers/net/ethernet/ibm/emac/tah.c b/drivers/net/ethernet/ibm/emac/tah.c index 32cb6c9007c5..9912456dca48 100644 --- a/drivers/net/ethernet/ibm/emac/tah.c +++ b/drivers/net/ethernet/ibm/emac/tah.c @@ -58,8 +58,7 @@ void tah_reset(struct platform_device *ofdev) --n; if (unlikely(!n)) - printk(KERN_ERR "%s: reset timeout\n", - ofdev->dev.of_node->full_name); + printk(KERN_ERR "%pOF: reset timeout\n", ofdev->dev.of_node); /* 10KB TAH TX FIFO accommodates the max MTU of 9000 */ out_be32(&p->mr, @@ -105,8 +104,7 @@ static int tah_probe(struct platform_device *ofdev) rc = -ENXIO; if (of_address_to_resource(np, 0, ®s)) { - printk(KERN_ERR "%s: Can't get registers address\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't get registers address\n", np); goto err_free; } @@ -114,8 +112,7 @@ static int tah_probe(struct platform_device *ofdev) dev->base = (struct tah_regs __iomem *)ioremap(regs.start, sizeof(struct tah_regs)); if (dev->base == NULL) { - printk(KERN_ERR "%s: Can't map device registers!\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't map device registers!\n", np); goto err_free; } @@ -124,8 +121,7 @@ static int tah_probe(struct platform_device *ofdev) /* Initialize TAH and enable IPv4 checksum verification, no TSO yet */ tah_reset(ofdev); - printk(KERN_INFO - "TAH %s initialized\n", ofdev->dev.of_node->full_name); + printk(KERN_INFO "TAH %pOF initialized\n", ofdev->dev.of_node); wmb(); return 0; diff --git a/drivers/net/ethernet/ibm/emac/zmii.c b/drivers/net/ethernet/ibm/emac/zmii.c index 8727b865ea02..89c42d362292 100644 --- a/drivers/net/ethernet/ibm/emac/zmii.c +++ b/drivers/net/ethernet/ibm/emac/zmii.c @@ -121,15 +121,15 @@ int zmii_attach(struct platform_device *ofdev, int input, int *mode) } else dev->mode = *mode; - printk(KERN_NOTICE "%s: bridge in %s mode\n", - ofdev->dev.of_node->full_name, + printk(KERN_NOTICE "%pOF: bridge in %s mode\n", + ofdev->dev.of_node, zmii_mode_name(dev->mode)); } else { /* All inputs must use the same mode */ if (*mode != PHY_MODE_NA && *mode != dev->mode) { printk(KERN_ERR - "%s: invalid mode %d specified for input %d\n", - ofdev->dev.of_node->full_name, *mode, input); + "%pOF: invalid mode %d specified for input %d\n", + ofdev->dev.of_node, *mode, input); mutex_unlock(&dev->lock); return -EINVAL; } @@ -250,8 +250,7 @@ static int zmii_probe(struct platform_device *ofdev) rc = -ENXIO; if (of_address_to_resource(np, 0, ®s)) { - printk(KERN_ERR "%s: Can't get registers address\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't get registers address\n", np); goto err_free; } @@ -259,8 +258,7 @@ static int zmii_probe(struct platform_device *ofdev) dev->base = (struct zmii_regs __iomem *)ioremap(regs.start, sizeof(struct zmii_regs)); if (dev->base == NULL) { - printk(KERN_ERR "%s: Can't map device registers!\n", - np->full_name); + printk(KERN_ERR "%pOF: Can't map device registers!\n", np); goto err_free; } @@ -270,8 +268,7 @@ static int zmii_probe(struct platform_device *ofdev) /* Disable all inputs by default */ out_be32(&dev->base->fer, 0); - printk(KERN_INFO - "ZMII %s initialized\n", ofdev->dev.of_node->full_name); + printk(KERN_INFO "ZMII %pOF initialized\n", ofdev->dev.of_node); wmb(); platform_set_drvdata(ofdev, dev); diff --git a/drivers/net/ethernet/ibm/ibmveth.c b/drivers/net/ethernet/ibm/ibmveth.c index d17c2b03f580..f210398200ec 100644 --- a/drivers/net/ethernet/ibm/ibmveth.c +++ b/drivers/net/ethernet/ibm/ibmveth.c @@ -1897,7 +1897,7 @@ static int ibmveth_resume(struct device *dev) return 0; } -static struct vio_device_id ibmveth_device_table[] = { +static const struct vio_device_id ibmveth_device_table[] = { { "network", "IBM,l-lan"}, { "", "" } }; diff --git a/drivers/net/ethernet/ibm/ibmvnic.c b/drivers/net/ethernet/ibm/ibmvnic.c index c45e8e3b82d3..cb8182f4fdfa 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.c +++ b/drivers/net/ethernet/ibm/ibmvnic.c @@ -347,6 +347,31 @@ static void replenish_pools(struct ibmvnic_adapter *adapter) } } +static void release_stats_buffers(struct ibmvnic_adapter *adapter) +{ + kfree(adapter->tx_stats_buffers); + kfree(adapter->rx_stats_buffers); +} + +static int init_stats_buffers(struct ibmvnic_adapter *adapter) +{ + adapter->tx_stats_buffers = + kcalloc(adapter->req_tx_queues, + sizeof(struct ibmvnic_tx_queue_stats), + GFP_KERNEL); + if (!adapter->tx_stats_buffers) + return -ENOMEM; + + adapter->rx_stats_buffers = + kcalloc(adapter->req_rx_queues, + sizeof(struct ibmvnic_rx_queue_stats), + GFP_KERNEL); + if (!adapter->rx_stats_buffers) + return -ENOMEM; + + return 0; +} + static void release_stats_token(struct ibmvnic_adapter *adapter) { struct device *dev = &adapter->vdev->dev; @@ -374,6 +399,7 @@ static int init_stats_token(struct ibmvnic_adapter *adapter) } adapter->stats_token = stok; + netdev_dbg(adapter->netdev, "Stats token initialized (%llx)\n", stok); return 0; } @@ -387,6 +413,8 @@ static int reset_rx_pools(struct ibmvnic_adapter *adapter) for (i = 0; i < rx_scrqs; i++) { rx_pool = &adapter->rx_pool[i]; + netdev_dbg(adapter->netdev, "Re-setting rx_pool[%d]\n", i); + rc = reset_long_term_buff(adapter, &rx_pool->long_term_buff); if (rc) return rc; @@ -419,6 +447,8 @@ static void release_rx_pools(struct ibmvnic_adapter *adapter) for (i = 0; i < rx_scrqs; i++) { rx_pool = &adapter->rx_pool[i]; + netdev_dbg(adapter->netdev, "Releasing rx_pool[%d]\n", i); + kfree(rx_pool->free_map); free_long_term_buff(adapter, &rx_pool->long_term_buff); @@ -465,7 +495,7 @@ static int init_rx_pools(struct net_device *netdev) rx_pool = &adapter->rx_pool[i]; netdev_dbg(adapter->netdev, - "Initializing rx_pool %d, %lld buffs, %lld bytes each\n", + "Initializing rx_pool[%d], %lld buffs, %lld bytes each\n", i, adapter->req_rx_add_entries_per_subcrq, be64_to_cpu(size_array[i])); @@ -515,6 +545,8 @@ static int reset_tx_pools(struct ibmvnic_adapter *adapter) tx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); for (i = 0; i < tx_scrqs; i++) { + netdev_dbg(adapter->netdev, "Re-setting tx_pool[%d]\n", i); + tx_pool = &adapter->tx_pool[i]; rc = reset_long_term_buff(adapter, &tx_pool->long_term_buff); @@ -545,6 +577,7 @@ static void release_tx_pools(struct ibmvnic_adapter *adapter) tx_scrqs = be32_to_cpu(adapter->login_rsp_buf->num_txsubm_subcrqs); for (i = 0; i < tx_scrqs; i++) { + netdev_dbg(adapter->netdev, "Releasing tx_pool[%d]\n", i); tx_pool = &adapter->tx_pool[i]; kfree(tx_pool->tx_buff); free_long_term_buff(adapter, &tx_pool->long_term_buff); @@ -571,6 +604,11 @@ static int init_tx_pools(struct net_device *netdev) for (i = 0; i < tx_subcrqs; i++) { tx_pool = &adapter->tx_pool[i]; + + netdev_dbg(adapter->netdev, + "Initializing tx_pool[%d], %lld buffs\n", + i, adapter->req_tx_entries_per_subcrq); + tx_pool->tx_buff = kcalloc(adapter->req_tx_entries_per_subcrq, sizeof(struct ibmvnic_tx_buff), GFP_KERNEL); @@ -641,8 +679,10 @@ static void ibmvnic_napi_disable(struct ibmvnic_adapter *adapter) if (!adapter->napi_enabled) return; - for (i = 0; i < adapter->req_rx_queues; i++) + for (i = 0; i < adapter->req_rx_queues; i++) { + netdev_dbg(adapter->netdev, "Disabling napi[%d]\n", i); napi_disable(&adapter->napi[i]); + } adapter->napi_enabled = false; } @@ -700,12 +740,16 @@ static void release_resources(struct ibmvnic_adapter *adapter) release_rx_pools(adapter); release_stats_token(adapter); + release_stats_buffers(adapter); release_error_buffers(adapter); if (adapter->napi) { for (i = 0; i < adapter->req_rx_queues; i++) { - if (&adapter->napi[i]) + if (&adapter->napi[i]) { + netdev_dbg(adapter->netdev, + "Releasing napi[%d]\n", i); netif_napi_del(&adapter->napi[i]); + } } } } @@ -718,7 +762,8 @@ static int set_link_state(struct ibmvnic_adapter *adapter, u8 link_state) bool resend; int rc; - netdev_err(netdev, "setting link state %d\n", link_state); + netdev_dbg(netdev, "setting link state %d\n", link_state); + memset(&crq, 0, sizeof(crq)); crq.logical_link_state.first = IBMVNIC_CRQ_CMD; crq.logical_link_state.cmd = LOGICAL_LINK_STATE; @@ -755,6 +800,9 @@ static int set_real_num_queues(struct net_device *netdev) struct ibmvnic_adapter *adapter = netdev_priv(netdev); int rc; + netdev_dbg(netdev, "Setting real tx/rx queues (%llx/%llx)\n", + adapter->req_tx_queues, adapter->req_rx_queues); + rc = netif_set_real_num_tx_queues(netdev, adapter->req_tx_queues); if (rc) { netdev_err(netdev, "failed to set the number of tx queues\n"); @@ -777,6 +825,10 @@ static int init_resources(struct ibmvnic_adapter *adapter) if (rc) return rc; + rc = init_stats_buffers(adapter); + if (rc) + return rc; + rc = init_stats_token(adapter); if (rc) return rc; @@ -788,6 +840,7 @@ static int init_resources(struct ibmvnic_adapter *adapter) return -ENOMEM; for (i = 0; i < adapter->req_rx_queues; i++) { + netdev_dbg(netdev, "Adding napi[%d]\n", i); netif_napi_add(netdev, &adapter->napi[i], ibmvnic_poll, NAPI_POLL_WEIGHT); } @@ -816,6 +869,7 @@ static int __ibmvnic_open(struct net_device *netdev) * set the logical link state to up */ for (i = 0; i < adapter->req_rx_queues; i++) { + netdev_dbg(netdev, "Enabling rx_scrq[%d] irq\n", i); if (prev_state == VNIC_CLOSED) enable_irq(adapter->rx_scrq[i]->irq); else @@ -823,6 +877,7 @@ static int __ibmvnic_open(struct net_device *netdev) } for (i = 0; i < adapter->req_tx_queues; i++) { + netdev_dbg(netdev, "Enabling tx_scrq[%d] irq\n", i); if (prev_state == VNIC_CLOSED) enable_irq(adapter->tx_scrq[i]->irq); else @@ -896,6 +951,7 @@ static void clean_tx_pools(struct ibmvnic_adapter *adapter) if (!tx_pool) continue; + netdev_dbg(adapter->netdev, "Cleaning tx_pool[%d]\n", i); for (j = 0; j < tx_entries; j++) { if (tx_pool->tx_buff[j].skb) { dev_kfree_skb_any(tx_pool->tx_buff[j].skb); @@ -923,8 +979,11 @@ static int __ibmvnic_close(struct net_device *netdev) if (adapter->tx_scrq) { for (i = 0; i < adapter->req_tx_queues; i++) - if (adapter->tx_scrq[i]->irq) + if (adapter->tx_scrq[i]->irq) { + netdev_dbg(adapter->netdev, + "Disabling tx_scrq[%d] irq\n", i); disable_irq(adapter->tx_scrq[i]->irq); + } } rc = set_link_state(adapter, IBMVNIC_LOGICAL_LNK_DN); @@ -943,8 +1002,11 @@ static int __ibmvnic_close(struct net_device *netdev) break; } - if (adapter->rx_scrq[i]->irq) + if (adapter->rx_scrq[i]->irq) { + netdev_dbg(adapter->netdev, + "Disabling rx_scrq[%d] irq\n", i); disable_irq(adapter->rx_scrq[i]->irq); + } } } @@ -1259,6 +1321,9 @@ out: netdev->stats.tx_packets += tx_packets; adapter->tx_send_failed += tx_send_failed; adapter->tx_map_failed += tx_map_failed; + adapter->tx_stats_buffers[queue_num].packets += tx_packets; + adapter->tx_stats_buffers[queue_num].bytes += tx_bytes; + adapter->tx_stats_buffers[queue_num].dropped_packets += tx_dropped; return ret; } @@ -1334,6 +1399,9 @@ static int do_reset(struct ibmvnic_adapter *adapter, struct net_device *netdev = adapter->netdev; int i, rc; + netdev_dbg(adapter->netdev, "Re-setting driver (%d)\n", + rwi->reset_reason); + netif_carrier_off(netdev); adapter->reset_reason = rwi->reset_reason; @@ -1458,6 +1526,7 @@ static void __ibmvnic_reset(struct work_struct *work) } if (rc) { + netdev_dbg(adapter->netdev, "Reset failed\n"); free_all_rwi(adapter); mutex_unlock(&adapter->reset_lock); return; @@ -1491,7 +1560,7 @@ static void ibmvnic_reset(struct ibmvnic_adapter *adapter, list_for_each(entry, &adapter->rwi_list) { tmp = list_entry(entry, struct ibmvnic_rwi, list); if (tmp->reset_reason == reason) { - netdev_err(netdev, "Matching reset found, skipping\n"); + netdev_dbg(netdev, "Skipping matching reset\n"); mutex_unlock(&adapter->rwi_lock); return; } @@ -1507,6 +1576,8 @@ static void ibmvnic_reset(struct ibmvnic_adapter *adapter, rwi->reset_reason = reason; list_add_tail(&rwi->list, &adapter->rwi_list); mutex_unlock(&adapter->rwi_lock); + + netdev_dbg(adapter->netdev, "Scheduling reset (reason %d)\n", reason); schedule_work(&adapter->ibmvnic_reset); } @@ -1560,7 +1631,8 @@ restart_poll: rx_comp.correlator); /* do error checking */ if (next->rx_comp.rc) { - netdev_err(netdev, "rx error %x\n", next->rx_comp.rc); + netdev_dbg(netdev, "rx buffer returned with rc %x\n", + be16_to_cpu(next->rx_comp.rc)); /* free the entry */ next->rx_comp.first = 0; remove_buff_from_pool(adapter, rx_buff); @@ -1599,6 +1671,8 @@ restart_poll: napi_gro_receive(napi, skb); /* send it up */ netdev->stats.rx_packets++; netdev->stats.rx_bytes += length; + adapter->rx_stats_buffers[scrq_num].packets++; + adapter->rx_stats_buffers[scrq_num].bytes += length; frames_processed++; } @@ -1708,18 +1782,36 @@ static u32 ibmvnic_get_link(struct net_device *netdev) static void ibmvnic_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) { - ring->rx_max_pending = 0; - ring->tx_max_pending = 0; + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + + ring->rx_max_pending = adapter->max_rx_add_entries_per_subcrq; + ring->tx_max_pending = adapter->max_tx_entries_per_subcrq; ring->rx_mini_max_pending = 0; ring->rx_jumbo_max_pending = 0; - ring->rx_pending = 0; - ring->tx_pending = 0; + ring->rx_pending = adapter->req_rx_add_entries_per_subcrq; + ring->tx_pending = adapter->req_tx_entries_per_subcrq; ring->rx_mini_pending = 0; ring->rx_jumbo_pending = 0; } +static void ibmvnic_get_channels(struct net_device *netdev, + struct ethtool_channels *channels) +{ + struct ibmvnic_adapter *adapter = netdev_priv(netdev); + + channels->max_rx = adapter->max_rx_queues; + channels->max_tx = adapter->max_tx_queues; + channels->max_other = 0; + channels->max_combined = 0; + channels->rx_count = adapter->req_rx_queues; + channels->tx_count = adapter->req_tx_queues; + channels->other_count = 0; + channels->combined_count = 0; +} + static void ibmvnic_get_strings(struct net_device *dev, u32 stringset, u8 *data) { + struct ibmvnic_adapter *adapter = netdev_priv(dev); int i; if (stringset != ETH_SS_STATS) @@ -1727,13 +1819,39 @@ static void ibmvnic_get_strings(struct net_device *dev, u32 stringset, u8 *data) for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++, data += ETH_GSTRING_LEN) memcpy(data, ibmvnic_stats[i].name, ETH_GSTRING_LEN); + + for (i = 0; i < adapter->req_tx_queues; i++) { + snprintf(data, ETH_GSTRING_LEN, "tx%d_packets", i); + data += ETH_GSTRING_LEN; + + snprintf(data, ETH_GSTRING_LEN, "tx%d_bytes", i); + data += ETH_GSTRING_LEN; + + snprintf(data, ETH_GSTRING_LEN, "tx%d_dropped_packets", i); + data += ETH_GSTRING_LEN; + } + + for (i = 0; i < adapter->req_rx_queues; i++) { + snprintf(data, ETH_GSTRING_LEN, "rx%d_packets", i); + data += ETH_GSTRING_LEN; + + snprintf(data, ETH_GSTRING_LEN, "rx%d_bytes", i); + data += ETH_GSTRING_LEN; + + snprintf(data, ETH_GSTRING_LEN, "rx%d_interrupts", i); + data += ETH_GSTRING_LEN; + } } static int ibmvnic_get_sset_count(struct net_device *dev, int sset) { + struct ibmvnic_adapter *adapter = netdev_priv(dev); + switch (sset) { case ETH_SS_STATS: - return ARRAY_SIZE(ibmvnic_stats); + return ARRAY_SIZE(ibmvnic_stats) + + adapter->req_tx_queues * NUM_TX_STATS + + adapter->req_rx_queues * NUM_RX_STATS; default: return -EOPNOTSUPP; } @@ -1744,7 +1862,7 @@ static void ibmvnic_get_ethtool_stats(struct net_device *dev, { struct ibmvnic_adapter *adapter = netdev_priv(dev); union ibmvnic_crq crq; - int i; + int i, j; memset(&crq, 0, sizeof(crq)); crq.request_statistics.first = IBMVNIC_CRQ_CMD; @@ -1759,7 +1877,26 @@ static void ibmvnic_get_ethtool_stats(struct net_device *dev, wait_for_completion(&adapter->stats_done); for (i = 0; i < ARRAY_SIZE(ibmvnic_stats); i++) - data[i] = IBMVNIC_GET_STAT(adapter, ibmvnic_stats[i].offset); + data[i] = be64_to_cpu(IBMVNIC_GET_STAT(adapter, + ibmvnic_stats[i].offset)); + + for (j = 0; j < adapter->req_tx_queues; j++) { + data[i] = adapter->tx_stats_buffers[j].packets; + i++; + data[i] = adapter->tx_stats_buffers[j].bytes; + i++; + data[i] = adapter->tx_stats_buffers[j].dropped_packets; + i++; + } + + for (j = 0; j < adapter->req_rx_queues; j++) { + data[i] = adapter->rx_stats_buffers[j].packets; + i++; + data[i] = adapter->rx_stats_buffers[j].bytes; + i++; + data[i] = adapter->rx_stats_buffers[j].interrupts; + i++; + } } static const struct ethtool_ops ibmvnic_ethtool_ops = { @@ -1768,6 +1905,7 @@ static const struct ethtool_ops ibmvnic_ethtool_ops = { .set_msglevel = ibmvnic_set_msglevel, .get_link = ibmvnic_get_link, .get_ringparam = ibmvnic_get_ringparam, + .get_channels = ibmvnic_get_channels, .get_strings = ibmvnic_get_strings, .get_sset_count = ibmvnic_get_sset_count, .get_ethtool_stats = ibmvnic_get_ethtool_stats, @@ -1800,12 +1938,14 @@ static int reset_sub_crq_queues(struct ibmvnic_adapter *adapter) int i, rc; for (i = 0; i < adapter->req_tx_queues; i++) { + netdev_dbg(adapter->netdev, "Re-setting tx_scrq[%d]\n", i); rc = reset_one_sub_crq_queue(adapter, adapter->tx_scrq[i]); if (rc) return rc; } for (i = 0; i < adapter->req_rx_queues; i++) { + netdev_dbg(adapter->netdev, "Re-setting rx_scrq[%d]\n", i); rc = reset_one_sub_crq_queue(adapter, adapter->rx_scrq[i]); if (rc) return rc; @@ -1909,6 +2049,8 @@ static void release_sub_crqs(struct ibmvnic_adapter *adapter) if (!adapter->tx_scrq[i]) continue; + netdev_dbg(adapter->netdev, "Releasing tx_scrq[%d]\n", + i); if (adapter->tx_scrq[i]->irq) { free_irq(adapter->tx_scrq[i]->irq, adapter->tx_scrq[i]); @@ -1928,6 +2070,8 @@ static void release_sub_crqs(struct ibmvnic_adapter *adapter) if (!adapter->rx_scrq[i]) continue; + netdev_dbg(adapter->netdev, "Releasing rx_scrq[%d]\n", + i); if (adapter->rx_scrq[i]->irq) { free_irq(adapter->rx_scrq[i]->irq, adapter->rx_scrq[i]); @@ -2064,6 +2208,8 @@ static irqreturn_t ibmvnic_interrupt_rx(int irq, void *instance) struct ibmvnic_sub_crq_queue *scrq = instance; struct ibmvnic_adapter *adapter = scrq->adapter; + adapter->rx_stats_buffers[scrq->scrq_num].interrupts++; + if (napi_schedule_prep(&adapter->napi[scrq->scrq_num])) { disable_scrq_irq(adapter, scrq); __napi_schedule(&adapter->napi[scrq->scrq_num]); @@ -2080,6 +2226,8 @@ static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter) int rc = 0; for (i = 0; i < adapter->req_tx_queues; i++) { + netdev_dbg(adapter->netdev, "Initializing tx_scrq[%d] irq\n", + i); scrq = adapter->tx_scrq[i]; scrq->irq = irq_create_mapping(NULL, scrq->hw_irq); @@ -2101,6 +2249,8 @@ static int init_sub_crq_irqs(struct ibmvnic_adapter *adapter) } for (i = 0; i < adapter->req_rx_queues; i++) { + netdev_dbg(adapter->netdev, "Initializing rx_scrq[%d] irq\n", + i); scrq = adapter->rx_scrq[i]; scrq->irq = irq_create_mapping(NULL, scrq->hw_irq); if (!scrq->irq) { @@ -3739,31 +3889,35 @@ static int ibmvnic_probe(struct vio_dev *dev, const struct vio_device_id *id) do { rc = ibmvnic_init(adapter); - if (rc && rc != EAGAIN) { - free_netdev(netdev); - return rc; - } + if (rc && rc != EAGAIN) + goto ibmvnic_init_fail; } while (rc == EAGAIN); netdev->mtu = adapter->req_mtu - ETH_HLEN; rc = device_create_file(&dev->dev, &dev_attr_failover); - if (rc) { - free_netdev(netdev); - return rc; - } + if (rc) + goto ibmvnic_init_fail; rc = register_netdev(netdev); if (rc) { dev_err(&dev->dev, "failed to register netdev rc=%d\n", rc); - device_remove_file(&dev->dev, &dev_attr_failover); - free_netdev(netdev); - return rc; + goto ibmvnic_register_fail; } dev_info(&dev->dev, "ibmvnic registered\n"); adapter->state = VNIC_PROBED; return 0; + +ibmvnic_register_fail: + device_remove_file(&dev->dev, &dev_attr_failover); + +ibmvnic_init_fail: + release_sub_crqs(adapter); + release_crq_queue(adapter); + free_netdev(netdev); + + return rc; } static int ibmvnic_remove(struct vio_dev *dev) @@ -3859,20 +4013,16 @@ static int ibmvnic_resume(struct device *dev) { struct net_device *netdev = dev_get_drvdata(dev); struct ibmvnic_adapter *adapter = netdev_priv(netdev); - int i; if (adapter->state != VNIC_OPEN) return 0; - /* kick the interrupt handlers just in case we lost an interrupt */ - for (i = 0; i < adapter->req_rx_queues; i++) - ibmvnic_interrupt_rx(adapter->rx_scrq[i]->irq, - adapter->rx_scrq[i]); + tasklet_schedule(&adapter->tasklet); return 0; } -static struct vio_device_id ibmvnic_device_table[] = { +static const struct vio_device_id ibmvnic_device_table[] = { {"network", "IBM,vnic"}, {"", "" } }; diff --git a/drivers/net/ethernet/ibm/ibmvnic.h b/drivers/net/ethernet/ibm/ibmvnic.h index 8eff6e15f4bb..d02257ccc377 100644 --- a/drivers/net/ethernet/ibm/ibmvnic.h +++ b/drivers/net/ethernet/ibm/ibmvnic.h @@ -166,6 +166,20 @@ struct ibmvnic_statistics { u8 reserved[72]; } __packed __aligned(8); +#define NUM_TX_STATS 3 +struct ibmvnic_tx_queue_stats { + u64 packets; + u64 bytes; + u64 dropped_packets; +}; + +#define NUM_RX_STATS 3 +struct ibmvnic_rx_queue_stats { + u64 packets; + u64 bytes; + u64 interrupts; +}; + struct ibmvnic_acl_buffer { __be32 len; __be32 version; @@ -956,6 +970,9 @@ struct ibmvnic_adapter { int tx_send_failed; int tx_map_failed; + struct ibmvnic_tx_queue_stats *tx_stats_buffers; + struct ibmvnic_rx_queue_stats *rx_stats_buffers; + int phys_link_state; int logical_link_state; diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h index 66bd5060a65b..d803b1a12349 100644 --- a/drivers/net/ethernet/intel/e1000e/hw.h +++ b/drivers/net/ethernet/intel/e1000e/hw.h @@ -100,6 +100,10 @@ struct e1000_hw; #define E1000_DEV_ID_PCH_CNP_I219_V6 0x15BE #define E1000_DEV_ID_PCH_CNP_I219_LM7 0x15BB #define E1000_DEV_ID_PCH_CNP_I219_V7 0x15BC +#define E1000_DEV_ID_PCH_ICP_I219_LM8 0x15DF +#define E1000_DEV_ID_PCH_ICP_I219_V8 0x15E0 +#define E1000_DEV_ID_PCH_ICP_I219_LM9 0x15E1 +#define E1000_DEV_ID_PCH_ICP_I219_V9 0x15E2 #define E1000_REVISION_4 4 diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index 68ea8b4555ab..d6d4ed7acf03 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -2437,6 +2437,8 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw) if (hw->phy.revision < 2) { e1000e_phy_sw_reset(hw); ret_val = e1e_wphy(hw, MII_BMCR, 0x3140); + if (ret_val) + return ret_val; } } diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index 2dcb5463d9b8..327dfe5bedc0 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -7544,6 +7544,10 @@ static const struct pci_device_id e1000_pci_tbl[] = { { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_V6), board_pch_cnp }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_LM7), board_pch_cnp }, { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_CNP_I219_V7), board_pch_cnp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_LM8), board_pch_cnp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_V8), board_pch_cnp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_LM9), board_pch_cnp }, + { PCI_VDEVICE(INTEL, E1000_DEV_ID_PCH_ICP_I219_V9), board_pch_cnp }, { 0, 0, 0, 0, 0, 0, 0 } /* terminate list */ }; diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 5e37387c7082..e69d49d91d67 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1265,15 +1265,17 @@ err_queueing_scheme: return err; } -static int __fm10k_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +static int __fm10k_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) { - if (tc->type != TC_SETUP_MQPRIO) - return -EINVAL; + struct tc_mqprio_qopt *mqprio = type_data; + + if (type != TC_SETUP_MQPRIO) + return -EOPNOTSUPP; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - return fm10k_setup_tc(dev, tc->mqprio->num_tc); + return fm10k_setup_tc(dev, mqprio->num_tc); } static void fm10k_assign_l2_accel(struct fm10k_intfc *interface, diff --git a/drivers/net/ethernet/intel/i40e/i40e.h b/drivers/net/ethernet/intel/i40e/i40e.h index d616f698e155..d0c1bf5441d8 100644 --- a/drivers/net/ethernet/intel/i40e/i40e.h +++ b/drivers/net/ethernet/intel/i40e/i40e.h @@ -75,11 +75,11 @@ #define I40E_MIN_VSI_ALLOC 83 /* LAN, ATR, FCOE, 64 VF */ /* max 16 qps */ #define i40e_default_queues_per_vmdq(pf) \ - (((pf)->flags & I40E_FLAG_RSS_AQ_CAPABLE) ? 4 : 1) + (((pf)->hw_features & I40E_HW_RSS_AQ_CAPABLE) ? 4 : 1) #define I40E_DEFAULT_QUEUES_PER_VF 4 #define I40E_DEFAULT_QUEUES_PER_TC 1 /* should be a power of 2 */ #define i40e_pf_get_max_q_per_tc(pf) \ - (((pf)->flags & I40E_FLAG_128_QP_RSS_CAPABLE) ? 128 : 64) + (((pf)->hw_features & I40E_HW_128_QP_RSS_CAPABLE) ? 128 : 64) #define I40E_FDIR_RING 0 #define I40E_FDIR_RING_COUNT 32 #define I40E_MAX_AQ_BUF_SIZE 4096 @@ -401,6 +401,27 @@ struct i40e_pf { struct timer_list service_timer; struct work_struct service_task; + u64 hw_features; +#define I40E_HW_RSS_AQ_CAPABLE BIT_ULL(0) +#define I40E_HW_128_QP_RSS_CAPABLE BIT_ULL(1) +#define I40E_HW_ATR_EVICT_CAPABLE BIT_ULL(2) +#define I40E_HW_WB_ON_ITR_CAPABLE BIT_ULL(3) +#define I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE BIT_ULL(4) +#define I40E_HW_NO_PCI_LINK_CHECK BIT_ULL(5) +#define I40E_HW_100M_SGMII_CAPABLE BIT_ULL(6) +#define I40E_HW_NO_DCB_SUPPORT BIT_ULL(7) +#define I40E_HW_USE_SET_LLDP_MIB BIT_ULL(8) +#define I40E_HW_GENEVE_OFFLOAD_CAPABLE BIT_ULL(9) +#define I40E_HW_PTP_L4_CAPABLE BIT_ULL(10) +#define I40E_HW_WOL_MC_MAGIC_PKT_WAKE BIT_ULL(11) +#define I40E_HW_MPLS_HDR_OFFLOAD_CAPABLE BIT_ULL(12) +#define I40E_HW_HAVE_CRT_RETIMER BIT_ULL(13) +#define I40E_HW_OUTER_UDP_CSUM_CAPABLE BIT_ULL(14) +#define I40E_HW_PHY_CONTROLS_LEDS BIT_ULL(15) +#define I40E_HW_STOP_FW_LLDP BIT_ULL(16) +#define I40E_HW_PORT_ID_VALID BIT_ULL(17) +#define I40E_HW_RESTART_AUTONEG BIT_ULL(18) + u64 flags; #define I40E_FLAG_RX_CSUM_ENABLED BIT_ULL(1) #define I40E_FLAG_MSI_ENABLED BIT_ULL(2) @@ -420,33 +441,14 @@ struct i40e_pf { #define I40E_FLAG_PTP BIT_ULL(25) #define I40E_FLAG_MFP_ENABLED BIT_ULL(26) #define I40E_FLAG_UDP_FILTER_SYNC BIT_ULL(27) -#define I40E_FLAG_PORT_ID_VALID BIT_ULL(28) #define I40E_FLAG_DCB_CAPABLE BIT_ULL(29) -#define I40E_FLAG_RSS_AQ_CAPABLE BIT_ULL(31) -#define I40E_FLAG_HW_ATR_EVICT_CAPABLE BIT_ULL(32) -#define I40E_FLAG_OUTER_UDP_CSUM_CAPABLE BIT_ULL(33) -#define I40E_FLAG_128_QP_RSS_CAPABLE BIT_ULL(34) -#define I40E_FLAG_WB_ON_ITR_CAPABLE BIT_ULL(35) #define I40E_FLAG_VEB_STATS_ENABLED BIT_ULL(37) -#define I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE BIT_ULL(38) #define I40E_FLAG_LINK_POLLING_ENABLED BIT_ULL(39) #define I40E_FLAG_VEB_MODE_ENABLED BIT_ULL(40) -#define I40E_FLAG_GENEVE_OFFLOAD_CAPABLE BIT_ULL(41) -#define I40E_FLAG_NO_PCI_LINK_CHECK BIT_ULL(42) -#define I40E_FLAG_100M_SGMII_CAPABLE BIT_ULL(43) -#define I40E_FLAG_RESTART_AUTONEG BIT_ULL(44) -#define I40E_FLAG_NO_DCB_SUPPORT BIT_ULL(45) -#define I40E_FLAG_USE_SET_LLDP_MIB BIT_ULL(46) -#define I40E_FLAG_STOP_FW_LLDP BIT_ULL(47) -#define I40E_FLAG_PHY_CONTROLS_LEDS BIT_ULL(48) -#define I40E_FLAG_PF_MAC BIT_ULL(50) #define I40E_FLAG_TRUE_PROMISC_SUPPORT BIT_ULL(51) -#define I40E_FLAG_HAVE_CRT_RETIMER BIT_ULL(52) -#define I40E_FLAG_PTP_L4_CAPABLE BIT_ULL(53) #define I40E_FLAG_CLIENT_RESET BIT_ULL(54) #define I40E_FLAG_TEMP_LINK_POLLING BIT_ULL(55) #define I40E_FLAG_CLIENT_L2_CHANGE BIT_ULL(56) -#define I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE BIT_ULL(57) #define I40E_FLAG_LEGACY_RX BIT_ULL(58) struct i40e_client_instance *cinst; diff --git a/drivers/net/ethernet/intel/i40e/i40e_common.c b/drivers/net/ethernet/intel/i40e/i40e_common.c index 8e082a946411..111426ba5fbc 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_common.c +++ b/drivers/net/ethernet/intel/i40e/i40e_common.c @@ -328,9 +328,9 @@ void i40e_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc, len = buf_len; /* write the full 16-byte chunks */ if (hw->debug_mask & mask) { - char prefix[20]; + char prefix[27]; - snprintf(prefix, 20, + snprintf(prefix, sizeof(prefix), "i40e %02x:%02x.%x: \t0x", hw->bus.bus_id, hw->bus.device, @@ -2529,6 +2529,10 @@ i40e_status i40e_update_link_info(struct i40e_hw *hw) if (status) return status; + hw->phy.link_info.req_fec_info = + abilities.fec_cfg_curr_mod_ext_info & + (I40E_AQ_REQUEST_FEC_KR | I40E_AQ_REQUEST_FEC_RS); + memcpy(hw->phy.link_info.module_type, &abilities.module_type, sizeof(hw->phy.link_info.module_type)); } diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 9692a5294fa3..05e89864f781 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -271,7 +271,7 @@ static void i40e_phy_type_to_ethtool(struct i40e_pf *pf, u32 *supported, *advertising |= ADVERTISED_Autoneg; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) *advertising |= ADVERTISED_1000baseT_Full; - if (pf->flags & I40E_FLAG_100M_SGMII_CAPABLE) { + if (pf->hw_features & I40E_HW_100M_SGMII_CAPABLE) { *supported |= SUPPORTED_100baseT_Full; *advertising |= ADVERTISED_100baseT_Full; } @@ -340,12 +340,12 @@ static void i40e_phy_type_to_ethtool(struct i40e_pf *pf, u32 *supported, *advertising |= ADVERTISED_20000baseKR2_Full; } if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_KR) { - if (!(pf->flags & I40E_FLAG_HAVE_CRT_RETIMER)) + if (!(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) *supported |= SUPPORTED_10000baseKR_Full | SUPPORTED_Autoneg; *advertising |= ADVERTISED_Autoneg; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_10GB) - if (!(pf->flags & I40E_FLAG_HAVE_CRT_RETIMER)) + if (!(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) *advertising |= ADVERTISED_10000baseKR_Full; } if (phy_types & I40E_CAP_PHY_TYPE_10GBASE_KX4) { @@ -356,12 +356,12 @@ static void i40e_phy_type_to_ethtool(struct i40e_pf *pf, u32 *supported, *advertising |= ADVERTISED_10000baseKX4_Full; } if (phy_types & I40E_CAP_PHY_TYPE_1000BASE_KX) { - if (!(pf->flags & I40E_FLAG_HAVE_CRT_RETIMER)) + if (!(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) *supported |= SUPPORTED_1000baseKX_Full | SUPPORTED_Autoneg; *advertising |= ADVERTISED_Autoneg; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) - if (!(pf->flags & I40E_FLAG_HAVE_CRT_RETIMER)) + if (!(pf->hw_features & I40E_HW_HAVE_CRT_RETIMER)) *advertising |= ADVERTISED_1000baseKX_Full; } if (phy_types & I40E_CAP_PHY_TYPE_25GBASE_KR || @@ -474,7 +474,7 @@ static void i40e_get_settings_link_up(struct i40e_hw *hw, SUPPORTED_1000baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_1GB) advertising |= ADVERTISED_1000baseT_Full; - if (pf->flags & I40E_FLAG_100M_SGMII_CAPABLE) { + if (pf->hw_features & I40E_HW_100M_SGMII_CAPABLE) { supported |= SUPPORTED_100baseT_Full; if (hw_link_info->requested_speeds & I40E_LINK_SPEED_100MB) @@ -1091,7 +1091,7 @@ static void i40e_get_regs(struct net_device *netdev, struct ethtool_regs *regs, struct i40e_pf *pf = np->vsi->back; struct i40e_hw *hw = &pf->hw; u32 *reg_buf = p; - int i, j, ri; + unsigned int i, j, ri; u32 reg; /* Tell ethtool which driver-version-specific regs output we have. @@ -1550,9 +1550,9 @@ static void i40e_get_ethtool_stats(struct net_device *netdev, struct i40e_ring *tx_ring, *rx_ring; struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; + unsigned int j; int i = 0; char *p; - int j; struct rtnl_link_stats64 *net_stats = i40e_get_vsi_stats_struct(vsi); unsigned int start; @@ -1637,7 +1637,7 @@ static void i40e_get_strings(struct net_device *netdev, u32 stringset, struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; char *p = (char *)data; - int i; + unsigned int i; switch (stringset) { case ETH_SS_TEST: @@ -1765,7 +1765,7 @@ static int i40e_get_ts_info(struct net_device *dev, BIT(HWTSTAMP_FILTER_PTP_V2_L2_SYNC) | BIT(HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ); - if (pf->flags & I40E_FLAG_PTP_L4_CAPABLE) + if (pf->hw_features & I40E_HW_PTP_L4_CAPABLE) info->rx_filters |= BIT(HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | BIT(HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ) | BIT(HWTSTAMP_FILTER_PTP_V2_EVENT) | @@ -2005,7 +2005,7 @@ static int i40e_set_phys_id(struct net_device *netdev, switch (state) { case ETHTOOL_ID_ACTIVE: - if (!(pf->flags & I40E_FLAG_PHY_CONTROLS_LEDS)) { + if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) { pf->led_status = i40e_led_get(hw); } else { i40e_aq_set_phy_debug(hw, I40E_PHY_DEBUG_ALL, NULL); @@ -2015,19 +2015,19 @@ static int i40e_set_phys_id(struct net_device *netdev, } return blink_freq; case ETHTOOL_ID_ON: - if (!(pf->flags & I40E_FLAG_PHY_CONTROLS_LEDS)) + if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) i40e_led_set(hw, 0xf, false); else ret = i40e_led_set_phy(hw, true, pf->led_status, 0); break; case ETHTOOL_ID_OFF: - if (!(pf->flags & I40E_FLAG_PHY_CONTROLS_LEDS)) + if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) i40e_led_set(hw, 0x0, false); else ret = i40e_led_set_phy(hw, false, pf->led_status, 0); break; case ETHTOOL_ID_INACTIVE: - if (!(pf->flags & I40E_FLAG_PHY_CONTROLS_LEDS)) { + if (!(pf->hw_features & I40E_HW_PHY_CONTROLS_LEDS)) { i40e_led_set(hw, pf->led_status, false); } else { ret = i40e_led_set_phy(hw, false, pf->led_status, @@ -2194,14 +2194,29 @@ static int __i40e_set_coalesce(struct net_device *netdev, int queue) { struct i40e_netdev_priv *np = netdev_priv(netdev); + u16 intrl_reg, cur_rx_itr, cur_tx_itr; struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - u16 intrl_reg; int i; if (ec->tx_max_coalesced_frames_irq || ec->rx_max_coalesced_frames_irq) vsi->work_limit = ec->tx_max_coalesced_frames_irq; + if (queue < 0) { + cur_rx_itr = vsi->rx_rings[0]->rx_itr_setting; + cur_tx_itr = vsi->tx_rings[0]->tx_itr_setting; + } else if (queue < vsi->num_queue_pairs) { + cur_rx_itr = vsi->rx_rings[queue]->rx_itr_setting; + cur_tx_itr = vsi->tx_rings[queue]->tx_itr_setting; + } else { + netif_info(pf, drv, netdev, "Invalid queue value, queue range is 0 - %d\n", + vsi->num_queue_pairs - 1); + return -EINVAL; + } + + cur_tx_itr &= ~I40E_ITR_DYNAMIC; + cur_rx_itr &= ~I40E_ITR_DYNAMIC; + /* tx_coalesce_usecs_high is ignored, use rx-usecs-high instead */ if (ec->tx_coalesce_usecs_high != vsi->int_rate_limit) { netif_info(pf, drv, netdev, "tx-usecs-high is not used, please program rx-usecs-high\n"); @@ -2214,15 +2229,34 @@ static int __i40e_set_coalesce(struct net_device *netdev, return -EINVAL; } - if (ec->rx_coalesce_usecs == 0) { - if (ec->use_adaptive_rx_coalesce) - netif_info(pf, drv, netdev, "rx-usecs=0, need to disable adaptive-rx for a complete disable\n"); - } else if ((ec->rx_coalesce_usecs < (I40E_MIN_ITR << 1)) || - (ec->rx_coalesce_usecs > (I40E_MAX_ITR << 1))) { - netif_info(pf, drv, netdev, "Invalid value, rx-usecs range is 0-8160\n"); - return -EINVAL; + if (ec->rx_coalesce_usecs != cur_rx_itr && + ec->use_adaptive_rx_coalesce) { + netif_info(pf, drv, netdev, "RX interrupt moderation cannot be changed if adaptive-rx is enabled.\n"); + return -EINVAL; } + if (ec->rx_coalesce_usecs > (I40E_MAX_ITR << 1)) { + netif_info(pf, drv, netdev, "Invalid value, rx-usecs range is 0-8160\n"); + return -EINVAL; + } + + if (ec->tx_coalesce_usecs != cur_tx_itr && + ec->use_adaptive_tx_coalesce) { + netif_info(pf, drv, netdev, "TX interrupt moderation cannot be changed if adaptive-tx is enabled.\n"); + return -EINVAL; + } + + if (ec->tx_coalesce_usecs > (I40E_MAX_ITR << 1)) { + netif_info(pf, drv, netdev, "Invalid value, tx-usecs range is 0-8160\n"); + return -EINVAL; + } + + if (ec->use_adaptive_rx_coalesce && !cur_rx_itr) + ec->rx_coalesce_usecs = I40E_MIN_ITR << 1; + + if (ec->use_adaptive_tx_coalesce && !cur_tx_itr) + ec->tx_coalesce_usecs = I40E_MIN_ITR << 1; + intrl_reg = i40e_intrl_usec_to_reg(ec->rx_coalesce_usecs_high); vsi->int_rate_limit = INTRL_REG_TO_USEC(intrl_reg); if (vsi->int_rate_limit != ec->rx_coalesce_usecs_high) { @@ -2230,27 +2264,14 @@ static int __i40e_set_coalesce(struct net_device *netdev, vsi->int_rate_limit); } - if (ec->tx_coalesce_usecs == 0) { - if (ec->use_adaptive_tx_coalesce) - netif_info(pf, drv, netdev, "tx-usecs=0, need to disable adaptive-tx for a complete disable\n"); - } else if ((ec->tx_coalesce_usecs < (I40E_MIN_ITR << 1)) || - (ec->tx_coalesce_usecs > (I40E_MAX_ITR << 1))) { - netif_info(pf, drv, netdev, "Invalid value, tx-usecs range is 0-8160\n"); - return -EINVAL; - } - /* rx and tx usecs has per queue value. If user doesn't specify the queue, * apply to all queues. */ if (queue < 0) { for (i = 0; i < vsi->num_queue_pairs; i++) i40e_set_itr_per_queue(vsi, ec, i); - } else if (queue < vsi->num_queue_pairs) { - i40e_set_itr_per_queue(vsi, ec, queue); } else { - netif_info(pf, drv, netdev, "Invalid queue value, queue range is 0 - %d\n", - vsi->num_queue_pairs - 1); - return -EINVAL; + i40e_set_itr_per_queue(vsi, ec, queue); } return 0; @@ -2727,22 +2748,22 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc) switch (nfc->flow_type) { case TCP_V4_FLOW: flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV4_TCP; - if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) + if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) hena |= BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK); break; case TCP_V6_FLOW: flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV6_TCP; - if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) + if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) hena |= BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV4_TCP_SYN_NO_ACK); - if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) + if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) hena |= BIT_ULL(I40E_FILTER_PCTYPE_NONF_IPV6_TCP_SYN_NO_ACK); break; case UDP_V4_FLOW: flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV4_UDP; - if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) + if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) hena |= BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV4_UDP) | BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV4_UDP); @@ -2751,7 +2772,7 @@ static int i40e_set_rss_hash_opt(struct i40e_pf *pf, struct ethtool_rxnfc *nfc) break; case UDP_V6_FLOW: flow_pctype = I40E_FILTER_PCTYPE_NONF_IPV6_UDP; - if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) + if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) hena |= BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP); @@ -4069,23 +4090,26 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) struct i40e_netdev_priv *np = netdev_priv(dev); struct i40e_vsi *vsi = np->vsi; struct i40e_pf *pf = vsi->back; - u64 changed_flags; + u64 orig_flags, new_flags, changed_flags; u32 i, j; - changed_flags = pf->flags; + orig_flags = READ_ONCE(pf->flags); + new_flags = orig_flags; for (i = 0; i < I40E_PRIV_FLAGS_STR_LEN; i++) { const struct i40e_priv_flags *priv_flags; priv_flags = &i40e_gstrings_priv_flags[i]; - if (priv_flags->read_only) - continue; - if (flags & BIT(i)) - pf->flags |= priv_flags->flag; + new_flags |= priv_flags->flag; else - pf->flags &= ~(priv_flags->flag); + new_flags &= ~(priv_flags->flag); + + /* If this is a read-only flag, it can't be changed */ + if (priv_flags->read_only && + ((orig_flags ^ new_flags) & ~BIT(i))) + return -EOPNOTSUPP; } if (pf->hw.pf_id != 0) @@ -4096,18 +4120,40 @@ static int i40e_set_priv_flags(struct net_device *dev, u32 flags) priv_flags = &i40e_gl_gstrings_priv_flags[j]; - if (priv_flags->read_only) - continue; - if (flags & BIT(i + j)) - pf->flags |= priv_flags->flag; + new_flags |= priv_flags->flag; else - pf->flags &= ~(priv_flags->flag); + new_flags &= ~(priv_flags->flag); + + /* If this is a read-only flag, it can't be changed */ + if (priv_flags->read_only && + ((orig_flags ^ new_flags) & ~BIT(i))) + return -EOPNOTSUPP; } flags_complete: - /* check for flags that changed */ - changed_flags ^= pf->flags; + /* Before we finalize any flag changes, we need to perform some + * checks to ensure that the changes are supported and safe. + */ + + /* ATR eviction is not supported on all devices */ + if ((new_flags & I40E_FLAG_HW_ATR_EVICT_ENABLED) && + !(pf->hw_features & I40E_HW_ATR_EVICT_CAPABLE)) + return -EOPNOTSUPP; + + /* Compare and exchange the new flags into place. If we failed, that + * is if cmpxchg64 returns anything but the old value, this means that + * something else has modified the flags variable since we copied it + * originally. We'll just punt with an error and log something in the + * message buffer. + */ + if (cmpxchg64(&pf->flags, orig_flags, new_flags) != orig_flags) { + dev_warn(&pf->pdev->dev, + "Unable to update pf->flags as it was modified by another thread...\n"); + return -EAGAIN; + } + + changed_flags = orig_flags ^ new_flags; /* Process any additional changes needed as a result of flag changes. * The changed_flags value reflects the list of bits that were @@ -4121,10 +4167,6 @@ flags_complete: set_bit(__I40E_FD_FLUSH_REQUESTED, pf->state); } - /* Only allow ATR evict on hardware that is capable of handling it */ - if (pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) - pf->flags &= ~I40E_FLAG_HW_ATR_EVICT_ENABLED; - if (changed_flags & I40E_FLAG_TRUE_PROMISC_SUPPORT) { u16 sw_flags = 0, valid_flags = 0; int ret; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 2db93d3f6d23..6498da8806cb 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -2595,9 +2595,20 @@ int i40e_vsi_add_vlan(struct i40e_vsi *vsi, u16 vid) { int err; - if (!vid || vsi->info.pvid) + if (vsi->info.pvid) return -EINVAL; + /* The network stack will attempt to add VID=0, with the intention to + * receive priority tagged packets with a VLAN of 0. Our HW receives + * these packets by default when configured to receive untagged + * packets, so we don't need to add a filter for this case. + * Additionally, HW interprets adding a VID=0 filter as meaning to + * receive *only* tagged traffic and stops receiving untagged traffic. + * Thus, we do not want to actually add a filter for VID=0 + */ + if (!vid) + return 0; + /* Locked once because all functions invoked below iterates list*/ spin_lock_bh(&vsi->mac_filter_hash_lock); err = i40e_add_vlan_all_mac(vsi, vid); @@ -2674,15 +2685,7 @@ static int i40e_vlan_rx_add_vid(struct net_device *netdev, if (vid >= VLAN_N_VID) return -EINVAL; - /* If the network stack called us with vid = 0 then - * it is asking to receive priority tagged packets with - * vlan id 0. Our HW receives them by default when configured - * to receive untagged packets so there is no need to add an - * extra filter for vlan 0 tagged packets. - */ - if (vid) - ret = i40e_vsi_add_vlan(vsi, vid); - + ret = i40e_vsi_add_vlan(vsi, vid); if (!ret) set_bit(vid, vsi->active_vlans); @@ -2714,44 +2717,6 @@ static int i40e_vlan_rx_kill_vid(struct net_device *netdev, } /** - * i40e_macaddr_init - explicitly write the mac address filters - * - * @vsi: pointer to the vsi - * @macaddr: the MAC address - * - * This is needed when the macaddr has been obtained by other - * means than the default, e.g., from Open Firmware or IDPROM. - * Returns 0 on success, negative on failure - **/ -static int i40e_macaddr_init(struct i40e_vsi *vsi, u8 *macaddr) -{ - int ret; - struct i40e_aqc_add_macvlan_element_data element; - - ret = i40e_aq_mac_address_write(&vsi->back->hw, - I40E_AQC_WRITE_TYPE_LAA_WOL, - macaddr, NULL); - if (ret) { - dev_info(&vsi->back->pdev->dev, - "Addr change for VSI failed: %d\n", ret); - return -EADDRNOTAVAIL; - } - - memset(&element, 0, sizeof(element)); - ether_addr_copy(element.mac_addr, macaddr); - element.flags = cpu_to_le16(I40E_AQC_MACVLAN_ADD_PERFECT_MATCH); - ret = i40e_aq_add_macvlan(&vsi->back->hw, vsi->seid, &element, 1, NULL); - if (ret) { - dev_info(&vsi->back->pdev->dev, - "add filter failed err %s aq_err %s\n", - i40e_stat_str(&vsi->back->hw, ret), - i40e_aq_str(&vsi->back->hw, - vsi->back->hw.aq.asq_last_status)); - } - return ret; -} - -/** * i40e_restore_vlan - Reinstate vlans when vsi/netdev comes back up * @vsi: the vsi being brought back up **/ @@ -2909,22 +2874,15 @@ static void i40e_vsi_free_rx_resources(struct i40e_vsi *vsi) static void i40e_config_xps_tx_ring(struct i40e_ring *ring) { struct i40e_vsi *vsi = ring->vsi; - cpumask_var_t mask; if (!ring->q_vector || !ring->netdev) return; - /* Single TC mode enable XPS */ - if (vsi->tc_config.numtc <= 1) { - if (!test_and_set_bit(__I40E_TX_XPS_INIT_DONE, &ring->state)) - netif_set_xps_queue(ring->netdev, - &ring->q_vector->affinity_mask, - ring->queue_index); - } else if (alloc_cpumask_var(&mask, GFP_KERNEL)) { - /* Disable XPS to allow selection based on TC */ - bitmap_zero(cpumask_bits(mask), nr_cpumask_bits); - netif_set_xps_queue(ring->netdev, mask, ring->queue_index); - free_cpumask_var(mask); + if ((vsi->tc_config.numtc <= 1) && + !test_and_set_bit(__I40E_TX_XPS_INIT_DONE, &ring->state)) { + netif_set_xps_queue(ring->netdev, + get_cpu_mask(ring->q_vector->v_idx), + ring->queue_index); } /* schedule our worker thread which will take care of @@ -3203,19 +3161,8 @@ static void i40e_vsi_config_dcb_rings(struct i40e_vsi *vsi) **/ static void i40e_set_vsi_rx_mode(struct i40e_vsi *vsi) { - struct i40e_pf *pf = vsi->back; - int err; - if (vsi->netdev) i40e_set_rx_mode(vsi->netdev); - - if (!!(pf->flags & I40E_FLAG_PF_MAC)) { - err = i40e_macaddr_init(vsi, pf->hw.mac.addr); - if (err) { - dev_warn(&pf->pdev->dev, - "could not set up macaddr; err %d\n", err); - } - } } /** @@ -3495,7 +3442,7 @@ static void i40e_irq_affinity_notify(struct irq_affinity_notify *notify, struct i40e_q_vector *q_vector = container_of(notify, struct i40e_q_vector, affinity_notify); - q_vector->affinity_mask = *mask; + cpumask_copy(&q_vector->affinity_mask, mask); } /** @@ -3559,8 +3506,10 @@ static int i40e_vsi_request_irq_msix(struct i40e_vsi *vsi, char *basename) q_vector->affinity_notify.notify = i40e_irq_affinity_notify; q_vector->affinity_notify.release = i40e_irq_affinity_release; irq_set_affinity_notifier(irq_num, &q_vector->affinity_notify); - /* assign the mask for this irq */ - irq_set_affinity_hint(irq_num, &q_vector->affinity_mask); + /* get_cpu_mask returns a static constant mask with + * a permanent lifetime so it's ok to use here. + */ + irq_set_affinity_hint(irq_num, get_cpu_mask(q_vector->v_idx)); } vsi->irqs_ready = true; @@ -4342,7 +4291,7 @@ static void i40e_vsi_free_irq(struct i40e_vsi *vsi) /* clear the affinity notifier in the IRQ descriptor */ irq_set_affinity_notifier(irq_num, NULL); - /* clear the affinity_mask in the IRQ descriptor */ + /* remove our suggested affinity mask for this IRQ */ irq_set_affinity_hint(irq_num, NULL); synchronize_irq(irq_num); free_irq(irq_num, vsi->q_vectors[i]); @@ -4773,7 +4722,7 @@ static void i40e_detect_recover_hung(struct i40e_pf *pf) { struct net_device *netdev; struct i40e_vsi *vsi; - int i; + unsigned int i; /* Only for LAN VSI */ vsi = pf->vsi[pf->lan_vsi]; @@ -5350,7 +5299,7 @@ static int i40e_init_pf_dcb(struct i40e_pf *pf) int err = 0; /* Do not enable DCB for SW1 and SW2 images even if the FW is capable */ - if (pf->flags & I40E_FLAG_NO_DCB_SUPPORT) + if (pf->hw_features & I40E_HW_NO_DCB_SUPPORT) goto out; /* Get the initial DCB configuration */ @@ -5400,6 +5349,7 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) char *speed = "Unknown"; char *fc = "Unknown"; char *fec = ""; + char *req_fec = ""; char *an = ""; new_speed = vsi->back->hw.phy.link_info.link_speed; @@ -5461,6 +5411,7 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) } if (vsi->back->hw.phy.link_info.link_speed == I40E_LINK_SPEED_25GB) { + req_fec = ", Requested FEC: None"; fec = ", FEC: None"; an = ", Autoneg: False"; @@ -5473,10 +5424,22 @@ void i40e_print_link_message(struct i40e_vsi *vsi, bool isup) else if (vsi->back->hw.phy.link_info.fec_info & I40E_AQ_CONFIG_FEC_RS_ENA) fec = ", FEC: CL108 RS-FEC"; + + /* 'CL108 RS-FEC' should be displayed when RS is requested, or + * both RS and FC are requested + */ + if (vsi->back->hw.phy.link_info.req_fec_info & + (I40E_AQ_REQUEST_FEC_KR | I40E_AQ_REQUEST_FEC_RS)) { + if (vsi->back->hw.phy.link_info.req_fec_info & + I40E_AQ_REQUEST_FEC_RS) + req_fec = ", Requested FEC: CL108 RS-FEC"; + else + req_fec = ", Requested FEC: CL74 FC-FEC/BASE-R"; + } } - netdev_info(vsi->netdev, "NIC Link is Up, %sbps Full Duplex%s%s, Flow Control: %s\n", - speed, fec, an, fc); + netdev_info(vsi->netdev, "NIC Link is Up, %sbps Full Duplex%s%s%s, Flow Control: %s\n", + speed, req_fec, fec, an, fc); } /** @@ -5656,16 +5619,17 @@ exit: return ret; } -static int __i40e_setup_tc(struct net_device *netdev, u32 handle, - u32 chain_index, __be16 proto, - struct tc_to_netdev *tc) +static int __i40e_setup_tc(struct net_device *netdev, enum tc_setup_type type, + void *type_data) { - if (tc->type != TC_SETUP_MQPRIO) - return -EINVAL; + struct tc_mqprio_qopt *mqprio = type_data; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + if (type != TC_SETUP_MQPRIO) + return -EOPNOTSUPP; - return i40e_setup_tc(netdev, tc->mqprio->num_tc); + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + + return i40e_setup_tc(netdev, mqprio->num_tc); } /** @@ -7331,7 +7295,7 @@ static void i40e_rebuild(struct i40e_pf *pf, bool reinit, bool lock_acquired) wr32(hw, I40E_REG_MSS, val); } - if (pf->flags & I40E_FLAG_RESTART_AUTONEG) { + if (pf->hw_features & I40E_HW_RESTART_AUTONEG) { msleep(75); ret = i40e_aq_set_link_restart_an(&pf->hw, true, NULL); if (ret) @@ -7520,6 +7484,18 @@ static void i40e_handle_mdd_event(struct i40e_pf *pf) i40e_flush(hw); } +static const char *i40e_tunnel_name(struct i40e_udp_port_config *port) +{ + switch (port->type) { + case UDP_TUNNEL_TYPE_VXLAN: + return "vxlan"; + case UDP_TUNNEL_TYPE_GENEVE: + return "geneve"; + default: + return "unknown"; + } +} + /** * i40e_sync_udp_filters - Trigger a sync event for existing UDP filters * @pf: board private structure @@ -7565,14 +7541,14 @@ static void i40e_sync_udp_filters_subtask(struct i40e_pf *pf) ret = i40e_aq_del_udp_tunnel(hw, i, NULL); if (ret) { - dev_dbg(&pf->pdev->dev, - "%s %s port %d, index %d failed, err %s aq_err %s\n", - pf->udp_ports[i].type ? "vxlan" : "geneve", - port ? "add" : "delete", - port, i, - i40e_stat_str(&pf->hw, ret), - i40e_aq_str(&pf->hw, - pf->hw.aq.asq_last_status)); + dev_info(&pf->pdev->dev, + "%s %s port %d, index %d failed, err %s aq_err %s\n", + i40e_tunnel_name(&pf->udp_ports[i]), + port ? "add" : "delete", + port, i, + i40e_stat_str(&pf->hw, ret), + i40e_aq_str(&pf->hw, + pf->hw.aq.asq_last_status)); pf->udp_ports[i].port = 0; } } @@ -7957,7 +7933,7 @@ static int i40e_alloc_rings(struct i40e_vsi *vsi) ring->count = vsi->num_desc; ring->size = 0; ring->dcb_tc = 0; - if (vsi->back->flags & I40E_FLAG_WB_ON_ITR_CAPABLE) + if (vsi->back->hw_features & I40E_HW_WB_ON_ITR_CAPABLE) ring->flags = I40E_TXR_FLAGS_WB_ON_ITR; ring->tx_itr_setting = pf->tx_itr_default; vsi->tx_rings[i] = ring++; @@ -7974,7 +7950,7 @@ static int i40e_alloc_rings(struct i40e_vsi *vsi) ring->count = vsi->num_desc; ring->size = 0; ring->dcb_tc = 0; - if (vsi->back->flags & I40E_FLAG_WB_ON_ITR_CAPABLE) + if (vsi->back->hw_features & I40E_HW_WB_ON_ITR_CAPABLE) ring->flags = I40E_TXR_FLAGS_WB_ON_ITR; set_ring_xdp(ring); ring->tx_itr_setting = pf->tx_itr_default; @@ -8261,7 +8237,7 @@ static int i40e_vsi_alloc_q_vector(struct i40e_vsi *vsi, int v_idx, int cpu) q_vector->vsi = vsi; q_vector->v_idx = v_idx; - cpumask_set_cpu(cpu, &q_vector->affinity_mask); + cpumask_copy(&q_vector->affinity_mask, cpu_possible_mask); if (vsi->netdev) netif_napi_add(vsi->netdev, &q_vector->napi, @@ -8510,7 +8486,7 @@ static int i40e_vsi_config_rss(struct i40e_vsi *vsi) u8 *lut; int ret; - if (!(pf->flags & I40E_FLAG_RSS_AQ_CAPABLE)) + if (!(pf->hw_features & I40E_HW_RSS_AQ_CAPABLE)) return 0; if (!vsi->rss_size) @@ -8640,7 +8616,7 @@ int i40e_config_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size) { struct i40e_pf *pf = vsi->back; - if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE) + if (pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) return i40e_config_rss_aq(vsi, seed, lut, lut_size); else return i40e_config_rss_reg(vsi, seed, lut, lut_size); @@ -8659,7 +8635,7 @@ int i40e_get_rss(struct i40e_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size) { struct i40e_pf *pf = vsi->back; - if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE) + if (pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) return i40e_get_rss_aq(vsi, seed, lut, lut_size); else return i40e_get_rss_reg(vsi, seed, lut, lut_size); @@ -8987,25 +8963,56 @@ static int i40e_sw_init(struct i40e_pf *pf) pf->hw.func_caps.fd_filters_best_effort; } + if (pf->hw.mac.type == I40E_MAC_X722) { + pf->hw_features |= (I40E_HW_RSS_AQ_CAPABLE | + I40E_HW_128_QP_RSS_CAPABLE | + I40E_HW_ATR_EVICT_CAPABLE | + I40E_HW_WB_ON_ITR_CAPABLE | + I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE | + I40E_HW_NO_PCI_LINK_CHECK | + I40E_HW_USE_SET_LLDP_MIB | + I40E_HW_GENEVE_OFFLOAD_CAPABLE | + I40E_HW_PTP_L4_CAPABLE | + I40E_HW_WOL_MC_MAGIC_PKT_WAKE | + I40E_HW_OUTER_UDP_CSUM_CAPABLE); + +#define I40E_FDEVICT_PCTYPE_DEFAULT 0xc03 + if (rd32(&pf->hw, I40E_GLQF_FDEVICTENA(1)) != + I40E_FDEVICT_PCTYPE_DEFAULT) { + dev_warn(&pf->pdev->dev, + "FD EVICT PCTYPES are not right, disable FD HW EVICT\n"); + pf->hw_features &= ~I40E_HW_ATR_EVICT_CAPABLE; + } + } else if ((pf->hw.aq.api_maj_ver > 1) || + ((pf->hw.aq.api_maj_ver == 1) && + (pf->hw.aq.api_min_ver > 4))) { + /* Supported in FW API version higher than 1.4 */ + pf->hw_features |= I40E_HW_GENEVE_OFFLOAD_CAPABLE; + } + + /* Enable HW ATR eviction if possible */ + if (pf->hw_features & I40E_HW_ATR_EVICT_CAPABLE) + pf->flags |= I40E_FLAG_HW_ATR_EVICT_ENABLED; + if ((pf->hw.mac.type == I40E_MAC_XL710) && (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 33)) || (pf->hw.aq.fw_maj_ver < 4))) { - pf->flags |= I40E_FLAG_RESTART_AUTONEG; + pf->hw_features |= I40E_HW_RESTART_AUTONEG; /* No DCB support for FW < v4.33 */ - pf->flags |= I40E_FLAG_NO_DCB_SUPPORT; + pf->hw_features |= I40E_HW_NO_DCB_SUPPORT; } /* Disable FW LLDP if FW < v4.3 */ if ((pf->hw.mac.type == I40E_MAC_XL710) && (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver < 3)) || (pf->hw.aq.fw_maj_ver < 4))) - pf->flags |= I40E_FLAG_STOP_FW_LLDP; + pf->hw_features |= I40E_HW_STOP_FW_LLDP; /* Use the FW Set LLDP MIB API if FW > v4.40 */ if ((pf->hw.mac.type == I40E_MAC_XL710) && (((pf->hw.aq.fw_maj_ver == 4) && (pf->hw.aq.fw_min_ver >= 40)) || (pf->hw.aq.fw_maj_ver >= 5))) - pf->flags |= I40E_FLAG_USE_SET_LLDP_MIB; + pf->hw_features |= I40E_HW_USE_SET_LLDP_MIB; if (pf->hw.func_caps.vmdq) { pf->num_vmdq_vsis = I40E_DEFAULT_NUM_VMDQ_VSI; @@ -9028,29 +9035,6 @@ static int i40e_sw_init(struct i40e_pf *pf) I40E_MAX_VF_COUNT); } #endif /* CONFIG_PCI_IOV */ - if (pf->hw.mac.type == I40E_MAC_X722) { - pf->flags |= I40E_FLAG_RSS_AQ_CAPABLE - | I40E_FLAG_128_QP_RSS_CAPABLE - | I40E_FLAG_HW_ATR_EVICT_CAPABLE - | I40E_FLAG_OUTER_UDP_CSUM_CAPABLE - | I40E_FLAG_WB_ON_ITR_CAPABLE - | I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE - | I40E_FLAG_NO_PCI_LINK_CHECK - | I40E_FLAG_USE_SET_LLDP_MIB - | I40E_FLAG_GENEVE_OFFLOAD_CAPABLE - | I40E_FLAG_PTP_L4_CAPABLE - | I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE; - } else if ((pf->hw.aq.api_maj_ver > 1) || - ((pf->hw.aq.api_maj_ver == 1) && - (pf->hw.aq.api_min_ver > 4))) { - /* Supported in FW API version higher than 1.4 */ - pf->flags |= I40E_FLAG_GENEVE_OFFLOAD_CAPABLE; - } - - /* Enable HW ATR eviction if possible */ - if (pf->flags & I40E_FLAG_HW_ATR_EVICT_CAPABLE) - pf->flags |= I40E_FLAG_HW_ATR_EVICT_ENABLED; - pf->eeprom_version = 0xDEAD; pf->lan_veb = I40E_NO_VEB; pf->lan_vsi = I40E_NO_VSI; @@ -9231,7 +9215,7 @@ static void i40e_udp_tunnel_add(struct net_device *netdev, pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_VXLAN; break; case UDP_TUNNEL_TYPE_GENEVE: - if (!(pf->flags & I40E_FLAG_GENEVE_OFFLOAD_CAPABLE)) + if (!(pf->hw_features & I40E_HW_GENEVE_OFFLOAD_CAPABLE)) return; pf->udp_ports[next_idx].type = I40E_AQC_TUNNEL_TYPE_NGE; break; @@ -9298,7 +9282,7 @@ static int i40e_get_phys_port_id(struct net_device *netdev, struct i40e_pf *pf = np->vsi->back; struct i40e_hw *hw = &pf->hw; - if (!(pf->flags & I40E_FLAG_PORT_ID_VALID)) + if (!(pf->hw_features & I40E_HW_PORT_ID_VALID)) return -EOPNOTSUPP; ppid->id_len = min_t(int, sizeof(hw->mac.port_addr), sizeof(ppid->id)); @@ -9589,6 +9573,7 @@ static int i40e_xdp(struct net_device *dev, return i40e_xdp_setup(vsi, xdp->prog); case XDP_QUERY_PROG: xdp->prog_attached = i40e_enabled_xdp_vsi(vsi); + xdp->prog_id = vsi->xdp_prog ? vsi->xdp_prog->aux->id : 0; return 0; default: return -EINVAL; @@ -9675,7 +9660,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) NETIF_F_RXCSUM | 0; - if (!(pf->flags & I40E_FLAG_OUTER_UDP_CSUM_CAPABLE)) + if (!(pf->hw_features & I40E_HW_OUTER_UDP_CSUM_CAPABLE)) netdev->gso_partial_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM; netdev->gso_partial_features |= NETIF_F_GSO_GRE_CSUM; @@ -9714,8 +9699,13 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) i40e_add_mac_filter(vsi, mac_addr); spin_unlock_bh(&vsi->mac_filter_hash_lock); } else { - /* relate the VSI_VMDQ name to the VSI_MAIN name */ - snprintf(netdev->name, IFNAMSIZ, "%sv%%d", + /* Relate the VSI_VMDQ name to the VSI_MAIN name. Note that we + * are still limited by IFNAMSIZ, but we're adding 'v%d\0' to + * the end, which is 4 bytes long, so force truncation of the + * original name by IFNAMSIZ - 4 + */ + snprintf(netdev->name, IFNAMSIZ, "%.*sv%%d", + IFNAMSIZ - 4, pf->vsi[pf->lan_vsi]->netdev->name); random_ether_addr(mac_addr); @@ -9756,8 +9746,7 @@ static int i40e_config_netdev(struct i40e_vsi *vsi) /* MTU range: 68 - 9706 */ netdev->min_mtu = ETH_MIN_MTU; - netdev->max_mtu = I40E_MAX_RXBUFFER - - (ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN); + netdev->max_mtu = I40E_MAX_RXBUFFER - I40E_PACKET_HDR_PAD; return 0; } @@ -9890,13 +9879,15 @@ static int i40e_add_vsi(struct i40e_vsi *vsi) */ ret = i40e_vsi_config_tc(vsi, enabled_tc); if (ret) { + /* Single TC condition is not fatal, + * message and continue + */ dev_info(&pf->pdev->dev, "failed to configure TCs for main VSI tc_map 0x%08x, err %s aq_err %s\n", enabled_tc, i40e_stat_str(&pf->hw, ret), i40e_aq_str(&pf->hw, pf->hw.aq.asq_last_status)); - ret = -ENOENT; } } break; @@ -10387,17 +10378,6 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, switch (vsi->type) { /* setup the netdev if needed */ case I40E_VSI_MAIN: - /* Apply relevant filters if a platform-specific mac - * address was selected. - */ - if (!!(pf->flags & I40E_FLAG_PF_MAC)) { - ret = i40e_macaddr_init(vsi, pf->hw.mac.addr); - if (ret) { - dev_warn(&pf->pdev->dev, - "could not set up macaddr; err %d\n", - ret); - } - } case I40E_VSI_VMDQ2: ret = i40e_config_netdev(vsi); if (ret) @@ -10434,7 +10414,7 @@ struct i40e_vsi *i40e_vsi_setup(struct i40e_pf *pf, u8 type, break; } - if ((pf->flags & I40E_FLAG_RSS_AQ_CAPABLE) && + if ((pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) && (vsi->type == I40E_VSI_VMDQ2)) { ret = i40e_vsi_config_rss(vsi); } @@ -11443,7 +11423,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * Ignore error return codes because if it was already disabled via * hardware settings this will fail */ - if (pf->flags & I40E_FLAG_STOP_FW_LLDP) { + if (pf->hw_features & I40E_HW_STOP_FW_LLDP) { dev_info(&pdev->dev, "Stopping firmware LLDP agent.\n"); i40e_aq_stop_lldp(hw, true, NULL); } @@ -11460,7 +11440,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) ether_addr_copy(hw->mac.perm_addr, hw->mac.addr); i40e_get_port_mac_addr(hw, hw->mac.port_addr); if (is_valid_ether_addr(hw->mac.port_addr)) - pf->flags |= I40E_FLAG_PORT_ID_VALID; + pf->hw_features |= I40E_HW_PORT_ID_VALID; pci_set_drvdata(pdev, pf); pci_save_state(pdev); @@ -11576,7 +11556,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) wr32(hw, I40E_REG_MSS, val); } - if (pf->flags & I40E_FLAG_RESTART_AUTONEG) { + if (pf->hw_features & I40E_HW_RESTART_AUTONEG) { msleep(75); err = i40e_aq_set_link_restart_an(&pf->hw, true, NULL); if (err) @@ -11663,7 +11643,7 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * and will report PCI Gen 1 x 1 by default so don't bother * checking them. */ - if (!(pf->flags & I40E_FLAG_NO_PCI_LINK_CHECK)) { + if (!(pf->hw_features & I40E_HW_NO_PCI_LINK_CHECK)) { char speed[PCI_SPEED_SIZE] = "Unknown"; char width[PCI_WIDTH_SIZE] = "Unknown"; @@ -11734,9 +11714,9 @@ static int i40e_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if ((pf->hw.device_id == I40E_DEV_ID_10G_BASE_T) || (pf->hw.device_id == I40E_DEV_ID_10G_BASE_T4)) - pf->flags |= I40E_FLAG_PHY_CONTROLS_LEDS; + pf->hw_features |= I40E_HW_PHY_CONTROLS_LEDS; if (pf->hw.device_id == I40E_DEV_ID_SFP_I_X722) - pf->flags |= I40E_FLAG_HAVE_CRT_RETIMER; + pf->hw_features |= I40E_HW_HAVE_CRT_RETIMER; /* print a string summarizing features */ i40e_print_features(pf); @@ -12048,7 +12028,7 @@ static void i40e_shutdown(struct pci_dev *pdev) */ i40e_notify_client_of_netdev_close(pf->vsi[pf->lan_vsi], false); - if (pf->wol_en && (pf->flags & I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE)) + if (pf->wol_en && (pf->hw_features & I40E_HW_WOL_MC_MAGIC_PKT_WAKE)) i40e_enable_mc_magic_wake(pf); i40e_prep_for_reset(pf, false); @@ -12080,7 +12060,7 @@ static int i40e_suspend(struct pci_dev *pdev, pm_message_t state) set_bit(__I40E_SUSPENDED, pf->state); set_bit(__I40E_DOWN, pf->state); - if (pf->wol_en && (pf->flags & I40E_FLAG_WOL_MC_MAGIC_PKT_WAKE)) + if (pf->wol_en && (pf->hw_features & I40E_HW_WOL_MC_MAGIC_PKT_WAKE)) i40e_enable_mc_magic_wake(pf); i40e_prep_for_reset(pf, false); @@ -12089,7 +12069,10 @@ static int i40e_suspend(struct pci_dev *pdev, pm_message_t state) wr32(hw, I40E_PFPM_WUFC, (pf->wol_en ? I40E_PFPM_WUFC_MAG_MASK : 0)); i40e_stop_misc_vector(pf); - + if (pf->msix_entries) { + synchronize_irq(pf->msix_entries[0].vector); + free_irq(pf->msix_entries[0].vector, pf); + } retval = pci_save_state(pdev); if (retval) return retval; @@ -12129,6 +12112,15 @@ static int i40e_resume(struct pci_dev *pdev) /* handling the reset will rebuild the device state */ if (test_and_clear_bit(__I40E_SUSPENDED, pf->state)) { clear_bit(__I40E_DOWN, pf->state); + if (pf->msix_entries) { + err = request_irq(pf->msix_entries[0].vector, + i40e_intr, 0, pf->int_name, pf); + if (err) { + dev_err(&pf->pdev->dev, + "request_irq for %s failed: %d\n", + pf->int_name, err); + } + } i40e_reset_and_rebuild(pf, false, false); } @@ -12168,12 +12160,14 @@ static int __init i40e_init_module(void) i40e_driver_string, i40e_driver_version_str); pr_info("%s: %s\n", i40e_driver_name, i40e_copyright); - /* we will see if single thread per module is enough for now, - * it can't be any worse than using the system workqueue which - * was already single threaded + /* There is no need to throttle the number of active tasks because + * each device limits its own task using a state bit for scheduling + * the service task, and the device tasks do not interfere with each + * other, so we don't set a max task limit. We must set WQ_MEM_RECLAIM + * since we need to be able to guarantee forward progress even under + * memory pressure. */ - i40e_wq = alloc_workqueue("%s", WQ_UNBOUND | WQ_MEM_RECLAIM, 1, - i40e_driver_name); + i40e_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, i40e_driver_name); if (!i40e_wq) { pr_err("%s: Failed to create workqueue\n", i40e_driver_name); return -ENOMEM; diff --git a/drivers/net/ethernet/intel/i40e/i40e_nvm.c b/drivers/net/ethernet/intel/i40e/i40e_nvm.c index 800bd55d0159..57505b1df98d 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_nvm.c +++ b/drivers/net/ethernet/intel/i40e/i40e_nvm.c @@ -134,8 +134,25 @@ i40e_i40e_acquire_nvm_exit: **/ void i40e_release_nvm(struct i40e_hw *hw) { - if (!hw->nvm.blank_nvm_mode) - i40e_aq_release_resource(hw, I40E_NVM_RESOURCE_ID, 0, NULL); + i40e_status ret_code = I40E_SUCCESS; + u32 total_delay = 0; + + if (hw->nvm.blank_nvm_mode) + return; + + ret_code = i40e_aq_release_resource(hw, I40E_NVM_RESOURCE_ID, 0, NULL); + + /* there are some rare cases when trying to release the resource + * results in an admin Q timeout, so handle them correctly + */ + while ((ret_code == I40E_ERR_ADMIN_QUEUE_TIMEOUT) && + (total_delay < hw->aq.asq_cmd_timeout)) { + usleep_range(1000, 2000); + ret_code = i40e_aq_release_resource(hw, + I40E_NVM_RESOURCE_ID, + 0, NULL); + total_delay++; + } } /** @@ -230,6 +247,7 @@ static i40e_status i40e_read_nvm_aq(struct i40e_hw *hw, u8 module_pointer, struct i40e_asq_cmd_details cmd_details; memset(&cmd_details, 0, sizeof(cmd_details)); + cmd_details.wb_desc = &hw->nvm_wb_desc; /* Here we are checking the SR limit only for the flat memory model. * We cannot do it for the module-based model, as we did not acquire @@ -266,7 +284,7 @@ static i40e_status i40e_read_nvm_aq(struct i40e_hw *hw, u8 module_pointer, * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) * @data: word read from the Shadow RAM * - * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. + * Reads one 16 bit word from the Shadow RAM using the AdminQ **/ static i40e_status i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset, u16 *data) @@ -280,27 +298,49 @@ static i40e_status i40e_read_nvm_word_aq(struct i40e_hw *hw, u16 offset, } /** - * i40e_read_nvm_word - Reads Shadow RAM + * __i40e_read_nvm_word - Reads nvm word, assumes called does the locking * @hw: pointer to the HW structure * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) * @data: word read from the Shadow RAM * - * Reads one 16 bit word from the Shadow RAM using the GLNVM_SRCTL register. + * Reads one 16 bit word from the Shadow RAM. + * + * Do not use this function except in cases where the nvm lock is already + * taken via i40e_acquire_nvm(). + **/ +static i40e_status __i40e_read_nvm_word(struct i40e_hw *hw, + u16 offset, u16 *data) +{ + i40e_status ret_code = 0; + + if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) + ret_code = i40e_read_nvm_word_aq(hw, offset, data); + else + ret_code = i40e_read_nvm_word_srctl(hw, offset, data); + return ret_code; +} + +/** + * i40e_read_nvm_word - Reads nvm word and acquire lock if necessary + * @hw: pointer to the HW structure + * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF) + * @data: word read from the Shadow RAM + * + * Reads one 16 bit word from the Shadow RAM. **/ i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, u16 *data) { - enum i40e_status_code ret_code = 0; + i40e_status ret_code = 0; ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); - if (!ret_code) { - if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) { - ret_code = i40e_read_nvm_word_aq(hw, offset, data); - } else { - ret_code = i40e_read_nvm_word_srctl(hw, offset, data); - } - i40e_release_nvm(hw); - } + if (ret_code) + return ret_code; + + ret_code = __i40e_read_nvm_word(hw, offset, data); + + i40e_release_nvm(hw); + return ret_code; } @@ -393,31 +433,25 @@ read_nvm_buffer_aq_exit: } /** - * i40e_read_nvm_buffer - Reads Shadow RAM buffer + * __i40e_read_nvm_buffer - Reads nvm buffer, caller must acquire lock * @hw: pointer to the HW structure * @offset: offset of the Shadow RAM word to read (0x000000 - 0x001FFF). * @words: (in) number of words to read; (out) number of words actually read * @data: words read from the Shadow RAM * * Reads 16 bit words (data buffer) from the SR using the i40e_read_nvm_srrd() - * method. The buffer read is preceded by the NVM ownership take - * and followed by the release. + * method. **/ -i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset, - u16 *words, u16 *data) +static i40e_status __i40e_read_nvm_buffer(struct i40e_hw *hw, + u16 offset, u16 *words, + u16 *data) { - enum i40e_status_code ret_code = 0; + i40e_status ret_code = 0; - if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) { - ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); - if (!ret_code) { - ret_code = i40e_read_nvm_buffer_aq(hw, offset, words, - data); - i40e_release_nvm(hw); - } - } else { + if (hw->flags & I40E_HW_FLAG_AQ_SRCTL_ACCESS_ENABLE) + ret_code = i40e_read_nvm_buffer_aq(hw, offset, words, data); + else ret_code = i40e_read_nvm_buffer_srctl(hw, offset, words, data); - } return ret_code; } @@ -499,15 +533,15 @@ static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw, data = (u16 *)vmem.va; /* read pointer to VPD area */ - ret_code = i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, &vpd_module); + ret_code = __i40e_read_nvm_word(hw, I40E_SR_VPD_PTR, &vpd_module); if (ret_code) { ret_code = I40E_ERR_NVM_CHECKSUM; goto i40e_calc_nvm_checksum_exit; } /* read pointer to PCIe Alt Auto-load module */ - ret_code = i40e_read_nvm_word(hw, I40E_SR_PCIE_ALT_AUTO_LOAD_PTR, - &pcie_alt_module); + ret_code = __i40e_read_nvm_word(hw, I40E_SR_PCIE_ALT_AUTO_LOAD_PTR, + &pcie_alt_module); if (ret_code) { ret_code = I40E_ERR_NVM_CHECKSUM; goto i40e_calc_nvm_checksum_exit; @@ -521,7 +555,7 @@ static i40e_status i40e_calc_nvm_checksum(struct i40e_hw *hw, if ((i % I40E_SR_SECTOR_SIZE_IN_WORDS) == 0) { u16 words = I40E_SR_SECTOR_SIZE_IN_WORDS; - ret_code = i40e_read_nvm_buffer(hw, i, &words, data); + ret_code = __i40e_read_nvm_buffer(hw, i, &words, data); if (ret_code) { ret_code = I40E_ERR_NVM_CHECKSUM; goto i40e_calc_nvm_checksum_exit; @@ -593,14 +627,19 @@ i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw, u16 checksum_sr = 0; u16 checksum_local = 0; + /* We must acquire the NVM lock in order to correctly synchronize the + * NVM accesses across multiple PFs. Without doing so it is possible + * for one of the PFs to read invalid data potentially indicating that + * the checksum is invalid. + */ + ret_code = i40e_acquire_nvm(hw, I40E_RESOURCE_READ); + if (ret_code) + return ret_code; ret_code = i40e_calc_nvm_checksum(hw, &checksum_local); + __i40e_read_nvm_word(hw, I40E_SR_SW_CHECKSUM_WORD, &checksum_sr); + i40e_release_nvm(hw); if (ret_code) - goto i40e_validate_nvm_checksum_exit; - - /* Do not use i40e_read_nvm_word() because we do not want to take - * the synchronization semaphores twice here. - */ - i40e_read_nvm_word(hw, I40E_SR_SW_CHECKSUM_WORD, &checksum_sr); + return ret_code; /* Verify read checksum from EEPROM is the same as * calculated checksum @@ -612,7 +651,6 @@ i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw, if (checksum) *checksum = checksum_local; -i40e_validate_nvm_checksum_exit: return ret_code; } @@ -736,6 +774,15 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, hw->nvmupd_state = I40E_NVMUPD_STATE_INIT; } + /* Acquire lock to prevent race condition where adminq_task + * can execute after i40e_nvmupd_nvm_read/write but before state + * variables (nvm_wait_opcode, nvm_release_on_done) are updated. + * + * During NVMUpdate, it is observed that lock could be held for + * ~5ms for most commands. However lock is held for ~60ms for + * NVMUPD_CSUM_LCB command. + */ + mutex_lock(&hw->aq.arq_mutex); switch (hw->nvmupd_state) { case I40E_NVMUPD_STATE_INIT: status = i40e_nvmupd_state_init(hw, cmd, bytes, perrno); @@ -756,7 +803,8 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, */ if (cmd->offset == 0xffff) { i40e_nvmupd_check_wait_event(hw, hw->nvm_wait_opcode); - return 0; + status = 0; + goto exit; } status = I40E_ERR_NOT_READY; @@ -771,6 +819,8 @@ i40e_status i40e_nvmupd_command(struct i40e_hw *hw, *perrno = -ESRCH; break; } +exit: + mutex_unlock(&hw->aq.arq_mutex); return status; } @@ -997,6 +1047,7 @@ retry: break; case I40E_NVMUPD_CSUM_CON: + /* Assumes the caller has acquired the nvm */ status = i40e_update_nvm_checksum(hw); if (status) { *perrno = hw->aq.asq_last_status ? @@ -1011,6 +1062,7 @@ retry: break; case I40E_NVMUPD_CSUM_LCB: + /* Assumes the caller has acquired the nvm */ status = i40e_update_nvm_checksum(hw); if (status) { *perrno = hw->aq.asq_last_status ? diff --git a/drivers/net/ethernet/intel/i40e/i40e_prototype.h b/drivers/net/ethernet/intel/i40e/i40e_prototype.h index df613ea40313..a39b13197891 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_prototype.h +++ b/drivers/net/ethernet/intel/i40e/i40e_prototype.h @@ -311,8 +311,6 @@ i40e_status i40e_acquire_nvm(struct i40e_hw *hw, void i40e_release_nvm(struct i40e_hw *hw); i40e_status i40e_read_nvm_word(struct i40e_hw *hw, u16 offset, u16 *data); -i40e_status i40e_read_nvm_buffer(struct i40e_hw *hw, u16 offset, - u16 *words, u16 *data); i40e_status i40e_update_nvm_checksum(struct i40e_hw *hw); i40e_status i40e_validate_nvm_checksum(struct i40e_hw *hw, u16 *checksum); diff --git a/drivers/net/ethernet/intel/i40e/i40e_ptp.c b/drivers/net/ethernet/intel/i40e/i40e_ptp.c index 1a0be835fa06..d8456c381c99 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ptp.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ptp.c @@ -158,13 +158,12 @@ static int i40e_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb) static int i40e_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) { struct i40e_pf *pf = container_of(ptp, struct i40e_pf, ptp_caps); - struct timespec64 now, then; + struct timespec64 now; - then = ns_to_timespec64(delta); mutex_lock(&pf->tmreg_lock); i40e_ptp_read(pf, &now); - now = timespec64_add(now, then); + timespec64_add_ns(&now, delta); i40e_ptp_write(pf, (const struct timespec64 *)&now); mutex_unlock(&pf->tmreg_lock); @@ -570,7 +569,7 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: - if (!(pf->flags & I40E_FLAG_PTP_L4_CAPABLE)) + if (!(pf->hw_features & I40E_HW_PTP_L4_CAPABLE)) return -ERANGE; pf->ptp_rx = true; tsyntype = I40E_PRTTSYN_CTL1_V1MESSTYPE0_MASK | @@ -584,7 +583,7 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, case HWTSTAMP_FILTER_PTP_V2_L4_SYNC: case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ: case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ: - if (!(pf->flags & I40E_FLAG_PTP_L4_CAPABLE)) + if (!(pf->hw_features & I40E_HW_PTP_L4_CAPABLE)) return -ERANGE; /* fall through */ case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: @@ -593,7 +592,7 @@ static int i40e_ptp_set_timestamp_mode(struct i40e_pf *pf, pf->ptp_rx = true; tsyntype = I40E_PRTTSYN_CTL1_V2MESSTYPE0_MASK | I40E_PRTTSYN_CTL1_TSYNTYPE_V2; - if (pf->flags & I40E_FLAG_PTP_L4_CAPABLE) { + if (pf->hw_features & I40E_HW_PTP_L4_CAPABLE) { tsyntype |= I40E_PRTTSYN_CTL1_UDP_ENA_MASK; config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; } else { diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.c b/drivers/net/ethernet/intel/i40e/i40e_txrx.c index 2194960d5855..1519dfb851d0 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.c @@ -860,7 +860,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, netdev_tx_completed_queue(txring_txq(tx_ring), total_packets, total_bytes); -#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2) +#define TX_WAKE_THRESHOLD ((s16)(DESC_NEEDED * 2)) if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) && (I40E_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD))) { /* Make sure that anybody stopping the queue after this @@ -959,19 +959,31 @@ void i40e_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) { enum i40e_latency_range new_latency_range = rc->latency_range; - struct i40e_q_vector *qv = rc->ring->q_vector; u32 new_itr = rc->itr; int bytes_per_int; - int usecs; + unsigned int usecs, estimated_usecs; if (rc->total_packets == 0 || !rc->itr) return false; + usecs = (rc->itr << 1) * ITR_COUNTDOWN_START; + bytes_per_int = rc->total_bytes / usecs; + + /* The calculations in this algorithm depend on interrupts actually + * firing at the ITR rate. This may not happen if the packet rate is + * really low, or if we've been napi polling. Check to make sure + * that's not the case before we continue. + */ + estimated_usecs = jiffies_to_usecs(jiffies - rc->last_itr_update); + if (estimated_usecs > usecs) { + new_latency_range = I40E_LOW_LATENCY; + goto reset_latency; + } + /* simple throttlerate management * 0-10MB/s lowest (50000 ints/s) * 10-20MB/s low (20000 ints/s) * 20-1249MB/s bulk (18000 ints/s) - * > 40000 Rx packets per second (8000 ints/s) * * The math works out because the divisor is in 10^(-6) which * turns the bytes/us input value into MB/s values, but @@ -979,9 +991,6 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) * are in 2 usec increments in the ITR registers, and make sure * to use the smoothed values that the countdown timer gives us. */ - usecs = (rc->itr << 1) * ITR_COUNTDOWN_START; - bytes_per_int = rc->total_bytes / usecs; - switch (new_latency_range) { case I40E_LOWEST_LATENCY: if (bytes_per_int > 10) @@ -994,24 +1003,13 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) new_latency_range = I40E_LOWEST_LATENCY; break; case I40E_BULK_LATENCY: - case I40E_ULTRA_LATENCY: default: if (bytes_per_int <= 20) new_latency_range = I40E_LOW_LATENCY; break; } - /* this is to adjust RX more aggressively when streaming small - * packets. The value of 40000 was picked as it is just beyond - * what the hardware can receive per second if in low latency - * mode. - */ -#define RX_ULTRA_PACKET_RATE 40000 - - if ((((rc->total_packets * 1000000) / usecs) > RX_ULTRA_PACKET_RATE) && - (&qv->rx == rc)) - new_latency_range = I40E_ULTRA_LATENCY; - +reset_latency: rc->latency_range = new_latency_range; switch (new_latency_range) { @@ -1024,21 +1022,18 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) case I40E_BULK_LATENCY: new_itr = I40E_ITR_18K; break; - case I40E_ULTRA_LATENCY: - new_itr = I40E_ITR_8K; - break; default: break; } rc->total_bytes = 0; rc->total_packets = 0; + rc->last_itr_update = jiffies; if (new_itr != rc->itr) { rc->itr = new_itr; return true; } - return false; } @@ -2065,7 +2060,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); bool failure = false, xdp_xmit = false; - while (likely(total_rx_packets < budget)) { + while (likely(total_rx_packets < (unsigned int)budget)) { struct i40e_rx_buffer *rx_buffer; union i40e_rx_desc *rx_desc; struct xdp_buff xdp; @@ -2198,7 +2193,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) rx_ring->q_vector->rx.total_bytes += total_rx_bytes; /* guarantee a trip back through this routine if there was a failure */ - return failure ? budget : total_rx_packets; + return failure ? budget : (int)total_rx_packets; } static u32 i40e_buildreg_itr(const int type, const u16 itr) @@ -2243,6 +2238,12 @@ static inline void i40e_update_enable_itr(struct i40e_vsi *vsi, int idx = q_vector->v_idx; int rx_itr_setting, tx_itr_setting; + /* If we don't have MSIX, then we only need to re-enable icr0 */ + if (!(vsi->back->flags & I40E_FLAG_MSIX_ENABLED)) { + i40e_irq_dynamic_enable_icr0(vsi->back, false); + return; + } + vector = (q_vector->v_idx + vsi->base_vector); /* avoid dynamic calculation if in countdown mode OR if @@ -2363,7 +2364,6 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) /* If work not completed, return budget and polling will return */ if (!clean_complete) { - const cpumask_t *aff_mask = &q_vector->affinity_mask; int cpu_id = smp_processor_id(); /* It is possible that the interrupt affinity has changed but, @@ -2373,15 +2373,22 @@ int i40e_napi_poll(struct napi_struct *napi, int budget) * continue to poll, otherwise we must stop polling so the * interrupt can move to the correct cpu. */ - if (likely(cpumask_test_cpu(cpu_id, aff_mask) || - !(vsi->back->flags & I40E_FLAG_MSIX_ENABLED))) { + if (!cpumask_test_cpu(cpu_id, &q_vector->affinity_mask)) { + /* Tell napi that we are done polling */ + napi_complete_done(napi, work_done); + + /* Force an interrupt */ + i40e_force_wb(vsi, q_vector); + + /* Return budget-1 so that polling stops */ + return budget - 1; + } tx_only: - if (arm_wb) { - q_vector->tx.ring[0].tx_stats.tx_force_wb++; - i40e_enable_wb_on_itr(vsi, q_vector); - } - return budget; + if (arm_wb) { + q_vector->tx.ring[0].tx_stats.tx_force_wb++; + i40e_enable_wb_on_itr(vsi, q_vector); } + return budget; } if (vsi->back->flags & I40E_TXR_FLAGS_WB_ON_ITR) @@ -2390,16 +2397,7 @@ tx_only: /* Work is done so exit the polling mode and re-enable the interrupt */ napi_complete_done(napi, work_done); - /* If we're prematurely stopping polling to fix the interrupt - * affinity we want to make sure polling starts back up so we - * issue a call to i40e_force_wb which triggers a SW interrupt. - */ - if (!clean_complete) - i40e_force_wb(vsi, q_vector); - else if (!(vsi->back->flags & I40E_FLAG_MSIX_ENABLED)) - i40e_irq_dynamic_enable_icr0(vsi->back, false); - else - i40e_update_enable_itr(vsi, q_vector); + i40e_update_enable_itr(vsi, q_vector); return min(work_done, budget - 1); } @@ -2453,9 +2451,15 @@ static void i40e_atr(struct i40e_ring *tx_ring, struct sk_buff *skb, hlen = (hdr.network[0] & 0x0F) << 2; l4_proto = hdr.ipv4->protocol; } else { - hlen = hdr.network - skb->data; - l4_proto = ipv6_find_hdr(skb, &hlen, IPPROTO_TCP, NULL, NULL); - hlen -= hdr.network - skb->data; + /* find the start of the innermost ipv6 header */ + unsigned int inner_hlen = hdr.network - skb->data; + unsigned int h_offset = inner_hlen; + + /* this function updates h_offset to the end of the header */ + l4_proto = + ipv6_find_hdr(skb, &h_offset, IPPROTO_TCP, NULL, NULL); + /* hlen will contain our best estimate of the tcp header */ + hlen = h_offset - inner_hlen; } if (l4_proto != IPPROTO_TCP) diff --git a/drivers/net/ethernet/intel/i40e/i40e_txrx.h b/drivers/net/ethernet/intel/i40e/i40e_txrx.h index b288d58313a6..2f848bc5e391 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40e/i40e_txrx.h @@ -112,7 +112,7 @@ enum i40e_dyn_idx_t { BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP)) #define i40e_pf_get_default_rss_hena(pf) \ - (((pf)->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) ? \ + (((pf)->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) ? \ I40E_DEFAULT_RSS_HENA_EXPANDED : I40E_DEFAULT_RSS_HENA) /* Supported Rx Buffer Sizes (a multiple of 128) */ @@ -130,6 +130,7 @@ enum i40e_dyn_idx_t { * i.e. RXBUFFER_512 --> 1216 byte skb (size-2048 slab) */ #define I40E_RX_HDR_SIZE I40E_RXBUFFER_256 +#define I40E_PACKET_HDR_PAD (ETH_HLEN + ETH_FCS_LEN + (VLAN_HLEN * 2)) #define i40e_rx_desc i40e_32byte_rx_desc #define I40E_RX_DMA_ATTR \ @@ -453,7 +454,6 @@ enum i40e_latency_range { I40E_LOWEST_LATENCY = 0, I40E_LOW_LATENCY = 1, I40E_BULK_LATENCY = 2, - I40E_ULTRA_LATENCY = 3, }; struct i40e_ring_container { @@ -461,6 +461,7 @@ struct i40e_ring_container { struct i40e_ring *ring; unsigned int total_bytes; /* total bytes processed this int */ unsigned int total_packets; /* total packets processed this int */ + unsigned long last_itr_update; /* jiffies of last ITR update */ u16 count; enum i40e_latency_range latency_range; u16 itr; diff --git a/drivers/net/ethernet/intel/i40e/i40e_type.h b/drivers/net/ethernet/intel/i40e/i40e_type.h index 3a18ed13edc4..fd4bbdd88b57 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_type.h +++ b/drivers/net/ethernet/intel/i40e/i40e_type.h @@ -185,6 +185,7 @@ struct i40e_link_status { enum i40e_aq_link_speed link_speed; u8 link_info; u8 an_info; + u8 req_fec_info; u8 fec_info; u8 ext_info; u8 loopback; diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c index ecbe40ea8ffe..4d1e670f490e 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c +++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c @@ -1528,54 +1528,54 @@ static int i40e_vc_get_vf_resources_msg(struct i40e_vf *vf, u8 *msg) VIRTCHNL_VF_OFFLOAD_RSS_REG | VIRTCHNL_VF_OFFLOAD_VLAN; - vfres->vf_offload_flags = VIRTCHNL_VF_OFFLOAD_L2; + vfres->vf_cap_flags = VIRTCHNL_VF_OFFLOAD_L2; vsi = pf->vsi[vf->lan_vsi_idx]; if (!vsi->info.pvid) - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_VLAN; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_VLAN; if (i40e_vf_client_capable(pf, vf->vf_id) && (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_IWARP)) { - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_IWARP; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_IWARP; set_bit(I40E_VF_STATE_IWARPENA, &vf->vf_states); } if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PF) { - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PF; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PF; } else { - if ((pf->flags & I40E_FLAG_RSS_AQ_CAPABLE) && + if ((pf->hw_features & I40E_HW_RSS_AQ_CAPABLE) && (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_AQ)) - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_RSS_AQ; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_AQ; else - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_RSS_REG; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_REG; } - if (pf->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) { + if (pf->hw_features & I40E_HW_MULTIPLE_TCP_UDP_RSS_PCTYPE) { if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2) - vfres->vf_offload_flags |= + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2; } if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ENCAP) - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP; - if ((pf->flags & I40E_FLAG_OUTER_UDP_CSUM_CAPABLE) && + if ((pf->hw_features & I40E_HW_OUTER_UDP_CSUM_CAPABLE) && (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM)) - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM; if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RX_POLLING) { if (pf->flags & I40E_FLAG_MFP_ENABLED) { dev_err(&pf->pdev->dev, "VF %d requested polling mode: this feature is supported only when the device is running in single function per port (SFP) mode\n", vf->vf_id); - ret = I40E_ERR_PARAM; + aq_ret = I40E_ERR_PARAM; goto err; } - vfres->vf_offload_flags |= VIRTCHNL_VF_OFFLOAD_RX_POLLING; + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RX_POLLING; } - if (pf->flags & I40E_FLAG_WB_ON_ITR_CAPABLE) { + if (pf->hw_features & I40E_HW_WB_ON_ITR_CAPABLE) { if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_WB_ON_ITR) - vfres->vf_offload_flags |= + vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_WB_ON_ITR; } @@ -1741,16 +1741,14 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, NULL); } else if (i40e_getnum_vf_vsi_vlan_filters(vsi)) { hash_for_each(vsi->mac_filter_hash, bkt, f, hlist) { - aq_ret = 0; - if (f->vlan >= 0 && f->vlan <= I40E_MAX_VLANID) { - aq_ret = - i40e_aq_set_vsi_uc_promisc_on_vlan(hw, - vsi->seid, - alluni, - f->vlan, - NULL); - aq_err = pf->hw.aq.asq_last_status; - } + if (f->vlan < 0 || f->vlan > I40E_MAX_VLANID) + continue; + aq_ret = i40e_aq_set_vsi_uc_promisc_on_vlan(hw, + vsi->seid, + alluni, + f->vlan, + NULL); + aq_err = pf->hw.aq.asq_last_status; if (aq_ret) dev_err(&pf->pdev->dev, "Could not add VLAN %d to Unicast promiscuous domain err %s aq_err %s\n", @@ -1760,7 +1758,7 @@ static int i40e_vc_config_promiscuous_mode_msg(struct i40e_vf *vf, } } else { aq_ret = i40e_aq_set_vsi_unicast_promiscuous(hw, vsi->seid, - allmulti, NULL, + alluni, NULL, true); aq_err = pf->hw.aq.asq_last_status; if (aq_ret) { @@ -2532,6 +2530,60 @@ err: } /** + * i40e_vc_enable_vlan_stripping + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * @msglen: msg length + * + * Enable vlan header stripping for the VF + **/ +static int i40e_vc_enable_vlan_stripping(struct i40e_vf *vf, u8 *msg, + u16 msglen) +{ + struct i40e_vsi *vsi = vf->pf->vsi[vf->lan_vsi_idx]; + i40e_status aq_ret = 0; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + + i40e_vlan_stripping_enable(vsi); + + /* send the response to the VF */ +err: + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_ENABLE_VLAN_STRIPPING, + aq_ret); +} + +/** + * i40e_vc_disable_vlan_stripping + * @vf: pointer to the VF info + * @msg: pointer to the msg buffer + * @msglen: msg length + * + * Disable vlan header stripping for the VF + **/ +static int i40e_vc_disable_vlan_stripping(struct i40e_vf *vf, u8 *msg, + u16 msglen) +{ + struct i40e_vsi *vsi = vf->pf->vsi[vf->lan_vsi_idx]; + i40e_status aq_ret = 0; + + if (!test_bit(I40E_VF_STATE_ACTIVE, &vf->vf_states)) { + aq_ret = I40E_ERR_PARAM; + goto err; + } + + i40e_vlan_stripping_disable(vsi); + + /* send the response to the VF */ +err: + return i40e_vc_send_resp_to_vf(vf, VIRTCHNL_OP_DISABLE_VLAN_STRIPPING, + aq_ret); +} + +/** * i40e_vc_process_vf_msg * @pf: pointer to the PF structure * @vf_id: source VF id @@ -2650,6 +2702,12 @@ int i40e_vc_process_vf_msg(struct i40e_pf *pf, s16 vf_id, u32 v_opcode, case VIRTCHNL_OP_SET_RSS_HENA: ret = i40e_vc_set_rss_hena(vf, msg, msglen); break; + case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING: + ret = i40e_vc_enable_vlan_stripping(vf, msg, msglen); + break; + case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING: + ret = i40e_vc_disable_vlan_stripping(vf, msg, msglen); + break; case VIRTCHNL_OP_UNKNOWN: default: @@ -2764,7 +2822,6 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) spin_unlock_bh(&vsi->mac_filter_hash_lock); - dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", mac, vf_id); /* program mac filter */ if (i40e_sync_vsi_filters(vsi)) { dev_err(&pf->pdev->dev, "Unable to program ucast filters\n"); @@ -2772,7 +2829,16 @@ int i40e_ndo_set_vf_mac(struct net_device *netdev, int vf_id, u8 *mac) goto error_param; } ether_addr_copy(vf->default_lan_addr.addr, mac); - vf->pf_set_mac = true; + + if (is_zero_ether_addr(mac)) { + vf->pf_set_mac = false; + dev_info(&pf->pdev->dev, "Removing MAC on VF %d\n", vf_id); + } else { + vf->pf_set_mac = true; + dev_info(&pf->pdev->dev, "Setting MAC %pM on VF %d\n", + mac, vf_id); + } + /* Force the VF driver stop so it has to reload with new MAC address */ i40e_vc_disable_vf(pf, vf); dev_info(&pf->pdev->dev, "Reload the VF driver to make this change effective.\n"); diff --git a/drivers/net/ethernet/intel/i40evf/i40e_common.c b/drivers/net/ethernet/intel/i40evf/i40e_common.c index 1dd1938f594f..8d3a2bfe186a 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_common.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_common.c @@ -333,9 +333,9 @@ void i40evf_debug_aq(struct i40e_hw *hw, enum i40e_debug_mask mask, void *desc, len = buf_len; /* write the full 16-byte chunks */ if (hw->debug_mask & mask) { - char prefix[20]; + char prefix[27]; - snprintf(prefix, 20, + snprintf(prefix, sizeof(prefix), "i40evf %02x:%02x.%x: \t0x", hw->bus.bus_id, hw->bus.device, @@ -1104,7 +1104,7 @@ void i40e_vf_parse_hw_config(struct i40e_hw *hw, hw->dev_caps.num_rx_qp = msg->num_queue_pairs; hw->dev_caps.num_tx_qp = msg->num_queue_pairs; hw->dev_caps.num_msix_vectors_vf = msg->max_vectors; - hw->dev_caps.dcb = msg->vf_offload_flags & + hw->dev_caps.dcb = msg->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_L2; hw->dev_caps.fcoe = 0; for (i = 0; i < msg->num_vsis; i++) { diff --git a/drivers/net/ethernet/intel/i40evf/i40e_osdep.h b/drivers/net/ethernet/intel/i40evf/i40e_osdep.h index 5e314fd3c016..a90737786c34 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_osdep.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_osdep.h @@ -54,7 +54,7 @@ struct i40e_dma_mem { void *va; dma_addr_t pa; u32 size; -} __packed; +}; #define i40e_allocate_dma_mem(h, m, unused, s, a) \ i40evf_allocate_dma_mem_d(h, m, s, a) @@ -63,7 +63,7 @@ struct i40e_dma_mem { struct i40e_virt_mem { void *va; u32 size; -} __packed; +}; #define i40e_allocate_virt_mem(h, m, s) i40evf_allocate_virt_mem_d(h, m, s) #define i40e_free_virt_mem(h, m) i40evf_free_virt_mem_d(h, m) diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c index 12b02e530503..c32c62462c84 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.c +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.c @@ -275,7 +275,7 @@ static bool i40e_clean_tx_irq(struct i40e_vsi *vsi, netdev_tx_completed_queue(txring_txq(tx_ring), total_packets, total_bytes); -#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2) +#define TX_WAKE_THRESHOLD ((s16)(DESC_NEEDED * 2)) if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) && (I40E_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD))) { /* Make sure that anybody stopping the queue after this @@ -357,19 +357,31 @@ void i40evf_force_wb(struct i40e_vsi *vsi, struct i40e_q_vector *q_vector) static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) { enum i40e_latency_range new_latency_range = rc->latency_range; - struct i40e_q_vector *qv = rc->ring->q_vector; u32 new_itr = rc->itr; int bytes_per_int; - int usecs; + unsigned int usecs, estimated_usecs; if (rc->total_packets == 0 || !rc->itr) return false; + usecs = (rc->itr << 1) * ITR_COUNTDOWN_START; + bytes_per_int = rc->total_bytes / usecs; + + /* The calculations in this algorithm depend on interrupts actually + * firing at the ITR rate. This may not happen if the packet rate is + * really low, or if we've been napi polling. Check to make sure + * that's not the case before we continue. + */ + estimated_usecs = jiffies_to_usecs(jiffies - rc->last_itr_update); + if (estimated_usecs > usecs) { + new_latency_range = I40E_LOW_LATENCY; + goto reset_latency; + } + /* simple throttlerate management * 0-10MB/s lowest (50000 ints/s) * 10-20MB/s low (20000 ints/s) * 20-1249MB/s bulk (18000 ints/s) - * > 40000 Rx packets per second (8000 ints/s) * * The math works out because the divisor is in 10^(-6) which * turns the bytes/us input value into MB/s values, but @@ -377,9 +389,6 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) * are in 2 usec increments in the ITR registers, and make sure * to use the smoothed values that the countdown timer gives us. */ - usecs = (rc->itr << 1) * ITR_COUNTDOWN_START; - bytes_per_int = rc->total_bytes / usecs; - switch (new_latency_range) { case I40E_LOWEST_LATENCY: if (bytes_per_int > 10) @@ -392,24 +401,13 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) new_latency_range = I40E_LOWEST_LATENCY; break; case I40E_BULK_LATENCY: - case I40E_ULTRA_LATENCY: default: if (bytes_per_int <= 20) new_latency_range = I40E_LOW_LATENCY; break; } - /* this is to adjust RX more aggressively when streaming small - * packets. The value of 40000 was picked as it is just beyond - * what the hardware can receive per second if in low latency - * mode. - */ -#define RX_ULTRA_PACKET_RATE 40000 - - if ((((rc->total_packets * 1000000) / usecs) > RX_ULTRA_PACKET_RATE) && - (&qv->rx == rc)) - new_latency_range = I40E_ULTRA_LATENCY; - +reset_latency: rc->latency_range = new_latency_range; switch (new_latency_range) { @@ -422,21 +420,18 @@ static bool i40e_set_new_dynamic_itr(struct i40e_ring_container *rc) case I40E_BULK_LATENCY: new_itr = I40E_ITR_18K; break; - case I40E_ULTRA_LATENCY: - new_itr = I40E_ITR_8K; - break; default: break; } rc->total_bytes = 0; rc->total_packets = 0; + rc->last_itr_update = jiffies; if (new_itr != rc->itr) { rc->itr = new_itr; return true; } - return false; } @@ -1299,7 +1294,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) u16 cleaned_count = I40E_DESC_UNUSED(rx_ring); bool failure = false; - while (likely(total_rx_packets < budget)) { + while (likely(total_rx_packets < (unsigned int)budget)) { struct i40e_rx_buffer *rx_buffer; union i40e_rx_desc *rx_desc; unsigned int size; @@ -1406,7 +1401,7 @@ static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget) rx_ring->q_vector->rx.total_bytes += total_rx_bytes; /* guarantee a trip back through this routine if there was a failure */ - return failure ? budget : total_rx_packets; + return failure ? budget : (int)total_rx_packets; } static u32 i40e_buildreg_itr(const int type, const u16 itr) @@ -1575,7 +1570,6 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) /* If work not completed, return budget and polling will return */ if (!clean_complete) { - const cpumask_t *aff_mask = &q_vector->affinity_mask; int cpu_id = smp_processor_id(); /* It is possible that the interrupt affinity has changed but, @@ -1585,14 +1579,22 @@ int i40evf_napi_poll(struct napi_struct *napi, int budget) * continue to poll, otherwise we must stop polling so the * interrupt can move to the correct cpu. */ - if (likely(cpumask_test_cpu(cpu_id, aff_mask))) { + if (!cpumask_test_cpu(cpu_id, &q_vector->affinity_mask)) { + /* Tell napi that we are done polling */ + napi_complete_done(napi, work_done); + + /* Force an interrupt */ + i40evf_force_wb(vsi, q_vector); + + /* Return budget-1 so that polling stops */ + return budget - 1; + } tx_only: - if (arm_wb) { - q_vector->tx.ring[0].tx_stats.tx_force_wb++; - i40e_enable_wb_on_itr(vsi, q_vector); - } - return budget; + if (arm_wb) { + q_vector->tx.ring[0].tx_stats.tx_force_wb++; + i40e_enable_wb_on_itr(vsi, q_vector); } + return budget; } if (vsi->back->flags & I40E_TXR_FLAGS_WB_ON_ITR) @@ -1601,14 +1603,7 @@ tx_only: /* Work is done so exit the polling mode and re-enable the interrupt */ napi_complete_done(napi, work_done); - /* If we're prematurely stopping polling to fix the interrupt - * affinity we want to make sure polling starts back up so we - * issue a call to i40evf_force_wb which triggers a SW interrupt. - */ - if (!clean_complete) - i40evf_force_wb(vsi, q_vector); - else - i40e_update_enable_itr(vsi, q_vector); + i40e_update_enable_itr(vsi, q_vector); return min(work_done, budget - 1); } diff --git a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h index 901282c87cf6..0d9f98bc07bd 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_txrx.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_txrx.h @@ -98,10 +98,6 @@ enum i40e_dyn_idx_t { BIT_ULL(I40E_FILTER_PCTYPE_NONF_UNICAST_IPV6_UDP) | \ BIT_ULL(I40E_FILTER_PCTYPE_NONF_MULTICAST_IPV6_UDP)) -#define i40e_pf_get_default_rss_hena(pf) \ - (((pf)->flags & I40E_FLAG_MULTIPLE_TCP_UDP_RSS_PCTYPE) ? \ - I40E_DEFAULT_RSS_HENA_EXPANDED : I40E_DEFAULT_RSS_HENA) - /* Supported Rx Buffer Sizes (a multiple of 128) */ #define I40E_RXBUFFER_256 256 #define I40E_RXBUFFER_1536 1536 /* 128B aligned standard Ethernet frame */ @@ -117,6 +113,7 @@ enum i40e_dyn_idx_t { * i.e. RXBUFFER_512 --> 1216 byte skb (size-2048 slab) */ #define I40E_RX_HDR_SIZE I40E_RXBUFFER_256 +#define I40E_PACKET_HDR_PAD (ETH_HLEN + ETH_FCS_LEN + (VLAN_HLEN * 2)) #define i40e_rx_desc i40e_32byte_rx_desc #define I40E_RX_DMA_ATTR \ @@ -428,7 +425,6 @@ enum i40e_latency_range { I40E_LOWEST_LATENCY = 0, I40E_LOW_LATENCY = 1, I40E_BULK_LATENCY = 2, - I40E_ULTRA_LATENCY = 3, }; struct i40e_ring_container { @@ -436,6 +432,7 @@ struct i40e_ring_container { struct i40e_ring *ring; unsigned int total_bytes; /* total bytes processed this int */ unsigned int total_packets; /* total packets processed this int */ + unsigned long last_itr_update; /* jiffies of last ITR update */ u16 count; enum i40e_latency_range latency_range; u16 itr; diff --git a/drivers/net/ethernet/intel/i40evf/i40e_type.h b/drivers/net/ethernet/intel/i40evf/i40e_type.h index bde7f24af1c6..2ea919d9cdcf 100644 --- a/drivers/net/ethernet/intel/i40evf/i40e_type.h +++ b/drivers/net/ethernet/intel/i40evf/i40e_type.h @@ -159,6 +159,7 @@ struct i40e_link_status { enum i40e_aq_link_speed link_speed; u8 link_info; u8 an_info; + u8 req_fec_info; u8 fec_info; u8 ext_info; u8 loopback; diff --git a/drivers/net/ethernet/intel/i40evf/i40evf.h b/drivers/net/ethernet/intel/i40evf/i40evf.h index 6cc92089fecb..82f69031e5cd 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf.h +++ b/drivers/net/ethernet/intel/i40evf/i40evf.h @@ -39,6 +39,18 @@ #include <linux/tcp.h> #include <linux/sctp.h> #include <linux/ipv6.h> +#include <linux/kernel.h> +#include <linux/bitops.h> +#include <linux/timer.h> +#include <linux/workqueue.h> +#include <linux/wait.h> +#include <linux/delay.h> +#include <linux/gfp.h> +#include <linux/skbuff.h> +#include <linux/dma-mapping.h> +#include <linux/etherdevice.h> +#include <linux/socket.h> +#include <linux/jiffies.h> #include <net/ip6_checksum.h> #include <net/udp.h> @@ -109,7 +121,7 @@ struct i40e_q_vector { #define ITR_COUNTDOWN_START 100 u8 itr_countdown; /* when 0 or 1 update ITR */ int v_idx; /* vector index in list */ - char name[IFNAMSIZ + 9]; + char name[IFNAMSIZ + 15]; bool arm_wb_state; cpumask_t affinity_mask; struct irq_affinity_notify affinity_notify; @@ -183,6 +195,7 @@ struct i40evf_adapter { struct work_struct adminq_task; struct delayed_work client_task; struct delayed_work init_task; + wait_queue_head_t down_waitqueue; struct i40e_q_vector *q_vectors; struct list_head vlan_filter_list; char misc_vector_name[IFNAMSIZ + 9]; @@ -225,8 +238,6 @@ struct i40evf_adapter { /* duplicates for common code */ #define I40E_FLAG_DCB_ENABLED 0 #define I40E_FLAG_RX_CSUM_ENABLED I40EVF_FLAG_RX_CSUM_ENABLED -#define I40E_FLAG_WB_ON_ITR_CAPABLE I40EVF_FLAG_WB_ON_ITR_CAPABLE -#define I40E_FLAG_OUTER_UDP_CSUM_CAPABLE I40EVF_FLAG_OUTER_UDP_CSUM_CAPABLE #define I40E_FLAG_LEGACY_RX I40EVF_FLAG_LEGACY_RX /* flags for admin queue service task */ u32 aq_required; @@ -250,6 +261,8 @@ struct i40evf_adapter { #define I40EVF_FLAG_AQ_RELEASE_PROMISC BIT(16) #define I40EVF_FLAG_AQ_REQUEST_ALLMULTI BIT(17) #define I40EVF_FLAG_AQ_RELEASE_ALLMULTI BIT(18) +#define I40EVF_FLAG_AQ_ENABLE_VLAN_STRIPPING BIT(19) +#define I40EVF_FLAG_AQ_DISABLE_VLAN_STRIPPING BIT(20) /* OS defined structs */ struct net_device *netdev; @@ -266,19 +279,19 @@ struct i40evf_adapter { enum virtchnl_link_speed link_speed; enum virtchnl_ops current_op; #define CLIENT_ALLOWED(_a) ((_a)->vf_res ? \ - (_a)->vf_res->vf_offload_flags & \ + (_a)->vf_res->vf_cap_flags & \ VIRTCHNL_VF_OFFLOAD_IWARP : \ 0) #define CLIENT_ENABLED(_a) ((_a)->cinst) /* RSS by the PF should be preferred over RSS via other methods. */ -#define RSS_PF(_a) ((_a)->vf_res->vf_offload_flags & \ +#define RSS_PF(_a) ((_a)->vf_res->vf_cap_flags & \ VIRTCHNL_VF_OFFLOAD_RSS_PF) -#define RSS_AQ(_a) ((_a)->vf_res->vf_offload_flags & \ +#define RSS_AQ(_a) ((_a)->vf_res->vf_cap_flags & \ VIRTCHNL_VF_OFFLOAD_RSS_AQ) -#define RSS_REG(_a) (!((_a)->vf_res->vf_offload_flags & \ +#define RSS_REG(_a) (!((_a)->vf_res->vf_cap_flags & \ (VIRTCHNL_VF_OFFLOAD_RSS_AQ | \ VIRTCHNL_VF_OFFLOAD_RSS_PF))) -#define VLAN_ALLOWED(_a) ((_a)->vf_res->vf_offload_flags & \ +#define VLAN_ALLOWED(_a) ((_a)->vf_res->vf_cap_flags & \ VIRTCHNL_VF_OFFLOAD_VLAN) struct virtchnl_vf_resource *vf_res; /* incl. all VSIs */ struct virtchnl_vsi_resource *vsi_res; /* our LAN VSI */ @@ -347,6 +360,8 @@ void i40evf_get_hena(struct i40evf_adapter *adapter); void i40evf_set_hena(struct i40evf_adapter *adapter); void i40evf_set_rss_key(struct i40evf_adapter *adapter); void i40evf_set_rss_lut(struct i40evf_adapter *adapter); +void i40evf_enable_vlan_stripping(struct i40evf_adapter *adapter); +void i40evf_disable_vlan_stripping(struct i40evf_adapter *adapter); void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, enum virtchnl_ops v_opcode, i40e_status v_retval, u8 *msg, u16 msglen); diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c index 9bb2cc7dd4e4..65874d6b3ab9 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_ethtool.c @@ -165,7 +165,7 @@ static void i40evf_get_ethtool_stats(struct net_device *netdev, struct ethtool_stats *stats, u64 *data) { struct i40evf_adapter *adapter = netdev_priv(netdev); - int i, j; + unsigned int i, j; char *p; for (i = 0; i < I40EVF_GLOBAL_STATS_LEN; i++) { @@ -197,7 +197,7 @@ static void i40evf_get_strings(struct net_device *netdev, u32 sset, u8 *data) int i; if (sset == ETH_SS_STATS) { - for (i = 0; i < I40EVF_GLOBAL_STATS_LEN; i++) { + for (i = 0; i < (int)I40EVF_GLOBAL_STATS_LEN; i++) { memcpy(p, i40evf_gstrings_stats[i].stat_string, ETH_GSTRING_LEN); p += ETH_GSTRING_LEN; @@ -258,29 +258,50 @@ static u32 i40evf_get_priv_flags(struct net_device *netdev) static int i40evf_set_priv_flags(struct net_device *netdev, u32 flags) { struct i40evf_adapter *adapter = netdev_priv(netdev); - u64 changed_flags; + u32 orig_flags, new_flags, changed_flags; u32 i; - changed_flags = adapter->flags; + orig_flags = READ_ONCE(adapter->flags); + new_flags = orig_flags; for (i = 0; i < I40EVF_PRIV_FLAGS_STR_LEN; i++) { const struct i40evf_priv_flags *priv_flags; priv_flags = &i40evf_gstrings_priv_flags[i]; - if (priv_flags->read_only) - continue; - if (flags & BIT(i)) - adapter->flags |= priv_flags->flag; + new_flags |= priv_flags->flag; else - adapter->flags &= ~(priv_flags->flag); + new_flags &= ~(priv_flags->flag); + + if (priv_flags->read_only && + ((orig_flags ^ new_flags) & ~BIT(i))) + return -EOPNOTSUPP; + } + + /* Before we finalize any flag changes, any checks which we need to + * perform to determine if the new flags will be supported should go + * here... + */ + + /* Compare and exchange the new flags into place. If we failed, that + * is if cmpxchg returns anything but the old value, this means + * something else must have modified the flags variable since we + * copied it. We'll just punt with an error and log something in the + * message buffer. + */ + if (cmpxchg(&adapter->flags, orig_flags, new_flags) != orig_flags) { + dev_warn(&adapter->pdev->dev, + "Unable to update adapter->flags as it was modified by another thread...\n"); + return -EAGAIN; } - /* check for flags that changed */ - changed_flags ^= adapter->flags; + changed_flags = orig_flags ^ new_flags; - /* Process any additional changes needed as a result of flag changes. */ + /* Process any additional changes needed as a result of flag changes. + * The changed_flags value reflects the list of bits that were changed + * in the code above. + */ /* issue a reset to force legacy-rx change to take effect */ if (changed_flags & I40EVF_FLAG_LEGACY_RX) { diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_main.c b/drivers/net/ethernet/intel/i40evf/i40evf_main.c index 7c213a347909..1825d956bb00 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_main.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_main.c @@ -520,7 +520,7 @@ static void i40evf_irq_affinity_notify(struct irq_affinity_notify *notify, struct i40e_q_vector *q_vector = container_of(notify, struct i40e_q_vector, affinity_notify); - q_vector->affinity_mask = *mask; + cpumask_copy(&q_vector->affinity_mask, mask); } /** @@ -543,9 +543,9 @@ static void i40evf_irq_affinity_release(struct kref *ref) {} static int i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename) { - int vector, err, q_vectors; - int rx_int_idx = 0, tx_int_idx = 0; - int irq_num; + unsigned int vector, q_vectors; + unsigned int rx_int_idx = 0, tx_int_idx = 0; + int irq_num, err; i40evf_irq_disable(adapter); /* Decrement for Other and TCP Timer vectors */ @@ -556,18 +556,15 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename) irq_num = adapter->msix_entries[vector + NONQ_VECS].vector; if (q_vector->tx.ring && q_vector->rx.ring) { - snprintf(q_vector->name, sizeof(q_vector->name) - 1, - "i40evf-%s-%s-%d", basename, - "TxRx", rx_int_idx++); + snprintf(q_vector->name, sizeof(q_vector->name), + "i40evf-%s-TxRx-%d", basename, rx_int_idx++); tx_int_idx++; } else if (q_vector->rx.ring) { - snprintf(q_vector->name, sizeof(q_vector->name) - 1, - "i40evf-%s-%s-%d", basename, - "rx", rx_int_idx++); + snprintf(q_vector->name, sizeof(q_vector->name), + "i40evf-%s-rx-%d", basename, rx_int_idx++); } else if (q_vector->tx.ring) { - snprintf(q_vector->name, sizeof(q_vector->name) - 1, - "i40evf-%s-%s-%d", basename, - "tx", tx_int_idx++); + snprintf(q_vector->name, sizeof(q_vector->name), + "i40evf-%s-tx-%d", basename, tx_int_idx++); } else { /* skip this unused q_vector */ continue; @@ -587,8 +584,10 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename) q_vector->affinity_notify.release = i40evf_irq_affinity_release; irq_set_affinity_notifier(irq_num, &q_vector->affinity_notify); - /* assign the mask for this irq */ - irq_set_affinity_hint(irq_num, &q_vector->affinity_mask); + /* get_cpu_mask returns a static constant mask with + * a permanent lifetime so it's ok to use here. + */ + irq_set_affinity_hint(irq_num, get_cpu_mask(q_vector->v_idx)); } return 0; @@ -1143,6 +1142,7 @@ void i40evf_down(struct i40evf_adapter *adapter) } clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); + mod_timer_pending(&adapter->watchdog_timer, jiffies + 1); } /** @@ -1241,7 +1241,7 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter) tx_ring->dev = &adapter->pdev->dev; tx_ring->count = adapter->tx_desc_count; tx_ring->tx_itr_setting = (I40E_ITR_DYNAMIC | I40E_ITR_TX_DEF); - if (adapter->flags & I40E_FLAG_WB_ON_ITR_CAPABLE) + if (adapter->flags & I40EVF_FLAG_WB_ON_ITR_CAPABLE) tx_ring->flags |= I40E_TXR_FLAGS_WB_ON_ITR; rx_ring = &adapter->rx_rings[i]; @@ -1417,7 +1417,7 @@ static int i40evf_init_rss(struct i40evf_adapter *adapter) if (!RSS_PF(adapter)) { /* Enable PCTYPES for RSS, TCP/UDP with IPv4/IPv6 */ - if (adapter->vf_res->vf_offload_flags & + if (adapter->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2) adapter->hena = I40E_DEFAULT_RSS_HENA_EXPANDED; else @@ -1458,6 +1458,7 @@ static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter) q_vector->adapter = adapter; q_vector->vsi = &adapter->vsi; q_vector->v_idx = q_idx; + cpumask_copy(&q_vector->affinity_mask, cpu_possible_mask); netif_napi_add(adapter->netdev, &q_vector->napi, i40evf_napi_poll, NAPI_POLL_WEIGHT); } @@ -1678,6 +1679,16 @@ static void i40evf_watchdog_task(struct work_struct *work) goto watchdog_done; } + if (adapter->aq_required & I40EVF_FLAG_AQ_ENABLE_VLAN_STRIPPING) { + i40evf_enable_vlan_stripping(adapter); + goto watchdog_done; + } + + if (adapter->aq_required & I40EVF_FLAG_AQ_DISABLE_VLAN_STRIPPING) { + i40evf_disable_vlan_stripping(adapter); + goto watchdog_done; + } + if (adapter->aq_required & I40EVF_FLAG_AQ_CONFIGURE_QUEUES) { i40evf_configure_queues(adapter); goto watchdog_done; @@ -1794,6 +1805,7 @@ static void i40evf_disable_vf(struct i40evf_adapter *adapter) clear_bit(__I40EVF_IN_CRITICAL_TASK, &adapter->crit_section); adapter->flags &= ~I40EVF_FLAG_RESET_PENDING; adapter->state = __I40EVF_DOWN; + wake_up(&adapter->down_waitqueue); dev_info(&adapter->pdev->dev, "Reset task did not complete, VF disabled\n"); } @@ -1877,7 +1889,7 @@ static void i40evf_reset_task(struct work_struct *work) } continue_reset: - if (netif_running(adapter->netdev)) { + if (netif_running(netdev)) { netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); adapter->link_up = false; @@ -1939,12 +1951,13 @@ continue_reset: i40evf_irq_enable(adapter, true); } else { adapter->state = __I40EVF_DOWN; + wake_up(&adapter->down_waitqueue); } return; reset_err: dev_err(&adapter->pdev->dev, "failed to allocate resources during reinit\n"); - i40evf_close(adapter->netdev); + i40evf_close(netdev); } /** @@ -1957,8 +1970,8 @@ static void i40evf_adminq_task(struct work_struct *work) container_of(work, struct i40evf_adapter, adminq_task); struct i40e_hw *hw = &adapter->hw; struct i40e_arq_event_info event; - struct virtchnl_msg *v_msg; - i40e_status ret; + enum virtchnl_ops v_op; + i40e_status ret, v_ret; u32 val, oldval; u16 pending; @@ -1970,15 +1983,15 @@ static void i40evf_adminq_task(struct work_struct *work) if (!event.msg_buf) goto out; - v_msg = (struct virtchnl_msg *)&event.desc; do { ret = i40evf_clean_arq_element(hw, &event, &pending); - if (ret || !v_msg->v_opcode) + v_op = (enum virtchnl_ops)le32_to_cpu(event.desc.cookie_high); + v_ret = (i40e_status)le32_to_cpu(event.desc.cookie_low); + + if (ret || !v_op) break; /* No event to process or error cleaning ARQ */ - i40evf_virtchnl_completion(adapter, v_msg->v_opcode, - (i40e_status)v_msg->v_retval, - event.msg_buf, + i40evf_virtchnl_completion(adapter, v_op, v_ret, event.msg_buf, event.msg_len); if (pending != 0) memset(event.msg_buf, 0, I40EVF_MAX_AQ_BUF_SIZE); @@ -2238,6 +2251,7 @@ err_setup_tx: static int i40evf_close(struct net_device *netdev) { struct i40evf_adapter *adapter = netdev_priv(netdev); + int status; if (adapter->state <= __I40EVF_DOWN_PENDING) return 0; @@ -2255,7 +2269,18 @@ static int i40evf_close(struct net_device *netdev) * still active and can DMA into memory. Resources are cleared in * i40evf_virtchnl_completion() after we get confirmation from the PF * driver that the rings have been stopped. + * + * Also, we wait for state to transition to __I40EVF_DOWN before + * returning. State change occurs in i40evf_virtchnl_completion() after + * VF resources are released (which occurs after PF driver processes and + * responds to admin queue commands). */ + + status = wait_event_timeout(adapter->down_waitqueue, + adapter->state == __I40EVF_DOWN, + msecs_to_jiffies(200)); + if (!status) + netdev_warn(netdev, "Device resources not yet released\n"); return 0; } @@ -2282,6 +2307,28 @@ static int i40evf_change_mtu(struct net_device *netdev, int new_mtu) } /** + * i40e_set_features - set the netdev feature flags + * @netdev: ptr to the netdev being adjusted + * @features: the feature set that the stack is suggesting + * Note: expects to be called while under rtnl_lock() + **/ +static int i40evf_set_features(struct net_device *netdev, + netdev_features_t features) +{ + struct i40evf_adapter *adapter = netdev_priv(netdev); + + if (!VLAN_ALLOWED(adapter)) + return -EINVAL; + + if (features & NETIF_F_HW_VLAN_CTAG_RX) + adapter->aq_required |= I40EVF_FLAG_AQ_ENABLE_VLAN_STRIPPING; + else + adapter->aq_required |= I40EVF_FLAG_AQ_DISABLE_VLAN_STRIPPING; + + return 0; +} + +/** * i40evf_features_check - Validate encapsulated packet conforms to limits * @skb: skb buff * @netdev: This physical port's netdev @@ -2356,7 +2403,7 @@ static netdev_features_t i40evf_fix_features(struct net_device *netdev, struct i40evf_adapter *adapter = netdev_priv(netdev); features &= ~I40EVF_VLAN_FEATURES; - if (adapter->vf_res->vf_offload_flags & VIRTCHNL_VF_OFFLOAD_VLAN) + if (adapter->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_VLAN) features |= I40EVF_VLAN_FEATURES; return features; } @@ -2374,6 +2421,7 @@ static const struct net_device_ops i40evf_netdev_ops = { .ndo_vlan_rx_kill_vid = i40evf_vlan_rx_kill_vid, .ndo_features_check = i40evf_features_check, .ndo_fix_features = i40evf_fix_features, + .ndo_set_features = i40evf_set_features, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = i40evf_netpoll, #endif @@ -2443,7 +2491,7 @@ int i40evf_process_config(struct i40evf_adapter *adapter) /* advertise to stack only if offloads for encapsulated packets is * supported */ - if (vfres->vf_offload_flags & VIRTCHNL_VF_OFFLOAD_ENCAP) { + if (vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_ENCAP) { hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL | NETIF_F_GSO_GRE | NETIF_F_GSO_GRE_CSUM | @@ -2453,7 +2501,7 @@ int i40evf_process_config(struct i40evf_adapter *adapter) NETIF_F_GSO_PARTIAL | 0; - if (!(vfres->vf_offload_flags & + if (!(vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM)) netdev->gso_partial_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM; @@ -2481,7 +2529,7 @@ int i40evf_process_config(struct i40evf_adapter *adapter) adapter->vsi.work_limit = I40E_DEFAULT_IRQ_WORK; vsi->netdev = adapter->netdev; vsi->qs_handle = adapter->vsi_res->qset_handle; - if (vfres->vf_offload_flags & VIRTCHNL_VF_OFFLOAD_RSS_PF) { + if (vfres->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_RSS_PF) { adapter->rss_key_size = vfres->rss_key_size; adapter->rss_lut_size = vfres->rss_lut_size; } else { @@ -2625,7 +2673,7 @@ static void i40evf_init_task(struct work_struct *work) /* MTU range: 68 - 9710 */ netdev->min_mtu = ETH_MIN_MTU; - netdev->max_mtu = I40E_MAX_RXBUFFER - (ETH_HLEN + ETH_FCS_LEN); + netdev->max_mtu = I40E_MAX_RXBUFFER - I40E_PACKET_HDR_PAD; if (!is_valid_ether_addr(adapter->hw.mac.addr)) { dev_info(&pdev->dev, "Invalid MAC address %pM, using random\n", @@ -2649,7 +2697,7 @@ static void i40evf_init_task(struct work_struct *work) if (err) goto err_sw_init; i40evf_map_rings_to_vectors(adapter); - if (adapter->vf_res->vf_offload_flags & + if (adapter->vf_res->vf_cap_flags & VIRTCHNL_VF_OFFLOAD_WB_ON_ITR) adapter->flags |= I40EVF_FLAG_WB_ON_ITR_CAPABLE; @@ -2683,6 +2731,7 @@ static void i40evf_init_task(struct work_struct *work) adapter->state = __I40EVF_DOWN; set_bit(__I40E_VSI_DOWN, adapter->vsi.state); i40evf_misc_irq_enable(adapter); + wake_up(&adapter->down_waitqueue); adapter->rss_key = kzalloc(adapter->rss_key_size, GFP_KERNEL); adapter->rss_lut = kzalloc(adapter->rss_lut_size, GFP_KERNEL); @@ -2844,6 +2893,9 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) schedule_delayed_work(&adapter->init_task, msecs_to_jiffies(5 * (pdev->devfn & 0x07))); + /* Setup the wait queue for indicating transition to down status */ + init_waitqueue_head(&adapter->down_waitqueue); + return 0; err_ioremap: diff --git a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c index d2bb250a71af..85876f4fb1fb 100644 --- a/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c +++ b/drivers/net/ethernet/intel/i40evf/i40evf_virtchnl.c @@ -821,6 +821,46 @@ void i40evf_set_rss_lut(struct i40evf_adapter *adapter) } /** + * i40evf_enable_vlan_stripping + * @adapter: adapter structure + * + * Request VLAN header stripping to be enabled + **/ +void i40evf_enable_vlan_stripping(struct i40evf_adapter *adapter) +{ + if (adapter->current_op != VIRTCHNL_OP_UNKNOWN) { + /* bail because we already have a command pending */ + dev_err(&adapter->pdev->dev, "Cannot enable stripping, command %d pending\n", + adapter->current_op); + return; + } + adapter->current_op = VIRTCHNL_OP_ENABLE_VLAN_STRIPPING; + adapter->aq_required &= ~I40EVF_FLAG_AQ_ENABLE_VLAN_STRIPPING; + i40evf_send_pf_msg(adapter, VIRTCHNL_OP_ENABLE_VLAN_STRIPPING, + NULL, 0); +} + +/** + * i40evf_disable_vlan_stripping + * @adapter: adapter structure + * + * Request VLAN header stripping to be disabled + **/ +void i40evf_disable_vlan_stripping(struct i40evf_adapter *adapter) +{ + if (adapter->current_op != VIRTCHNL_OP_UNKNOWN) { + /* bail because we already have a command pending */ + dev_err(&adapter->pdev->dev, "Cannot disable stripping, command %d pending\n", + adapter->current_op); + return; + } + adapter->current_op = VIRTCHNL_OP_DISABLE_VLAN_STRIPPING; + adapter->aq_required &= ~I40EVF_FLAG_AQ_DISABLE_VLAN_STRIPPING; + i40evf_send_pf_msg(adapter, VIRTCHNL_OP_DISABLE_VLAN_STRIPPING, + NULL, 0); +} + +/** * i40evf_print_link_message - print link up or down * @adapter: adapter structure * @@ -991,8 +1031,10 @@ void i40evf_virtchnl_completion(struct i40evf_adapter *adapter, case VIRTCHNL_OP_DISABLE_QUEUES: i40evf_free_all_tx_resources(adapter); i40evf_free_all_rx_resources(adapter); - if (adapter->state == __I40EVF_DOWN_PENDING) + if (adapter->state == __I40EVF_DOWN_PENDING) { adapter->state = __I40EVF_DOWN; + wake_up(&adapter->down_waitqueue); + } break; case VIRTCHNL_OP_VERSION: case VIRTCHNL_OP_CONFIG_IRQ_MAP: diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index 4a50870e0fa7..c37cc8bccf47 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -340,6 +340,9 @@ static s32 igb_init_phy_params_82575(struct e1000_hw *hw) phy->ops.set_d3_lplu_state = igb_set_d3_lplu_state_82580; phy->ops.force_speed_duplex = igb_phy_force_speed_duplex_m88; break; + case BCM54616_E_PHY_ID: + phy->type = e1000_phy_bcm54616; + break; default: ret_val = -E1000_ERR_PHY; goto out; @@ -1659,6 +1662,9 @@ static s32 igb_setup_copper_link_82575(struct e1000_hw *hw) case e1000_phy_82580: ret_val = igb_copper_link_setup_82580(hw); break; + case e1000_phy_bcm54616: + ret_val = 0; + break; default: ret_val = -E1000_ERR_PHY; break; diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index d8517779439b..1de82f247312 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -889,6 +889,7 @@ #define I210_I_PHY_ID 0x01410C00 #define M88E1543_E_PHY_ID 0x01410EA0 #define M88E1512_E_PHY_ID 0x01410DD0 +#define BCM54616_E_PHY_ID 0x03625D10 /* M88E1000 Specific Registers */ #define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */ diff --git a/drivers/net/ethernet/intel/igb/e1000_hw.h b/drivers/net/ethernet/intel/igb/e1000_hw.h index 2fb2213cd562..6c9485ab4b57 100644 --- a/drivers/net/ethernet/intel/igb/e1000_hw.h +++ b/drivers/net/ethernet/intel/igb/e1000_hw.h @@ -128,6 +128,7 @@ enum e1000_phy_type { e1000_phy_ife, e1000_phy_82580, e1000_phy_i210, + e1000_phy_bcm54616, }; enum e1000_bus_type { @@ -491,13 +492,16 @@ struct e1000_fc_info { struct e1000_mbx_operations { s32 (*init_params)(struct e1000_hw *hw); - s32 (*read)(struct e1000_hw *, u32 *, u16, u16); - s32 (*write)(struct e1000_hw *, u32 *, u16, u16); - s32 (*read_posted)(struct e1000_hw *, u32 *, u16, u16); - s32 (*write_posted)(struct e1000_hw *, u32 *, u16, u16); - s32 (*check_for_msg)(struct e1000_hw *, u16); - s32 (*check_for_ack)(struct e1000_hw *, u16); - s32 (*check_for_rst)(struct e1000_hw *, u16); + s32 (*read)(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id, + bool unlock); + s32 (*write)(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id); + s32 (*read_posted)(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id); + s32 (*write_posted)(struct e1000_hw *hw, u32 *msg, u16 size, + u16 mbx_id); + s32 (*check_for_msg)(struct e1000_hw *hw, u16 mbx_id); + s32 (*check_for_ack)(struct e1000_hw *hw, u16 mbx_id); + s32 (*check_for_rst)(struct e1000_hw *hw, u16 mbx_id); + s32 (*unlock)(struct e1000_hw *hw, u16 mbx_id); }; struct e1000_mbx_stats { diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.c b/drivers/net/ethernet/intel/igb/e1000_mbx.c index 00e263f0c030..bffd58f7b2a1 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mbx.c +++ b/drivers/net/ethernet/intel/igb/e1000_mbx.c @@ -32,7 +32,8 @@ * * returns SUCCESS if it successfully read message from buffer **/ -s32 igb_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id) +s32 igb_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id, + bool unlock) { struct e1000_mbx_info *mbx = &hw->mbx; s32 ret_val = -E1000_ERR_MBX; @@ -42,7 +43,7 @@ s32 igb_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id) size = mbx->size; if (mbx->ops.read) - ret_val = mbx->ops.read(hw, msg, size, mbx_id); + ret_val = mbx->ops.read(hw, msg, size, mbx_id, unlock); return ret_val; } @@ -125,6 +126,24 @@ s32 igb_check_for_rst(struct e1000_hw *hw, u16 mbx_id) } /** + * igb_unlock_mbx - unlock the mailbox + * @hw: pointer to the HW structure + * @mbx_id: id of mailbox to check + * + * returns SUCCESS if the mailbox was unlocked or else ERR_MBX + **/ +s32 igb_unlock_mbx(struct e1000_hw *hw, u16 mbx_id) +{ + struct e1000_mbx_info *mbx = &hw->mbx; + s32 ret_val = -E1000_ERR_MBX; + + if (mbx->ops.unlock) + ret_val = mbx->ops.unlock(hw, mbx_id); + + return ret_val; +} + +/** * igb_poll_for_msg - Wait for message notification * @hw: pointer to the HW structure * @mbx_id: id of mailbox to write @@ -204,7 +223,7 @@ static s32 igb_read_posted_mbx(struct e1000_hw *hw, u32 *msg, u16 size, ret_val = igb_poll_for_msg(hw, mbx_id); if (!ret_val) - ret_val = mbx->ops.read(hw, msg, size, mbx_id); + ret_val = mbx->ops.read(hw, msg, size, mbx_id, true); out: return ret_val; } @@ -341,6 +360,26 @@ static s32 igb_obtain_mbx_lock_pf(struct e1000_hw *hw, u16 vf_number) } /** + * igb_release_mbx_lock_pf - release mailbox lock + * @hw: pointer to the HW structure + * @vf_number: the VF index + * + * return SUCCESS if we released the mailbox lock + **/ +static s32 igb_release_mbx_lock_pf(struct e1000_hw *hw, u16 vf_number) +{ + u32 p2v_mailbox; + + /* drop PF lock of mailbox, if set */ + p2v_mailbox = rd32(E1000_P2VMAILBOX(vf_number)); + if (p2v_mailbox & E1000_P2VMAILBOX_PFU) + wr32(E1000_P2VMAILBOX(vf_number), + p2v_mailbox & ~E1000_P2VMAILBOX_PFU); + + return 0; +} + +/** * igb_write_mbx_pf - Places a message in the mailbox * @hw: pointer to the HW structure * @msg: The message buffer @@ -385,13 +424,14 @@ out_no_write: * @msg: The message buffer * @size: Length of buffer * @vf_number: the VF index + * @unlock: unlock the mailbox when done? * * This function copies a message from the mailbox buffer to the caller's * memory buffer. The presumption is that the caller knows that there was * a message due to a VF request so no polling for message is needed. **/ static s32 igb_read_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size, - u16 vf_number) + u16 vf_number, bool unlock) { s32 ret_val; u16 i; @@ -405,8 +445,12 @@ static s32 igb_read_mbx_pf(struct e1000_hw *hw, u32 *msg, u16 size, for (i = 0; i < size; i++) msg[i] = array_rd32(E1000_VMBMEM(vf_number), i); - /* Acknowledge the message and release buffer */ - wr32(E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_ACK); + /* Acknowledge the message and release mailbox lock (or not) */ + if (unlock) + wr32(E1000_P2VMAILBOX(vf_number), E1000_P2VMAILBOX_ACK); + else + wr32(E1000_P2VMAILBOX(vf_number), + E1000_P2VMAILBOX_ACK | E1000_P2VMAILBOX_PFU); /* update stats */ hw->mbx.stats.msgs_rx++; @@ -437,6 +481,7 @@ s32 igb_init_mbx_params_pf(struct e1000_hw *hw) mbx->ops.check_for_msg = igb_check_for_msg_pf; mbx->ops.check_for_ack = igb_check_for_ack_pf; mbx->ops.check_for_rst = igb_check_for_rst_pf; + mbx->ops.unlock = igb_release_mbx_lock_pf; mbx->stats.msgs_tx = 0; mbx->stats.msgs_rx = 0; diff --git a/drivers/net/ethernet/intel/igb/e1000_mbx.h b/drivers/net/ethernet/intel/igb/e1000_mbx.h index 3e7fed73df15..a62b08e1572e 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mbx.h +++ b/drivers/net/ethernet/intel/igb/e1000_mbx.h @@ -67,11 +67,13 @@ #define E1000_PF_CONTROL_MSG 0x0100 /* PF control message */ -s32 igb_read_mbx(struct e1000_hw *, u32 *, u16, u16); -s32 igb_write_mbx(struct e1000_hw *, u32 *, u16, u16); -s32 igb_check_for_msg(struct e1000_hw *, u16); -s32 igb_check_for_ack(struct e1000_hw *, u16); -s32 igb_check_for_rst(struct e1000_hw *, u16); -s32 igb_init_mbx_params_pf(struct e1000_hw *); +s32 igb_read_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id, + bool unlock); +s32 igb_write_mbx(struct e1000_hw *hw, u32 *msg, u16 size, u16 mbx_id); +s32 igb_check_for_msg(struct e1000_hw *hw, u16 mbx_id); +s32 igb_check_for_ack(struct e1000_hw *hw, u16 mbx_id); +s32 igb_check_for_rst(struct e1000_hw *hw, u16 mbx_id); +s32 igb_unlock_mbx(struct e1000_hw *hw, u16 mbx_id); +s32 igb_init_mbx_params_pf(struct e1000_hw *hw); #endif /* _E1000_MBX_H_ */ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index ec62410b035a..fd4a46b03cc8 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1791,6 +1791,8 @@ void igb_down(struct igb_adapter *adapter) wr32(E1000_RCTL, rctl & ~E1000_RCTL_EN); /* flush and sleep below */ + igb_nfc_filter_exit(adapter); + netif_carrier_off(netdev); netif_tx_stop_all_queues(netdev); @@ -3317,8 +3319,6 @@ static int __igb_close(struct net_device *netdev, bool suspending) igb_down(adapter); igb_free_irq(adapter); - igb_nfc_filter_exit(adapter); - igb_free_all_tx_resources(adapter); igb_free_all_rx_resources(adapter); @@ -5380,7 +5380,8 @@ netdev_tx_t igb_xmit_frame_ring(struct sk_buff *skb, if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP)) { struct igb_adapter *adapter = netdev_priv(tx_ring->netdev); - if (!test_and_set_bit_lock(__IGB_PTP_TX_IN_PROGRESS, + if (adapter->tstamp_config.tx_type & HWTSTAMP_TX_ON && + !test_and_set_bit_lock(__IGB_PTP_TX_IN_PROGRESS, &adapter->state)) { skb_shinfo(skb)->tx_flags |= SKBTX_IN_PROGRESS; tx_flags |= IGB_TX_FLAGS_TSTAMP; @@ -5745,8 +5746,6 @@ static void igb_tsync_interrupt(struct igb_adapter *adapter) event.type = PTP_CLOCK_PPS; if (adapter->ptp_caps.pps) ptp_clock_event(adapter->ptp_clock, &event); - else - dev_err(&adapter->pdev->dev, "unexpected SYS WRAP"); ack |= TSINTR_SYS_WRAP; } @@ -6676,32 +6675,33 @@ static void igb_rcv_msg_from_vf(struct igb_adapter *adapter, u32 vf) struct vf_data_storage *vf_data = &adapter->vf_data[vf]; s32 retval; - retval = igb_read_mbx(hw, msgbuf, E1000_VFMAILBOX_SIZE, vf); + retval = igb_read_mbx(hw, msgbuf, E1000_VFMAILBOX_SIZE, vf, false); if (retval) { /* if receive failed revoke VF CTS stats and restart init */ dev_err(&pdev->dev, "Error receiving message from VF\n"); vf_data->flags &= ~IGB_VF_FLAG_CTS; if (!time_after(jiffies, vf_data->last_nack + (2 * HZ))) - return; + goto unlock; goto out; } /* this is a message we already processed, do nothing */ if (msgbuf[0] & (E1000_VT_MSGTYPE_ACK | E1000_VT_MSGTYPE_NACK)) - return; + goto unlock; /* until the vf completes a reset it should not be * allowed to start any configuration. */ if (msgbuf[0] == E1000_VF_RESET) { + /* unlocks mailbox */ igb_vf_reset_msg(adapter, vf); return; } if (!(vf_data->flags & IGB_VF_FLAG_CTS)) { if (!time_after(jiffies, vf_data->last_nack + (2 * HZ))) - return; + goto unlock; retval = -1; goto out; } @@ -6742,7 +6742,12 @@ out: else msgbuf[0] |= E1000_VT_MSGTYPE_ACK; + /* unlocks mailbox */ igb_write_mbx(hw, msgbuf, 1, vf); + return; + +unlock: + igb_unlock_mbx(hw, vf); } static void igb_msg_task(struct igb_adapter *adapter) diff --git a/drivers/net/ethernet/intel/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c index 34faa113a8a0..a127688e83e6 100644 --- a/drivers/net/ethernet/intel/igbvf/ethtool.c +++ b/drivers/net/ethernet/intel/igbvf/ethtool.c @@ -296,8 +296,12 @@ static int igbvf_link_test(struct igbvf_adapter *adapter, u64 *data) struct e1000_hw *hw = &adapter->hw; *data = 0; + spin_lock_bh(&hw->mbx_lock); + hw->mac.ops.check_for_link(hw); + spin_unlock_bh(&hw->mbx_lock); + if (!(er32(STATUS) & E1000_STATUS_LU)) *data = 1; diff --git a/drivers/net/ethernet/intel/igbvf/mbx.c b/drivers/net/ethernet/intel/igbvf/mbx.c index 01752f44ace2..c9a441632e9f 100644 --- a/drivers/net/ethernet/intel/igbvf/mbx.c +++ b/drivers/net/ethernet/intel/igbvf/mbx.c @@ -264,6 +264,8 @@ static s32 e1000_write_mbx_vf(struct e1000_hw *hw, u32 *msg, u16 size) s32 err; u16 i; + WARN_ON_ONCE(!spin_is_locked(&hw->mbx_lock)); + /* lock the mailbox to prevent pf/vf race condition */ err = e1000_obtain_mbx_lock_vf(hw); if (err) @@ -300,6 +302,8 @@ static s32 e1000_read_mbx_vf(struct e1000_hw *hw, u32 *msg, u16 size) s32 err; u16 i; + WARN_ON_ONCE(!spin_is_locked(&hw->mbx_lock)); + /* lock the mailbox to prevent pf/vf race condition */ err = e1000_obtain_mbx_lock_vf(hw); if (err) diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 1b9cbbe88f6f..1ed556911b14 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -1235,7 +1235,12 @@ static void igbvf_set_rlpml(struct igbvf_adapter *adapter) struct e1000_hw *hw = &adapter->hw; max_frame_size = adapter->max_frame_size + VLAN_TAG_SIZE; + + spin_lock_bh(&hw->mbx_lock); + e1000_rlpml_set_vf(hw, max_frame_size); + + spin_unlock_bh(&hw->mbx_lock); } static int igbvf_vlan_rx_add_vid(struct net_device *netdev, @@ -1244,10 +1249,16 @@ static int igbvf_vlan_rx_add_vid(struct net_device *netdev, struct igbvf_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; + spin_lock_bh(&hw->mbx_lock); + if (hw->mac.ops.set_vfta(hw, vid, true)) { dev_err(&adapter->pdev->dev, "Failed to add vlan id %d\n", vid); + spin_unlock_bh(&hw->mbx_lock); return -EINVAL; } + + spin_unlock_bh(&hw->mbx_lock); + set_bit(vid, adapter->active_vlans); return 0; } @@ -1258,11 +1269,17 @@ static int igbvf_vlan_rx_kill_vid(struct net_device *netdev, struct igbvf_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; + spin_lock_bh(&hw->mbx_lock); + if (hw->mac.ops.set_vfta(hw, vid, false)) { dev_err(&adapter->pdev->dev, "Failed to remove vlan id %d\n", vid); + spin_unlock_bh(&hw->mbx_lock); return -EINVAL; } + + spin_unlock_bh(&hw->mbx_lock); + clear_bit(vid, adapter->active_vlans); return 0; } @@ -1428,7 +1445,11 @@ static void igbvf_set_multi(struct net_device *netdev) netdev_for_each_mc_addr(ha, netdev) memcpy(mta_list + (i++ * ETH_ALEN), ha->addr, ETH_ALEN); + spin_lock_bh(&hw->mbx_lock); + hw->mac.ops.update_mc_addr_list(hw, mta_list, i, 0, 0); + + spin_unlock_bh(&hw->mbx_lock); kfree(mta_list); } @@ -1449,16 +1470,24 @@ static int igbvf_set_uni(struct net_device *netdev) return -ENOSPC; } + spin_lock_bh(&hw->mbx_lock); + /* Clear all unicast MAC filters */ hw->mac.ops.set_uc_addr(hw, E1000_VF_MAC_FILTER_CLR, NULL); + spin_unlock_bh(&hw->mbx_lock); + if (!netdev_uc_empty(netdev)) { struct netdev_hw_addr *ha; /* Add MAC filters one by one */ netdev_for_each_uc_addr(ha, netdev) { + spin_lock_bh(&hw->mbx_lock); + hw->mac.ops.set_uc_addr(hw, E1000_VF_MAC_FILTER_ADD, ha->addr); + + spin_unlock_bh(&hw->mbx_lock); udelay(200); } } @@ -1503,12 +1532,16 @@ static void igbvf_reset(struct igbvf_adapter *adapter) struct net_device *netdev = adapter->netdev; struct e1000_hw *hw = &adapter->hw; + spin_lock_bh(&hw->mbx_lock); + /* Allow time for pending master requests to run */ if (mac->ops.reset_hw(hw)) dev_err(&adapter->pdev->dev, "PF still resetting\n"); mac->ops.init_hw(hw); + spin_unlock_bh(&hw->mbx_lock); + if (is_valid_ether_addr(adapter->hw.mac.addr)) { memcpy(netdev->dev_addr, adapter->hw.mac.addr, netdev->addr_len); @@ -1643,6 +1676,7 @@ static int igbvf_sw_init(struct igbvf_adapter *adapter) igbvf_irq_disable(adapter); spin_lock_init(&adapter->stats_lock); + spin_lock_init(&adapter->hw.mbx_lock); set_bit(__IGBVF_DOWN, &adapter->state); return 0; @@ -1786,8 +1820,12 @@ static int igbvf_set_mac(struct net_device *netdev, void *p) memcpy(hw->mac.addr, addr->sa_data, netdev->addr_len); + spin_lock_bh(&hw->mbx_lock); + hw->mac.ops.rar_set(hw, hw->mac.addr, 0); + spin_unlock_bh(&hw->mbx_lock); + if (!ether_addr_equal(addr->sa_data, hw->mac.addr)) return -EADDRNOTAVAIL; @@ -1858,7 +1896,12 @@ static bool igbvf_has_link(struct igbvf_adapter *adapter) if (test_bit(__IGBVF_DOWN, &adapter->state)) return false; + spin_lock_bh(&hw->mbx_lock); + ret_val = hw->mac.ops.check_for_link(hw); + + spin_unlock_bh(&hw->mbx_lock); + link_active = !hw->mac.get_link_status; /* if check for link returns error we will need to reset */ @@ -2808,6 +2851,8 @@ static int igbvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->min_mtu = ETH_MIN_MTU; netdev->max_mtu = MAX_STD_JUMBO_FRAME_SIZE; + spin_lock_bh(&hw->mbx_lock); + /*reset the controller to put the device in a known good state */ err = hw->mac.ops.reset_hw(hw); if (err) { @@ -2824,6 +2869,8 @@ static int igbvf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->addr_len); } + spin_unlock_bh(&hw->mbx_lock); + if (!is_valid_ether_addr(netdev->dev_addr)) { dev_info(&pdev->dev, "Assigning random MAC address.\n"); eth_hw_addr_random(netdev); diff --git a/drivers/net/ethernet/intel/igbvf/vf.c b/drivers/net/ethernet/intel/igbvf/vf.c index 528be116184e..9577ccf4b26a 100644 --- a/drivers/net/ethernet/intel/igbvf/vf.c +++ b/drivers/net/ethernet/intel/igbvf/vf.c @@ -149,7 +149,7 @@ static s32 e1000_reset_hw_vf(struct e1000_hw *hw) msgbuf[0] = E1000_VF_RESET; mbx->ops.write_posted(hw, msgbuf, 1); - msleep(10); + mdelay(10); /* set our "perm_addr" based on info provided by PF */ ret_val = mbx->ops.read_posted(hw, msgbuf, 3); @@ -230,6 +230,7 @@ static void e1000_update_mc_addr_list_vf(struct e1000_hw *hw, u16 *hash_list = (u16 *)&msgbuf[1]; u32 hash_value; u32 cnt, i; + s32 ret_val; /* Each entry in the list uses 1 16 bit word. We have 30 * 16 bit words available in our HW msg buffer (minus 1 for the @@ -250,7 +251,9 @@ static void e1000_update_mc_addr_list_vf(struct e1000_hw *hw, mc_addr_list += ETH_ALEN; } - mbx->ops.write_posted(hw, msgbuf, E1000_VFMAILBOX_SIZE); + ret_val = mbx->ops.write_posted(hw, msgbuf, E1000_VFMAILBOX_SIZE); + if (!ret_val) + mbx->ops.read_posted(hw, msgbuf, 1); } /** @@ -293,11 +296,14 @@ void e1000_rlpml_set_vf(struct e1000_hw *hw, u16 max_size) { struct e1000_mbx_info *mbx = &hw->mbx; u32 msgbuf[2]; + s32 ret_val; msgbuf[0] = E1000_VF_SET_LPE; msgbuf[1] = max_size; - mbx->ops.write_posted(hw, msgbuf, 2); + ret_val = mbx->ops.write_posted(hw, msgbuf, 2); + if (!ret_val) + mbx->ops.read_posted(hw, msgbuf, 1); } /** diff --git a/drivers/net/ethernet/intel/igbvf/vf.h b/drivers/net/ethernet/intel/igbvf/vf.h index 4cf78b0dec50..d213eefb6169 100644 --- a/drivers/net/ethernet/intel/igbvf/vf.h +++ b/drivers/net/ethernet/intel/igbvf/vf.h @@ -245,6 +245,7 @@ struct e1000_hw { struct e1000_mac_info mac; struct e1000_mbx_info mbx; + spinlock_t mbx_lock; /* serializes mailbox ops */ union { struct e1000_dev_spec_vf vf; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c index 4e35e7017f3d..2c19070d2a0b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_common.c @@ -79,16 +79,28 @@ bool ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw) switch (hw->phy.media_type) { case ixgbe_media_type_fiber: - hw->mac.ops.check_link(hw, &speed, &link_up, false); - /* if link is down, assume supported */ - if (link_up) - supported = speed == IXGBE_LINK_SPEED_1GB_FULL ? + /* flow control autoneg black list */ + switch (hw->device_id) { + case IXGBE_DEV_ID_X550EM_A_SFP: + case IXGBE_DEV_ID_X550EM_A_SFP_N: + supported = false; + break; + default: + hw->mac.ops.check_link(hw, &speed, &link_up, false); + /* if link is down, assume supported */ + if (link_up) + supported = speed == IXGBE_LINK_SPEED_1GB_FULL ? true : false; - else - supported = true; + else + supported = true; + } + break; case ixgbe_media_type_backplane: - supported = true; + if (hw->device_id == IXGBE_DEV_ID_X550EM_X_XFI) + supported = false; + else + supported = true; break; case ixgbe_media_type_copper: /* only some copper devices support flow control autoneg */ @@ -111,6 +123,10 @@ bool ixgbe_device_supports_autoneg_fc(struct ixgbe_hw *hw) break; } + if (!supported) + hw_dbg(hw, "Device %x does not support flow control autoneg\n", + hw->device_id); + return supported; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c index b45fdc98033d..f1bfae0c41d0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_lib.c @@ -1018,8 +1018,12 @@ static void ixgbe_free_q_vector(struct ixgbe_adapter *adapter, int v_idx) struct ixgbe_q_vector *q_vector = adapter->q_vector[v_idx]; struct ixgbe_ring *ring; - ixgbe_for_each_ring(ring, q_vector->tx) - adapter->tx_ring[ring->queue_index] = NULL; + ixgbe_for_each_ring(ring, q_vector->tx) { + if (ring_is_xdp(ring)) + adapter->xdp_ring[ring->queue_index] = NULL; + else + adapter->tx_ring[ring->queue_index] = NULL; + } ixgbe_for_each_ring(ring, q_vector->rx) adapter->rx_ring[ring->queue_index] = NULL; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index f1dbdf26d8e1..d962368d08d0 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -386,7 +386,7 @@ u32 ixgbe_read_reg(struct ixgbe_hw *hw, u32 reg) if (ixgbe_removed(reg_addr)) return IXGBE_FAILED_READ_REG; if (unlikely(hw->phy.nw_mng_if_sel & - IXGBE_NW_MNG_IF_SEL_ENABLE_10_100M)) { + IXGBE_NW_MNG_IF_SEL_SGMII_ENABLE)) { struct ixgbe_adapter *adapter; int i; @@ -2214,7 +2214,7 @@ static struct sk_buff *ixgbe_run_xdp(struct ixgbe_adapter *adapter, struct ixgbe_ring *rx_ring, struct xdp_buff *xdp) { - int result = IXGBE_XDP_PASS; + int err, result = IXGBE_XDP_PASS; struct bpf_prog *xdp_prog; u32 act; @@ -2231,6 +2231,13 @@ static struct sk_buff *ixgbe_run_xdp(struct ixgbe_adapter *adapter, case XDP_TX: result = ixgbe_xmit_xdp_ring(adapter, xdp); break; + case XDP_REDIRECT: + err = xdp_do_redirect(adapter->netdev, xdp, xdp_prog); + if (!err) + result = IXGBE_XDP_TX; + else + result = IXGBE_XDP_CONSUMED; + break; default: bpf_warn_invalid_xdp_action(act); /* fallthrough */ @@ -2408,6 +2415,8 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, */ wmb(); writel(ring->next_to_use, ring->tail); + + xdp_do_flush_map(); } u64_stats_update_begin(&rx_ring->syncp); @@ -5810,6 +5819,9 @@ void ixgbe_down(struct ixgbe_adapter *adapter) usleep_range(10000, 20000); + /* synchronize_sched() needed for pending XDP buffers to drain */ + if (adapter->xdp_ring[0]) + synchronize_sched(); netif_tx_stop_all_queues(netdev); /* call carrier off first to avoid false dev_watchdog timeouts */ @@ -8839,7 +8851,6 @@ static int ixgbe_delete_clsu32(struct ixgbe_adapter *adapter, } static int ixgbe_configure_clsu32_add_hnode(struct ixgbe_adapter *adapter, - __be16 protocol, struct tc_cls_u32_offload *cls) { u32 uhtid = TC_U32_USERHTID(cls->hnode.handle); @@ -8941,7 +8952,7 @@ static int parse_tc_actions(struct ixgbe_adapter *adapter, LIST_HEAD(actions); int err; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return -EINVAL; tcf_exts_to_list(exts, &actions); @@ -9025,9 +9036,9 @@ static int ixgbe_clsu32_build_input(struct ixgbe_fdir_filter *input, } static int ixgbe_configure_clsu32(struct ixgbe_adapter *adapter, - __be16 protocol, struct tc_cls_u32_offload *cls) { + __be16 protocol = cls->common.protocol; u32 loc = cls->knode.handle & 0xfffff; struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_mat_field *field_ptr; @@ -9214,41 +9225,49 @@ free_jump: return err; } -static int __ixgbe_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +static int ixgbe_setup_tc_cls_u32(struct net_device *dev, + struct tc_cls_u32_offload *cls_u32) { struct ixgbe_adapter *adapter = netdev_priv(dev); - if (chain_index) + if (!is_classid_clsact_ingress(cls_u32->common.classid) || + cls_u32->common.chain_index) return -EOPNOTSUPP; - if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) && - tc->type == TC_SETUP_CLSU32) { - switch (tc->cls_u32->command) { - case TC_CLSU32_NEW_KNODE: - case TC_CLSU32_REPLACE_KNODE: - return ixgbe_configure_clsu32(adapter, - proto, tc->cls_u32); - case TC_CLSU32_DELETE_KNODE: - return ixgbe_delete_clsu32(adapter, tc->cls_u32); - case TC_CLSU32_NEW_HNODE: - case TC_CLSU32_REPLACE_HNODE: - return ixgbe_configure_clsu32_add_hnode(adapter, proto, - tc->cls_u32); - case TC_CLSU32_DELETE_HNODE: - return ixgbe_configure_clsu32_del_hnode(adapter, - tc->cls_u32); - default: - return -EINVAL; - } + switch (cls_u32->command) { + case TC_CLSU32_NEW_KNODE: + case TC_CLSU32_REPLACE_KNODE: + return ixgbe_configure_clsu32(adapter, cls_u32); + case TC_CLSU32_DELETE_KNODE: + return ixgbe_delete_clsu32(adapter, cls_u32); + case TC_CLSU32_NEW_HNODE: + case TC_CLSU32_REPLACE_HNODE: + return ixgbe_configure_clsu32_add_hnode(adapter, cls_u32); + case TC_CLSU32_DELETE_HNODE: + return ixgbe_configure_clsu32_del_hnode(adapter, cls_u32); + default: + return -EOPNOTSUPP; } +} - if (tc->type != TC_SETUP_MQPRIO) - return -EINVAL; - - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; +static int ixgbe_setup_tc_mqprio(struct net_device *dev, + struct tc_mqprio_qopt *mqprio) +{ + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + return ixgbe_setup_tc(dev, mqprio->num_tc); +} - return ixgbe_setup_tc(dev, tc->mqprio->num_tc); +static int __ixgbe_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + switch (type) { + case TC_SETUP_CLSU32: + return ixgbe_setup_tc_cls_u32(dev, type_data); + case TC_SETUP_MQPRIO: + return ixgbe_setup_tc_mqprio(dev, type_data); + default: + return -EOPNOTSUPP; + } } #ifdef CONFIG_PCI_IOV @@ -9823,6 +9842,53 @@ static int ixgbe_xdp(struct net_device *dev, struct netdev_xdp *xdp) } } +static int ixgbe_xdp_xmit(struct net_device *dev, struct xdp_buff *xdp) +{ + struct ixgbe_adapter *adapter = netdev_priv(dev); + struct ixgbe_ring *ring; + int err; + + if (unlikely(test_bit(__IXGBE_DOWN, &adapter->state))) + return -ENETDOWN; + + /* During program transitions its possible adapter->xdp_prog is assigned + * but ring has not been configured yet. In this case simply abort xmit. + */ + ring = adapter->xdp_prog ? adapter->xdp_ring[smp_processor_id()] : NULL; + if (unlikely(!ring)) + return -ENXIO; + + err = ixgbe_xmit_xdp_ring(adapter, xdp); + if (err != IXGBE_XDP_TX) + return -ENOSPC; + + return 0; +} + +static void ixgbe_xdp_flush(struct net_device *dev) +{ + struct ixgbe_adapter *adapter = netdev_priv(dev); + struct ixgbe_ring *ring; + + /* Its possible the device went down between xdp xmit and flush so + * we need to ensure device is still up. + */ + if (unlikely(test_bit(__IXGBE_DOWN, &adapter->state))) + return; + + ring = adapter->xdp_prog ? adapter->xdp_ring[smp_processor_id()] : NULL; + if (unlikely(!ring)) + return; + + /* Force memory writes to complete before letting h/w know there + * are new descriptors to fetch. + */ + wmb(); + writel(ring->next_to_use, ring->tail); + + return; +} + static const struct net_device_ops ixgbe_netdev_ops = { .ndo_open = ixgbe_open, .ndo_stop = ixgbe_close, @@ -9869,6 +9935,8 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_udp_tunnel_del = ixgbe_del_udp_tunnel_port, .ndo_features_check = ixgbe_features_check, .ndo_xdp = ixgbe_xdp, + .ndo_xdp_xmit = ixgbe_xdp_xmit, + .ndo_xdp_flush = ixgbe_xdp_flush, }; /** diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 0760bd7eeb01..112d24c6c9ce 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -679,8 +679,9 @@ update_vlvfb: static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter, int vf, int index, unsigned char *mac_addr) { - struct list_head *pos; struct vf_macvlans *entry; + struct list_head *pos; + int retval = 0; if (index <= 1) { list_for_each(pos, &adapter->vf_mvs.l) { @@ -721,13 +722,15 @@ static int ixgbe_set_vf_macvlan(struct ixgbe_adapter *adapter, if (!entry || !entry->free) return -ENOSPC; + retval = ixgbe_add_mac_filter(adapter, mac_addr, vf); + if (retval < 0) + return retval; + entry->free = false; entry->is_macvlan = true; entry->vf = vf; memcpy(entry->vf_macvlan, mac_addr, ETH_ALEN); - ixgbe_add_mac_filter(adapter, mac_addr, vf); - return 0; } diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h index 9c2460c5ef1b..ffa0ee5cd0f5 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_type.h @@ -3778,8 +3778,8 @@ struct ixgbe_info { #define IXGBE_NW_MNG_IF_SEL_PHY_SPEED_1G BIT(19) #define IXGBE_NW_MNG_IF_SEL_PHY_SPEED_2_5G BIT(20) #define IXGBE_NW_MNG_IF_SEL_PHY_SPEED_10G BIT(21) -#define IXGBE_NW_MNG_IF_SEL_ENABLE_10_100M BIT(23) -#define IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE BIT(24) +#define IXGBE_NW_MNG_IF_SEL_SGMII_ENABLE BIT(25) +#define IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE BIT(24) /* X552 only */ #define IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD_SHIFT 3 #define IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD \ (0x1F << IXGBE_NW_MNG_IF_SEL_MDIO_PHY_ADD_SHIFT) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c index 72d84a065e34..19fbb2f28ea4 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_x550.c @@ -1555,9 +1555,14 @@ static s32 ixgbe_restart_an_internal_phy_x550em(struct ixgbe_hw *hw) **/ static s32 ixgbe_setup_ixfi_x550em(struct ixgbe_hw *hw, ixgbe_link_speed *speed) { + struct ixgbe_mac_info *mac = &hw->mac; s32 status; u32 reg_val; + /* iXFI is only supported with X552 */ + if (mac->type != ixgbe_mac_X550EM_x) + return IXGBE_ERR_LINK_SETUP; + /* Disable AN and force speed to 10G Serial. */ status = ixgbe_read_iosf_sb_reg_x550(hw, IXGBE_KRM_LINK_CTRL_1(hw->bus.lan_id), @@ -1874,8 +1879,10 @@ static s32 ixgbe_setup_mac_link_t_X550em(struct ixgbe_hw *hw, else force_speed = IXGBE_LINK_SPEED_1GB_FULL; - /* If internal link mode is XFI, then setup XFI internal link. */ - if (!(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) { + /* If X552 and internal link mode is XFI, then setup XFI internal link. + */ + if (hw->mac.type == ixgbe_mac_X550EM_x && + !(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE)) { status = ixgbe_setup_ixfi_x550em(hw, &force_speed); if (status) @@ -2404,17 +2411,30 @@ static s32 ixgbe_enable_lasi_ext_t_x550em(struct ixgbe_hw *hw) status = ixgbe_get_lasi_ext_t_x550em(hw, &lsc); /* Enable link status change alarm */ - status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK, - MDIO_MMD_AN, ®); - if (status) - return status; - reg |= IXGBE_MDIO_PMA_TX_VEN_LASI_INT_EN; + /* Enable the LASI interrupts on X552 devices to receive notifications + * of the link configurations of the external PHY and correspondingly + * support the configuration of the internal iXFI link, since iXFI does + * not support auto-negotiation. This is not required for X553 devices + * having KR support, which performs auto-negotiations and which is used + * as the internal link to the external PHY. Hence adding a check here + * to avoid enabling LASI interrupts for X553 devices. + */ + if (hw->mac.type != ixgbe_mac_x550em_a) { + status = hw->phy.ops.read_reg(hw, + IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK, + MDIO_MMD_AN, ®); + if (status) + return status; - status = hw->phy.ops.write_reg(hw, IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK, - MDIO_MMD_AN, reg); - if (status) - return status; + reg |= IXGBE_MDIO_PMA_TX_VEN_LASI_INT_EN; + + status = hw->phy.ops.write_reg(hw, + IXGBE_MDIO_PMA_TX_VEN_LASI_INT_MASK, + MDIO_MMD_AN, reg); + if (status) + return status; + } /* Enable high temperature failure and global fault alarms */ status = hw->phy.ops.read_reg(hw, IXGBE_MDIO_GLOBAL_INT_MASK, @@ -2615,7 +2635,8 @@ static s32 ixgbe_setup_internal_phy_t_x550em(struct ixgbe_hw *hw) if (hw->mac.ops.get_media_type(hw) != ixgbe_media_type_copper) return IXGBE_ERR_CONFIG; - if (hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE) { + if (!(hw->mac.type == ixgbe_mac_X550EM_x && + !(hw->phy.nw_mng_if_sel & IXGBE_NW_MNG_IF_SEL_INT_PHY_MODE))) { speed = IXGBE_LINK_SPEED_10GB_FULL | IXGBE_LINK_SPEED_1GB_FULL; return ixgbe_setup_kr_speed_x550em(hw, speed); @@ -2822,7 +2843,7 @@ static s32 ixgbe_setup_fc_x550em(struct ixgbe_hw *hw) { bool pause, asm_dir; u32 reg_val; - s32 rc; + s32 rc = 0; /* Validate the requested mode */ if (hw->fc.strict_ieee && hw->fc.requested_mode == ixgbe_fc_rx_pause) { @@ -2865,32 +2886,37 @@ static s32 ixgbe_setup_fc_x550em(struct ixgbe_hw *hw) return IXGBE_ERR_CONFIG; } - if (hw->device_id != IXGBE_DEV_ID_X550EM_X_KR && - hw->device_id != IXGBE_DEV_ID_X550EM_A_KR && - hw->device_id != IXGBE_DEV_ID_X550EM_A_KR_L) - return 0; - - rc = hw->mac.ops.read_iosf_sb_reg(hw, - IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id), - IXGBE_SB_IOSF_TARGET_KR_PHY, - ®_val); - if (rc) - return rc; - - reg_val &= ~(IXGBE_KRM_AN_CNTL_1_SYM_PAUSE | - IXGBE_KRM_AN_CNTL_1_ASM_PAUSE); - if (pause) - reg_val |= IXGBE_KRM_AN_CNTL_1_SYM_PAUSE; - if (asm_dir) - reg_val |= IXGBE_KRM_AN_CNTL_1_ASM_PAUSE; - rc = hw->mac.ops.write_iosf_sb_reg(hw, - IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id), - IXGBE_SB_IOSF_TARGET_KR_PHY, - reg_val); - - /* This device does not fully support AN. */ - hw->fc.disable_fc_autoneg = true; + switch (hw->device_id) { + case IXGBE_DEV_ID_X550EM_X_KR: + case IXGBE_DEV_ID_X550EM_A_KR: + case IXGBE_DEV_ID_X550EM_A_KR_L: + rc = hw->mac.ops.read_iosf_sb_reg(hw, + IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, + ®_val); + if (rc) + return rc; + reg_val &= ~(IXGBE_KRM_AN_CNTL_1_SYM_PAUSE | + IXGBE_KRM_AN_CNTL_1_ASM_PAUSE); + if (pause) + reg_val |= IXGBE_KRM_AN_CNTL_1_SYM_PAUSE; + if (asm_dir) + reg_val |= IXGBE_KRM_AN_CNTL_1_ASM_PAUSE; + rc = hw->mac.ops.write_iosf_sb_reg(hw, + IXGBE_KRM_AN_CNTL_1(hw->bus.lan_id), + IXGBE_SB_IOSF_TARGET_KR_PHY, + reg_val); + + /* This device does not fully support AN. */ + hw->fc.disable_fc_autoneg = true; + break; + case IXGBE_DEV_ID_X550EM_X_XFI: + hw->fc.disable_fc_autoneg = true; + break; + default: + break; + } return rc; } diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 9c94ea9b2b80..81c1fac00d33 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -183,8 +183,6 @@ static char mv643xx_eth_driver_version[] = "1.4"; #define DEFAULT_TX_QUEUE_SIZE 512 #define SKB_DMA_REALIGN ((PAGE_SIZE - NET_SKB_PAD) % SMP_CACHE_BYTES) -#define TSO_HEADER_SIZE 128 - /* Max number of allowed TCP segments for software TSO */ #define MV643XX_MAX_TSO_SEGS 100 #define MV643XX_MAX_SKB_DESCS (MV643XX_MAX_TSO_SEGS * 2 + MAX_SKB_FRAGS) @@ -1123,7 +1121,7 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force) struct sk_buff *skb = __skb_dequeue(&txq->tx_skb); if (!WARN_ON(!skb)) - dev_kfree_skb(skb); + dev_consume_skb_any(skb); } if (cmd_sts & ERROR_SUMMARY) { @@ -2026,7 +2024,7 @@ static void rxq_deinit(struct rx_queue *rxq) for (i = 0; i < rxq->rx_ring_size; i++) { if (rxq->rx_skb[i]) { - dev_kfree_skb(rxq->rx_skb[i]); + dev_consume_skb_any(rxq->rx_skb[i]); rxq->rx_desc_count--; } } diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 0aab74c2a209..64a04975bcf8 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -281,9 +281,6 @@ */ #define MVNETA_RSS_LU_TABLE_SIZE 1 -/* TSO header size */ -#define TSO_HEADER_SIZE 128 - /* Max number of Rx descriptors */ #define MVNETA_MAX_RXD 128 @@ -4332,7 +4329,7 @@ static int mvneta_probe(struct platform_device *pdev) } } - dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; + dev->features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_TSO; dev->hw_features |= dev->features; dev->vlan_features |= dev->features; dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 4d598ca8503a..dd0ee2691c86 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -18,6 +18,7 @@ #include <linux/inetdevice.h> #include <linux/mbus.h> #include <linux/module.h> +#include <linux/mfd/syscon.h> #include <linux/interrupt.h> #include <linux/cpumask.h> #include <linux/of.h> @@ -27,12 +28,15 @@ #include <linux/of_address.h> #include <linux/of_device.h> #include <linux/phy.h> +#include <linux/phy/phy.h> #include <linux/clk.h> #include <linux/hrtimer.h> #include <linux/ktime.h> +#include <linux/regmap.h> #include <uapi/linux/ppp_defs.h> #include <net/ip.h> #include <net/ipv6.h> +#include <net/tso.h> /* RX Fifo Registers */ #define MVPP2_RX_DATA_FIFO_SIZE_REG(port) (0x00 + 4 * (port)) @@ -120,6 +124,9 @@ #define MVPP2_TXQ_DESC_ADDR_REG 0x2084 #define MVPP2_TXQ_DESC_SIZE_REG 0x2088 #define MVPP2_TXQ_DESC_SIZE_MASK 0x3ff0 +#define MVPP2_TXQ_THRESH_REG 0x2094 +#define MVPP2_TXQ_THRESH_OFFSET 16 +#define MVPP2_TXQ_THRESH_MASK 0x3fff #define MVPP2_AGGR_TXQ_UPDATE_REG 0x2090 #define MVPP2_TXQ_INDEX_REG 0x2098 #define MVPP2_TXQ_PREF_BUF_REG 0x209c @@ -183,22 +190,25 @@ #define MVPP22_AXI_CODE_DOMAIN_SYSTEM 3 /* Interrupt Cause and Mask registers */ +#define MVPP2_ISR_TX_THRESHOLD_REG(port) (0x5140 + 4 * (port)) +#define MVPP2_MAX_ISR_TX_THRESHOLD 0xfffff0 + #define MVPP2_ISR_RX_THRESHOLD_REG(rxq) (0x5200 + 4 * (rxq)) #define MVPP2_MAX_ISR_RX_THRESHOLD 0xfffff0 -#define MVPP21_ISR_RXQ_GROUP_REG(rxq) (0x5400 + 4 * (rxq)) +#define MVPP21_ISR_RXQ_GROUP_REG(port) (0x5400 + 4 * (port)) -#define MVPP22_ISR_RXQ_GROUP_INDEX_REG 0x5400 +#define MVPP22_ISR_RXQ_GROUP_INDEX_REG 0x5400 #define MVPP22_ISR_RXQ_GROUP_INDEX_SUBGROUP_MASK 0xf -#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_MASK 0x380 -#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET 7 +#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_MASK 0x380 +#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET 7 #define MVPP22_ISR_RXQ_GROUP_INDEX_SUBGROUP_MASK 0xf -#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_MASK 0x380 +#define MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_MASK 0x380 -#define MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG 0x5404 -#define MVPP22_ISR_RXQ_SUB_GROUP_STARTQ_MASK 0x1f -#define MVPP22_ISR_RXQ_SUB_GROUP_SIZE_MASK 0xf00 -#define MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET 8 +#define MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG 0x5404 +#define MVPP22_ISR_RXQ_SUB_GROUP_STARTQ_MASK 0x1f +#define MVPP22_ISR_RXQ_SUB_GROUP_SIZE_MASK 0xf00 +#define MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET 8 #define MVPP2_ISR_ENABLE_REG(port) (0x5420 + 4 * (port)) #define MVPP2_ISR_ENABLE_INTERRUPT(mask) ((mask) & 0xffff) @@ -206,6 +216,7 @@ #define MVPP2_ISR_RX_TX_CAUSE_REG(port) (0x5480 + 4 * (port)) #define MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK 0xffff #define MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK 0xff0000 +#define MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET 16 #define MVPP2_CAUSE_RX_FIFO_OVERRUN_MASK BIT(24) #define MVPP2_CAUSE_FCS_ERR_MASK BIT(25) #define MVPP2_CAUSE_TX_FIFO_UNDERRUN_MASK BIT(26) @@ -265,7 +276,7 @@ #define MVPP2_BM_VIRT_RLS_REG 0x64c0 #define MVPP22_BM_ADDR_HIGH_RLS_REG 0x64c4 #define MVPP22_BM_ADDR_HIGH_PHYS_RLS_MASK 0xff -#define MVPP22_BM_ADDR_HIGH_VIRT_RLS_MASK 0xff00 +#define MVPP22_BM_ADDR_HIGH_VIRT_RLS_MASK 0xff00 #define MVPP22_BM_ADDR_HIGH_VIRT_RLS_SHIFT 8 /* TX Scheduler registers */ @@ -307,57 +318,87 @@ /* Per-port registers */ #define MVPP2_GMAC_CTRL_0_REG 0x0 -#define MVPP2_GMAC_PORT_EN_MASK BIT(0) -#define MVPP2_GMAC_MAX_RX_SIZE_OFFS 2 -#define MVPP2_GMAC_MAX_RX_SIZE_MASK 0x7ffc -#define MVPP2_GMAC_MIB_CNTR_EN_MASK BIT(15) +#define MVPP2_GMAC_PORT_EN_MASK BIT(0) +#define MVPP2_GMAC_PORT_TYPE_MASK BIT(1) +#define MVPP2_GMAC_MAX_RX_SIZE_OFFS 2 +#define MVPP2_GMAC_MAX_RX_SIZE_MASK 0x7ffc +#define MVPP2_GMAC_MIB_CNTR_EN_MASK BIT(15) #define MVPP2_GMAC_CTRL_1_REG 0x4 -#define MVPP2_GMAC_PERIODIC_XON_EN_MASK BIT(1) -#define MVPP2_GMAC_GMII_LB_EN_MASK BIT(5) -#define MVPP2_GMAC_PCS_LB_EN_BIT 6 -#define MVPP2_GMAC_PCS_LB_EN_MASK BIT(6) -#define MVPP2_GMAC_SA_LOW_OFFS 7 +#define MVPP2_GMAC_PERIODIC_XON_EN_MASK BIT(1) +#define MVPP2_GMAC_GMII_LB_EN_MASK BIT(5) +#define MVPP2_GMAC_PCS_LB_EN_BIT 6 +#define MVPP2_GMAC_PCS_LB_EN_MASK BIT(6) +#define MVPP2_GMAC_SA_LOW_OFFS 7 #define MVPP2_GMAC_CTRL_2_REG 0x8 -#define MVPP2_GMAC_INBAND_AN_MASK BIT(0) -#define MVPP2_GMAC_PCS_ENABLE_MASK BIT(3) -#define MVPP2_GMAC_PORT_RGMII_MASK BIT(4) -#define MVPP2_GMAC_PORT_RESET_MASK BIT(6) +#define MVPP2_GMAC_INBAND_AN_MASK BIT(0) +#define MVPP2_GMAC_FLOW_CTRL_MASK GENMASK(2, 1) +#define MVPP2_GMAC_PCS_ENABLE_MASK BIT(3) +#define MVPP2_GMAC_PORT_RGMII_MASK BIT(4) +#define MVPP2_GMAC_DISABLE_PADDING BIT(5) +#define MVPP2_GMAC_PORT_RESET_MASK BIT(6) #define MVPP2_GMAC_AUTONEG_CONFIG 0xc -#define MVPP2_GMAC_FORCE_LINK_DOWN BIT(0) -#define MVPP2_GMAC_FORCE_LINK_PASS BIT(1) -#define MVPP2_GMAC_CONFIG_MII_SPEED BIT(5) -#define MVPP2_GMAC_CONFIG_GMII_SPEED BIT(6) -#define MVPP2_GMAC_AN_SPEED_EN BIT(7) -#define MVPP2_GMAC_FC_ADV_EN BIT(9) -#define MVPP2_GMAC_CONFIG_FULL_DUPLEX BIT(12) -#define MVPP2_GMAC_AN_DUPLEX_EN BIT(13) +#define MVPP2_GMAC_FORCE_LINK_DOWN BIT(0) +#define MVPP2_GMAC_FORCE_LINK_PASS BIT(1) +#define MVPP2_GMAC_IN_BAND_AUTONEG BIT(2) +#define MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS BIT(3) +#define MVPP2_GMAC_CONFIG_MII_SPEED BIT(5) +#define MVPP2_GMAC_CONFIG_GMII_SPEED BIT(6) +#define MVPP2_GMAC_AN_SPEED_EN BIT(7) +#define MVPP2_GMAC_FC_ADV_EN BIT(9) +#define MVPP2_GMAC_FLOW_CTRL_AUTONEG BIT(11) +#define MVPP2_GMAC_CONFIG_FULL_DUPLEX BIT(12) +#define MVPP2_GMAC_AN_DUPLEX_EN BIT(13) +#define MVPP2_GMAC_STATUS0 0x10 +#define MVPP2_GMAC_STATUS0_LINK_UP BIT(0) #define MVPP2_GMAC_PORT_FIFO_CFG_1_REG 0x1c -#define MVPP2_GMAC_TX_FIFO_MIN_TH_OFFS 6 -#define MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK 0x1fc0 -#define MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(v) (((v) << 6) & \ +#define MVPP2_GMAC_TX_FIFO_MIN_TH_OFFS 6 +#define MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK 0x1fc0 +#define MVPP2_GMAC_TX_FIFO_MIN_TH_MASK(v) (((v) << 6) & \ MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK) +#define MVPP22_GMAC_INT_STAT 0x20 +#define MVPP22_GMAC_INT_STAT_LINK BIT(1) +#define MVPP22_GMAC_INT_MASK 0x24 +#define MVPP22_GMAC_INT_MASK_LINK_STAT BIT(1) #define MVPP22_GMAC_CTRL_4_REG 0x90 -#define MVPP22_CTRL4_EXT_PIN_GMII_SEL BIT(0) -#define MVPP22_CTRL4_DP_CLK_SEL BIT(5) -#define MVPP22_CTRL4_SYNC_BYPASS BIT(6) -#define MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE BIT(7) +#define MVPP22_CTRL4_EXT_PIN_GMII_SEL BIT(0) +#define MVPP22_CTRL4_DP_CLK_SEL BIT(5) +#define MVPP22_CTRL4_SYNC_BYPASS_DIS BIT(6) +#define MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE BIT(7) +#define MVPP22_GMAC_INT_SUM_MASK 0xa4 +#define MVPP22_GMAC_INT_SUM_MASK_LINK_STAT BIT(1) /* Per-port XGMAC registers. PPv2.2 only, only for GOP port 0, * relative to port->base. */ #define MVPP22_XLG_CTRL0_REG 0x100 -#define MVPP22_XLG_CTRL0_PORT_EN BIT(0) -#define MVPP22_XLG_CTRL0_MAC_RESET_DIS BIT(1) -#define MVPP22_XLG_CTRL0_MIB_CNT_DIS BIT(14) - +#define MVPP22_XLG_CTRL0_PORT_EN BIT(0) +#define MVPP22_XLG_CTRL0_MAC_RESET_DIS BIT(1) +#define MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN BIT(7) +#define MVPP22_XLG_CTRL0_MIB_CNT_DIS BIT(14) +#define MVPP22_XLG_CTRL1_REG 0x104 +#define MVPP22_XLG_CTRL1_FRAMESIZELIMIT_OFFS 0 +#define MVPP22_XLG_CTRL1_FRAMESIZELIMIT_MASK 0x1fff +#define MVPP22_XLG_STATUS 0x10c +#define MVPP22_XLG_STATUS_LINK_UP BIT(0) +#define MVPP22_XLG_INT_STAT 0x114 +#define MVPP22_XLG_INT_STAT_LINK BIT(1) +#define MVPP22_XLG_INT_MASK 0x118 +#define MVPP22_XLG_INT_MASK_LINK BIT(1) #define MVPP22_XLG_CTRL3_REG 0x11c -#define MVPP22_XLG_CTRL3_MACMODESELECT_MASK (7 << 13) -#define MVPP22_XLG_CTRL3_MACMODESELECT_GMAC (0 << 13) -#define MVPP22_XLG_CTRL3_MACMODESELECT_10G (1 << 13) +#define MVPP22_XLG_CTRL3_MACMODESELECT_MASK (7 << 13) +#define MVPP22_XLG_CTRL3_MACMODESELECT_GMAC (0 << 13) +#define MVPP22_XLG_CTRL3_MACMODESELECT_10G (1 << 13) +#define MVPP22_XLG_EXT_INT_MASK 0x15c +#define MVPP22_XLG_EXT_INT_MASK_XLG BIT(1) +#define MVPP22_XLG_EXT_INT_MASK_GIG BIT(2) +#define MVPP22_XLG_CTRL4_REG 0x184 +#define MVPP22_XLG_CTRL4_FWD_FC BIT(5) +#define MVPP22_XLG_CTRL4_FWD_PFC BIT(6) +#define MVPP22_XLG_CTRL4_MACMODSELECT_GMAC BIT(12) /* SMI registers. PPv2.2 only, relative to priv->iface_base. */ #define MVPP22_SMI_MISC_CFG_REG 0x1204 -#define MVPP22_SMI_POLLING_EN BIT(10) +#define MVPP22_SMI_POLLING_EN BIT(10) #define MVPP22_GMAC_BASE(port) (0x7000 + (port) * 0x1000 + 0xe00) @@ -367,11 +408,44 @@ #define MVPP2_QUEUE_NEXT_DESC(q, index) \ (((index) < (q)->last_desc) ? ((index) + 1) : 0) +/* XPCS registers. PPv2.2 only */ +#define MVPP22_MPCS_BASE(port) (0x7000 + (port) * 0x1000) +#define MVPP22_MPCS_CTRL 0x14 +#define MVPP22_MPCS_CTRL_FWD_ERR_CONN BIT(10) +#define MVPP22_MPCS_CLK_RESET 0x14c +#define MAC_CLK_RESET_SD_TX BIT(0) +#define MAC_CLK_RESET_SD_RX BIT(1) +#define MAC_CLK_RESET_MAC BIT(2) +#define MVPP22_MPCS_CLK_RESET_DIV_RATIO(n) ((n) << 4) +#define MVPP22_MPCS_CLK_RESET_DIV_SET BIT(11) + +/* XPCS registers. PPv2.2 only */ +#define MVPP22_XPCS_BASE(port) (0x7400 + (port) * 0x1000) +#define MVPP22_XPCS_CFG0 0x0 +#define MVPP22_XPCS_CFG0_PCS_MODE(n) ((n) << 3) +#define MVPP22_XPCS_CFG0_ACTIVE_LANE(n) ((n) << 5) + +/* System controller registers. Accessed through a regmap. */ +#define GENCONF_SOFT_RESET1 0x1108 +#define GENCONF_SOFT_RESET1_GOP BIT(6) +#define GENCONF_PORT_CTRL0 0x1110 +#define GENCONF_PORT_CTRL0_BUS_WIDTH_SELECT BIT(1) +#define GENCONF_PORT_CTRL0_RX_DATA_SAMPLE BIT(29) +#define GENCONF_PORT_CTRL0_CLK_DIV_PHASE_CLR BIT(31) +#define GENCONF_PORT_CTRL1 0x1114 +#define GENCONF_PORT_CTRL1_EN(p) BIT(p) +#define GENCONF_PORT_CTRL1_RESET(p) (BIT(p) << 28) +#define GENCONF_CTRL0 0x1120 +#define GENCONF_CTRL0_PORT0_RGMII BIT(0) +#define GENCONF_CTRL0_PORT1_RGMII_MII BIT(1) +#define GENCONF_CTRL0_PORT1_RGMII BIT(2) + /* Various constants */ /* Coalescing */ #define MVPP2_TXDONE_COAL_PKTS_THRESH 15 #define MVPP2_TXDONE_HRTIMER_PERIOD_NS 1000000UL +#define MVPP2_TXDONE_COAL_USEC 1000 #define MVPP2_RX_COAL_PKTS 32 #define MVPP2_RX_COAL_USEC 100 @@ -685,7 +759,8 @@ enum mvpp2_prs_l3_cast { #define MVPP21_ADDR_SPACE_SZ 0 #define MVPP22_ADDR_SPACE_SZ SZ_64K -#define MVPP2_MAX_CPUS 4 +#define MVPP2_MAX_THREADS 8 +#define MVPP2_MAX_QVECS MVPP2_MAX_THREADS enum mvpp2_bm_type { MVPP2_BM_FREE, @@ -701,11 +776,17 @@ struct mvpp2 { void __iomem *lms_base; void __iomem *iface_base; - /* On PPv2.2, each CPU can access the base register through a - * separate address space, each 64 KB apart from each - * other. + /* On PPv2.2, each "software thread" can access the base + * register through a separate address space, each 64 KB apart + * from each other. Typically, such address spaces will be + * used per CPU. + */ + void __iomem *swth_base[MVPP2_MAX_THREADS]; + + /* On PPv2.2, some port control registers are located into the system + * controller space. These registers are accessible through a regmap. */ - void __iomem *cpu_base[MVPP2_MAX_CPUS]; + struct regmap *sysctrl_base; /* Common clocks */ struct clk *pp_clk; @@ -752,6 +833,18 @@ struct mvpp2_port_pcpu { struct tasklet_struct tx_done_tasklet; }; +struct mvpp2_queue_vector { + int irq; + struct napi_struct napi; + enum { MVPP2_QUEUE_VECTOR_SHARED, MVPP2_QUEUE_VECTOR_PRIVATE } type; + int sw_thread_id; + u16 sw_thread_mask; + int first_rxq; + int nrxqs; + u32 pending_cause_rx; + struct mvpp2_port *port; +}; + struct mvpp2_port { u8 id; @@ -760,7 +853,7 @@ struct mvpp2_port { */ int gop_id; - int irq; + int link_irq; struct mvpp2 *priv; @@ -768,14 +861,13 @@ struct mvpp2_port { void __iomem *base; struct mvpp2_rx_queue **rxqs; + unsigned int nrxqs; struct mvpp2_tx_queue **txqs; + unsigned int ntxqs; struct net_device *dev; int pkt_size; - u32 pending_cause_rx; - struct napi_struct napi; - /* Per-CPU port control */ struct mvpp2_port_pcpu __percpu *pcpu; @@ -788,6 +880,7 @@ struct mvpp2_port { phy_interface_t phy_interface; struct device_node *phy_node; + struct phy *comphy; unsigned int link; unsigned int duplex; unsigned int speed; @@ -797,6 +890,12 @@ struct mvpp2_port { /* Index of first port's physical RXQ */ u8 first_rxq; + + struct mvpp2_queue_vector qvecs[MVPP2_MAX_QVECS]; + unsigned int nqvecs; + bool has_tx_irqs; + + u32 tx_time_coal; }; /* The mvpp2_tx_desc and mvpp2_rx_desc structures describe the @@ -932,6 +1031,10 @@ struct mvpp2_txq_pcpu { /* Index of the TX DMA descriptor to be cleaned up */ int txq_get_index; + + /* DMA buffer for TSO headers */ + char *tso_headers; + dma_addr_t tso_headers_dma; }; struct mvpp2_tx_queue { @@ -1062,12 +1165,14 @@ struct mvpp2_bm_pool { u32 port_map; }; -/* Static declaractions */ +/* Queue modes */ +#define MVPP2_QDIST_SINGLE_MODE 0 +#define MVPP2_QDIST_MULTI_MODE 1 -/* Number of RXQs used by single port */ -static int rxq_number = MVPP2_DEFAULT_RXQ; -/* Number of TXQs used by single port */ -static int txq_number = MVPP2_MAX_TXQ; +static int queue_mode = MVPP2_QDIST_SINGLE_MODE; + +module_param(queue_mode, int, 0444); +MODULE_PARM_DESC(queue_mode, "Set queue_mode (single=0, multi=1)"); #define MVPP2_DRIVER_NAME "mvpp2" #define MVPP2_DRIVER_VERSION "1.0" @@ -1076,12 +1181,12 @@ static int txq_number = MVPP2_MAX_TXQ; static void mvpp2_write(struct mvpp2 *priv, u32 offset, u32 data) { - writel(data, priv->cpu_base[0] + offset); + writel(data, priv->swth_base[0] + offset); } static u32 mvpp2_read(struct mvpp2 *priv, u32 offset) { - return readl(priv->cpu_base[0] + offset); + return readl(priv->swth_base[0] + offset); } /* These accessors should be used to access: @@ -1123,13 +1228,13 @@ static u32 mvpp2_read(struct mvpp2 *priv, u32 offset) static void mvpp2_percpu_write(struct mvpp2 *priv, int cpu, u32 offset, u32 data) { - writel(data, priv->cpu_base[cpu] + offset); + writel(data, priv->swth_base[cpu] + offset); } static u32 mvpp2_percpu_read(struct mvpp2 *priv, int cpu, u32 offset) { - return readl(priv->cpu_base[cpu] + offset); + return readl(priv->swth_base[cpu] + offset); } static dma_addr_t mvpp2_txdesc_dma_addr_get(struct mvpp2_port *port, @@ -4070,7 +4175,7 @@ static int mvpp2_swf_bm_pool_init(struct mvpp2_port *port) port->pool_long->port_map |= (1 << port->id); - for (rxq = 0; rxq < rxq_number; rxq++) + for (rxq = 0; rxq < port->nrxqs; rxq++) mvpp2_rxq_long_pool_set(port, rxq, port->pool_long->id); } @@ -4084,7 +4189,7 @@ static int mvpp2_swf_bm_pool_init(struct mvpp2_port *port) port->pool_short->port_map |= (1 << port->id); - for (rxq = 0; rxq < rxq_number; rxq++) + for (rxq = 0; rxq < port->nrxqs; rxq++) mvpp2_rxq_short_pool_set(port, rxq, port->pool_short->id); } @@ -4125,22 +4230,40 @@ static int mvpp2_bm_update_mtu(struct net_device *dev, int mtu) static inline void mvpp2_interrupts_enable(struct mvpp2_port *port) { - int cpu, cpu_mask = 0; + int i, sw_thread_mask = 0; + + for (i = 0; i < port->nqvecs; i++) + sw_thread_mask |= port->qvecs[i].sw_thread_mask; - for_each_present_cpu(cpu) - cpu_mask |= 1 << cpu; mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id), - MVPP2_ISR_ENABLE_INTERRUPT(cpu_mask)); + MVPP2_ISR_ENABLE_INTERRUPT(sw_thread_mask)); } static inline void mvpp2_interrupts_disable(struct mvpp2_port *port) { - int cpu, cpu_mask = 0; + int i, sw_thread_mask = 0; + + for (i = 0; i < port->nqvecs; i++) + sw_thread_mask |= port->qvecs[i].sw_thread_mask; - for_each_present_cpu(cpu) - cpu_mask |= 1 << cpu; mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id), - MVPP2_ISR_DISABLE_INTERRUPT(cpu_mask)); + MVPP2_ISR_DISABLE_INTERRUPT(sw_thread_mask)); +} + +static inline void mvpp2_qvec_interrupt_enable(struct mvpp2_queue_vector *qvec) +{ + struct mvpp2_port *port = qvec->port; + + mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id), + MVPP2_ISR_ENABLE_INTERRUPT(qvec->sw_thread_mask)); +} + +static inline void mvpp2_qvec_interrupt_disable(struct mvpp2_queue_vector *qvec) +{ + struct mvpp2_port *port = qvec->port; + + mvpp2_write(port->priv, MVPP2_ISR_ENABLE_REG(port->id), + MVPP2_ISR_DISABLE_INTERRUPT(qvec->sw_thread_mask)); } /* Mask the current CPU's Rx/Tx interrupts @@ -4162,15 +4285,346 @@ static void mvpp2_interrupts_mask(void *arg) static void mvpp2_interrupts_unmask(void *arg) { struct mvpp2_port *port = arg; + u32 val; + + val = MVPP2_CAUSE_MISC_SUM_MASK | + MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; + if (port->has_tx_irqs) + val |= MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; mvpp2_percpu_write(port->priv, smp_processor_id(), - MVPP2_ISR_RX_TX_MASK_REG(port->id), - (MVPP2_CAUSE_MISC_SUM_MASK | - MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK)); + MVPP2_ISR_RX_TX_MASK_REG(port->id), val); +} + +static void +mvpp2_shared_interrupt_mask_unmask(struct mvpp2_port *port, bool mask) +{ + u32 val; + int i; + + if (port->priv->hw_version != MVPP22) + return; + + if (mask) + val = 0; + else + val = MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; + + for (i = 0; i < port->nqvecs; i++) { + struct mvpp2_queue_vector *v = port->qvecs + i; + + if (v->type != MVPP2_QUEUE_VECTOR_SHARED) + continue; + + mvpp2_percpu_write(port->priv, v->sw_thread_id, + MVPP2_ISR_RX_TX_MASK_REG(port->id), val); + } } /* Port configuration routines */ +static void mvpp22_gop_init_rgmii(struct mvpp2_port *port) +{ + struct mvpp2 *priv = port->priv; + u32 val; + + regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL0, &val); + val |= GENCONF_PORT_CTRL0_BUS_WIDTH_SELECT; + regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL0, val); + + regmap_read(priv->sysctrl_base, GENCONF_CTRL0, &val); + if (port->gop_id == 2) + val |= GENCONF_CTRL0_PORT0_RGMII | GENCONF_CTRL0_PORT1_RGMII; + else if (port->gop_id == 3) + val |= GENCONF_CTRL0_PORT1_RGMII_MII; + regmap_write(priv->sysctrl_base, GENCONF_CTRL0, val); +} + +static void mvpp22_gop_init_sgmii(struct mvpp2_port *port) +{ + struct mvpp2 *priv = port->priv; + u32 val; + + regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL0, &val); + val |= GENCONF_PORT_CTRL0_BUS_WIDTH_SELECT | + GENCONF_PORT_CTRL0_RX_DATA_SAMPLE; + regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL0, val); + + if (port->gop_id > 1) { + regmap_read(priv->sysctrl_base, GENCONF_CTRL0, &val); + if (port->gop_id == 2) + val &= ~GENCONF_CTRL0_PORT0_RGMII; + else if (port->gop_id == 3) + val &= ~GENCONF_CTRL0_PORT1_RGMII_MII; + regmap_write(priv->sysctrl_base, GENCONF_CTRL0, val); + } +} + +static void mvpp22_gop_init_10gkr(struct mvpp2_port *port) +{ + struct mvpp2 *priv = port->priv; + void __iomem *mpcs = priv->iface_base + MVPP22_MPCS_BASE(port->gop_id); + void __iomem *xpcs = priv->iface_base + MVPP22_XPCS_BASE(port->gop_id); + u32 val; + + /* XPCS */ + val = readl(xpcs + MVPP22_XPCS_CFG0); + val &= ~(MVPP22_XPCS_CFG0_PCS_MODE(0x3) | + MVPP22_XPCS_CFG0_ACTIVE_LANE(0x3)); + val |= MVPP22_XPCS_CFG0_ACTIVE_LANE(2); + writel(val, xpcs + MVPP22_XPCS_CFG0); + + /* MPCS */ + val = readl(mpcs + MVPP22_MPCS_CTRL); + val &= ~MVPP22_MPCS_CTRL_FWD_ERR_CONN; + writel(val, mpcs + MVPP22_MPCS_CTRL); + + val = readl(mpcs + MVPP22_MPCS_CLK_RESET); + val &= ~(MVPP22_MPCS_CLK_RESET_DIV_RATIO(0x7) | MAC_CLK_RESET_MAC | + MAC_CLK_RESET_SD_RX | MAC_CLK_RESET_SD_TX); + val |= MVPP22_MPCS_CLK_RESET_DIV_RATIO(1); + writel(val, mpcs + MVPP22_MPCS_CLK_RESET); + + val &= ~MVPP22_MPCS_CLK_RESET_DIV_SET; + val |= MAC_CLK_RESET_MAC | MAC_CLK_RESET_SD_RX | MAC_CLK_RESET_SD_TX; + writel(val, mpcs + MVPP22_MPCS_CLK_RESET); +} + +static int mvpp22_gop_init(struct mvpp2_port *port) +{ + struct mvpp2 *priv = port->priv; + u32 val; + + if (!priv->sysctrl_base) + return 0; + + switch (port->phy_interface) { + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_RXID: + case PHY_INTERFACE_MODE_RGMII_TXID: + if (port->gop_id == 0) + goto invalid_conf; + mvpp22_gop_init_rgmii(port); + break; + case PHY_INTERFACE_MODE_SGMII: + mvpp22_gop_init_sgmii(port); + break; + case PHY_INTERFACE_MODE_10GKR: + if (port->gop_id != 0) + goto invalid_conf; + mvpp22_gop_init_10gkr(port); + break; + default: + goto unsupported_conf; + } + + regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL1, &val); + val |= GENCONF_PORT_CTRL1_RESET(port->gop_id) | + GENCONF_PORT_CTRL1_EN(port->gop_id); + regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL1, val); + + regmap_read(priv->sysctrl_base, GENCONF_PORT_CTRL0, &val); + val |= GENCONF_PORT_CTRL0_CLK_DIV_PHASE_CLR; + regmap_write(priv->sysctrl_base, GENCONF_PORT_CTRL0, val); + + regmap_read(priv->sysctrl_base, GENCONF_SOFT_RESET1, &val); + val |= GENCONF_SOFT_RESET1_GOP; + regmap_write(priv->sysctrl_base, GENCONF_SOFT_RESET1, val); + +unsupported_conf: + return 0; + +invalid_conf: + netdev_err(port->dev, "Invalid port configuration\n"); + return -EINVAL; +} + +static void mvpp22_gop_unmask_irq(struct mvpp2_port *port) +{ + u32 val; + + if (phy_interface_mode_is_rgmii(port->phy_interface) || + port->phy_interface == PHY_INTERFACE_MODE_SGMII) { + /* Enable the GMAC link status irq for this port */ + val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK); + val |= MVPP22_GMAC_INT_SUM_MASK_LINK_STAT; + writel(val, port->base + MVPP22_GMAC_INT_SUM_MASK); + } + + if (port->gop_id == 0) { + /* Enable the XLG/GIG irqs for this port */ + val = readl(port->base + MVPP22_XLG_EXT_INT_MASK); + if (port->phy_interface == PHY_INTERFACE_MODE_10GKR) + val |= MVPP22_XLG_EXT_INT_MASK_XLG; + else + val |= MVPP22_XLG_EXT_INT_MASK_GIG; + writel(val, port->base + MVPP22_XLG_EXT_INT_MASK); + } +} + +static void mvpp22_gop_mask_irq(struct mvpp2_port *port) +{ + u32 val; + + if (port->gop_id == 0) { + val = readl(port->base + MVPP22_XLG_EXT_INT_MASK); + val &= ~(MVPP22_XLG_EXT_INT_MASK_XLG | + MVPP22_XLG_EXT_INT_MASK_GIG); + writel(val, port->base + MVPP22_XLG_EXT_INT_MASK); + } + + if (phy_interface_mode_is_rgmii(port->phy_interface) || + port->phy_interface == PHY_INTERFACE_MODE_SGMII) { + val = readl(port->base + MVPP22_GMAC_INT_SUM_MASK); + val &= ~MVPP22_GMAC_INT_SUM_MASK_LINK_STAT; + writel(val, port->base + MVPP22_GMAC_INT_SUM_MASK); + } +} + +static void mvpp22_gop_setup_irq(struct mvpp2_port *port) +{ + u32 val; + + if (phy_interface_mode_is_rgmii(port->phy_interface) || + port->phy_interface == PHY_INTERFACE_MODE_SGMII) { + val = readl(port->base + MVPP22_GMAC_INT_MASK); + val |= MVPP22_GMAC_INT_MASK_LINK_STAT; + writel(val, port->base + MVPP22_GMAC_INT_MASK); + } + + if (port->gop_id == 0) { + val = readl(port->base + MVPP22_XLG_INT_MASK); + val |= MVPP22_XLG_INT_MASK_LINK; + writel(val, port->base + MVPP22_XLG_INT_MASK); + } + + mvpp22_gop_unmask_irq(port); +} + +static int mvpp22_comphy_init(struct mvpp2_port *port) +{ + enum phy_mode mode; + int ret; + + if (!port->comphy) + return 0; + + switch (port->phy_interface) { + case PHY_INTERFACE_MODE_SGMII: + mode = PHY_MODE_SGMII; + break; + case PHY_INTERFACE_MODE_10GKR: + mode = PHY_MODE_10GKR; + break; + default: + return -EINVAL; + } + + ret = phy_set_mode(port->comphy, mode); + if (ret) + return ret; + + return phy_power_on(port->comphy); +} + +static void mvpp2_port_mii_gmac_configure_mode(struct mvpp2_port *port) +{ + u32 val; + + if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) { + val = readl(port->base + MVPP22_GMAC_CTRL_4_REG); + val |= MVPP22_CTRL4_SYNC_BYPASS_DIS | MVPP22_CTRL4_DP_CLK_SEL | + MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE; + val &= ~MVPP22_CTRL4_EXT_PIN_GMII_SEL; + writel(val, port->base + MVPP22_GMAC_CTRL_4_REG); + + val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); + val |= MVPP2_GMAC_DISABLE_PADDING; + val &= ~MVPP2_GMAC_FLOW_CTRL_MASK; + writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); + } else if (phy_interface_mode_is_rgmii(port->phy_interface)) { + val = readl(port->base + MVPP22_GMAC_CTRL_4_REG); + val |= MVPP22_CTRL4_EXT_PIN_GMII_SEL | + MVPP22_CTRL4_SYNC_BYPASS_DIS | + MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE; + val &= ~MVPP22_CTRL4_DP_CLK_SEL; + writel(val, port->base + MVPP22_GMAC_CTRL_4_REG); + + val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); + val &= ~MVPP2_GMAC_DISABLE_PADDING; + writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); + } + + /* The port is connected to a copper PHY */ + val = readl(port->base + MVPP2_GMAC_CTRL_0_REG); + val &= ~MVPP2_GMAC_PORT_TYPE_MASK; + writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); + + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); + val |= MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS | + MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG | + MVPP2_GMAC_AN_DUPLEX_EN; + if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) + val |= MVPP2_GMAC_IN_BAND_AUTONEG; + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); +} + +static void mvpp2_port_mii_gmac_configure(struct mvpp2_port *port) +{ + u32 val; + + /* Force link down */ + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); + val &= ~MVPP2_GMAC_FORCE_LINK_PASS; + val |= MVPP2_GMAC_FORCE_LINK_DOWN; + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); + + /* Set the GMAC in a reset state */ + val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); + val |= MVPP2_GMAC_PORT_RESET_MASK; + writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); + + /* Configure the PCS and in-band AN */ + val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); + if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) { + val |= MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PCS_ENABLE_MASK; + } else if (phy_interface_mode_is_rgmii(port->phy_interface)) { + val &= ~MVPP2_GMAC_PCS_ENABLE_MASK; + val |= MVPP2_GMAC_PORT_RGMII_MASK; + } + writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); + + mvpp2_port_mii_gmac_configure_mode(port); + + /* Unset the GMAC reset state */ + val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); + val &= ~MVPP2_GMAC_PORT_RESET_MASK; + writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); + + /* Stop forcing link down */ + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); + val &= ~MVPP2_GMAC_FORCE_LINK_DOWN; + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); +} + +static void mvpp2_port_mii_xlg_configure(struct mvpp2_port *port) +{ + u32 val; + + if (port->gop_id != 0) + return; + + val = readl(port->base + MVPP22_XLG_CTRL0_REG); + val |= MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN; + writel(val, port->base + MVPP22_XLG_CTRL0_REG); + + val = readl(port->base + MVPP22_XLG_CTRL4_REG); + val &= ~MVPP22_XLG_CTRL4_MACMODSELECT_GMAC; + val |= MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC; + writel(val, port->base + MVPP22_XLG_CTRL4_REG); +} + static void mvpp22_port_mii_set(struct mvpp2_port *port) { u32 val; @@ -4188,38 +4642,18 @@ static void mvpp22_port_mii_set(struct mvpp2_port *port) writel(val, port->base + MVPP22_XLG_CTRL3_REG); } - - val = readl(port->base + MVPP22_GMAC_CTRL_4_REG); - if (port->phy_interface == PHY_INTERFACE_MODE_RGMII) - val |= MVPP22_CTRL4_EXT_PIN_GMII_SEL; - else - val &= ~MVPP22_CTRL4_EXT_PIN_GMII_SEL; - val &= ~MVPP22_CTRL4_DP_CLK_SEL; - val |= MVPP22_CTRL4_SYNC_BYPASS; - val |= MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE; - writel(val, port->base + MVPP22_GMAC_CTRL_4_REG); } static void mvpp2_port_mii_set(struct mvpp2_port *port) { - u32 val; - if (port->priv->hw_version == MVPP22) mvpp22_port_mii_set(port); - val = readl(port->base + MVPP2_GMAC_CTRL_2_REG); - - switch (port->phy_interface) { - case PHY_INTERFACE_MODE_SGMII: - val |= MVPP2_GMAC_INBAND_AN_MASK; - break; - case PHY_INTERFACE_MODE_RGMII: - val |= MVPP2_GMAC_PORT_RGMII_MASK; - default: - val &= ~MVPP2_GMAC_PCS_ENABLE_MASK; - } - - writel(val, port->base + MVPP2_GMAC_CTRL_2_REG); + if (phy_interface_mode_is_rgmii(port->phy_interface) || + port->phy_interface == PHY_INTERFACE_MODE_SGMII) + mvpp2_port_mii_gmac_configure(port); + else if (port->phy_interface == PHY_INTERFACE_MODE_10GKR) + mvpp2_port_mii_xlg_configure(port); } static void mvpp2_port_fc_adv_enable(struct mvpp2_port *port) @@ -4326,6 +4760,18 @@ static inline void mvpp2_gmac_max_rx_size_set(struct mvpp2_port *port) writel(val, port->base + MVPP2_GMAC_CTRL_0_REG); } +/* Change maximum receive size of the port */ +static inline void mvpp2_xlg_max_rx_size_set(struct mvpp2_port *port) +{ + u32 val; + + val = readl(port->base + MVPP22_XLG_CTRL1_REG); + val &= ~MVPP22_XLG_CTRL1_FRAMESIZELIMIT_MASK; + val |= ((port->pkt_size - MVPP2_MH_SIZE) / 2) << + MVPP22_XLG_CTRL1_FRAMESIZELIMIT_OFFS; + writel(val, port->base + MVPP22_XLG_CTRL1_REG); +} + /* Set defaults to the MVPP2 port */ static void mvpp2_defaults_set(struct mvpp2_port *port) { @@ -4376,7 +4822,7 @@ static void mvpp2_defaults_set(struct mvpp2_port *port) MVPP2_RX_LOW_LATENCY_PKT_SIZE(256)); /* Enable Rx cache snoop */ - for (lrxq = 0; lrxq < rxq_number; lrxq++) { + for (lrxq = 0; lrxq < port->nrxqs; lrxq++) { queue = port->rxqs[lrxq]->id; val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue)); val |= MVPP2_SNOOP_PKT_SIZE_MASK | @@ -4394,7 +4840,7 @@ static void mvpp2_ingress_enable(struct mvpp2_port *port) u32 val; int lrxq, queue; - for (lrxq = 0; lrxq < rxq_number; lrxq++) { + for (lrxq = 0; lrxq < port->nrxqs; lrxq++) { queue = port->rxqs[lrxq]->id; val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue)); val &= ~MVPP2_RXQ_DISABLE_MASK; @@ -4407,7 +4853,7 @@ static void mvpp2_ingress_disable(struct mvpp2_port *port) u32 val; int lrxq, queue; - for (lrxq = 0; lrxq < rxq_number; lrxq++) { + for (lrxq = 0; lrxq < port->nrxqs; lrxq++) { queue = port->rxqs[lrxq]->id; val = mvpp2_read(port->priv, MVPP2_RXQ_CONFIG_REG(queue)); val |= MVPP2_RXQ_DISABLE_MASK; @@ -4426,7 +4872,7 @@ static void mvpp2_egress_enable(struct mvpp2_port *port) /* Enable all initialized TXs. */ qmap = 0; - for (queue = 0; queue < txq_number; queue++) { + for (queue = 0; queue < port->ntxqs; queue++) { struct mvpp2_tx_queue *txq = port->txqs[queue]; if (txq->descs) @@ -4712,7 +5158,7 @@ static void mvpp2_txq_sent_counter_clear(void *arg) struct mvpp2_port *port = arg; int queue; - for (queue = 0; queue < txq_number; queue++) { + for (queue = 0; queue < port->ntxqs; queue++) { int id = port->txqs[queue]->id; mvpp2_percpu_read(port->priv, smp_processor_id(), @@ -4753,7 +5199,7 @@ static void mvpp2_txp_max_tx_size_set(struct mvpp2_port *port) mvpp2_write(port->priv, MVPP2_TXP_SCHED_TOKEN_SIZE_REG, val); } - for (txq = 0; txq < txq_number; txq++) { + for (txq = 0; txq < port->ntxqs; txq++) { val = mvpp2_read(port->priv, MVPP2_TXQ_SCHED_TOKEN_SIZE_REG(txq)); size = val & MVPP2_TXQ_TOKEN_SIZE_MAX; @@ -4787,6 +5233,23 @@ static void mvpp2_rx_pkts_coal_set(struct mvpp2_port *port, put_cpu(); } +/* For some reason in the LSP this is done on each CPU. Why ? */ +static void mvpp2_tx_pkts_coal_set(struct mvpp2_port *port, + struct mvpp2_tx_queue *txq) +{ + int cpu = get_cpu(); + u32 val; + + if (txq->done_pkts_coal > MVPP2_TXQ_THRESH_MASK) + txq->done_pkts_coal = MVPP2_TXQ_THRESH_MASK; + + val = (txq->done_pkts_coal << MVPP2_TXQ_THRESH_OFFSET); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_NUM_REG, txq->id); + mvpp2_percpu_write(port->priv, cpu, MVPP2_TXQ_THRESH_REG, val); + + put_cpu(); +} + static u32 mvpp2_usec_to_cycles(u32 usec, unsigned long clk_hz) { u64 tmp = (u64)clk_hz * usec; @@ -4823,6 +5286,22 @@ static void mvpp2_rx_time_coal_set(struct mvpp2_port *port, mvpp2_write(port->priv, MVPP2_ISR_RX_THRESHOLD_REG(rxq->id), val); } +static void mvpp2_tx_time_coal_set(struct mvpp2_port *port) +{ + unsigned long freq = port->priv->tclk; + u32 val = mvpp2_usec_to_cycles(port->tx_time_coal, freq); + + if (val > MVPP2_MAX_ISR_TX_THRESHOLD) { + port->tx_time_coal = + mvpp2_cycles_to_usec(MVPP2_MAX_ISR_TX_THRESHOLD, freq); + + /* re-evaluate to get actual register value */ + val = mvpp2_usec_to_cycles(port->tx_time_coal, freq); + } + + mvpp2_write(port->priv, MVPP2_ISR_TX_THRESHOLD_REG(port->id), val); +} + /* Free Tx queue skbuffs */ static void mvpp2_txq_bufs_free(struct mvpp2_port *port, struct mvpp2_tx_queue *txq, @@ -4881,7 +5360,8 @@ static void mvpp2_txq_done(struct mvpp2_port *port, struct mvpp2_tx_queue *txq, netif_tx_wake_queue(nq); } -static unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause) +static unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause, + int cpu) { struct mvpp2_tx_queue *txq; struct mvpp2_txq_pcpu *txq_pcpu; @@ -4892,7 +5372,7 @@ static unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause) if (!txq) break; - txq_pcpu = this_cpu_ptr(txq->pcpu); + txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); if (txq_pcpu->count) { mvpp2_txq_done(port, txq, txq_pcpu); @@ -4908,15 +5388,14 @@ static unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause) /* Allocate and initialize descriptors for aggr TXQ */ static int mvpp2_aggr_txq_init(struct platform_device *pdev, - struct mvpp2_tx_queue *aggr_txq, - int desc_num, int cpu, + struct mvpp2_tx_queue *aggr_txq, int cpu, struct mvpp2 *priv) { u32 txq_dma; /* Allocate memory for TX descriptors */ aggr_txq->descs = dma_alloc_coherent(&pdev->dev, - desc_num * MVPP2_DESC_ALIGNED_SIZE, + MVPP2_AGGR_TXQ_SIZE * MVPP2_DESC_ALIGNED_SIZE, &aggr_txq->descs_dma, GFP_KERNEL); if (!aggr_txq->descs) return -ENOMEM; @@ -4937,7 +5416,8 @@ static int mvpp2_aggr_txq_init(struct platform_device *pdev, MVPP22_AGGR_TXQ_DESC_ADDR_OFFS; mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_ADDR_REG(cpu), txq_dma); - mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_SIZE_REG(cpu), desc_num); + mvpp2_write(priv, MVPP2_AGGR_TXQ_DESC_SIZE_REG(cpu), + MVPP2_AGGR_TXQ_SIZE); return 0; } @@ -5118,6 +5598,14 @@ static int mvpp2_txq_init(struct mvpp2_port *port, txq_pcpu->reserved_num = 0; txq_pcpu->txq_put_index = 0; txq_pcpu->txq_get_index = 0; + + txq_pcpu->tso_headers = + dma_alloc_coherent(port->dev->dev.parent, + MVPP2_AGGR_TXQ_SIZE * TSO_HEADER_SIZE, + &txq_pcpu->tso_headers_dma, + GFP_KERNEL); + if (!txq_pcpu->tso_headers) + goto cleanup; } return 0; @@ -5125,6 +5613,11 @@ cleanup: for_each_present_cpu(cpu) { txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); kfree(txq_pcpu->buffs); + + dma_free_coherent(port->dev->dev.parent, + MVPP2_AGGR_TXQ_SIZE * MVPP2_DESC_ALIGNED_SIZE, + txq_pcpu->tso_headers, + txq_pcpu->tso_headers_dma); } dma_free_coherent(port->dev->dev.parent, @@ -5144,6 +5637,11 @@ static void mvpp2_txq_deinit(struct mvpp2_port *port, for_each_present_cpu(cpu) { txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); kfree(txq_pcpu->buffs); + + dma_free_coherent(port->dev->dev.parent, + MVPP2_AGGR_TXQ_SIZE * MVPP2_DESC_ALIGNED_SIZE, + txq_pcpu->tso_headers, + txq_pcpu->tso_headers_dma); } if (txq->descs) @@ -5229,7 +5727,7 @@ static void mvpp2_cleanup_txqs(struct mvpp2_port *port) val |= MVPP2_TX_PORT_FLUSH_MASK(port->id); mvpp2_write(port->priv, MVPP2_TX_PORT_FLUSH_REG, val); - for (queue = 0; queue < txq_number; queue++) { + for (queue = 0; queue < port->ntxqs; queue++) { txq = port->txqs[queue]; mvpp2_txq_clean(port, txq); mvpp2_txq_deinit(port, txq); @@ -5246,7 +5744,7 @@ static void mvpp2_cleanup_rxqs(struct mvpp2_port *port) { int queue; - for (queue = 0; queue < rxq_number; queue++) + for (queue = 0; queue < port->nrxqs; queue++) mvpp2_rxq_deinit(port, port->rxqs[queue]); } @@ -5255,7 +5753,7 @@ static int mvpp2_setup_rxqs(struct mvpp2_port *port) { int queue, err; - for (queue = 0; queue < rxq_number; queue++) { + for (queue = 0; queue < port->nrxqs; queue++) { err = mvpp2_rxq_init(port, port->rxqs[queue]); if (err) goto err_cleanup; @@ -5273,13 +5771,21 @@ static int mvpp2_setup_txqs(struct mvpp2_port *port) struct mvpp2_tx_queue *txq; int queue, err; - for (queue = 0; queue < txq_number; queue++) { + for (queue = 0; queue < port->ntxqs; queue++) { txq = port->txqs[queue]; err = mvpp2_txq_init(port, txq); if (err) goto err_cleanup; } + if (port->has_tx_irqs) { + mvpp2_tx_time_coal_set(port); + for (queue = 0; queue < port->ntxqs; queue++) { + txq = port->txqs[queue]; + mvpp2_tx_pkts_coal_set(port, txq); + } + } + on_each_cpu(mvpp2_txq_sent_counter_clear, port, 1); return 0; @@ -5291,72 +5797,170 @@ err_cleanup: /* The callback for per-port interrupt */ static irqreturn_t mvpp2_isr(int irq, void *dev_id) { + struct mvpp2_queue_vector *qv = dev_id; + + mvpp2_qvec_interrupt_disable(qv); + + napi_schedule(&qv->napi); + + return IRQ_HANDLED; +} + +/* Per-port interrupt for link status changes */ +static irqreturn_t mvpp2_link_status_isr(int irq, void *dev_id) +{ struct mvpp2_port *port = (struct mvpp2_port *)dev_id; + struct net_device *dev = port->dev; + bool event = false, link = false; + u32 val; - mvpp2_interrupts_disable(port); + mvpp22_gop_mask_irq(port); - napi_schedule(&port->napi); + if (port->gop_id == 0 && + port->phy_interface == PHY_INTERFACE_MODE_10GKR) { + val = readl(port->base + MVPP22_XLG_INT_STAT); + if (val & MVPP22_XLG_INT_STAT_LINK) { + event = true; + val = readl(port->base + MVPP22_XLG_STATUS); + if (val & MVPP22_XLG_STATUS_LINK_UP) + link = true; + } + } else if (phy_interface_mode_is_rgmii(port->phy_interface) || + port->phy_interface == PHY_INTERFACE_MODE_SGMII) { + val = readl(port->base + MVPP22_GMAC_INT_STAT); + if (val & MVPP22_GMAC_INT_STAT_LINK) { + event = true; + val = readl(port->base + MVPP2_GMAC_STATUS0); + if (val & MVPP2_GMAC_STATUS0_LINK_UP) + link = true; + } + } + + if (!netif_running(dev) || !event) + goto handled; + + if (link) { + mvpp2_interrupts_enable(port); + mvpp2_egress_enable(port); + mvpp2_ingress_enable(port); + netif_carrier_on(dev); + netif_tx_wake_all_queues(dev); + } else { + netif_tx_stop_all_queues(dev); + netif_carrier_off(dev); + mvpp2_ingress_disable(port); + mvpp2_egress_disable(port); + + mvpp2_interrupts_disable(port); + } + +handled: + mvpp22_gop_unmask_irq(port); return IRQ_HANDLED; } +static void mvpp2_gmac_set_autoneg(struct mvpp2_port *port, + struct phy_device *phydev) +{ + u32 val; + + if (port->phy_interface != PHY_INTERFACE_MODE_RGMII && + port->phy_interface != PHY_INTERFACE_MODE_RGMII_ID && + port->phy_interface != PHY_INTERFACE_MODE_RGMII_RXID && + port->phy_interface != PHY_INTERFACE_MODE_RGMII_TXID && + port->phy_interface != PHY_INTERFACE_MODE_SGMII) + return; + + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); + val &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | + MVPP2_GMAC_CONFIG_GMII_SPEED | + MVPP2_GMAC_CONFIG_FULL_DUPLEX | + MVPP2_GMAC_AN_SPEED_EN | + MVPP2_GMAC_AN_DUPLEX_EN); + + if (phydev->duplex) + val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; + + if (phydev->speed == SPEED_1000) + val |= MVPP2_GMAC_CONFIG_GMII_SPEED; + else if (phydev->speed == SPEED_100) + val |= MVPP2_GMAC_CONFIG_MII_SPEED; + + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); +} + /* Adjust link */ static void mvpp2_link_event(struct net_device *dev) { struct mvpp2_port *port = netdev_priv(dev); struct phy_device *phydev = dev->phydev; - int status_change = 0; + bool link_reconfigured = false; u32 val; if (phydev->link) { + if (port->phy_interface != phydev->interface && port->comphy) { + /* disable current port for reconfiguration */ + mvpp2_interrupts_disable(port); + netif_carrier_off(port->dev); + mvpp2_port_disable(port); + phy_power_off(port->comphy); + + /* comphy reconfiguration */ + port->phy_interface = phydev->interface; + mvpp22_comphy_init(port); + + /* gop/mac reconfiguration */ + mvpp22_gop_init(port); + mvpp2_port_mii_set(port); + + link_reconfigured = true; + } + if ((port->speed != phydev->speed) || (port->duplex != phydev->duplex)) { - u32 val; - - val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); - val &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | - MVPP2_GMAC_CONFIG_GMII_SPEED | - MVPP2_GMAC_CONFIG_FULL_DUPLEX | - MVPP2_GMAC_AN_SPEED_EN | - MVPP2_GMAC_AN_DUPLEX_EN); - - if (phydev->duplex) - val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX; - - if (phydev->speed == SPEED_1000) - val |= MVPP2_GMAC_CONFIG_GMII_SPEED; - else if (phydev->speed == SPEED_100) - val |= MVPP2_GMAC_CONFIG_MII_SPEED; - - writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); + mvpp2_gmac_set_autoneg(port, phydev); port->duplex = phydev->duplex; port->speed = phydev->speed; } } - if (phydev->link != port->link) { - if (!phydev->link) { - port->duplex = -1; - port->speed = 0; - } - + if (phydev->link != port->link || link_reconfigured) { port->link = phydev->link; - status_change = 1; - } - if (status_change) { if (phydev->link) { - val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); - val |= (MVPP2_GMAC_FORCE_LINK_PASS | - MVPP2_GMAC_FORCE_LINK_DOWN); - writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); + if (port->phy_interface == PHY_INTERFACE_MODE_RGMII || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_ID || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID || + port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID || + port->phy_interface == PHY_INTERFACE_MODE_SGMII) { + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG); + val |= (MVPP2_GMAC_FORCE_LINK_PASS | + MVPP2_GMAC_FORCE_LINK_DOWN); + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG); + } + + mvpp2_interrupts_enable(port); + mvpp2_port_enable(port); + mvpp2_egress_enable(port); mvpp2_ingress_enable(port); + netif_carrier_on(dev); + netif_tx_wake_all_queues(dev); } else { + port->duplex = -1; + port->speed = 0; + + netif_tx_stop_all_queues(dev); + netif_carrier_off(dev); mvpp2_ingress_disable(port); mvpp2_egress_disable(port); + + mvpp2_port_disable(port); + mvpp2_interrupts_disable(port); } + phy_print_status(phydev); } } @@ -5385,8 +5989,8 @@ static void mvpp2_tx_proc_cb(unsigned long data) port_pcpu->timer_scheduled = false; /* Process all the Tx queues */ - cause = (1 << txq_number) - 1; - tx_todo = mvpp2_tx_done(port, cause); + cause = (1 << port->ntxqs) - 1; + tx_todo = mvpp2_tx_done(port, cause, smp_processor_id()); /* Set the timer in case not all the packets were processed */ if (tx_todo) @@ -5498,8 +6102,8 @@ static u32 mvpp2_skb_tx_csum(struct mvpp2_port *port, struct sk_buff *skb) } /* Main rx processing */ -static int mvpp2_rx(struct mvpp2_port *port, int rx_todo, - struct mvpp2_rx_queue *rxq) +static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi, + int rx_todo, struct mvpp2_rx_queue *rxq) { struct net_device *dev = port->dev; int rx_received; @@ -5577,7 +6181,7 @@ err_drop_frame: skb->protocol = eth_type_trans(skb, dev); mvpp2_rx_csum(port, rx_status, skb); - napi_gro_receive(&port->napi, skb); + napi_gro_receive(napi, skb); } if (rcvd_pkts) { @@ -5665,6 +6269,123 @@ cleanup: return -ENOMEM; } +static inline void mvpp2_tso_put_hdr(struct sk_buff *skb, + struct net_device *dev, + struct mvpp2_tx_queue *txq, + struct mvpp2_tx_queue *aggr_txq, + struct mvpp2_txq_pcpu *txq_pcpu, + int hdr_sz) +{ + struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2_tx_desc *tx_desc = mvpp2_txq_next_desc_get(aggr_txq); + dma_addr_t addr; + + mvpp2_txdesc_txq_set(port, tx_desc, txq->id); + mvpp2_txdesc_size_set(port, tx_desc, hdr_sz); + + addr = txq_pcpu->tso_headers_dma + + txq_pcpu->txq_put_index * TSO_HEADER_SIZE; + mvpp2_txdesc_offset_set(port, tx_desc, addr & MVPP2_TX_DESC_ALIGN); + mvpp2_txdesc_dma_addr_set(port, tx_desc, addr & ~MVPP2_TX_DESC_ALIGN); + + mvpp2_txdesc_cmd_set(port, tx_desc, mvpp2_skb_tx_csum(port, skb) | + MVPP2_TXD_F_DESC | + MVPP2_TXD_PADDING_DISABLE); + mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc); +} + +static inline int mvpp2_tso_put_data(struct sk_buff *skb, + struct net_device *dev, struct tso_t *tso, + struct mvpp2_tx_queue *txq, + struct mvpp2_tx_queue *aggr_txq, + struct mvpp2_txq_pcpu *txq_pcpu, + int sz, bool left, bool last) +{ + struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2_tx_desc *tx_desc = mvpp2_txq_next_desc_get(aggr_txq); + dma_addr_t buf_dma_addr; + + mvpp2_txdesc_txq_set(port, tx_desc, txq->id); + mvpp2_txdesc_size_set(port, tx_desc, sz); + + buf_dma_addr = dma_map_single(dev->dev.parent, tso->data, sz, + DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(dev->dev.parent, buf_dma_addr))) { + mvpp2_txq_desc_put(txq); + return -ENOMEM; + } + + mvpp2_txdesc_offset_set(port, tx_desc, + buf_dma_addr & MVPP2_TX_DESC_ALIGN); + mvpp2_txdesc_dma_addr_set(port, tx_desc, + buf_dma_addr & ~MVPP2_TX_DESC_ALIGN); + + if (!left) { + mvpp2_txdesc_cmd_set(port, tx_desc, MVPP2_TXD_L_DESC); + if (last) { + mvpp2_txq_inc_put(port, txq_pcpu, skb, tx_desc); + return 0; + } + } else { + mvpp2_txdesc_cmd_set(port, tx_desc, 0); + } + + mvpp2_txq_inc_put(port, txq_pcpu, NULL, tx_desc); + return 0; +} + +static int mvpp2_tx_tso(struct sk_buff *skb, struct net_device *dev, + struct mvpp2_tx_queue *txq, + struct mvpp2_tx_queue *aggr_txq, + struct mvpp2_txq_pcpu *txq_pcpu) +{ + struct mvpp2_port *port = netdev_priv(dev); + struct tso_t tso; + int hdr_sz = skb_transport_offset(skb) + tcp_hdrlen(skb); + int i, len, descs = 0; + + /* Check number of available descriptors */ + if (mvpp2_aggr_desc_num_check(port->priv, aggr_txq, + tso_count_descs(skb)) || + mvpp2_txq_reserved_desc_num_proc(port->priv, txq, txq_pcpu, + tso_count_descs(skb))) + return 0; + + tso_start(skb, &tso); + len = skb->len - hdr_sz; + while (len > 0) { + int left = min_t(int, skb_shinfo(skb)->gso_size, len); + char *hdr = txq_pcpu->tso_headers + + txq_pcpu->txq_put_index * TSO_HEADER_SIZE; + + len -= left; + descs++; + + tso_build_hdr(skb, hdr, &tso, left, len == 0); + mvpp2_tso_put_hdr(skb, dev, txq, aggr_txq, txq_pcpu, hdr_sz); + + while (left > 0) { + int sz = min_t(int, tso.size, left); + left -= sz; + descs++; + + if (mvpp2_tso_put_data(skb, dev, &tso, txq, aggr_txq, + txq_pcpu, sz, left, len == 0)) + goto release; + tso_build_data(skb, &tso, sz); + } + } + + return descs; + +release: + for (i = descs - 1; i >= 0; i--) { + struct mvpp2_tx_desc *tx_desc = txq->descs + i; + tx_desc_unmap_put(port, txq, tx_desc); + } + return 0; +} + /* Main tx processing */ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) { @@ -5682,6 +6403,10 @@ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) txq_pcpu = this_cpu_ptr(txq->pcpu); aggr_txq = &port->priv->aggr_txqs[smp_processor_id()]; + if (skb_is_gso(skb)) { + frags = mvpp2_tx_tso(skb, dev, txq, aggr_txq, txq_pcpu); + goto out; + } frags = skb_shinfo(skb)->nr_frags + 1; /* Check number of available descriptors */ @@ -5731,22 +6456,21 @@ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) } } - txq_pcpu->reserved_num -= frags; - txq_pcpu->count += frags; - aggr_txq->count += frags; - - /* Enable transmit */ - wmb(); - mvpp2_aggr_txq_pend_desc_add(port, frags); - - if (txq_pcpu->size - txq_pcpu->count < MAX_SKB_FRAGS + 1) { - struct netdev_queue *nq = netdev_get_tx_queue(dev, txq_id); - - netif_tx_stop_queue(nq); - } out: if (frags > 0) { struct mvpp2_pcpu_stats *stats = this_cpu_ptr(port->stats); + struct netdev_queue *nq = netdev_get_tx_queue(dev, txq_id); + + txq_pcpu->reserved_num -= frags; + txq_pcpu->count += frags; + aggr_txq->count += frags; + + /* Enable transmit */ + wmb(); + mvpp2_aggr_txq_pend_desc_add(port, frags); + + if (txq_pcpu->size - txq_pcpu->count < MAX_SKB_FRAGS + 1) + netif_tx_stop_queue(nq); u64_stats_update_begin(&stats->syncp); stats->tx_packets++; @@ -5762,7 +6486,8 @@ out: mvpp2_txq_done(port, txq, txq_pcpu); /* Set the timer in case not all frags were processed */ - if (txq_pcpu->count <= frags && txq_pcpu->count > 0) { + if (!port->has_tx_irqs && txq_pcpu->count <= frags && + txq_pcpu->count > 0) { struct mvpp2_port_pcpu *port_pcpu = this_cpu_ptr(port->pcpu); mvpp2_timer_set(port_pcpu); @@ -5783,11 +6508,14 @@ static inline void mvpp2_cause_error(struct net_device *dev, int cause) static int mvpp2_poll(struct napi_struct *napi, int budget) { - u32 cause_rx_tx, cause_rx, cause_misc; + u32 cause_rx_tx, cause_rx, cause_tx, cause_misc; int rx_done = 0; struct mvpp2_port *port = netdev_priv(napi->dev); + struct mvpp2_queue_vector *qv; int cpu = smp_processor_id(); + qv = container_of(napi, struct mvpp2_queue_vector, napi); + /* Rx/Tx cause register * * Bits 0-15: each bit indicates received packets on the Rx queue @@ -5798,11 +6526,10 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) * * Each CPU has its own Rx/Tx cause register */ - cause_rx_tx = mvpp2_percpu_read(port->priv, cpu, + cause_rx_tx = mvpp2_percpu_read(port->priv, qv->sw_thread_id, MVPP2_ISR_RX_TX_CAUSE_REG(port->id)); - cause_rx_tx &= ~MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; - cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK; + cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK; if (cause_misc) { mvpp2_cause_error(port->dev, cause_misc); @@ -5813,10 +6540,16 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) cause_rx_tx & ~MVPP2_CAUSE_MISC_SUM_MASK); } - cause_rx = cause_rx_tx & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; + cause_tx = cause_rx_tx & MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; + if (cause_tx) { + cause_tx >>= MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_OFFSET; + mvpp2_tx_done(port, cause_tx, qv->sw_thread_id); + } /* Process RX packets */ - cause_rx |= port->pending_cause_rx; + cause_rx = cause_rx_tx & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; + cause_rx <<= qv->first_rxq; + cause_rx |= qv->pending_cause_rx; while (cause_rx && budget > 0) { int count; struct mvpp2_rx_queue *rxq; @@ -5825,7 +6558,7 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) if (!rxq) break; - count = mvpp2_rx(port, budget, rxq); + count = mvpp2_rx(port, napi, budget, rxq); rx_done += count; budget -= count; if (budget > 0) { @@ -5841,9 +6574,9 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) cause_rx = 0; napi_complete_done(napi, rx_done); - mvpp2_interrupts_enable(port); + mvpp2_qvec_interrupt_enable(qv); } - port->pending_cause_rx = cause_rx; + qv->pending_cause_rx = cause_rx; return rx_done; } @@ -5851,17 +6584,32 @@ static int mvpp2_poll(struct napi_struct *napi, int budget) static void mvpp2_start_dev(struct mvpp2_port *port) { struct net_device *ndev = port->dev; + int i; + + if (port->gop_id == 0 && + (port->phy_interface == PHY_INTERFACE_MODE_XAUI || + port->phy_interface == PHY_INTERFACE_MODE_10GKR)) + mvpp2_xlg_max_rx_size_set(port); + else + mvpp2_gmac_max_rx_size_set(port); - mvpp2_gmac_max_rx_size_set(port); mvpp2_txp_max_tx_size_set(port); - napi_enable(&port->napi); + for (i = 0; i < port->nqvecs; i++) + napi_enable(&port->qvecs[i].napi); /* Enable interrupts on all CPUs */ mvpp2_interrupts_enable(port); + if (port->priv->hw_version == MVPP22) { + mvpp22_comphy_init(port); + mvpp22_gop_init(port); + } + + mvpp2_port_mii_set(port); mvpp2_port_enable(port); - phy_start(ndev->phydev); + if (ndev->phydev) + phy_start(ndev->phydev); netif_tx_start_all_queues(port->dev); } @@ -5869,6 +6617,7 @@ static void mvpp2_start_dev(struct mvpp2_port *port) static void mvpp2_stop_dev(struct mvpp2_port *port) { struct net_device *ndev = port->dev; + int i; /* Stop new packets from arriving to RXQs */ mvpp2_ingress_disable(port); @@ -5878,14 +6627,17 @@ static void mvpp2_stop_dev(struct mvpp2_port *port) /* Disable interrupts on all CPUs */ mvpp2_interrupts_disable(port); - napi_disable(&port->napi); + for (i = 0; i < port->nqvecs; i++) + napi_disable(&port->qvecs[i].napi); netif_carrier_off(port->dev); netif_tx_stop_all_queues(port->dev); mvpp2_egress_disable(port); mvpp2_port_disable(port); - phy_stop(ndev->phydev); + if (ndev->phydev) + phy_stop(ndev->phydev); + phy_power_off(port->comphy); } static int mvpp2_check_ringparam_valid(struct net_device *dev, @@ -5941,6 +6693,10 @@ static int mvpp2_phy_connect(struct mvpp2_port *port) { struct phy_device *phy_dev; + /* No PHY is attached */ + if (!port->phy_node) + return 0; + phy_dev = of_phy_connect(port->dev, port->phy_node, mvpp2_link_event, 0, port->phy_interface); if (!phy_dev) { @@ -5961,12 +6717,56 @@ static void mvpp2_phy_disconnect(struct mvpp2_port *port) { struct net_device *ndev = port->dev; + if (!ndev->phydev) + return; + phy_disconnect(ndev->phydev); } +static int mvpp2_irqs_init(struct mvpp2_port *port) +{ + int err, i; + + for (i = 0; i < port->nqvecs; i++) { + struct mvpp2_queue_vector *qv = port->qvecs + i; + + err = request_irq(qv->irq, mvpp2_isr, 0, port->dev->name, qv); + if (err) + goto err; + + if (qv->type == MVPP2_QUEUE_VECTOR_PRIVATE) + irq_set_affinity_hint(qv->irq, + cpumask_of(qv->sw_thread_id)); + } + + return 0; +err: + for (i = 0; i < port->nqvecs; i++) { + struct mvpp2_queue_vector *qv = port->qvecs + i; + + irq_set_affinity_hint(qv->irq, NULL); + free_irq(qv->irq, qv); + } + + return err; +} + +static void mvpp2_irqs_deinit(struct mvpp2_port *port) +{ + int i; + + for (i = 0; i < port->nqvecs; i++) { + struct mvpp2_queue_vector *qv = port->qvecs + i; + + irq_set_affinity_hint(qv->irq, NULL); + free_irq(qv->irq, qv); + } +} + static int mvpp2_open(struct net_device *dev) { struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2 *priv = port->priv; unsigned char mac_bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; int err; @@ -6006,28 +6806,44 @@ static int mvpp2_open(struct net_device *dev) goto err_cleanup_rxqs; } - err = request_irq(port->irq, mvpp2_isr, 0, dev->name, port); + err = mvpp2_irqs_init(port); if (err) { - netdev_err(port->dev, "cannot request IRQ %d\n", port->irq); + netdev_err(port->dev, "cannot init IRQs\n"); goto err_cleanup_txqs; } + if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq) { + err = request_irq(port->link_irq, mvpp2_link_status_isr, 0, + dev->name, port); + if (err) { + netdev_err(port->dev, "cannot request link IRQ %d\n", + port->link_irq); + goto err_free_irq; + } + + mvpp22_gop_setup_irq(port); + } + /* In default link is down */ netif_carrier_off(port->dev); err = mvpp2_phy_connect(port); if (err < 0) - goto err_free_irq; + goto err_free_link_irq; /* Unmask interrupts on all CPUs */ on_each_cpu(mvpp2_interrupts_unmask, port, 1); + mvpp2_shared_interrupt_mask_unmask(port, false); mvpp2_start_dev(port); return 0; +err_free_link_irq: + if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq) + free_irq(port->link_irq, port); err_free_irq: - free_irq(port->irq, port); + mvpp2_irqs_deinit(port); err_cleanup_txqs: mvpp2_cleanup_txqs(port); err_cleanup_rxqs: @@ -6039,6 +6855,7 @@ static int mvpp2_stop(struct net_device *dev) { struct mvpp2_port *port = netdev_priv(dev); struct mvpp2_port_pcpu *port_pcpu; + struct mvpp2 *priv = port->priv; int cpu; mvpp2_stop_dev(port); @@ -6046,14 +6863,20 @@ static int mvpp2_stop(struct net_device *dev) /* Mask interrupts on all CPUs */ on_each_cpu(mvpp2_interrupts_mask, port, 1); + mvpp2_shared_interrupt_mask_unmask(port, true); - free_irq(port->irq, port); - for_each_present_cpu(cpu) { - port_pcpu = per_cpu_ptr(port->pcpu, cpu); + if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq) + free_irq(port->link_irq, port); + + mvpp2_irqs_deinit(port); + if (!port->has_tx_irqs) { + for_each_present_cpu(cpu) { + port_pcpu = per_cpu_ptr(port->pcpu, cpu); - hrtimer_cancel(&port_pcpu->tx_done_timer); - port_pcpu->timer_scheduled = false; - tasklet_kill(&port_pcpu->tx_done_tasklet); + hrtimer_cancel(&port_pcpu->tx_done_timer); + port_pcpu->timer_scheduled = false; + tasklet_kill(&port_pcpu->tx_done_tasklet); + } } mvpp2_cleanup_rxqs(port); mvpp2_cleanup_txqs(port); @@ -6228,7 +7051,7 @@ static int mvpp2_ethtool_set_coalesce(struct net_device *dev, struct mvpp2_port *port = netdev_priv(dev); int queue; - for (queue = 0; queue < rxq_number; queue++) { + for (queue = 0; queue < port->nrxqs; queue++) { struct mvpp2_rx_queue *rxq = port->rxqs[queue]; rxq->time_coal = c->rx_coalesce_usecs; @@ -6237,10 +7060,18 @@ static int mvpp2_ethtool_set_coalesce(struct net_device *dev, mvpp2_rx_time_coal_set(port, rxq); } - for (queue = 0; queue < txq_number; queue++) { + if (port->has_tx_irqs) { + port->tx_time_coal = c->tx_coalesce_usecs; + mvpp2_tx_time_coal_set(port); + } + + for (queue = 0; queue < port->ntxqs; queue++) { struct mvpp2_tx_queue *txq = port->txqs[queue]; txq->done_pkts_coal = c->tx_max_coalesced_frames; + + if (port->has_tx_irqs) + mvpp2_tx_pkts_coal_set(port, txq); } return 0; @@ -6365,6 +7196,129 @@ static const struct ethtool_ops mvpp2_eth_tool_ops = { .set_link_ksettings = phy_ethtool_set_link_ksettings, }; +/* Used for PPv2.1, or PPv2.2 with the old Device Tree binding that + * had a single IRQ defined per-port. + */ +static int mvpp2_simple_queue_vectors_init(struct mvpp2_port *port, + struct device_node *port_node) +{ + struct mvpp2_queue_vector *v = &port->qvecs[0]; + + v->first_rxq = 0; + v->nrxqs = port->nrxqs; + v->type = MVPP2_QUEUE_VECTOR_SHARED; + v->sw_thread_id = 0; + v->sw_thread_mask = *cpumask_bits(cpu_online_mask); + v->port = port; + v->irq = irq_of_parse_and_map(port_node, 0); + if (v->irq <= 0) + return -EINVAL; + netif_napi_add(port->dev, &v->napi, mvpp2_poll, + NAPI_POLL_WEIGHT); + + port->nqvecs = 1; + + return 0; +} + +static int mvpp2_multi_queue_vectors_init(struct mvpp2_port *port, + struct device_node *port_node) +{ + struct mvpp2_queue_vector *v; + int i, ret; + + port->nqvecs = num_possible_cpus(); + if (queue_mode == MVPP2_QDIST_SINGLE_MODE) + port->nqvecs += 1; + + for (i = 0; i < port->nqvecs; i++) { + char irqname[16]; + + v = port->qvecs + i; + + v->port = port; + v->type = MVPP2_QUEUE_VECTOR_PRIVATE; + v->sw_thread_id = i; + v->sw_thread_mask = BIT(i); + + snprintf(irqname, sizeof(irqname), "tx-cpu%d", i); + + if (queue_mode == MVPP2_QDIST_MULTI_MODE) { + v->first_rxq = i * MVPP2_DEFAULT_RXQ; + v->nrxqs = MVPP2_DEFAULT_RXQ; + } else if (queue_mode == MVPP2_QDIST_SINGLE_MODE && + i == (port->nqvecs - 1)) { + v->first_rxq = 0; + v->nrxqs = port->nrxqs; + v->type = MVPP2_QUEUE_VECTOR_SHARED; + strncpy(irqname, "rx-shared", sizeof(irqname)); + } + + v->irq = of_irq_get_byname(port_node, irqname); + if (v->irq <= 0) { + ret = -EINVAL; + goto err; + } + + netif_napi_add(port->dev, &v->napi, mvpp2_poll, + NAPI_POLL_WEIGHT); + } + + return 0; + +err: + for (i = 0; i < port->nqvecs; i++) + irq_dispose_mapping(port->qvecs[i].irq); + return ret; +} + +static int mvpp2_queue_vectors_init(struct mvpp2_port *port, + struct device_node *port_node) +{ + if (port->has_tx_irqs) + return mvpp2_multi_queue_vectors_init(port, port_node); + else + return mvpp2_simple_queue_vectors_init(port, port_node); +} + +static void mvpp2_queue_vectors_deinit(struct mvpp2_port *port) +{ + int i; + + for (i = 0; i < port->nqvecs; i++) + irq_dispose_mapping(port->qvecs[i].irq); +} + +/* Configure Rx queue group interrupt for this port */ +static void mvpp2_rx_irqs_setup(struct mvpp2_port *port) +{ + struct mvpp2 *priv = port->priv; + u32 val; + int i; + + if (priv->hw_version == MVPP21) { + mvpp2_write(priv, MVPP21_ISR_RXQ_GROUP_REG(port->id), + port->nrxqs); + return; + } + + /* Handle the more complicated PPv2.2 case */ + for (i = 0; i < port->nqvecs; i++) { + struct mvpp2_queue_vector *qv = port->qvecs + i; + + if (!qv->nrxqs) + continue; + + val = qv->sw_thread_id; + val |= port->id << MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET; + mvpp2_write(priv, MVPP22_ISR_RXQ_GROUP_INDEX_REG, val); + + val = qv->first_rxq; + val |= qv->nrxqs << MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET; + mvpp2_write(priv, MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG, val); + } +} + /* Initialize port HW */ static int mvpp2_port_init(struct mvpp2_port *port) { @@ -6373,15 +7327,22 @@ static int mvpp2_port_init(struct mvpp2_port *port) struct mvpp2_txq_pcpu *txq_pcpu; int queue, cpu, err; - if (port->first_rxq + rxq_number > + /* Checks for hardware constraints */ + if (port->first_rxq + port->nrxqs > MVPP2_MAX_PORTS * priv->max_port_rxqs) return -EINVAL; + if (port->nrxqs % 4 || (port->nrxqs > priv->max_port_rxqs) || + (port->ntxqs > MVPP2_MAX_TXQ)) + return -EINVAL; + /* Disable port */ mvpp2_egress_disable(port); mvpp2_port_disable(port); - port->txqs = devm_kcalloc(dev, txq_number, sizeof(*port->txqs), + port->tx_time_coal = MVPP2_TXDONE_COAL_USEC; + + port->txqs = devm_kcalloc(dev, port->ntxqs, sizeof(*port->txqs), GFP_KERNEL); if (!port->txqs) return -ENOMEM; @@ -6389,7 +7350,7 @@ static int mvpp2_port_init(struct mvpp2_port *port) /* Associate physical Tx queues to this port and initialize. * The mapping is predefined. */ - for (queue = 0; queue < txq_number; queue++) { + for (queue = 0; queue < port->ntxqs; queue++) { int queue_phy_id = mvpp2_txq_phys(port->id, queue); struct mvpp2_tx_queue *txq; @@ -6416,7 +7377,7 @@ static int mvpp2_port_init(struct mvpp2_port *port) port->txqs[queue] = txq; } - port->rxqs = devm_kcalloc(dev, rxq_number, sizeof(*port->rxqs), + port->rxqs = devm_kcalloc(dev, port->nrxqs, sizeof(*port->rxqs), GFP_KERNEL); if (!port->rxqs) { err = -ENOMEM; @@ -6424,7 +7385,7 @@ static int mvpp2_port_init(struct mvpp2_port *port) } /* Allocate and initialize Rx queue for this port */ - for (queue = 0; queue < rxq_number; queue++) { + for (queue = 0; queue < port->nrxqs; queue++) { struct mvpp2_rx_queue *rxq; /* Map physical Rx queue to port's logical Rx queue */ @@ -6441,22 +7402,10 @@ static int mvpp2_port_init(struct mvpp2_port *port) port->rxqs[queue] = rxq; } - /* Configure Rx queue group interrupt for this port */ - if (priv->hw_version == MVPP21) { - mvpp2_write(priv, MVPP21_ISR_RXQ_GROUP_REG(port->id), - rxq_number); - } else { - u32 val; - - val = (port->id << MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET); - mvpp2_write(priv, MVPP22_ISR_RXQ_GROUP_INDEX_REG, val); - - val = (rxq_number << MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET); - mvpp2_write(priv, MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG, val); - } + mvpp2_rx_irqs_setup(port); /* Create Rx descriptor rings */ - for (queue = 0; queue < rxq_number; queue++) { + for (queue = 0; queue < port->nrxqs; queue++) { struct mvpp2_rx_queue *rxq = port->rxqs[queue]; rxq->size = port->rx_ring_size; @@ -6484,7 +7433,7 @@ static int mvpp2_port_init(struct mvpp2_port *port) return 0; err_free_percpu: - for (queue = 0; queue < txq_number; queue++) { + for (queue = 0; queue < port->ntxqs; queue++) { if (!port->txqs[queue]) continue; free_percpu(port->txqs[queue]->pcpu); @@ -6492,35 +7441,93 @@ err_free_percpu: return err; } +/* Checks if the port DT description has the TX interrupts + * described. On PPv2.1, there are no such interrupts. On PPv2.2, + * there are available, but we need to keep support for old DTs. + */ +static bool mvpp2_port_has_tx_irqs(struct mvpp2 *priv, + struct device_node *port_node) +{ + char *irqs[5] = { "rx-shared", "tx-cpu0", "tx-cpu1", + "tx-cpu2", "tx-cpu3" }; + int ret, i; + + if (priv->hw_version == MVPP21) + return false; + + for (i = 0; i < 5; i++) { + ret = of_property_match_string(port_node, "interrupt-names", + irqs[i]); + if (ret < 0) + return false; + } + + return true; +} + +static void mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv, + struct device_node *port_node, + char **mac_from) +{ + struct mvpp2_port *port = netdev_priv(dev); + char hw_mac_addr[ETH_ALEN] = {0}; + const char *dt_mac_addr; + + dt_mac_addr = of_get_mac_address(port_node); + if (dt_mac_addr && is_valid_ether_addr(dt_mac_addr)) { + *mac_from = "device tree"; + ether_addr_copy(dev->dev_addr, dt_mac_addr); + return; + } + + if (priv->hw_version == MVPP21) { + mvpp21_get_mac_address(port, hw_mac_addr); + if (is_valid_ether_addr(hw_mac_addr)) { + *mac_from = "hardware"; + ether_addr_copy(dev->dev_addr, hw_mac_addr); + return; + } + } + + *mac_from = "random"; + eth_hw_addr_random(dev); +} + /* Ports initialization */ static int mvpp2_port_probe(struct platform_device *pdev, struct device_node *port_node, struct mvpp2 *priv) { struct device_node *phy_node; + struct phy *comphy; struct mvpp2_port *port; struct mvpp2_port_pcpu *port_pcpu; struct net_device *dev; struct resource *res; - const char *dt_mac_addr; - const char *mac_from; - char hw_mac_addr[ETH_ALEN] = {0}; + char *mac_from = ""; + unsigned int ntxqs, nrxqs; + bool has_tx_irqs; u32 id; int features; int phy_mode; int err, i, cpu; - dev = alloc_etherdev_mqs(sizeof(*port), txq_number, rxq_number); + has_tx_irqs = mvpp2_port_has_tx_irqs(priv, port_node); + + if (!has_tx_irqs) + queue_mode = MVPP2_QDIST_SINGLE_MODE; + + ntxqs = MVPP2_MAX_TXQ; + if (priv->hw_version == MVPP22 && queue_mode == MVPP2_QDIST_MULTI_MODE) + nrxqs = MVPP2_DEFAULT_RXQ * num_possible_cpus(); + else + nrxqs = MVPP2_DEFAULT_RXQ; + + dev = alloc_etherdev_mqs(sizeof(*port), ntxqs, nrxqs); if (!dev) return -ENOMEM; phy_node = of_parse_phandle(port_node, "phy", 0); - if (!phy_node) { - dev_err(&pdev->dev, "missing phy\n"); - err = -ENODEV; - goto err_free_netdev; - } - phy_mode = of_get_phy_mode(port_node); if (phy_mode < 0) { dev_err(&pdev->dev, "incorrect phy mode\n"); @@ -6528,6 +7535,15 @@ static int mvpp2_port_probe(struct platform_device *pdev, goto err_free_netdev; } + comphy = devm_of_phy_get(&pdev->dev, port_node, NULL); + if (IS_ERR(comphy)) { + if (PTR_ERR(comphy) == -EPROBE_DEFER) { + err = -EPROBE_DEFER; + goto err_free_netdev; + } + comphy = NULL; + } + if (of_property_read_u32(port_node, "port-id", &id)) { err = -EINVAL; dev_err(&pdev->dev, "missing port-id value\n"); @@ -6540,25 +7556,37 @@ static int mvpp2_port_probe(struct platform_device *pdev, dev->ethtool_ops = &mvpp2_eth_tool_ops; port = netdev_priv(dev); + port->dev = dev; + port->ntxqs = ntxqs; + port->nrxqs = nrxqs; + port->priv = priv; + port->has_tx_irqs = has_tx_irqs; - port->irq = irq_of_parse_and_map(port_node, 0); - if (port->irq <= 0) { - err = -EINVAL; + err = mvpp2_queue_vectors_init(port, port_node); + if (err) goto err_free_netdev; + + port->link_irq = of_irq_get_byname(port_node, "link"); + if (port->link_irq == -EPROBE_DEFER) { + err = -EPROBE_DEFER; + goto err_deinit_qvecs; } + if (port->link_irq <= 0) + /* the link irq is optional */ + port->link_irq = 0; if (of_property_read_bool(port_node, "marvell,loopback")) port->flags |= MVPP2_F_LOOPBACK; - port->priv = priv; port->id = id; if (priv->hw_version == MVPP21) - port->first_rxq = port->id * rxq_number; + port->first_rxq = port->id * port->nrxqs; else port->first_rxq = port->id * priv->max_port_rxqs; port->phy_node = phy_node; port->phy_interface = phy_mode; + port->comphy = comphy; if (priv->hw_version == MVPP21) { res = platform_get_resource(pdev, IORESOURCE_MEM, 2 + id); @@ -6572,7 +7600,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, &port->gop_id)) { err = -EINVAL; dev_err(&pdev->dev, "missing gop-port-id value\n"); - goto err_free_irq; + goto err_deinit_qvecs; } port->base = priv->iface_base + MVPP22_GMAC_BASE(port->gop_id); @@ -6585,25 +7613,10 @@ static int mvpp2_port_probe(struct platform_device *pdev, goto err_free_irq; } - dt_mac_addr = of_get_mac_address(port_node); - if (dt_mac_addr && is_valid_ether_addr(dt_mac_addr)) { - mac_from = "device tree"; - ether_addr_copy(dev->dev_addr, dt_mac_addr); - } else { - if (priv->hw_version == MVPP21) - mvpp21_get_mac_address(port, hw_mac_addr); - if (is_valid_ether_addr(hw_mac_addr)) { - mac_from = "hardware"; - ether_addr_copy(dev->dev_addr, hw_mac_addr); - } else { - mac_from = "random"; - eth_hw_addr_random(dev); - } - } + mvpp2_port_copy_mac_addr(dev, priv, port_node, &mac_from); port->tx_ring_size = MVPP2_MAX_TXD; port->rx_ring_size = MVPP2_MAX_RXD; - port->dev = dev; SET_NETDEV_DEV(dev, &pdev->dev); err = mvpp2_port_init(port); @@ -6612,7 +7625,6 @@ static int mvpp2_port_probe(struct platform_device *pdev, goto err_free_stats; } - mvpp2_port_mii_set(port); mvpp2_port_periodic_xon_disable(port); if (priv->hw_version == MVPP21) @@ -6626,20 +7638,22 @@ static int mvpp2_port_probe(struct platform_device *pdev, goto err_free_txq_pcpu; } - for_each_present_cpu(cpu) { - port_pcpu = per_cpu_ptr(port->pcpu, cpu); + if (!port->has_tx_irqs) { + for_each_present_cpu(cpu) { + port_pcpu = per_cpu_ptr(port->pcpu, cpu); - hrtimer_init(&port_pcpu->tx_done_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_REL_PINNED); - port_pcpu->tx_done_timer.function = mvpp2_hr_timer_cb; - port_pcpu->timer_scheduled = false; + hrtimer_init(&port_pcpu->tx_done_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL_PINNED); + port_pcpu->tx_done_timer.function = mvpp2_hr_timer_cb; + port_pcpu->timer_scheduled = false; - tasklet_init(&port_pcpu->tx_done_tasklet, mvpp2_tx_proc_cb, - (unsigned long)dev); + tasklet_init(&port_pcpu->tx_done_tasklet, + mvpp2_tx_proc_cb, + (unsigned long)dev); + } } - netif_napi_add(dev, &port->napi, mvpp2_poll, NAPI_POLL_WEIGHT); - features = NETIF_F_SG | NETIF_F_IP_CSUM; + features = NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO; dev->features = features | NETIF_F_RXCSUM; dev->hw_features |= features | NETIF_F_RXCSUM | NETIF_F_GRO; dev->vlan_features |= features; @@ -6662,12 +7676,15 @@ static int mvpp2_port_probe(struct platform_device *pdev, err_free_port_pcpu: free_percpu(port->pcpu); err_free_txq_pcpu: - for (i = 0; i < txq_number; i++) + for (i = 0; i < port->ntxqs; i++) free_percpu(port->txqs[i]->pcpu); err_free_stats: free_percpu(port->stats); err_free_irq: - irq_dispose_mapping(port->irq); + if (port->link_irq) + irq_dispose_mapping(port->link_irq); +err_deinit_qvecs: + mvpp2_queue_vectors_deinit(port); err_free_netdev: of_node_put(phy_node); free_netdev(dev); @@ -6683,9 +7700,11 @@ static void mvpp2_port_remove(struct mvpp2_port *port) of_node_put(port->phy_node); free_percpu(port->pcpu); free_percpu(port->stats); - for (i = 0; i < txq_number; i++) + for (i = 0; i < port->ntxqs; i++) free_percpu(port->txqs[i]->pcpu); - irq_dispose_mapping(port->irq); + mvpp2_queue_vectors_deinit(port); + if (port->link_irq) + irq_dispose_mapping(port->link_irq); free_netdev(port->dev); } @@ -6800,13 +7819,6 @@ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) int err, i; u32 val; - /* Checks for hardware constraints */ - if (rxq_number % 4 || (rxq_number > priv->max_port_rxqs) || - (txq_number > MVPP2_MAX_TXQ)) { - dev_err(&pdev->dev, "invalid queue size parameter\n"); - return -EINVAL; - } - /* MBUS windows configuration */ dram_target_info = mv_mbus_dram_info(); if (dram_target_info) @@ -6836,8 +7848,7 @@ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) for_each_present_cpu(i) { priv->aggr_txqs[i].id = i; priv->aggr_txqs[i].size = MVPP2_AGGR_TXQ_SIZE; - err = mvpp2_aggr_txq_init(pdev, &priv->aggr_txqs[i], - MVPP2_AGGR_TXQ_SIZE, i, priv); + err = mvpp2_aggr_txq_init(pdev, &priv->aggr_txqs[i], i, priv); if (err < 0) return err; } @@ -6845,23 +7856,6 @@ static int mvpp2_init(struct platform_device *pdev, struct mvpp2 *priv) /* Rx Fifo Init */ mvpp2_rx_fifo_init(priv); - /* Reset Rx queue group interrupt configuration */ - for (i = 0; i < MVPP2_MAX_PORTS; i++) { - if (priv->hw_version == MVPP21) { - mvpp2_write(priv, MVPP21_ISR_RXQ_GROUP_REG(i), - rxq_number); - continue; - } else { - u32 val; - - val = (i << MVPP22_ISR_RXQ_GROUP_INDEX_GROUP_OFFSET); - mvpp2_write(priv, MVPP22_ISR_RXQ_GROUP_INDEX_REG, val); - - val = (rxq_number << MVPP22_ISR_RXQ_SUB_GROUP_SIZE_OFFSET); - mvpp2_write(priv, MVPP22_ISR_RXQ_SUB_GROUP_CONFIG_REG, val); - } - } - if (priv->hw_version == MVPP21) writel(MVPP2_EXT_GLOBAL_CTRL_DEFAULT, priv->lms_base + MVPP2_MNG_EXTENDED_GLOBAL_CTRL_REG); @@ -6892,7 +7886,7 @@ static int mvpp2_probe(struct platform_device *pdev) struct mvpp2 *priv; struct resource *res; void __iomem *base; - int port_count, cpu; + int port_count, i; int err; priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); @@ -6917,14 +7911,25 @@ static int mvpp2_probe(struct platform_device *pdev) priv->iface_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->iface_base)) return PTR_ERR(priv->iface_base); + + priv->sysctrl_base = + syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "marvell,system-controller"); + if (IS_ERR(priv->sysctrl_base)) + /* The system controller regmap is optional for dt + * compatibility reasons. When not provided, the + * configuration of the GoP relies on the + * firmware/bootloader. + */ + priv->sysctrl_base = NULL; } - for_each_present_cpu(cpu) { + for (i = 0; i < MVPP2_MAX_THREADS; i++) { u32 addr_space_sz; addr_space_sz = (priv->hw_version == MVPP21 ? MVPP21_ADDR_SPACE_SZ : MVPP22_ADDR_SPACE_SZ); - priv->cpu_base[cpu] = base + cpu * addr_space_sz; + priv->swth_base[i] = base + i * addr_space_sz; } if (priv->hw_version == MVPP21) diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index 5d7d94de4e00..8a835e82256a 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -3516,7 +3516,7 @@ static const char *skge_board_name(const struct skge_hw *hw) if (skge_chips[i].id == hw->chip_id) return skge_chips[i].name; - snprintf(buf, sizeof buf, "chipid 0x%x", hw->chip_id); + snprintf(buf, sizeof(buf), "chipid 0x%x", hw->chip_id); return buf; } diff --git a/drivers/net/ethernet/mediatek/Kconfig b/drivers/net/ethernet/mediatek/Kconfig index 698bb89aa901..f9149d2a4694 100644 --- a/drivers/net/ethernet/mediatek/Kconfig +++ b/drivers/net/ethernet/mediatek/Kconfig @@ -7,11 +7,11 @@ config NET_VENDOR_MEDIATEK if NET_VENDOR_MEDIATEK config NET_MEDIATEK_SOC - tristate "MediaTek MT7623 Gigabit ethernet support" - depends on NET_VENDOR_MEDIATEK && (MACH_MT7623 || MACH_MT2701) + tristate "MediaTek SoC Gigabit Ethernet support" + depends on NET_VENDOR_MEDIATEK select PHYLIB ---help--- This driver supports the gigabit ethernet MACs in the - MediaTek MT2701/MT7623 chipset family. + MediaTek SoC family. endif #NET_VENDOR_MEDIATEK diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index e588a0cdb074..5e81a7263654 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -53,7 +53,8 @@ static const struct mtk_ethtool_stats { }; static const char * const mtk_clks_source_name[] = { - "ethif", "esw", "gp1", "gp2", "trgpll" + "ethif", "esw", "gp0", "gp1", "gp2", "trgpll", "sgmii_tx250m", + "sgmii_rx250m", "sgmii_cdr_ref", "sgmii_cdr_fb", "sgmii_ck", "eth2pll" }; void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg) @@ -163,6 +164,47 @@ static void mtk_gmac0_rgmii_adjust(struct mtk_eth *eth, int speed) mtk_w32(eth, val, TRGMII_TCK_CTRL); } +static void mtk_gmac_sgmii_hw_setup(struct mtk_eth *eth, int mac_id) +{ + u32 val; + + /* Setup the link timer and QPHY power up inside SGMIISYS */ + regmap_write(eth->sgmiisys, SGMSYS_PCS_LINK_TIMER, + SGMII_LINK_TIMER_DEFAULT); + + regmap_read(eth->sgmiisys, SGMSYS_SGMII_MODE, &val); + val |= SGMII_REMOTE_FAULT_DIS; + regmap_write(eth->sgmiisys, SGMSYS_SGMII_MODE, val); + + regmap_read(eth->sgmiisys, SGMSYS_PCS_CONTROL_1, &val); + val |= SGMII_AN_RESTART; + regmap_write(eth->sgmiisys, SGMSYS_PCS_CONTROL_1, val); + + regmap_read(eth->sgmiisys, SGMSYS_QPHY_PWR_STATE_CTRL, &val); + val &= ~SGMII_PHYA_PWD; + regmap_write(eth->sgmiisys, SGMSYS_QPHY_PWR_STATE_CTRL, val); + + /* Determine MUX for which GMAC uses the SGMII interface */ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_DUAL_GMAC_SHARED_SGMII)) { + regmap_read(eth->ethsys, ETHSYS_SYSCFG0, &val); + val &= ~SYSCFG0_SGMII_MASK; + val |= !mac_id ? SYSCFG0_SGMII_GMAC1 : SYSCFG0_SGMII_GMAC2; + regmap_write(eth->ethsys, ETHSYS_SYSCFG0, val); + + dev_info(eth->dev, "setup shared sgmii for gmac=%d\n", + mac_id); + } + + /* Setup the GMAC1 going through SGMII path when SoC also support + * ESW on GMAC1 + */ + if (MTK_HAS_CAPS(eth->soc->caps, MTK_GMAC1_ESW | MTK_GMAC1_SGMII) && + !mac_id) { + mtk_w32(eth, 0, MTK_MAC_MISC); + dev_info(eth->dev, "setup gmac1 going through sgmii"); + } +} + static void mtk_phy_link_adjust(struct net_device *dev) { struct mtk_mac *mac = netdev_priv(dev); @@ -185,7 +227,8 @@ static void mtk_phy_link_adjust(struct net_device *dev) break; }; - if (mac->id == 0 && !mac->trgmii) + if (MTK_HAS_CAPS(mac->hw->soc->caps, MTK_GMAC1_TRGMII) && + !mac->id && !mac->trgmii) mtk_gmac0_rgmii_adjust(mac->hw, dev->phydev->speed); if (dev->phydev->link) @@ -269,6 +312,7 @@ static int mtk_phy_connect(struct net_device *dev) if (!np) return -ENODEV; + mac->ge_mode = 0; switch (of_get_phy_mode(np)) { case PHY_INTERFACE_MODE_TRGMII: mac->trgmii = true; @@ -276,7 +320,10 @@ static int mtk_phy_connect(struct net_device *dev) case PHY_INTERFACE_MODE_RGMII_RXID: case PHY_INTERFACE_MODE_RGMII_ID: case PHY_INTERFACE_MODE_RGMII: - mac->ge_mode = 0; + break; + case PHY_INTERFACE_MODE_SGMII: + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) + mtk_gmac_sgmii_hw_setup(eth, mac->id); break; case PHY_INTERFACE_MODE_MII: mac->ge_mode = 1; @@ -1032,7 +1079,6 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget) unsigned int done[MTK_MAX_DEVS]; unsigned int bytes[MTK_MAX_DEVS]; u32 cpu, dma; - static int condition; int total = 0, i; memset(done, 0, sizeof(done)); @@ -1056,10 +1102,8 @@ static int mtk_poll_tx(struct mtk_eth *eth, int budget) mac = 1; skb = tx_buf->skb; - if (!skb) { - condition = 1; + if (!skb) break; - } if (skb != (struct sk_buff *)MTK_DMA_DUMMY_DESC) { bytes[mac] += skb->len; @@ -1241,9 +1285,19 @@ static void mtk_tx_clean(struct mtk_eth *eth) static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag) { - struct mtk_rx_ring *ring = ð->rx_ring[ring_no]; + struct mtk_rx_ring *ring; int rx_data_len, rx_dma_size; int i; + u32 offset = 0; + + if (rx_flag == MTK_RX_FLAGS_QDMA) { + if (ring_no) + return -EINVAL; + ring = ð->rx_ring_qdma; + offset = 0x1000; + } else { + ring = ð->rx_ring[ring_no]; + } if (rx_flag == MTK_RX_FLAGS_HWLRO) { rx_data_len = MTK_MAX_LRO_RX_LENGTH; @@ -1293,17 +1347,16 @@ static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag) */ wmb(); - mtk_w32(eth, ring->phys, MTK_PRX_BASE_PTR_CFG(ring_no)); - mtk_w32(eth, rx_dma_size, MTK_PRX_MAX_CNT_CFG(ring_no)); - mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg); - mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), MTK_PDMA_RST_IDX); + mtk_w32(eth, ring->phys, MTK_PRX_BASE_PTR_CFG(ring_no) + offset); + mtk_w32(eth, rx_dma_size, MTK_PRX_MAX_CNT_CFG(ring_no) + offset); + mtk_w32(eth, ring->calc_idx, ring->crx_idx_reg + offset); + mtk_w32(eth, MTK_PST_DRX_IDX_CFG(ring_no), MTK_PDMA_RST_IDX + offset); return 0; } -static void mtk_rx_clean(struct mtk_eth *eth, int ring_no) +static void mtk_rx_clean(struct mtk_eth *eth, struct mtk_rx_ring *ring) { - struct mtk_rx_ring *ring = ð->rx_ring[ring_no]; int i; if (ring->data && ring->dma) { @@ -1629,6 +1682,10 @@ static int mtk_dma_init(struct mtk_eth *eth) if (err) return err; + err = mtk_rx_alloc(eth, 0, MTK_RX_FLAGS_QDMA); + if (err) + return err; + err = mtk_rx_alloc(eth, 0, MTK_RX_FLAGS_NORMAL); if (err) return err; @@ -1668,12 +1725,13 @@ static void mtk_dma_free(struct mtk_eth *eth) eth->phy_scratch_ring = 0; } mtk_tx_clean(eth); - mtk_rx_clean(eth, 0); + mtk_rx_clean(eth, ð->rx_ring[0]); + mtk_rx_clean(eth, ð->rx_ring_qdma); if (eth->hwlro) { mtk_hwlro_rx_uninit(eth); for (i = 1; i < MTK_MAX_RX_RING_NUM; i++) - mtk_rx_clean(eth, i); + mtk_rx_clean(eth, ð->rx_ring[i]); } kfree(eth->scratch_head); @@ -1740,7 +1798,9 @@ static int mtk_start_dma(struct mtk_eth *eth) mtk_w32(eth, MTK_TX_WB_DDONE | MTK_TX_DMA_EN | - MTK_DMA_SIZE_16DWORDS | MTK_NDP_CO_PRO, + MTK_DMA_SIZE_16DWORDS | MTK_NDP_CO_PRO | + MTK_RX_DMA_EN | MTK_RX_2B_OFFSET | + MTK_RX_BT_32DWORDS, MTK_QDMA_GLO_CFG); mtk_w32(eth, @@ -1837,9 +1897,36 @@ static void ethsys_reset(struct mtk_eth *eth, u32 reset_bits) mdelay(10); } +static void mtk_clk_disable(struct mtk_eth *eth) +{ + int clk; + + for (clk = MTK_CLK_MAX - 1; clk >= 0; clk--) + clk_disable_unprepare(eth->clks[clk]); +} + +static int mtk_clk_enable(struct mtk_eth *eth) +{ + int clk, ret; + + for (clk = 0; clk < MTK_CLK_MAX ; clk++) { + ret = clk_prepare_enable(eth->clks[clk]); + if (ret) + goto err_disable_clks; + } + + return 0; + +err_disable_clks: + while (--clk >= 0) + clk_disable_unprepare(eth->clks[clk]); + + return ret; +} + static int mtk_hw_init(struct mtk_eth *eth) { - int i, val; + int i, val, ret; if (test_and_set_bit(MTK_HW_INIT, ð->state)) return 0; @@ -1847,10 +1934,10 @@ static int mtk_hw_init(struct mtk_eth *eth) pm_runtime_enable(eth->dev); pm_runtime_get_sync(eth->dev); - clk_prepare_enable(eth->clks[MTK_CLK_ETHIF]); - clk_prepare_enable(eth->clks[MTK_CLK_ESW]); - clk_prepare_enable(eth->clks[MTK_CLK_GP1]); - clk_prepare_enable(eth->clks[MTK_CLK_GP2]); + ret = mtk_clk_enable(eth); + if (ret) + goto err_disable_pm; + ethsys_reset(eth, RSTCTRL_FE); ethsys_reset(eth, RSTCTRL_PPE); @@ -1918,6 +2005,12 @@ static int mtk_hw_init(struct mtk_eth *eth) } return 0; + +err_disable_pm: + pm_runtime_put_sync(eth->dev); + pm_runtime_disable(eth->dev); + + return ret; } static int mtk_hw_deinit(struct mtk_eth *eth) @@ -1925,10 +2018,7 @@ static int mtk_hw_deinit(struct mtk_eth *eth) if (!test_and_clear_bit(MTK_HW_INIT, ð->state)) return 0; - clk_disable_unprepare(eth->clks[MTK_CLK_GP2]); - clk_disable_unprepare(eth->clks[MTK_CLK_GP1]); - clk_disable_unprepare(eth->clks[MTK_CLK_ESW]); - clk_disable_unprepare(eth->clks[MTK_CLK_ETHIF]); + mtk_clk_disable(eth); pm_runtime_put_sync(eth->dev); pm_runtime_disable(eth->dev); @@ -2395,6 +2485,7 @@ static int mtk_get_chip_id(struct mtk_eth *eth, u32 *chip_id) static bool mtk_is_hwlro_supported(struct mtk_eth *eth) { switch (eth->chip_id) { + case MT7622_ETH: case MT7623_ETH: return true; } @@ -2406,6 +2497,7 @@ static int mtk_probe(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); struct device_node *mac_np; + const struct of_device_id *match; struct mtk_eth *eth; int err; int i; @@ -2414,6 +2506,9 @@ static int mtk_probe(struct platform_device *pdev) if (!eth) return -ENOMEM; + match = of_match_device(of_mtk_match, &pdev->dev); + eth->soc = (struct mtk_soc_data *)match->data; + eth->dev = &pdev->dev; eth->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(eth->base)) @@ -2430,6 +2525,16 @@ static int mtk_probe(struct platform_device *pdev) return PTR_ERR(eth->ethsys); } + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { + eth->sgmiisys = + syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "mediatek,sgmiisys"); + if (IS_ERR(eth->sgmiisys)) { + dev_err(&pdev->dev, "no sgmiisys regmap found\n"); + return PTR_ERR(eth->sgmiisys); + } + } + eth->pctl = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "mediatek,pctl"); if (IS_ERR(eth->pctl)) { @@ -2450,7 +2555,12 @@ static int mtk_probe(struct platform_device *pdev) if (IS_ERR(eth->clks[i])) { if (PTR_ERR(eth->clks[i]) == -EPROBE_DEFER) return -EPROBE_DEFER; - return -ENODEV; + if (eth->soc->required_clks & BIT(i)) { + dev_err(&pdev->dev, "clock %s not found\n", + mtk_clks_source_name[i]); + return -EINVAL; + } + eth->clks[i] = NULL; } } @@ -2553,8 +2663,25 @@ static int mtk_remove(struct platform_device *pdev) return 0; } +static const struct mtk_soc_data mt2701_data = { + .caps = MTK_GMAC1_TRGMII, + .required_clks = MT7623_CLKS_BITMAP +}; + +static const struct mtk_soc_data mt7622_data = { + .caps = MTK_DUAL_GMAC_SHARED_SGMII | MTK_GMAC1_ESW, + .required_clks = MT7622_CLKS_BITMAP +}; + +static const struct mtk_soc_data mt7623_data = { + .caps = MTK_GMAC1_TRGMII, + .required_clks = MT7623_CLKS_BITMAP +}; + const struct of_device_id of_mtk_match[] = { - { .compatible = "mediatek,mt2701-eth" }, + { .compatible = "mediatek,mt2701-eth", .data = &mt2701_data}, + { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data}, + { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data}, {}, }; MODULE_DEVICE_TABLE(of, of_mtk_match); diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.h b/drivers/net/ethernet/mediatek/mtk_eth_soc.h index 5868a09f623a..3d3c24a28112 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h @@ -302,6 +302,9 @@ #define PHY_IAC_REG_SHIFT 25 #define PHY_IAC_TIMEOUT HZ +#define MTK_MAC_MISC 0x1000c +#define MTK_MUX_TO_ESW BIT(0) + /* Mac control registers */ #define MTK_MAC_MCR(x) (0x10100 + (x * 0x100)) #define MAC_MCR_MAX_RX_1536 BIT(24) @@ -357,11 +360,15 @@ #define ETHSYS_CHIPID0_3 0x0 #define ETHSYS_CHIPID4_7 0x4 #define MT7623_ETH 7623 +#define MT7622_ETH 7622 /* ethernet subsystem config register */ #define ETHSYS_SYSCFG0 0x14 #define SYSCFG0_GE_MASK 0x3 #define SYSCFG0_GE_MODE(x, y) (x << (12 + (y * 2))) +#define SYSCFG0_SGMII_MASK (3 << 8) +#define SYSCFG0_SGMII_GMAC1 ((2 << 8) & GENMASK(9, 8)) +#define SYSCFG0_SGMII_GMAC2 ((3 << 8) & GENMASK(9, 8)) /* ethernet subsystem clock register */ #define ETHSYS_CLKCFG0 0x2c @@ -372,6 +379,23 @@ #define RSTCTRL_FE BIT(6) #define RSTCTRL_PPE BIT(31) +/* SGMII subsystem config registers */ +/* Register to auto-negotiation restart */ +#define SGMSYS_PCS_CONTROL_1 0x0 +#define SGMII_AN_RESTART BIT(9) + +/* Register to programmable link timer, the unit in 2 * 8ns */ +#define SGMSYS_PCS_LINK_TIMER 0x18 +#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & GENMASK(19, 0)) + +/* Register to control remote fault */ +#define SGMSYS_SGMII_MODE 0x20 +#define SGMII_REMOTE_FAULT_DIS BIT(8) + +/* Register to power up QPHY */ +#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8 +#define SGMII_PHYA_PWD BIT(4) + struct mtk_rx_dma { unsigned int rxd1; unsigned int rxd2; @@ -437,12 +461,31 @@ enum mtk_tx_flags { enum mtk_clks_map { MTK_CLK_ETHIF, MTK_CLK_ESW, + MTK_CLK_GP0, MTK_CLK_GP1, MTK_CLK_GP2, MTK_CLK_TRGPLL, + MTK_CLK_SGMII_TX_250M, + MTK_CLK_SGMII_RX_250M, + MTK_CLK_SGMII_CDR_REF, + MTK_CLK_SGMII_CDR_FB, + MTK_CLK_SGMII_CK, + MTK_CLK_ETH2PLL, MTK_CLK_MAX }; +#define MT7623_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \ + BIT(MTK_CLK_GP1) | BIT(MTK_CLK_GP2) | \ + BIT(MTK_CLK_TRGPLL)) +#define MT7622_CLKS_BITMAP (BIT(MTK_CLK_ETHIF) | BIT(MTK_CLK_ESW) | \ + BIT(MTK_CLK_GP0) | BIT(MTK_CLK_GP1) | \ + BIT(MTK_CLK_GP2) | \ + BIT(MTK_CLK_SGMII_TX_250M) | \ + BIT(MTK_CLK_SGMII_RX_250M) | \ + BIT(MTK_CLK_SGMII_CDR_REF) | \ + BIT(MTK_CLK_SGMII_CDR_FB) | \ + BIT(MTK_CLK_SGMII_CK) | \ + BIT(MTK_CLK_ETH2PLL)) enum mtk_dev_state { MTK_HW_INIT, MTK_RESETTING @@ -489,6 +532,7 @@ struct mtk_tx_ring { enum mtk_rx_flags { MTK_RX_FLAGS_NORMAL = 0, MTK_RX_FLAGS_HWLRO, + MTK_RX_FLAGS_QDMA, }; /* struct mtk_rx_ring - This struct holds info describing a RX ring @@ -511,6 +555,28 @@ struct mtk_rx_ring { u32 crx_idx_reg; }; +#define MTK_TRGMII BIT(0) +#define MTK_GMAC1_TRGMII (BIT(1) | MTK_TRGMII) +#define MTK_ESW BIT(4) +#define MTK_GMAC1_ESW (BIT(5) | MTK_ESW) +#define MTK_SGMII BIT(8) +#define MTK_GMAC1_SGMII (BIT(9) | MTK_SGMII) +#define MTK_GMAC2_SGMII (BIT(10) | MTK_SGMII) +#define MTK_DUAL_GMAC_SHARED_SGMII (BIT(11) | MTK_GMAC1_SGMII | \ + MTK_GMAC2_SGMII) +#define MTK_HAS_CAPS(caps, _x) (((caps) & (_x)) == (_x)) + +/* struct mtk_eth_data - This is the structure holding all differences + * among various plaforms + * @caps Flags shown the extra capability for the SoC + * @required_clks Flags shown the bitmap for required clocks on + * the target SoC + */ +struct mtk_soc_data { + u32 caps; + u32 required_clks; +}; + /* currently no SoC has more than 2 macs */ #define MTK_MAX_DEVS 2 @@ -529,11 +595,14 @@ struct mtk_rx_ring { * @msg_enable: Ethtool msg level * @ethsys: The register map pointing at the range used to setup * MII modes + * @sgmiisys: The register map pointing at the range used to setup + * SGMII modes * @pctl: The register map pointing at the range used to setup * GMAC port drive/slew values * @dma_refcnt: track how many netdevs are using the DMA engine - * @tx_ring: Pointer to the memore holding info about the TX ring - * @rx_ring: Pointer to the memore holding info about the RX ring + * @tx_ring: Pointer to the memory holding info about the TX ring + * @rx_ring: Pointer to the memory holding info about the RX ring + * @rx_ring_qdma: Pointer to the memory holding info about the QDMA RX ring * @tx_napi: The TX NAPI struct * @rx_napi: The RX NAPI struct * @scratch_ring: Newer SoCs need memory for a second HW managed TX ring @@ -542,7 +611,8 @@ struct mtk_rx_ring { * @clks: clock array for all clocks required * @mii_bus: If there is a bus we need to create an instance for it * @pending_work: The workqueue used to reset the dma ring - * @state Initialization and runtime state of the device. + * @state: Initialization and runtime state of the device + * @soc: Holding specific data among vaious SoCs */ struct mtk_eth { @@ -558,12 +628,14 @@ struct mtk_eth { u32 msg_enable; unsigned long sysclk; struct regmap *ethsys; + struct regmap *sgmiisys; struct regmap *pctl; u32 chip_id; bool hwlro; atomic_t dma_refcnt; struct mtk_tx_ring tx_ring; struct mtk_rx_ring rx_ring[MTK_MAX_RX_RING_NUM]; + struct mtk_rx_ring rx_ring_qdma; struct napi_struct tx_napi; struct napi_struct rx_napi; struct mtk_tx_dma *scratch_ring; @@ -574,6 +646,8 @@ struct mtk_eth { struct mii_bus *mii_bus; struct work_struct pending_work; unsigned long state; + + const struct mtk_soc_data *soc; }; /* struct mtk_mac - the structure that holds the info about the MACs of the diff --git a/drivers/net/ethernet/mellanox/Kconfig b/drivers/net/ethernet/mellanox/Kconfig index 84a200764111..872548cd9431 100644 --- a/drivers/net/ethernet/mellanox/Kconfig +++ b/drivers/net/ethernet/mellanox/Kconfig @@ -5,9 +5,10 @@ config NET_VENDOR_MELLANOX bool "Mellanox devices" default y - depends on PCI + depends on PCI || I2C ---help--- - If you have a network (Ethernet) card belonging to this class, say Y. + If you have a network (Ethernet or RDMA) device belonging to this + class, say Y. Note that the answer to this question doesn't directly affect the kernel: saying N will just cause the configurator to skip all diff --git a/drivers/net/ethernet/mellanox/mlx4/alloc.c b/drivers/net/ethernet/mellanox/mlx4/alloc.c index b651c1210555..6dabd983e7e0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx4/alloc.c @@ -186,7 +186,7 @@ int mlx4_bitmap_init(struct mlx4_bitmap *bitmap, u32 num, u32 mask, bitmap->effective_len = bitmap->avail; spin_lock_init(&bitmap->lock); bitmap->table = kzalloc(BITS_TO_LONGS(bitmap->max) * - sizeof (long), GFP_KERNEL); + sizeof(long), GFP_KERNEL); if (!bitmap->table) return -ENOMEM; diff --git a/drivers/net/ethernet/mellanox/mlx4/cmd.c b/drivers/net/ethernet/mellanox/mlx4/cmd.c index 78b89ceb4f46..6a9086dc1e92 100644 --- a/drivers/net/ethernet/mellanox/mlx4/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx4/cmd.c @@ -1958,19 +1958,19 @@ static void mlx4_allocate_port_vpps(struct mlx4_dev *dev, int port) int i; int err; int num_vfs; - u16 availible_vpp; + u16 available_vpp; u8 vpp_param[MLX4_NUM_UP]; struct mlx4_qos_manager *port_qos; struct mlx4_priv *priv = mlx4_priv(dev); - err = mlx4_ALLOCATE_VPP_get(dev, port, &availible_vpp, vpp_param); + err = mlx4_ALLOCATE_VPP_get(dev, port, &available_vpp, vpp_param); if (err) { - mlx4_info(dev, "Failed query availible VPPs\n"); + mlx4_info(dev, "Failed query available VPPs\n"); return; } port_qos = &priv->mfunc.master.qos_ctl[port]; - num_vfs = (availible_vpp / + num_vfs = (available_vpp / bitmap_weight(port_qos->priority_bm, MLX4_NUM_UP)); for (i = 0; i < MLX4_NUM_UP; i++) { @@ -1985,14 +1985,14 @@ static void mlx4_allocate_port_vpps(struct mlx4_dev *dev, int port) } /* Query actual allocated VPP, just to make sure */ - err = mlx4_ALLOCATE_VPP_get(dev, port, &availible_vpp, vpp_param); + err = mlx4_ALLOCATE_VPP_get(dev, port, &available_vpp, vpp_param); if (err) { - mlx4_info(dev, "Failed query availible VPPs\n"); + mlx4_info(dev, "Failed query available VPPs\n"); return; } port_qos->num_of_qos_vfs = num_vfs; - mlx4_dbg(dev, "Port %d Availible VPPs %d\n", port, availible_vpp); + mlx4_dbg(dev, "Port %d Available VPPs %d\n", port, available_vpp); for (i = 0; i < MLX4_NUM_UP; i++) mlx4_dbg(dev, "Port %d UP %d Allocated %d VPPs\n", port, i, @@ -2637,7 +2637,7 @@ int mlx4_cmd_use_events(struct mlx4_dev *dev) int err = 0; priv->cmd.context = kmalloc(priv->cmd.max_cmds * - sizeof (struct mlx4_cmd_context), + sizeof(struct mlx4_cmd_context), GFP_KERNEL); if (!priv->cmd.context) return -ENOMEM; @@ -2695,7 +2695,7 @@ struct mlx4_cmd_mailbox *mlx4_alloc_cmd_mailbox(struct mlx4_dev *dev) { struct mlx4_cmd_mailbox *mailbox; - mailbox = kmalloc(sizeof *mailbox, GFP_KERNEL); + mailbox = kmalloc(sizeof(*mailbox), GFP_KERNEL); if (!mailbox) return ERR_PTR(-ENOMEM); @@ -2891,7 +2891,7 @@ static int mlx4_set_vport_qos(struct mlx4_priv *priv, int slave, int port, memset(vpp_qos, 0, sizeof(struct mlx4_vport_qos_param) * MLX4_NUM_UP); if (slave > port_qos->num_of_qos_vfs) { - mlx4_info(dev, "No availible VPP resources for this VF\n"); + mlx4_info(dev, "No available VPP resources for this VF\n"); return -EINVAL; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_cq.c b/drivers/net/ethernet/mellanox/mlx4/en_cq.c index f849eec21824..1e487acb4667 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_cq.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_cq.c @@ -209,12 +209,10 @@ int mlx4_en_set_cq_moder(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) cq->moder_cnt, cq->moder_time); } -int mlx4_en_arm_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) +void mlx4_en_arm_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq) { mlx4_cq_arm(&cq->mcq, MLX4_CQ_DB_REQ_NOT, priv->mdev->uar_map, &priv->mdev->uar_lock); - - return 0; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_main.c b/drivers/net/ethernet/mellanox/mlx4/en_main.c index 2b0cbca4beb5..686e18de9a97 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_main.c @@ -147,7 +147,7 @@ void mlx4_en_update_loopback_state(struct net_device *dev, mutex_unlock(&priv->mdev->state_lock); } -static int mlx4_en_get_profile(struct mlx4_en_dev *mdev) +static void mlx4_en_get_profile(struct mlx4_en_dev *mdev) { struct mlx4_en_profile *params = &mdev->profile; int i; @@ -176,8 +176,6 @@ static int mlx4_en_get_profile(struct mlx4_en_dev *mdev) params->prof[i].rss_rings = 0; params->prof[i].inline_thold = inline_thold; } - - return 0; } static void *mlx4_en_get_netdev(struct mlx4_dev *dev, void *ctx, u8 port) @@ -309,10 +307,7 @@ static void *mlx4_en_add(struct mlx4_dev *dev) } /* Build device profile according to supplied module parameters */ - if (mlx4_en_get_profile(mdev)) { - mlx4_err(mdev, "Bad module parameters, aborting\n"); - goto err_mr; - } + mlx4_en_get_profile(mdev); /* Configure which ports to start according to module parameters */ mdev->port_cnt = 0; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index e3e6d9fa69fd..9c218f1cfc6c 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -130,19 +130,20 @@ out: return err; } -static int __mlx4_en_setup_tc(struct net_device *dev, u32 handle, - u32 chain_index, __be16 proto, - struct tc_to_netdev *tc) +static int __mlx4_en_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) { - if (tc->type != TC_SETUP_MQPRIO) - return -EINVAL; + struct tc_mqprio_qopt *mqprio = type_data; + + if (type != TC_SETUP_MQPRIO) + return -EOPNOTSUPP; - if (tc->mqprio->num_tc && tc->mqprio->num_tc != MLX4_EN_NUM_UP_HIGH) + if (mqprio->num_tc && mqprio->num_tc != MLX4_EN_NUM_UP_HIGH) return -EINVAL; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - return mlx4_en_alloc_tx_queue_per_tc(dev, tc->mqprio->num_tc); + return mlx4_en_alloc_tx_queue_per_tc(dev, mqprio->num_tc); } #ifdef CONFIG_RFS_ACCEL @@ -732,6 +733,21 @@ static int mlx4_en_replace_mac(struct mlx4_en_priv *priv, int qpn, return __mlx4_replace_mac(dev, priv->port, qpn, new_mac_u64); } +static void mlx4_en_update_user_mac(struct mlx4_en_priv *priv, + unsigned char new_mac[ETH_ALEN + 2]) +{ + struct mlx4_en_dev *mdev = priv->mdev; + int err; + + if (!(mdev->dev->caps.flags2 & MLX4_DEV_CAP_FLAG2_USER_MAC_EN)) + return; + + err = mlx4_SET_PORT_user_mac(mdev->dev, priv->port, new_mac); + if (err) + en_err(priv, "Failed to pass user MAC(%pM) to Firmware for port %d, with error %d\n", + new_mac, priv->port, err); +} + static int mlx4_en_do_set_mac(struct mlx4_en_priv *priv, unsigned char new_mac[ETH_ALEN + 2]) { @@ -766,8 +782,12 @@ static int mlx4_en_set_mac(struct net_device *dev, void *addr) mutex_lock(&mdev->state_lock); memcpy(new_mac, saddr->sa_data, ETH_ALEN); err = mlx4_en_do_set_mac(priv, new_mac); - if (!err) - memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN); + if (err) + goto out; + + memcpy(dev->dev_addr, saddr->sa_data, ETH_ALEN); + mlx4_en_update_user_mac(priv, new_mac); +out: mutex_unlock(&mdev->state_lock); return err; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_resources.c b/drivers/net/ethernet/mellanox/mlx4/en_resources.c index 86d2d42d658d..5a47f9669621 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_resources.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_resources.c @@ -44,7 +44,7 @@ void mlx4_en_fill_qp_context(struct mlx4_en_priv *priv, int size, int stride, struct mlx4_en_dev *mdev = priv->mdev; struct net_device *dev = priv->dev; - memset(context, 0, sizeof *context); + memset(context, 0, sizeof(*context)); context->flags = cpu_to_be32(7 << 16 | rss << MLX4_RSS_QPC_FLAG_OFFSET); context->pd = cpu_to_be32(mdev->priv_pdn); context->mtu_msgmax = 0xff; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c index ec24c4057be6..b97a55c827eb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c @@ -1056,7 +1056,7 @@ static int mlx4_en_config_rss_qp(struct mlx4_en_priv *priv, int qpn, } qp->event = mlx4_en_sqp_event; - memset(context, 0, sizeof *context); + memset(context, 0, sizeof(*context)); mlx4_en_fill_qp_context(priv, ring->actual_size, ring->stride, 0, 0, qpn, ring->cqn, -1, context); context->db_rec_addr = cpu_to_be64(ring->wqres.db.dma); diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c index a81db2582555..8a32a8f7f9c0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c @@ -644,7 +644,7 @@ static void build_inline_wqe(struct mlx4_en_tx_desc *tx_desc, void *fragptr) { struct mlx4_wqe_inline_seg *inl = &tx_desc->inl; - int spc = MLX4_INLINE_ALIGN - CTRL_SIZE - sizeof *inl; + int spc = MLX4_INLINE_ALIGN - CTRL_SIZE - sizeof(*inl); unsigned int hlen = skb_headlen(skb); if (skb->len <= spc) { diff --git a/drivers/net/ethernet/mellanox/mlx4/eq.c b/drivers/net/ethernet/mellanox/mlx4/eq.c index 07406cf2eacd..6f57c052053e 100644 --- a/drivers/net/ethernet/mellanox/mlx4/eq.c +++ b/drivers/net/ethernet/mellanox/mlx4/eq.c @@ -259,7 +259,7 @@ int mlx4_gen_pkey_eqe(struct mlx4_dev *dev, int slave, u8 port) if (!s_slave->active) return 0; - memset(&eqe, 0, sizeof eqe); + memset(&eqe, 0, sizeof(eqe)); eqe.type = MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT; eqe.subtype = MLX4_DEV_PMC_SUBTYPE_PKEY_TABLE; @@ -276,7 +276,7 @@ int mlx4_gen_guid_change_eqe(struct mlx4_dev *dev, int slave, u8 port) /*don't send if we don't have the that slave */ if (dev->persist->num_vfs < slave) return 0; - memset(&eqe, 0, sizeof eqe); + memset(&eqe, 0, sizeof(eqe)); eqe.type = MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT; eqe.subtype = MLX4_DEV_PMC_SUBTYPE_GUID_INFO; @@ -295,7 +295,7 @@ int mlx4_gen_port_state_change_eqe(struct mlx4_dev *dev, int slave, u8 port, /*don't send if we don't have the that slave */ if (dev->persist->num_vfs < slave) return 0; - memset(&eqe, 0, sizeof eqe); + memset(&eqe, 0, sizeof(eqe)); eqe.type = MLX4_EVENT_TYPE_PORT_CHANGE; eqe.subtype = port_subtype_change; @@ -432,7 +432,7 @@ int mlx4_gen_slaves_port_mgt_ev(struct mlx4_dev *dev, u8 port, int attr) { struct mlx4_eqe eqe; - memset(&eqe, 0, sizeof eqe); + memset(&eqe, 0, sizeof(eqe)); eqe.type = MLX4_EVENT_TYPE_PORT_MNG_CHG_EVENT; eqe.subtype = MLX4_DEV_PMC_SUBTYPE_PORT_INFO; @@ -726,7 +726,7 @@ static int mlx4_eq_int(struct mlx4_dev *dev, struct mlx4_eq *eq) } memcpy(&priv->mfunc.master.comm_arm_bit_vector, eqe->event.comm_channel_arm.bit_vec, - sizeof eqe->event.comm_channel_arm.bit_vec); + sizeof(eqe->event.comm_channel_arm.bit_vec)); queue_work(priv->mfunc.master.comm_wq, &priv->mfunc.master.comm_work); break; @@ -984,15 +984,15 @@ static int mlx4_create_eq(struct mlx4_dev *dev, int nent, */ npages = PAGE_ALIGN(eq->nent * dev->caps.eqe_size) / PAGE_SIZE; - eq->page_list = kmalloc(npages * sizeof *eq->page_list, - GFP_KERNEL); + eq->page_list = kmalloc_array(npages, sizeof(*eq->page_list), + GFP_KERNEL); if (!eq->page_list) goto err_out; for (i = 0; i < npages; ++i) eq->page_list[i].buf = NULL; - dma_list = kmalloc(npages * sizeof *dma_list, GFP_KERNEL); + dma_list = kmalloc_array(npages, sizeof(*dma_list), GFP_KERNEL); if (!dma_list) goto err_out_free; @@ -1161,7 +1161,7 @@ int mlx4_alloc_eq_table(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); priv->eq_table.eq = kcalloc(dev->caps.num_eqs - dev->caps.reserved_eqs, - sizeof *priv->eq_table.eq, GFP_KERNEL); + sizeof(*priv->eq_table.eq), GFP_KERNEL); if (!priv->eq_table.eq) return -ENOMEM; @@ -1180,7 +1180,7 @@ int mlx4_init_eq_table(struct mlx4_dev *dev) int i; priv->eq_table.uar_map = kcalloc(mlx4_num_eq_uar(dev), - sizeof *priv->eq_table.uar_map, + sizeof(*priv->eq_table.uar_map), GFP_KERNEL); if (!priv->eq_table.uar_map) { err = -ENOMEM; diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 041c0ed65929..16c09949afd5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -36,6 +36,7 @@ #include <linux/mlx4/cmd.h> #include <linux/module.h> #include <linux/cache.h> +#include <linux/kernel.h> #include "fw.h" #include "icm.h" @@ -57,7 +58,7 @@ MODULE_PARM_DESC(enable_qos, "Enable Enhanced QoS support (default: off)"); do { \ void *__p = (char *) (source) + (offset); \ u64 val; \ - switch (sizeof (dest)) { \ + switch (sizeof(dest)) { \ case 1: (dest) = *(u8 *) __p; break; \ case 2: (dest) = be16_to_cpup(__p); break; \ case 4: (dest) = be32_to_cpup(__p); break; \ @@ -162,6 +163,7 @@ static void dump_dev_cap_flags2(struct mlx4_dev *dev, u64 flags) [35] = "Diag counters per port", [36] = "QinQ VST mode support", [37] = "sl to vl mapping table change event support", + [38] = "user MAC support", }; int i; @@ -679,22 +681,22 @@ int mlx4_QUERY_FUNC_CAP(struct mlx4_dev *dev, u8 gen_or_port, if (func_cap->flags1 & QUERY_FUNC_CAP_VF_ENABLE_QP0) { MLX4_GET(qkey, outbox, QUERY_FUNC_CAP_PRIV_VF_QKEY_OFFSET); - func_cap->qp0_qkey = qkey; + func_cap->spec_qps.qp0_qkey = qkey; } else { - func_cap->qp0_qkey = 0; + func_cap->spec_qps.qp0_qkey = 0; } MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP0_TUNNEL); - func_cap->qp0_tunnel_qpn = size & 0xFFFFFF; + func_cap->spec_qps.qp0_tunnel = size & 0xFFFFFF; MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP0_PROXY); - func_cap->qp0_proxy_qpn = size & 0xFFFFFF; + func_cap->spec_qps.qp0_proxy = size & 0xFFFFFF; MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP1_TUNNEL); - func_cap->qp1_tunnel_qpn = size & 0xFFFFFF; + func_cap->spec_qps.qp1_tunnel = size & 0xFFFFFF; MLX4_GET(size, outbox, QUERY_FUNC_CAP_QP1_PROXY); - func_cap->qp1_proxy_qpn = size & 0xFFFFFF; + func_cap->spec_qps.qp1_proxy = size & 0xFFFFFF; if (func_cap->flags1 & QUERY_FUNC_CAP_FLAGS1_NIC_INFO) MLX4_GET(func_cap->phys_port_id, outbox, @@ -778,6 +780,7 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) #define QUERY_DEV_CAP_MAX_DESC_SZ_SQ_OFFSET 0x52 #define QUERY_DEV_CAP_MAX_SG_RQ_OFFSET 0x55 #define QUERY_DEV_CAP_MAX_DESC_SZ_RQ_OFFSET 0x56 +#define QUERY_DEV_CAP_USER_MAC_EN_OFFSET 0x5C #define QUERY_DEV_CAP_SVLAN_BY_QP_OFFSET 0x5D #define QUERY_DEV_CAP_MAX_QP_MCG_OFFSET 0x61 #define QUERY_DEV_CAP_RSVD_MCG_OFFSET 0x62 @@ -949,6 +952,9 @@ int mlx4_QUERY_DEV_CAP(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) MLX4_GET(size, outbox, QUERY_DEV_CAP_MAX_DESC_SZ_SQ_OFFSET); dev_cap->max_sq_desc_sz = size; + MLX4_GET(field, outbox, QUERY_DEV_CAP_USER_MAC_EN_OFFSET); + if (field & (1 << 2)) + dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_USER_MAC_EN; MLX4_GET(field, outbox, QUERY_DEV_CAP_SVLAN_BY_QP_OFFSET); if (field & 0x1) dev_cap->flags2 |= MLX4_DEV_CAP_FLAG2_SVLAN_BY_QP; @@ -1534,7 +1540,7 @@ int mlx4_map_cmd(struct mlx4_dev *dev, u16 op, struct mlx4_icm *icm, u64 virt) for (i = 0; i < mlx4_icm_size(&iter) >> lg; ++i) { if (virt != -1) { pages[nent * 2] = cpu_to_be64(virt); - virt += 1 << lg; + virt += 1ULL << lg; } pages[nent * 2 + 1] = @@ -2450,14 +2456,14 @@ int mlx4_config_dev_retrieval(struct mlx4_dev *dev, csum_mask = (config_dev.rx_checksum_val >> CONFIG_DEV_RX_CSUM_MODE_PORT1_BIT_OFFSET) & CONFIG_DEV_RX_CSUM_MODE_MASK; - if (csum_mask >= sizeof(config_dev_csum_flags)/sizeof(config_dev_csum_flags[0])) + if (csum_mask >= ARRAY_SIZE(config_dev_csum_flags)) return -EINVAL; params->rx_csum_flags_port_1 = config_dev_csum_flags[csum_mask]; csum_mask = (config_dev.rx_checksum_val >> CONFIG_DEV_RX_CSUM_MODE_PORT2_BIT_OFFSET) & CONFIG_DEV_RX_CSUM_MODE_MASK; - if (csum_mask >= sizeof(config_dev_csum_flags)/sizeof(config_dev_csum_flags[0])) + if (csum_mask >= ARRAY_SIZE(config_dev_csum_flags)) return -EINVAL; params->rx_csum_flags_port_2 = config_dev_csum_flags[csum_mask]; diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h index b52ba01aa486..cd6399c76bfd 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw.h @@ -144,11 +144,7 @@ struct mlx4_func_cap { int max_eq; int reserved_eq; int mcg_quota; - u32 qp0_qkey; - u32 qp0_tunnel_qpn; - u32 qp0_proxy_qpn; - u32 qp1_tunnel_qpn; - u32 qp1_proxy_qpn; + struct mlx4_spec_qps spec_qps; u32 reserved_lkey; u8 physical_port; u8 flags0; diff --git a/drivers/net/ethernet/mellanox/mlx4/fw_qos.c b/drivers/net/ethernet/mellanox/mlx4/fw_qos.c index 8f2fde0487c4..3a09d7122d3b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw_qos.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw_qos.c @@ -65,7 +65,7 @@ struct mlx4_set_port_scheduler_context { /* Granular Qos (per VF) section */ struct mlx4_alloc_vpp_param { - __be32 availible_vpp; + __be32 available_vpp; __be32 vpp_p_up[MLX4_NUM_UP]; }; @@ -157,7 +157,7 @@ int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, EXPORT_SYMBOL(mlx4_SET_PORT_SCHEDULER); int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port, - u16 *availible_vpp, u8 *vpp_p_up) + u16 *available_vpp, u8 *vpp_p_up) { int i; int err; @@ -179,7 +179,7 @@ int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port, goto out; /* Total number of supported VPPs */ - *availible_vpp = (u16)be32_to_cpu(out_param->availible_vpp); + *available_vpp = (u16)be32_to_cpu(out_param->available_vpp); for (i = 0; i < MLX4_NUM_UP; i++) vpp_p_up[i] = (u8)be32_to_cpu(out_param->vpp_p_up[i]); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw_qos.h b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h index ac1f331878e6..582997577a04 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw_qos.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw_qos.h @@ -84,23 +84,23 @@ int mlx4_SET_PORT_PRIO2TC(struct mlx4_dev *dev, u8 port, u8 *prio2tc); int mlx4_SET_PORT_SCHEDULER(struct mlx4_dev *dev, u8 port, u8 *tc_tx_bw, u8 *pg, u16 *ratelimit); /** - * mlx4_ALLOCATE_VPP_get - Query port VPP availible resources and allocation. - * Before distribution of VPPs to priorities, only availible_vpp is returned. + * mlx4_ALLOCATE_VPP_get - Query port VPP available resources and allocation. + * Before distribution of VPPs to priorities, only available_vpp is returned. * After initialization it returns the distribution of VPPs among priorities. * * @dev: mlx4_dev. * @port: Physical port number. - * @availible_vpp: Pointer to variable where number of availible VPPs is stored + * @available_vpp: Pointer to variable where number of available VPPs is stored * @vpp_p_up: Distribution of VPPs to priorities is stored in this array * * Returns 0 on success or a negative mlx4_core errno code. **/ int mlx4_ALLOCATE_VPP_get(struct mlx4_dev *dev, u8 port, - u16 *availible_vpp, u8 *vpp_p_up); + u16 *available_vpp, u8 *vpp_p_up); /** * mlx4_ALLOCATE_VPP_set - Distribution of VPPs among differnt priorities. * The total number of VPPs assigned to all for a port must not exceed - * the value reported by availible_vpp in mlx4_ALLOCATE_VPP_get. + * the value reported by available_vpp in mlx4_ALLOCATE_VPP_get. * VPP allocation is allowed only after the port type has been set, * and while no QPs are open for this port. * diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c index 5a7816e7c7b4..a822f7a56bc5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/icm.c +++ b/drivers/net/ethernet/mellanox/mlx4/icm.c @@ -400,7 +400,7 @@ int mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table, obj_per_chunk = MLX4_TABLE_CHUNK_SIZE / obj_size; num_icm = (nobj + obj_per_chunk - 1) / obj_per_chunk; - table->icm = kcalloc(num_icm, sizeof *table->icm, GFP_KERNEL); + table->icm = kcalloc(num_icm, sizeof(*table->icm), GFP_KERNEL); if (!table->icm) return -ENOMEM; table->virt = virt; diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.h b/drivers/net/ethernet/mellanox/mlx4/icm.h index dee67fa39107..c9169a490557 100644 --- a/drivers/net/ethernet/mellanox/mlx4/icm.h +++ b/drivers/net/ethernet/mellanox/mlx4/icm.h @@ -39,8 +39,8 @@ #include <linux/mutex.h> #define MLX4_ICM_CHUNK_LEN \ - ((256 - sizeof (struct list_head) - 2 * sizeof (int)) / \ - (sizeof (struct scatterlist))) + ((256 - sizeof(struct list_head) - 2 * sizeof(int)) / \ + (sizeof(struct scatterlist))) enum { MLX4_ICM_PAGE_SHIFT = 12, diff --git a/drivers/net/ethernet/mellanox/mlx4/intf.c b/drivers/net/ethernet/mellanox/mlx4/intf.c index e00f627331cb..2edcce98ab2d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/intf.c +++ b/drivers/net/ethernet/mellanox/mlx4/intf.c @@ -53,7 +53,7 @@ static void mlx4_add_device(struct mlx4_interface *intf, struct mlx4_priv *priv) { struct mlx4_device_context *dev_ctx; - dev_ctx = kmalloc(sizeof *dev_ctx, GFP_KERNEL); + dev_ctx = kmalloc(sizeof(*dev_ctx), GFP_KERNEL); if (!dev_ctx) return; diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index a594bfd9e095..e61c99ef741d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -34,6 +34,7 @@ */ #include <linux/module.h> +#include <linux/kernel.h> #include <linux/init.h> #include <linux/errno.h> #include <linux/pci.h> @@ -121,7 +122,7 @@ static char mlx4_version[] = DRV_NAME ": Mellanox ConnectX core driver v" DRV_VERSION "\n"; -static struct mlx4_profile default_profile = { +static const struct mlx4_profile default_profile = { .num_qp = 1 << 18, .num_srq = 1 << 16, .rdmarc_per_qp = 1 << 4, @@ -131,7 +132,7 @@ static struct mlx4_profile default_profile = { .num_mtt = 1 << 20, /* It is really num mtt segements */ }; -static struct mlx4_profile low_mem_profile = { +static const struct mlx4_profile low_mem_profile = { .num_qp = 1 << 17, .num_srq = 1 << 6, .rdmarc_per_qp = 1 << 4, @@ -819,38 +820,93 @@ static void slave_adjust_steering_mode(struct mlx4_dev *dev, mlx4_steering_mode_str(dev->caps.steering_mode)); } +static void mlx4_slave_destroy_special_qp_cap(struct mlx4_dev *dev) +{ + kfree(dev->caps.spec_qps); + dev->caps.spec_qps = NULL; +} + +static int mlx4_slave_special_qp_cap(struct mlx4_dev *dev) +{ + struct mlx4_func_cap *func_cap = NULL; + struct mlx4_caps *caps = &dev->caps; + int i, err = 0; + + func_cap = kzalloc(sizeof(*func_cap), GFP_KERNEL); + caps->spec_qps = kcalloc(caps->num_ports, sizeof(*caps->spec_qps), GFP_KERNEL); + + if (!func_cap || !caps->spec_qps) { + mlx4_err(dev, "Failed to allocate memory for special qps cap\n"); + err = -ENOMEM; + goto err_mem; + } + + for (i = 1; i <= caps->num_ports; ++i) { + err = mlx4_QUERY_FUNC_CAP(dev, i, func_cap); + if (err) { + mlx4_err(dev, "QUERY_FUNC_CAP port command failed for port %d, aborting (%d)\n", + i, err); + goto err_mem; + } + caps->spec_qps[i - 1] = func_cap->spec_qps; + caps->port_mask[i] = caps->port_type[i]; + caps->phys_port_id[i] = func_cap->phys_port_id; + err = mlx4_get_slave_pkey_gid_tbl_len(dev, i, + &caps->gid_table_len[i], + &caps->pkey_table_len[i]); + if (err) { + mlx4_err(dev, "QUERY_PORT command failed for port %d, aborting (%d)\n", + i, err); + goto err_mem; + } + } + +err_mem: + if (err) + mlx4_slave_destroy_special_qp_cap(dev); + kfree(func_cap); + return err; +} + static int mlx4_slave_cap(struct mlx4_dev *dev) { int err; u32 page_size; - struct mlx4_dev_cap dev_cap; - struct mlx4_func_cap func_cap; - struct mlx4_init_hca_param hca_param; - u8 i; + struct mlx4_dev_cap *dev_cap = NULL; + struct mlx4_func_cap *func_cap = NULL; + struct mlx4_init_hca_param *hca_param = NULL; + + hca_param = kzalloc(sizeof(*hca_param), GFP_KERNEL); + func_cap = kzalloc(sizeof(*func_cap), GFP_KERNEL); + dev_cap = kzalloc(sizeof(*dev_cap), GFP_KERNEL); + if (!hca_param || !func_cap || !dev_cap) { + mlx4_err(dev, "Failed to allocate memory for slave_cap\n"); + err = -ENOMEM; + goto free_mem; + } - memset(&hca_param, 0, sizeof(hca_param)); - err = mlx4_QUERY_HCA(dev, &hca_param); + err = mlx4_QUERY_HCA(dev, hca_param); if (err) { mlx4_err(dev, "QUERY_HCA command failed, aborting\n"); - return err; + goto free_mem; } /* fail if the hca has an unknown global capability * at this time global_caps should be always zeroed */ - if (hca_param.global_caps) { + if (hca_param->global_caps) { mlx4_err(dev, "Unknown hca global capabilities\n"); - return -EINVAL; + err = -EINVAL; + goto free_mem; } - dev->caps.hca_core_clock = hca_param.hca_core_clock; + dev->caps.hca_core_clock = hca_param->hca_core_clock; - memset(&dev_cap, 0, sizeof(dev_cap)); - dev->caps.max_qp_dest_rdma = 1 << hca_param.log_rd_per_qp; - err = mlx4_dev_cap(dev, &dev_cap); + dev->caps.max_qp_dest_rdma = 1 << hca_param->log_rd_per_qp; + err = mlx4_dev_cap(dev, dev_cap); if (err) { mlx4_err(dev, "QUERY_DEV_CAP command failed, aborting\n"); - return err; + goto free_mem; } err = mlx4_QUERY_FW(dev); @@ -862,21 +918,23 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) if (page_size > PAGE_SIZE) { mlx4_err(dev, "HCA minimum page size of %d bigger than kernel PAGE_SIZE of %ld, aborting\n", page_size, PAGE_SIZE); - return -ENODEV; + err = -ENODEV; + goto free_mem; } /* Set uar_page_shift for VF */ - dev->uar_page_shift = hca_param.uar_page_sz + 12; + dev->uar_page_shift = hca_param->uar_page_sz + 12; /* Make sure the master uar page size is valid */ if (dev->uar_page_shift > PAGE_SHIFT) { mlx4_err(dev, "Invalid configuration: uar page size is larger than system page size\n"); - return -ENODEV; + err = -ENODEV; + goto free_mem; } /* Set reserved_uars based on the uar_page_shift */ - mlx4_set_num_reserved_uars(dev, &dev_cap); + mlx4_set_num_reserved_uars(dev, dev_cap); /* Although uar page size in FW differs from system page size, * upper software layers (mlx4_ib, mlx4_en and part of mlx4_core) @@ -884,34 +942,35 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) */ dev->caps.uar_page_size = PAGE_SIZE; - memset(&func_cap, 0, sizeof(func_cap)); - err = mlx4_QUERY_FUNC_CAP(dev, 0, &func_cap); + err = mlx4_QUERY_FUNC_CAP(dev, 0, func_cap); if (err) { mlx4_err(dev, "QUERY_FUNC_CAP general command failed, aborting (%d)\n", err); - return err; + goto free_mem; } - if ((func_cap.pf_context_behaviour | PF_CONTEXT_BEHAVIOUR_MASK) != + if ((func_cap->pf_context_behaviour | PF_CONTEXT_BEHAVIOUR_MASK) != PF_CONTEXT_BEHAVIOUR_MASK) { mlx4_err(dev, "Unknown pf context behaviour %x known flags %x\n", - func_cap.pf_context_behaviour, PF_CONTEXT_BEHAVIOUR_MASK); - return -EINVAL; - } - - dev->caps.num_ports = func_cap.num_ports; - dev->quotas.qp = func_cap.qp_quota; - dev->quotas.srq = func_cap.srq_quota; - dev->quotas.cq = func_cap.cq_quota; - dev->quotas.mpt = func_cap.mpt_quota; - dev->quotas.mtt = func_cap.mtt_quota; - dev->caps.num_qps = 1 << hca_param.log_num_qps; - dev->caps.num_srqs = 1 << hca_param.log_num_srqs; - dev->caps.num_cqs = 1 << hca_param.log_num_cqs; - dev->caps.num_mpts = 1 << hca_param.log_mpt_sz; - dev->caps.num_eqs = func_cap.max_eq; - dev->caps.reserved_eqs = func_cap.reserved_eq; - dev->caps.reserved_lkey = func_cap.reserved_lkey; + func_cap->pf_context_behaviour, + PF_CONTEXT_BEHAVIOUR_MASK); + err = -EINVAL; + goto free_mem; + } + + dev->caps.num_ports = func_cap->num_ports; + dev->quotas.qp = func_cap->qp_quota; + dev->quotas.srq = func_cap->srq_quota; + dev->quotas.cq = func_cap->cq_quota; + dev->quotas.mpt = func_cap->mpt_quota; + dev->quotas.mtt = func_cap->mtt_quota; + dev->caps.num_qps = 1 << hca_param->log_num_qps; + dev->caps.num_srqs = 1 << hca_param->log_num_srqs; + dev->caps.num_cqs = 1 << hca_param->log_num_cqs; + dev->caps.num_mpts = 1 << hca_param->log_mpt_sz; + dev->caps.num_eqs = func_cap->max_eq; + dev->caps.reserved_eqs = func_cap->reserved_eq; + dev->caps.reserved_lkey = func_cap->reserved_lkey; dev->caps.num_pds = MLX4_NUM_PDS; dev->caps.num_mgms = 0; dev->caps.num_amgms = 0; @@ -919,43 +978,16 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) if (dev->caps.num_ports > MLX4_MAX_PORTS) { mlx4_err(dev, "HCA has %d ports, but we only support %d, aborting\n", dev->caps.num_ports, MLX4_MAX_PORTS); - return -ENODEV; + err = -ENODEV; + goto free_mem; } mlx4_replace_zero_macs(dev); - dev->caps.qp0_qkey = kcalloc(dev->caps.num_ports, sizeof(u32), GFP_KERNEL); - dev->caps.qp0_tunnel = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); - dev->caps.qp0_proxy = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); - dev->caps.qp1_tunnel = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); - dev->caps.qp1_proxy = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); - - if (!dev->caps.qp0_tunnel || !dev->caps.qp0_proxy || - !dev->caps.qp1_tunnel || !dev->caps.qp1_proxy || - !dev->caps.qp0_qkey) { - err = -ENOMEM; - goto err_mem; - } - - for (i = 1; i <= dev->caps.num_ports; ++i) { - err = mlx4_QUERY_FUNC_CAP(dev, i, &func_cap); - if (err) { - mlx4_err(dev, "QUERY_FUNC_CAP port command failed for port %d, aborting (%d)\n", - i, err); - goto err_mem; - } - dev->caps.qp0_qkey[i - 1] = func_cap.qp0_qkey; - dev->caps.qp0_tunnel[i - 1] = func_cap.qp0_tunnel_qpn; - dev->caps.qp0_proxy[i - 1] = func_cap.qp0_proxy_qpn; - dev->caps.qp1_tunnel[i - 1] = func_cap.qp1_tunnel_qpn; - dev->caps.qp1_proxy[i - 1] = func_cap.qp1_proxy_qpn; - dev->caps.port_mask[i] = dev->caps.port_type[i]; - dev->caps.phys_port_id[i] = func_cap.phys_port_id; - err = mlx4_get_slave_pkey_gid_tbl_len(dev, i, - &dev->caps.gid_table_len[i], - &dev->caps.pkey_table_len[i]); - if (err) - goto err_mem; + err = mlx4_slave_special_qp_cap(dev); + if (err) { + mlx4_err(dev, "Set special QP caps failed. aborting\n"); + goto free_mem; } if (dev->caps.uar_page_size * (dev->caps.num_uars - @@ -970,7 +1002,7 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) goto err_mem; } - if (hca_param.dev_cap_enabled & MLX4_DEV_CAP_64B_EQE_ENABLED) { + if (hca_param->dev_cap_enabled & MLX4_DEV_CAP_64B_EQE_ENABLED) { dev->caps.eqe_size = 64; dev->caps.eqe_factor = 1; } else { @@ -978,20 +1010,20 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) dev->caps.eqe_factor = 0; } - if (hca_param.dev_cap_enabled & MLX4_DEV_CAP_64B_CQE_ENABLED) { + if (hca_param->dev_cap_enabled & MLX4_DEV_CAP_64B_CQE_ENABLED) { dev->caps.cqe_size = 64; dev->caps.userspace_caps |= MLX4_USER_DEV_CAP_LARGE_CQE; } else { dev->caps.cqe_size = 32; } - if (hca_param.dev_cap_enabled & MLX4_DEV_CAP_EQE_STRIDE_ENABLED) { - dev->caps.eqe_size = hca_param.eqe_size; + if (hca_param->dev_cap_enabled & MLX4_DEV_CAP_EQE_STRIDE_ENABLED) { + dev->caps.eqe_size = hca_param->eqe_size; dev->caps.eqe_factor = 0; } - if (hca_param.dev_cap_enabled & MLX4_DEV_CAP_CQE_STRIDE_ENABLED) { - dev->caps.cqe_size = hca_param.cqe_size; + if (hca_param->dev_cap_enabled & MLX4_DEV_CAP_CQE_STRIDE_ENABLED) { + dev->caps.cqe_size = hca_param->cqe_size; /* User still need to know when CQE > 32B */ dev->caps.userspace_caps |= MLX4_USER_DEV_CAP_LARGE_CQE; } @@ -999,31 +1031,27 @@ static int mlx4_slave_cap(struct mlx4_dev *dev) dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_TS; mlx4_warn(dev, "Timestamping is not supported in slave mode\n"); - slave_adjust_steering_mode(dev, &dev_cap, &hca_param); + dev->caps.flags2 &= ~MLX4_DEV_CAP_FLAG2_USER_MAC_EN; + mlx4_dbg(dev, "User MAC FW update is not supported in slave mode\n"); + + slave_adjust_steering_mode(dev, dev_cap, hca_param); mlx4_dbg(dev, "RSS support for IP fragments is %s\n", - hca_param.rss_ip_frags ? "on" : "off"); + hca_param->rss_ip_frags ? "on" : "off"); - if (func_cap.extra_flags & MLX4_QUERY_FUNC_FLAGS_BF_RES_QP && + if (func_cap->extra_flags & MLX4_QUERY_FUNC_FLAGS_BF_RES_QP && dev->caps.bf_reg_size) dev->caps.alloc_res_qp_mask |= MLX4_RESERVE_ETH_BF_QP; - if (func_cap.extra_flags & MLX4_QUERY_FUNC_FLAGS_A0_RES_QP) + if (func_cap->extra_flags & MLX4_QUERY_FUNC_FLAGS_A0_RES_QP) dev->caps.alloc_res_qp_mask |= MLX4_RESERVE_A0_QP; - return 0; - err_mem: - kfree(dev->caps.qp0_qkey); - kfree(dev->caps.qp0_tunnel); - kfree(dev->caps.qp0_proxy); - kfree(dev->caps.qp1_tunnel); - kfree(dev->caps.qp1_proxy); - dev->caps.qp0_qkey = NULL; - dev->caps.qp0_tunnel = NULL; - dev->caps.qp0_proxy = NULL; - dev->caps.qp1_tunnel = NULL; - dev->caps.qp1_proxy = NULL; - + if (err) + mlx4_slave_destroy_special_qp_cap(dev); +free_mem: + kfree(hca_param); + kfree(func_cap); + kfree(dev_cap); return err; } @@ -2399,7 +2427,7 @@ static int mlx4_init_hca(struct mlx4_dev *dev) dev->caps.rx_checksum_flags_port[2] = params.rx_csum_flags_port_2; } priv->eq_table.inta_pin = adapter.inta_pin; - memcpy(dev->board_id, adapter.board_id, sizeof dev->board_id); + memcpy(dev->board_id, adapter.board_id, sizeof(dev->board_id)); return 0; @@ -2407,13 +2435,8 @@ unmap_bf: unmap_internal_clock(dev); unmap_bf_area(dev); - if (mlx4_is_slave(dev)) { - kfree(dev->caps.qp0_qkey); - kfree(dev->caps.qp0_tunnel); - kfree(dev->caps.qp0_proxy); - kfree(dev->caps.qp1_tunnel); - kfree(dev->caps.qp1_proxy); - } + if (mlx4_is_slave(dev)) + mlx4_slave_destroy_special_qp_cap(dev); err_close: if (mlx4_is_slave(dev)) @@ -2870,7 +2893,7 @@ static void mlx4_enable_msi_x(struct mlx4_dev *dev) dev->caps.num_eqs - dev->caps.reserved_eqs, MAX_MSIX); - entries = kcalloc(nreq, sizeof *entries, GFP_KERNEL); + entries = kcalloc(nreq, sizeof(*entries), GFP_KERNEL); if (!entries) goto no_msi; @@ -3597,13 +3620,8 @@ err_master_mfunc: mlx4_multi_func_cleanup(dev); } - if (mlx4_is_slave(dev)) { - kfree(dev->caps.qp0_qkey); - kfree(dev->caps.qp0_tunnel); - kfree(dev->caps.qp0_proxy); - kfree(dev->caps.qp1_tunnel); - kfree(dev->caps.qp1_proxy); - } + if (mlx4_is_slave(dev)) + mlx4_slave_destroy_special_qp_cap(dev); err_close: mlx4_close_hca(dev); @@ -3659,7 +3677,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data, * per port, we must limit the number of VFs to 63 (since their are * 128 MACs) */ - for (i = 0; i < sizeof(nvfs)/sizeof(nvfs[0]) && i < num_vfs_argc; + for (i = 0; i < ARRAY_SIZE(nvfs) && i < num_vfs_argc; total_vfs += nvfs[param_map[num_vfs_argc - 1][i]], i++) { nvfs[param_map[num_vfs_argc - 1][i]] = num_vfs[i]; if (nvfs[i] < 0) { @@ -3668,7 +3686,7 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data, goto err_disable_pdev; } } - for (i = 0; i < sizeof(prb_vf)/sizeof(prb_vf[0]) && i < probe_vfs_argc; + for (i = 0; i < ARRAY_SIZE(prb_vf) && i < probe_vfs_argc; i++) { prb_vf[param_map[probe_vfs_argc - 1][i]] = probe_vf[i]; if (prb_vf[i] < 0 || prb_vf[i] > nvfs[i]) { @@ -3747,11 +3765,11 @@ static int __mlx4_init_one(struct pci_dev *pdev, int pci_dev_data, if (total_vfs) { unsigned vfs_offset = 0; - for (i = 0; i < sizeof(nvfs)/sizeof(nvfs[0]) && + for (i = 0; i < ARRAY_SIZE(nvfs) && vfs_offset + nvfs[i] < extended_func_num(pdev); vfs_offset += nvfs[i], i++) ; - if (i == sizeof(nvfs)/sizeof(nvfs[0])) { + if (i == ARRAY_SIZE(nvfs)) { err = -ENODEV; goto err_release_regions; } @@ -3783,7 +3801,6 @@ err_release_regions: err_disable_pdev: mlx4_pci_disable_device(&priv->dev); - pci_set_drvdata(pdev, NULL); return err; } @@ -3944,11 +3961,7 @@ static void mlx4_unload_one(struct pci_dev *pdev) if (!mlx4_is_slave(dev)) mlx4_free_ownership(dev); - kfree(dev->caps.qp0_qkey); - kfree(dev->caps.qp0_tunnel); - kfree(dev->caps.qp0_proxy); - kfree(dev->caps.qp1_tunnel); - kfree(dev->caps.qp1_proxy); + mlx4_slave_destroy_special_qp_cap(dev); kfree(dev->dev_vfs); mlx4_clean_dev(dev); @@ -3998,7 +4011,6 @@ static void mlx4_remove_one(struct pci_dev *pdev) devlink_unregister(devlink); kfree(dev->persist); devlink_free(devlink); - pci_set_drvdata(pdev, NULL); } static int restore_current_port_types(struct mlx4_dev *dev, diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index 0710b3677464..4c5306dbcf11 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -162,7 +162,7 @@ static int new_steering_entry(struct mlx4_dev *dev, u8 port, return -EINVAL; s_steer = &mlx4_priv(dev)->steer[port - 1]; - new_entry = kzalloc(sizeof *new_entry, GFP_KERNEL); + new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL); if (!new_entry) return -ENOMEM; @@ -175,7 +175,7 @@ static int new_steering_entry(struct mlx4_dev *dev, u8 port, */ pqp = get_promisc_qp(dev, port, steer, qpn); if (pqp) { - dqp = kmalloc(sizeof *dqp, GFP_KERNEL); + dqp = kmalloc(sizeof(*dqp), GFP_KERNEL); if (!dqp) { err = -ENOMEM; goto out_alloc; @@ -274,7 +274,7 @@ static int existing_steering_entry(struct mlx4_dev *dev, u8 port, } /* add the qp as a duplicate on this index */ - dqp = kmalloc(sizeof *dqp, GFP_KERNEL); + dqp = kmalloc(sizeof(*dqp), GFP_KERNEL); if (!dqp) return -ENOMEM; dqp->qpn = qpn; @@ -443,7 +443,7 @@ static int add_promisc_qp(struct mlx4_dev *dev, u8 port, goto out_mutex; } - pqp = kmalloc(sizeof *pqp, GFP_KERNEL); + pqp = kmalloc(sizeof(*pqp), GFP_KERNEL); if (!pqp) { err = -ENOMEM; goto out_mutex; @@ -514,7 +514,7 @@ static int add_promisc_qp(struct mlx4_dev *dev, u8 port, /* add the new qpn to list of promisc qps */ list_add_tail(&pqp->list, &s_steer->promisc_qps[steer]); /* now need to add all the promisc qps to default entry */ - memset(mgm, 0, sizeof *mgm); + memset(mgm, 0, sizeof(*mgm)); members_count = 0; list_for_each_entry(dqp, &s_steer->promisc_qps[steer], list) { if (members_count == dev->caps.num_qp_per_mgm) { @@ -1144,7 +1144,7 @@ int mlx4_qp_attach_common(struct mlx4_dev *dev, struct mlx4_qp *qp, u8 gid[16], index += dev->caps.num_mgms; new_entry = 1; - memset(mgm, 0, sizeof *mgm); + memset(mgm, 0, sizeof(*mgm)); memcpy(mgm->gid, gid, 16); } diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 852d00a5b016..c68da1986e51 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -807,6 +807,8 @@ struct mlx4_set_port_general_context { u8 phv_en; u8 reserved6[5]; __be16 user_mtu; + u16 reserved7; + u8 user_mac[6]; }; struct mlx4_set_port_rqp_calc_context { diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h index d350b2158104..fdb3ad0cbe54 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h @@ -685,7 +685,7 @@ int mlx4_en_activate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq, int cq_idx); void mlx4_en_deactivate_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq); int mlx4_en_set_cq_moder(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq); -int mlx4_en_arm_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq); +void mlx4_en_arm_cq(struct mlx4_en_priv *priv, struct mlx4_en_cq *cq); void mlx4_en_tx_irq(struct mlx4_cq *mcq); u16 mlx4_en_select_queue(struct net_device *dev, struct sk_buff *skb, diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c index 24282cd017d3..c7c0764991c9 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mr.c +++ b/drivers/net/ethernet/mellanox/mlx4/mr.c @@ -106,9 +106,9 @@ static int mlx4_buddy_init(struct mlx4_buddy *buddy, int max_order) buddy->max_order = max_order; spin_lock_init(&buddy->lock); - buddy->bits = kcalloc(buddy->max_order + 1, sizeof (long *), + buddy->bits = kcalloc(buddy->max_order + 1, sizeof(long *), GFP_KERNEL); - buddy->num_free = kcalloc((buddy->max_order + 1), sizeof *buddy->num_free, + buddy->num_free = kcalloc(buddy->max_order + 1, sizeof(*buddy->num_free), GFP_KERNEL); if (!buddy->bits || !buddy->num_free) goto err_out; @@ -703,13 +703,13 @@ static int mlx4_write_mtt_chunk(struct mlx4_dev *dev, struct mlx4_mtt *mtt, return -ENOMEM; dma_sync_single_for_cpu(&dev->persist->pdev->dev, dma_handle, - npages * sizeof (u64), DMA_TO_DEVICE); + npages * sizeof(u64), DMA_TO_DEVICE); for (i = 0; i < npages; ++i) mtts[i] = cpu_to_be64(page_list[i] | MLX4_MTT_FLAG_PRESENT); dma_sync_single_for_device(&dev->persist->pdev->dev, dma_handle, - npages * sizeof (u64), DMA_TO_DEVICE); + npages * sizeof(u64), DMA_TO_DEVICE); return 0; } @@ -1052,7 +1052,7 @@ int mlx4_fmr_alloc(struct mlx4_dev *dev, u32 pd, u32 access, int max_pages, return -EINVAL; /* All MTTs must fit in the same page */ - if (max_pages * sizeof *fmr->mtts > PAGE_SIZE) + if (max_pages * sizeof(*fmr->mtts) > PAGE_SIZE) return -EINVAL; fmr->page_shift = page_shift; diff --git a/drivers/net/ethernet/mellanox/mlx4/port.c b/drivers/net/ethernet/mellanox/mlx4/port.c index 4e36e287d605..3ef3406ff4cb 100644 --- a/drivers/net/ethernet/mellanox/mlx4/port.c +++ b/drivers/net/ethernet/mellanox/mlx4/port.c @@ -52,6 +52,7 @@ #define MLX4_FLAG2_V_IGNORE_FCS_MASK BIT(1) #define MLX4_FLAG2_V_USER_MTU_MASK BIT(5) +#define MLX4_FLAG2_V_USER_MAC_MASK BIT(6) #define MLX4_FLAG_V_MTU_MASK BIT(0) #define MLX4_FLAG_V_PPRX_MASK BIT(1) #define MLX4_FLAG_V_PPTX_MASK BIT(2) @@ -1700,6 +1701,30 @@ int mlx4_SET_PORT_user_mtu(struct mlx4_dev *dev, u8 port, u16 user_mtu) } EXPORT_SYMBOL(mlx4_SET_PORT_user_mtu); +int mlx4_SET_PORT_user_mac(struct mlx4_dev *dev, u8 port, u8 *user_mac) +{ + struct mlx4_cmd_mailbox *mailbox; + struct mlx4_set_port_general_context *context; + u32 in_mod; + int err; + + mailbox = mlx4_alloc_cmd_mailbox(dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + context = mailbox->buf; + context->flags2 |= MLX4_FLAG2_V_USER_MAC_MASK; + memcpy(context->user_mac, user_mac, sizeof(context->user_mac)); + + in_mod = MLX4_SET_PORT_GENERAL << 8 | port; + err = mlx4_cmd(dev, mailbox->dma, in_mod, MLX4_SET_PORT_ETH_OPCODE, + MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B, + MLX4_CMD_NATIVE); + + mlx4_free_cmd_mailbox(dev, mailbox); + return err; +} +EXPORT_SYMBOL(mlx4_SET_PORT_user_mac); + int mlx4_SET_PORT_fcs_check(struct mlx4_dev *dev, u8 port, u8 ignore_fcs_value) { struct mlx4_cmd_mailbox *mailbox; diff --git a/drivers/net/ethernet/mellanox/mlx4/qp.c b/drivers/net/ethernet/mellanox/mlx4/qp.c index 5e5b4475b85e..728a2fb1f5c0 100644 --- a/drivers/net/ethernet/mellanox/mlx4/qp.c +++ b/drivers/net/ethernet/mellanox/mlx4/qp.c @@ -174,7 +174,7 @@ static int __mlx4_qp_modify(struct mlx4_dev *dev, struct mlx4_mtt *mtt, cpu_to_be16(mlx4_qp_roce_entropy(dev, qp->qpn)); *(__be32 *) mailbox->buf = cpu_to_be32(optpar); - memcpy(mailbox->buf + 8, context, sizeof *context); + memcpy(mailbox->buf + 8, context, sizeof(*context)); ((struct mlx4_qp_context *) (mailbox->buf + 8))->local_qpn = cpu_to_be32(qp->qpn); @@ -845,24 +845,21 @@ int mlx4_init_qp_table(struct mlx4_dev *dev) /* In mfunc, calculate proxy and tunnel qp offsets for the PF here, * since the PF does not call mlx4_slave_caps */ - dev->caps.qp0_tunnel = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); - dev->caps.qp0_proxy = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); - dev->caps.qp1_tunnel = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); - dev->caps.qp1_proxy = kcalloc(dev->caps.num_ports, sizeof (u32), GFP_KERNEL); - - if (!dev->caps.qp0_tunnel || !dev->caps.qp0_proxy || - !dev->caps.qp1_tunnel || !dev->caps.qp1_proxy) { + dev->caps.spec_qps = kcalloc(dev->caps.num_ports, + sizeof(*dev->caps.spec_qps), + GFP_KERNEL); + if (!dev->caps.spec_qps) { err = -ENOMEM; goto err_mem; } for (k = 0; k < dev->caps.num_ports; k++) { - dev->caps.qp0_proxy[k] = dev->phys_caps.base_proxy_sqpn + + dev->caps.spec_qps[k].qp0_proxy = dev->phys_caps.base_proxy_sqpn + 8 * mlx4_master_func_num(dev) + k; - dev->caps.qp0_tunnel[k] = dev->caps.qp0_proxy[k] + 8 * MLX4_MFUNC_MAX; - dev->caps.qp1_proxy[k] = dev->phys_caps.base_proxy_sqpn + + dev->caps.spec_qps[k].qp0_tunnel = dev->caps.spec_qps[k].qp0_proxy + 8 * MLX4_MFUNC_MAX; + dev->caps.spec_qps[k].qp1_proxy = dev->phys_caps.base_proxy_sqpn + 8 * mlx4_master_func_num(dev) + MLX4_MAX_PORTS + k; - dev->caps.qp1_tunnel[k] = dev->caps.qp1_proxy[k] + 8 * MLX4_MFUNC_MAX; + dev->caps.spec_qps[k].qp1_tunnel = dev->caps.spec_qps[k].qp1_proxy + 8 * MLX4_MFUNC_MAX; } } @@ -874,12 +871,8 @@ int mlx4_init_qp_table(struct mlx4_dev *dev) return err; err_mem: - kfree(dev->caps.qp0_tunnel); - kfree(dev->caps.qp0_proxy); - kfree(dev->caps.qp1_tunnel); - kfree(dev->caps.qp1_proxy); - dev->caps.qp0_tunnel = dev->caps.qp0_proxy = - dev->caps.qp1_tunnel = dev->caps.qp1_proxy = NULL; + kfree(dev->caps.spec_qps); + dev->caps.spec_qps = NULL; mlx4_cleanup_qp_zones(dev); return err; } @@ -908,7 +901,7 @@ int mlx4_qp_query(struct mlx4_dev *dev, struct mlx4_qp *qp, MLX4_CMD_QUERY_QP, MLX4_CMD_TIME_CLASS_A, MLX4_CMD_WRAPPED); if (!err) - memcpy(context, mailbox->buf + 8, sizeof *context); + memcpy(context, mailbox->buf + 8, sizeof(*context)); mlx4_free_cmd_mailbox(dev, mailbox); return err; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index 215e21c3dc8a..fabb53379727 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -1040,7 +1040,7 @@ static struct res_common *alloc_qp_tr(int id) { struct res_qp *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1058,7 +1058,7 @@ static struct res_common *alloc_mtt_tr(int id, int order) { struct res_mtt *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1074,7 +1074,7 @@ static struct res_common *alloc_mpt_tr(int id, int key) { struct res_mpt *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1089,7 +1089,7 @@ static struct res_common *alloc_eq_tr(int id) { struct res_eq *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1103,7 +1103,7 @@ static struct res_common *alloc_cq_tr(int id) { struct res_cq *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1118,7 +1118,7 @@ static struct res_common *alloc_srq_tr(int id) { struct res_srq *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1133,7 +1133,7 @@ static struct res_common *alloc_counter_tr(int id, int port) { struct res_counter *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1148,7 +1148,7 @@ static struct res_common *alloc_xrcdn_tr(int id) { struct res_xrcdn *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1162,7 +1162,7 @@ static struct res_common *alloc_fs_rule_tr(u64 id, int qpn) { struct res_fs_rule *ret; - ret = kzalloc(sizeof *ret, GFP_KERNEL); + ret = kzalloc(sizeof(*ret), GFP_KERNEL); if (!ret) return NULL; @@ -1274,7 +1274,7 @@ static int add_res_range(struct mlx4_dev *dev, int slave, u64 base, int count, struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker; struct rb_root *root = &tracker->res_tree[type]; - res_arr = kzalloc(count * sizeof *res_arr, GFP_KERNEL); + res_arr = kcalloc(count, sizeof(*res_arr), GFP_KERNEL); if (!res_arr) return -ENOMEM; @@ -2027,7 +2027,7 @@ static int mac_add_to_slave(struct mlx4_dev *dev, int slave, u64 mac, int port, if (mlx4_grant_resource(dev, slave, RES_MAC, 1, port)) return -EINVAL; - res = kzalloc(sizeof *res, GFP_KERNEL); + res = kzalloc(sizeof(*res), GFP_KERNEL); if (!res) { mlx4_release_resource(dev, slave, RES_MAC, 1, port); return -ENOMEM; @@ -4020,7 +4020,7 @@ static int add_mcg_res(struct mlx4_dev *dev, int slave, struct res_qp *rqp, struct res_gid *res; int err; - res = kzalloc(sizeof *res, GFP_KERNEL); + res = kzalloc(sizeof(*res), GFP_KERNEL); if (!res) return -ENOMEM; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig index 5aee05992f27..fdaef00465d7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Kconfig +++ b/drivers/net/ethernet/mellanox/mlx5/core/Kconfig @@ -34,6 +34,27 @@ config MLX5_CORE_EN ---help--- Ethernet support in Mellanox Technologies ConnectX-4 NIC. +config MLX5_MPFS + bool "Mellanox Technologies MLX5 MPFS support" + depends on MLX5_CORE_EN + default y + ---help--- + Mellanox Technologies Ethernet Multi-Physical Function Switch (MPFS) + support in ConnectX NIC. MPFs is required for when multi-PF configuration + is enabled to allow passing user configured unicast MAC addresses to the + requesting PF. + +config MLX5_ESWITCH + bool "Mellanox Technologies MLX5 SRIOV E-Switch support" + depends on MLX5_CORE_EN + default y + ---help--- + Mellanox Technologies Ethernet SRIOV E-Switch support in ConnectX NIC. + E-Switch provides internal SRIOV packet steering and switching for the + enabled VFs and PF in two available modes: + Legacy SRIOV mode (L2 mac vlan steering based). + Switchdev mode (eswitch offloads). + config MLX5_CORE_EN_DCB bool "Data Center Bridging (DCB) Support" default y diff --git a/drivers/net/ethernet/mellanox/mlx5/core/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/Makefile index 9d17e4e76d3a..87a3099808f3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/Makefile +++ b/drivers/net/ethernet/mellanox/mlx5/core/Makefile @@ -4,17 +4,21 @@ subdir-ccflags-y += -I$(src) mlx5_core-y := main.o cmd.o debugfs.o fw.o eq.o uar.o pagealloc.o \ health.o mcg.o cq.o srq.o alloc.o qp.o port.o mr.o pd.o \ mad.o transobj.o vport.o sriov.o fs_cmd.o fs_core.o \ - fs_counters.o rl.o lag.o dev.o wq.o lib/gid.o + fs_counters.o rl.o lag.o dev.o wq.o lib/gid.o \ + diag/fs_tracepoint.o mlx5_core-$(CONFIG_MLX5_ACCEL) += accel/ipsec.o mlx5_core-$(CONFIG_MLX5_FPGA) += fpga/cmd.o fpga/core.o fpga/conn.o fpga/sdk.o \ fpga/ipsec.o -mlx5_core-$(CONFIG_MLX5_CORE_EN) += eswitch.o eswitch_offloads.o \ - en_main.o en_common.o en_fs.o en_ethtool.o en_tx.o \ - en_rx.o en_rx_am.o en_txrx.o en_clock.o vxlan.o \ - en_tc.o en_arfs.o en_rep.o en_fs_ethtool.o en_selftest.o +mlx5_core-$(CONFIG_MLX5_CORE_EN) += en_main.o en_common.o en_fs.o en_ethtool.o \ + en_tx.o en_rx.o en_rx_am.o en_txrx.o en_clock.o vxlan.o \ + en_arfs.o en_fs_ethtool.o en_selftest.o + +mlx5_core-$(CONFIG_MLX5_MPFS) += lib/mpfs.o + +mlx5_core-$(CONFIG_MLX5_ESWITCH) += eswitch.o eswitch_offloads.o en_rep.o en_tc.o mlx5_core-$(CONFIG_MLX5_CORE_EN_DCB) += en_dcbnl.o @@ -22,3 +26,5 @@ mlx5_core-$(CONFIG_MLX5_CORE_IPOIB) += ipoib/ipoib.o ipoib/ethtool.o mlx5_core-$(CONFIG_MLX5_EN_IPSEC) += en_accel/ipsec.o en_accel/ipsec_rxtx.o \ en_accel/ipsec_stats.o + +CFLAGS_tracepoint.o := -I$(src) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c index 3c95f7f53802..47239bf7bf43 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/alloc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/alloc.c @@ -258,6 +258,7 @@ EXPORT_SYMBOL_GPL(mlx5_db_alloc); void mlx5_db_free(struct mlx5_core_dev *dev, struct mlx5_db *db) { u32 db_per_page = PAGE_SIZE / cache_line_size(); + mutex_lock(&dev->priv.pgdir_mutex); __set_bit(db->index, db->u.pgdir->bitmap); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c index 1acbb721f38d..1fffdebbc9e8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c @@ -802,7 +802,6 @@ static void cmd_work_handler(struct work_struct *work) bool poll_cmd = ent->polling; int alloc_ret; - sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem; down(sem); if (!ent->page_queue) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dev.c b/drivers/net/ethernet/mellanox/mlx5/core/dev.c index a62f4b6a21a5..ff60cf7342ca 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/dev.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/dev.c @@ -45,11 +45,70 @@ struct mlx5_device_context { unsigned long state; }; +struct mlx5_delayed_event { + struct list_head list; + struct mlx5_core_dev *dev; + enum mlx5_dev_event event; + unsigned long param; +}; + enum { MLX5_INTERFACE_ADDED, MLX5_INTERFACE_ATTACHED, }; +static void add_delayed_event(struct mlx5_priv *priv, + struct mlx5_core_dev *dev, + enum mlx5_dev_event event, + unsigned long param) +{ + struct mlx5_delayed_event *delayed_event; + + delayed_event = kzalloc(sizeof(*delayed_event), GFP_ATOMIC); + if (!delayed_event) { + mlx5_core_err(dev, "event %d is missed\n", event); + return; + } + + mlx5_core_dbg(dev, "Accumulating event %d\n", event); + delayed_event->dev = dev; + delayed_event->event = event; + delayed_event->param = param; + list_add_tail(&delayed_event->list, &priv->waiting_events_list); +} + +static void fire_delayed_event_locked(struct mlx5_device_context *dev_ctx, + struct mlx5_core_dev *dev, + struct mlx5_priv *priv) +{ + struct mlx5_delayed_event *de; + struct mlx5_delayed_event *n; + + /* stop delaying events */ + priv->is_accum_events = false; + + /* fire all accumulated events before new event comes */ + list_for_each_entry_safe(de, n, &priv->waiting_events_list, list) { + dev_ctx->intf->event(dev, dev_ctx->context, de->event, de->param); + list_del(&de->list); + kfree(de); + } +} + +static void cleanup_delayed_evets(struct mlx5_priv *priv) +{ + struct mlx5_delayed_event *de; + struct mlx5_delayed_event *n; + + spin_lock_irq(&priv->ctx_lock); + priv->is_accum_events = false; + list_for_each_entry_safe(de, n, &priv->waiting_events_list, list) { + list_del(&de->list); + kfree(de); + } + spin_unlock_irq(&priv->ctx_lock); +} + void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) { struct mlx5_device_context *dev_ctx; @@ -63,6 +122,12 @@ void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) return; dev_ctx->intf = intf; + /* accumulating events that can come after mlx5_ib calls to + * ib_register_device, till adding that interface to the events list. + */ + + priv->is_accum_events = true; + dev_ctx->context = intf->add(dev); set_bit(MLX5_INTERFACE_ADDED, &dev_ctx->state); if (intf->attach) @@ -71,6 +136,9 @@ void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) if (dev_ctx->context) { spin_lock_irq(&priv->ctx_lock); list_add_tail(&dev_ctx->list, &priv->ctx_list); + + fire_delayed_event_locked(dev_ctx, dev, priv); + #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING if (dev_ctx->intf->pfault) { if (priv->pfault) { @@ -84,6 +152,8 @@ void mlx5_add_device(struct mlx5_interface *intf, struct mlx5_priv *priv) spin_unlock_irq(&priv->ctx_lock); } else { kfree(dev_ctx); + /* delete all accumulated events */ + cleanup_delayed_evets(priv); } } @@ -341,6 +411,9 @@ void mlx5_core_event(struct mlx5_core_dev *dev, enum mlx5_dev_event event, spin_lock_irqsave(&priv->ctx_lock, flags); + if (priv->is_accum_events) + add_delayed_event(priv, dev, event, param); + list_for_each_entry(dev_ctx, &priv->ctx_list, list) if (dev_ctx->intf->event) dev_ctx->intf->event(dev, dev_ctx->context, event, param); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/Makefile b/drivers/net/ethernet/mellanox/mlx5/core/diag/Makefile new file mode 100644 index 000000000000..d8e17110f25d --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/Makefile @@ -0,0 +1 @@ +subdir-ccflags-y += -I$(src)/.. diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c new file mode 100644 index 000000000000..0be4575b58a2 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2017, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define CREATE_TRACE_POINTS + +#include "fs_tracepoint.h" +#include <linux/stringify.h> + +#define DECLARE_MASK_VAL(type, name) struct {type m; type v; } name +#define MASK_VAL(type, spec, name, mask, val, fld) \ + DECLARE_MASK_VAL(type, name) = \ + {.m = MLX5_GET(spec, mask, fld),\ + .v = MLX5_GET(spec, val, fld)} +#define MASK_VAL_BE(type, spec, name, mask, val, fld) \ + DECLARE_MASK_VAL(type, name) = \ + {.m = MLX5_GET_BE(type, spec, mask, fld),\ + .v = MLX5_GET_BE(type, spec, val, fld)} +#define GET_MASKED_VAL(name) (name.m & name.v) + +#define GET_MASK_VAL(name, type, mask, val, fld) \ + (name.m = MLX5_GET(type, mask, fld), \ + name.v = MLX5_GET(type, val, fld), \ + name.m & name.v) +#define PRINT_MASKED_VAL(name, p, format) { \ + if (name.m) \ + trace_seq_printf(p, __stringify(name) "=" format " ", name.v); \ + } +#define PRINT_MASKED_VALP(name, cast, p, format) { \ + if (name.m) \ + trace_seq_printf(p, __stringify(name) "=" format " ", \ + (cast)&name.v);\ + } + +static void print_lyr_2_4_hdrs(struct trace_seq *p, + const u32 *mask, const u32 *value) +{ +#define MASK_VAL_L2(type, name, fld) \ + MASK_VAL(type, fte_match_set_lyr_2_4, name, mask, value, fld) + DECLARE_MASK_VAL(u64, smac) = { + .m = MLX5_GET(fte_match_set_lyr_2_4, mask, smac_47_16) << 16 | + MLX5_GET(fte_match_set_lyr_2_4, mask, smac_15_0), + .v = MLX5_GET(fte_match_set_lyr_2_4, value, smac_47_16) << 16 | + MLX5_GET(fte_match_set_lyr_2_4, value, smac_15_0)}; + DECLARE_MASK_VAL(u64, dmac) = { + .m = MLX5_GET(fte_match_set_lyr_2_4, mask, dmac_47_16) << 16 | + MLX5_GET(fte_match_set_lyr_2_4, mask, dmac_15_0), + .v = MLX5_GET(fte_match_set_lyr_2_4, value, dmac_47_16) << 16 | + MLX5_GET(fte_match_set_lyr_2_4, value, dmac_15_0)}; + MASK_VAL_L2(u16, ethertype, ethertype); + + PRINT_MASKED_VALP(smac, u8 *, p, "%pM"); + PRINT_MASKED_VALP(dmac, u8 *, p, "%pM"); + PRINT_MASKED_VAL(ethertype, p, "%04x"); + + if (ethertype.m == 0xffff) { + if (ethertype.v == ETH_P_IP) { +#define MASK_VAL_L2_BE(type, name, fld) \ + MASK_VAL_BE(type, fte_match_set_lyr_2_4, name, mask, value, fld) + MASK_VAL_L2_BE(u32, src_ipv4, + src_ipv4_src_ipv6.ipv4_layout.ipv4); + MASK_VAL_L2_BE(u32, dst_ipv4, + dst_ipv4_dst_ipv6.ipv4_layout.ipv4); + + PRINT_MASKED_VALP(src_ipv4, typeof(&src_ipv4.v), p, + "%pI4"); + PRINT_MASKED_VALP(dst_ipv4, typeof(&dst_ipv4.v), p, + "%pI4"); + } else if (ethertype.v == ETH_P_IPV6) { + static const struct in6_addr full_ones = { + .in6_u.u6_addr32 = {htonl(0xffffffff), + htonl(0xffffffff), + htonl(0xffffffff), + htonl(0xffffffff)}, + }; + DECLARE_MASK_VAL(struct in6_addr, src_ipv6); + DECLARE_MASK_VAL(struct in6_addr, dst_ipv6); + + memcpy(src_ipv6.m.in6_u.u6_addr8, + MLX5_ADDR_OF(fte_match_set_lyr_2_4, mask, + src_ipv4_src_ipv6.ipv6_layout.ipv6), + sizeof(src_ipv6.m)); + memcpy(dst_ipv6.m.in6_u.u6_addr8, + MLX5_ADDR_OF(fte_match_set_lyr_2_4, mask, + dst_ipv4_dst_ipv6.ipv6_layout.ipv6), + sizeof(dst_ipv6.m)); + memcpy(src_ipv6.v.in6_u.u6_addr8, + MLX5_ADDR_OF(fte_match_set_lyr_2_4, value, + src_ipv4_src_ipv6.ipv6_layout.ipv6), + sizeof(src_ipv6.v)); + memcpy(dst_ipv6.v.in6_u.u6_addr8, + MLX5_ADDR_OF(fte_match_set_lyr_2_4, value, + dst_ipv4_dst_ipv6.ipv6_layout.ipv6), + sizeof(dst_ipv6.v)); + + if (!memcmp(&src_ipv6.m, &full_ones, sizeof(full_ones))) + trace_seq_printf(p, "src_ipv6=%pI6 ", + &src_ipv6.v); + if (!memcmp(&dst_ipv6.m, &full_ones, sizeof(full_ones))) + trace_seq_printf(p, "dst_ipv6=%pI6 ", + &dst_ipv6.v); + } + } + +#define PRINT_MASKED_VAL_L2(type, name, fld, p, format) {\ + MASK_VAL_L2(type, name, fld); \ + PRINT_MASKED_VAL(name, p, format); \ +} + + PRINT_MASKED_VAL_L2(u8, ip_protocol, ip_protocol, p, "%02x"); + PRINT_MASKED_VAL_L2(u16, tcp_flags, tcp_flags, p, "%x"); + PRINT_MASKED_VAL_L2(u16, tcp_sport, tcp_sport, p, "%u"); + PRINT_MASKED_VAL_L2(u16, tcp_dport, tcp_dport, p, "%u"); + PRINT_MASKED_VAL_L2(u16, udp_sport, udp_sport, p, "%u"); + PRINT_MASKED_VAL_L2(u16, udp_dport, udp_dport, p, "%u"); + PRINT_MASKED_VAL_L2(u16, first_vid, first_vid, p, "%04x"); + PRINT_MASKED_VAL_L2(u8, first_prio, first_prio, p, "%x"); + PRINT_MASKED_VAL_L2(u8, first_cfi, first_cfi, p, "%d"); + PRINT_MASKED_VAL_L2(u8, ip_dscp, ip_dscp, p, "%02x"); + PRINT_MASKED_VAL_L2(u8, ip_ecn, ip_ecn, p, "%x"); + PRINT_MASKED_VAL_L2(u8, cvlan_tag, cvlan_tag, p, "%d"); + PRINT_MASKED_VAL_L2(u8, svlan_tag, svlan_tag, p, "%d"); + PRINT_MASKED_VAL_L2(u8, frag, frag, p, "%d"); +} + +static void print_misc_parameters_hdrs(struct trace_seq *p, + const u32 *mask, const u32 *value) +{ +#define MASK_VAL_MISC(type, name, fld) \ + MASK_VAL(type, fte_match_set_misc, name, mask, value, fld) +#define PRINT_MASKED_VAL_MISC(type, name, fld, p, format) {\ + MASK_VAL_MISC(type, name, fld); \ + PRINT_MASKED_VAL(name, p, format); \ +} + DECLARE_MASK_VAL(u64, gre_key) = { + .m = MLX5_GET(fte_match_set_misc, mask, gre_key_h) << 8 | + MLX5_GET(fte_match_set_misc, mask, gre_key_l), + .v = MLX5_GET(fte_match_set_misc, value, gre_key_h) << 8 | + MLX5_GET(fte_match_set_misc, value, gre_key_l)}; + + PRINT_MASKED_VAL(gre_key, p, "%llu"); + PRINT_MASKED_VAL_MISC(u32, source_sqn, source_sqn, p, "%u"); + PRINT_MASKED_VAL_MISC(u16, source_port, source_port, p, "%u"); + PRINT_MASKED_VAL_MISC(u8, outer_second_prio, outer_second_prio, + p, "%u"); + PRINT_MASKED_VAL_MISC(u8, outer_second_cfi, outer_second_cfi, p, "%u"); + PRINT_MASKED_VAL_MISC(u16, outer_second_vid, outer_second_vid, p, "%u"); + PRINT_MASKED_VAL_MISC(u8, inner_second_prio, inner_second_prio, + p, "%u"); + PRINT_MASKED_VAL_MISC(u8, inner_second_cfi, inner_second_cfi, p, "%u"); + PRINT_MASKED_VAL_MISC(u16, inner_second_vid, inner_second_vid, p, "%u"); + + PRINT_MASKED_VAL_MISC(u8, outer_second_cvlan_tag, + outer_second_cvlan_tag, p, "%u"); + PRINT_MASKED_VAL_MISC(u8, inner_second_cvlan_tag, + inner_second_cvlan_tag, p, "%u"); + PRINT_MASKED_VAL_MISC(u8, outer_second_svlan_tag, + outer_second_svlan_tag, p, "%u"); + PRINT_MASKED_VAL_MISC(u8, inner_second_svlan_tag, + inner_second_svlan_tag, p, "%u"); + + PRINT_MASKED_VAL_MISC(u8, gre_protocol, gre_protocol, p, "%u"); + + PRINT_MASKED_VAL_MISC(u32, vxlan_vni, vxlan_vni, p, "%u"); + PRINT_MASKED_VAL_MISC(u32, outer_ipv6_flow_label, outer_ipv6_flow_label, + p, "%x"); + PRINT_MASKED_VAL_MISC(u32, inner_ipv6_flow_label, inner_ipv6_flow_label, + p, "%x"); +} + +const char *parse_fs_hdrs(struct trace_seq *p, + u8 match_criteria_enable, + const u32 *mask_outer, + const u32 *mask_misc, + const u32 *mask_inner, + const u32 *value_outer, + const u32 *value_misc, + const u32 *value_inner) +{ + const char *ret = trace_seq_buffer_ptr(p); + + if (match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS) { + trace_seq_printf(p, "[outer] "); + print_lyr_2_4_hdrs(p, mask_outer, value_outer); + } + + if (match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS) { + trace_seq_printf(p, "[misc] "); + print_misc_parameters_hdrs(p, mask_misc, value_misc); + } + if (match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS) { + trace_seq_printf(p, "[inner] "); + print_lyr_2_4_hdrs(p, mask_inner, value_inner); + } + trace_seq_putc(p, 0); + return ret; +} + +const char *parse_fs_dst(struct trace_seq *p, + const struct mlx5_flow_destination *dst, + u32 counter_id) +{ + const char *ret = trace_seq_buffer_ptr(p); + + switch (dst->type) { + case MLX5_FLOW_DESTINATION_TYPE_VPORT: + trace_seq_printf(p, "vport=%u\n", dst->vport_num); + break; + case MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE: + trace_seq_printf(p, "ft=%p\n", dst->ft); + break; + case MLX5_FLOW_DESTINATION_TYPE_TIR: + trace_seq_printf(p, "tir=%u\n", dst->tir_num); + break; + case MLX5_FLOW_DESTINATION_TYPE_COUNTER: + trace_seq_printf(p, "counter_id=%u\n", counter_id); + break; + } + + trace_seq_putc(p, 0); + return ret; +} + +EXPORT_TRACEPOINT_SYMBOL(mlx5_fs_add_fg); +EXPORT_TRACEPOINT_SYMBOL(mlx5_fs_del_fg); +EXPORT_TRACEPOINT_SYMBOL(mlx5_fs_set_fte); +EXPORT_TRACEPOINT_SYMBOL(mlx5_fs_del_fte); +EXPORT_TRACEPOINT_SYMBOL(mlx5_fs_add_rule); +EXPORT_TRACEPOINT_SYMBOL(mlx5_fs_del_rule); + diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.h b/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.h new file mode 100644 index 000000000000..1e3a6c3e4132 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fs_tracepoint.h @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2017, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#if !defined(_MLX5_FS_TP_) || defined(TRACE_HEADER_MULTI_READ) +#define _MLX5_FS_TP_ + +#include <linux/tracepoint.h> +#include <linux/trace_seq.h> +#include "../fs_core.h" + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mlx5 + +#define __parse_fs_hdrs(match_criteria_enable, mouter, mmisc, minner, vouter, \ + vinner, vmisc) \ + parse_fs_hdrs(p, match_criteria_enable, mouter, mmisc, minner, vouter,\ + vinner, vmisc) + +const char *parse_fs_hdrs(struct trace_seq *p, + u8 match_criteria_enable, + const u32 *mask_outer, + const u32 *mask_misc, + const u32 *mask_inner, + const u32 *value_outer, + const u32 *value_misc, + const u32 *value_inner); + +#define __parse_fs_dst(dst, counter_id) \ + parse_fs_dst(p, (const struct mlx5_flow_destination *)dst, counter_id) + +const char *parse_fs_dst(struct trace_seq *p, + const struct mlx5_flow_destination *dst, + u32 counter_id); + +TRACE_EVENT(mlx5_fs_add_fg, + TP_PROTO(const struct mlx5_flow_group *fg), + TP_ARGS(fg), + TP_STRUCT__entry( + __field(const struct mlx5_flow_group *, fg) + __field(const struct mlx5_flow_table *, ft) + __field(u32, start_index) + __field(u32, end_index) + __field(u32, id) + __field(u8, mask_enable) + __array(u32, mask_outer, MLX5_ST_SZ_DW(fte_match_set_lyr_2_4)) + __array(u32, mask_inner, MLX5_ST_SZ_DW(fte_match_set_lyr_2_4)) + __array(u32, mask_misc, MLX5_ST_SZ_DW(fte_match_set_misc)) + ), + TP_fast_assign( + __entry->fg = fg; + fs_get_obj(__entry->ft, fg->node.parent); + __entry->start_index = fg->start_index; + __entry->end_index = fg->start_index + fg->max_ftes; + __entry->id = fg->id; + __entry->mask_enable = fg->mask.match_criteria_enable; + memcpy(__entry->mask_outer, + MLX5_ADDR_OF(fte_match_param, + &fg->mask.match_criteria, + outer_headers), + sizeof(__entry->mask_outer)); + memcpy(__entry->mask_inner, + MLX5_ADDR_OF(fte_match_param, + &fg->mask.match_criteria, + inner_headers), + sizeof(__entry->mask_inner)); + memcpy(__entry->mask_misc, + MLX5_ADDR_OF(fte_match_param, + &fg->mask.match_criteria, + misc_parameters), + sizeof(__entry->mask_misc)); + + ), + TP_printk("fg=%p ft=%p id=%u start=%u end=%u bit_mask=%02x %s\n", + __entry->fg, __entry->ft, __entry->id, + __entry->start_index, __entry->end_index, + __entry->mask_enable, + __parse_fs_hdrs(__entry->mask_enable, + __entry->mask_outer, + __entry->mask_misc, + __entry->mask_inner, + __entry->mask_outer, + __entry->mask_misc, + __entry->mask_inner)) + ); + +TRACE_EVENT(mlx5_fs_del_fg, + TP_PROTO(const struct mlx5_flow_group *fg), + TP_ARGS(fg), + TP_STRUCT__entry( + __field(const struct mlx5_flow_group *, fg) + __field(u32, id) + ), + TP_fast_assign( + __entry->fg = fg; + __entry->id = fg->id; + + ), + TP_printk("fg=%p id=%u\n", + __entry->fg, __entry->id) + ); + +#define ACTION_FLAGS \ + {MLX5_FLOW_CONTEXT_ACTION_ALLOW, "ALLOW"},\ + {MLX5_FLOW_CONTEXT_ACTION_DROP, "DROP"},\ + {MLX5_FLOW_CONTEXT_ACTION_FWD_DEST, "FWD"},\ + {MLX5_FLOW_CONTEXT_ACTION_COUNT, "CNT"},\ + {MLX5_FLOW_CONTEXT_ACTION_ENCAP, "ENCAP"},\ + {MLX5_FLOW_CONTEXT_ACTION_DECAP, "DECAP"},\ + {MLX5_FLOW_CONTEXT_ACTION_MOD_HDR, "MOD_HDR"},\ + {MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO, "NEXT_PRIO"} + +TRACE_EVENT(mlx5_fs_set_fte, + TP_PROTO(const struct fs_fte *fte, bool new_fte), + TP_ARGS(fte, new_fte), + TP_STRUCT__entry( + __field(const struct fs_fte *, fte) + __field(const struct mlx5_flow_group *, fg) + __field(u32, group_index) + __field(u32, index) + __field(u32, action) + __field(u32, flow_tag) + __field(u8, mask_enable) + __field(bool, new_fte) + __array(u32, mask_outer, MLX5_ST_SZ_DW(fte_match_set_lyr_2_4)) + __array(u32, mask_inner, MLX5_ST_SZ_DW(fte_match_set_lyr_2_4)) + __array(u32, mask_misc, MLX5_ST_SZ_DW(fte_match_set_misc)) + __array(u32, value_outer, MLX5_ST_SZ_DW(fte_match_set_lyr_2_4)) + __array(u32, value_inner, MLX5_ST_SZ_DW(fte_match_set_lyr_2_4)) + __array(u32, value_misc, MLX5_ST_SZ_DW(fte_match_set_misc)) + ), + TP_fast_assign( + __entry->fte = fte; + __entry->new_fte = new_fte; + fs_get_obj(__entry->fg, fte->node.parent); + __entry->group_index = __entry->fg->id; + __entry->index = fte->index; + __entry->action = fte->action; + __entry->mask_enable = __entry->fg->mask.match_criteria_enable; + __entry->flow_tag = fte->flow_tag; + memcpy(__entry->mask_outer, + MLX5_ADDR_OF(fte_match_param, + &__entry->fg->mask.match_criteria, + outer_headers), + sizeof(__entry->mask_outer)); + memcpy(__entry->mask_inner, + MLX5_ADDR_OF(fte_match_param, + &__entry->fg->mask.match_criteria, + inner_headers), + sizeof(__entry->mask_inner)); + memcpy(__entry->mask_misc, + MLX5_ADDR_OF(fte_match_param, + &__entry->fg->mask.match_criteria, + misc_parameters), + sizeof(__entry->mask_misc)); + memcpy(__entry->value_outer, + MLX5_ADDR_OF(fte_match_param, + &fte->val, + outer_headers), + sizeof(__entry->value_outer)); + memcpy(__entry->value_inner, + MLX5_ADDR_OF(fte_match_param, + &fte->val, + inner_headers), + sizeof(__entry->value_inner)); + memcpy(__entry->value_misc, + MLX5_ADDR_OF(fte_match_param, + &fte->val, + misc_parameters), + sizeof(__entry->value_misc)); + + ), + TP_printk("op=%s fte=%p fg=%p index=%u group_index=%u action=<%s> flow_tag=%x %s\n", + __entry->new_fte ? "add" : "set", + __entry->fte, __entry->fg, __entry->index, + __entry->group_index, __print_flags(__entry->action, "|", + ACTION_FLAGS), + __entry->flow_tag, + __parse_fs_hdrs(__entry->mask_enable, + __entry->mask_outer, + __entry->mask_misc, + __entry->mask_inner, + __entry->value_outer, + __entry->value_misc, + __entry->value_inner)) + ); + +TRACE_EVENT(mlx5_fs_del_fte, + TP_PROTO(const struct fs_fte *fte), + TP_ARGS(fte), + TP_STRUCT__entry( + __field(const struct fs_fte *, fte) + __field(u32, index) + ), + TP_fast_assign( + __entry->fte = fte; + __entry->index = fte->index; + + ), + TP_printk("fte=%p index=%u\n", + __entry->fte, __entry->index) + ); + +TRACE_EVENT(mlx5_fs_add_rule, + TP_PROTO(const struct mlx5_flow_rule *rule), + TP_ARGS(rule), + TP_STRUCT__entry( + __field(const struct mlx5_flow_rule *, rule) + __field(const struct fs_fte *, fte) + __field(u32, sw_action) + __field(u32, index) + __field(u32, counter_id) + __array(u8, destination, sizeof(struct mlx5_flow_destination)) + ), + TP_fast_assign( + __entry->rule = rule; + fs_get_obj(__entry->fte, rule->node.parent); + __entry->index = __entry->fte->dests_size - 1; + __entry->sw_action = rule->sw_action; + memcpy(__entry->destination, + &rule->dest_attr, + sizeof(__entry->destination)); + if (rule->dest_attr.type & MLX5_FLOW_DESTINATION_TYPE_COUNTER && + rule->dest_attr.counter) + __entry->counter_id = + rule->dest_attr.counter->id; + ), + TP_printk("rule=%p fte=%p index=%u sw_action=<%s> [dst] %s\n", + __entry->rule, __entry->fte, __entry->index, + __print_flags(__entry->sw_action, "|", ACTION_FLAGS), + __parse_fs_dst(__entry->destination, __entry->counter_id)) + ); + +TRACE_EVENT(mlx5_fs_del_rule, + TP_PROTO(const struct mlx5_flow_rule *rule), + TP_ARGS(rule), + TP_STRUCT__entry( + __field(const struct mlx5_flow_rule *, rule) + __field(const struct fs_fte *, fte) + ), + TP_fast_assign( + __entry->rule = rule; + fs_get_obj(__entry->fte, rule->node.parent); + ), + TP_printk("rule=%p fte=%p\n", + __entry->rule, __entry->fte) + ); +#endif + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ./diag +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE fs_tracepoint +#include <trace/define_trace.h> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 040d1af310b4..cc13d3dbd366 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -291,10 +291,11 @@ struct mlx5e_tstamp { enum { MLX5E_RQ_STATE_ENABLED, - MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, MLX5E_RQ_STATE_AM, }; +#define MLX5E_TEST_BIT(state, nr) (state & BIT(nr)) + struct mlx5e_cq { /* data path - accessed per cqe */ struct mlx5_cqwq wq; @@ -342,7 +343,6 @@ enum { struct mlx5e_sq_wqe_info { u8 opcode; - u8 num_wqebbs; }; struct mlx5e_txqsq { @@ -418,13 +418,8 @@ struct mlx5e_xdpsq { struct mlx5e_icosq { /* data path */ - /* dirtied @completion */ - u16 cc; - /* dirtied @xmit */ u16 pc ____cacheline_aligned_in_smp; - u32 dma_fifo_pc; - u16 prev_cc; struct mlx5e_cq cq; @@ -438,7 +433,6 @@ struct mlx5e_icosq { void __iomem *uar_map; u32 sqn; u16 edge; - struct device *pdev; __be32 mkey_be; unsigned long state; @@ -507,7 +501,7 @@ struct mlx5e_rx_am { /* Adaptive Moderation */ */ #define MLX5E_CACHE_UNIT (MLX5_MPWRQ_PAGES_PER_WQE > NAPI_POLL_WEIGHT ? \ MLX5_MPWRQ_PAGES_PER_WQE : NAPI_POLL_WEIGHT) -#define MLX5E_CACHE_SIZE (2 * roundup_pow_of_two(MLX5E_CACHE_UNIT)) +#define MLX5E_CACHE_SIZE (4 * roundup_pow_of_two(MLX5E_CACHE_UNIT)) struct mlx5e_page_cache { u32 head; u32 tail; @@ -516,7 +510,7 @@ struct mlx5e_page_cache { struct mlx5e_rq; typedef void (*mlx5e_fp_handle_rx_cqe)(struct mlx5e_rq*, struct mlx5_cqe64*); -typedef int (*mlx5e_fp_alloc_wqe)(struct mlx5e_rq*, struct mlx5e_rx_wqe*, u16); +typedef bool (*mlx5e_fp_post_rx_wqes)(struct mlx5e_rq *rq); typedef void (*mlx5e_fp_dealloc_wqe)(struct mlx5e_rq*, u16); struct mlx5e_rq { @@ -527,21 +521,26 @@ struct mlx5e_rq { struct { struct mlx5e_wqe_frag_info *frag_info; u32 frag_sz; /* max possible skb frag_sz */ - bool page_reuse; - bool xdp_xmit; + union { + bool page_reuse; + bool xdp_xmit; + }; } wqe; struct { struct mlx5e_mpw_info *info; void *mtt_no_align; + u16 num_strides; + u8 log_stride_sz; + bool umr_in_progress; } mpwqe; }; struct { + u16 headroom; u8 page_order; - u32 wqe_sz; /* wqe data buffer size */ u8 map_dir; /* dma map direction */ } buff; - __be32 mkey_be; + struct mlx5e_channel *channel; struct device *pdev; struct net_device *netdev; struct mlx5e_tstamp *tstamp; @@ -550,12 +549,11 @@ struct mlx5e_rq { struct mlx5e_page_cache page_cache; mlx5e_fp_handle_rx_cqe handle_rx_cqe; - mlx5e_fp_alloc_wqe alloc_wqe; + mlx5e_fp_post_rx_wqes post_wqes; mlx5e_fp_dealloc_wqe dealloc_wqe; unsigned long state; int ix; - u16 rx_headroom; struct mlx5e_rx_am am; /* Adaptive Moderation */ @@ -565,19 +563,13 @@ struct mlx5e_rq { /* control */ struct mlx5_wq_ctrl wq_ctrl; + __be32 mkey_be; u8 wq_type; - u32 mpwqe_stride_sz; - u32 mpwqe_num_strides; u32 rqn; - struct mlx5e_channel *channel; struct mlx5_core_dev *mdev; struct mlx5_core_mkey umr_mkey; } ____cacheline_aligned_in_smp; -enum channel_flags { - MLX5E_CHANNEL_NAPI_SCHED = 1, -}; - struct mlx5e_channel { /* data path */ struct mlx5e_rq rq; @@ -589,7 +581,9 @@ struct mlx5e_channel { struct net_device *netdev; __be32 mkey_be; u8 num_tc; - unsigned long flags; + + /* data path - accessed per napi poll */ + struct irq_desc *irq_desc; /* control */ struct mlx5e_priv *priv; @@ -620,6 +614,12 @@ enum mlx5e_traffic_types { MLX5E_NUM_INDIR_TIRS = MLX5E_TT_ANY, }; +enum mlx5e_tunnel_types { + MLX5E_TT_IPV4_GRE, + MLX5E_TT_IPV6_GRE, + MLX5E_NUM_TUNNEL_TT, +}; + enum { MLX5E_STATE_ASYNC_EVENTS_ENABLED, MLX5E_STATE_OPENED, @@ -679,6 +679,7 @@ struct mlx5e_l2_table { struct mlx5e_ttc_table { struct mlx5e_flow_table ft; struct mlx5_flow_handle *rules[MLX5E_NUM_TT]; + struct mlx5_flow_handle *tunnel_rules[MLX5E_NUM_TUNNEL_TT]; }; #define ARFS_HASH_SHIFT BITS_PER_BYTE @@ -711,6 +712,7 @@ enum { MLX5E_VLAN_FT_LEVEL = 0, MLX5E_L2_FT_LEVEL, MLX5E_TTC_FT_LEVEL, + MLX5E_INNER_TTC_FT_LEVEL, MLX5E_ARFS_FT_LEVEL }; @@ -736,6 +738,7 @@ struct mlx5e_flow_steering { struct mlx5e_vlan_table vlan; struct mlx5e_l2_table l2; struct mlx5e_ttc_table ttc; + struct mlx5e_ttc_table inner_ttc; struct mlx5e_arfs_tables arfs; }; @@ -769,6 +772,7 @@ struct mlx5e_priv { u32 tisn[MLX5E_MAX_NUM_TC]; struct mlx5e_rqt indir_rqt; struct mlx5e_tir indir_tir[MLX5E_NUM_INDIR_TIRS]; + struct mlx5e_tir inner_indir_tir[MLX5E_NUM_INDIR_TIRS]; struct mlx5e_tir direct_tir[MLX5E_MAX_NUM_CHANNELS]; u32 tx_rates[MLX5E_MAX_NUM_SQS]; int hard_mtu; @@ -839,11 +843,9 @@ void mlx5e_page_release(struct mlx5e_rq *rq, struct mlx5e_dma_info *dma_info, void mlx5e_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe); void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe); bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq); -int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix); -int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix); +bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq); void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix); void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix); -void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq); void mlx5e_free_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi); void mlx5e_rx_am(struct mlx5e_rq *rq); @@ -903,7 +905,7 @@ int mlx5e_redirect_rqt(struct mlx5e_priv *priv, u32 rqtn, int sz, struct mlx5e_redirect_rqt_param rrp); void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_params *params, enum mlx5e_traffic_types tt, - void *tirc); + void *tirc, bool inner); int mlx5e_open_locked(struct net_device *netdev); int mlx5e_close_locked(struct net_device *netdev); @@ -922,8 +924,7 @@ void mlx5e_switch_priv_channels(struct mlx5e_priv *priv, void mlx5e_activate_priv_channels(struct mlx5e_priv *priv); void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv); -void mlx5e_build_default_indir_rqt(struct mlx5_core_dev *mdev, - u32 *indirection_rqt, int len, +void mlx5e_build_default_indir_rqt(u32 *indirection_rqt, int len, int num_channels); int mlx5e_get_max_linkspeed(struct mlx5_core_dev *mdev, u32 *speed); @@ -932,6 +933,12 @@ void mlx5e_set_rx_cq_mode_params(struct mlx5e_params *params, void mlx5e_set_rq_type_params(struct mlx5_core_dev *mdev, struct mlx5e_params *params, u8 rq_type); +static inline bool mlx5e_tunnel_inner_ft_supported(struct mlx5_core_dev *mdev) +{ + return (MLX5_CAP_ETH(mdev, tunnel_stateless_gre) && + MLX5_CAP_FLOWTABLE_NIC_RX(mdev, ft_field_support.inner_ip_version)); +} + static inline struct mlx5e_tx_wqe *mlx5e_post_nop(struct mlx5_wq_cyc *wq, u32 sqn, u16 *pc) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index f5594014715b..d12e9fc0d76b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -176,7 +176,6 @@ static bool mlx5e_query_global_pause_combined(struct mlx5e_priv *priv) int mlx5e_ethtool_get_sset_count(struct mlx5e_priv *priv, int sset) { - switch (sset) { case ETH_SS_STATS: return NUM_SW_COUNTERS + @@ -207,7 +206,7 @@ static int mlx5e_get_sset_count(struct net_device *dev, int sset) return mlx5e_ethtool_get_sset_count(priv, sset); } -static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data) +static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, u8 *data) { int i, j, tc, prio, idx = 0; unsigned long pfc_combined; @@ -242,10 +241,22 @@ static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data) strcpy(data + (idx++) * ETH_GSTRING_LEN, pport_phy_statistical_stats_desc[i].format); + for (i = 0; i < NUM_PPORT_ETH_EXT_COUNTERS(priv); i++) + strcpy(data + (idx++) * ETH_GSTRING_LEN, + pport_eth_ext_stats_desc[i].format); + for (i = 0; i < NUM_PCIE_PERF_COUNTERS(priv); i++) strcpy(data + (idx++) * ETH_GSTRING_LEN, pcie_perf_stats_desc[i].format); + for (i = 0; i < NUM_PCIE_PERF_COUNTERS64(priv); i++) + strcpy(data + (idx++) * ETH_GSTRING_LEN, + pcie_perf_stats_desc64[i].format); + + for (i = 0; i < NUM_PCIE_PERF_STALL_COUNTERS(priv); i++) + strcpy(data + (idx++) * ETH_GSTRING_LEN, + pcie_perf_stall_stats_desc[i].format); + for (prio = 0; prio < NUM_PPORT_PRIO; prio++) { for (i = 0; i < NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS; i++) sprintf(data + (idx++) * ETH_GSTRING_LEN, @@ -297,8 +308,7 @@ static void mlx5e_fill_stats_strings(struct mlx5e_priv *priv, uint8_t *data) priv->channel_tc2txq[i][tc]); } -void mlx5e_ethtool_get_strings(struct mlx5e_priv *priv, - uint32_t stringset, uint8_t *data) +void mlx5e_ethtool_get_strings(struct mlx5e_priv *priv, u32 stringset, u8 *data) { int i; @@ -320,8 +330,7 @@ void mlx5e_ethtool_get_strings(struct mlx5e_priv *priv, } } -static void mlx5e_get_strings(struct net_device *dev, - uint32_t stringset, uint8_t *data) +static void mlx5e_get_strings(struct net_device *dev, u32 stringset, u8 *data) { struct mlx5e_priv *priv = netdev_priv(dev); @@ -373,10 +382,22 @@ void mlx5e_ethtool_get_ethtool_stats(struct mlx5e_priv *priv, data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pport.phy_statistical_counters, pport_phy_statistical_stats_desc, i); + for (i = 0; i < NUM_PPORT_ETH_EXT_COUNTERS(priv); i++) + data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pport.eth_ext_counters, + pport_eth_ext_stats_desc, i); + for (i = 0; i < NUM_PCIE_PERF_COUNTERS(priv); i++) data[idx++] = MLX5E_READ_CTR32_BE(&priv->stats.pcie.pcie_perf_counters, pcie_perf_stats_desc, i); + for (i = 0; i < NUM_PCIE_PERF_COUNTERS64(priv); i++) + data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pcie.pcie_perf_counters, + pcie_perf_stats_desc64, i); + + for (i = 0; i < NUM_PCIE_PERF_STALL_COUNTERS(priv); i++) + data[idx++] = MLX5E_READ_CTR32_BE(&priv->stats.pcie.pcie_perf_counters, + pcie_perf_stall_stats_desc, i); + for (prio = 0; prio < NUM_PPORT_PRIO; prio++) { for (i = 0; i < NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS; i++) data[idx++] = MLX5E_READ_CTR64_BE(&priv->stats.pport.per_prio_counters[prio], @@ -642,8 +663,7 @@ int mlx5e_ethtool_set_channels(struct mlx5e_priv *priv, new_channels.params = priv->channels.params; new_channels.params.num_channels = count; if (!netif_is_rxfh_configured(priv->netdev)) - mlx5e_build_default_indir_rqt(priv->mdev, - new_channels.params.indirection_rqt, + mlx5e_build_default_indir_rqt(new_channels.params.indirection_rqt, MLX5E_INDIR_RQT_SIZE, count); if (!test_bit(MLX5E_STATE_OPENED, &priv->state)) { @@ -966,24 +986,27 @@ static u8 get_connector_port(u32 eth_proto, u8 connector_type) if (connector_type && connector_type < MLX5E_CONNECTOR_TYPE_NUMBER) return ptys2connector_type[connector_type]; - if (eth_proto & (MLX5E_PROT_MASK(MLX5E_10GBASE_SR) - | MLX5E_PROT_MASK(MLX5E_40GBASE_SR4) - | MLX5E_PROT_MASK(MLX5E_100GBASE_SR4) - | MLX5E_PROT_MASK(MLX5E_1000BASE_CX_SGMII))) { - return PORT_FIBRE; + if (eth_proto & + (MLX5E_PROT_MASK(MLX5E_10GBASE_SR) | + MLX5E_PROT_MASK(MLX5E_40GBASE_SR4) | + MLX5E_PROT_MASK(MLX5E_100GBASE_SR4) | + MLX5E_PROT_MASK(MLX5E_1000BASE_CX_SGMII))) { + return PORT_FIBRE; } - if (eth_proto & (MLX5E_PROT_MASK(MLX5E_40GBASE_CR4) - | MLX5E_PROT_MASK(MLX5E_10GBASE_CR) - | MLX5E_PROT_MASK(MLX5E_100GBASE_CR4))) { - return PORT_DA; + if (eth_proto & + (MLX5E_PROT_MASK(MLX5E_40GBASE_CR4) | + MLX5E_PROT_MASK(MLX5E_10GBASE_CR) | + MLX5E_PROT_MASK(MLX5E_100GBASE_CR4))) { + return PORT_DA; } - if (eth_proto & (MLX5E_PROT_MASK(MLX5E_10GBASE_KX4) - | MLX5E_PROT_MASK(MLX5E_10GBASE_KR) - | MLX5E_PROT_MASK(MLX5E_40GBASE_KR4) - | MLX5E_PROT_MASK(MLX5E_100GBASE_KR4))) { - return PORT_NONE; + if (eth_proto & + (MLX5E_PROT_MASK(MLX5E_10GBASE_KX4) | + MLX5E_PROT_MASK(MLX5E_10GBASE_KR) | + MLX5E_PROT_MASK(MLX5E_40GBASE_KR4) | + MLX5E_PROT_MASK(MLX5E_100GBASE_KR4))) { + return PORT_NONE; } return PORT_OTHER; @@ -1190,9 +1213,18 @@ static void mlx5e_modify_tirs_hash(struct mlx5e_priv *priv, void *in, int inlen) for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) { memset(tirc, 0, ctxlen); - mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc); + mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, false); mlx5_core_modify_tir(mdev, priv->indir_tir[tt].tirn, in, inlen); } + + if (!mlx5e_tunnel_inner_ft_supported(priv->mdev)) + return; + + for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) { + memset(tirc, 0, ctxlen); + mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, true); + mlx5_core_modify_tir(mdev, priv->inner_indir_tir[tt].tirn, in, inlen); + } } static int mlx5e_set_rxfh(struct net_device *dev, const u32 *indir, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c index dfccb5305e9c..f11fd07ac4dd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_fs.c @@ -36,6 +36,7 @@ #include <linux/tcp.h> #include <linux/mlx5/fs.h> #include "en.h" +#include "lib/mpfs.h" static int mlx5e_add_l2_flow_rule(struct mlx5e_priv *priv, struct mlx5e_l2_rule *ai, int type); @@ -65,6 +66,7 @@ struct mlx5e_l2_hash_node { struct hlist_node hlist; u8 action; struct mlx5e_l2_rule ai; + bool mpfs; }; static inline int mlx5e_hash_l2(u8 *addr) @@ -362,17 +364,30 @@ static void mlx5e_del_vlan_rules(struct mlx5e_priv *priv) static void mlx5e_execute_l2_action(struct mlx5e_priv *priv, struct mlx5e_l2_hash_node *hn) { - switch (hn->action) { + u8 action = hn->action; + int l2_err = 0; + + switch (action) { case MLX5E_ACTION_ADD: mlx5e_add_l2_flow_rule(priv, &hn->ai, MLX5E_FULLMATCH); + if (!is_multicast_ether_addr(hn->ai.addr)) { + l2_err = mlx5_mpfs_add_mac(priv->mdev, hn->ai.addr); + hn->mpfs = !l2_err; + } hn->action = MLX5E_ACTION_NONE; break; case MLX5E_ACTION_DEL: + if (!is_multicast_ether_addr(hn->ai.addr) && hn->mpfs) + l2_err = mlx5_mpfs_del_mac(priv->mdev, hn->ai.addr); mlx5e_del_l2_flow_rule(priv, &hn->ai); mlx5e_del_l2_from_hash(hn); break; } + + if (l2_err) + netdev_warn(priv->netdev, "MPFS, failed to %s mac %pM, err(%d)\n", + action == MLX5E_ACTION_ADD ? "add" : "del", hn->ai.addr, l2_err); } static void mlx5e_sync_netdev_addr(struct mlx5e_priv *priv) @@ -593,12 +608,21 @@ static void mlx5e_cleanup_ttc_rules(struct mlx5e_ttc_table *ttc) ttc->rules[i] = NULL; } } + + for (i = 0; i < MLX5E_NUM_TUNNEL_TT; i++) { + if (!IS_ERR_OR_NULL(ttc->tunnel_rules[i])) { + mlx5_del_flow_rules(ttc->tunnel_rules[i]); + ttc->tunnel_rules[i] = NULL; + } + } } -static struct { +struct mlx5e_etype_proto { u16 etype; u8 proto; -} ttc_rules[] = { +}; + +static struct mlx5e_etype_proto ttc_rules[] = { [MLX5E_TT_IPV4_TCP] = { .etype = ETH_P_IP, .proto = IPPROTO_TCP, @@ -645,6 +669,28 @@ static struct { }, }; +static struct mlx5e_etype_proto ttc_tunnel_rules[] = { + [MLX5E_TT_IPV4_GRE] = { + .etype = ETH_P_IP, + .proto = IPPROTO_GRE, + }, + [MLX5E_TT_IPV6_GRE] = { + .etype = ETH_P_IPV6, + .proto = IPPROTO_GRE, + }, +}; + +static u8 mlx5e_etype_to_ipv(u16 ethertype) +{ + if (ethertype == ETH_P_IP) + return 4; + + if (ethertype == ETH_P_IPV6) + return 6; + + return 0; +} + static struct mlx5_flow_handle * mlx5e_generate_ttc_rule(struct mlx5e_priv *priv, struct mlx5_flow_table *ft, @@ -652,10 +698,12 @@ mlx5e_generate_ttc_rule(struct mlx5e_priv *priv, u16 etype, u8 proto) { + int match_ipv_outer = MLX5_CAP_FLOWTABLE_NIC_RX(priv->mdev, ft_field_support.outer_ip_version); MLX5_DECLARE_FLOW_ACT(flow_act); struct mlx5_flow_handle *rule; struct mlx5_flow_spec *spec; int err = 0; + u8 ipv; spec = kvzalloc(sizeof(*spec), GFP_KERNEL); if (!spec) @@ -666,7 +714,13 @@ mlx5e_generate_ttc_rule(struct mlx5e_priv *priv, MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ip_protocol); MLX5_SET(fte_match_param, spec->match_value, outer_headers.ip_protocol, proto); } - if (etype) { + + ipv = mlx5e_etype_to_ipv(etype); + if (match_ipv_outer && ipv) { + spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ip_version); + MLX5_SET(fte_match_param, spec->match_value, outer_headers.ip_version, ipv); + } else if (etype) { spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS; MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.ethertype); MLX5_SET(fte_match_param, spec->match_value, outer_headers.ethertype, etype); @@ -708,6 +762,20 @@ static int mlx5e_generate_ttc_table_rules(struct mlx5e_priv *priv) goto del_rules; } + if (!mlx5e_tunnel_inner_ft_supported(priv->mdev)) + return 0; + + rules = ttc->tunnel_rules; + dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; + dest.ft = priv->fs.inner_ttc.ft.t; + for (tt = 0; tt < MLX5E_NUM_TUNNEL_TT; tt++) { + rules[tt] = mlx5e_generate_ttc_rule(priv, ft, &dest, + ttc_tunnel_rules[tt].etype, + ttc_tunnel_rules[tt].proto); + if (IS_ERR(rules[tt])) + goto del_rules; + } + return 0; del_rules: @@ -718,13 +786,23 @@ del_rules: } #define MLX5E_TTC_NUM_GROUPS 3 -#define MLX5E_TTC_GROUP1_SIZE BIT(3) -#define MLX5E_TTC_GROUP2_SIZE BIT(1) -#define MLX5E_TTC_GROUP3_SIZE BIT(0) +#define MLX5E_TTC_GROUP1_SIZE (BIT(3) + MLX5E_NUM_TUNNEL_TT) +#define MLX5E_TTC_GROUP2_SIZE BIT(1) +#define MLX5E_TTC_GROUP3_SIZE BIT(0) #define MLX5E_TTC_TABLE_SIZE (MLX5E_TTC_GROUP1_SIZE +\ MLX5E_TTC_GROUP2_SIZE +\ MLX5E_TTC_GROUP3_SIZE) -static int mlx5e_create_ttc_table_groups(struct mlx5e_ttc_table *ttc) + +#define MLX5E_INNER_TTC_NUM_GROUPS 3 +#define MLX5E_INNER_TTC_GROUP1_SIZE BIT(3) +#define MLX5E_INNER_TTC_GROUP2_SIZE BIT(1) +#define MLX5E_INNER_TTC_GROUP3_SIZE BIT(0) +#define MLX5E_INNER_TTC_TABLE_SIZE (MLX5E_INNER_TTC_GROUP1_SIZE +\ + MLX5E_INNER_TTC_GROUP2_SIZE +\ + MLX5E_INNER_TTC_GROUP3_SIZE) + +static int mlx5e_create_ttc_table_groups(struct mlx5e_ttc_table *ttc, + bool use_ipv) { int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); struct mlx5e_flow_table *ft = &ttc->ft; @@ -746,7 +824,10 @@ static int mlx5e_create_ttc_table_groups(struct mlx5e_ttc_table *ttc) /* L4 Group */ mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_protocol); - MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); + if (use_ipv) + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ip_version); + else + MLX5_SET_TO_ONES(fte_match_param, mc, outer_headers.ethertype); MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); MLX5_SET_CFG(in, start_flow_index, ix); ix += MLX5E_TTC_GROUP1_SIZE; @@ -787,6 +868,190 @@ err: return err; } +static struct mlx5_flow_handle * +mlx5e_generate_inner_ttc_rule(struct mlx5e_priv *priv, + struct mlx5_flow_table *ft, + struct mlx5_flow_destination *dest, + u16 etype, u8 proto) +{ + MLX5_DECLARE_FLOW_ACT(flow_act); + struct mlx5_flow_handle *rule; + struct mlx5_flow_spec *spec; + int err = 0; + u8 ipv; + + spec = kvzalloc(sizeof(*spec), GFP_KERNEL); + if (!spec) + return ERR_PTR(-ENOMEM); + + ipv = mlx5e_etype_to_ipv(etype); + if (etype && ipv) { + spec->match_criteria_enable = MLX5_MATCH_INNER_HEADERS; + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, inner_headers.ip_version); + MLX5_SET(fte_match_param, spec->match_value, inner_headers.ip_version, ipv); + } + + if (proto) { + spec->match_criteria_enable = MLX5_MATCH_INNER_HEADERS; + MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, inner_headers.ip_protocol); + MLX5_SET(fte_match_param, spec->match_value, inner_headers.ip_protocol, proto); + } + + rule = mlx5_add_flow_rules(ft, spec, &flow_act, dest, 1); + if (IS_ERR(rule)) { + err = PTR_ERR(rule); + netdev_err(priv->netdev, "%s: add rule failed\n", __func__); + } + + kvfree(spec); + return err ? ERR_PTR(err) : rule; +} + +static int mlx5e_generate_inner_ttc_table_rules(struct mlx5e_priv *priv) +{ + struct mlx5_flow_destination dest; + struct mlx5_flow_handle **rules; + struct mlx5e_ttc_table *ttc; + struct mlx5_flow_table *ft; + int err; + int tt; + + ttc = &priv->fs.inner_ttc; + ft = ttc->ft.t; + rules = ttc->rules; + + dest.type = MLX5_FLOW_DESTINATION_TYPE_TIR; + for (tt = 0; tt < MLX5E_NUM_TT; tt++) { + if (tt == MLX5E_TT_ANY) + dest.tir_num = priv->direct_tir[0].tirn; + else + dest.tir_num = priv->inner_indir_tir[tt].tirn; + + rules[tt] = mlx5e_generate_inner_ttc_rule(priv, ft, &dest, + ttc_rules[tt].etype, + ttc_rules[tt].proto); + if (IS_ERR(rules[tt])) + goto del_rules; + } + + return 0; + +del_rules: + err = PTR_ERR(rules[tt]); + rules[tt] = NULL; + mlx5e_cleanup_ttc_rules(ttc); + return err; +} + +static int mlx5e_create_inner_ttc_table_groups(struct mlx5e_ttc_table *ttc) +{ + int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); + struct mlx5e_flow_table *ft = &ttc->ft; + int ix = 0; + u32 *in; + int err; + u8 *mc; + + ft->g = kcalloc(MLX5E_INNER_TTC_NUM_GROUPS, sizeof(*ft->g), GFP_KERNEL); + if (!ft->g) + return -ENOMEM; + in = kvzalloc(inlen, GFP_KERNEL); + if (!in) { + kfree(ft->g); + return -ENOMEM; + } + + /* L4 Group */ + mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); + MLX5_SET_TO_ONES(fte_match_param, mc, inner_headers.ip_protocol); + MLX5_SET_TO_ONES(fte_match_param, mc, inner_headers.ip_version); + MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_INNER_HEADERS); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_INNER_TTC_GROUP1_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err; + ft->num_groups++; + + /* L3 Group */ + MLX5_SET(fte_match_param, mc, inner_headers.ip_protocol, 0); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_INNER_TTC_GROUP2_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err; + ft->num_groups++; + + /* Any Group */ + memset(in, 0, inlen); + MLX5_SET_CFG(in, start_flow_index, ix); + ix += MLX5E_INNER_TTC_GROUP3_SIZE; + MLX5_SET_CFG(in, end_flow_index, ix - 1); + ft->g[ft->num_groups] = mlx5_create_flow_group(ft->t, in); + if (IS_ERR(ft->g[ft->num_groups])) + goto err; + ft->num_groups++; + + kvfree(in); + return 0; + +err: + err = PTR_ERR(ft->g[ft->num_groups]); + ft->g[ft->num_groups] = NULL; + kvfree(in); + + return err; +} + +static int mlx5e_create_inner_ttc_table(struct mlx5e_priv *priv) +{ + struct mlx5e_ttc_table *ttc = &priv->fs.inner_ttc; + struct mlx5_flow_table_attr ft_attr = {}; + struct mlx5e_flow_table *ft = &ttc->ft; + int err; + + if (!mlx5e_tunnel_inner_ft_supported(priv->mdev)) + return 0; + + ft_attr.max_fte = MLX5E_INNER_TTC_TABLE_SIZE; + ft_attr.level = MLX5E_INNER_TTC_FT_LEVEL; + ft_attr.prio = MLX5E_NIC_PRIO; + + ft->t = mlx5_create_flow_table(priv->fs.ns, &ft_attr); + if (IS_ERR(ft->t)) { + err = PTR_ERR(ft->t); + ft->t = NULL; + return err; + } + + err = mlx5e_create_inner_ttc_table_groups(ttc); + if (err) + goto err; + + err = mlx5e_generate_inner_ttc_table_rules(priv); + if (err) + goto err; + + return 0; + +err: + mlx5e_destroy_flow_table(ft); + return err; +} + +static void mlx5e_destroy_inner_ttc_table(struct mlx5e_priv *priv) +{ + struct mlx5e_ttc_table *ttc = &priv->fs.inner_ttc; + + if (!mlx5e_tunnel_inner_ft_supported(priv->mdev)) + return; + + mlx5e_cleanup_ttc_rules(ttc); + mlx5e_destroy_flow_table(&ttc->ft); +} + void mlx5e_destroy_ttc_table(struct mlx5e_priv *priv) { struct mlx5e_ttc_table *ttc = &priv->fs.ttc; @@ -797,6 +1062,7 @@ void mlx5e_destroy_ttc_table(struct mlx5e_priv *priv) int mlx5e_create_ttc_table(struct mlx5e_priv *priv) { + bool match_ipv_outer = MLX5_CAP_FLOWTABLE_NIC_RX(priv->mdev, ft_field_support.outer_ip_version); struct mlx5e_ttc_table *ttc = &priv->fs.ttc; struct mlx5_flow_table_attr ft_attr = {}; struct mlx5e_flow_table *ft = &ttc->ft; @@ -813,7 +1079,7 @@ int mlx5e_create_ttc_table(struct mlx5e_priv *priv) return err; } - err = mlx5e_create_ttc_table_groups(ttc); + err = mlx5e_create_ttc_table_groups(ttc, match_ipv_outer); if (err) goto err; @@ -1139,11 +1405,18 @@ int mlx5e_create_flow_steering(struct mlx5e_priv *priv) priv->netdev->hw_features &= ~NETIF_F_NTUPLE; } + err = mlx5e_create_inner_ttc_table(priv); + if (err) { + netdev_err(priv->netdev, "Failed to create inner ttc table, err=%d\n", + err); + goto err_destroy_arfs_tables; + } + err = mlx5e_create_ttc_table(priv); if (err) { netdev_err(priv->netdev, "Failed to create ttc table, err=%d\n", err); - goto err_destroy_arfs_tables; + goto err_destroy_inner_ttc_table; } err = mlx5e_create_l2_table(priv); @@ -1168,6 +1441,8 @@ err_destroy_l2_table: mlx5e_destroy_l2_table(priv); err_destroy_ttc_table: mlx5e_destroy_ttc_table(priv); +err_destroy_inner_ttc_table: + mlx5e_destroy_inner_ttc_table(priv); err_destroy_arfs_tables: mlx5e_arfs_destroy_tables(priv); @@ -1179,6 +1454,7 @@ void mlx5e_destroy_flow_steering(struct mlx5e_priv *priv) mlx5e_destroy_vlan_table(priv); mlx5e_destroy_l2_table(priv); mlx5e_destroy_ttc_table(priv); + mlx5e_destroy_inner_ttc_table(priv); mlx5e_arfs_destroy_tables(priv); mlx5e_ethtool_cleanup_steering(priv); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index d75f3099d164..dfc29720ab77 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -213,6 +213,7 @@ static void mlx5e_update_sw_counters(struct mlx5e_priv *priv) s->rx_cache_full += rq_stats->cache_full; s->rx_cache_empty += rq_stats->cache_empty; s->rx_cache_busy += rq_stats->cache_busy; + s->rx_cache_waive += rq_stats->cache_waive; for (j = 0; j < priv->channels.params.num_tc; j++) { sq_stats = &c->sq[j].stats; @@ -293,6 +294,12 @@ static void mlx5e_update_pport_counters(struct mlx5e_priv *priv, bool full) mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPCNT, 0, 0); } + if (MLX5_CAP_PCAM_FEATURE(mdev, rx_buffer_fullness_counters)) { + out = pstats->eth_ext_counters; + MLX5_SET(ppcnt_reg, in, grp, MLX5_ETHERNET_EXTENDED_COUNTERS_GROUP); + mlx5_core_access_reg(mdev, in, sz, out, sz, MLX5_REG_PPCNT, 0, 0); + } + MLX5_SET(ppcnt_reg, in, grp, MLX5_PER_PRIORITY_COUNTERS_GROUP); for (prio = 0; prio < NUM_PPORT_PRIO; prio++) { out = pstats->per_prio_counters[prio]; @@ -593,12 +600,12 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, } rq->buff.map_dir = rq->xdp_prog ? DMA_BIDIRECTIONAL : DMA_FROM_DEVICE; - rq->rx_headroom = params->rq_headroom; + rq->buff.headroom = params->rq_headroom; switch (rq->wq_type) { case MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ: - rq->alloc_wqe = mlx5e_alloc_rx_mpwqe; + rq->post_wqes = mlx5e_post_rx_mpwqes; rq->dealloc_wqe = mlx5e_dealloc_rx_mpwqe; rq->handle_rx_cqe = c->priv->profile->rx_handlers.handle_rx_cqe_mpwqe; @@ -615,11 +622,10 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, goto err_rq_wq_destroy; } - rq->mpwqe_stride_sz = BIT(params->mpwqe_log_stride_sz); - rq->mpwqe_num_strides = BIT(params->mpwqe_log_num_strides); + rq->mpwqe.log_stride_sz = params->mpwqe_log_stride_sz; + rq->mpwqe.num_strides = BIT(params->mpwqe_log_num_strides); - rq->buff.wqe_sz = rq->mpwqe_stride_sz * rq->mpwqe_num_strides; - byte_count = rq->buff.wqe_sz; + byte_count = rq->mpwqe.num_strides << rq->mpwqe.log_stride_sz; err = mlx5e_create_rq_umr_mkey(mdev, rq); if (err) @@ -639,7 +645,7 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, err = -ENOMEM; goto err_rq_wq_destroy; } - rq->alloc_wqe = mlx5e_alloc_rx_wqe; + rq->post_wqes = mlx5e_post_rx_wqes; rq->dealloc_wqe = mlx5e_dealloc_rx_wqe; #ifdef CONFIG_MLX5_EN_IPSEC @@ -655,18 +661,17 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, goto err_rq_wq_destroy; } - rq->buff.wqe_sz = params->lro_en ? + byte_count = params->lro_en ? params->lro_wqe_sz : MLX5E_SW2HW_MTU(c->priv, c->netdev->mtu); #ifdef CONFIG_MLX5_EN_IPSEC if (MLX5_IPSEC_DEV(mdev)) - rq->buff.wqe_sz += MLX5E_METADATA_ETHER_LEN; + byte_count += MLX5E_METADATA_ETHER_LEN; #endif rq->wqe.page_reuse = !params->xdp_prog && !params->lro_en; - byte_count = rq->buff.wqe_sz; /* calc the required page order */ - rq->wqe.frag_sz = MLX5_SKB_FRAG_SZ(rq->rx_headroom + byte_count); + rq->wqe.frag_sz = MLX5_SKB_FRAG_SZ(rq->buff.headroom + byte_count); npages = DIV_ROUND_UP(rq->wqe.frag_sz, PAGE_SIZE); rq->buff.page_order = order_base_2(npages); @@ -677,6 +682,12 @@ static int mlx5e_alloc_rq(struct mlx5e_channel *c, for (i = 0; i < wq_sz; i++) { struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(&rq->wq, i); + if (rq->wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ) { + u64 dma_offset = (u64)mlx5e_get_wqe_mtt_offset(rq, i) << PAGE_SHIFT; + + wqe->data.addr = cpu_to_be64(dma_offset); + } + wqe->data.byte_count = cpu_to_be32(byte_count); wqe->data.lkey = rq->mkey_be; } @@ -883,7 +894,8 @@ static void mlx5e_free_rx_descs(struct mlx5e_rq *rq) u16 wqe_ix; /* UMR WQE (if in progress) is always at wq->head */ - if (test_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state)) + if (rq->wq_type == MLX5_WQ_TYPE_LINKED_LIST_STRIDING_RQ && + rq->mpwqe.umr_in_progress) mlx5e_free_rx_mpwqe(rq, &rq->mpwqe.info[wq->head]); while (!mlx5_wq_ll_is_empty(wq)) { @@ -926,7 +938,7 @@ static int mlx5e_open_rq(struct mlx5e_channel *c, goto err_destroy_rq; if (params->rx_am_enabled) - set_bit(MLX5E_RQ_STATE_AM, &c->rq.state); + c->rq.state |= BIT(MLX5E_RQ_STATE_AM); return 0; @@ -946,7 +958,6 @@ static void mlx5e_activate_rq(struct mlx5e_rq *rq) set_bit(MLX5E_RQ_STATE_ENABLED, &rq->state); sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_NOP; - sq->db.ico_wqe[pi].num_wqebbs = 1; nopwqe = mlx5e_post_nop(&sq->wq, sq->sqn, &sq->pc); mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &nopwqe->ctrl); } @@ -1048,7 +1059,6 @@ static int mlx5e_alloc_icosq(struct mlx5e_channel *c, struct mlx5_core_dev *mdev = c->mdev; int err; - sq->pdev = c->pdev; sq->mkey_be = c->mkey_be; sq->channel = c; sq->uar_map = mdev->mlx5e_res.bfreg.map; @@ -1752,7 +1762,9 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, struct mlx5e_cq_moder icocq_moder = {0, 0}; struct net_device *netdev = priv->netdev; struct mlx5e_channel *c; + unsigned int irq; int err; + int eqn; c = kzalloc_node(sizeof(*c), GFP_KERNEL, mlx5e_get_node(priv, ix)); if (!c) @@ -1768,6 +1780,9 @@ static int mlx5e_open_channel(struct mlx5e_priv *priv, int ix, c->num_tc = params->num_tc; c->xdp = !!params->xdp_prog; + mlx5_vector2eqn(priv->mdev, ix, &eqn, &irq); + c->irq_desc = irq_to_desc(irq); + netif_napi_add(netdev, &c->napi, mlx5e_napi_poll, 64); err = mlx5e_open_cq(c, icocq_moder, &cparam->icosq_cq, &c->icosq.cq); @@ -2345,9 +2360,10 @@ static void mlx5e_build_tir_ctx_lro(struct mlx5e_params *params, void *tirc) void mlx5e_build_indir_tir_ctx_hash(struct mlx5e_params *params, enum mlx5e_traffic_types tt, - void *tirc) + void *tirc, bool inner) { - void *hfso = MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_outer); + void *hfso = inner ? MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_inner) : + MLX5_ADDR_OF(tirc, tirc, rx_hash_field_selector_outer); #define MLX5_HASH_IP (MLX5_HASH_FIELD_SEL_SRC_IP |\ MLX5_HASH_FIELD_SEL_DST_IP) @@ -2496,6 +2512,21 @@ free_in: return err; } +static void mlx5e_build_inner_indir_tir_ctx(struct mlx5e_priv *priv, + enum mlx5e_traffic_types tt, + u32 *tirc) +{ + MLX5_SET(tirc, tirc, transport_domain, priv->mdev->mlx5e_res.td.tdn); + + mlx5e_build_tir_ctx_lro(&priv->channels.params, tirc); + + MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_INDIRECT); + MLX5_SET(tirc, tirc, indirect_table, priv->indir_rqt.rqtn); + MLX5_SET(tirc, tirc, tunneled_offload_en, 0x1); + + mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, true); +} + static int mlx5e_set_mtu(struct mlx5e_priv *priv, u16 mtu) { struct mlx5_core_dev *mdev = priv->mdev; @@ -2583,12 +2614,6 @@ static void mlx5e_build_channels_tx_maps(struct mlx5e_priv *priv) } } -static bool mlx5e_is_eswitch_vport_mngr(struct mlx5_core_dev *mdev) -{ - return (MLX5_CAP_GEN(mdev, vport_group_manager) && - MLX5_CAP_GEN(mdev, port_type) == MLX5_CAP_PORT_TYPE_ETH); -} - void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) { int num_txqs = priv->channels.num * priv->channels.params.num_tc; @@ -2602,7 +2627,7 @@ void mlx5e_activate_priv_channels(struct mlx5e_priv *priv) mlx5e_activate_channels(&priv->channels); netif_tx_start_all_queues(priv->netdev); - if (mlx5e_is_eswitch_vport_mngr(priv->mdev)) + if (MLX5_VPORT_MANAGER(priv->mdev)) mlx5e_add_sqs_fwd_rules(priv); mlx5e_wait_channels_min_rx_wqes(&priv->channels); @@ -2613,7 +2638,7 @@ void mlx5e_deactivate_priv_channels(struct mlx5e_priv *priv) { mlx5e_redirect_rqts_to_drop(priv); - if (mlx5e_is_eswitch_vport_mngr(priv->mdev)) + if (MLX5_VPORT_MANAGER(priv->mdev)) mlx5e_remove_sqs_fwd_rules(priv); /* FIXME: This is a W/A only for tx timeout watch dog false alarm when @@ -2690,6 +2715,8 @@ int mlx5e_open(struct net_device *netdev) mutex_lock(&priv->state_lock); err = mlx5e_open_locked(netdev); + if (!err) + mlx5_set_port_admin_status(priv->mdev, MLX5_PORT_UP); mutex_unlock(&priv->state_lock); return err; @@ -2724,6 +2751,7 @@ int mlx5e_close(struct net_device *netdev) return -ENODEV; mutex_lock(&priv->state_lock); + mlx5_set_port_admin_status(priv->mdev, MLX5_PORT_DOWN); err = mlx5e_close_locked(netdev); mutex_unlock(&priv->state_lock); @@ -2864,7 +2892,7 @@ static void mlx5e_build_indir_tir_ctx(struct mlx5e_priv *priv, MLX5_SET(tirc, tirc, disp_type, MLX5_TIRC_DISP_TYPE_INDIRECT); MLX5_SET(tirc, tirc, indirect_table, priv->indir_rqt.rqtn); - mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc); + mlx5e_build_indir_tir_ctx_hash(&priv->channels.params, tt, tirc, false); } static void mlx5e_build_direct_tir_ctx(struct mlx5e_priv *priv, u32 rqtn, u32 *tirc) @@ -2883,6 +2911,7 @@ int mlx5e_create_indirect_tirs(struct mlx5e_priv *priv) struct mlx5e_tir *tir; void *tirc; int inlen; + int i = 0; int err; u32 *in; int tt; @@ -2898,16 +2927,36 @@ int mlx5e_create_indirect_tirs(struct mlx5e_priv *priv) tirc = MLX5_ADDR_OF(create_tir_in, in, ctx); mlx5e_build_indir_tir_ctx(priv, tt, tirc); err = mlx5e_create_tir(priv->mdev, tir, in, inlen); - if (err) - goto err_destroy_tirs; + if (err) { + mlx5_core_warn(priv->mdev, "create indirect tirs failed, %d\n", err); + goto err_destroy_inner_tirs; + } } + if (!mlx5e_tunnel_inner_ft_supported(priv->mdev)) + goto out; + + for (i = 0; i < MLX5E_NUM_INDIR_TIRS; i++) { + memset(in, 0, inlen); + tir = &priv->inner_indir_tir[i]; + tirc = MLX5_ADDR_OF(create_tir_in, in, ctx); + mlx5e_build_inner_indir_tir_ctx(priv, i, tirc); + err = mlx5e_create_tir(priv->mdev, tir, in, inlen); + if (err) { + mlx5_core_warn(priv->mdev, "create inner indirect tirs failed, %d\n", err); + goto err_destroy_inner_tirs; + } + } + +out: kvfree(in); return 0; -err_destroy_tirs: - mlx5_core_warn(priv->mdev, "create indirect tirs failed, %d\n", err); +err_destroy_inner_tirs: + for (i--; i >= 0; i--) + mlx5e_destroy_tir(priv->mdev, &priv->inner_indir_tir[i]); + for (tt--; tt >= 0; tt--) mlx5e_destroy_tir(priv->mdev, &priv->indir_tir[tt]); @@ -2961,6 +3010,12 @@ void mlx5e_destroy_indirect_tirs(struct mlx5e_priv *priv) for (i = 0; i < MLX5E_NUM_INDIR_TIRS; i++) mlx5e_destroy_tir(priv->mdev, &priv->indir_tir[i]); + + if (!mlx5e_tunnel_inner_ft_supported(priv->mdev)) + return; + + for (i = 0; i < MLX5E_NUM_INDIR_TIRS; i++) + mlx5e_destroy_tir(priv->mdev, &priv->inner_indir_tir[i]); } void mlx5e_destroy_direct_tirs(struct mlx5e_priv *priv) @@ -3000,12 +3055,16 @@ static int mlx5e_modify_channels_vsd(struct mlx5e_channels *chs, bool vsd) return 0; } -static int mlx5e_setup_tc(struct net_device *netdev, u8 tc) +static int mlx5e_setup_tc_mqprio(struct net_device *netdev, + struct tc_mqprio_qopt *mqprio) { struct mlx5e_priv *priv = netdev_priv(netdev); struct mlx5e_channels new_channels = {}; + u8 tc = mqprio->num_tc; int err = 0; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + if (tc && tc != MLX5E_MAX_NUM_TC) return -EINVAL; @@ -3029,39 +3088,42 @@ out: return err; } -static int mlx5e_ndo_setup_tc(struct net_device *dev, u32 handle, - u32 chain_index, __be16 proto, - struct tc_to_netdev *tc) +#ifdef CONFIG_MLX5_ESWITCH +static int mlx5e_setup_tc_cls_flower(struct net_device *dev, + struct tc_cls_flower_offload *cls_flower) { struct mlx5e_priv *priv = netdev_priv(dev); - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) - goto mqprio; + if (!is_classid_clsact_ingress(cls_flower->common.classid) || + cls_flower->common.chain_index) + return -EOPNOTSUPP; - if (chain_index) + switch (cls_flower->command) { + case TC_CLSFLOWER_REPLACE: + return mlx5e_configure_flower(priv, cls_flower); + case TC_CLSFLOWER_DESTROY: + return mlx5e_delete_flower(priv, cls_flower); + case TC_CLSFLOWER_STATS: + return mlx5e_stats_flower(priv, cls_flower); + default: return -EOPNOTSUPP; + } +} +#endif - switch (tc->type) { +static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + switch (type) { +#ifdef CONFIG_MLX5_ESWITCH case TC_SETUP_CLSFLOWER: - switch (tc->cls_flower->command) { - case TC_CLSFLOWER_REPLACE: - return mlx5e_configure_flower(priv, proto, tc->cls_flower); - case TC_CLSFLOWER_DESTROY: - return mlx5e_delete_flower(priv, tc->cls_flower); - case TC_CLSFLOWER_STATS: - return mlx5e_stats_flower(priv, tc->cls_flower); - } + return mlx5e_setup_tc_cls_flower(dev, type_data); +#endif + case TC_SETUP_MQPRIO: + return mlx5e_setup_tc_mqprio(dev, type_data); default: return -EOPNOTSUPP; } - -mqprio: - if (tc->type != TC_SETUP_MQPRIO) - return -EINVAL; - - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - - return mlx5e_setup_tc(dev, tc->mqprio->num_tc); } static void @@ -3358,6 +3420,7 @@ static int mlx5e_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) } } +#ifdef CONFIG_MLX5_ESWITCH static int mlx5e_set_vf_mac(struct net_device *dev, int vf, u8 *mac) { struct mlx5e_priv *priv = netdev_priv(dev); @@ -3460,6 +3523,7 @@ static int mlx5e_get_vf_stats(struct net_device *dev, return mlx5_eswitch_get_vport_stats(mdev->priv.eswitch, vf + 1, vf_stats); } +#endif static void mlx5e_add_vxlan_port(struct net_device *netdev, struct udp_tunnel_info *ti) @@ -3489,13 +3553,13 @@ static void mlx5e_del_vxlan_port(struct net_device *netdev, mlx5e_vxlan_queue_work(priv, ti->sa_family, be16_to_cpu(ti->port), 0); } -static netdev_features_t mlx5e_vxlan_features_check(struct mlx5e_priv *priv, - struct sk_buff *skb, - netdev_features_t features) +static netdev_features_t mlx5e_tunnel_features_check(struct mlx5e_priv *priv, + struct sk_buff *skb, + netdev_features_t features) { struct udphdr *udph; - u16 proto; - u16 port = 0; + u8 proto; + u16 port; switch (vlan_get_protocol(skb)) { case htons(ETH_P_IP): @@ -3508,14 +3572,17 @@ static netdev_features_t mlx5e_vxlan_features_check(struct mlx5e_priv *priv, goto out; } - if (proto == IPPROTO_UDP) { + switch (proto) { + case IPPROTO_GRE: + return features; + case IPPROTO_UDP: udph = udp_hdr(skb); port = be16_to_cpu(udph->dest); - } - /* Verify if UDP port is being offloaded by HW */ - if (port && mlx5e_vxlan_lookup_port(priv, port)) - return features; + /* Verify if UDP port is being offloaded by HW */ + if (mlx5e_vxlan_lookup_port(priv, port)) + return features; + } out: /* Disable CSUM and GSO if the udp dport is not offloaded by HW */ @@ -3539,7 +3606,7 @@ static netdev_features_t mlx5e_features_check(struct sk_buff *skb, /* Validate if the tunneled packet is being offloaded by HW */ if (skb->encapsulation && (features & NETIF_F_CSUM_MASK || features & NETIF_F_GSO_MASK)) - return mlx5e_vxlan_features_check(priv, skb, features); + return mlx5e_tunnel_features_check(priv, skb, features); return features; } @@ -3636,7 +3703,6 @@ static int mlx5e_xdp_set(struct net_device *netdev, struct bpf_prog *prog) set_bit(MLX5E_RQ_STATE_ENABLED, &c->rq.state); /* napi_schedule in case we have missed anything */ - set_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags); napi_schedule(&c->napi); if (old_prog) @@ -3693,11 +3759,11 @@ static void mlx5e_netpoll(struct net_device *dev) } #endif -static const struct net_device_ops mlx5e_netdev_ops_basic = { +static const struct net_device_ops mlx5e_netdev_ops = { .ndo_open = mlx5e_open, .ndo_stop = mlx5e_close, .ndo_start_xmit = mlx5e_xmit, - .ndo_setup_tc = mlx5e_ndo_setup_tc, + .ndo_setup_tc = mlx5e_setup_tc, .ndo_select_queue = mlx5e_select_queue, .ndo_get_stats64 = mlx5e_get_stats, .ndo_set_rx_mode = mlx5e_set_rx_mode, @@ -3708,6 +3774,9 @@ static const struct net_device_ops mlx5e_netdev_ops_basic = { .ndo_change_mtu = mlx5e_change_mtu, .ndo_do_ioctl = mlx5e_ioctl, .ndo_set_tx_maxrate = mlx5e_set_tx_maxrate, + .ndo_udp_tunnel_add = mlx5e_add_vxlan_port, + .ndo_udp_tunnel_del = mlx5e_del_vxlan_port, + .ndo_features_check = mlx5e_features_check, #ifdef CONFIG_RFS_ACCEL .ndo_rx_flow_steer = mlx5e_rx_flow_steer, #endif @@ -3716,29 +3785,8 @@ static const struct net_device_ops mlx5e_netdev_ops_basic = { #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = mlx5e_netpoll, #endif -}; - -static const struct net_device_ops mlx5e_netdev_ops_sriov = { - .ndo_open = mlx5e_open, - .ndo_stop = mlx5e_close, - .ndo_start_xmit = mlx5e_xmit, - .ndo_setup_tc = mlx5e_ndo_setup_tc, - .ndo_select_queue = mlx5e_select_queue, - .ndo_get_stats64 = mlx5e_get_stats, - .ndo_set_rx_mode = mlx5e_set_rx_mode, - .ndo_set_mac_address = mlx5e_set_mac, - .ndo_vlan_rx_add_vid = mlx5e_vlan_rx_add_vid, - .ndo_vlan_rx_kill_vid = mlx5e_vlan_rx_kill_vid, - .ndo_set_features = mlx5e_set_features, - .ndo_change_mtu = mlx5e_change_mtu, - .ndo_do_ioctl = mlx5e_ioctl, - .ndo_udp_tunnel_add = mlx5e_add_vxlan_port, - .ndo_udp_tunnel_del = mlx5e_del_vxlan_port, - .ndo_set_tx_maxrate = mlx5e_set_tx_maxrate, - .ndo_features_check = mlx5e_features_check, -#ifdef CONFIG_RFS_ACCEL - .ndo_rx_flow_steer = mlx5e_rx_flow_steer, -#endif +#ifdef CONFIG_MLX5_ESWITCH + /* SRIOV E-Switch NDOs */ .ndo_set_vf_mac = mlx5e_set_vf_mac, .ndo_set_vf_vlan = mlx5e_set_vf_vlan, .ndo_set_vf_spoofchk = mlx5e_set_vf_spoofchk, @@ -3747,13 +3795,9 @@ static const struct net_device_ops mlx5e_netdev_ops_sriov = { .ndo_get_vf_config = mlx5e_get_vf_config, .ndo_set_vf_link_state = mlx5e_set_vf_link_state, .ndo_get_vf_stats = mlx5e_get_vf_stats, - .ndo_tx_timeout = mlx5e_tx_timeout, - .ndo_xdp = mlx5e_xdp, -#ifdef CONFIG_NET_POLL_CONTROLLER - .ndo_poll_controller = mlx5e_netpoll, -#endif .ndo_has_offload_stats = mlx5e_has_offload_stats, .ndo_get_offload_stats = mlx5e_get_offload_stats, +#endif }; static int mlx5e_check_required_hca_cap(struct mlx5_core_dev *mdev) @@ -3790,8 +3834,7 @@ u16 mlx5e_get_max_inline_cap(struct mlx5_core_dev *mdev) 2 /*sizeof(mlx5e_tx_wqe.inline_hdr_start)*/; } -void mlx5e_build_default_indir_rqt(struct mlx5_core_dev *mdev, - u32 *indirection_rqt, int len, +void mlx5e_build_default_indir_rqt(u32 *indirection_rqt, int len, int num_channels) { int i; @@ -3934,7 +3977,7 @@ void mlx5e_build_nic_params(struct mlx5_core_dev *mdev, /* RSS */ params->rss_hfunc = ETH_RSS_HASH_XOR; netdev_rss_key_fill(params->toeplitz_hash_key, sizeof(params->toeplitz_hash_key)); - mlx5e_build_default_indir_rqt(mdev, params->indirection_rqt, + mlx5e_build_default_indir_rqt(params->indirection_rqt, MLX5E_INDIR_RQT_SIZE, max_channels); } @@ -3973,9 +4016,11 @@ static void mlx5e_set_netdev_dev_addr(struct net_device *netdev) } } +#if IS_ENABLED(CONFIG_NET_SWITCHDEV) && IS_ENABLED(CONFIG_MLX5_ESWITCH) static const struct switchdev_ops mlx5e_switchdev_ops = { .switchdev_port_attr_get = mlx5e_attr_get, }; +#endif static void mlx5e_build_nic_netdev(struct net_device *netdev) { @@ -3986,15 +4031,12 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) SET_NETDEV_DEV(netdev, &mdev->pdev->dev); - if (MLX5_CAP_GEN(mdev, vport_group_manager)) { - netdev->netdev_ops = &mlx5e_netdev_ops_sriov; + netdev->netdev_ops = &mlx5e_netdev_ops; + #ifdef CONFIG_MLX5_CORE_EN_DCB - if (MLX5_CAP_GEN(mdev, qos)) - netdev->dcbnl_ops = &mlx5e_dcbnl_ops; + if (MLX5_CAP_GEN(mdev, vport_group_manager) && MLX5_CAP_GEN(mdev, qos)) + netdev->dcbnl_ops = &mlx5e_dcbnl_ops; #endif - } else { - netdev->netdev_ops = &mlx5e_netdev_ops_basic; - } netdev->watchdog_timeo = 15 * HZ; @@ -4017,20 +4059,32 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_FILTER; - if (mlx5e_vxlan_allowed(mdev)) { - netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL | - NETIF_F_GSO_UDP_TUNNEL_CSUM | - NETIF_F_GSO_PARTIAL; + if (mlx5e_vxlan_allowed(mdev) || MLX5_CAP_ETH(mdev, tunnel_stateless_gre)) { + netdev->hw_features |= NETIF_F_GSO_PARTIAL; netdev->hw_enc_features |= NETIF_F_IP_CSUM; netdev->hw_enc_features |= NETIF_F_IPV6_CSUM; netdev->hw_enc_features |= NETIF_F_TSO; netdev->hw_enc_features |= NETIF_F_TSO6; - netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL; - netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL_CSUM | - NETIF_F_GSO_PARTIAL; + netdev->hw_enc_features |= NETIF_F_GSO_PARTIAL; + } + + if (mlx5e_vxlan_allowed(mdev)) { + netdev->hw_features |= NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; + netdev->hw_enc_features |= NETIF_F_GSO_UDP_TUNNEL | + NETIF_F_GSO_UDP_TUNNEL_CSUM; netdev->gso_partial_features = NETIF_F_GSO_UDP_TUNNEL_CSUM; } + if (MLX5_CAP_ETH(mdev, tunnel_stateless_gre)) { + netdev->hw_features |= NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM; + netdev->hw_enc_features |= NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM; + netdev->gso_partial_features |= NETIF_F_GSO_GRE | + NETIF_F_GSO_GRE_CSUM; + } + mlx5_query_port_fcs(mdev, &fcs_supported, &fcs_enabled); if (fcs_supported) @@ -4066,8 +4120,8 @@ static void mlx5e_build_nic_netdev(struct net_device *netdev) mlx5e_set_netdev_dev_addr(netdev); -#ifdef CONFIG_NET_SWITCHDEV - if (MLX5_CAP_GEN(mdev, vport_group_manager)) +#if IS_ENABLED(CONFIG_NET_SWITCHDEV) && IS_ENABLED(CONFIG_MLX5_ESWITCH) + if (MLX5_VPORT_MANAGER(mdev)) netdev->switchdev_ops = &mlx5e_switchdev_ops; #endif @@ -4199,6 +4253,10 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) mlx5e_init_l2_addr(priv); + /* Marking the link as currently not needed by the Driver */ + if (!netif_running(netdev)) + mlx5_set_port_admin_status(mdev, MLX5_PORT_DOWN); + /* MTU range: 68 - hw-specific max */ netdev->min_mtu = ETH_MIN_MTU; mlx5_query_port_max_mtu(priv->mdev, &max_mtu, 1); @@ -4209,7 +4267,7 @@ static void mlx5e_nic_enable(struct mlx5e_priv *priv) mlx5e_enable_async_events(priv); - if (MLX5_CAP_GEN(mdev, vport_group_manager)) + if (MLX5_VPORT_MANAGER(priv->mdev)) mlx5e_register_vport_reps(priv); if (netdev->reg_state != NETREG_REGISTERED) @@ -4243,7 +4301,7 @@ static void mlx5e_nic_disable(struct mlx5e_priv *priv) queue_work(priv->wq, &priv->set_rx_mode_work); - if (MLX5_CAP_GEN(mdev, vport_group_manager)) + if (MLX5_VPORT_MANAGER(priv->mdev)) mlx5e_unregister_vport_reps(priv); mlx5e_disable_async_events(priv); @@ -4416,32 +4474,29 @@ static void mlx5e_detach(struct mlx5_core_dev *mdev, void *vpriv) static void *mlx5e_add(struct mlx5_core_dev *mdev) { - struct mlx5_eswitch *esw = mdev->priv.eswitch; - int total_vfs = MLX5_TOTAL_VPORTS(mdev); - struct mlx5e_rep_priv *rpriv = NULL; + struct net_device *netdev; + void *rpriv = NULL; void *priv; - int vport; int err; - struct net_device *netdev; err = mlx5e_check_required_hca_cap(mdev); if (err) return NULL; - if (MLX5_CAP_GEN(mdev, vport_group_manager)) { - rpriv = kzalloc(sizeof(*rpriv), GFP_KERNEL); +#ifdef CONFIG_MLX5_ESWITCH + if (MLX5_VPORT_MANAGER(mdev)) { + rpriv = mlx5e_alloc_nic_rep_priv(mdev); if (!rpriv) { - mlx5_core_warn(mdev, - "Not creating net device, Failed to alloc rep priv data\n"); + mlx5_core_warn(mdev, "Failed to alloc NIC rep priv data\n"); return NULL; } - rpriv->rep = &esw->offloads.vport_reps[0]; } +#endif netdev = mlx5e_create_netdev(mdev, &mlx5e_nic_profile, rpriv); if (!netdev) { mlx5_core_err(mdev, "mlx5e_create_netdev failed\n"); - goto err_unregister_reps; + goto err_free_rpriv; } priv = netdev_priv(netdev); @@ -4462,14 +4517,9 @@ static void *mlx5e_add(struct mlx5_core_dev *mdev) err_detach: mlx5e_detach(mdev, priv); - err_destroy_netdev: mlx5e_destroy_netdev(priv); - -err_unregister_reps: - for (vport = 1; vport < total_vfs; vport++) - mlx5_eswitch_unregister_vport_rep(esw, vport); - +err_free_rpriv: kfree(rpriv); return NULL; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 45e60be9c277..45e03c427faf 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -613,15 +613,18 @@ static int mlx5e_rep_open(struct net_device *dev) struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; int err; - err = mlx5e_open(dev); + mutex_lock(&priv->state_lock); + err = mlx5e_open_locked(dev); if (err) - return err; + goto unlock; - err = mlx5_eswitch_set_vport_state(esw, rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_UP); - if (!err) + if (!mlx5_eswitch_set_vport_state(esw, rep->vport, + MLX5_ESW_VPORT_ADMIN_STATE_UP)) netif_carrier_on(dev); - return 0; +unlock: + mutex_unlock(&priv->state_lock); + return err; } static int mlx5e_rep_close(struct net_device *dev) @@ -630,10 +633,13 @@ static int mlx5e_rep_close(struct net_device *dev) struct mlx5e_rep_priv *rpriv = priv->ppriv; struct mlx5_eswitch_rep *rep = rpriv->rep; struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; + int ret; + mutex_lock(&priv->state_lock); (void)mlx5_eswitch_set_vport_state(esw, rep->vport, MLX5_ESW_VPORT_ADMIN_STATE_DOWN); - - return mlx5e_close(dev); + ret = mlx5e_close_locked(dev); + mutex_unlock(&priv->state_lock); + return ret; } static int mlx5e_rep_get_phys_port_name(struct net_device *dev, @@ -651,37 +657,42 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev, return 0; } -static int mlx5e_rep_ndo_setup_tc(struct net_device *dev, u32 handle, - u32 chain_index, __be16 proto, - struct tc_to_netdev *tc) +static int +mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, + struct tc_cls_flower_offload *cls_flower) { struct mlx5e_priv *priv = netdev_priv(dev); - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) + if (!is_classid_clsact_ingress(cls_flower->common.classid) || + cls_flower->common.chain_index) return -EOPNOTSUPP; - if (tc->egress_dev) { + if (cls_flower->egress_dev) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; - struct net_device *uplink_dev = mlx5_eswitch_get_uplink_netdev(esw); - return uplink_dev->netdev_ops->ndo_setup_tc(uplink_dev, handle, - chain_index, - proto, tc); + dev = mlx5_eswitch_get_uplink_netdev(esw); + return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, + cls_flower); } - if (chain_index) + switch (cls_flower->command) { + case TC_CLSFLOWER_REPLACE: + return mlx5e_configure_flower(priv, cls_flower); + case TC_CLSFLOWER_DESTROY: + return mlx5e_delete_flower(priv, cls_flower); + case TC_CLSFLOWER_STATS: + return mlx5e_stats_flower(priv, cls_flower); + default: return -EOPNOTSUPP; + } +} - switch (tc->type) { +static int mlx5e_rep_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + switch (type) { case TC_SETUP_CLSFLOWER: - switch (tc->cls_flower->command) { - case TC_CLSFLOWER_REPLACE: - return mlx5e_configure_flower(priv, proto, tc->cls_flower); - case TC_CLSFLOWER_DESTROY: - return mlx5e_delete_flower(priv, tc->cls_flower); - case TC_CLSFLOWER_STATS: - return mlx5e_stats_flower(priv, tc->cls_flower); - } + return mlx5e_rep_setup_tc_cls_flower(dev, type_data); default: return -EOPNOTSUPP; } @@ -773,7 +784,7 @@ static const struct net_device_ops mlx5e_netdev_ops_rep = { .ndo_stop = mlx5e_rep_close, .ndo_start_xmit = mlx5e_xmit, .ndo_get_phys_port_name = mlx5e_rep_get_phys_port_name, - .ndo_setup_tc = mlx5e_rep_ndo_setup_tc, + .ndo_setup_tc = mlx5e_rep_setup_tc, .ndo_get_stats64 = mlx5e_rep_get_stats, .ndo_has_offload_stats = mlx5e_has_offload_stats, .ndo_get_offload_stats = mlx5e_get_offload_stats, @@ -913,7 +924,7 @@ static int mlx5e_get_rep_max_num_channels(struct mlx5_core_dev *mdev) return MLX5E_PORT_REPRESENTOR_NCH; } -static struct mlx5e_profile mlx5e_rep_profile = { +static const struct mlx5e_profile mlx5e_rep_profile = { .init = mlx5e_init_rep, .init_rx = mlx5e_init_rep_rx, .cleanup_rx = mlx5e_cleanup_rep_rx, @@ -1099,3 +1110,16 @@ void mlx5e_unregister_vport_reps(struct mlx5e_priv *priv) mlx5e_rep_unregister_vf_vports(priv); /* VFs vports */ mlx5_eswitch_unregister_vport_rep(esw, 0); /* UPLINK PF*/ } + +void *mlx5e_alloc_nic_rep_priv(struct mlx5_core_dev *mdev) +{ + struct mlx5_eswitch *esw = mdev->priv.eswitch; + struct mlx5e_rep_priv *rpriv; + + rpriv = kzalloc(sizeof(*rpriv), GFP_KERNEL); + if (!rpriv) + return NULL; + + rpriv->rep = &esw->offloads.vport_reps[0]; + return rpriv; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h index a0a1a7a1d6c0..5659ed9f51e6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.h @@ -38,6 +38,7 @@ #include "eswitch.h" #include "en.h" +#ifdef CONFIG_MLX5_ESWITCH struct mlx5e_neigh_update_table { struct rhashtable neigh_ht; /* Save the neigh hash entries in a list in addition to the hash table @@ -123,6 +124,7 @@ struct mlx5e_encap_entry { int encap_size; }; +void *mlx5e_alloc_nic_rep_priv(struct mlx5_core_dev *mdev); void mlx5e_register_vport_reps(struct mlx5e_priv *priv); void mlx5e_unregister_vport_reps(struct mlx5e_priv *priv); bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv); @@ -141,5 +143,12 @@ void mlx5e_rep_encap_entry_detach(struct mlx5e_priv *priv, struct mlx5e_encap_entry *e); void mlx5e_rep_queue_neigh_stats_work(struct mlx5e_priv *priv); +#else /* CONFIG_MLX5_ESWITCH */ +static inline void mlx5e_register_vport_reps(struct mlx5e_priv *priv) {} +static inline void mlx5e_unregister_vport_reps(struct mlx5e_priv *priv) {} +static inline bool mlx5e_is_uplink_rep(struct mlx5e_priv *priv) { return false; } +static inline int mlx5e_add_sqs_fwd_rules(struct mlx5e_priv *priv) { return 0; } +static inline void mlx5e_remove_sqs_fwd_rules(struct mlx5e_priv *priv) {} +#endif #endif /* __MLX5E_REP_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 7344433259fc..f1dd638384d3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -163,7 +163,7 @@ static inline u32 mlx5e_decompress_cqes_start(struct mlx5e_rq *rq, static inline bool mlx5e_page_is_reserved(struct page *page) { - return page_is_pfmemalloc(page) || page_to_nid(page) != numa_node_id(); + return page_is_pfmemalloc(page) || page_to_nid(page) != numa_mem_id(); } static inline bool mlx5e_rx_cache_put(struct mlx5e_rq *rq, @@ -177,8 +177,10 @@ static inline bool mlx5e_rx_cache_put(struct mlx5e_rq *rq, return false; } - if (unlikely(page_is_pfmemalloc(dma_info->page))) + if (unlikely(mlx5e_page_is_reserved(dma_info->page))) { + rq->stats.cache_waive++; return false; + } cache->page_cache[cache->tail] = *dma_info; cache->tail = tail_next; @@ -252,7 +254,7 @@ static inline bool mlx5e_page_reuse(struct mlx5e_rq *rq, !mlx5e_page_is_reserved(wi->di.page); } -int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix) +static int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix) { struct mlx5e_wqe_frag_info *wi = &rq->wqe.frag_info[ix]; @@ -263,8 +265,7 @@ int mlx5e_alloc_rx_wqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix) wi->offset = 0; } - wqe->data.addr = cpu_to_be64(wi->di.addr + wi->offset + - rq->rx_headroom); + wqe->data.addr = cpu_to_be64(wi->di.addr + wi->offset + rq->buff.headroom); return 0; } @@ -296,7 +297,7 @@ void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix) static inline int mlx5e_mpwqe_strides_per_page(struct mlx5e_rq *rq) { - return rq->mpwqe_num_strides >> MLX5_MPWRQ_WQE_PAGE_ORDER; + return rq->mpwqe.num_strides >> MLX5_MPWRQ_WQE_PAGE_ORDER; } static inline void mlx5e_add_skb_frag_mpwqe(struct mlx5e_rq *rq, @@ -305,7 +306,7 @@ static inline void mlx5e_add_skb_frag_mpwqe(struct mlx5e_rq *rq, u32 page_idx, u32 frag_offset, u32 len) { - unsigned int truesize = ALIGN(len, rq->mpwqe_stride_sz); + unsigned int truesize = ALIGN(len, BIT(rq->mpwqe.log_stride_sz)); dma_sync_single_for_cpu(rq->pdev, wi->umr.dma_info[page_idx].addr + frag_offset, @@ -358,7 +359,6 @@ static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix) /* fill sq edge with nops to avoid wqe wrap around */ while ((pi = (sq->pc & wq->sz_m1)) > sq->edge) { sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_NOP; - sq->db.ico_wqe[pi].num_wqebbs = 1; mlx5e_post_nop(wq, sq->sqn, &sq->pc); } @@ -369,41 +369,35 @@ static inline void mlx5e_post_umr_wqe(struct mlx5e_rq *rq, u16 ix) MLX5_OPCODE_UMR); sq->db.ico_wqe[pi].opcode = MLX5_OPCODE_UMR; - sq->db.ico_wqe[pi].num_wqebbs = num_wqebbs; sq->pc += num_wqebbs; mlx5e_notify_hw(&sq->wq, sq->pc, sq->uar_map, &wqe->ctrl); } static int mlx5e_alloc_rx_umr_mpwqe(struct mlx5e_rq *rq, - struct mlx5e_rx_wqe *wqe, u16 ix) { struct mlx5e_mpw_info *wi = &rq->mpwqe.info[ix]; - u64 dma_offset = (u64)mlx5e_get_wqe_mtt_offset(rq, ix) << PAGE_SHIFT; int pg_strides = mlx5e_mpwqe_strides_per_page(rq); + struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[0]; int err; int i; - for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++) { - struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[i]; - + for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++, dma_info++) { err = mlx5e_page_alloc_mapped(rq, dma_info); if (unlikely(err)) goto err_unmap; wi->umr.mtt[i] = cpu_to_be64(dma_info->addr | MLX5_EN_WR); page_ref_add(dma_info->page, pg_strides); - wi->skbs_frags[i] = 0; } + memset(wi->skbs_frags, 0, sizeof(*wi->skbs_frags) * MLX5_MPWRQ_PAGES_PER_WQE); wi->consumed_strides = 0; - wqe->data.addr = cpu_to_be64(dma_offset); return 0; err_unmap: while (--i >= 0) { - struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[i]; - + dma_info--; page_ref_sub(dma_info->page, pg_strides); mlx5e_page_release(rq, dma_info, true); } @@ -414,27 +408,21 @@ err_unmap: void mlx5e_free_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi) { int pg_strides = mlx5e_mpwqe_strides_per_page(rq); + struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[0]; int i; - for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++) { - struct mlx5e_dma_info *dma_info = &wi->umr.dma_info[i]; - + for (i = 0; i < MLX5_MPWRQ_PAGES_PER_WQE; i++, dma_info++) { page_ref_sub(dma_info->page, pg_strides - wi->skbs_frags[i]); mlx5e_page_release(rq, dma_info, true); } } -void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq) +static void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq) { struct mlx5_wq_ll *wq = &rq->wq; struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(wq, wq->head); - clear_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state); - - if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) { - mlx5e_free_rx_mpwqe(rq, &rq->mpwqe.info[wq->head]); - return; - } + rq->mpwqe.umr_in_progress = false; mlx5_wq_ll_push(wq, be16_to_cpu(wqe->next.next_wqe_index)); @@ -444,16 +432,18 @@ void mlx5e_post_rx_mpwqe(struct mlx5e_rq *rq) mlx5_wq_ll_update_db_record(wq); } -int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, struct mlx5e_rx_wqe *wqe, u16 ix) +static int mlx5e_alloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix) { int err; - err = mlx5e_alloc_rx_umr_mpwqe(rq, wqe, ix); - if (unlikely(err)) + err = mlx5e_alloc_rx_umr_mpwqe(rq, ix); + if (unlikely(err)) { + rq->stats.buff_alloc_err++; return err; - set_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state); + } + rq->mpwqe.umr_in_progress = true; mlx5e_post_umr_wqe(rq, ix); - return -EBUSY; + return 0; } void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix) @@ -463,94 +453,150 @@ void mlx5e_dealloc_rx_mpwqe(struct mlx5e_rq *rq, u16 ix) mlx5e_free_rx_mpwqe(rq, wi); } -#define RQ_CANNOT_POST(rq) \ - (!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state) || \ - test_bit(MLX5E_RQ_STATE_UMR_WQE_IN_PROGRESS, &rq->state)) - bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq) { struct mlx5_wq_ll *wq = &rq->wq; + int err; - if (unlikely(RQ_CANNOT_POST(rq))) + if (unlikely(!MLX5E_TEST_BIT(rq->state, MLX5E_RQ_STATE_ENABLED))) return false; - while (!mlx5_wq_ll_is_full(wq)) { + if (mlx5_wq_ll_is_full(wq)) + return false; + + do { struct mlx5e_rx_wqe *wqe = mlx5_wq_ll_get_wqe(wq, wq->head); - int err; - err = rq->alloc_wqe(rq, wqe, wq->head); - if (err == -EBUSY) - return true; + err = mlx5e_alloc_rx_wqe(rq, wqe, wq->head); if (unlikely(err)) { rq->stats.buff_alloc_err++; break; } mlx5_wq_ll_push(wq, be16_to_cpu(wqe->next.next_wqe_index)); - } + } while (!mlx5_wq_ll_is_full(wq)); /* ensure wqes are visible to device before updating doorbell record */ dma_wmb(); mlx5_wq_ll_update_db_record(wq); - return !mlx5_wq_ll_is_full(wq); + return !!err; +} + +static inline void mlx5e_poll_ico_single_cqe(struct mlx5e_cq *cq, + struct mlx5e_icosq *sq, + struct mlx5e_rq *rq, + struct mlx5_cqe64 *cqe) +{ + struct mlx5_wq_cyc *wq = &sq->wq; + u16 ci = be16_to_cpu(cqe->wqe_counter) & wq->sz_m1; + struct mlx5e_sq_wqe_info *icowi = &sq->db.ico_wqe[ci]; + + mlx5_cqwq_pop(&cq->wq); + + if (unlikely((cqe->op_own >> 4) != MLX5_CQE_REQ)) { + WARN_ONCE(true, "mlx5e: Bad OP in ICOSQ CQE: 0x%x\n", + cqe->op_own); + return; + } + + if (likely(icowi->opcode == MLX5_OPCODE_UMR)) { + mlx5e_post_rx_mpwqe(rq); + return; + } + + if (unlikely(icowi->opcode != MLX5_OPCODE_NOP)) + WARN_ONCE(true, + "mlx5e: Bad OPCODE in ICOSQ WQE info: 0x%x\n", + icowi->opcode); +} + +static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq, struct mlx5e_rq *rq) +{ + struct mlx5e_icosq *sq = container_of(cq, struct mlx5e_icosq, cq); + struct mlx5_cqe64 *cqe; + + if (unlikely(!MLX5E_TEST_BIT(sq->state, MLX5E_SQ_STATE_ENABLED))) + return; + + cqe = mlx5_cqwq_get_cqe(&cq->wq); + if (likely(!cqe)) + return; + + /* by design, there's only a single cqe */ + mlx5e_poll_ico_single_cqe(cq, sq, rq, cqe); + + mlx5_cqwq_update_db_record(&cq->wq); +} + +bool mlx5e_post_rx_mpwqes(struct mlx5e_rq *rq) +{ + struct mlx5_wq_ll *wq = &rq->wq; + + if (unlikely(!MLX5E_TEST_BIT(rq->state, MLX5E_RQ_STATE_ENABLED))) + return false; + + mlx5e_poll_ico_cq(&rq->channel->icosq.cq, rq); + + if (mlx5_wq_ll_is_full(wq)) + return false; + + if (!rq->mpwqe.umr_in_progress) + mlx5e_alloc_rx_mpwqe(rq, wq->head); + + return true; } static void mlx5e_lro_update_hdr(struct sk_buff *skb, struct mlx5_cqe64 *cqe, u32 cqe_bcnt) { struct ethhdr *eth = (struct ethhdr *)(skb->data); - struct iphdr *ipv4; - struct ipv6hdr *ipv6; struct tcphdr *tcp; int network_depth = 0; __be16 proto; u16 tot_len; + void *ip_p; u8 l4_hdr_type = get_cqe_l4_hdr_type(cqe); - int tcp_ack = ((CQE_L4_HDR_TYPE_TCP_ACK_NO_DATA == l4_hdr_type) || - (CQE_L4_HDR_TYPE_TCP_ACK_AND_DATA == l4_hdr_type)); + u8 tcp_ack = (l4_hdr_type == CQE_L4_HDR_TYPE_TCP_ACK_NO_DATA) || + (l4_hdr_type == CQE_L4_HDR_TYPE_TCP_ACK_AND_DATA); skb->mac_len = ETH_HLEN; proto = __vlan_get_protocol(skb, eth->h_proto, &network_depth); - ipv4 = (struct iphdr *)(skb->data + network_depth); - ipv6 = (struct ipv6hdr *)(skb->data + network_depth); tot_len = cqe_bcnt - network_depth; + ip_p = skb->data + network_depth; if (proto == htons(ETH_P_IP)) { - tcp = (struct tcphdr *)(skb->data + network_depth + - sizeof(struct iphdr)); - ipv6 = NULL; - skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; - } else { - tcp = (struct tcphdr *)(skb->data + network_depth + - sizeof(struct ipv6hdr)); - ipv4 = NULL; - skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; - } - - if (get_cqe_lro_tcppsh(cqe)) - tcp->psh = 1; + struct iphdr *ipv4 = ip_p; - if (tcp_ack) { - tcp->ack = 1; - tcp->ack_seq = cqe->lro_ack_seq_num; - tcp->window = cqe->lro_tcp_win; - } + tcp = ip_p + sizeof(struct iphdr); + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4; - if (ipv4) { ipv4->ttl = cqe->lro_min_ttl; ipv4->tot_len = cpu_to_be16(tot_len); ipv4->check = 0; ipv4->check = ip_fast_csum((unsigned char *)ipv4, ipv4->ihl); } else { + struct ipv6hdr *ipv6 = ip_p; + + tcp = ip_p + sizeof(struct ipv6hdr); + skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6; + ipv6->hop_limit = cqe->lro_min_ttl; ipv6->payload_len = cpu_to_be16(tot_len - sizeof(struct ipv6hdr)); } + + tcp->psh = get_cqe_lro_tcppsh(cqe); + + if (tcp_ack) { + tcp->ack = 1; + tcp->ack_seq = cqe->lro_ack_seq_num; + tcp->window = cqe->lro_tcp_win; + } } static inline void mlx5e_skb_set_hash(struct mlx5_cqe64 *cqe, @@ -776,9 +822,9 @@ struct sk_buff *skb_from_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe, struct mlx5e_wqe_frag_info *wi, u32 cqe_bcnt) { struct mlx5e_dma_info *di = &wi->di; + u16 rx_headroom = rq->buff.headroom; struct sk_buff *skb; void *va, *data; - u16 rx_headroom = rq->rx_headroom; bool consumed; u32 frag_size; @@ -857,6 +903,7 @@ wq_ll_pop: &wqe->next.next_wqe_index); } +#ifdef CONFIG_MLX5_ESWITCH void mlx5e_handle_rx_cqe_rep(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe) { struct net_device *netdev = rq->netdev; @@ -901,6 +948,7 @@ wq_ll_pop: mlx5_wq_ll_pop(&rq->wq, wqe_counter_be, &wqe->next.next_wqe_index); } +#endif static inline void mlx5e_mpwqe_fill_rx_skb(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe, @@ -909,7 +957,7 @@ static inline void mlx5e_mpwqe_fill_rx_skb(struct mlx5e_rq *rq, struct sk_buff *skb) { u16 stride_ix = mpwrq_get_cqe_stride_index(cqe); - u32 wqe_offset = stride_ix * rq->mpwqe_stride_sz; + u32 wqe_offset = stride_ix << rq->mpwqe.log_stride_sz; u32 head_offset = wqe_offset & (PAGE_SIZE - 1); u32 page_idx = wqe_offset >> PAGE_SHIFT; u32 head_page_idx = page_idx; @@ -977,7 +1025,7 @@ void mlx5e_handle_rx_cqe_mpwrq(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe) napi_gro_receive(rq->cq.napi, skb); mpwrq_cqe_out: - if (likely(wi->consumed_strides < rq->mpwqe_num_strides)) + if (likely(wi->consumed_strides < rq->mpwqe.num_strides)) return; mlx5e_free_rx_mpwqe(rq, wi); @@ -987,21 +1035,23 @@ mpwrq_cqe_out: int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) { struct mlx5e_rq *rq = container_of(cq, struct mlx5e_rq, cq); - struct mlx5e_xdpsq *xdpsq = &rq->xdpsq; + struct mlx5e_xdpsq *xdpsq; + struct mlx5_cqe64 *cqe; int work_done = 0; - if (unlikely(!test_bit(MLX5E_RQ_STATE_ENABLED, &rq->state))) + if (unlikely(!MLX5E_TEST_BIT(rq->state, MLX5E_RQ_STATE_ENABLED))) return 0; if (cq->decmprs_left) work_done += mlx5e_decompress_cqes_cont(rq, cq, 0, budget); - for (; work_done < budget; work_done++) { - struct mlx5_cqe64 *cqe = mlx5_cqwq_get_cqe(&cq->wq); + cqe = mlx5_cqwq_get_cqe(&cq->wq); + if (!cqe) + return 0; - if (!cqe) - break; + xdpsq = &rq->xdpsq; + do { if (mlx5_get_cqe_format(cqe) == MLX5_COMPRESSED) { work_done += mlx5e_decompress_cqes_start(rq, cq, @@ -1012,7 +1062,7 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) mlx5_cqwq_pop(&cq->wq); rq->handle_rx_cqe(rq, cqe); - } + } while ((++work_done < budget) && (cqe = mlx5_cqwq_get_cqe(&cq->wq))); if (xdpsq->db.doorbell) { mlx5e_xmit_xdp_doorbell(xdpsq); @@ -1030,13 +1080,18 @@ int mlx5e_poll_rx_cq(struct mlx5e_cq *cq, int budget) bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) { struct mlx5e_xdpsq *sq; + struct mlx5_cqe64 *cqe; struct mlx5e_rq *rq; u16 sqcc; int i; sq = container_of(cq, struct mlx5e_xdpsq, cq); - if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + if (unlikely(!MLX5E_TEST_BIT(sq->state, MLX5E_SQ_STATE_ENABLED))) + return false; + + cqe = mlx5_cqwq_get_cqe(&cq->wq); + if (!cqe) return false; rq = container_of(sq, struct mlx5e_rq, xdpsq); @@ -1046,15 +1101,11 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) */ sqcc = sq->cc; - for (i = 0; i < MLX5E_TX_CQ_POLL_BUDGET; i++) { - struct mlx5_cqe64 *cqe; + i = 0; + do { u16 wqe_counter; bool last_wqe; - cqe = mlx5_cqwq_get_cqe(&cq->wq); - if (!cqe) - break; - mlx5_cqwq_pop(&cq->wq); wqe_counter = be16_to_cpu(cqe->wqe_counter); @@ -1072,7 +1123,7 @@ bool mlx5e_poll_xdpsq_cq(struct mlx5e_cq *cq) /* Recycle RX page */ mlx5e_page_release(rq, di, true); } while (!last_wqe); - } + } while ((++i < MLX5E_TX_CQ_POLL_BUDGET) && (cqe = mlx5_cqwq_get_cqe(&cq->wq))); mlx5_cqwq_update_db_record(&cq->wq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h index e65517eafc58..6d199ffb1c0b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_stats.h @@ -47,7 +47,7 @@ struct counter_desc { char format[ETH_GSTRING_LEN]; - int offset; /* Byte offset */ + size_t offset; /* Byte offset */ }; struct mlx5e_sw_stats { @@ -84,6 +84,7 @@ struct mlx5e_sw_stats { u64 rx_cache_full; u64 rx_cache_empty; u64 rx_cache_busy; + u64 rx_cache_waive; /* Special handling counters */ u64 link_down_events_phy; @@ -123,6 +124,7 @@ static const struct counter_desc sw_stats_desc[] = { { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cache_full) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cache_empty) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cache_busy) }, + { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, rx_cache_waive) }, { MLX5E_DECLARE_STAT(struct mlx5e_sw_stats, link_down_events_phy) }, }; @@ -216,6 +218,12 @@ static const struct counter_desc vport_stats_desc[] = { MLX5_GET64(ppcnt_reg, pstats->per_prio_counters[prio], \ counter_set.eth_per_prio_grp_data_layout.c##_high) #define NUM_PPORT_PRIO 8 +#define PPORT_ETH_EXT_OFF(c) \ + MLX5_BYTE_OFF(ppcnt_reg, \ + counter_set.eth_extended_cntrs_grp_data_layout.c##_high) +#define PPORT_ETH_EXT_GET(pstats, c) \ + MLX5_GET64(ppcnt_reg, (pstats)->eth_ext_counters, \ + counter_set.eth_extended_cntrs_grp_data_layout.c##_high) struct mlx5e_pport_stats { __be64 IEEE_802_3_counters[MLX5_ST_SZ_QW(ppcnt_reg)]; @@ -224,6 +232,7 @@ struct mlx5e_pport_stats { __be64 per_prio_counters[NUM_PPORT_PRIO][MLX5_ST_SZ_QW(ppcnt_reg)]; __be64 phy_counters[MLX5_ST_SZ_QW(ppcnt_reg)]; __be64 phy_statistical_counters[MLX5_ST_SZ_QW(ppcnt_reg)]; + __be64 eth_ext_counters[MLX5_ST_SZ_QW(ppcnt_reg)]; }; static const struct counter_desc pport_802_3_stats_desc[] = { @@ -290,12 +299,22 @@ static const struct counter_desc pport_per_prio_pfc_stats_desc[] = { { "rx_%s_pause_transition", PPORT_PER_PRIO_OFF(rx_pause_transition) }, }; +static const struct counter_desc pport_eth_ext_stats_desc[] = { + { "rx_buffer_passed_thres_phy", PPORT_ETH_EXT_OFF(rx_buffer_almost_full) }, +}; + #define PCIE_PERF_OFF(c) \ MLX5_BYTE_OFF(mpcnt_reg, counter_set.pcie_perf_cntrs_grp_data_layout.c) #define PCIE_PERF_GET(pcie_stats, c) \ MLX5_GET(mpcnt_reg, (pcie_stats)->pcie_perf_counters, \ counter_set.pcie_perf_cntrs_grp_data_layout.c) +#define PCIE_PERF_OFF64(c) \ + MLX5_BYTE_OFF(mpcnt_reg, counter_set.pcie_perf_cntrs_grp_data_layout.c##_high) +#define PCIE_PERF_GET64(pcie_stats, c) \ + MLX5_GET64(mpcnt_reg, (pcie_stats)->pcie_perf_counters, \ + counter_set.pcie_perf_cntrs_grp_data_layout.c##_high) + struct mlx5e_pcie_stats { __be64 pcie_perf_counters[MLX5_ST_SZ_QW(mpcnt_reg)]; }; @@ -305,6 +324,17 @@ static const struct counter_desc pcie_perf_stats_desc[] = { { "tx_pci_signal_integrity", PCIE_PERF_OFF(tx_errors) }, }; +static const struct counter_desc pcie_perf_stats_desc64[] = { + { "outbound_pci_buffer_overflow", PCIE_PERF_OFF64(tx_overflow_buffer_pkt) }, +}; + +static const struct counter_desc pcie_perf_stall_stats_desc[] = { + { "outbound_pci_stalled_rd", PCIE_PERF_OFF(outbound_stalled_reads) }, + { "outbound_pci_stalled_wr", PCIE_PERF_OFF(outbound_stalled_writes) }, + { "outbound_pci_stalled_rd_events", PCIE_PERF_OFF(outbound_stalled_reads_events) }, + { "outbound_pci_stalled_wr_events", PCIE_PERF_OFF(outbound_stalled_writes_events) }, +}; + struct mlx5e_rq_stats { u64 packets; u64 bytes; @@ -326,6 +356,7 @@ struct mlx5e_rq_stats { u64 cache_full; u64 cache_empty; u64 cache_busy; + u64 cache_waive; }; static const struct counter_desc rq_stats_desc[] = { @@ -349,6 +380,7 @@ static const struct counter_desc rq_stats_desc[] = { { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cache_full) }, { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cache_empty) }, { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cache_busy) }, + { MLX5E_DECLARE_RX_STAT(struct mlx5e_rq_stats, cache_waive) }, }; struct mlx5e_sq_stats { @@ -397,17 +429,29 @@ static const struct counter_desc sq_stats_desc[] = { #define NUM_PCIE_PERF_COUNTERS(priv) \ (ARRAY_SIZE(pcie_perf_stats_desc) * \ MLX5_CAP_MCAM_FEATURE((priv)->mdev, pcie_performance_group)) +#define NUM_PCIE_PERF_COUNTERS64(priv) \ + (ARRAY_SIZE(pcie_perf_stats_desc64) * \ + MLX5_CAP_MCAM_FEATURE((priv)->mdev, tx_overflow_buffer_pkt)) +#define NUM_PCIE_PERF_STALL_COUNTERS(priv) \ + (ARRAY_SIZE(pcie_perf_stall_stats_desc) * \ + MLX5_CAP_MCAM_FEATURE((priv)->mdev, pcie_outbound_stalled)) #define NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS \ ARRAY_SIZE(pport_per_prio_traffic_stats_desc) #define NUM_PPORT_PER_PRIO_PFC_COUNTERS \ ARRAY_SIZE(pport_per_prio_pfc_stats_desc) +#define NUM_PPORT_ETH_EXT_COUNTERS(priv) \ + (ARRAY_SIZE(pport_eth_ext_stats_desc) * \ + MLX5_CAP_PCAM_FEATURE((priv)->mdev, rx_buffer_fullness_counters)) #define NUM_PPORT_COUNTERS(priv) (NUM_PPORT_802_3_COUNTERS + \ NUM_PPORT_2863_COUNTERS + \ NUM_PPORT_2819_COUNTERS + \ NUM_PPORT_PHY_STATISTICAL_COUNTERS(priv) + \ NUM_PPORT_PER_PRIO_TRAFFIC_COUNTERS * \ - NUM_PPORT_PRIO) -#define NUM_PCIE_COUNTERS(priv) NUM_PCIE_PERF_COUNTERS(priv) + NUM_PPORT_PRIO + \ + NUM_PPORT_ETH_EXT_COUNTERS(priv)) +#define NUM_PCIE_COUNTERS(priv) (NUM_PCIE_PERF_COUNTERS(priv) + \ + NUM_PCIE_PERF_COUNTERS64(priv) +\ + NUM_PCIE_PERF_STALL_COUNTERS(priv)) #define NUM_RQ_STATS ARRAY_SIZE(rq_stats_desc) #define NUM_SQ_STATS ARRAY_SIZE(sq_stats_desc) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 7f282e8f4e7f..da503e6411da 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1326,7 +1326,7 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, LIST_HEAD(actions); int err; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return -EINVAL; attr->flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; @@ -1837,7 +1837,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, bool encap = false; int err = 0; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return -EINVAL; memset(attr, 0, sizeof(*attr)); @@ -1937,7 +1937,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, return err; } -int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, +int mlx5e_configure_flower(struct mlx5e_priv *priv, struct tc_cls_flower_offload *f) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index ecbe30d808ae..c14c263a739b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -33,12 +33,15 @@ #ifndef __MLX5_EN_TC_H__ #define __MLX5_EN_TC_H__ +#include <net/pkt_cls.h> + #define MLX5E_TC_FLOW_ID_MASK 0x0000ffff +#ifdef CONFIG_MLX5_ESWITCH int mlx5e_tc_init(struct mlx5e_priv *priv); void mlx5e_tc_cleanup(struct mlx5e_priv *priv); -int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, +int mlx5e_configure_flower(struct mlx5e_priv *priv, struct tc_cls_flower_offload *f); int mlx5e_delete_flower(struct mlx5e_priv *priv, struct tc_cls_flower_offload *f); @@ -60,4 +63,10 @@ static inline int mlx5e_tc_num_filters(struct mlx5e_priv *priv) return atomic_read(&priv->fs.tc.ht.nelems); } +#else /* CONFIG_MLX5_ESWITCH */ +static inline int mlx5e_tc_init(struct mlx5e_priv *priv) { return 0; } +static inline void mlx5e_tc_cleanup(struct mlx5e_priv *priv) {} +static inline int mlx5e_tc_num_filters(struct mlx5e_priv *priv) { return 0; } +#endif + #endif /* __MLX5_EN_TC_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c index 31353e5c3c78..fee43e40fa16 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tx.c @@ -394,6 +394,7 @@ netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev) bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) { struct mlx5e_txqsq *sq; + struct mlx5_cqe64 *cqe; u32 dma_fifo_cc; u32 nbytes; u16 npkts; @@ -402,7 +403,11 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) sq = container_of(cq, struct mlx5e_txqsq, cq); - if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) + if (unlikely(!MLX5E_TEST_BIT(sq->state, MLX5E_SQ_STATE_ENABLED))) + return false; + + cqe = mlx5_cqwq_get_cqe(&cq->wq); + if (!cqe) return false; npkts = 0; @@ -416,15 +421,11 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) /* avoid dirtying sq cache line every cqe */ dma_fifo_cc = sq->dma_fifo_cc; - for (i = 0; i < MLX5E_TX_CQ_POLL_BUDGET; i++) { - struct mlx5_cqe64 *cqe; + i = 0; + do { u16 wqe_counter; bool last_wqe; - cqe = mlx5_cqwq_get_cqe(&cq->wq); - if (!cqe) - break; - mlx5_cqwq_pop(&cq->wq); wqe_counter = be16_to_cpu(cqe->wqe_counter); @@ -467,7 +468,8 @@ bool mlx5e_poll_tx_cq(struct mlx5e_cq *cq, int napi_budget) sqcc += wi->num_wqebbs; napi_consume_skb(skb, napi_budget); } while (!last_wqe); - } + + } while ((++i < MLX5E_TX_CQ_POLL_BUDGET) && (cqe = mlx5_cqwq_get_cqe(&cq->wq))); mlx5_cqwq_update_db_record(&cq->wq); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c index 92db28a9ed43..e906b754415c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_txrx.c @@ -30,66 +30,18 @@ * SOFTWARE. */ +#include <linux/irq.h> #include "en.h" -static inline void mlx5e_poll_ico_single_cqe(struct mlx5e_cq *cq, - struct mlx5e_icosq *sq, - struct mlx5_cqe64 *cqe, - u16 *sqcc) +static inline bool mlx5e_channel_no_affinity_change(struct mlx5e_channel *c) { - struct mlx5_wq_cyc *wq = &sq->wq; - u16 ci = be16_to_cpu(cqe->wqe_counter) & wq->sz_m1; - struct mlx5e_sq_wqe_info *icowi = &sq->db.ico_wqe[ci]; - struct mlx5e_rq *rq = &sq->channel->rq; - - prefetch(rq); - mlx5_cqwq_pop(&cq->wq); - *sqcc += icowi->num_wqebbs; - - if (unlikely((cqe->op_own >> 4) != MLX5_CQE_REQ)) { - WARN_ONCE(true, "mlx5e: Bad OP in ICOSQ CQE: 0x%x\n", - cqe->op_own); - return; - } - - if (likely(icowi->opcode == MLX5_OPCODE_UMR)) { - mlx5e_post_rx_mpwqe(rq); - return; - } - - if (unlikely(icowi->opcode != MLX5_OPCODE_NOP)) - WARN_ONCE(true, - "mlx5e: Bad OPCODE in ICOSQ WQE info: 0x%x\n", - icowi->opcode); -} - -static void mlx5e_poll_ico_cq(struct mlx5e_cq *cq) -{ - struct mlx5e_icosq *sq = container_of(cq, struct mlx5e_icosq, cq); - struct mlx5_cqe64 *cqe; - u16 sqcc; - - if (unlikely(!test_bit(MLX5E_SQ_STATE_ENABLED, &sq->state))) - return; - - cqe = mlx5_cqwq_get_cqe(&cq->wq); - if (likely(!cqe)) - return; + int current_cpu = smp_processor_id(); + const struct cpumask *aff; + struct irq_data *idata; - /* sq->cc must be updated only after mlx5_cqwq_update_db_record(), - * otherwise a cq overrun may occur - */ - sqcc = sq->cc; - - /* by design, there's only a single cqe */ - mlx5e_poll_ico_single_cqe(cq, sq, cqe, &sqcc); - - mlx5_cqwq_update_db_record(&cq->wq); - - /* ensure cq space is freed before enabling more cqes */ - wmb(); - - sq->cc = sqcc; + idata = irq_desc_get_irq_data(c->irq_desc); + aff = irq_data_get_affinity_mask(idata); + return cpumask_test_cpu(current_cpu, aff); } int mlx5e_napi_poll(struct napi_struct *napi, int budget) @@ -100,8 +52,6 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) int work_done; int i; - clear_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags); - for (i = 0; i < c->num_tc; i++) busy |= mlx5e_poll_tx_cq(&c->sq[i].cq, budget); @@ -111,25 +61,22 @@ int mlx5e_napi_poll(struct napi_struct *napi, int budget) work_done = mlx5e_poll_rx_cq(&c->rq.cq, budget); busy |= work_done == budget; - mlx5e_poll_ico_cq(&c->icosq.cq); + busy |= c->rq.post_wqes(&c->rq); - busy |= mlx5e_post_rx_wqes(&c->rq); - - if (busy) - return budget; - - napi_complete_done(napi, work_done); + if (busy) { + if (likely(mlx5e_channel_no_affinity_change(c))) + return budget; + if (work_done == budget) + work_done--; + } - /* avoid losing completion event during/after polling cqs */ - if (test_bit(MLX5E_CHANNEL_NAPI_SCHED, &c->flags)) { - napi_schedule(napi); + if (unlikely(!napi_complete_done(napi, work_done))) return work_done; - } for (i = 0; i < c->num_tc; i++) mlx5e_cq_arm(&c->sq[i].cq); - if (test_bit(MLX5E_RQ_STATE_AM, &c->rq.state)) + if (MLX5E_TEST_BIT(c->rq.state, MLX5E_RQ_STATE_AM)) mlx5e_rx_am(&c->rq); mlx5e_cq_arm(&c->rq.cq); @@ -143,7 +90,6 @@ void mlx5e_completion_event(struct mlx5_core_cq *mcq) struct mlx5e_cq *cq = container_of(mcq, struct mlx5e_cq, mcq); cq->event_ctr++; - set_bit(MLX5E_CHANNEL_NAPI_SCHED, &cq->channel->flags); napi_schedule(cq->napi); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eq.c b/drivers/net/ethernet/mellanox/mlx5/core/eq.c index edd11ebd392e..fc606bfd1d6e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eq.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eq.c @@ -36,9 +36,7 @@ #include <linux/mlx5/cmd.h> #include "mlx5_core.h" #include "fpga/core.h" -#ifdef CONFIG_MLX5_CORE_EN #include "eswitch.h" -#endif enum { MLX5_EQE_SIZE = sizeof(struct mlx5_eqe), @@ -193,6 +191,7 @@ static void eq_update_ci(struct mlx5_eq *eq, int arm) { __be32 __iomem *addr = eq->doorbell + (arm ? 0 : 2); u32 val = (eq->cons_index & 0xffffff) | (eq->eqn << 24); + __raw_writel((__force u32)cpu_to_be32(val), addr); /* We still want ordering, just not swabbing, so add a barrier */ mb(); @@ -483,11 +482,9 @@ static irqreturn_t mlx5_eq_int(int irq, void *eq_ptr) } break; -#ifdef CONFIG_MLX5_CORE_EN case MLX5_EVENT_TYPE_NIC_VPORT_CHANGE: mlx5_eswitch_vport_event(dev->priv.eswitch, eqe); break; -#endif case MLX5_EVENT_TYPE_PORT_MODULE_EVENT: mlx5_port_module_event(dev, eqe); @@ -702,9 +699,7 @@ int mlx5_start_eqs(struct mlx5_core_dev *dev) u64 async_event_mask = MLX5_ASYNC_EVENT_MASK; int err; - if (MLX5_CAP_GEN(dev, port_type) == MLX5_CAP_PORT_TYPE_ETH && - MLX5_CAP_GEN(dev, vport_group_manager) && - mlx5_core_is_pf(dev)) + if (MLX5_VPORT_MANAGER(dev)) async_event_mask |= (1ull << MLX5_EVENT_TYPE_NIC_VPORT_CHANGE); if (MLX5_CAP_GEN(dev, port_type) == MLX5_CAP_PORT_TYPE_ETH && diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index 5b41f692acad..c77f4c0c7769 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -46,19 +46,13 @@ enum { MLX5_ACTION_DEL = 2, }; -/* E-Switch UC L2 table hash node */ -struct esw_uc_addr { - struct l2addr_node node; - u32 table_index; - u32 vport; -}; - /* Vport UC/MC hash node */ struct vport_addr { struct l2addr_node node; u8 action; u32 vport; - struct mlx5_flow_handle *flow_rule; /* SRIOV only */ + struct mlx5_flow_handle *flow_rule; + bool mpfs; /* UC MAC was added to MPFs */ /* A flag indicating that mac was added due to mc promiscuous vport */ bool mc_promisc; }; @@ -154,81 +148,6 @@ static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport, return modify_esw_vport_context_cmd(dev, vport, in, sizeof(in)); } -/* HW L2 Table (MPFS) management */ -static int set_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index, - u8 *mac, u8 vlan_valid, u16 vlan) -{ - u32 in[MLX5_ST_SZ_DW(set_l2_table_entry_in)] = {0}; - u32 out[MLX5_ST_SZ_DW(set_l2_table_entry_out)] = {0}; - u8 *in_mac_addr; - - MLX5_SET(set_l2_table_entry_in, in, opcode, - MLX5_CMD_OP_SET_L2_TABLE_ENTRY); - MLX5_SET(set_l2_table_entry_in, in, table_index, index); - MLX5_SET(set_l2_table_entry_in, in, vlan_valid, vlan_valid); - MLX5_SET(set_l2_table_entry_in, in, vlan, vlan); - - in_mac_addr = MLX5_ADDR_OF(set_l2_table_entry_in, in, mac_address); - ether_addr_copy(&in_mac_addr[2], mac); - - return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); -} - -static int del_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index) -{ - u32 in[MLX5_ST_SZ_DW(delete_l2_table_entry_in)] = {0}; - u32 out[MLX5_ST_SZ_DW(delete_l2_table_entry_out)] = {0}; - - MLX5_SET(delete_l2_table_entry_in, in, opcode, - MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY); - MLX5_SET(delete_l2_table_entry_in, in, table_index, index); - return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); -} - -static int alloc_l2_table_index(struct mlx5_l2_table *l2_table, u32 *ix) -{ - int err = 0; - - *ix = find_first_zero_bit(l2_table->bitmap, l2_table->size); - if (*ix >= l2_table->size) - err = -ENOSPC; - else - __set_bit(*ix, l2_table->bitmap); - - return err; -} - -static void free_l2_table_index(struct mlx5_l2_table *l2_table, u32 ix) -{ - __clear_bit(ix, l2_table->bitmap); -} - -static int set_l2_table_entry(struct mlx5_core_dev *dev, u8 *mac, - u8 vlan_valid, u16 vlan, - u32 *index) -{ - struct mlx5_l2_table *l2_table = &dev->priv.eswitch->l2_table; - int err; - - err = alloc_l2_table_index(l2_table, index); - if (err) - return err; - - err = set_l2_table_entry_cmd(dev, *index, mac, vlan_valid, vlan); - if (err) - free_l2_table_index(l2_table, *index); - - return err; -} - -static void del_l2_table_entry(struct mlx5_core_dev *dev, u32 index) -{ - struct mlx5_l2_table *l2_table = &dev->priv.eswitch->l2_table; - - del_l2_table_entry_cmd(dev, index); - free_l2_table_index(l2_table, index); -} - /* E-Switch FDB */ static struct mlx5_flow_handle * __esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u32 vport, bool rx_rule, @@ -455,65 +374,60 @@ typedef int (*vport_addr_action)(struct mlx5_eswitch *esw, static int esw_add_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) { - struct hlist_head *hash = esw->l2_table.l2_hash; - struct esw_uc_addr *esw_uc; u8 *mac = vaddr->node.addr; u32 vport = vaddr->vport; int err; - esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr); - if (esw_uc) { + /* Skip mlx5_mpfs_add_mac for PFs, + * it is already done by the PF netdev in mlx5e_execute_l2_action + */ + if (!vport) + goto fdb_add; + + err = mlx5_mpfs_add_mac(esw->dev, mac); + if (err) { esw_warn(esw->dev, - "Failed to set L2 mac(%pM) for vport(%d), mac is already in use by vport(%d)\n", - mac, vport, esw_uc->vport); - return -EEXIST; + "Failed to add L2 table mac(%pM) for vport(%d), err(%d)\n", + mac, vport, err); + return err; } + vaddr->mpfs = true; - esw_uc = l2addr_hash_add(hash, mac, struct esw_uc_addr, GFP_KERNEL); - if (!esw_uc) - return -ENOMEM; - esw_uc->vport = vport; - - err = set_l2_table_entry(esw->dev, mac, 0, 0, &esw_uc->table_index); - if (err) - goto abort; - +fdb_add: /* SRIOV is enabled: Forward UC MAC to vport */ if (esw->fdb_table.fdb && esw->mode == SRIOV_LEGACY) vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport); - esw_debug(esw->dev, "\tADDED UC MAC: vport[%d] %pM index:%d fr(%p)\n", - vport, mac, esw_uc->table_index, vaddr->flow_rule); - return err; -abort: - l2addr_hash_del(esw_uc); - return err; + esw_debug(esw->dev, "\tADDED UC MAC: vport[%d] %pM fr(%p)\n", + vport, mac, vaddr->flow_rule); + + return 0; } static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr) { - struct hlist_head *hash = esw->l2_table.l2_hash; - struct esw_uc_addr *esw_uc; u8 *mac = vaddr->node.addr; u32 vport = vaddr->vport; + int err = 0; - esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr); - if (!esw_uc || esw_uc->vport != vport) { - esw_debug(esw->dev, - "MAC(%pM) doesn't belong to vport (%d)\n", - mac, vport); - return -EINVAL; - } - esw_debug(esw->dev, "\tDELETE UC MAC: vport[%d] %pM index:%d fr(%p)\n", - vport, mac, esw_uc->table_index, vaddr->flow_rule); + /* Skip mlx5_mpfs_del_mac for PFs, + * it is already done by the PF netdev in mlx5e_execute_l2_action + */ + if (!vport || !vaddr->mpfs) + goto fdb_del; - del_l2_table_entry(esw->dev, esw_uc->table_index); + err = mlx5_mpfs_del_mac(esw->dev, mac); + if (err) + esw_warn(esw->dev, + "Failed to del L2 table mac(%pM) for vport(%d), err(%d)\n", + mac, vport, err); + vaddr->mpfs = false; +fdb_del: if (vaddr->flow_rule) mlx5_del_flow_rules(vaddr->flow_rule); vaddr->flow_rule = NULL; - l2addr_hash_del(esw_uc); return 0; } @@ -1611,13 +1525,14 @@ static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num) } /* Public E-Switch API */ +#define ESW_ALLOWED(esw) ((esw) && MLX5_VPORT_MANAGER((esw)->dev)) + int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) { int err; int i, enabled_events; - if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || - MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) + if (!ESW_ALLOWED(esw)) return 0; if (!MLX5_CAP_GEN(esw->dev, eswitch_flow_table) || @@ -1634,7 +1549,6 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) esw_info(esw->dev, "E-Switch enable SRIOV: nvfs(%d) mode (%d)\n", nvfs, mode); esw->mode = mode; - esw_disable_vport(esw, 0); if (mode == SRIOV_LEGACY) err = esw_create_legacy_fdb_table(esw, nvfs + 1); @@ -1647,7 +1561,11 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) if (err) esw_warn(esw->dev, "Failed to create eswitch TSAR"); - enabled_events = (mode == SRIOV_LEGACY) ? SRIOV_VPORT_EVENTS : UC_ADDR_CHANGE; + /* Don't enable vport events when in SRIOV_OFFLOADS mode, since: + * 1. L2 table (MPFS) is programmed by PF/VF representors netdevs set_rx_mode + * 2. FDB/Eswitch is programmed by user space tools + */ + enabled_events = (mode == SRIOV_LEGACY) ? SRIOV_VPORT_EVENTS : 0; for (i = 0; i <= nvfs; i++) esw_enable_vport(esw, i, enabled_events); @@ -1656,7 +1574,6 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) return 0; abort: - esw_enable_vport(esw, 0, UC_ADDR_CHANGE); esw->mode = SRIOV_NONE; return err; } @@ -1667,9 +1584,7 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) int nvports; int i; - if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || - MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH || - esw->mode == SRIOV_NONE) + if (!ESW_ALLOWED(esw) || esw->mode == SRIOV_NONE) return; esw_info(esw->dev, "disable SRIOV: active vports(%d) mode(%d)\n", @@ -1692,44 +1607,21 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) esw_offloads_cleanup(esw, nvports); esw->mode = SRIOV_NONE; - /* VPORT 0 (PF) must be enabled back with non-sriov configuration */ - esw_enable_vport(esw, 0, UC_ADDR_CHANGE); -} - -void mlx5_eswitch_attach(struct mlx5_eswitch *esw) -{ - if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || - MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) - return; - - esw_enable_vport(esw, 0, UC_ADDR_CHANGE); - /* VF Vports will be enabled when SRIOV is enabled */ -} - -void mlx5_eswitch_detach(struct mlx5_eswitch *esw) -{ - if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || - MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) - return; - - esw_disable_vport(esw, 0); } int mlx5_eswitch_init(struct mlx5_core_dev *dev) { - int l2_table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table); int total_vports = MLX5_TOTAL_VPORTS(dev); struct mlx5_eswitch *esw; int vport_num; int err; - if (!MLX5_CAP_GEN(dev, vport_group_manager) || - MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) + if (!MLX5_VPORT_MANAGER(dev)) return 0; esw_info(dev, - "Total vports %d, l2 table size(%d), per vport: max uc(%d) max mc(%d)\n", - total_vports, l2_table_size, + "Total vports %d, per vport: max uc(%d) max mc(%d)\n", + total_vports, MLX5_MAX_UC_PER_VPORT(dev), MLX5_MAX_MC_PER_VPORT(dev)); @@ -1739,14 +1631,6 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) esw->dev = dev; - esw->l2_table.bitmap = kcalloc(BITS_TO_LONGS(l2_table_size), - sizeof(uintptr_t), GFP_KERNEL); - if (!esw->l2_table.bitmap) { - err = -ENOMEM; - goto abort; - } - esw->l2_table.size = l2_table_size; - esw->work_queue = create_singlethread_workqueue("mlx5_esw_wq"); if (!esw->work_queue) { err = -ENOMEM; @@ -1797,7 +1681,6 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev) abort: if (esw->work_queue) destroy_workqueue(esw->work_queue); - kfree(esw->l2_table.bitmap); kfree(esw->vports); kfree(esw->offloads.vport_reps); kfree(esw); @@ -1806,15 +1689,13 @@ abort: void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) { - if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) || - MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH) + if (!esw || !MLX5_VPORT_MANAGER(esw->dev)) return; esw_info(esw->dev, "cleanup\n"); esw->dev->priv.eswitch = NULL; destroy_workqueue(esw->work_queue); - kfree(esw->l2_table.bitmap); kfree(esw->offloads.vport_reps); kfree(esw->vports); kfree(esw); @@ -1838,8 +1719,6 @@ void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe) } /* Vport Administration */ -#define ESW_ALLOWED(esw) \ - (esw && MLX5_CAP_GEN(esw->dev, vport_group_manager) && mlx5_core_is_pf(esw->dev)) #define LEGAL_VPORT(esw, vport) (vport >= 0 && vport < esw->total_vports) int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h index 834a33050969..565c8b7a399a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.h @@ -37,6 +37,15 @@ #include <linux/if_link.h> #include <net/devlink.h> #include <linux/mlx5/device.h> +#include "lib/mpfs.h" + +enum { + SRIOV_NONE, + SRIOV_LEGACY, + SRIOV_OFFLOADS +}; + +#ifdef CONFIG_MLX5_ESWITCH #define MLX5_MAX_UC_PER_VPORT(dev) \ (1 << MLX5_CAP_GEN(dev, log_max_current_uc_list)) @@ -44,9 +53,6 @@ #define MLX5_MAX_MC_PER_VPORT(dev) \ (1 << MLX5_CAP_GEN(dev, log_max_current_mc_list)) -#define MLX5_L2_ADDR_HASH_SIZE (BIT(BITS_PER_BYTE)) -#define MLX5_L2_ADDR_HASH(addr) (addr[5]) - #define FDB_UPLINK_VPORT 0xffff #define MLX5_MIN_BW_SHARE 1 @@ -54,48 +60,6 @@ #define MLX5_RATE_TO_BW_SHARE(rate, divider, limit) \ min_t(u32, max_t(u32, (rate) / (divider), MLX5_MIN_BW_SHARE), limit) -/* L2 -mac address based- hash helpers */ -struct l2addr_node { - struct hlist_node hlist; - u8 addr[ETH_ALEN]; -}; - -#define for_each_l2hash_node(hn, tmp, hash, i) \ - for (i = 0; i < MLX5_L2_ADDR_HASH_SIZE; i++) \ - hlist_for_each_entry_safe(hn, tmp, &hash[i], hlist) - -#define l2addr_hash_find(hash, mac, type) ({ \ - int ix = MLX5_L2_ADDR_HASH(mac); \ - bool found = false; \ - type *ptr = NULL; \ - \ - hlist_for_each_entry(ptr, &hash[ix], node.hlist) \ - if (ether_addr_equal(ptr->node.addr, mac)) {\ - found = true; \ - break; \ - } \ - if (!found) \ - ptr = NULL; \ - ptr; \ -}) - -#define l2addr_hash_add(hash, mac, type, gfp) ({ \ - int ix = MLX5_L2_ADDR_HASH(mac); \ - type *ptr = NULL; \ - \ - ptr = kzalloc(sizeof(type), gfp); \ - if (ptr) { \ - ether_addr_copy(ptr->node.addr, mac); \ - hlist_add_head(&ptr->node.hlist, &hash[ix]);\ - } \ - ptr; \ -}) - -#define l2addr_hash_del(ptr) ({ \ - hlist_del(&ptr->node.hlist); \ - kfree(ptr); \ -}) - struct vport_ingress { struct mlx5_flow_table *acl; struct mlx5_flow_group *allow_untagged_spoofchk_grp; @@ -150,12 +114,6 @@ struct mlx5_vport { u16 enabled_events; }; -struct mlx5_l2_table { - struct hlist_head l2_hash[MLX5_L2_ADDR_HASH_SIZE]; - u32 size; - unsigned long *bitmap; -}; - struct mlx5_eswitch_fdb { void *fdb; union { @@ -175,12 +133,6 @@ struct mlx5_eswitch_fdb { }; }; -enum { - SRIOV_NONE, - SRIOV_LEGACY, - SRIOV_OFFLOADS -}; - struct mlx5_esw_sq { struct mlx5_flow_handle *send_to_vport_rule; struct list_head list; @@ -222,7 +174,6 @@ struct esw_mc_addr { /* SRIOV only */ struct mlx5_eswitch { struct mlx5_core_dev *dev; - struct mlx5_l2_table l2_table; struct mlx5_eswitch_fdb fdb_table; struct hlist_head mc_table[MLX5_L2_ADDR_HASH_SIZE]; struct workqueue_struct *work_queue; @@ -250,8 +201,6 @@ int esw_offloads_init(struct mlx5_eswitch *esw, int nvports); /* E-Switch API */ int mlx5_eswitch_init(struct mlx5_core_dev *dev); void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw); -void mlx5_eswitch_attach(struct mlx5_eswitch *esw); -void mlx5_eswitch_detach(struct mlx5_eswitch *esw); void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe); int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode); void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw); @@ -345,4 +294,13 @@ int __mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw, #define esw_debug(dev, format, ...) \ mlx5_core_dbg_mask(dev, MLX5_DEBUG_ESWITCH_MASK, format, ##__VA_ARGS__) +#else /* CONFIG_MLX5_ESWITCH */ +/* eswitch API stubs */ +static inline int mlx5_eswitch_init(struct mlx5_core_dev *dev) { return 0; } +static inline void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw) {} +static inline void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe) {} +static inline int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs, int mode) { return 0; } +static inline void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw) {} +#endif /* CONFIG_MLX5_ESWITCH */ + #endif /* __MLX5_ESWITCH_H__ */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 5bc0593bd76e..d9fd8570b07c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -433,6 +433,8 @@ static int esw_create_offloads_fast_fdb_table(struct mlx5_eswitch *esw) struct mlx5_flow_table *fdb = NULL; int esw_size, err = 0; u32 flags = 0; + u32 max_flow_counter = (MLX5_CAP_GEN(dev, max_flow_counter_31_16) << 16) | + MLX5_CAP_GEN(dev, max_flow_counter_15_0); root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB); if (!root_ns) { @@ -443,9 +445,9 @@ static int esw_create_offloads_fast_fdb_table(struct mlx5_eswitch *esw) esw_debug(dev, "Create offloads FDB table, min (max esw size(2^%d), max counters(%d)*groups(%d))\n", MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size), - MLX5_CAP_GEN(dev, max_flow_counter), ESW_OFFLOADS_NUM_GROUPS); + max_flow_counter, ESW_OFFLOADS_NUM_GROUPS); - esw_size = min_t(int, MLX5_CAP_GEN(dev, max_flow_counter) * ESW_OFFLOADS_NUM_GROUPS, + esw_size = min_t(int, max_flow_counter * ESW_OFFLOADS_NUM_GROUPS, 1 << MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size)); if (esw->offloads.encap != DEVLINK_ESWITCH_ENCAP_MODE_NONE) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index e750f07793b8..e0d0efd903bc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -263,7 +263,7 @@ static int mlx5_cmd_set_fte(struct mlx5_core_dev *dev, MLX5_SET(flow_context, in_flow_context, modify_header_id, fte->modify_id); in_match_value = MLX5_ADDR_OF(flow_context, in_flow_context, match_value); - memcpy(in_match_value, &fte->val, MLX5_ST_SZ_BYTES(fte_match_param)); + memcpy(in_match_value, &fte->val, sizeof(fte->val)); in_dests = MLX5_ADDR_OF(flow_context, in_flow_context, destination); if (fte->action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST) { @@ -359,7 +359,7 @@ int mlx5_cmd_delete_fte(struct mlx5_core_dev *dev, return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); } -int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u16 *id) +int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u32 *id) { u32 in[MLX5_ST_SZ_DW(alloc_flow_counter_in)] = {0}; u32 out[MLX5_ST_SZ_DW(alloc_flow_counter_out)] = {0}; @@ -374,7 +374,7 @@ int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u16 *id) return err; } -int mlx5_cmd_fc_free(struct mlx5_core_dev *dev, u16 id) +int mlx5_cmd_fc_free(struct mlx5_core_dev *dev, u32 id) { u32 in[MLX5_ST_SZ_DW(dealloc_flow_counter_in)] = {0}; u32 out[MLX5_ST_SZ_DW(dealloc_flow_counter_out)] = {0}; @@ -385,7 +385,7 @@ int mlx5_cmd_fc_free(struct mlx5_core_dev *dev, u16 id) return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); } -int mlx5_cmd_fc_query(struct mlx5_core_dev *dev, u16 id, +int mlx5_cmd_fc_query(struct mlx5_core_dev *dev, u32 id, u64 *packets, u64 *bytes) { u32 out[MLX5_ST_SZ_BYTES(query_flow_counter_out) + @@ -409,14 +409,14 @@ int mlx5_cmd_fc_query(struct mlx5_core_dev *dev, u16 id, } struct mlx5_cmd_fc_bulk { - u16 id; + u32 id; int num; int outlen; u32 out[0]; }; struct mlx5_cmd_fc_bulk * -mlx5_cmd_fc_bulk_alloc(struct mlx5_core_dev *dev, u16 id, int num) +mlx5_cmd_fc_bulk_alloc(struct mlx5_core_dev *dev, u32 id, int num) { struct mlx5_cmd_fc_bulk *b; int outlen = @@ -453,7 +453,7 @@ mlx5_cmd_fc_bulk_query(struct mlx5_core_dev *dev, struct mlx5_cmd_fc_bulk *b) } void mlx5_cmd_fc_bulk_get(struct mlx5_core_dev *dev, - struct mlx5_cmd_fc_bulk *b, u16 id, + struct mlx5_cmd_fc_bulk *b, u32 id, u64 *packets, u64 *bytes) { int index = id - b->id; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h index 0f98a7cf4877..c6d7bdf255b6 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h @@ -74,20 +74,20 @@ int mlx5_cmd_update_root_ft(struct mlx5_core_dev *dev, struct mlx5_flow_table *ft, u32 underlay_qpn); -int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u16 *id); -int mlx5_cmd_fc_free(struct mlx5_core_dev *dev, u16 id); -int mlx5_cmd_fc_query(struct mlx5_core_dev *dev, u16 id, +int mlx5_cmd_fc_alloc(struct mlx5_core_dev *dev, u32 *id); +int mlx5_cmd_fc_free(struct mlx5_core_dev *dev, u32 id); +int mlx5_cmd_fc_query(struct mlx5_core_dev *dev, u32 id, u64 *packets, u64 *bytes); struct mlx5_cmd_fc_bulk; struct mlx5_cmd_fc_bulk * -mlx5_cmd_fc_bulk_alloc(struct mlx5_core_dev *dev, u16 id, int num); +mlx5_cmd_fc_bulk_alloc(struct mlx5_core_dev *dev, u32 id, int num); void mlx5_cmd_fc_bulk_free(struct mlx5_cmd_fc_bulk *b); int mlx5_cmd_fc_bulk_query(struct mlx5_core_dev *dev, struct mlx5_cmd_fc_bulk *b); void mlx5_cmd_fc_bulk_get(struct mlx5_core_dev *dev, - struct mlx5_cmd_fc_bulk *b, u16 id, + struct mlx5_cmd_fc_bulk *b, u32 id, u64 *packets, u64 *bytes); #endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index e8690fe46bf2..5a7bea688ec8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -36,6 +36,7 @@ #include "mlx5_core.h" #include "fs_core.h" #include "fs_cmd.h" +#include "diag/fs_tracepoint.h" #define INIT_TREE_NODE_ARRAY_SIZE(...) (sizeof((struct init_tree_node[]){__VA_ARGS__}) /\ sizeof(struct init_tree_node)) @@ -82,8 +83,8 @@ #define ETHTOOL_PRIO_NUM_LEVELS 1 #define ETHTOOL_NUM_PRIOS 11 #define ETHTOOL_MIN_LEVEL (KERNEL_MIN_LEVEL + ETHTOOL_NUM_PRIOS) -/* Vlan, mac, ttc, aRFS */ -#define KERNEL_NIC_PRIO_NUM_LEVELS 4 +/* Vlan, mac, ttc, inner ttc, aRFS */ +#define KERNEL_NIC_PRIO_NUM_LEVELS 5 #define KERNEL_NIC_NUM_PRIOS 1 /* One more level for tc */ #define KERNEL_MIN_LEVEL (KERNEL_NIC_PRIO_NUM_LEVELS + 1) @@ -150,6 +151,23 @@ enum fs_i_mutex_lock_class { FS_MUTEX_CHILD }; +static const struct rhashtable_params rhash_fte = { + .key_len = FIELD_SIZEOF(struct fs_fte, val), + .key_offset = offsetof(struct fs_fte, val), + .head_offset = offsetof(struct fs_fte, hash), + .automatic_shrinking = true, + .min_size = 1, +}; + +static const struct rhashtable_params rhash_fg = { + .key_len = FIELD_SIZEOF(struct mlx5_flow_group, mask), + .key_offset = offsetof(struct mlx5_flow_group, mask), + .head_offset = offsetof(struct mlx5_flow_group, hash), + .automatic_shrinking = true, + .min_size = 1, + +}; + static void del_rule(struct fs_node *node); static void del_flow_table(struct fs_node *node); static void del_flow_group(struct fs_node *node); @@ -255,71 +273,77 @@ static struct fs_prio *find_prio(struct mlx5_flow_namespace *ns, return NULL; } -static bool masked_memcmp(void *mask, void *val1, void *val2, size_t size) +static bool check_last_reserved(const u32 *match_criteria) { - unsigned int i; + char *match_criteria_reserved = + MLX5_ADDR_OF(fte_match_param, match_criteria, MLX5_FTE_MATCH_PARAM_RESERVED); - for (i = 0; i < size; i++, mask++, val1++, val2++) - if ((*((u8 *)val1) & (*(u8 *)mask)) != - ((*(u8 *)val2) & (*(u8 *)mask))) - return false; - - return true; + return !match_criteria_reserved[0] && + !memcmp(match_criteria_reserved, match_criteria_reserved + 1, + MLX5_FLD_SZ_BYTES(fte_match_param, + MLX5_FTE_MATCH_PARAM_RESERVED) - 1); } -static bool compare_match_value(struct mlx5_flow_group_mask *mask, - void *fte_param1, void *fte_param2) +static bool check_valid_mask(u8 match_criteria_enable, const u32 *match_criteria) { - if (mask->match_criteria_enable & - 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS) { - void *fte_match1 = MLX5_ADDR_OF(fte_match_param, - fte_param1, outer_headers); - void *fte_match2 = MLX5_ADDR_OF(fte_match_param, - fte_param2, outer_headers); - void *fte_mask = MLX5_ADDR_OF(fte_match_param, - mask->match_criteria, outer_headers); + if (match_criteria_enable & ~( + (1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS) | + (1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS) | + (1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS))) + return false; - if (!masked_memcmp(fte_mask, fte_match1, fte_match2, - MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4))) + if (!(match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_OUTER_HEADERS)) { + char *fg_type_mask = MLX5_ADDR_OF(fte_match_param, + match_criteria, outer_headers); + + if (fg_type_mask[0] || + memcmp(fg_type_mask, fg_type_mask + 1, + MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4) - 1)) return false; } - if (mask->match_criteria_enable & - 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS) { - void *fte_match1 = MLX5_ADDR_OF(fte_match_param, - fte_param1, misc_parameters); - void *fte_match2 = MLX5_ADDR_OF(fte_match_param, - fte_param2, misc_parameters); - void *fte_mask = MLX5_ADDR_OF(fte_match_param, - mask->match_criteria, misc_parameters); + if (!(match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_MISC_PARAMETERS)) { + char *fg_type_mask = MLX5_ADDR_OF(fte_match_param, + match_criteria, misc_parameters); - if (!masked_memcmp(fte_mask, fte_match1, fte_match2, - MLX5_ST_SZ_BYTES(fte_match_set_misc))) + if (fg_type_mask[0] || + memcmp(fg_type_mask, fg_type_mask + 1, + MLX5_ST_SZ_BYTES(fte_match_set_misc) - 1)) return false; } - if (mask->match_criteria_enable & - 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS) { - void *fte_match1 = MLX5_ADDR_OF(fte_match_param, - fte_param1, inner_headers); - void *fte_match2 = MLX5_ADDR_OF(fte_match_param, - fte_param2, inner_headers); - void *fte_mask = MLX5_ADDR_OF(fte_match_param, - mask->match_criteria, inner_headers); + if (!(match_criteria_enable & + 1 << MLX5_CREATE_FLOW_GROUP_IN_MATCH_CRITERIA_ENABLE_INNER_HEADERS)) { + char *fg_type_mask = MLX5_ADDR_OF(fte_match_param, + match_criteria, inner_headers); - if (!masked_memcmp(fte_mask, fte_match1, fte_match2, - MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4))) + if (fg_type_mask[0] || + memcmp(fg_type_mask, fg_type_mask + 1, + MLX5_ST_SZ_BYTES(fte_match_set_lyr_2_4) - 1)) return false; } - return true; + + return check_last_reserved(match_criteria); } -static bool compare_match_criteria(u8 match_criteria_enable1, - u8 match_criteria_enable2, - void *mask1, void *mask2) +static bool check_valid_spec(const struct mlx5_flow_spec *spec) { - return match_criteria_enable1 == match_criteria_enable2 && - !memcmp(mask1, mask2, MLX5_ST_SZ_BYTES(fte_match_param)); + int i; + + if (!check_valid_mask(spec->match_criteria_enable, spec->match_criteria)) { + pr_warn("mlx5_core: Match criteria given mismatches match_criteria_enable\n"); + return false; + } + + for (i = 0; i < MLX5_ST_SZ_DW_MATCH_PARAM; i++) + if (spec->match_value[i] & ~spec->match_criteria[i]) { + pr_warn("mlx5_core: match_value differs from match_criteria\n"); + return false; + } + + return check_last_reserved(spec->match_value); } static struct mlx5_flow_root_namespace *find_root(struct fs_node *node) @@ -360,6 +384,8 @@ static void del_flow_table(struct fs_node *node) err = mlx5_cmd_destroy_flow_table(dev, ft); if (err) mlx5_core_warn(dev, "flow steering can't destroy ft\n"); + ida_destroy(&ft->fte_allocator); + rhltable_destroy(&ft->fgs_hash); fs_get_obj(prio, ft->node.parent); prio->num_ft--; } @@ -370,22 +396,16 @@ static void del_rule(struct fs_node *node) struct mlx5_flow_table *ft; struct mlx5_flow_group *fg; struct fs_fte *fte; - u32 *match_value; int modify_mask; struct mlx5_core_dev *dev = get_dev(node); - int match_len = MLX5_ST_SZ_BYTES(fte_match_param); int err; bool update_fte = false; - match_value = kvzalloc(match_len, GFP_KERNEL); - if (!match_value) - return; - fs_get_obj(rule, node); fs_get_obj(fte, rule->node.parent); fs_get_obj(fg, fte->node.parent); - memcpy(match_value, fte->val, sizeof(fte->val)); fs_get_obj(ft, fg->node.parent); + trace_mlx5_fs_del_rule(rule); list_del(&rule->node.list); if (rule->sw_action == MLX5_FLOW_CONTEXT_ACTION_FWD_NEXT_PRIO) { mutex_lock(&rule->dest_attr.ft->lock); @@ -414,7 +434,18 @@ out: "%s can't del rule fg id=%d fte_index=%d\n", __func__, fg->id, fte->index); } - kvfree(match_value); +} + +static void destroy_fte(struct fs_fte *fte, struct mlx5_flow_group *fg) +{ + struct mlx5_flow_table *ft; + int ret; + + ret = rhashtable_remove_fast(&fg->ftes_hash, &fte->hash, rhash_fte); + WARN_ON(ret); + fte->status = 0; + fs_get_obj(ft, fg->node.parent); + ida_simple_remove(&ft->fte_allocator, fte->index); } static void del_fte(struct fs_node *node) @@ -428,6 +459,7 @@ static void del_fte(struct fs_node *node) fs_get_obj(fte, node); fs_get_obj(fg, fte->node.parent); fs_get_obj(ft, fg->node.parent); + trace_mlx5_fs_del_fte(fte); dev = get_dev(&ft->node); err = mlx5_cmd_delete_fte(dev, ft, @@ -437,8 +469,7 @@ static void del_fte(struct fs_node *node) "flow steering can't delete fte in index %d of flow group id %d\n", fte->index, fg->id); - fte->status = 0; - fg->num_ftes--; + destroy_fte(fte, fg); } static void del_flow_group(struct fs_node *node) @@ -446,14 +477,21 @@ static void del_flow_group(struct fs_node *node) struct mlx5_flow_group *fg; struct mlx5_flow_table *ft; struct mlx5_core_dev *dev; + int err; fs_get_obj(fg, node); fs_get_obj(ft, fg->node.parent); dev = get_dev(&ft->node); + trace_mlx5_fs_del_fg(fg); if (ft->autogroup.active) ft->autogroup.num_groups--; + rhashtable_destroy(&fg->ftes_hash); + err = rhltable_remove(&ft->fgs_hash, + &fg->hash, + rhash_fg); + WARN_ON(err); if (mlx5_cmd_destroy_flow_group(dev, ft, fg->id)) mlx5_core_warn(dev, "flow steering can't destroy fg %d of ft %d\n", fg->id, ft->id); @@ -488,10 +526,17 @@ static struct mlx5_flow_group *alloc_flow_group(u32 *create_fg_in) u8 match_criteria_enable = MLX5_GET(create_flow_group_in, create_fg_in, match_criteria_enable); + int ret; + fg = kzalloc(sizeof(*fg), GFP_KERNEL); if (!fg) return ERR_PTR(-ENOMEM); + ret = rhashtable_init(&fg->ftes_hash, &rhash_fte); + if (ret) { + kfree(fg); + return ERR_PTR(ret); + } fg->mask.match_criteria_enable = match_criteria_enable; memcpy(&fg->mask.match_criteria, match_criteria, sizeof(fg->mask.match_criteria)); @@ -509,10 +554,17 @@ static struct mlx5_flow_table *alloc_flow_table(int level, u16 vport, int max_ft u32 flags) { struct mlx5_flow_table *ft; + int ret; ft = kzalloc(sizeof(*ft), GFP_KERNEL); if (!ft) - return NULL; + return ERR_PTR(-ENOMEM); + + ret = rhltable_init(&ft->fgs_hash, &rhash_fg); + if (ret) { + kfree(ft); + return ERR_PTR(ret); + } ft->level = level; ft->node.type = FS_TYPE_FLOW_TABLE; @@ -523,6 +575,7 @@ static struct mlx5_flow_table *alloc_flow_table(int level, u16 vport, int max_ft ft->flags = flags; INIT_LIST_HEAD(&ft->fwd_rules); mutex_init(&ft->lock); + ida_init(&ft->fte_allocator); return ft; } @@ -812,8 +865,8 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa ft_attr->max_fte ? roundup_pow_of_two(ft_attr->max_fte) : 0, root->table_type, op_mod, ft_attr->flags); - if (!ft) { - err = -ENOMEM; + if (IS_ERR(ft)) { + err = PTR_ERR(ft); goto unlock_root; } @@ -839,6 +892,7 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa destroy_ft: mlx5_cmd_destroy_flow_table(root->dev, ft); free_ft: + ida_destroy(&ft->fte_allocator); kfree(ft); unlock_root: mutex_unlock(&root->chain_lock); @@ -924,11 +978,13 @@ static struct mlx5_flow_group *create_flow_group_common(struct mlx5_flow_table * if (IS_ERR(fg)) return fg; + err = rhltable_insert(&ft->fgs_hash, &fg->hash, rhash_fg); + if (err) + goto err_free_fg; + err = mlx5_cmd_create_flow_group(dev, ft, fg_in, &fg->id); - if (err) { - kfree(fg); - return ERR_PTR(err); - } + if (err) + goto err_remove_fg; if (ft->autogroup.active) ft->autogroup.num_groups++; @@ -938,14 +994,33 @@ static struct mlx5_flow_group *create_flow_group_common(struct mlx5_flow_table * /* Add node to group list */ list_add(&fg->node.list, prev_fg); + trace_mlx5_fs_add_fg(fg); return fg; + +err_remove_fg: + WARN_ON(rhltable_remove(&ft->fgs_hash, + &fg->hash, + rhash_fg)); +err_free_fg: + rhashtable_destroy(&fg->ftes_hash); + kfree(fg); + + return ERR_PTR(err); } struct mlx5_flow_group *mlx5_create_flow_group(struct mlx5_flow_table *ft, u32 *fg_in) { + void *match_criteria = MLX5_ADDR_OF(create_flow_group_in, + fg_in, match_criteria); + u8 match_criteria_enable = MLX5_GET(create_flow_group_in, + fg_in, + match_criteria_enable); struct mlx5_flow_group *fg; + if (!check_valid_mask(match_criteria_enable, match_criteria)) + return ERR_PTR(-EINVAL); + if (ft->autogroup.active) return ERR_PTR(-EPERM); @@ -1102,43 +1177,38 @@ free_handle: return ERR_PTR(err); } -/* Assumed fg is locked */ -static unsigned int get_free_fte_index(struct mlx5_flow_group *fg, - struct list_head **prev) -{ - struct fs_fte *fte; - unsigned int start = fg->start_index; - - if (prev) - *prev = &fg->node.children; - - /* assumed list is sorted by index */ - fs_for_each_fte(fte, fg) { - if (fte->index != start) - return start; - start++; - if (prev) - *prev = &fte->node.list; - } - - return start; -} - -/* prev is output, prev->next = new_fte */ static struct fs_fte *create_fte(struct mlx5_flow_group *fg, u32 *match_value, - struct mlx5_flow_act *flow_act, - struct list_head **prev) + struct mlx5_flow_act *flow_act) { + struct mlx5_flow_table *ft; struct fs_fte *fte; int index; + int ret; + + fs_get_obj(ft, fg->node.parent); + index = ida_simple_get(&ft->fte_allocator, fg->start_index, + fg->start_index + fg->max_ftes, + GFP_KERNEL); + if (index < 0) + return ERR_PTR(index); - index = get_free_fte_index(fg, prev); fte = alloc_fte(flow_act, match_value, index); - if (IS_ERR(fte)) - return fte; + if (IS_ERR(fte)) { + ret = PTR_ERR(fte); + goto err_alloc; + } + ret = rhashtable_insert_fast(&fg->ftes_hash, &fte->hash, rhash_fte); + if (ret) + goto err_hash; return fte; + +err_hash: + kfree(fte); +err_alloc: + ida_simple_remove(&ft->fte_allocator, index); + return ERR_PTR(ret); } static struct mlx5_flow_group *create_autogroup(struct mlx5_flow_table *ft, @@ -1226,79 +1296,104 @@ static struct mlx5_flow_rule *find_flow_rule(struct fs_fte *fte, return NULL; } +static bool check_conflicting_actions(u32 action1, u32 action2) +{ + u32 xored_actions = action1 ^ action2; + + /* if one rule only wants to count, it's ok */ + if (action1 == MLX5_FLOW_CONTEXT_ACTION_COUNT || + action2 == MLX5_FLOW_CONTEXT_ACTION_COUNT) + return false; + + if (xored_actions & (MLX5_FLOW_CONTEXT_ACTION_DROP | + MLX5_FLOW_CONTEXT_ACTION_ENCAP | + MLX5_FLOW_CONTEXT_ACTION_DECAP)) + return true; + + return false; +} + +static int check_conflicting_ftes(struct fs_fte *fte, const struct mlx5_flow_act *flow_act) +{ + if (check_conflicting_actions(flow_act->action, fte->action)) { + mlx5_core_warn(get_dev(&fte->node), + "Found two FTEs with conflicting actions\n"); + return -EEXIST; + } + + if (fte->flow_tag != flow_act->flow_tag) { + mlx5_core_warn(get_dev(&fte->node), + "FTE flow tag %u already exists with different flow tag %u\n", + fte->flow_tag, + flow_act->flow_tag); + return -EEXIST; + } + + return 0; +} + static struct mlx5_flow_handle *add_rule_fg(struct mlx5_flow_group *fg, u32 *match_value, struct mlx5_flow_act *flow_act, struct mlx5_flow_destination *dest, - int dest_num) + int dest_num, + struct fs_fte *fte) { struct mlx5_flow_handle *handle; struct mlx5_flow_table *ft; - struct list_head *prev; - struct fs_fte *fte; int i; - nested_lock_ref_node(&fg->node, FS_MUTEX_PARENT); - fs_for_each_fte(fte, fg) { + if (fte) { + int old_action; + int ret; + nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD); - if (compare_match_value(&fg->mask, match_value, &fte->val) && - (flow_act->action & fte->action)) { - int old_action = fte->action; - - if (fte->flow_tag != flow_act->flow_tag) { - mlx5_core_warn(get_dev(&fte->node), - "FTE flow tag %u already exists with different flow tag %u\n", - fte->flow_tag, - flow_act->flow_tag); - handle = ERR_PTR(-EEXIST); - goto unlock_fte; - } + ret = check_conflicting_ftes(fte, flow_act); + if (ret) { + handle = ERR_PTR(ret); + goto unlock_fte; + } - fte->action |= flow_act->action; - handle = add_rule_fte(fte, fg, dest, dest_num, - old_action != flow_act->action); - if (IS_ERR(handle)) { - fte->action = old_action; - goto unlock_fte; - } else { - goto add_rules; - } + old_action = fte->action; + fte->action |= flow_act->action; + handle = add_rule_fte(fte, fg, dest, dest_num, + old_action != flow_act->action); + if (IS_ERR(handle)) { + fte->action = old_action; + goto unlock_fte; + } else { + trace_mlx5_fs_set_fte(fte, false); + goto add_rules; } - unlock_ref_node(&fte->node); } fs_get_obj(ft, fg->node.parent); - if (fg->num_ftes >= fg->max_ftes) { - handle = ERR_PTR(-ENOSPC); - goto unlock_fg; - } - fte = create_fte(fg, match_value, flow_act, &prev); - if (IS_ERR(fte)) { - handle = (void *)fte; - goto unlock_fg; - } + fte = create_fte(fg, match_value, flow_act); + if (IS_ERR(fte)) + return (void *)fte; tree_init_node(&fte->node, 0, del_fte); nested_lock_ref_node(&fte->node, FS_MUTEX_CHILD); handle = add_rule_fte(fte, fg, dest, dest_num, false); if (IS_ERR(handle)) { unlock_ref_node(&fte->node); + destroy_fte(fte, fg); kfree(fte); - goto unlock_fg; + return handle; } - fg->num_ftes++; - tree_add_node(&fte->node, &fg->node); - list_add(&fte->node.list, prev); + /* fte list isn't sorted */ + list_add_tail(&fte->node.list, &fg->node.children); + trace_mlx5_fs_set_fte(fte, true); add_rules: for (i = 0; i < handle->num_rules; i++) { - if (atomic_read(&handle->rule[i]->node.refcount) == 1) + if (atomic_read(&handle->rule[i]->node.refcount) == 1) { tree_add_node(&handle->rule[i]->node, &fte->node); + trace_mlx5_fs_add_rule(handle->rule[i]); + } } unlock_fte: unlock_ref_node(&fte->node); -unlock_fg: - unlock_ref_node(&fg->node); return handle; } @@ -1347,6 +1442,96 @@ static bool dest_is_valid(struct mlx5_flow_destination *dest, } static struct mlx5_flow_handle * +try_add_to_existing_fg(struct mlx5_flow_table *ft, + struct mlx5_flow_spec *spec, + struct mlx5_flow_act *flow_act, + struct mlx5_flow_destination *dest, + int dest_num) +{ + struct mlx5_flow_group *g; + struct mlx5_flow_handle *rule = ERR_PTR(-ENOENT); + struct rhlist_head *tmp, *list; + struct match_list { + struct list_head list; + struct mlx5_flow_group *g; + } match_list, *iter; + LIST_HEAD(match_head); + + rcu_read_lock(); + /* Collect all fgs which has a matching match_criteria */ + list = rhltable_lookup(&ft->fgs_hash, spec, rhash_fg); + rhl_for_each_entry_rcu(g, tmp, list, hash) { + struct match_list *curr_match; + + if (likely(list_empty(&match_head))) { + match_list.g = g; + list_add_tail(&match_list.list, &match_head); + continue; + } + curr_match = kmalloc(sizeof(*curr_match), GFP_ATOMIC); + + if (!curr_match) { + rcu_read_unlock(); + rule = ERR_PTR(-ENOMEM); + goto free_list; + } + curr_match->g = g; + list_add_tail(&curr_match->list, &match_head); + } + rcu_read_unlock(); + + /* Try to find a fg that already contains a matching fte */ + list_for_each_entry(iter, &match_head, list) { + struct fs_fte *fte; + + g = iter->g; + nested_lock_ref_node(&g->node, FS_MUTEX_PARENT); + fte = rhashtable_lookup_fast(&g->ftes_hash, spec->match_value, + rhash_fte); + if (fte) { + rule = add_rule_fg(g, spec->match_value, + flow_act, dest, dest_num, fte); + unlock_ref_node(&g->node); + goto free_list; + } + unlock_ref_node(&g->node); + } + + /* No group with matching fte found. Try to add a new fte to any + * matching fg. + */ + list_for_each_entry(iter, &match_head, list) { + g = iter->g; + + nested_lock_ref_node(&g->node, FS_MUTEX_PARENT); + rule = add_rule_fg(g, spec->match_value, + flow_act, dest, dest_num, NULL); + if (!IS_ERR(rule) || PTR_ERR(rule) != -ENOSPC) { + unlock_ref_node(&g->node); + goto free_list; + } + unlock_ref_node(&g->node); + } + +free_list: + if (!list_empty(&match_head)) { + struct match_list *match_tmp; + + /* The most common case is having one FG. Since we want to + * optimize this case, we save the first on the stack. + * Therefore, no need to free it. + */ + list_del(&list_first_entry(&match_head, typeof(*iter), list)->list); + list_for_each_entry_safe(iter, match_tmp, &match_head, list) { + list_del(&iter->list); + kfree(iter); + } + } + + return rule; +} + +static struct mlx5_flow_handle * _mlx5_add_flow_rules(struct mlx5_flow_table *ft, struct mlx5_flow_spec *spec, struct mlx5_flow_act *flow_act, @@ -1358,22 +1543,18 @@ _mlx5_add_flow_rules(struct mlx5_flow_table *ft, struct mlx5_flow_handle *rule; int i; + if (!check_valid_spec(spec)) + return ERR_PTR(-EINVAL); + for (i = 0; i < dest_num; i++) { if (!dest_is_valid(&dest[i], flow_act->action, ft)) return ERR_PTR(-EINVAL); } nested_lock_ref_node(&ft->node, FS_MUTEX_GRANDPARENT); - fs_for_each_fg(g, ft) - if (compare_match_criteria(g->mask.match_criteria_enable, - spec->match_criteria_enable, - g->mask.match_criteria, - spec->match_criteria)) { - rule = add_rule_fg(g, spec->match_value, - flow_act, dest, dest_num); - if (!IS_ERR(rule) || PTR_ERR(rule) != -ENOSPC) - goto unlock; - } + rule = try_add_to_existing_fg(ft, spec, flow_act, dest, dest_num); + if (!IS_ERR(rule)) + goto unlock; g = create_autogroup(ft, spec->match_criteria_enable, spec->match_criteria); @@ -1382,7 +1563,8 @@ _mlx5_add_flow_rules(struct mlx5_flow_table *ft, goto unlock; } - rule = add_rule_fg(g, spec->match_value, flow_act, dest, dest_num); + rule = add_rule_fg(g, spec->match_value, flow_act, dest, + dest_num, NULL); if (IS_ERR(rule)) { /* Remove assumes refcount > 0 and autogroup creates a group * with a refcount = 0. diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h index 990acee6fb09..5509a752f98e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.h @@ -34,6 +34,7 @@ #define _MLX5_FS_CORE_ #include <linux/mlx5/fs.h> +#include <linux/rhashtable.h> enum fs_node_type { FS_TYPE_NAMESPACE, @@ -118,6 +119,8 @@ struct mlx5_flow_table { /* FWD rules that point on this flow table */ struct list_head fwd_rules; u32 flags; + struct ida fte_allocator; + struct rhltable fgs_hash; }; struct mlx5_fc_cache { @@ -136,17 +139,29 @@ struct mlx5_fc { u64 lastpackets; u64 lastbytes; - u16 id; + u32 id; bool deleted; bool aging; struct mlx5_fc_cache cache ____cacheline_aligned_in_smp; }; +#define MLX5_FTE_MATCH_PARAM_RESERVED reserved_at_600 +/* Calculate the fte_match_param length and without the reserved length. + * Make sure the reserved field is the last. + */ +#define MLX5_ST_SZ_DW_MATCH_PARAM \ + ((MLX5_BYTE_OFF(fte_match_param, MLX5_FTE_MATCH_PARAM_RESERVED) / sizeof(u32)) + \ + BUILD_BUG_ON_ZERO(MLX5_ST_SZ_BYTES(fte_match_param) != \ + MLX5_FLD_SZ_BYTES(fte_match_param, \ + MLX5_FTE_MATCH_PARAM_RESERVED) +\ + MLX5_BYTE_OFF(fte_match_param, \ + MLX5_FTE_MATCH_PARAM_RESERVED))) + /* Type of children is mlx5_flow_rule */ struct fs_fte { struct fs_node node; - u32 val[MLX5_ST_SZ_DW(fte_match_param)]; + u32 val[MLX5_ST_SZ_DW_MATCH_PARAM]; u32 dests_size; u32 flow_tag; u32 index; @@ -155,6 +170,7 @@ struct fs_fte { u32 modify_id; enum fs_fte_status status; struct mlx5_fc *counter; + struct rhash_head hash; }; /* Type of children is mlx5_flow_table/namespace */ @@ -174,7 +190,7 @@ struct mlx5_flow_namespace { struct mlx5_flow_group_mask { u8 match_criteria_enable; - u32 match_criteria[MLX5_ST_SZ_DW(fte_match_param)]; + u32 match_criteria[MLX5_ST_SZ_DW_MATCH_PARAM]; }; /* Type of children is fs_fte */ @@ -183,8 +199,9 @@ struct mlx5_flow_group { struct mlx5_flow_group_mask mask; u32 start_index; u32 max_ftes; - u32 num_ftes; u32 id; + struct rhashtable ftes_hash; + struct rhlist_head hash; }; struct mlx5_flow_root_namespace { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c index 6507d8acc54d..89d1f8650033 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_counters.c @@ -38,6 +38,8 @@ #include "fs_cmd.h" #define MLX5_FC_STATS_PERIOD msecs_to_jiffies(1000) +/* Max number of counters to query in bulk read is 32K */ +#define MLX5_SW_MAX_COUNTERS_BULK BIT(15) /* locking scheme: * @@ -90,16 +92,21 @@ static void mlx5_fc_stats_insert(struct rb_root *root, struct mlx5_fc *counter) rb_insert_color(&counter->node, root); } +/* The function returns the last node that was queried so the caller + * function can continue calling it till all counters are queried. + */ static struct rb_node *mlx5_fc_stats_query(struct mlx5_core_dev *dev, struct mlx5_fc *first, - u16 last_id) + u32 last_id) { struct mlx5_cmd_fc_bulk *b; struct rb_node *node = NULL; - u16 afirst_id; + u32 afirst_id; int num; int err; - int max_bulk = 1 << MLX5_CAP_GEN(dev, log_max_flow_counter_bulk); + + int max_bulk = min_t(int, MLX5_SW_MAX_COUNTERS_BULK, + (1 << MLX5_CAP_GEN(dev, log_max_flow_counter_bulk))); /* first id must be aligned to 4 when using bulk query */ afirst_id = first->id & ~0x3; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c index eb04e97d8765..43c126c63955 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c @@ -39,10 +39,11 @@ static void mlx5i_get_drvinfo(struct net_device *dev, struct mlx5e_priv *priv = mlx5i_epriv(dev); mlx5e_ethtool_get_drvinfo(priv, drvinfo); + strlcpy(drvinfo->driver, DRIVER_NAME "[ib_ipoib]", + sizeof(drvinfo->driver)); } -static void mlx5i_get_strings(struct net_device *dev, - uint32_t stringset, uint8_t *data) +static void mlx5i_get_strings(struct net_device *dev, u32 stringset, u8 *data) { struct mlx5e_priv *priv = mlx5i_epriv(dev); @@ -129,17 +130,123 @@ static int mlx5i_flash_device(struct net_device *netdev, return mlx5e_ethtool_flash_device(priv, flash); } +enum mlx5_ptys_width { + MLX5_PTYS_WIDTH_1X = 1 << 0, + MLX5_PTYS_WIDTH_2X = 1 << 1, + MLX5_PTYS_WIDTH_4X = 1 << 2, + MLX5_PTYS_WIDTH_8X = 1 << 3, + MLX5_PTYS_WIDTH_12X = 1 << 4, +}; + +static inline int mlx5_ptys_width_enum_to_int(enum mlx5_ptys_width width) +{ + switch (width) { + case MLX5_PTYS_WIDTH_1X: return 1; + case MLX5_PTYS_WIDTH_2X: return 2; + case MLX5_PTYS_WIDTH_4X: return 4; + case MLX5_PTYS_WIDTH_8X: return 8; + case MLX5_PTYS_WIDTH_12X: return 12; + default: return -1; + } +} + +enum mlx5_ptys_rate { + MLX5_PTYS_RATE_SDR = 1 << 0, + MLX5_PTYS_RATE_DDR = 1 << 1, + MLX5_PTYS_RATE_QDR = 1 << 2, + MLX5_PTYS_RATE_FDR10 = 1 << 3, + MLX5_PTYS_RATE_FDR = 1 << 4, + MLX5_PTYS_RATE_EDR = 1 << 5, + MLX5_PTYS_RATE_HDR = 1 << 6, +}; + +static inline int mlx5_ptys_rate_enum_to_int(enum mlx5_ptys_rate rate) +{ + switch (rate) { + case MLX5_PTYS_RATE_SDR: return 2500; + case MLX5_PTYS_RATE_DDR: return 5000; + case MLX5_PTYS_RATE_QDR: + case MLX5_PTYS_RATE_FDR10: return 10000; + case MLX5_PTYS_RATE_FDR: return 14000; + case MLX5_PTYS_RATE_EDR: return 25000; + case MLX5_PTYS_RATE_HDR: return 50000; + default: return -1; + } +} + +static int mlx5i_get_port_settings(struct net_device *netdev, + u16 *ib_link_width_oper, u16 *ib_proto_oper) +{ + struct mlx5e_priv *priv = mlx5i_epriv(netdev); + struct mlx5_core_dev *mdev = priv->mdev; + u32 out[MLX5_ST_SZ_DW(ptys_reg)] = {0}; + int ret; + + ret = mlx5_query_port_ptys(mdev, out, sizeof(out), MLX5_PTYS_IB, 1); + if (ret) + return ret; + + *ib_link_width_oper = MLX5_GET(ptys_reg, out, ib_link_width_oper); + *ib_proto_oper = MLX5_GET(ptys_reg, out, ib_proto_oper); + + return 0; +} + +static int mlx5i_get_speed_settings(u16 ib_link_width_oper, u16 ib_proto_oper) +{ + int rate, width; + + rate = mlx5_ptys_rate_enum_to_int(ib_proto_oper); + if (rate < 0) + return -EINVAL; + width = mlx5_ptys_width_enum_to_int(ib_link_width_oper); + if (width < 0) + return -EINVAL; + + return rate * width; +} + +static int mlx5i_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *link_ksettings) +{ + u16 ib_link_width_oper; + u16 ib_proto_oper; + int speed, ret; + + ret = mlx5i_get_port_settings(netdev, &ib_link_width_oper, &ib_proto_oper); + if (ret) + return ret; + + ethtool_link_ksettings_zero_link_mode(link_ksettings, supported); + ethtool_link_ksettings_zero_link_mode(link_ksettings, advertising); + + speed = mlx5i_get_speed_settings(ib_link_width_oper, ib_proto_oper); + if (speed < 0) + return -EINVAL; + + link_ksettings->base.duplex = DUPLEX_FULL; + link_ksettings->base.port = PORT_OTHER; + + link_ksettings->base.autoneg = AUTONEG_DISABLE; + + link_ksettings->base.speed = speed; + + return 0; +} + const struct ethtool_ops mlx5i_ethtool_ops = { - .get_drvinfo = mlx5i_get_drvinfo, - .get_strings = mlx5i_get_strings, - .get_sset_count = mlx5i_get_sset_count, - .get_ethtool_stats = mlx5i_get_ethtool_stats, - .get_ringparam = mlx5i_get_ringparam, - .set_ringparam = mlx5i_set_ringparam, - .flash_device = mlx5i_flash_device, - .get_channels = mlx5i_get_channels, - .set_channels = mlx5i_set_channels, - .get_coalesce = mlx5i_get_coalesce, - .set_coalesce = mlx5i_set_coalesce, - .get_ts_info = mlx5i_get_ts_info, + .get_drvinfo = mlx5i_get_drvinfo, + .get_strings = mlx5i_get_strings, + .get_sset_count = mlx5i_get_sset_count, + .get_ethtool_stats = mlx5i_get_ethtool_stats, + .get_ringparam = mlx5i_get_ringparam, + .set_ringparam = mlx5i_set_ringparam, + .flash_device = mlx5i_flash_device, + .get_channels = mlx5i_get_channels, + .set_channels = mlx5i_set_channels, + .get_coalesce = mlx5i_get_coalesce, + .set_coalesce = mlx5i_set_coalesce, + .get_ts_info = mlx5i_get_ts_info, + .get_link_ksettings = mlx5i_get_link_ksettings, + .get_link = ethtool_op_get_link, }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c b/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c new file mode 100644 index 000000000000..7cb67122e8b5 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2017, Mellanox Technologies. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/etherdevice.h> +#include <linux/mlx5/driver.h> +#include <linux/mlx5/mlx5_ifc.h> +#include "mlx5_core.h" +#include "lib/mpfs.h" + +/* HW L2 Table (MPFS) management */ +static int set_l2table_entry_cmd(struct mlx5_core_dev *dev, u32 index, u8 *mac) +{ + u32 in[MLX5_ST_SZ_DW(set_l2_table_entry_in)] = {0}; + u32 out[MLX5_ST_SZ_DW(set_l2_table_entry_out)] = {0}; + u8 *in_mac_addr; + + MLX5_SET(set_l2_table_entry_in, in, opcode, MLX5_CMD_OP_SET_L2_TABLE_ENTRY); + MLX5_SET(set_l2_table_entry_in, in, table_index, index); + + in_mac_addr = MLX5_ADDR_OF(set_l2_table_entry_in, in, mac_address); + ether_addr_copy(&in_mac_addr[2], mac); + + return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); +} + +static int del_l2table_entry_cmd(struct mlx5_core_dev *dev, u32 index) +{ + u32 in[MLX5_ST_SZ_DW(delete_l2_table_entry_in)] = {0}; + u32 out[MLX5_ST_SZ_DW(delete_l2_table_entry_out)] = {0}; + + MLX5_SET(delete_l2_table_entry_in, in, opcode, MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY); + MLX5_SET(delete_l2_table_entry_in, in, table_index, index); + return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out)); +} + +/* UC L2 table hash node */ +struct l2table_node { + struct l2addr_node node; + u32 index; /* index in HW l2 table */ +}; + +struct mlx5_mpfs { + struct hlist_head hash[MLX5_L2_ADDR_HASH_SIZE]; + struct mutex lock; /* Synchronize l2 table access */ + u32 size; + unsigned long *bitmap; +}; + +static int alloc_l2table_index(struct mlx5_mpfs *l2table, u32 *ix) +{ + int err = 0; + + *ix = find_first_zero_bit(l2table->bitmap, l2table->size); + if (*ix >= l2table->size) + err = -ENOSPC; + else + __set_bit(*ix, l2table->bitmap); + + return err; +} + +static void free_l2table_index(struct mlx5_mpfs *l2table, u32 ix) +{ + __clear_bit(ix, l2table->bitmap); +} + +int mlx5_mpfs_init(struct mlx5_core_dev *dev) +{ + int l2table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table); + struct mlx5_mpfs *mpfs; + + if (!MLX5_VPORT_MANAGER(dev)) + return 0; + + mpfs = kzalloc(sizeof(*mpfs), GFP_KERNEL); + if (!mpfs) + return -ENOMEM; + + mutex_init(&mpfs->lock); + mpfs->size = l2table_size; + mpfs->bitmap = kcalloc(BITS_TO_LONGS(l2table_size), + sizeof(uintptr_t), GFP_KERNEL); + if (!mpfs->bitmap) { + kfree(mpfs); + return -ENOMEM; + } + + dev->priv.mpfs = mpfs; + return 0; +} + +void mlx5_mpfs_cleanup(struct mlx5_core_dev *dev) +{ + struct mlx5_mpfs *mpfs = dev->priv.mpfs; + + if (!MLX5_VPORT_MANAGER(dev)) + return; + + WARN_ON(!hlist_empty(mpfs->hash)); + kfree(mpfs->bitmap); + kfree(mpfs); +} + +int mlx5_mpfs_add_mac(struct mlx5_core_dev *dev, u8 *mac) +{ + struct mlx5_mpfs *mpfs = dev->priv.mpfs; + struct l2table_node *l2addr; + u32 index; + int err; + + if (!MLX5_VPORT_MANAGER(dev)) + return 0; + + mutex_lock(&mpfs->lock); + + l2addr = l2addr_hash_find(mpfs->hash, mac, struct l2table_node); + if (l2addr) { + err = -EEXIST; + goto abort; + } + + err = alloc_l2table_index(mpfs, &index); + if (err) + goto abort; + + l2addr = l2addr_hash_add(mpfs->hash, mac, struct l2table_node, GFP_KERNEL); + if (!l2addr) { + free_l2table_index(mpfs, index); + err = -ENOMEM; + goto abort; + } + + l2addr->index = index; + err = set_l2table_entry_cmd(dev, index, mac); + if (err) { + l2addr_hash_del(l2addr); + free_l2table_index(mpfs, index); + } + + mlx5_core_dbg(dev, "MPFS mac added %pM, index (%d)\n", mac, index); +abort: + mutex_unlock(&mpfs->lock); + return err; +} + +int mlx5_mpfs_del_mac(struct mlx5_core_dev *dev, u8 *mac) +{ + struct mlx5_mpfs *mpfs = dev->priv.mpfs; + struct l2table_node *l2addr; + int err = 0; + u32 index; + + if (!MLX5_VPORT_MANAGER(dev)) + return 0; + + mutex_lock(&mpfs->lock); + + l2addr = l2addr_hash_find(mpfs->hash, mac, struct l2table_node); + if (!l2addr) { + err = -ENOENT; + goto unlock; + } + + index = l2addr->index; + del_l2table_entry_cmd(dev, index); + l2addr_hash_del(l2addr); + free_l2table_index(mpfs, index); + mlx5_core_dbg(dev, "MPFS mac deleted %pM, index (%d)\n", mac, index); +unlock: + mutex_unlock(&mpfs->lock); + return err; +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.h b/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.h new file mode 100644 index 000000000000..4a7b2c3203a7 --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlx5/core/lib/mpfs.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2017, Mellanox Technologies, Ltd. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __MLX5_MPFS_H__ +#define __MLX5_MPFS_H__ + +#include <linux/if_ether.h> +#include <linux/mlx5/device.h> + +/* L2 -mac address based- hash helpers */ +#define MLX5_L2_ADDR_HASH_SIZE (BIT(BITS_PER_BYTE)) +#define MLX5_L2_ADDR_HASH(addr) (addr[5]) + +struct l2addr_node { + struct hlist_node hlist; + u8 addr[ETH_ALEN]; +}; + +#define for_each_l2hash_node(hn, tmp, hash, i) \ + for (i = 0; i < MLX5_L2_ADDR_HASH_SIZE; i++) \ + hlist_for_each_entry_safe(hn, tmp, &(hash)[i], hlist) + +#define l2addr_hash_find(hash, mac, type) ({ \ + int ix = MLX5_L2_ADDR_HASH(mac); \ + bool found = false; \ + type *ptr = NULL; \ + \ + hlist_for_each_entry(ptr, &(hash)[ix], node.hlist) \ + if (ether_addr_equal(ptr->node.addr, mac)) {\ + found = true; \ + break; \ + } \ + if (!found) \ + ptr = NULL; \ + ptr; \ +}) + +#define l2addr_hash_add(hash, mac, type, gfp) ({ \ + int ix = MLX5_L2_ADDR_HASH(mac); \ + type *ptr = NULL; \ + \ + ptr = kzalloc(sizeof(type), gfp); \ + if (ptr) { \ + ether_addr_copy(ptr->node.addr, mac); \ + hlist_add_head(&ptr->node.hlist, &(hash)[ix]);\ + } \ + ptr; \ +}) + +#define l2addr_hash_del(ptr) ({ \ + hlist_del(&(ptr)->node.hlist); \ + kfree(ptr); \ +}) + +#ifdef CONFIG_MLX5_MPFS +int mlx5_mpfs_init(struct mlx5_core_dev *dev); +void mlx5_mpfs_cleanup(struct mlx5_core_dev *dev); +int mlx5_mpfs_add_mac(struct mlx5_core_dev *dev, u8 *mac); +int mlx5_mpfs_del_mac(struct mlx5_core_dev *dev, u8 *mac); +#else /* #ifndef CONFIG_MLX5_MPFS */ +static inline int mlx5_mpfs_init(struct mlx5_core_dev *dev) { return 0; } +static inline void mlx5_mpfs_cleanup(struct mlx5_core_dev *dev) {} +static inline int mlx5_mpfs_add_mac(struct mlx5_core_dev *dev, u8 *mac) { return 0; } +static inline int mlx5_mpfs_del_mac(struct mlx5_core_dev *dev, u8 *mac) { return 0; } +#endif +#endif diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index 8c4b45ef539c..0d2c8dcd6eae 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -54,9 +54,8 @@ #include <net/devlink.h> #include "mlx5_core.h" #include "fs_core.h" -#ifdef CONFIG_MLX5_CORE_EN +#include "lib/mpfs.h" #include "eswitch.h" -#endif #include "lib/mlx5.h" #include "fpga/core.h" #include "accel/ipsec.h" @@ -788,7 +787,6 @@ static int mlx5_core_set_issi(struct mlx5_core_dev *dev) return -EOPNOTSUPP; } - static int mlx5_pci_init(struct mlx5_core_dev *dev, struct mlx5_priv *priv) { struct pci_dev *pdev = dev->pdev; @@ -897,13 +895,17 @@ static int mlx5_init_once(struct mlx5_core_dev *dev, struct mlx5_priv *priv) goto err_tables_cleanup; } -#ifdef CONFIG_MLX5_CORE_EN + err = mlx5_mpfs_init(dev); + if (err) { + dev_err(&pdev->dev, "Failed to init l2 table %d\n", err); + goto err_rl_cleanup; + } + err = mlx5_eswitch_init(dev); if (err) { dev_err(&pdev->dev, "Failed to init eswitch %d\n", err); - goto err_rl_cleanup; + goto err_mpfs_cleanup; } -#endif err = mlx5_sriov_init(dev); if (err) { @@ -922,13 +924,11 @@ static int mlx5_init_once(struct mlx5_core_dev *dev, struct mlx5_priv *priv) err_sriov_cleanup: mlx5_sriov_cleanup(dev); err_eswitch_cleanup: -#ifdef CONFIG_MLX5_CORE_EN mlx5_eswitch_cleanup(dev->priv.eswitch); - +err_mpfs_cleanup: + mlx5_mpfs_cleanup(dev); err_rl_cleanup: -#endif mlx5_cleanup_rl_table(dev); - err_tables_cleanup: mlx5_cleanup_mkey_table(dev); mlx5_cleanup_srq_table(dev); @@ -946,9 +946,8 @@ static void mlx5_cleanup_once(struct mlx5_core_dev *dev) { mlx5_fpga_cleanup(dev); mlx5_sriov_cleanup(dev); -#ifdef CONFIG_MLX5_CORE_EN mlx5_eswitch_cleanup(dev->priv.eswitch); -#endif + mlx5_mpfs_cleanup(dev); mlx5_cleanup_rl_table(dev); mlx5_cleanup_reserved_gids(dev); mlx5_cleanup_mkey_table(dev); @@ -1106,10 +1105,6 @@ static int mlx5_load_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv, goto err_fs; } -#ifdef CONFIG_MLX5_CORE_EN - mlx5_eswitch_attach(dev->priv.eswitch); -#endif - err = mlx5_sriov_attach(dev); if (err) { dev_err(&pdev->dev, "sriov init failed %d\n", err); @@ -1152,9 +1147,6 @@ err_fpga_start: mlx5_sriov_detach(dev); err_sriov: -#ifdef CONFIG_MLX5_CORE_EN - mlx5_eswitch_detach(dev->priv.eswitch); -#endif mlx5_cleanup_fs(dev); err_fs: @@ -1225,9 +1217,6 @@ static int mlx5_unload_one(struct mlx5_core_dev *dev, struct mlx5_priv *priv, mlx5_fpga_device_stop(dev); mlx5_sriov_detach(dev); -#ifdef CONFIG_MLX5_CORE_EN - mlx5_eswitch_detach(dev->priv.eswitch); -#endif mlx5_cleanup_fs(dev); free_comp_eqs(dev); mlx5_stop_eqs(dev); @@ -1258,7 +1247,7 @@ struct mlx5_core_event_handler { }; static const struct devlink_ops mlx5_devlink_ops = { -#ifdef CONFIG_MLX5_CORE_EN +#ifdef CONFIG_MLX5_ESWITCH .eswitch_mode_set = mlx5_devlink_eswitch_mode_set, .eswitch_mode_get = mlx5_devlink_eswitch_mode_get, .eswitch_inline_mode_set = mlx5_devlink_eswitch_inline_mode_set, @@ -1298,6 +1287,9 @@ static int init_one(struct pci_dev *pdev, mutex_init(&dev->pci_status_mutex); mutex_init(&dev->intf_state_mutex); + INIT_LIST_HEAD(&priv->waiting_events_list); + priv->is_accum_events = false; + #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING err = init_srcu_struct(&priv->pfault_srcu); if (err) { @@ -1352,7 +1344,6 @@ clean_srcu: cleanup_srcu_struct(&priv->pfault_srcu); clean_dev: #endif - pci_set_drvdata(pdev, NULL); devlink_free(devlink); return err; @@ -1379,7 +1370,6 @@ static void remove_one(struct pci_dev *pdev) #ifdef CONFIG_INFINIBAND_ON_DEMAND_PAGING cleanup_srcu_struct(&priv->pfault_srcu); #endif - pci_set_drvdata(pdev, NULL); devlink_free(devlink); } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h index 01d637dac533..b7c2900b75f9 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/mlx5_core.h @@ -43,6 +43,10 @@ #define DRIVER_VERSION "5.0-0" #define MLX5_TOTAL_VPORTS(mdev) (1 + pci_sriov_get_totalvfs(mdev->pdev)) +#define MLX5_VPORT_MANAGER(mdev) \ + (MLX5_CAP_GEN(mdev, vport_group_manager) && \ + (MLX5_CAP_GEN(mdev, port_type) == MLX5_CAP_PORT_TYPE_ETH) && \ + mlx5_core_is_pf(mdev)) extern uint mlx5_core_debug_mask; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c index 28d8472b36f1..6c48e9959b65 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sriov.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sriov.c @@ -34,9 +34,7 @@ #include <linux/mlx5/driver.h> #include <linux/mlx5/vport.h> #include "mlx5_core.h" -#ifdef CONFIG_MLX5_CORE_EN #include "eswitch.h" -#endif bool mlx5_sriov_is_enabled(struct mlx5_core_dev *dev) { @@ -90,14 +88,12 @@ static int mlx5_device_enable_sriov(struct mlx5_core_dev *dev, int num_vfs) return -EBUSY; } -#ifdef CONFIG_MLX5_CORE_EN err = mlx5_eswitch_enable_sriov(dev->priv.eswitch, num_vfs, SRIOV_LEGACY); if (err) { mlx5_core_warn(dev, "failed to enable eswitch SRIOV (%d)\n", err); return err; } -#endif for (vf = 0; vf < num_vfs; vf++) { err = mlx5_core_enable_hca(dev, vf + 1); @@ -117,7 +113,6 @@ static int mlx5_device_enable_sriov(struct mlx5_core_dev *dev, int num_vfs) } } mlx5_core_dbg(dev, "successfully enabled VF* %d\n", vf); - } return 0; @@ -130,11 +125,7 @@ static void mlx5_device_disable_sriov(struct mlx5_core_dev *dev) int vf; if (!sriov->enabled_vfs) -#ifdef CONFIG_MLX5_CORE_EN - goto disable_sriov_resources; -#else - return; -#endif + goto out; for (vf = 0; vf < sriov->num_vfs; vf++) { if (!sriov->vfs_ctx[vf].enabled) @@ -148,10 +139,8 @@ static void mlx5_device_disable_sriov(struct mlx5_core_dev *dev) sriov->enabled_vfs--; } -#ifdef CONFIG_MLX5_CORE_EN -disable_sriov_resources: +out: mlx5_eswitch_disable_sriov(dev->priv.eswitch); -#endif if (mlx5_wait_for_vf_pages(dev)) mlx5_core_warn(dev, "timeout reclaiming VFs pages\n"); diff --git a/drivers/net/ethernet/mellanox/mlxsw/Kconfig b/drivers/net/ethernet/mellanox/mlxsw/Kconfig index 695adff89d71..d56eea310509 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Kconfig +++ b/drivers/net/ethernet/mellanox/mlxsw/Kconfig @@ -75,6 +75,7 @@ config MLXSW_SPECTRUM depends on MLXSW_CORE && MLXSW_PCI && NET_SWITCHDEV && VLAN_8021Q depends on PSAMPLE || PSAMPLE=n depends on BRIDGE || BRIDGE=n + depends on IPV6 || IPV6=n select PARMAN select MLXFW default m diff --git a/drivers/net/ethernet/mellanox/mlxsw/Makefile b/drivers/net/ethernet/mellanox/mlxsw/Makefile index 62fc42f396bb..891ff418bb5e 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/Makefile +++ b/drivers/net/ethernet/mellanox/mlxsw/Makefile @@ -16,8 +16,9 @@ mlxsw_spectrum-objs := spectrum.o spectrum_buffers.o \ spectrum_switchdev.o spectrum_router.o \ spectrum_kvdl.o spectrum_acl_tcam.o \ spectrum_acl.o spectrum_flower.o \ - spectrum_cnt.o spectrum_dpipe.o \ - spectrum_fid.o + spectrum_cnt.o spectrum_fid.o \ + spectrum_ipip.o mlxsw_spectrum-$(CONFIG_MLXSW_SPECTRUM_DCB) += spectrum_dcb.o +mlxsw_spectrum-$(CONFIG_NET_DEVLINK) += spectrum_dpipe.o obj-$(CONFIG_MLXSW_MINIMAL) += mlxsw_minimal.o mlxsw_minimal-objs := minimal.o diff --git a/drivers/net/ethernet/mellanox/mlxsw/core.c b/drivers/net/ethernet/mellanox/mlxsw/core.c index affe84eb4bff..9d5e7cf288be 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core.c +++ b/drivers/net/ethernet/mellanox/mlxsw/core.c @@ -667,7 +667,7 @@ static int mlxsw_emad_reg_access(struct mlxsw_core *mlxsw_core, int err; dev_dbg(mlxsw_core->bus_info->dev, "EMAD reg access (tid=%llx,reg_id=%x(%s),type=%s)\n", - trans->tid, reg->id, mlxsw_reg_id_str(reg->id), + tid, reg->id, mlxsw_reg_id_str(reg->id), mlxsw_core_reg_access_type_str(type)); skb = mlxsw_emad_alloc(mlxsw_core, reg->len); diff --git a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h index 9807ef814e42..f6963b0b4a55 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h +++ b/drivers/net/ethernet/mellanox/mlxsw/core_acl_flex_keys.h @@ -57,6 +57,9 @@ enum mlxsw_afk_element { MLXSW_AFK_ELEMENT_VID, MLXSW_AFK_ELEMENT_PCP, MLXSW_AFK_ELEMENT_TCP_FLAGS, + MLXSW_AFK_ELEMENT_IP_TTL_, + MLXSW_AFK_ELEMENT_IP_ECN, + MLXSW_AFK_ELEMENT_IP_DSCP, MLXSW_AFK_ELEMENT_MAX, }; @@ -104,6 +107,9 @@ static const struct mlxsw_afk_element_info mlxsw_afk_element_infos[] = { MLXSW_AFK_ELEMENT_INFO_U32(VID, 0x10, 8, 12), MLXSW_AFK_ELEMENT_INFO_U32(PCP, 0x10, 20, 3), MLXSW_AFK_ELEMENT_INFO_U32(TCP_FLAGS, 0x10, 23, 9), + MLXSW_AFK_ELEMENT_INFO_U32(IP_TTL_, 0x14, 0, 8), + MLXSW_AFK_ELEMENT_INFO_U32(IP_ECN, 0x14, 9, 2), + MLXSW_AFK_ELEMENT_INFO_U32(IP_DSCP, 0x14, 11, 6), MLXSW_AFK_ELEMENT_INFO_U32(SRC_IP4, 0x18, 0, 32), MLXSW_AFK_ELEMENT_INFO_U32(DST_IP4, 0x1C, 0, 32), MLXSW_AFK_ELEMENT_INFO_BUF(SRC_IP6_HI, 0x18, 8), diff --git a/drivers/net/ethernet/mellanox/mlxsw/reg.h b/drivers/net/ethernet/mellanox/mlxsw/reg.h index 1bd34d9a7b9e..cc27c5de5a1d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/reg.h +++ b/drivers/net/ethernet/mellanox/mlxsw/reg.h @@ -5,6 +5,7 @@ * Copyright (c) 2015 Elad Raz <eladr@mellanox.com> * Copyright (c) 2015-2017 Jiri Pirko <jiri@mellanox.com> * Copyright (c) 2016 Yotam Gigi <yotamg@mellanox.com> + * Copyright (c) 2017 Petr Machata <petrm@mellanox.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -3679,15 +3680,17 @@ enum mlxsw_reg_htgt_trap_group { MLXSW_REG_HTGT_TRAP_GROUP_SP_LACP, MLXSW_REG_HTGT_TRAP_GROUP_SP_LLDP, MLXSW_REG_HTGT_TRAP_GROUP_SP_IGMP, - MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP_IPV4, + MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP, MLXSW_REG_HTGT_TRAP_GROUP_SP_OSPF, MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP, - MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP_MISS, + MLXSW_REG_HTGT_TRAP_GROUP_SP_HOST_MISS, MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP, MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE, MLXSW_REG_HTGT_TRAP_GROUP_SP_IP2ME, MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP, MLXSW_REG_HTGT_TRAP_GROUP_SP_EVENT, + MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_MLD, + MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND, }; /* reg_htgt_trap_group @@ -3952,10 +3955,12 @@ MLXSW_ITEM32(reg, rgcr, pcp_rw, 0x18, 16, 2); */ MLXSW_ITEM32(reg, rgcr, activity_dis, 0x20, 0, 8); -static inline void mlxsw_reg_rgcr_pack(char *payload, bool ipv4_en) +static inline void mlxsw_reg_rgcr_pack(char *payload, bool ipv4_en, + bool ipv6_en) { MLXSW_REG_ZERO(rgcr, payload); mlxsw_reg_rgcr_ipv4_en_set(payload, ipv4_en); + mlxsw_reg_rgcr_ipv6_en_set(payload, ipv6_en); } /* RITR - Router Interface Table Register @@ -3988,16 +3993,18 @@ MLXSW_ITEM32(reg, ritr, ipv4, 0x00, 29, 1); MLXSW_ITEM32(reg, ritr, ipv6, 0x00, 28, 1); enum mlxsw_reg_ritr_if_type { + /* VLAN interface. */ MLXSW_REG_RITR_VLAN_IF, + /* FID interface. */ MLXSW_REG_RITR_FID_IF, + /* Sub-port interface. */ MLXSW_REG_RITR_SP_IF, + /* Loopback Interface. */ + MLXSW_REG_RITR_LOOPBACK_IF, }; /* reg_ritr_type - * Router interface type. - * 0 - VLAN interface. - * 1 - FID interface. - * 2 - Sub-port interface. + * Router interface type as per enum mlxsw_reg_ritr_if_type. * Access: RW */ MLXSW_ITEM32(reg, ritr, type, 0x00, 23, 3); @@ -4125,6 +4132,67 @@ MLXSW_ITEM32(reg, ritr, sp_if_system_port, 0x08, 0, 16); */ MLXSW_ITEM32(reg, ritr, sp_if_vid, 0x18, 0, 12); +/* Loopback Interface */ + +enum mlxsw_reg_ritr_loopback_protocol { + /* IPinIP IPv4 underlay Unicast */ + MLXSW_REG_RITR_LOOPBACK_PROTOCOL_IPIP_IPV4, + /* IPinIP IPv6 underlay Unicast */ + MLXSW_REG_RITR_LOOPBACK_PROTOCOL_IPIP_IPV6, +}; + +/* reg_ritr_loopback_protocol + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, loopback_protocol, 0x08, 28, 4); + +enum mlxsw_reg_ritr_loopback_ipip_type { + /* Tunnel is IPinIP. */ + MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_IP, + /* Tunnel is GRE, no key. */ + MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_IN_IP, + /* Tunnel is GRE, with a key. */ + MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_KEY_IN_IP, +}; + +/* reg_ritr_loopback_ipip_type + * Encapsulation type. + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, loopback_ipip_type, 0x10, 24, 4); + +enum mlxsw_reg_ritr_loopback_ipip_options { + /* The key is defined by gre_key. */ + MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET, +}; + +/* reg_ritr_loopback_ipip_options + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, loopback_ipip_options, 0x10, 20, 4); + +/* reg_ritr_loopback_ipip_uvr + * Underlay Virtual Router ID. + * Range is 0..cap_max_virtual_routers-1. + * Reserved for Spectrum-2. + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, loopback_ipip_uvr, 0x10, 0, 16); + +/* reg_ritr_loopback_ipip_usip* + * Encapsulation Underlay source IP. + * Access: RW + */ +MLXSW_ITEM_BUF(reg, ritr, loopback_ipip_usip6, 0x18, 16); +MLXSW_ITEM32(reg, ritr, loopback_ipip_usip4, 0x24, 0, 32); + +/* reg_ritr_loopback_ipip_gre_key + * GRE Key. + * Reserved when ipip_type is not IP_IN_GRE_KEY_IN_IP. + * Access: RW + */ +MLXSW_ITEM32(reg, ritr, loopback_ipip_gre_key, 0x28, 0, 32); + /* Shared between ingress/egress */ enum mlxsw_reg_ritr_counter_set_type { /* No Count. */ @@ -4195,24 +4263,54 @@ static inline void mlxsw_reg_ritr_sp_if_pack(char *payload, bool lag, static inline void mlxsw_reg_ritr_pack(char *payload, bool enable, enum mlxsw_reg_ritr_if_type type, - u16 rif, u16 vr_id, u16 mtu, - const char *mac) + u16 rif, u16 vr_id, u16 mtu) { bool op = enable ? MLXSW_REG_RITR_RIF_CREATE : MLXSW_REG_RITR_RIF_DEL; MLXSW_REG_ZERO(ritr, payload); mlxsw_reg_ritr_enable_set(payload, enable); mlxsw_reg_ritr_ipv4_set(payload, 1); + mlxsw_reg_ritr_ipv6_set(payload, 1); mlxsw_reg_ritr_type_set(payload, type); mlxsw_reg_ritr_op_set(payload, op); mlxsw_reg_ritr_rif_set(payload, rif); mlxsw_reg_ritr_ipv4_fe_set(payload, 1); + mlxsw_reg_ritr_ipv6_fe_set(payload, 1); mlxsw_reg_ritr_lb_en_set(payload, 1); mlxsw_reg_ritr_virtual_router_set(payload, vr_id); mlxsw_reg_ritr_mtu_set(payload, mtu); +} + +static inline void mlxsw_reg_ritr_mac_pack(char *payload, const char *mac) +{ mlxsw_reg_ritr_if_mac_memcpy_to(payload, mac); } +static inline void +mlxsw_reg_ritr_loopback_ipip_common_pack(char *payload, + enum mlxsw_reg_ritr_loopback_ipip_type ipip_type, + enum mlxsw_reg_ritr_loopback_ipip_options options, + u16 uvr_id, u32 gre_key) +{ + mlxsw_reg_ritr_loopback_ipip_type_set(payload, ipip_type); + mlxsw_reg_ritr_loopback_ipip_options_set(payload, options); + mlxsw_reg_ritr_loopback_ipip_uvr_set(payload, uvr_id); + mlxsw_reg_ritr_loopback_ipip_gre_key_set(payload, gre_key); +} + +static inline void +mlxsw_reg_ritr_loopback_ipip4_pack(char *payload, + enum mlxsw_reg_ritr_loopback_ipip_type ipip_type, + enum mlxsw_reg_ritr_loopback_ipip_options options, + u16 uvr_id, u32 usip, u32 gre_key) +{ + mlxsw_reg_ritr_loopback_protocol_set(payload, + MLXSW_REG_RITR_LOOPBACK_PROTOCOL_IPIP_IPV4); + mlxsw_reg_ritr_loopback_ipip_common_pack(payload, ipip_type, options, + uvr_id, gre_key); + mlxsw_reg_ritr_loopback_ipip_usip4_set(payload, usip); +} + /* RATR - Router Adjacency Table Register * -------------------------------------- * The RATR register is used to configure the Router Adjacency (next-hop) @@ -4268,6 +4366,38 @@ MLXSW_ITEM32(reg, ratr, v, 0x00, 24, 1); */ MLXSW_ITEM32(reg, ratr, a, 0x00, 16, 1); +enum mlxsw_reg_ratr_type { + /* Ethernet */ + MLXSW_REG_RATR_TYPE_ETHERNET, + /* IPoIB Unicast without GRH. + * Reserved for Spectrum. + */ + MLXSW_REG_RATR_TYPE_IPOIB_UC, + /* IPoIB Unicast with GRH. Supported only in table 0 (Ethernet unicast + * adjacency). + * Reserved for Spectrum. + */ + MLXSW_REG_RATR_TYPE_IPOIB_UC_W_GRH, + /* IPoIB Multicast. + * Reserved for Spectrum. + */ + MLXSW_REG_RATR_TYPE_IPOIB_MC, + /* MPLS. + * Reserved for SwitchX/-2. + */ + MLXSW_REG_RATR_TYPE_MPLS, + /* IPinIP Encap. + * Reserved for SwitchX/-2. + */ + MLXSW_REG_RATR_TYPE_IPIP, +}; + +/* reg_ratr_type + * Adjacency entry type. + * Access: RW + */ +MLXSW_ITEM32(reg, ratr, type, 0x04, 28, 4); + /* reg_ratr_adjacency_index_low * Bits 15:0 of index into the adjacency table. * For SwitchX and SwitchX-2, the adjacency table is linear and @@ -4297,17 +4427,17 @@ enum mlxsw_reg_ratr_trap_action { */ MLXSW_ITEM32(reg, ratr, trap_action, 0x0C, 28, 4); -enum mlxsw_reg_ratr_trap_id { - MLXSW_REG_RATR_TRAP_ID_RTR_EGRESS0 = 0, - MLXSW_REG_RATR_TRAP_ID_RTR_EGRESS1 = 1, -}; - /* reg_ratr_adjacency_index_high * Bits 23:16 of the adjacency_index. * Access: Index */ MLXSW_ITEM32(reg, ratr, adjacency_index_high, 0x0C, 16, 8); +enum mlxsw_reg_ratr_trap_id { + MLXSW_REG_RATR_TRAP_ID_RTR_EGRESS0, + MLXSW_REG_RATR_TRAP_ID_RTR_EGRESS1, +}; + /* reg_ratr_trap_id * Trap ID to be reported to CPU. * Trap-ID is RTR_EGRESS0 or RTR_EGRESS1. @@ -4322,14 +4452,44 @@ MLXSW_ITEM32(reg, ratr, trap_id, 0x0C, 0, 8); */ MLXSW_ITEM_BUF(reg, ratr, eth_destination_mac, 0x12, 6); +enum mlxsw_reg_ratr_ipip_type { + /* IPv4, address set by mlxsw_reg_ratr_ipip_ipv4_udip. */ + MLXSW_REG_RATR_IPIP_TYPE_IPV4, + /* IPv6, address set by mlxsw_reg_ratr_ipip_ipv6_ptr. */ + MLXSW_REG_RATR_IPIP_TYPE_IPV6, +}; + +/* reg_ratr_ipip_type + * Underlay destination ip type. + * Note: the type field must match the protocol of the router interface. + * Access: RW + */ +MLXSW_ITEM32(reg, ratr, ipip_type, 0x10, 16, 4); + +/* reg_ratr_ipip_ipv4_udip + * Underlay ipv4 dip. + * Reserved when ipip_type is IPv6. + * Access: RW + */ +MLXSW_ITEM32(reg, ratr, ipip_ipv4_udip, 0x18, 0, 32); + +/* reg_ratr_ipip_ipv6_ptr + * Pointer to IPv6 underlay destination ip address. + * For Spectrum: Pointer to KVD linear space. + * Access: RW + */ +MLXSW_ITEM32(reg, ratr, ipip_ipv6_ptr, 0x1C, 0, 24); + static inline void mlxsw_reg_ratr_pack(char *payload, enum mlxsw_reg_ratr_op op, bool valid, + enum mlxsw_reg_ratr_type type, u32 adjacency_index, u16 egress_rif) { MLXSW_REG_ZERO(ratr, payload); mlxsw_reg_ratr_op_set(payload, op); mlxsw_reg_ratr_v_set(payload, valid); + mlxsw_reg_ratr_type_set(payload, type); mlxsw_reg_ratr_adjacency_index_low_set(payload, adjacency_index); mlxsw_reg_ratr_adjacency_index_high_set(payload, adjacency_index >> 16); mlxsw_reg_ratr_egress_router_interface_set(payload, egress_rif); @@ -4341,6 +4501,12 @@ static inline void mlxsw_reg_ratr_eth_entry_pack(char *payload, mlxsw_reg_ratr_eth_destination_mac_memcpy_to(payload, dest_mac); } +static inline void mlxsw_reg_ratr_ipip4_entry_pack(char *payload, u32 ipv4_udip) +{ + mlxsw_reg_ratr_ipip_type_set(payload, MLXSW_REG_RATR_IPIP_TYPE_IPV4); + mlxsw_reg_ratr_ipip_ipv4_udip_set(payload, ipv4_udip); +} + /* RICNT - Router Interface Counter Register * ----------------------------------------- * The RICNT register retrieves per port performance counters @@ -4712,12 +4878,13 @@ MLXSW_ITEM32(reg, ralue, prefix_len, 0x08, 0, 8); /* reg_ralue_dip* * The prefix of the route or of the marker that the object of the LPM * is compared with. The most significant bits of the dip are the prefix. - * The list significant bits must be '0' if the prefix_len is smaller + * The least significant bits must be '0' if the prefix_len is smaller * than 128 for IPv6 or smaller than 32 for IPv4. * IPv4 address uses bits dip[31:0] and bits dip[127:32] are reserved. * Access: Index */ MLXSW_ITEM32(reg, ralue, dip4, 0x18, 0, 32); +MLXSW_ITEM_BUF(reg, ralue, dip6, 0x0C, 16); enum mlxsw_reg_ralue_entry_type { MLXSW_REG_RALUE_ENTRY_TYPE_MARKER_ENTRY = 1, @@ -4806,7 +4973,7 @@ MLXSW_ITEM32(reg, ralue, ecmp_size, 0x28, 0, 13); */ MLXSW_ITEM32(reg, ralue, local_erif, 0x24, 0, 16); -/* reg_ralue_v +/* reg_ralue_ip2me_v * Valid bit for the tunnel_ptr field. * If valid = 0 then trap to CPU as IP2ME trap ID. * If valid = 1 and the packet format allows NVE or IPinIP tunnel @@ -4816,15 +4983,15 @@ MLXSW_ITEM32(reg, ralue, local_erif, 0x24, 0, 16); * Only relevant in case of IP2ME action. * Access: RW */ -MLXSW_ITEM32(reg, ralue, v, 0x24, 31, 1); +MLXSW_ITEM32(reg, ralue, ip2me_v, 0x24, 31, 1); -/* reg_ralue_tunnel_ptr +/* reg_ralue_ip2me_tunnel_ptr * Tunnel Pointer for NVE or IPinIP tunnel decapsulation. * For Spectrum, pointer to KVD Linear. * Only relevant in case of IP2ME action. * Access: RW */ -MLXSW_ITEM32(reg, ralue, tunnel_ptr, 0x24, 0, 24); +MLXSW_ITEM32(reg, ralue, ip2me_tunnel_ptr, 0x24, 0, 24); static inline void mlxsw_reg_ralue_pack(char *payload, enum mlxsw_reg_ralxx_protocol protocol, @@ -4851,6 +5018,16 @@ static inline void mlxsw_reg_ralue_pack4(char *payload, mlxsw_reg_ralue_dip4_set(payload, dip); } +static inline void mlxsw_reg_ralue_pack6(char *payload, + enum mlxsw_reg_ralxx_protocol protocol, + enum mlxsw_reg_ralue_op op, + u16 virtual_router, u8 prefix_len, + const void *dip) +{ + mlxsw_reg_ralue_pack(payload, protocol, op, virtual_router, prefix_len); + mlxsw_reg_ralue_dip6_memcpy_to(payload, dip); +} + static inline void mlxsw_reg_ralue_act_remote_pack(char *payload, enum mlxsw_reg_ralue_trap_action trap_action, @@ -4883,6 +5060,15 @@ mlxsw_reg_ralue_act_ip2me_pack(char *payload) MLXSW_REG_RALUE_ACTION_TYPE_IP2ME); } +static inline void +mlxsw_reg_ralue_act_ip2me_tun_pack(char *payload, u32 tunnel_ptr) +{ + mlxsw_reg_ralue_action_type_set(payload, + MLXSW_REG_RALUE_ACTION_TYPE_IP2ME); + mlxsw_reg_ralue_ip2me_v_set(payload, 1); + mlxsw_reg_ralue_ip2me_tunnel_ptr_set(payload, tunnel_ptr); +} + /* RAUHT - Router Algorithmic LPM Unicast Host Table Register * ---------------------------------------------------------- * The RAUHT register is used to configure and query the Unicast Host table in @@ -4954,6 +5140,7 @@ MLXSW_ITEM32(reg, rauht, rif, 0x00, 0, 16); * Access: Index */ MLXSW_ITEM32(reg, rauht, dip4, 0x1C, 0x0, 32); +MLXSW_ITEM_BUF(reg, rauht, dip6, 0x10, 16); enum mlxsw_reg_rauht_trap_action { MLXSW_REG_RAUHT_TRAP_ACTION_NOP, @@ -4982,6 +5169,15 @@ enum mlxsw_reg_rauht_trap_id { */ MLXSW_ITEM32(reg, rauht, trap_id, 0x60, 0, 9); +enum mlxsw_reg_flow_counter_set_type { + /* No count */ + MLXSW_REG_FLOW_COUNTER_SET_TYPE_NO_COUNT = 0x00, + /* Count packets and bytes */ + MLXSW_REG_FLOW_COUNTER_SET_TYPE_PACKETS_BYTES = 0x03, + /* Count only packets */ + MLXSW_REG_FLOW_COUNTER_SET_TYPE_PACKETS = 0x05, +}; + /* reg_rauht_counter_set_type * Counter set type for flow counters * Access: RW @@ -5018,6 +5214,23 @@ static inline void mlxsw_reg_rauht_pack4(char *payload, mlxsw_reg_rauht_dip4_set(payload, dip); } +static inline void mlxsw_reg_rauht_pack6(char *payload, + enum mlxsw_reg_rauht_op op, u16 rif, + const char *mac, const char *dip) +{ + mlxsw_reg_rauht_pack(payload, op, rif, mac); + mlxsw_reg_rauht_type_set(payload, MLXSW_REG_RAUHT_TYPE_IPV6); + mlxsw_reg_rauht_dip6_memcpy_to(payload, dip); +} + +static inline void mlxsw_reg_rauht_pack_counter(char *payload, + u64 counter_index) +{ + mlxsw_reg_rauht_counter_index_set(payload, counter_index); + mlxsw_reg_rauht_counter_set_type_set(payload, + MLXSW_REG_FLOW_COUNTER_SET_TYPE_PACKETS_BYTES); +} + /* RALEU - Router Algorithmic LPM ECMP Update Register * --------------------------------------------------- * The register enables updating the ECMP section in the action for multiple @@ -5216,6 +5429,30 @@ MLXSW_ITEM32_INDEXED(reg, rauhtd, ipv4_ent_rif, MLXSW_REG_RAUHTD_BASE_LEN, 0, MLXSW_ITEM32_INDEXED(reg, rauhtd, ipv4_ent_dip, MLXSW_REG_RAUHTD_BASE_LEN, 0, 32, MLXSW_REG_RAUHTD_IPV4_ENT_LEN, 0x04, false); +#define MLXSW_REG_RAUHTD_IPV6_ENT_LEN 0x20 + +/* reg_rauhtd_ipv6_ent_a + * Activity. Set for new entries. Set if a packet lookup has hit on the + * specific entry. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, rauhtd, ipv6_ent_a, MLXSW_REG_RAUHTD_BASE_LEN, 16, 1, + MLXSW_REG_RAUHTD_IPV6_ENT_LEN, 0x00, false); + +/* reg_rauhtd_ipv6_ent_rif + * Router interface. + * Access: RO + */ +MLXSW_ITEM32_INDEXED(reg, rauhtd, ipv6_ent_rif, MLXSW_REG_RAUHTD_BASE_LEN, 0, + 16, MLXSW_REG_RAUHTD_IPV6_ENT_LEN, 0x00, false); + +/* reg_rauhtd_ipv6_ent_dip + * Destination IPv6 address. + * Access: RO + */ +MLXSW_ITEM_BUF_INDEXED(reg, rauhtd, ipv6_ent_dip, MLXSW_REG_RAUHTD_BASE_LEN, + 16, MLXSW_REG_RAUHTD_IPV6_ENT_LEN, 0x10); + static inline void mlxsw_reg_rauhtd_ent_ipv4_unpack(char *payload, int ent_index, u16 *p_rif, u32 *p_dip) @@ -5224,6 +5461,141 @@ static inline void mlxsw_reg_rauhtd_ent_ipv4_unpack(char *payload, *p_dip = mlxsw_reg_rauhtd_ipv4_ent_dip_get(payload, ent_index); } +static inline void mlxsw_reg_rauhtd_ent_ipv6_unpack(char *payload, + int rec_index, u16 *p_rif, + char *p_dip) +{ + *p_rif = mlxsw_reg_rauhtd_ipv6_ent_rif_get(payload, rec_index); + mlxsw_reg_rauhtd_ipv6_ent_dip_memcpy_from(payload, rec_index, p_dip); +} + +/* RTDP - Routing Tunnel Decap Properties Register + * ----------------------------------------------- + * The RTDP register is used for configuring the tunnel decap properties of NVE + * and IPinIP. + */ +#define MLXSW_REG_RTDP_ID 0x8020 +#define MLXSW_REG_RTDP_LEN 0x44 + +MLXSW_REG_DEFINE(rtdp, MLXSW_REG_RTDP_ID, MLXSW_REG_RTDP_LEN); + +enum mlxsw_reg_rtdp_type { + MLXSW_REG_RTDP_TYPE_NVE, + MLXSW_REG_RTDP_TYPE_IPIP, +}; + +/* reg_rtdp_type + * Type of the RTDP entry as per enum mlxsw_reg_rtdp_type. + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, type, 0x00, 28, 4); + +/* reg_rtdp_tunnel_index + * Index to the Decap entry. + * For Spectrum, Index to KVD Linear. + * Access: Index + */ +MLXSW_ITEM32(reg, rtdp, tunnel_index, 0x00, 0, 24); + +/* IPinIP */ + +/* reg_rtdp_ipip_irif + * Ingress Router Interface for the overlay router + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, ipip_irif, 0x04, 16, 16); + +enum mlxsw_reg_rtdp_ipip_sip_check { + /* No sip checks. */ + MLXSW_REG_RTDP_IPIP_SIP_CHECK_NO, + /* Filter packet if underlay is not IPv4 or if underlay SIP does not + * equal ipv4_usip. + */ + MLXSW_REG_RTDP_IPIP_SIP_CHECK_FILTER_IPV4, + /* Filter packet if underlay is not IPv6 or if underlay SIP does not + * equal ipv6_usip. + */ + MLXSW_REG_RTDP_IPIP_SIP_CHECK_FILTER_IPV6 = 3, +}; + +/* reg_rtdp_ipip_sip_check + * SIP check to perform. If decapsulation failed due to these configurations + * then trap_id is IPIP_DECAP_ERROR. + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, ipip_sip_check, 0x04, 0, 3); + +/* If set, allow decapsulation of IPinIP (without GRE). */ +#define MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_IPIP BIT(0) +/* If set, allow decapsulation of IPinGREinIP without a key. */ +#define MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_GRE BIT(1) +/* If set, allow decapsulation of IPinGREinIP with a key. */ +#define MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_GRE_KEY BIT(2) + +/* reg_rtdp_ipip_type_check + * Flags as per MLXSW_REG_RTDP_IPIP_TYPE_CHECK_*. If decapsulation failed due to + * these configurations then trap_id is IPIP_DECAP_ERROR. + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, ipip_type_check, 0x08, 24, 3); + +/* reg_rtdp_ipip_gre_key_check + * Whether GRE key should be checked. When check is enabled: + * - A packet received as IPinIP (without GRE) will always pass. + * - A packet received as IPinGREinIP without a key will not pass the check. + * - A packet received as IPinGREinIP with a key will pass the check only if the + * key in the packet is equal to expected_gre_key. + * If decapsulation failed due to GRE key then trap_id is IPIP_DECAP_ERROR. + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, ipip_gre_key_check, 0x08, 23, 1); + +/* reg_rtdp_ipip_ipv4_usip + * Underlay IPv4 address for ipv4 source address check. + * Reserved when sip_check is not '1'. + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, ipip_ipv4_usip, 0x0C, 0, 32); + +/* reg_rtdp_ipip_ipv6_usip_ptr + * This field is valid when sip_check is "sipv6 check explicitly". This is a + * pointer to the IPv6 DIP which is configured by RIPS. For Spectrum, the index + * is to the KVD linear. + * Reserved when sip_check is not MLXSW_REG_RTDP_IPIP_SIP_CHECK_FILTER_IPV6. + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, ipip_ipv6_usip_ptr, 0x10, 0, 24); + +/* reg_rtdp_ipip_expected_gre_key + * GRE key for checking. + * Reserved when gre_key_check is '0'. + * Access: RW + */ +MLXSW_ITEM32(reg, rtdp, ipip_expected_gre_key, 0x14, 0, 32); + +static inline void mlxsw_reg_rtdp_pack(char *payload, + enum mlxsw_reg_rtdp_type type, + u32 tunnel_index) +{ + MLXSW_REG_ZERO(rtdp, payload); + mlxsw_reg_rtdp_type_set(payload, type); + mlxsw_reg_rtdp_tunnel_index_set(payload, tunnel_index); +} + +static inline void +mlxsw_reg_rtdp_ipip4_pack(char *payload, u16 irif, + enum mlxsw_reg_rtdp_ipip_sip_check sip_check, + unsigned int type_check, bool gre_key_check, + u32 ipv4_usip, u32 expected_gre_key) +{ + mlxsw_reg_rtdp_ipip_irif_set(payload, irif); + mlxsw_reg_rtdp_ipip_sip_check_set(payload, sip_check); + mlxsw_reg_rtdp_ipip_type_check_set(payload, type_check); + mlxsw_reg_rtdp_ipip_gre_key_check_set(payload, gre_key_check); + mlxsw_reg_rtdp_ipip_ipv4_usip_set(payload, ipv4_usip); + mlxsw_reg_rtdp_ipip_expected_gre_key_set(payload, expected_gre_key); +} + /* MFCR - Management Fan Control Register * -------------------------------------- * This register controls the settings of the Fan Speed PWM mechanism. @@ -5982,15 +6354,6 @@ static inline void mlxsw_reg_mpsc_pack(char *payload, u8 local_port, bool e, MLXSW_REG_DEFINE(mgpc, MLXSW_REG_MGPC_ID, MLXSW_REG_MGPC_LEN); -enum mlxsw_reg_mgpc_counter_set_type { - /* No count */ - MLXSW_REG_MGPC_COUNTER_SET_TYPE_NO_COUT = 0x00, - /* Count packets and bytes */ - MLXSW_REG_MGPC_COUNTER_SET_TYPE_PACKETS_BYTES = 0x03, - /* Count only packets */ - MLXSW_REG_MGPC_COUNTER_SET_TYPE_PACKETS = 0x05, -}; - /* reg_mgpc_counter_set_type * Counter set type. * Access: OP @@ -6030,7 +6393,7 @@ MLXSW_ITEM64(reg, mgpc, packet_counter, 0x10, 0, 64); static inline void mlxsw_reg_mgpc_pack(char *payload, u32 counter_index, enum mlxsw_reg_mgpc_opcode opcode, - enum mlxsw_reg_mgpc_counter_set_type set_type) + enum mlxsw_reg_flow_counter_set_type set_type) { MLXSW_REG_ZERO(mgpc, payload); mlxsw_reg_mgpc_counter_index_set(payload, counter_index); @@ -6494,6 +6857,7 @@ static const struct mlxsw_reg_info *mlxsw_reg_infos[] = { MLXSW_REG(rgcr), MLXSW_REG(ritr), MLXSW_REG(ratr), + MLXSW_REG(rtdp), MLXSW_REG(ricnt), MLXSW_REG(ralta), MLXSW_REG(ralst), diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index c6a3e61b53bd..ed7cd6c48019 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -58,6 +58,7 @@ #include <net/tc_act/tc_mirred.h> #include <net/netevent.h> #include <net/tc_act/tc_sample.h> +#include <net/addrconf.h> #include "spectrum.h" #include "pci.h" @@ -381,12 +382,14 @@ int mlxsw_sp_flow_counter_get(struct mlxsw_sp *mlxsw_sp, int err; mlxsw_reg_mgpc_pack(mgpc_pl, counter_index, MLXSW_REG_MGPC_OPCODE_NOP, - MLXSW_REG_MGPC_COUNTER_SET_TYPE_PACKETS_BYTES); + MLXSW_REG_FLOW_COUNTER_SET_TYPE_PACKETS_BYTES); err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(mgpc), mgpc_pl); if (err) return err; - *packets = mlxsw_reg_mgpc_packet_counter_get(mgpc_pl); - *bytes = mlxsw_reg_mgpc_byte_counter_get(mgpc_pl); + if (packets) + *packets = mlxsw_reg_mgpc_packet_counter_get(mgpc_pl); + if (bytes) + *bytes = mlxsw_reg_mgpc_byte_counter_get(mgpc_pl); return 0; } @@ -396,7 +399,7 @@ static int mlxsw_sp_flow_counter_clear(struct mlxsw_sp *mlxsw_sp, char mgpc_pl[MLXSW_REG_MGPC_LEN]; mlxsw_reg_mgpc_pack(mgpc_pl, counter_index, MLXSW_REG_MGPC_OPCODE_CLEAR, - MLXSW_REG_MGPC_COUNTER_SET_TYPE_PACKETS_BYTES); + MLXSW_REG_FLOW_COUNTER_SET_TYPE_PACKETS_BYTES); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(mgpc), mgpc_pl); } @@ -1616,16 +1619,16 @@ mlxsw_sp_port_del_cls_matchall_sample(struct mlxsw_sp_port *mlxsw_sp_port) } static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, - __be16 protocol, - struct tc_cls_matchall_offload *cls, + struct tc_cls_matchall_offload *f, bool ingress) { struct mlxsw_sp_port_mall_tc_entry *mall_tc_entry; + __be16 protocol = f->common.protocol; const struct tc_action *a; LIST_HEAD(actions); int err; - if (!tc_single_action(cls->exts)) { + if (!tcf_exts_has_one_action(f->exts)) { netdev_err(mlxsw_sp_port->dev, "only singular actions are supported\n"); return -EOPNOTSUPP; } @@ -1633,9 +1636,9 @@ static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, mall_tc_entry = kzalloc(sizeof(*mall_tc_entry), GFP_KERNEL); if (!mall_tc_entry) return -ENOMEM; - mall_tc_entry->cookie = cls->cookie; + mall_tc_entry->cookie = f->cookie; - tcf_exts_to_list(cls->exts, &actions); + tcf_exts_to_list(f->exts, &actions); a = list_first_entry(&actions, struct tc_action, list); if (is_tcf_mirred_egress_mirror(a) && protocol == htons(ETH_P_ALL)) { @@ -1647,7 +1650,7 @@ static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, mirror, a, ingress); } else if (is_tcf_sample(a) && protocol == htons(ETH_P_ALL)) { mall_tc_entry->type = MLXSW_SP_PORT_MALL_SAMPLE; - err = mlxsw_sp_port_add_cls_matchall_sample(mlxsw_sp_port, cls, + err = mlxsw_sp_port_add_cls_matchall_sample(mlxsw_sp_port, f, a, ingress); } else { err = -EOPNOTSUPP; @@ -1665,12 +1668,12 @@ err_add_action: } static void mlxsw_sp_port_del_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, - struct tc_cls_matchall_offload *cls) + struct tc_cls_matchall_offload *f) { struct mlxsw_sp_port_mall_tc_entry *mall_tc_entry; mall_tc_entry = mlxsw_sp_port_mall_tc_entry_find(mlxsw_sp_port, - cls->cookie); + f->cookie); if (!mall_tc_entry) { netdev_dbg(mlxsw_sp_port->dev, "tc entry not found on port\n"); return; @@ -1692,49 +1695,72 @@ static void mlxsw_sp_port_del_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, kfree(mall_tc_entry); } -static int mlxsw_sp_setup_tc(struct net_device *dev, u32 handle, - u32 chain_index, __be16 proto, - struct tc_to_netdev *tc) +static int mlxsw_sp_setup_tc_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, + struct tc_cls_matchall_offload *f) { - struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); - bool ingress = TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS); + bool ingress; - if (chain_index) + if (is_classid_clsact_ingress(f->common.classid)) + ingress = true; + else if (is_classid_clsact_egress(f->common.classid)) + ingress = false; + else return -EOPNOTSUPP; - switch (tc->type) { - case TC_SETUP_MATCHALL: - switch (tc->cls_mall->command) { - case TC_CLSMATCHALL_REPLACE: - return mlxsw_sp_port_add_cls_matchall(mlxsw_sp_port, - proto, - tc->cls_mall, - ingress); - case TC_CLSMATCHALL_DESTROY: - mlxsw_sp_port_del_cls_matchall(mlxsw_sp_port, - tc->cls_mall); - return 0; - default: - return -EOPNOTSUPP; - } - case TC_SETUP_CLSFLOWER: - switch (tc->cls_flower->command) { - case TC_CLSFLOWER_REPLACE: - return mlxsw_sp_flower_replace(mlxsw_sp_port, ingress, - proto, tc->cls_flower); - case TC_CLSFLOWER_DESTROY: - mlxsw_sp_flower_destroy(mlxsw_sp_port, ingress, - tc->cls_flower); - return 0; - case TC_CLSFLOWER_STATS: - return mlxsw_sp_flower_stats(mlxsw_sp_port, ingress, - tc->cls_flower); - default: - return -EOPNOTSUPP; - } + if (f->common.chain_index) + return -EOPNOTSUPP; + + switch (f->command) { + case TC_CLSMATCHALL_REPLACE: + return mlxsw_sp_port_add_cls_matchall(mlxsw_sp_port, f, + ingress); + case TC_CLSMATCHALL_DESTROY: + mlxsw_sp_port_del_cls_matchall(mlxsw_sp_port, f); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int +mlxsw_sp_setup_tc_cls_flower(struct mlxsw_sp_port *mlxsw_sp_port, + struct tc_cls_flower_offload *f) +{ + bool ingress; + + if (is_classid_clsact_ingress(f->common.classid)) + ingress = true; + else if (is_classid_clsact_egress(f->common.classid)) + ingress = false; + else + return -EOPNOTSUPP; + + switch (f->command) { + case TC_CLSFLOWER_REPLACE: + return mlxsw_sp_flower_replace(mlxsw_sp_port, ingress, f); + case TC_CLSFLOWER_DESTROY: + mlxsw_sp_flower_destroy(mlxsw_sp_port, ingress, f); + return 0; + case TC_CLSFLOWER_STATS: + return mlxsw_sp_flower_stats(mlxsw_sp_port, ingress, f); + default: + return -EOPNOTSUPP; } +} - return -EOPNOTSUPP; +static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) +{ + struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); + + switch (type) { + case TC_SETUP_CLSMATCHALL: + return mlxsw_sp_setup_tc_cls_matchall(mlxsw_sp_port, type_data); + case TC_SETUP_CLSFLOWER: + return mlxsw_sp_setup_tc_cls_flower(mlxsw_sp_port, type_data); + default: + return -EOPNOTSUPP; + } } static const struct net_device_ops mlxsw_sp_port_netdev_ops = { @@ -3333,15 +3359,48 @@ static const struct mlxsw_listener mlxsw_sp_listener[] = { MLXSW_SP_RXL_MARK(ARPBC, MIRROR_TO_CPU, ARP, false), MLXSW_SP_RXL_MARK(ARPUC, MIRROR_TO_CPU, ARP, false), MLXSW_SP_RXL_NO_MARK(FID_MISS, TRAP_TO_CPU, IP2ME, false), + MLXSW_SP_RXL_MARK(IPV6_MLDV12_LISTENER_QUERY, MIRROR_TO_CPU, IPV6_MLD, + false), + MLXSW_SP_RXL_NO_MARK(IPV6_MLDV1_LISTENER_REPORT, TRAP_TO_CPU, IPV6_MLD, + false), + MLXSW_SP_RXL_NO_MARK(IPV6_MLDV1_LISTENER_DONE, TRAP_TO_CPU, IPV6_MLD, + false), + MLXSW_SP_RXL_NO_MARK(IPV6_MLDV2_LISTENER_REPORT, TRAP_TO_CPU, IPV6_MLD, + false), /* L3 traps */ - MLXSW_SP_RXL_NO_MARK(MTUERROR, TRAP_TO_CPU, ROUTER_EXP, false), - MLXSW_SP_RXL_NO_MARK(TTLERROR, TRAP_TO_CPU, ROUTER_EXP, false), - MLXSW_SP_RXL_NO_MARK(LBERROR, TRAP_TO_CPU, ROUTER_EXP, false), - MLXSW_SP_RXL_MARK(OSPF, TRAP_TO_CPU, OSPF, false), - MLXSW_SP_RXL_NO_MARK(IP2ME, TRAP_TO_CPU, IP2ME, false), - MLXSW_SP_RXL_NO_MARK(RTR_INGRESS0, TRAP_TO_CPU, REMOTE_ROUTE, false), - MLXSW_SP_RXL_NO_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, ARP_MISS, false), - MLXSW_SP_RXL_NO_MARK(BGP_IPV4, TRAP_TO_CPU, BGP_IPV4, false), + MLXSW_SP_RXL_MARK(MTUERROR, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(TTLERROR, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(LBERROR, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(IP2ME, TRAP_TO_CPU, IP2ME, false), + MLXSW_SP_RXL_MARK(IPV6_UNSPECIFIED_ADDRESS, TRAP_TO_CPU, ROUTER_EXP, + false), + MLXSW_SP_RXL_MARK(IPV6_LINK_LOCAL_DEST, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(IPV6_LINK_LOCAL_SRC, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(IPV6_ALL_NODES_LINK, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(IPV6_ALL_ROUTERS_LINK, TRAP_TO_CPU, ROUTER_EXP, + false), + MLXSW_SP_RXL_MARK(IPV4_OSPF, TRAP_TO_CPU, OSPF, false), + MLXSW_SP_RXL_MARK(IPV6_OSPF, TRAP_TO_CPU, OSPF, false), + MLXSW_SP_RXL_MARK(IPV6_DHCP, TRAP_TO_CPU, DHCP, false), + MLXSW_SP_RXL_MARK(RTR_INGRESS0, TRAP_TO_CPU, REMOTE_ROUTE, false), + MLXSW_SP_RXL_MARK(IPV4_BGP, TRAP_TO_CPU, BGP, false), + MLXSW_SP_RXL_MARK(IPV6_BGP, TRAP_TO_CPU, BGP, false), + MLXSW_SP_RXL_MARK(L3_IPV6_ROUTER_SOLICITATION, TRAP_TO_CPU, IPV6_ND, + false), + MLXSW_SP_RXL_MARK(L3_IPV6_ROUTER_ADVERTISMENT, TRAP_TO_CPU, IPV6_ND, + false), + MLXSW_SP_RXL_MARK(L3_IPV6_NEIGHBOR_SOLICITATION, TRAP_TO_CPU, IPV6_ND, + false), + MLXSW_SP_RXL_MARK(L3_IPV6_NEIGHBOR_ADVERTISMENT, TRAP_TO_CPU, IPV6_ND, + false), + MLXSW_SP_RXL_MARK(L3_IPV6_REDIRECTION, TRAP_TO_CPU, IPV6_ND, false), + MLXSW_SP_RXL_MARK(IPV6_MC_LINK_LOCAL_DEST, TRAP_TO_CPU, ROUTER_EXP, + false), + MLXSW_SP_RXL_MARK(HOST_MISS_IPV4, TRAP_TO_CPU, HOST_MISS, false), + MLXSW_SP_RXL_MARK(HOST_MISS_IPV6, TRAP_TO_CPU, HOST_MISS, false), + MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV4, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(ROUTER_ALERT_IPV6, TRAP_TO_CPU, ROUTER_EXP, false), + MLXSW_SP_RXL_MARK(IPIP_DECAP_ERROR, TRAP_TO_CPU, ROUTER_EXP, false), /* PKT Sample trap */ MLXSW_RXL(mlxsw_sp_rx_listener_sample_func, PKT_SAMPLE, MIRROR_TO_CPU, false, SP_IP2ME, DISCARD), @@ -3376,15 +3435,17 @@ static int mlxsw_sp_cpu_policers_set(struct mlxsw_core *mlxsw_core) burst_size = 7; break; case MLXSW_REG_HTGT_TRAP_GROUP_SP_IGMP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_MLD: rate = 16 * 1024; burst_size = 10; break; - case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP_IPV4: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP: - case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP_MISS: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_HOST_MISS: case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND: rate = 1024; burst_size = 7; break; @@ -3433,21 +3494,23 @@ static int mlxsw_sp_trap_groups_set(struct mlxsw_core *mlxsw_core) priority = 5; tc = 5; break; - case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP_IPV4: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_BGP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_DHCP: priority = 4; tc = 4; break; case MLXSW_REG_HTGT_TRAP_GROUP_SP_IGMP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_IP2ME: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_MLD: priority = 3; tc = 3; break; case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_IPV6_ND: priority = 2; tc = 2; break; - case MLXSW_REG_HTGT_TRAP_GROUP_SP_ARP_MISS: + case MLXSW_REG_HTGT_TRAP_GROUP_SP_HOST_MISS: case MLXSW_REG_HTGT_TRAP_GROUP_SP_ROUTER_EXP: case MLXSW_REG_HTGT_TRAP_GROUP_SP_REMOTE_ROUTE: priority = 1; @@ -3694,7 +3757,7 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core) mlxsw_sp_fids_fini(mlxsw_sp); } -static struct mlxsw_config_profile mlxsw_sp_config_profile = { +static const struct mlxsw_config_profile mlxsw_sp_config_profile = { .used_max_vepa_channels = 1, .max_vepa_channels = 0, .used_max_mid = 1, @@ -4363,6 +4426,10 @@ static struct notifier_block mlxsw_sp_inetaddr_nb __read_mostly = { .priority = 10, /* Must be called before FIB notifier block */ }; +static struct notifier_block mlxsw_sp_inet6addr_nb __read_mostly = { + .notifier_call = mlxsw_sp_inet6addr_event, +}; + static struct notifier_block mlxsw_sp_router_netevent_nb __read_mostly = { .notifier_call = mlxsw_sp_router_netevent_event, }; @@ -4383,6 +4450,7 @@ static int __init mlxsw_sp_module_init(void) register_netdevice_notifier(&mlxsw_sp_netdevice_nb); register_inetaddr_notifier(&mlxsw_sp_inetaddr_nb); + register_inet6addr_notifier(&mlxsw_sp_inet6addr_nb); register_netevent_notifier(&mlxsw_sp_router_netevent_nb); err = mlxsw_core_driver_register(&mlxsw_sp_driver); @@ -4399,6 +4467,7 @@ err_pci_driver_register: mlxsw_core_driver_unregister(&mlxsw_sp_driver); err_core_driver_register: unregister_netevent_notifier(&mlxsw_sp_router_netevent_nb); + unregister_inet6addr_notifier(&mlxsw_sp_inet6addr_nb); unregister_inetaddr_notifier(&mlxsw_sp_inetaddr_nb); unregister_netdevice_notifier(&mlxsw_sp_netdevice_nb); return err; @@ -4409,6 +4478,7 @@ static void __exit mlxsw_sp_module_exit(void) mlxsw_pci_driver_unregister(&mlxsw_sp_pci_driver); mlxsw_core_driver_unregister(&mlxsw_sp_driver); unregister_netevent_notifier(&mlxsw_sp_router_netevent_nb); + unregister_inet6addr_notifier(&mlxsw_sp_inet6addr_nb); unregister_inetaddr_notifier(&mlxsw_sp_inetaddr_nb); unregister_netdevice_notifier(&mlxsw_sp_netdevice_nb); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 5ef98d4d0ab6..84ce83acdc19 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -77,6 +77,7 @@ enum mlxsw_sp_rif_type { MLXSW_SP_RIF_TYPE_SUBPORT, MLXSW_SP_RIF_TYPE_VLAN, MLXSW_SP_RIF_TYPE_FID, + MLXSW_SP_RIF_TYPE_IPIP_LB, /* IP-in-IP loopback. */ MLXSW_SP_RIF_TYPE_MAX, }; @@ -384,6 +385,8 @@ int mlxsw_sp_router_netevent_event(struct notifier_block *unused, int mlxsw_sp_netdevice_router_port_event(struct net_device *dev); int mlxsw_sp_inetaddr_event(struct notifier_block *unused, unsigned long event, void *ptr); +int mlxsw_sp_inet6addr_event(struct notifier_block *unused, + unsigned long event, void *ptr); int mlxsw_sp_netdevice_vrf_event(struct net_device *l3_dev, unsigned long event, struct netdev_notifier_changeupper_info *info); void @@ -415,6 +418,7 @@ struct mlxsw_sp_acl_profile_ops { int (*ruleset_bind)(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv, struct net_device *dev, bool ingress); void (*ruleset_unbind)(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv); + u16 (*ruleset_group_id)(void *ruleset_priv); size_t rule_priv_size; int (*rule_add)(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv, void *rule_priv, @@ -438,11 +442,16 @@ struct mlxsw_sp_acl_ruleset; /* spectrum_acl.c */ struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl); struct mlxsw_sp_acl_ruleset * -mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, - struct net_device *dev, bool ingress, +mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, + bool ingress, u32 chain_index, + enum mlxsw_sp_acl_profile profile); +struct mlxsw_sp_acl_ruleset * +mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, + bool ingress, u32 chain_index, enum mlxsw_sp_acl_profile profile); void mlxsw_sp_acl_ruleset_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset *ruleset); +u16 mlxsw_sp_acl_ruleset_group_id(struct mlxsw_sp_acl_ruleset *ruleset); struct mlxsw_sp_acl_rule_info * mlxsw_sp_acl_rulei_create(struct mlxsw_sp_acl *acl); @@ -506,7 +515,7 @@ extern const struct mlxsw_sp_acl_ops mlxsw_sp_acl_tcam_ops; /* spectrum_flower.c */ int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, - __be16 protocol, struct tc_cls_flower_offload *f); + struct tc_cls_flower_offload *f); void mlxsw_sp_flower_destroy(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, struct tc_cls_flower_offload *f); int mlxsw_sp_flower_stats(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c index 01a1501b56ca..4b2455e3e079 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl.c @@ -74,6 +74,7 @@ struct mlxsw_afk *mlxsw_sp_acl_afk(struct mlxsw_sp_acl *acl) struct mlxsw_sp_acl_ruleset_ht_key { struct net_device *dev; /* dev this ruleset is bound to */ bool ingress; + u32 chain_index; const struct mlxsw_sp_acl_profile_ops *ops; }; @@ -163,7 +164,8 @@ static void mlxsw_sp_acl_ruleset_destroy(struct mlxsw_sp *mlxsw_sp, static int mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_ruleset *ruleset, - struct net_device *dev, bool ingress) + struct net_device *dev, bool ingress, + u32 chain_index) { const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops; struct mlxsw_sp_acl *acl = mlxsw_sp->acl; @@ -171,13 +173,20 @@ static int mlxsw_sp_acl_ruleset_bind(struct mlxsw_sp *mlxsw_sp, ruleset->ht_key.dev = dev; ruleset->ht_key.ingress = ingress; + ruleset->ht_key.chain_index = chain_index; err = rhashtable_insert_fast(&acl->ruleset_ht, &ruleset->ht_node, mlxsw_sp_acl_ruleset_ht_params); if (err) return err; - err = ops->ruleset_bind(mlxsw_sp, ruleset->priv, dev, ingress); - if (err) - goto err_ops_ruleset_bind; + if (!ruleset->ht_key.chain_index) { + /* We only need ruleset with chain index 0, the implicit one, + * to be directly bound to device. The rest of the rulesets + * are bound by "Goto action set". + */ + err = ops->ruleset_bind(mlxsw_sp, ruleset->priv, dev, ingress); + if (err) + goto err_ops_ruleset_bind; + } return 0; err_ops_ruleset_bind: @@ -192,7 +201,8 @@ static void mlxsw_sp_acl_ruleset_unbind(struct mlxsw_sp *mlxsw_sp, const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops; struct mlxsw_sp_acl *acl = mlxsw_sp->acl; - ops->ruleset_unbind(mlxsw_sp, ruleset->priv); + if (!ruleset->ht_key.chain_index) + ops->ruleset_unbind(mlxsw_sp, ruleset->priv); rhashtable_remove_fast(&acl->ruleset_ht, &ruleset->ht_node, mlxsw_sp_acl_ruleset_ht_params); } @@ -211,14 +221,48 @@ static void mlxsw_sp_acl_ruleset_ref_dec(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_acl_ruleset_destroy(mlxsw_sp, ruleset); } +static struct mlxsw_sp_acl_ruleset * +__mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp_acl *acl, struct net_device *dev, + bool ingress, u32 chain_index, + const struct mlxsw_sp_acl_profile_ops *ops) +{ + struct mlxsw_sp_acl_ruleset_ht_key ht_key; + + memset(&ht_key, 0, sizeof(ht_key)); + ht_key.dev = dev; + ht_key.ingress = ingress; + ht_key.chain_index = chain_index; + ht_key.ops = ops; + return rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key, + mlxsw_sp_acl_ruleset_ht_params); +} + struct mlxsw_sp_acl_ruleset * -mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, - struct net_device *dev, bool ingress, +mlxsw_sp_acl_ruleset_lookup(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, + bool ingress, u32 chain_index, + enum mlxsw_sp_acl_profile profile) +{ + const struct mlxsw_sp_acl_profile_ops *ops; + struct mlxsw_sp_acl *acl = mlxsw_sp->acl; + struct mlxsw_sp_acl_ruleset *ruleset; + + ops = acl->ops->profile_ops(mlxsw_sp, profile); + if (!ops) + return ERR_PTR(-EINVAL); + ruleset = __mlxsw_sp_acl_ruleset_lookup(acl, dev, ingress, + chain_index, ops); + if (!ruleset) + return ERR_PTR(-ENOENT); + return ruleset; +} + +struct mlxsw_sp_acl_ruleset * +mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, struct net_device *dev, + bool ingress, u32 chain_index, enum mlxsw_sp_acl_profile profile) { const struct mlxsw_sp_acl_profile_ops *ops; struct mlxsw_sp_acl *acl = mlxsw_sp->acl; - struct mlxsw_sp_acl_ruleset_ht_key ht_key; struct mlxsw_sp_acl_ruleset *ruleset; int err; @@ -226,12 +270,8 @@ mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, if (!ops) return ERR_PTR(-EINVAL); - memset(&ht_key, 0, sizeof(ht_key)); - ht_key.dev = dev; - ht_key.ingress = ingress; - ht_key.ops = ops; - ruleset = rhashtable_lookup_fast(&acl->ruleset_ht, &ht_key, - mlxsw_sp_acl_ruleset_ht_params); + ruleset = __mlxsw_sp_acl_ruleset_lookup(acl, dev, ingress, + chain_index, ops); if (ruleset) { mlxsw_sp_acl_ruleset_ref_inc(ruleset); return ruleset; @@ -239,7 +279,8 @@ mlxsw_sp_acl_ruleset_get(struct mlxsw_sp *mlxsw_sp, ruleset = mlxsw_sp_acl_ruleset_create(mlxsw_sp, ops); if (IS_ERR(ruleset)) return ruleset; - err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, ruleset, dev, ingress); + err = mlxsw_sp_acl_ruleset_bind(mlxsw_sp, ruleset, dev, + ingress, chain_index); if (err) goto err_ruleset_bind; return ruleset; @@ -255,6 +296,13 @@ void mlxsw_sp_acl_ruleset_put(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_acl_ruleset_ref_dec(mlxsw_sp, ruleset); } +u16 mlxsw_sp_acl_ruleset_group_id(struct mlxsw_sp_acl_ruleset *ruleset) +{ + const struct mlxsw_sp_acl_profile_ops *ops = ruleset->ht_key.ops; + + return ops->ruleset_group_id(ruleset->priv); +} + static int mlxsw_sp_acl_rulei_counter_alloc(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_acl_rule_info *rulei) @@ -369,7 +417,7 @@ int mlxsw_sp_acl_rulei_act_fwd(struct mlxsw_sp *mlxsw_sp, local_port = mlxsw_sp_port->local_port; in_port = false; } else { - /* If out_dev is NULL, the called wants to + /* If out_dev is NULL, the caller wants to * set forward to ingress port. */ local_port = 0; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h index 85d5001a5818..fb8031828454 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_flex_keys.h @@ -70,6 +70,9 @@ static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_ipv4_dip[] = { static struct mlxsw_afk_element_inst mlxsw_sp_afk_element_info_ipv4[] = { MLXSW_AFK_ELEMENT_INST_U32(SRC_IP4, 0x00, 0, 32), + MLXSW_AFK_ELEMENT_INST_U32(IP_ECN, 0x04, 4, 2), + MLXSW_AFK_ELEMENT_INST_U32(IP_TTL_, 0x04, 24, 8), + MLXSW_AFK_ELEMENT_INST_U32(IP_DSCP, 0x08, 0, 6), MLXSW_AFK_ELEMENT_INST_U32(TCP_FLAGS, 0x08, 8, 9), /* TCP_CONTROL+TCP_ECN */ }; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c index 61a10f166f97..50b40de1fb91 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.c @@ -295,6 +295,12 @@ mlxsw_sp_acl_tcam_group_unbind(struct mlxsw_sp *mlxsw_sp, mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ppbt), ppbt_pl); } +static u16 +mlxsw_sp_acl_tcam_group_id(struct mlxsw_sp_acl_tcam_group *group) +{ + return group->id; +} + static unsigned int mlxsw_sp_acl_tcam_region_prio(struct mlxsw_sp_acl_tcam_region *region) { @@ -984,6 +990,9 @@ static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv4[] = { MLXSW_AFK_ELEMENT_VID, MLXSW_AFK_ELEMENT_PCP, MLXSW_AFK_ELEMENT_TCP_FLAGS, + MLXSW_AFK_ELEMENT_IP_TTL_, + MLXSW_AFK_ELEMENT_IP_ECN, + MLXSW_AFK_ELEMENT_IP_DSCP, }; static const enum mlxsw_afk_element mlxsw_sp_acl_tcam_pattern_ipv6[] = { @@ -1060,6 +1069,14 @@ mlxsw_sp_acl_tcam_flower_ruleset_unbind(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_acl_tcam_group_unbind(mlxsw_sp, &ruleset->group); } +static u16 +mlxsw_sp_acl_tcam_flower_ruleset_group_id(void *ruleset_priv) +{ + struct mlxsw_sp_acl_tcam_flower_ruleset *ruleset = ruleset_priv; + + return mlxsw_sp_acl_tcam_group_id(&ruleset->group); +} + static int mlxsw_sp_acl_tcam_flower_rule_add(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv, void *rule_priv, @@ -1096,6 +1113,7 @@ static const struct mlxsw_sp_acl_profile_ops mlxsw_sp_acl_tcam_flower_ops = { .ruleset_del = mlxsw_sp_acl_tcam_flower_ruleset_del, .ruleset_bind = mlxsw_sp_acl_tcam_flower_ruleset_bind, .ruleset_unbind = mlxsw_sp_acl_tcam_flower_ruleset_unbind, + .ruleset_group_id = mlxsw_sp_acl_tcam_flower_ruleset_group_id, .rule_priv_size = sizeof(struct mlxsw_sp_acl_tcam_flower_rule), .rule_add = mlxsw_sp_acl_tcam_flower_rule_add, .rule_del = mlxsw_sp_acl_tcam_flower_rule_del, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index af2c65a3fd9f..51e6846da72b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -74,6 +74,9 @@ static struct devlink_dpipe_header mlxsw_sp_dpipe_header_metadata = { static struct devlink_dpipe_header *mlxsw_dpipe_headers[] = { &mlxsw_sp_dpipe_header_metadata, + &devlink_dpipe_header_ethernet, + &devlink_dpipe_header_ipv4, + &devlink_dpipe_header_ipv6, }; static struct devlink_dpipe_headers mlxsw_sp_dpipe_headers = { @@ -114,26 +117,6 @@ static int mlxsw_sp_dpipe_table_erif_matches_dump(void *priv, return devlink_dpipe_match_put(skb, &match); } -static void mlxsw_sp_erif_entry_clear(struct devlink_dpipe_entry *entry) -{ - unsigned int value_count, value_index; - struct devlink_dpipe_value *value; - - value = entry->action_values; - value_count = entry->action_values_count; - for (value_index = 0; value_index < value_count; value_index++) { - kfree(value[value_index].value); - kfree(value[value_index].mask); - } - - value = entry->match_values; - value_count = entry->match_values_count; - for (value_index = 0; value_index < value_count; value_index++) { - kfree(value[value_index].value); - kfree(value[value_index].mask); - } -} - static void mlxsw_sp_erif_match_action_prepare(struct devlink_dpipe_match *match, struct devlink_dpipe_action *action) @@ -215,8 +198,8 @@ static int mlxsw_sp_erif_entry_get(struct mlxsw_sp *mlxsw_sp, } static int -mlxsw_sp_table_erif_entries_dump(void *priv, bool counters_enabled, - struct devlink_dpipe_dump_ctx *dump_ctx) +mlxsw_sp_dpipe_table_erif_entries_dump(void *priv, bool counters_enabled, + struct devlink_dpipe_dump_ctx *dump_ctx) { struct devlink_dpipe_value match_value, action_value; struct devlink_dpipe_action action = {0}; @@ -270,16 +253,16 @@ start_again: goto start_again; rtnl_unlock(); - mlxsw_sp_erif_entry_clear(&entry); + devlink_dpipe_entry_clear(&entry); return 0; err_entry_append: err_entry_get: rtnl_unlock(); - mlxsw_sp_erif_entry_clear(&entry); + devlink_dpipe_entry_clear(&entry); return err; } -static int mlxsw_sp_table_erif_counters_update(void *priv, bool enable) +static int mlxsw_sp_dpipe_table_erif_counters_update(void *priv, bool enable) { struct mlxsw_sp *mlxsw_sp = priv; int i; @@ -301,24 +284,29 @@ static int mlxsw_sp_table_erif_counters_update(void *priv, bool enable) return 0; } +static u64 mlxsw_sp_dpipe_table_erif_size_get(void *priv) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + return MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); +} + static struct devlink_dpipe_table_ops mlxsw_sp_erif_ops = { .matches_dump = mlxsw_sp_dpipe_table_erif_matches_dump, .actions_dump = mlxsw_sp_dpipe_table_erif_actions_dump, - .entries_dump = mlxsw_sp_table_erif_entries_dump, - .counters_set_update = mlxsw_sp_table_erif_counters_update, + .entries_dump = mlxsw_sp_dpipe_table_erif_entries_dump, + .counters_set_update = mlxsw_sp_dpipe_table_erif_counters_update, + .size_get = mlxsw_sp_dpipe_table_erif_size_get, }; static int mlxsw_sp_dpipe_erif_table_init(struct mlxsw_sp *mlxsw_sp) { struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); - u64 table_size; - table_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); return devlink_dpipe_table_register(devlink, MLXSW_SP_DPIPE_TABLE_NAME_ERIF, &mlxsw_sp_erif_ops, - mlxsw_sp, table_size, - false); + mlxsw_sp, false); } static void mlxsw_sp_dpipe_erif_table_fini(struct mlxsw_sp *mlxsw_sp) @@ -328,6 +316,516 @@ static void mlxsw_sp_dpipe_erif_table_fini(struct mlxsw_sp *mlxsw_sp) devlink_dpipe_table_unregister(devlink, MLXSW_SP_DPIPE_TABLE_NAME_ERIF); } +static int mlxsw_sp_dpipe_table_host_matches_dump(struct sk_buff *skb, int type) +{ + struct devlink_dpipe_match match = {0}; + int err; + + match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; + match.header = &mlxsw_sp_dpipe_header_metadata; + match.field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT; + + err = devlink_dpipe_match_put(skb, &match); + if (err) + return err; + + switch (type) { + case AF_INET: + match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; + match.header = &devlink_dpipe_header_ipv4; + match.field_id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP; + break; + case AF_INET6: + match.type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; + match.header = &devlink_dpipe_header_ipv6; + match.field_id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP; + break; + default: + WARN_ON(1); + return -EINVAL; + } + + return devlink_dpipe_match_put(skb, &match); +} + +static int +mlxsw_sp_dpipe_table_host4_matches_dump(void *priv, struct sk_buff *skb) +{ + return mlxsw_sp_dpipe_table_host_matches_dump(skb, AF_INET); +} + +static int +mlxsw_sp_dpipe_table_host_actions_dump(void *priv, struct sk_buff *skb) +{ + struct devlink_dpipe_action action = {0}; + + action.type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY; + action.header = &devlink_dpipe_header_ethernet; + action.field_id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC; + + return devlink_dpipe_action_put(skb, &action); +} + +enum mlxsw_sp_dpipe_table_host_match { + MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF, + MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP, + MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT, +}; + +static void +mlxsw_sp_dpipe_table_host_match_action_prepare(struct devlink_dpipe_match *matches, + struct devlink_dpipe_action *action, + int type) +{ + struct devlink_dpipe_match *match; + + match = &matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF]; + match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; + match->header = &mlxsw_sp_dpipe_header_metadata; + match->field_id = MLXSW_SP_DPIPE_FIELD_METADATA_ERIF_PORT; + + match = &matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP]; + match->type = DEVLINK_DPIPE_MATCH_TYPE_FIELD_EXACT; + switch (type) { + case AF_INET: + match->header = &devlink_dpipe_header_ipv4; + match->field_id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP; + break; + case AF_INET6: + match->header = &devlink_dpipe_header_ipv6; + match->field_id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP; + break; + default: + WARN_ON(1); + return; + } + + action->type = DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY; + action->header = &devlink_dpipe_header_ethernet; + action->field_id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC; +} + +static int +mlxsw_sp_dpipe_table_host_entry_prepare(struct devlink_dpipe_entry *entry, + struct devlink_dpipe_value *match_values, + struct devlink_dpipe_match *matches, + struct devlink_dpipe_value *action_value, + struct devlink_dpipe_action *action, + int type) +{ + struct devlink_dpipe_value *match_value; + struct devlink_dpipe_match *match; + + entry->match_values = match_values; + entry->match_values_count = MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT; + + entry->action_values = action_value; + entry->action_values_count = 1; + + match = &matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF]; + match_value = &match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF]; + + match_value->match = match; + match_value->value_size = sizeof(u32); + match_value->value = kmalloc(match_value->value_size, GFP_KERNEL); + if (!match_value->value) + return -ENOMEM; + + match = &matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP]; + match_value = &match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP]; + + match_value->match = match; + switch (type) { + case AF_INET: + match_value->value_size = sizeof(u32); + break; + case AF_INET6: + match_value->value_size = sizeof(struct in6_addr); + break; + default: + WARN_ON(1); + return -EINVAL; + } + + match_value->value = kmalloc(match_value->value_size, GFP_KERNEL); + if (!match_value->value) + return -ENOMEM; + + action_value->action = action; + action_value->value_size = sizeof(u64); + action_value->value = kmalloc(action_value->value_size, GFP_KERNEL); + if (!action_value->value) + return -ENOMEM; + + return 0; +} + +static void +__mlxsw_sp_dpipe_table_host_entry_fill(struct devlink_dpipe_entry *entry, + struct mlxsw_sp_rif *rif, + unsigned char *ha, void *dip) +{ + struct devlink_dpipe_value *value; + u32 *rif_value; + u8 *ha_value; + + /* Set Match RIF index */ + value = &entry->match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_RIF]; + + rif_value = value->value; + *rif_value = mlxsw_sp_rif_index(rif); + value->mapping_value = mlxsw_sp_rif_dev_ifindex(rif); + value->mapping_valid = true; + + /* Set Match DIP */ + value = &entry->match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_DIP]; + memcpy(value->value, dip, value->value_size); + + /* Set Action DMAC */ + value = entry->action_values; + ha_value = value->value; + ether_addr_copy(ha_value, ha); +} + +static void +mlxsw_sp_dpipe_table_host4_entry_fill(struct devlink_dpipe_entry *entry, + struct mlxsw_sp_neigh_entry *neigh_entry, + struct mlxsw_sp_rif *rif) +{ + unsigned char *ha; + u32 dip; + + ha = mlxsw_sp_neigh_entry_ha(neigh_entry); + dip = mlxsw_sp_neigh4_entry_dip(neigh_entry); + __mlxsw_sp_dpipe_table_host_entry_fill(entry, rif, ha, &dip); +} + +static void +mlxsw_sp_dpipe_table_host6_entry_fill(struct devlink_dpipe_entry *entry, + struct mlxsw_sp_neigh_entry *neigh_entry, + struct mlxsw_sp_rif *rif) +{ + struct in6_addr *dip; + unsigned char *ha; + + ha = mlxsw_sp_neigh_entry_ha(neigh_entry); + dip = mlxsw_sp_neigh6_entry_dip(neigh_entry); + + __mlxsw_sp_dpipe_table_host_entry_fill(entry, rif, ha, dip); +} + +static void +mlxsw_sp_dpipe_table_host_entry_fill(struct mlxsw_sp *mlxsw_sp, + struct devlink_dpipe_entry *entry, + struct mlxsw_sp_neigh_entry *neigh_entry, + struct mlxsw_sp_rif *rif, + int type) +{ + int err; + + switch (type) { + case AF_INET: + mlxsw_sp_dpipe_table_host4_entry_fill(entry, neigh_entry, rif); + break; + case AF_INET6: + mlxsw_sp_dpipe_table_host6_entry_fill(entry, neigh_entry, rif); + break; + default: + WARN_ON(1); + return; + } + + err = mlxsw_sp_neigh_counter_get(mlxsw_sp, neigh_entry, + &entry->counter); + if (!err) + entry->counter_valid = true; +} + +static int +mlxsw_sp_dpipe_table_host_entries_get(struct mlxsw_sp *mlxsw_sp, + struct devlink_dpipe_entry *entry, + bool counters_enabled, + struct devlink_dpipe_dump_ctx *dump_ctx, + int type) +{ + int rif_neigh_count = 0; + int rif_neigh_skip = 0; + int neigh_count = 0; + int rif_count; + int i, j; + int err; + + rtnl_lock(); + i = 0; + rif_count = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); +start_again: + err = devlink_dpipe_entry_ctx_prepare(dump_ctx); + if (err) + goto err_ctx_prepare; + j = 0; + rif_neigh_skip = rif_neigh_count; + for (; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) { + struct mlxsw_sp_rif *rif = mlxsw_sp_rif_by_index(mlxsw_sp, i); + struct mlxsw_sp_neigh_entry *neigh_entry; + + if (!rif) + continue; + + rif_neigh_count = 0; + mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) { + int neigh_type = mlxsw_sp_neigh_entry_type(neigh_entry); + + if (neigh_type != type) + continue; + + if (neigh_type == AF_INET6 && + mlxsw_sp_neigh_ipv6_ignore(neigh_entry)) + continue; + + if (rif_neigh_count < rif_neigh_skip) + goto skip; + + mlxsw_sp_dpipe_table_host_entry_fill(mlxsw_sp, entry, + neigh_entry, rif, + type); + entry->index = neigh_count; + err = devlink_dpipe_entry_ctx_append(dump_ctx, entry); + if (err) { + if (err == -EMSGSIZE) { + if (!j) + goto err_entry_append; + else + goto out; + } + goto err_entry_append; + } + neigh_count++; + j++; +skip: + rif_neigh_count++; + } + rif_neigh_skip = 0; + } +out: + devlink_dpipe_entry_ctx_close(dump_ctx); + if (i != rif_count) + goto start_again; + + rtnl_unlock(); + return 0; + +err_ctx_prepare: +err_entry_append: + rtnl_unlock(); + return err; +} + +static int +mlxsw_sp_dpipe_table_host_entries_dump(struct mlxsw_sp *mlxsw_sp, + bool counters_enabled, + struct devlink_dpipe_dump_ctx *dump_ctx, + int type) +{ + struct devlink_dpipe_value match_values[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT]; + struct devlink_dpipe_match matches[MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT]; + struct devlink_dpipe_value action_value; + struct devlink_dpipe_action action = {0}; + struct devlink_dpipe_entry entry = {0}; + int err; + + memset(matches, 0, MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT * + sizeof(matches[0])); + memset(match_values, 0, MLXSW_SP_DPIPE_TABLE_HOST_MATCH_COUNT * + sizeof(match_values[0])); + memset(&action_value, 0, sizeof(action_value)); + + mlxsw_sp_dpipe_table_host_match_action_prepare(matches, &action, type); + err = mlxsw_sp_dpipe_table_host_entry_prepare(&entry, match_values, + matches, &action_value, + &action, type); + if (err) + goto out; + + err = mlxsw_sp_dpipe_table_host_entries_get(mlxsw_sp, &entry, + counters_enabled, dump_ctx, + type); +out: + devlink_dpipe_entry_clear(&entry); + return err; +} + +static int +mlxsw_sp_dpipe_table_host4_entries_dump(void *priv, bool counters_enabled, + struct devlink_dpipe_dump_ctx *dump_ctx) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + return mlxsw_sp_dpipe_table_host_entries_dump(mlxsw_sp, + counters_enabled, + dump_ctx, AF_INET); +} + +static void +mlxsw_sp_dpipe_table_host_counters_update(struct mlxsw_sp *mlxsw_sp, + bool enable, int type) +{ + int i; + + rtnl_lock(); + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) { + struct mlxsw_sp_rif *rif = mlxsw_sp_rif_by_index(mlxsw_sp, i); + struct mlxsw_sp_neigh_entry *neigh_entry; + + if (!rif) + continue; + mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) { + int neigh_type = mlxsw_sp_neigh_entry_type(neigh_entry); + + if (neigh_type != type) + continue; + + if (neigh_type == AF_INET6 && + mlxsw_sp_neigh_ipv6_ignore(neigh_entry)) + continue; + + mlxsw_sp_neigh_entry_counter_update(mlxsw_sp, + neigh_entry, + enable); + } + } + rtnl_unlock(); +} + +static int mlxsw_sp_dpipe_table_host4_counters_update(void *priv, bool enable) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + mlxsw_sp_dpipe_table_host_counters_update(mlxsw_sp, enable, AF_INET); + return 0; +} + +static u64 +mlxsw_sp_dpipe_table_host_size_get(struct mlxsw_sp *mlxsw_sp, int type) +{ + u64 size = 0; + int i; + + rtnl_lock(); + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); i++) { + struct mlxsw_sp_rif *rif = mlxsw_sp_rif_by_index(mlxsw_sp, i); + struct mlxsw_sp_neigh_entry *neigh_entry; + + if (!rif) + continue; + mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) { + int neigh_type = mlxsw_sp_neigh_entry_type(neigh_entry); + + if (neigh_type != type) + continue; + + if (neigh_type == AF_INET6 && + mlxsw_sp_neigh_ipv6_ignore(neigh_entry)) + continue; + + size++; + } + } + rtnl_unlock(); + + return size; +} + +static u64 mlxsw_sp_dpipe_table_host4_size_get(void *priv) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + return mlxsw_sp_dpipe_table_host_size_get(mlxsw_sp, AF_INET); +} + +static struct devlink_dpipe_table_ops mlxsw_sp_host4_ops = { + .matches_dump = mlxsw_sp_dpipe_table_host4_matches_dump, + .actions_dump = mlxsw_sp_dpipe_table_host_actions_dump, + .entries_dump = mlxsw_sp_dpipe_table_host4_entries_dump, + .counters_set_update = mlxsw_sp_dpipe_table_host4_counters_update, + .size_get = mlxsw_sp_dpipe_table_host4_size_get, +}; + +static int mlxsw_sp_dpipe_host4_table_init(struct mlxsw_sp *mlxsw_sp) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + + return devlink_dpipe_table_register(devlink, + MLXSW_SP_DPIPE_TABLE_NAME_HOST4, + &mlxsw_sp_host4_ops, + mlxsw_sp, false); +} + +static void mlxsw_sp_dpipe_host4_table_fini(struct mlxsw_sp *mlxsw_sp) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + + devlink_dpipe_table_unregister(devlink, + MLXSW_SP_DPIPE_TABLE_NAME_HOST4); +} + +static int +mlxsw_sp_dpipe_table_host6_matches_dump(void *priv, struct sk_buff *skb) +{ + return mlxsw_sp_dpipe_table_host_matches_dump(skb, AF_INET6); +} + +static int +mlxsw_sp_dpipe_table_host6_entries_dump(void *priv, bool counters_enabled, + struct devlink_dpipe_dump_ctx *dump_ctx) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + return mlxsw_sp_dpipe_table_host_entries_dump(mlxsw_sp, + counters_enabled, + dump_ctx, AF_INET6); +} + +static int mlxsw_sp_dpipe_table_host6_counters_update(void *priv, bool enable) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + mlxsw_sp_dpipe_table_host_counters_update(mlxsw_sp, enable, AF_INET6); + return 0; +} + +static u64 mlxsw_sp_dpipe_table_host6_size_get(void *priv) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + return mlxsw_sp_dpipe_table_host_size_get(mlxsw_sp, AF_INET6); +} + +static struct devlink_dpipe_table_ops mlxsw_sp_host6_ops = { + .matches_dump = mlxsw_sp_dpipe_table_host6_matches_dump, + .actions_dump = mlxsw_sp_dpipe_table_host_actions_dump, + .entries_dump = mlxsw_sp_dpipe_table_host6_entries_dump, + .counters_set_update = mlxsw_sp_dpipe_table_host6_counters_update, + .size_get = mlxsw_sp_dpipe_table_host6_size_get, +}; + +static int mlxsw_sp_dpipe_host6_table_init(struct mlxsw_sp *mlxsw_sp) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + + return devlink_dpipe_table_register(devlink, + MLXSW_SP_DPIPE_TABLE_NAME_HOST6, + &mlxsw_sp_host6_ops, + mlxsw_sp, false); +} + +static void mlxsw_sp_dpipe_host6_table_fini(struct mlxsw_sp *mlxsw_sp) +{ + struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + + devlink_dpipe_table_unregister(devlink, + MLXSW_SP_DPIPE_TABLE_NAME_HOST6); +} + int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp) { struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); @@ -339,10 +837,22 @@ int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp) return err; err = mlxsw_sp_dpipe_erif_table_init(mlxsw_sp); if (err) - goto err_erif_register; + goto err_erif_table_init; + + err = mlxsw_sp_dpipe_host4_table_init(mlxsw_sp); + if (err) + goto err_host4_table_init; + + err = mlxsw_sp_dpipe_host6_table_init(mlxsw_sp); + if (err) + goto err_host6_table_init; return 0; -err_erif_register: +err_host6_table_init: + mlxsw_sp_dpipe_host4_table_fini(mlxsw_sp); +err_host4_table_init: + mlxsw_sp_dpipe_erif_table_fini(mlxsw_sp); +err_erif_table_init: devlink_dpipe_headers_unregister(priv_to_devlink(mlxsw_sp->core)); return err; } @@ -351,6 +861,8 @@ void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp) { struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); + mlxsw_sp_dpipe_host6_table_fini(mlxsw_sp); + mlxsw_sp_dpipe_host4_table_fini(mlxsw_sp); mlxsw_sp_dpipe_erif_table_fini(mlxsw_sp); devlink_dpipe_headers_unregister(devlink); } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h index d2089298cba3..283fde4e6783 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.h @@ -35,9 +35,26 @@ #ifndef _MLXSW_PIPELINE_H_ #define _MLXSW_PIPELINE_H_ +#if IS_ENABLED(CONFIG_NET_DEVLINK) + int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp); void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp); +#else + +static inline int mlxsw_sp_dpipe_init(struct mlxsw_sp *mlxsw_sp) +{ + return 0; +} + +static inline void mlxsw_sp_dpipe_fini(struct mlxsw_sp *mlxsw_sp) +{ +} + +#endif + #define MLXSW_SP_DPIPE_TABLE_NAME_ERIF "mlxsw_erif" +#define MLXSW_SP_DPIPE_TABLE_NAME_HOST4 "mlxsw_host4" +#define MLXSW_SP_DPIPE_TABLE_NAME_HOST6 "mlxsw_host6" #endif /* _MLXSW_PIPELINE_H_*/ diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c index 6afbe9ec64e2..bbd238e50f05 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c @@ -109,7 +109,6 @@ static const int mlxsw_sp_sfgc_uc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = { static const int mlxsw_sp_sfgc_bc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = { [MLXSW_REG_SFGC_TYPE_BROADCAST] = 1, - [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6] = 1, [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP] = 1, [MLXSW_REG_SFGC_TYPE_IPV4_LINK_LOCAL] = 1, [MLXSW_REG_SFGC_TYPE_IPV6_ALL_HOST] = 1, @@ -117,6 +116,7 @@ static const int mlxsw_sp_sfgc_bc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = { static const int mlxsw_sp_sfgc_mc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = { [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4] = 1, + [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6] = 1, }; static const int *mlxsw_sp_packet_type_sfgc_types[] = { diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 21bb2bf62d3e..8aace9a06a5d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -45,7 +45,7 @@ #include "core_acl_flex_keys.h" static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, - struct net_device *dev, + struct net_device *dev, bool ingress, struct mlxsw_sp_acl_rule_info *rulei, struct tcf_exts *exts) { @@ -53,7 +53,7 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, LIST_HEAD(actions); int err; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return 0; /* Count action is inserted first */ @@ -71,6 +71,20 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, err = mlxsw_sp_acl_rulei_act_trap(rulei); if (err) return err; + } else if (is_tcf_gact_goto_chain(a)) { + u32 chain_index = tcf_gact_goto_chain_index(a); + struct mlxsw_sp_acl_ruleset *ruleset; + u16 group_id; + + ruleset = mlxsw_sp_acl_ruleset_lookup(mlxsw_sp, dev, + ingress, + chain_index, + MLXSW_SP_ACL_PROFILE_FLOWER); + if (IS_ERR(ruleset)) + return PTR_ERR(ruleset); + + group_id = mlxsw_sp_acl_ruleset_group_id(ruleset); + mlxsw_sp_acl_rulei_act_jump(rulei, group_id); } else if (is_tcf_mirred_egress_redirect(a)) { int ifindex = tcf_mirred_ifindex(a); struct net_device *out_dev; @@ -212,11 +226,46 @@ static int mlxsw_sp_flower_parse_tcp(struct mlxsw_sp *mlxsw_sp, return 0; } +static int mlxsw_sp_flower_parse_ip(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_acl_rule_info *rulei, + struct tc_cls_flower_offload *f, + u16 n_proto) +{ + struct flow_dissector_key_ip *key, *mask; + + if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_IP)) + return 0; + + if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6) { + dev_err(mlxsw_sp->bus_info->dev, "IP keys supported only for IPv4/6\n"); + return -EINVAL; + } + + key = skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_IP, + f->key); + mask = skb_flow_dissector_target(f->dissector, + FLOW_DISSECTOR_KEY_IP, + f->mask); + mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_TTL_, + key->ttl, mask->ttl); + + mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_ECN, + key->tos & 0x3, mask->tos & 0x3); + + mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_IP_DSCP, + key->tos >> 6, mask->tos >> 6); + + return 0; +} + static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, - struct net_device *dev, + struct net_device *dev, bool ingress, struct mlxsw_sp_acl_rule_info *rulei, struct tc_cls_flower_offload *f) { + u16 n_proto_mask = 0; + u16 n_proto_key = 0; u16 addr_type = 0; u8 ip_proto = 0; int err; @@ -229,12 +278,13 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | BIT(FLOW_DISSECTOR_KEY_PORTS) | BIT(FLOW_DISSECTOR_KEY_TCP) | + BIT(FLOW_DISSECTOR_KEY_IP) | BIT(FLOW_DISSECTOR_KEY_VLAN))) { dev_err(mlxsw_sp->bus_info->dev, "Unsupported key\n"); return -EOPNOTSUPP; } - mlxsw_sp_acl_rulei_priority(rulei, f->prio); + mlxsw_sp_acl_rulei_priority(rulei, f->common.prio); if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) { struct flow_dissector_key_control *key = @@ -253,8 +303,8 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, skb_flow_dissector_target(f->dissector, FLOW_DISSECTOR_KEY_BASIC, f->mask); - u16 n_proto_key = ntohs(key->n_proto); - u16 n_proto_mask = ntohs(mask->n_proto); + n_proto_key = ntohs(key->n_proto); + n_proto_mask = ntohs(mask->n_proto); if (n_proto_key == ETH_P_ALL) { n_proto_key = 0; @@ -324,11 +374,16 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, if (err) return err; - return mlxsw_sp_flower_parse_actions(mlxsw_sp, dev, rulei, f->exts); + err = mlxsw_sp_flower_parse_ip(mlxsw_sp, rulei, f, n_proto_key & n_proto_mask); + if (err) + return err; + + return mlxsw_sp_flower_parse_actions(mlxsw_sp, dev, ingress, + rulei, f->exts); } int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, - __be16 protocol, struct tc_cls_flower_offload *f) + struct tc_cls_flower_offload *f) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct net_device *dev = mlxsw_sp_port->dev; @@ -338,6 +393,7 @@ int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, int err; ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, dev, ingress, + f->common.chain_index, MLXSW_SP_ACL_PROFILE_FLOWER); if (IS_ERR(ruleset)) return PTR_ERR(ruleset); @@ -349,7 +405,7 @@ int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, } rulei = mlxsw_sp_acl_rule_rulei(rule); - err = mlxsw_sp_flower_parse(mlxsw_sp, dev, rulei, f); + err = mlxsw_sp_flower_parse(mlxsw_sp, dev, ingress, rulei, f); if (err) goto err_flower_parse; @@ -381,7 +437,7 @@ void mlxsw_sp_flower_destroy(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, struct mlxsw_sp_acl_rule *rule; ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, mlxsw_sp_port->dev, - ingress, + ingress, f->common.chain_index, MLXSW_SP_ACL_PROFILE_FLOWER); if (IS_ERR(ruleset)) return; @@ -407,7 +463,7 @@ int mlxsw_sp_flower_stats(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, int err; ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, mlxsw_sp_port->dev, - ingress, + ingress, f->common.chain_index, MLXSW_SP_ACL_PROFILE_FLOWER); if (WARN_ON(IS_ERR(ruleset))) return -EINVAL; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c new file mode 100644 index 000000000000..702fe945227c --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c @@ -0,0 +1,214 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.c + * Copyright (c) 2017 Mellanox Technologies. All rights reserved. + * Copyright (c) 2017 Petr Machata <petrm@mellanox.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <net/ip_tunnels.h> + +#include "spectrum_ipip.h" + +static bool +mlxsw_sp_ipip_netdev_has_ikey(const struct net_device *ol_dev) +{ + struct ip_tunnel *tun = netdev_priv(ol_dev); + + return !!(tun->parms.i_flags & TUNNEL_KEY); +} + +static bool +mlxsw_sp_ipip_netdev_has_okey(const struct net_device *ol_dev) +{ + struct ip_tunnel *tun = netdev_priv(ol_dev); + + return !!(tun->parms.o_flags & TUNNEL_KEY); +} + +static u32 mlxsw_sp_ipip_netdev_ikey(const struct net_device *ol_dev) +{ + struct ip_tunnel *tun = netdev_priv(ol_dev); + + return mlxsw_sp_ipip_netdev_has_ikey(ol_dev) ? + be32_to_cpu(tun->parms.i_key) : 0; +} + +static u32 mlxsw_sp_ipip_netdev_okey(const struct net_device *ol_dev) +{ + struct ip_tunnel *tun = netdev_priv(ol_dev); + + return mlxsw_sp_ipip_netdev_has_okey(ol_dev) ? + be32_to_cpu(tun->parms.o_key) : 0; +} + +static int +mlxsw_sp_ipip_nexthop_update_gre4(struct mlxsw_sp *mlxsw_sp, u32 adj_index, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + u16 rif_index = mlxsw_sp_ipip_lb_rif_index(ipip_entry->ol_lb); + __be32 daddr4 = mlxsw_sp_ipip_netdev_daddr4(ipip_entry->ol_dev); + char ratr_pl[MLXSW_REG_RATR_LEN]; + + mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY, + true, MLXSW_REG_RATR_TYPE_IPIP, + adj_index, rif_index); + mlxsw_reg_ratr_ipip4_entry_pack(ratr_pl, be32_to_cpu(daddr4)); + + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl); +} + +static int +mlxsw_sp_ipip_fib_entry_op_gre4_rtdp(struct mlxsw_sp *mlxsw_sp, + u32 tunnel_index, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + bool has_ikey = mlxsw_sp_ipip_netdev_has_ikey(ipip_entry->ol_dev); + u16 rif_index = mlxsw_sp_ipip_lb_rif_index(ipip_entry->ol_lb); + u32 ikey = mlxsw_sp_ipip_netdev_ikey(ipip_entry->ol_dev); + char rtdp_pl[MLXSW_REG_RTDP_LEN]; + unsigned int type_check; + u32 daddr4; + + mlxsw_reg_rtdp_pack(rtdp_pl, MLXSW_REG_RTDP_TYPE_IPIP, tunnel_index); + + type_check = has_ikey ? + MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_GRE_KEY : + MLXSW_REG_RTDP_IPIP_TYPE_CHECK_ALLOW_GRE; + + /* Linux demuxes tunnels based on packet SIP (which must match tunnel + * remote IP). Thus configure decap so that it filters out packets that + * are not IPv4 or have the wrong SIP. IPIP_DECAP_ERROR trap is + * generated for packets that fail this criterion. Linux then handles + * such packets in slow path and generates ICMP destination unreachable. + */ + daddr4 = be32_to_cpu(mlxsw_sp_ipip_netdev_daddr4(ipip_entry->ol_dev)); + mlxsw_reg_rtdp_ipip4_pack(rtdp_pl, rif_index, + MLXSW_REG_RTDP_IPIP_SIP_CHECK_FILTER_IPV4, + type_check, has_ikey, daddr4, ikey); + + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rtdp), rtdp_pl); +} + +static int +mlxsw_sp_ipip_fib_entry_op_gre4_ralue(struct mlxsw_sp *mlxsw_sp, + u32 dip, u8 prefix_len, u16 ul_vr_id, + enum mlxsw_reg_ralue_op op, + u32 tunnel_index) +{ + char ralue_pl[MLXSW_REG_RALUE_LEN]; + + mlxsw_reg_ralue_pack4(ralue_pl, MLXSW_REG_RALXX_PROTOCOL_IPV4, op, + ul_vr_id, prefix_len, dip); + mlxsw_reg_ralue_act_ip2me_tun_pack(ralue_pl, tunnel_index); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); +} + +static int mlxsw_sp_ipip_fib_entry_op_gre4(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry, + enum mlxsw_reg_ralue_op op, + u32 tunnel_index) +{ + u16 ul_vr_id = mlxsw_sp_ipip_lb_ul_vr_id(ipip_entry->ol_lb); + __be32 dip; + int err; + + err = mlxsw_sp_ipip_fib_entry_op_gre4_rtdp(mlxsw_sp, tunnel_index, + ipip_entry); + if (err) + return err; + + dip = mlxsw_sp_ipip_netdev_saddr(MLXSW_SP_L3_PROTO_IPV4, + ipip_entry->ol_dev).addr4; + return mlxsw_sp_ipip_fib_entry_op_gre4_ralue(mlxsw_sp, be32_to_cpu(dip), + 32, ul_vr_id, op, + tunnel_index); +} + +static bool mlxsw_sp_ipip_tunnel_complete(enum mlxsw_sp_l3proto proto, + const struct net_device *ol_dev) +{ + union mlxsw_sp_l3addr saddr = mlxsw_sp_ipip_netdev_saddr(proto, ol_dev); + union mlxsw_sp_l3addr daddr = mlxsw_sp_ipip_netdev_daddr(proto, ol_dev); + union mlxsw_sp_l3addr naddr = {0}; + + /* Tunnels with unset local or remote address are valid in Linux and + * used for lightweight tunnels (LWT) and Non-Broadcast Multi-Access + * (NBMA) tunnels. In principle these can be offloaded, but the driver + * currently doesn't support this. So punt. + */ + return memcmp(&saddr, &naddr, sizeof(naddr)) && + memcmp(&daddr, &naddr, sizeof(naddr)); +} + +static bool mlxsw_sp_ipip_can_offload_gre4(const struct mlxsw_sp *mlxsw_sp, + const struct net_device *ol_dev, + enum mlxsw_sp_l3proto ol_proto) +{ + struct ip_tunnel *tunnel = netdev_priv(ol_dev); + __be16 okflags = TUNNEL_KEY; /* We can't offload any other features. */ + bool inherit_ttl = tunnel->parms.iph.ttl == 0; + bool inherit_tos = tunnel->parms.iph.tos & 0x1; + + return (tunnel->parms.i_flags & ~okflags) == 0 && + (tunnel->parms.o_flags & ~okflags) == 0 && + inherit_ttl && inherit_tos && + mlxsw_sp_ipip_tunnel_complete(MLXSW_SP_L3_PROTO_IPV4, ol_dev); +} + +static struct mlxsw_sp_rif_ipip_lb_config +mlxsw_sp_ipip_ol_loopback_config_gre4(struct mlxsw_sp *mlxsw_sp, + const struct net_device *ol_dev) +{ + enum mlxsw_reg_ritr_loopback_ipip_type lb_ipipt; + + lb_ipipt = mlxsw_sp_ipip_netdev_has_okey(ol_dev) ? + MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_KEY_IN_IP : + MLXSW_REG_RITR_LOOPBACK_IPIP_TYPE_IP_IN_GRE_IN_IP; + return (struct mlxsw_sp_rif_ipip_lb_config){ + .lb_ipipt = lb_ipipt, + .okey = mlxsw_sp_ipip_netdev_okey(ol_dev), + .ul_protocol = MLXSW_SP_L3_PROTO_IPV4, + .saddr = mlxsw_sp_ipip_netdev_saddr(MLXSW_SP_L3_PROTO_IPV4, + ol_dev), + }; +} + +static const struct mlxsw_sp_ipip_ops mlxsw_sp_ipip_gre4_ops = { + .dev_type = ARPHRD_IPGRE, + .ul_proto = MLXSW_SP_L3_PROTO_IPV4, + .nexthop_update = mlxsw_sp_ipip_nexthop_update_gre4, + .fib_entry_op = mlxsw_sp_ipip_fib_entry_op_gre4, + .can_offload = mlxsw_sp_ipip_can_offload_gre4, + .ol_loopback_config = mlxsw_sp_ipip_ol_loopback_config_gre4, +}; + +const struct mlxsw_sp_ipip_ops *mlxsw_sp_ipip_ops_arr[] = { + [MLXSW_SP_IPIP_TYPE_GRE4] = &mlxsw_sp_ipip_gre4_ops, +}; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h new file mode 100644 index 000000000000..1c2db831d83b --- /dev/null +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h @@ -0,0 +1,79 @@ +/* + * drivers/net/ethernet/mellanox/mlxsw/spectrum_ipip.h + * Copyright (c) 2017 Mellanox Technologies. All rights reserved. + * Copyright (c) 2017 Petr Machata <petrm@mellanox.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the names of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _MLXSW_IPIP_H_ +#define _MLXSW_IPIP_H_ + +#include "spectrum_router.h" +#include <net/ip_fib.h> + +enum mlxsw_sp_ipip_type { + MLXSW_SP_IPIP_TYPE_GRE4, + MLXSW_SP_IPIP_TYPE_MAX, +}; + +struct mlxsw_sp_ipip_entry { + enum mlxsw_sp_ipip_type ipipt; + struct net_device *ol_dev; /* Overlay. */ + struct mlxsw_sp_rif_ipip_lb *ol_lb; + unsigned int ref_count; /* Number of next hops using the tunnel. */ + struct mlxsw_sp_fib_entry *decap_fib_entry; + struct list_head ipip_list_node; +}; + +struct mlxsw_sp_ipip_ops { + int dev_type; + enum mlxsw_sp_l3proto ul_proto; /* Underlay. */ + + int (*nexthop_update)(struct mlxsw_sp *mlxsw_sp, u32 adj_index, + struct mlxsw_sp_ipip_entry *ipip_entry); + + bool (*can_offload)(const struct mlxsw_sp *mlxsw_sp, + const struct net_device *ol_dev, + enum mlxsw_sp_l3proto ol_proto); + + /* Return a configuration for creating an overlay loopback RIF. */ + struct mlxsw_sp_rif_ipip_lb_config + (*ol_loopback_config)(struct mlxsw_sp *mlxsw_sp, + const struct net_device *ol_dev); + + int (*fib_entry_op)(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry, + enum mlxsw_reg_ralue_op op, + u32 tunnel_index); +}; + +extern const struct mlxsw_sp_ipip_ops *mlxsw_sp_ipip_ops_arr[]; + +#endif /* _MLXSW_IPIP_H_*/ diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 4b2e0fd7d51e..f0fb898533fb 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -1,9 +1,10 @@ /* * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c - * Copyright (c) 2016 Mellanox Technologies. All rights reserved. + * Copyright (c) 2016-2017 Mellanox Technologies. All rights reserved. * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com> * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com> * Copyright (c) 2016 Yotam Gigi <yotamg@mellanox.com> + * Copyright (c) 2017 Petr Machata <petrm@mellanox.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -43,18 +44,27 @@ #include <linux/inetdevice.h> #include <linux/netdevice.h> #include <linux/if_bridge.h> +#include <linux/socket.h> +#include <linux/route.h> #include <net/netevent.h> #include <net/neighbour.h> #include <net/arp.h> #include <net/ip_fib.h> +#include <net/ip6_fib.h> #include <net/fib_rules.h> +#include <net/ip_tunnels.h> #include <net/l3mdev.h> +#include <net/addrconf.h> +#include <net/ndisc.h> +#include <net/ipv6.h> +#include <net/fib_notifier.h> #include "spectrum.h" #include "core.h" #include "reg.h" #include "spectrum_cnt.h" #include "spectrum_dpipe.h" +#include "spectrum_ipip.h" #include "spectrum_router.h" struct mlxsw_sp_vr; @@ -79,9 +89,11 @@ struct mlxsw_sp_router { struct delayed_work nexthop_probe_dw; #define MLXSW_SP_UNRESOLVED_NH_PROBE_INTERVAL 5000 /* ms */ struct list_head nexthop_neighs_list; + struct list_head ipip_list; bool aborted; struct notifier_block fib_nb; const struct mlxsw_sp_rif_ops **rif_ops_arr; + const struct mlxsw_sp_ipip_ops **ipip_ops_arr; }; struct mlxsw_sp_rif { @@ -122,6 +134,17 @@ struct mlxsw_sp_rif_subport { bool lag; }; +struct mlxsw_sp_rif_ipip_lb { + struct mlxsw_sp_rif common; + struct mlxsw_sp_rif_ipip_lb_config lb_config; + u16 ul_vr_id; /* Reserved for Spectrum-2. */ +}; + +struct mlxsw_sp_rif_params_ipip_lb { + struct mlxsw_sp_rif_params common; + struct mlxsw_sp_rif_ipip_lb_config lb_config; +}; + struct mlxsw_sp_rif_ops { enum mlxsw_sp_rif_type type; size_t rif_size; @@ -304,7 +327,7 @@ static struct mlxsw_sp_rif * mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp, const struct net_device *dev); -#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE) +#define MLXSW_SP_PREFIX_COUNT (sizeof(struct in6_addr) * BITS_PER_BYTE + 1) struct mlxsw_sp_prefix_usage { DECLARE_BITMAP(b, MLXSW_SP_PREFIX_COUNT); @@ -314,19 +337,6 @@ struct mlxsw_sp_prefix_usage { for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT) static bool -mlxsw_sp_prefix_usage_subset(struct mlxsw_sp_prefix_usage *prefix_usage1, - struct mlxsw_sp_prefix_usage *prefix_usage2) -{ - unsigned char prefix; - - mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage1) { - if (!test_bit(prefix, prefix_usage2->b)) - return false; - } - return true; -} - -static bool mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1, struct mlxsw_sp_prefix_usage *prefix_usage2) { @@ -371,6 +381,14 @@ enum mlxsw_sp_fib_entry_type { MLXSW_SP_FIB_ENTRY_TYPE_REMOTE, MLXSW_SP_FIB_ENTRY_TYPE_LOCAL, MLXSW_SP_FIB_ENTRY_TYPE_TRAP, + + /* This is a special case of local delivery, where a packet should be + * decapsulated on reception. Note that there is no corresponding ENCAP, + * because that's a type of next hop, not of FIB entry. (There can be + * several next hops in a REMOTE entry, and some of them may be + * encapsulating entries.) + */ + MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP, }; struct mlxsw_sp_nexthop_group; @@ -384,11 +402,9 @@ struct mlxsw_sp_fib_node { struct mlxsw_sp_fib_key key; }; -struct mlxsw_sp_fib_entry_params { - u32 tb_id; - u32 prio; - u8 tos; - u8 type; +struct mlxsw_sp_fib_entry_decap { + struct mlxsw_sp_ipip_entry *ipip_entry; + u32 tunnel_index; }; struct mlxsw_sp_fib_entry { @@ -397,13 +413,26 @@ struct mlxsw_sp_fib_entry { enum mlxsw_sp_fib_entry_type type; struct list_head nexthop_group_node; struct mlxsw_sp_nexthop_group *nh_group; - struct mlxsw_sp_fib_entry_params params; - bool offloaded; + struct mlxsw_sp_fib_entry_decap decap; /* Valid for decap entries. */ +}; + +struct mlxsw_sp_fib4_entry { + struct mlxsw_sp_fib_entry common; + u32 tb_id; + u32 prio; + u8 tos; + u8 type; +}; + +struct mlxsw_sp_fib6_entry { + struct mlxsw_sp_fib_entry common; + struct list_head rt6_list; + unsigned int nrt6; }; -enum mlxsw_sp_l3proto { - MLXSW_SP_L3_PROTO_IPV4, - MLXSW_SP_L3_PROTO_IPV6, +struct mlxsw_sp_rt6 { + struct list_head list; + struct rt6_info *rt; }; struct mlxsw_sp_lpm_tree { @@ -428,6 +457,7 @@ struct mlxsw_sp_vr { u32 tb_id; /* kernel fib table id */ unsigned int rif_count; struct mlxsw_sp_fib *fib4; + struct mlxsw_sp_fib *fib6; }; static const struct rhashtable_params mlxsw_sp_fib_ht_params; @@ -487,15 +517,15 @@ static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp, return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl); } -static int mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_lpm_tree *lpm_tree) +static void mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_lpm_tree *lpm_tree) { char ralta_pl[MLXSW_REG_RALTA_LEN]; mlxsw_reg_ralta_pack(ralta_pl, false, (enum mlxsw_reg_ralxx_protocol) lpm_tree->proto, lpm_tree->id); - return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl); } static int @@ -551,10 +581,10 @@ err_left_struct_set: return ERR_PTR(err); } -static int mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_lpm_tree *lpm_tree) +static void mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_lpm_tree *lpm_tree) { - return mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree); + mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree); } static struct mlxsw_sp_lpm_tree * @@ -571,24 +601,21 @@ mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp, lpm_tree->proto == proto && mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage, prefix_usage)) - goto inc_ref_count; + return lpm_tree; } - lpm_tree = mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, - proto); - if (IS_ERR(lpm_tree)) - return lpm_tree; + return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto); +} -inc_ref_count: +static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree) +{ lpm_tree->ref_count++; - return lpm_tree; } -static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_lpm_tree *lpm_tree) +static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_lpm_tree *lpm_tree) { if (--lpm_tree->ref_count == 0) - return mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree); - return 0; + mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree); } #define MLXSW_SP_LPM_TREE_MIN 1 /* tree 0 is reserved */ @@ -625,7 +652,7 @@ static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp) static bool mlxsw_sp_vr_is_used(const struct mlxsw_sp_vr *vr) { - return !!vr->fib4; + return !!vr->fib4 || !!vr->fib6; } static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp) @@ -642,13 +669,13 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp) } static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp, - const struct mlxsw_sp_fib *fib) + const struct mlxsw_sp_fib *fib, u8 tree_id) { char raltb_pl[MLXSW_REG_RALTB_LEN]; mlxsw_reg_raltb_pack(raltb_pl, fib->vr->id, (enum mlxsw_reg_ralxx_protocol) fib->proto, - fib->lpm_tree->id); + tree_id); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl); } @@ -694,7 +721,7 @@ static struct mlxsw_sp_fib *mlxsw_sp_vr_fib(const struct mlxsw_sp_vr *vr, case MLXSW_SP_L3_PROTO_IPV4: return vr->fib4; case MLXSW_SP_L3_PROTO_IPV6: - BUG_ON(1); + return vr->fib6; } return NULL; } @@ -703,6 +730,7 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp, u32 tb_id) { struct mlxsw_sp_vr *vr; + int err; vr = mlxsw_sp_vr_find_unused(mlxsw_sp); if (!vr) @@ -710,54 +738,26 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp, vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4); if (IS_ERR(vr->fib4)) return ERR_CAST(vr->fib4); + vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6); + if (IS_ERR(vr->fib6)) { + err = PTR_ERR(vr->fib6); + goto err_fib6_create; + } vr->tb_id = tb_id; return vr; -} -static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr) -{ +err_fib6_create: mlxsw_sp_fib_destroy(vr->fib4); vr->fib4 = NULL; + return ERR_PTR(err); } -static int -mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib *fib, - struct mlxsw_sp_prefix_usage *req_prefix_usage) +static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr) { - struct mlxsw_sp_lpm_tree *lpm_tree = fib->lpm_tree; - struct mlxsw_sp_lpm_tree *new_tree; - int err; - - if (mlxsw_sp_prefix_usage_eq(req_prefix_usage, &lpm_tree->prefix_usage)) - return 0; - - new_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage, - fib->proto); - if (IS_ERR(new_tree)) { - /* We failed to get a tree according to the required - * prefix usage. However, the current tree might be still good - * for us if our requirement is subset of the prefixes used - * in the tree. - */ - if (mlxsw_sp_prefix_usage_subset(req_prefix_usage, - &lpm_tree->prefix_usage)) - return 0; - return PTR_ERR(new_tree); - } - - /* Prevent packet loss by overwriting existing binding */ - fib->lpm_tree = new_tree; - err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib); - if (err) - goto err_tree_bind; - mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); - - return 0; - -err_tree_bind: - fib->lpm_tree = lpm_tree; - mlxsw_sp_lpm_tree_put(mlxsw_sp, new_tree); - return err; + mlxsw_sp_fib_destroy(vr->fib6); + vr->fib6 = NULL; + mlxsw_sp_fib_destroy(vr->fib4); + vr->fib4 = NULL; } static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id) @@ -773,10 +773,105 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id) static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr) { - if (!vr->rif_count && list_empty(&vr->fib4->node_list)) + if (!vr->rif_count && list_empty(&vr->fib4->node_list) && + list_empty(&vr->fib6->node_list)) mlxsw_sp_vr_destroy(vr); } +static bool +mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr, + enum mlxsw_sp_l3proto proto, u8 tree_id) +{ + struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto); + + if (!mlxsw_sp_vr_is_used(vr)) + return false; + if (fib->lpm_tree && fib->lpm_tree->id == tree_id) + return true; + return false; +} + +static int mlxsw_sp_vr_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib *fib, + struct mlxsw_sp_lpm_tree *new_tree) +{ + struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree; + int err; + + err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id); + if (err) + return err; + fib->lpm_tree = new_tree; + mlxsw_sp_lpm_tree_hold(new_tree); + mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree); + return 0; +} + +static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib *fib, + struct mlxsw_sp_lpm_tree *new_tree) +{ + struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree; + enum mlxsw_sp_l3proto proto = fib->proto; + u8 old_id, new_id = new_tree->id; + struct mlxsw_sp_vr *vr; + int i, err; + + if (!old_tree) + goto no_replace; + old_id = old_tree->id; + + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { + vr = &mlxsw_sp->router->vrs[i]; + if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, old_id)) + continue; + err = mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp, + mlxsw_sp_vr_fib(vr, proto), + new_tree); + if (err) + goto err_tree_replace; + } + + return 0; + +err_tree_replace: + for (i--; i >= 0; i--) { + if (!mlxsw_sp_vr_lpm_tree_should_replace(vr, proto, new_id)) + continue; + mlxsw_sp_vr_lpm_tree_replace(mlxsw_sp, + mlxsw_sp_vr_fib(vr, proto), + old_tree); + } + return err; + +no_replace: + err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id); + if (err) + return err; + fib->lpm_tree = new_tree; + mlxsw_sp_lpm_tree_hold(new_tree); + return 0; +} + +static void +mlxsw_sp_vrs_prefixes(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_l3proto proto, + struct mlxsw_sp_prefix_usage *req_prefix_usage) +{ + int i; + + for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { + struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i]; + struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto); + unsigned char prefix; + + if (!mlxsw_sp_vr_is_used(vr)) + continue; + mlxsw_sp_prefix_usage_for_each(prefix, &fib->prefix_usage) + mlxsw_sp_prefix_usage_set(req_prefix_usage, prefix); + } +} + static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp) { struct mlxsw_sp_vr *vr; @@ -816,6 +911,374 @@ static void mlxsw_sp_vrs_fini(struct mlxsw_sp *mlxsw_sp) kfree(mlxsw_sp->router->vrs); } +static struct net_device * +__mlxsw_sp_ipip_netdev_ul_dev_get(const struct net_device *ol_dev) +{ + struct ip_tunnel *tun = netdev_priv(ol_dev); + struct net *net = dev_net(ol_dev); + + return __dev_get_by_index(net, tun->parms.link); +} + +static u32 mlxsw_sp_ipip_dev_ul_tb_id(const struct net_device *ol_dev) +{ + struct net_device *d = __mlxsw_sp_ipip_netdev_ul_dev_get(ol_dev); + + if (d) + return l3mdev_fib_table(d) ? : RT_TABLE_MAIN; + else + return l3mdev_fib_table(ol_dev) ? : RT_TABLE_MAIN; +} + +static struct mlxsw_sp_rif * +mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, + const struct mlxsw_sp_rif_params *params); + +static struct mlxsw_sp_rif_ipip_lb * +mlxsw_sp_ipip_ol_ipip_lb_create(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_ipip_type ipipt, + struct net_device *ol_dev) +{ + struct mlxsw_sp_rif_params_ipip_lb lb_params; + const struct mlxsw_sp_ipip_ops *ipip_ops; + struct mlxsw_sp_rif *rif; + + ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipipt]; + lb_params = (struct mlxsw_sp_rif_params_ipip_lb) { + .common.dev = ol_dev, + .common.lag = false, + .lb_config = ipip_ops->ol_loopback_config(mlxsw_sp, ol_dev), + }; + + rif = mlxsw_sp_rif_create(mlxsw_sp, &lb_params.common); + if (IS_ERR(rif)) + return ERR_CAST(rif); + return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common); +} + +static struct mlxsw_sp_ipip_entry * +mlxsw_sp_ipip_entry_alloc(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_ipip_type ipipt, + struct net_device *ol_dev) +{ + struct mlxsw_sp_ipip_entry *ipip_entry; + struct mlxsw_sp_ipip_entry *ret = NULL; + + ipip_entry = kzalloc(sizeof(*ipip_entry), GFP_KERNEL); + if (!ipip_entry) + return ERR_PTR(-ENOMEM); + + ipip_entry->ol_lb = mlxsw_sp_ipip_ol_ipip_lb_create(mlxsw_sp, ipipt, + ol_dev); + if (IS_ERR(ipip_entry->ol_lb)) { + ret = ERR_CAST(ipip_entry->ol_lb); + goto err_ol_ipip_lb_create; + } + + ipip_entry->ipipt = ipipt; + ipip_entry->ol_dev = ol_dev; + + return ipip_entry; + +err_ol_ipip_lb_create: + kfree(ipip_entry); + return ret; +} + +static void +mlxsw_sp_ipip_entry_destroy(struct mlxsw_sp_ipip_entry *ipip_entry) +{ + WARN_ON(ipip_entry->ref_count > 0); + mlxsw_sp_rif_destroy(&ipip_entry->ol_lb->common); + kfree(ipip_entry); +} + +static __be32 +mlxsw_sp_ipip_netdev_saddr4(const struct net_device *ol_dev) +{ + struct ip_tunnel *tun = netdev_priv(ol_dev); + + return tun->parms.iph.saddr; +} + +union mlxsw_sp_l3addr +mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto, + const struct net_device *ol_dev) +{ + switch (proto) { + case MLXSW_SP_L3_PROTO_IPV4: + return (union mlxsw_sp_l3addr) { + .addr4 = mlxsw_sp_ipip_netdev_saddr4(ol_dev), + }; + case MLXSW_SP_L3_PROTO_IPV6: + break; + }; + + WARN_ON(1); + return (union mlxsw_sp_l3addr) { + .addr4 = 0, + }; +} + +__be32 mlxsw_sp_ipip_netdev_daddr4(const struct net_device *ol_dev) +{ + struct ip_tunnel *tun = netdev_priv(ol_dev); + + return tun->parms.iph.daddr; +} + +union mlxsw_sp_l3addr +mlxsw_sp_ipip_netdev_daddr(enum mlxsw_sp_l3proto proto, + const struct net_device *ol_dev) +{ + switch (proto) { + case MLXSW_SP_L3_PROTO_IPV4: + return (union mlxsw_sp_l3addr) { + .addr4 = mlxsw_sp_ipip_netdev_daddr4(ol_dev), + }; + case MLXSW_SP_L3_PROTO_IPV6: + break; + }; + + WARN_ON(1); + return (union mlxsw_sp_l3addr) { + .addr4 = 0, + }; +} + +static bool mlxsw_sp_l3addr_eq(const union mlxsw_sp_l3addr *addr1, + const union mlxsw_sp_l3addr *addr2) +{ + return !memcmp(addr1, addr2, sizeof(*addr1)); +} + +static bool +mlxsw_sp_ipip_entry_saddr_matches(struct mlxsw_sp *mlxsw_sp, + const enum mlxsw_sp_l3proto ul_proto, + union mlxsw_sp_l3addr saddr, + u32 ul_tb_id, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + u32 tun_ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev); + enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt; + union mlxsw_sp_l3addr tun_saddr; + + if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto) + return false; + + tun_saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ipip_entry->ol_dev); + return tun_ul_tb_id == ul_tb_id && + mlxsw_sp_l3addr_eq(&tun_saddr, &saddr); +} + +static int +mlxsw_sp_fib_entry_decap_init(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + u32 tunnel_index; + int err; + + err = mlxsw_sp_kvdl_alloc(mlxsw_sp, 1, &tunnel_index); + if (err) + return err; + + ipip_entry->decap_fib_entry = fib_entry; + fib_entry->decap.ipip_entry = ipip_entry; + fib_entry->decap.tunnel_index = tunnel_index; + return 0; +} + +static void mlxsw_sp_fib_entry_decap_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) +{ + /* Unlink this node from the IPIP entry that it's the decap entry of. */ + fib_entry->decap.ipip_entry->decap_fib_entry = NULL; + fib_entry->decap.ipip_entry = NULL; + mlxsw_sp_kvdl_free(mlxsw_sp, fib_entry->decap.tunnel_index); +} + +static struct mlxsw_sp_fib_node * +mlxsw_sp_fib_node_lookup(struct mlxsw_sp_fib *fib, const void *addr, + size_t addr_len, unsigned char prefix_len); +static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry); + +static void +mlxsw_sp_ipip_entry_demote_decap(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + struct mlxsw_sp_fib_entry *fib_entry = ipip_entry->decap_fib_entry; + + mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, fib_entry); + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; + + mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); +} + +static void +mlxsw_sp_ipip_entry_promote_decap(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry, + struct mlxsw_sp_fib_entry *decap_fib_entry) +{ + if (mlxsw_sp_fib_entry_decap_init(mlxsw_sp, decap_fib_entry, + ipip_entry)) + return; + decap_fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP; + + if (mlxsw_sp_fib_entry_update(mlxsw_sp, decap_fib_entry)) + mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry); +} + +/* Given an IPIP entry, find the corresponding decap route. */ +static struct mlxsw_sp_fib_entry * +mlxsw_sp_ipip_entry_find_decap(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + static struct mlxsw_sp_fib_node *fib_node; + const struct mlxsw_sp_ipip_ops *ipip_ops; + struct mlxsw_sp_fib_entry *fib_entry; + unsigned char saddr_prefix_len; + union mlxsw_sp_l3addr saddr; + struct mlxsw_sp_fib *ul_fib; + struct mlxsw_sp_vr *ul_vr; + const void *saddrp; + size_t saddr_len; + u32 ul_tb_id; + u32 saddr4; + + ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt]; + + ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ipip_entry->ol_dev); + ul_vr = mlxsw_sp_vr_find(mlxsw_sp, ul_tb_id); + if (!ul_vr) + return NULL; + + ul_fib = mlxsw_sp_vr_fib(ul_vr, ipip_ops->ul_proto); + saddr = mlxsw_sp_ipip_netdev_saddr(ipip_ops->ul_proto, + ipip_entry->ol_dev); + + switch (ipip_ops->ul_proto) { + case MLXSW_SP_L3_PROTO_IPV4: + saddr4 = be32_to_cpu(saddr.addr4); + saddrp = &saddr4; + saddr_len = 4; + saddr_prefix_len = 32; + break; + case MLXSW_SP_L3_PROTO_IPV6: + WARN_ON(1); + return NULL; + } + + fib_node = mlxsw_sp_fib_node_lookup(ul_fib, saddrp, saddr_len, + saddr_prefix_len); + if (!fib_node || list_empty(&fib_node->entry_list)) + return NULL; + + fib_entry = list_first_entry(&fib_node->entry_list, + struct mlxsw_sp_fib_entry, list); + if (fib_entry->type != MLXSW_SP_FIB_ENTRY_TYPE_TRAP) + return NULL; + + return fib_entry; +} + +static struct mlxsw_sp_ipip_entry * +mlxsw_sp_ipip_entry_get(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_ipip_type ipipt, + struct net_device *ol_dev) +{ + u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(ol_dev); + struct mlxsw_sp_router *router = mlxsw_sp->router; + struct mlxsw_sp_fib_entry *decap_fib_entry; + struct mlxsw_sp_ipip_entry *ipip_entry; + enum mlxsw_sp_l3proto ul_proto; + union mlxsw_sp_l3addr saddr; + + list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list, + ipip_list_node) { + if (ipip_entry->ol_dev == ol_dev) + goto inc_ref_count; + + /* The configuration where several tunnels have the same local + * address in the same underlay table needs special treatment in + * the HW. That is currently not implemented in the driver. + */ + ul_proto = router->ipip_ops_arr[ipip_entry->ipipt]->ul_proto; + saddr = mlxsw_sp_ipip_netdev_saddr(ul_proto, ol_dev); + if (mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, saddr, + ul_tb_id, ipip_entry)) + return ERR_PTR(-EEXIST); + } + + ipip_entry = mlxsw_sp_ipip_entry_alloc(mlxsw_sp, ipipt, ol_dev); + if (IS_ERR(ipip_entry)) + return ipip_entry; + + decap_fib_entry = mlxsw_sp_ipip_entry_find_decap(mlxsw_sp, ipip_entry); + if (decap_fib_entry) + mlxsw_sp_ipip_entry_promote_decap(mlxsw_sp, ipip_entry, + decap_fib_entry); + + list_add_tail(&ipip_entry->ipip_list_node, + &mlxsw_sp->router->ipip_list); + +inc_ref_count: + ++ipip_entry->ref_count; + return ipip_entry; +} + +static void +mlxsw_sp_ipip_entry_put(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + if (--ipip_entry->ref_count == 0) { + list_del(&ipip_entry->ipip_list_node); + if (ipip_entry->decap_fib_entry) + mlxsw_sp_ipip_entry_demote_decap(mlxsw_sp, ipip_entry); + mlxsw_sp_ipip_entry_destroy(ipip_entry); + } +} + +static bool +mlxsw_sp_ipip_entry_matches_decap(struct mlxsw_sp *mlxsw_sp, + const struct net_device *ul_dev, + enum mlxsw_sp_l3proto ul_proto, + union mlxsw_sp_l3addr ul_dip, + struct mlxsw_sp_ipip_entry *ipip_entry) +{ + u32 ul_tb_id = l3mdev_fib_table(ul_dev) ? : RT_TABLE_MAIN; + enum mlxsw_sp_ipip_type ipipt = ipip_entry->ipipt; + struct net_device *ipip_ul_dev; + + if (mlxsw_sp->router->ipip_ops_arr[ipipt]->ul_proto != ul_proto) + return false; + + ipip_ul_dev = __mlxsw_sp_ipip_netdev_ul_dev_get(ipip_entry->ol_dev); + return mlxsw_sp_ipip_entry_saddr_matches(mlxsw_sp, ul_proto, ul_dip, + ul_tb_id, ipip_entry) && + (!ipip_ul_dev || ipip_ul_dev == ul_dev); +} + +/* Given decap parameters, find the corresponding IPIP entry. */ +static struct mlxsw_sp_ipip_entry * +mlxsw_sp_ipip_entry_find_by_decap(struct mlxsw_sp *mlxsw_sp, + const struct net_device *ul_dev, + enum mlxsw_sp_l3proto ul_proto, + union mlxsw_sp_l3addr ul_dip) +{ + struct mlxsw_sp_ipip_entry *ipip_entry; + + list_for_each_entry(ipip_entry, &mlxsw_sp->router->ipip_list, + ipip_list_node) + if (mlxsw_sp_ipip_entry_matches_decap(mlxsw_sp, ul_dev, + ul_proto, ul_dip, + ipip_entry)) + return ipip_entry; + + return NULL; +} + struct mlxsw_sp_neigh_key { struct neighbour *n; }; @@ -831,6 +1294,8 @@ struct mlxsw_sp_neigh_entry { * this neigh entry */ struct list_head nexthop_neighs_list_node; + unsigned int counter_index; + bool counter_valid; }; static const struct rhashtable_params mlxsw_sp_neigh_ht_params = { @@ -839,6 +1304,62 @@ static const struct rhashtable_params mlxsw_sp_neigh_ht_params = { .key_len = sizeof(struct mlxsw_sp_neigh_key), }; +struct mlxsw_sp_neigh_entry * +mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif, + struct mlxsw_sp_neigh_entry *neigh_entry) +{ + if (!neigh_entry) { + if (list_empty(&rif->neigh_list)) + return NULL; + else + return list_first_entry(&rif->neigh_list, + typeof(*neigh_entry), + rif_list_node); + } + if (neigh_entry->rif_list_node.next == &rif->neigh_list) + return NULL; + return list_next_entry(neigh_entry, rif_list_node); +} + +int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry) +{ + return neigh_entry->key.n->tbl->family; +} + +unsigned char * +mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry) +{ + return neigh_entry->ha; +} + +u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry) +{ + struct neighbour *n; + + n = neigh_entry->key.n; + return ntohl(*((__be32 *) n->primary_key)); +} + +struct in6_addr * +mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry) +{ + struct neighbour *n; + + n = neigh_entry->key.n; + return (struct in6_addr *) &n->primary_key; +} + +int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry, + u64 *p_counter) +{ + if (!neigh_entry->counter_valid) + return -EINVAL; + + return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index, + p_counter, NULL); +} + static struct mlxsw_sp_neigh_entry * mlxsw_sp_neigh_entry_alloc(struct mlxsw_sp *mlxsw_sp, struct neighbour *n, u16 rif) @@ -879,6 +1400,53 @@ mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_neigh_ht_params); } +static bool +mlxsw_sp_neigh_counter_should_alloc(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry) +{ + struct devlink *devlink; + const char *table_name; + + switch (mlxsw_sp_neigh_entry_type(neigh_entry)) { + case AF_INET: + table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST4; + break; + case AF_INET6: + table_name = MLXSW_SP_DPIPE_TABLE_NAME_HOST6; + break; + default: + WARN_ON(1); + return false; + } + + devlink = priv_to_devlink(mlxsw_sp->core); + return devlink_dpipe_table_counter_enabled(devlink, table_name); +} + +static void +mlxsw_sp_neigh_counter_alloc(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry) +{ + if (!mlxsw_sp_neigh_counter_should_alloc(mlxsw_sp, neigh_entry)) + return; + + if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &neigh_entry->counter_index)) + return; + + neigh_entry->counter_valid = true; +} + +static void +mlxsw_sp_neigh_counter_free(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry) +{ + if (!neigh_entry->counter_valid) + return; + mlxsw_sp_flow_counter_free(mlxsw_sp, + neigh_entry->counter_index); + neigh_entry->counter_valid = false; +} + static struct mlxsw_sp_neigh_entry * mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n) { @@ -898,6 +1466,7 @@ mlxsw_sp_neigh_entry_create(struct mlxsw_sp *mlxsw_sp, struct neighbour *n) if (err) goto err_neigh_entry_insert; + mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry); list_add(&neigh_entry->rif_list_node, &rif->neigh_list); return neigh_entry; @@ -912,6 +1481,7 @@ mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_neigh_entry *neigh_entry) { list_del(&neigh_entry->rif_list_node); + mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry); mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry); mlxsw_sp_neigh_entry_free(neigh_entry); } @@ -929,8 +1499,15 @@ mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, struct neighbour *n) static void mlxsw_sp_router_neighs_update_interval_init(struct mlxsw_sp *mlxsw_sp) { - unsigned long interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME); + unsigned long interval; +#if IS_ENABLED(CONFIG_IPV6) + interval = min_t(unsigned long, + NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME), + NEIGH_VAR(&nd_tbl.parms, DELAY_PROBE_TIME)); +#else + interval = NEIGH_VAR(&arp_tbl.parms, DELAY_PROBE_TIME); +#endif mlxsw_sp->router->neighs_update.interval = jiffies_to_msecs(interval); } @@ -965,6 +1542,44 @@ static void mlxsw_sp_router_neigh_ent_ipv4_process(struct mlxsw_sp *mlxsw_sp, neigh_release(n); } +#if IS_ENABLED(CONFIG_IPV6) +static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp, + char *rauhtd_pl, + int rec_index) +{ + struct net_device *dev; + struct neighbour *n; + struct in6_addr dip; + u16 rif; + + mlxsw_reg_rauhtd_ent_ipv6_unpack(rauhtd_pl, rec_index, &rif, + (char *) &dip); + + if (!mlxsw_sp->router->rifs[rif]) { + dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect RIF in neighbour entry\n"); + return; + } + + dev = mlxsw_sp->router->rifs[rif]->dev; + n = neigh_lookup(&nd_tbl, &dip, dev); + if (!n) { + netdev_err(dev, "Failed to find matching neighbour for IP=%pI6c\n", + &dip); + return; + } + + netdev_dbg(dev, "Updating neighbour with IP=%pI6c\n", &dip); + neigh_event_send(n, NULL); + neigh_release(n); +} +#else +static void mlxsw_sp_router_neigh_ent_ipv6_process(struct mlxsw_sp *mlxsw_sp, + char *rauhtd_pl, + int rec_index) +{ +} +#endif + static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp, char *rauhtd_pl, int rec_index) @@ -988,6 +1603,15 @@ static void mlxsw_sp_router_neigh_rec_ipv4_process(struct mlxsw_sp *mlxsw_sp, } +static void mlxsw_sp_router_neigh_rec_ipv6_process(struct mlxsw_sp *mlxsw_sp, + char *rauhtd_pl, + int rec_index) +{ + /* One record contains one entry. */ + mlxsw_sp_router_neigh_ent_ipv6_process(mlxsw_sp, rauhtd_pl, + rec_index); +} + static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp, char *rauhtd_pl, int rec_index) { @@ -997,7 +1621,8 @@ static void mlxsw_sp_router_neigh_rec_process(struct mlxsw_sp *mlxsw_sp, rec_index); break; case MLXSW_REG_RAUHTD_TYPE_IPV6: - WARN_ON_ONCE(1); + mlxsw_sp_router_neigh_rec_ipv6_process(mlxsw_sp, rauhtd_pl, + rec_index); break; } } @@ -1022,22 +1647,20 @@ static bool mlxsw_sp_router_rauhtd_is_full(char *rauhtd_pl) return false; } -static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp) +static int +__mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp, + char *rauhtd_pl, + enum mlxsw_reg_rauhtd_type type) { - char *rauhtd_pl; - u8 num_rec; - int i, err; - - rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL); - if (!rauhtd_pl) - return -ENOMEM; + int i, num_rec; + int err; /* Make sure the neighbour's netdev isn't removed in the * process. */ rtnl_lock(); do { - mlxsw_reg_rauhtd_pack(rauhtd_pl, MLXSW_REG_RAUHTD_TYPE_IPV4); + mlxsw_reg_rauhtd_pack(rauhtd_pl, type); err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(rauhtd), rauhtd_pl); if (err) { @@ -1051,6 +1674,27 @@ static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp) } while (mlxsw_sp_router_rauhtd_is_full(rauhtd_pl)); rtnl_unlock(); + return err; +} + +static int mlxsw_sp_router_neighs_update_rauhtd(struct mlxsw_sp *mlxsw_sp) +{ + enum mlxsw_reg_rauhtd_type type; + char *rauhtd_pl; + int err; + + rauhtd_pl = kmalloc(MLXSW_REG_RAUHTD_LEN, GFP_KERNEL); + if (!rauhtd_pl) + return -ENOMEM; + + type = MLXSW_REG_RAUHTD_TYPE_IPV4; + err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type); + if (err) + goto out; + + type = MLXSW_REG_RAUHTD_TYPE_IPV6; + err = __mlxsw_sp_router_neighs_update_rauhtd(mlxsw_sp, rauhtd_pl, type); +out: kfree(rauhtd_pl); return err; } @@ -1143,9 +1787,43 @@ mlxsw_sp_router_neigh_entry_op4(struct mlxsw_sp *mlxsw_sp, mlxsw_reg_rauht_pack4(rauht_pl, op, neigh_entry->rif, neigh_entry->ha, dip); + if (neigh_entry->counter_valid) + mlxsw_reg_rauht_pack_counter(rauht_pl, + neigh_entry->counter_index); + mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl); +} + +static void +mlxsw_sp_router_neigh_entry_op6(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry, + enum mlxsw_reg_rauht_op op) +{ + struct neighbour *n = neigh_entry->key.n; + char rauht_pl[MLXSW_REG_RAUHT_LEN]; + const char *dip = n->primary_key; + + mlxsw_reg_rauht_pack6(rauht_pl, op, neigh_entry->rif, neigh_entry->ha, + dip); + if (neigh_entry->counter_valid) + mlxsw_reg_rauht_pack_counter(rauht_pl, + neigh_entry->counter_index); mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl); } +bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry) +{ + struct neighbour *n = neigh_entry->key.n; + + /* Packets with a link-local destination address are trapped + * after LPM lookup and never reach the neighbour table, so + * there is no need to program such neighbours to the device. + */ + if (ipv6_addr_type((struct in6_addr *) &n->primary_key) & + IPV6_ADDR_LINKLOCAL) + return true; + return false; +} + static void mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_neigh_entry *neigh_entry, @@ -1154,11 +1832,29 @@ mlxsw_sp_neigh_entry_update(struct mlxsw_sp *mlxsw_sp, if (!adding && !neigh_entry->connected) return; neigh_entry->connected = adding; - if (neigh_entry->key.n->tbl == &arp_tbl) + if (neigh_entry->key.n->tbl->family == AF_INET) { mlxsw_sp_router_neigh_entry_op4(mlxsw_sp, neigh_entry, mlxsw_sp_rauht_op(adding)); - else + } else if (neigh_entry->key.n->tbl->family == AF_INET6) { + if (mlxsw_sp_neigh_ipv6_ignore(neigh_entry)) + return; + mlxsw_sp_router_neigh_entry_op6(mlxsw_sp, neigh_entry, + mlxsw_sp_rauht_op(adding)); + } else { WARN_ON_ONCE(1); + } +} + +void +mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry, + bool adding) +{ + if (adding) + mlxsw_sp_neigh_counter_alloc(mlxsw_sp, neigh_entry); + else + mlxsw_sp_neigh_counter_free(mlxsw_sp, neigh_entry); + mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, true); } struct mlxsw_sp_neigh_event_work { @@ -1227,7 +1923,8 @@ int mlxsw_sp_router_netevent_event(struct notifier_block *unused, p = ptr; /* We don't care about changes in the default table. */ - if (!p->dev || p->tbl != &arp_tbl) + if (!p->dev || (p->tbl->family != AF_INET && + p->tbl->family != AF_INET6)) return NOTIFY_DONE; /* We are in atomic context and can't take RTNL mutex, @@ -1246,7 +1943,7 @@ int mlxsw_sp_router_netevent_event(struct notifier_block *unused, case NETEVENT_NEIGH_UPDATE: n = ptr; - if (n->tbl != &arp_tbl) + if (n->tbl->family != AF_INET && n->tbl->family != AF_INET6) return NOTIFY_DONE; mlxsw_sp_port = mlxsw_sp_port_lower_dev_hold(n->dev); @@ -1307,27 +2004,23 @@ static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp) rhashtable_destroy(&mlxsw_sp->router->neigh_ht); } -static int mlxsw_sp_neigh_rif_flush(struct mlxsw_sp *mlxsw_sp, - const struct mlxsw_sp_rif *rif) -{ - char rauht_pl[MLXSW_REG_RAUHT_LEN]; - - mlxsw_reg_rauht_pack(rauht_pl, MLXSW_REG_RAUHT_OP_WRITE_DELETE_ALL, - rif->rif_index, rif->addr); - return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rauht), rauht_pl); -} - static void mlxsw_sp_neigh_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_rif *rif) { struct mlxsw_sp_neigh_entry *neigh_entry, *tmp; - mlxsw_sp_neigh_rif_flush(mlxsw_sp, rif); list_for_each_entry_safe(neigh_entry, tmp, &rif->neigh_list, - rif_list_node) + rif_list_node) { + mlxsw_sp_neigh_entry_update(mlxsw_sp, neigh_entry, false); mlxsw_sp_neigh_entry_destroy(mlxsw_sp, neigh_entry); + } } +enum mlxsw_sp_nexthop_type { + MLXSW_SP_NEXTHOP_TYPE_ETH, + MLXSW_SP_NEXTHOP_TYPE_IPIP, +}; + struct mlxsw_sp_nexthop_key { struct fib_nh *fib_nh; }; @@ -1340,6 +2033,8 @@ struct mlxsw_sp_nexthop { */ struct rhash_head ht_node; struct mlxsw_sp_nexthop_key key; + unsigned char gw_addr[sizeof(struct in6_addr)]; + int ifindex; struct mlxsw_sp_rif *rif; u8 should_offload:1, /* set indicates this neigh is connected and * should be put to KVD linear area of this group. @@ -1350,17 +2045,18 @@ struct mlxsw_sp_nexthop { update:1; /* set indicates that MAC of this neigh should be * updated in HW */ - struct mlxsw_sp_neigh_entry *neigh_entry; -}; - -struct mlxsw_sp_nexthop_group_key { - struct fib_info *fi; + enum mlxsw_sp_nexthop_type type; + union { + struct mlxsw_sp_neigh_entry *neigh_entry; + struct mlxsw_sp_ipip_entry *ipip_entry; + }; }; struct mlxsw_sp_nexthop_group { + void *priv; struct rhash_head ht_node; struct list_head fib_list; /* list of fib entries that use this group */ - struct mlxsw_sp_nexthop_group_key key; + struct neigh_table *neigh_tbl; u8 adj_index_valid:1, gateway:1; /* routes using the group use a gateway */ u32 adj_index; @@ -1370,15 +2066,154 @@ struct mlxsw_sp_nexthop_group { #define nh_rif nexthops[0].rif }; +static struct fib_info * +mlxsw_sp_nexthop4_group_fi(const struct mlxsw_sp_nexthop_group *nh_grp) +{ + return nh_grp->priv; +} + +struct mlxsw_sp_nexthop_group_cmp_arg { + enum mlxsw_sp_l3proto proto; + union { + struct fib_info *fi; + struct mlxsw_sp_fib6_entry *fib6_entry; + }; +}; + +static bool +mlxsw_sp_nexthop6_group_has_nexthop(const struct mlxsw_sp_nexthop_group *nh_grp, + const struct in6_addr *gw, int ifindex) +{ + int i; + + for (i = 0; i < nh_grp->count; i++) { + const struct mlxsw_sp_nexthop *nh; + + nh = &nh_grp->nexthops[i]; + if (nh->ifindex == ifindex && + ipv6_addr_equal(gw, (struct in6_addr *) nh->gw_addr)) + return true; + } + + return false; +} + +static bool +mlxsw_sp_nexthop6_group_cmp(const struct mlxsw_sp_nexthop_group *nh_grp, + const struct mlxsw_sp_fib6_entry *fib6_entry) +{ + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + + if (nh_grp->count != fib6_entry->nrt6) + return false; + + list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { + struct in6_addr *gw; + int ifindex; + + ifindex = mlxsw_sp_rt6->rt->dst.dev->ifindex; + gw = &mlxsw_sp_rt6->rt->rt6i_gateway; + if (!mlxsw_sp_nexthop6_group_has_nexthop(nh_grp, gw, ifindex)) + return false; + } + + return true; +} + +static int +mlxsw_sp_nexthop_group_cmp(struct rhashtable_compare_arg *arg, const void *ptr) +{ + const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = arg->key; + const struct mlxsw_sp_nexthop_group *nh_grp = ptr; + + switch (cmp_arg->proto) { + case MLXSW_SP_L3_PROTO_IPV4: + return cmp_arg->fi != mlxsw_sp_nexthop4_group_fi(nh_grp); + case MLXSW_SP_L3_PROTO_IPV6: + return !mlxsw_sp_nexthop6_group_cmp(nh_grp, + cmp_arg->fib6_entry); + default: + WARN_ON(1); + return 1; + } +} + +static int +mlxsw_sp_nexthop_group_type(const struct mlxsw_sp_nexthop_group *nh_grp) +{ + return nh_grp->neigh_tbl->family; +} + +static u32 mlxsw_sp_nexthop_group_hash_obj(const void *data, u32 len, u32 seed) +{ + const struct mlxsw_sp_nexthop_group *nh_grp = data; + const struct mlxsw_sp_nexthop *nh; + struct fib_info *fi; + unsigned int val; + int i; + + switch (mlxsw_sp_nexthop_group_type(nh_grp)) { + case AF_INET: + fi = mlxsw_sp_nexthop4_group_fi(nh_grp); + return jhash(&fi, sizeof(fi), seed); + case AF_INET6: + val = nh_grp->count; + for (i = 0; i < nh_grp->count; i++) { + nh = &nh_grp->nexthops[i]; + val ^= nh->ifindex; + } + return jhash(&val, sizeof(val), seed); + default: + WARN_ON(1); + return 0; + } +} + +static u32 +mlxsw_sp_nexthop6_group_hash(struct mlxsw_sp_fib6_entry *fib6_entry, u32 seed) +{ + unsigned int val = fib6_entry->nrt6; + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + struct net_device *dev; + + list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { + dev = mlxsw_sp_rt6->rt->dst.dev; + val ^= dev->ifindex; + } + + return jhash(&val, sizeof(val), seed); +} + +static u32 +mlxsw_sp_nexthop_group_hash(const void *data, u32 len, u32 seed) +{ + const struct mlxsw_sp_nexthop_group_cmp_arg *cmp_arg = data; + + switch (cmp_arg->proto) { + case MLXSW_SP_L3_PROTO_IPV4: + return jhash(&cmp_arg->fi, sizeof(cmp_arg->fi), seed); + case MLXSW_SP_L3_PROTO_IPV6: + return mlxsw_sp_nexthop6_group_hash(cmp_arg->fib6_entry, seed); + default: + WARN_ON(1); + return 0; + } +} + static const struct rhashtable_params mlxsw_sp_nexthop_group_ht_params = { - .key_offset = offsetof(struct mlxsw_sp_nexthop_group, key), .head_offset = offsetof(struct mlxsw_sp_nexthop_group, ht_node), - .key_len = sizeof(struct mlxsw_sp_nexthop_group_key), + .hashfn = mlxsw_sp_nexthop_group_hash, + .obj_hashfn = mlxsw_sp_nexthop_group_hash_obj, + .obj_cmpfn = mlxsw_sp_nexthop_group_cmp, }; static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group *nh_grp) { + if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 && + !nh_grp->gateway) + return 0; + return rhashtable_insert_fast(&mlxsw_sp->router->nexthop_group_ht, &nh_grp->ht_node, mlxsw_sp_nexthop_group_ht_params); @@ -1387,16 +2222,38 @@ static int mlxsw_sp_nexthop_group_insert(struct mlxsw_sp *mlxsw_sp, static void mlxsw_sp_nexthop_group_remove(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group *nh_grp) { + if (mlxsw_sp_nexthop_group_type(nh_grp) == AF_INET6 && + !nh_grp->gateway) + return; + rhashtable_remove_fast(&mlxsw_sp->router->nexthop_group_ht, &nh_grp->ht_node, mlxsw_sp_nexthop_group_ht_params); } static struct mlxsw_sp_nexthop_group * -mlxsw_sp_nexthop_group_lookup(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_nexthop_group_key key) +mlxsw_sp_nexthop4_group_lookup(struct mlxsw_sp *mlxsw_sp, + struct fib_info *fi) +{ + struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg; + + cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV4; + cmp_arg.fi = fi; + return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht, + &cmp_arg, + mlxsw_sp_nexthop_group_ht_params); +} + +static struct mlxsw_sp_nexthop_group * +mlxsw_sp_nexthop6_group_lookup(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry) { - return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht, &key, + struct mlxsw_sp_nexthop_group_cmp_arg cmp_arg; + + cmp_arg.proto = MLXSW_SP_L3_PROTO_IPV6; + cmp_arg.fib6_entry = fib6_entry; + return rhashtable_lookup_fast(&mlxsw_sp->router->nexthop_group_ht, + &cmp_arg, mlxsw_sp_nexthop_group_ht_params); } @@ -1473,15 +2330,26 @@ static int mlxsw_sp_nexthop_mac_update(struct mlxsw_sp *mlxsw_sp, u32 adj_index, char ratr_pl[MLXSW_REG_RATR_LEN]; mlxsw_reg_ratr_pack(ratr_pl, MLXSW_REG_RATR_OP_WRITE_WRITE_ENTRY, - true, adj_index, neigh_entry->rif); + true, MLXSW_REG_RATR_TYPE_ETHERNET, + adj_index, neigh_entry->rif); mlxsw_reg_ratr_eth_entry_pack(ratr_pl, neigh_entry->ha); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ratr), ratr_pl); } +static int mlxsw_sp_nexthop_ipip_update(struct mlxsw_sp *mlxsw_sp, + u32 adj_index, + struct mlxsw_sp_nexthop *nh) +{ + const struct mlxsw_sp_ipip_ops *ipip_ops; + + ipip_ops = mlxsw_sp->router->ipip_ops_arr[nh->ipip_entry->ipipt]; + return ipip_ops->nexthop_update(mlxsw_sp, adj_index, nh->ipip_entry); +} + static int -mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_nexthop_group *nh_grp, - bool reallocate) +mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group *nh_grp, + bool reallocate) { u32 adj_index = nh_grp->adj_index; /* base */ struct mlxsw_sp_nexthop *nh; @@ -1497,8 +2365,16 @@ mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp, } if (nh->update || reallocate) { - err = mlxsw_sp_nexthop_mac_update(mlxsw_sp, - adj_index, nh); + switch (nh->type) { + case MLXSW_SP_NEXTHOP_TYPE_ETH: + err = mlxsw_sp_nexthop_mac_update + (mlxsw_sp, adj_index, nh); + break; + case MLXSW_SP_NEXTHOP_TYPE_IPIP: + err = mlxsw_sp_nexthop_ipip_update + (mlxsw_sp, adj_index, nh); + break; + } if (err) return err; nh->update = 0; @@ -1509,9 +2385,6 @@ mlxsw_sp_nexthop_group_mac_update(struct mlxsw_sp *mlxsw_sp, return 0; } -static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry); - static bool mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node, const struct mlxsw_sp_fib_entry *fib_entry); @@ -1535,6 +2408,24 @@ mlxsw_sp_nexthop_fib_entries_update(struct mlxsw_sp *mlxsw_sp, } static void +mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry, + enum mlxsw_reg_ralue_op op, int err); + +static void +mlxsw_sp_nexthop_fib_entries_refresh(struct mlxsw_sp_nexthop_group *nh_grp) +{ + enum mlxsw_reg_ralue_op op = MLXSW_REG_RALUE_OP_WRITE_WRITE; + struct mlxsw_sp_fib_entry *fib_entry; + + list_for_each_entry(fib_entry, &nh_grp->fib_list, nexthop_group_node) { + if (!mlxsw_sp_fib_node_entry_is_first(fib_entry->fib_node, + fib_entry)) + continue; + mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, 0); + } +} + +static void mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group *nh_grp) { @@ -1556,7 +2447,7 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp, for (i = 0; i < nh_grp->count; i++) { nh = &nh_grp->nexthops[i]; - if (nh->should_offload ^ nh->offloaded) { + if (nh->should_offload != nh->offloaded) { offload_change = true; if (nh->should_offload) nh->update = 1; @@ -1568,8 +2459,7 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp, /* Nothing was added or removed, so no need to reallocate. Just * update MAC on existing adjacency indexes. */ - err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, - false); + err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, false); if (err) { dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n"); goto set_trap; @@ -1596,7 +2486,7 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp, nh_grp->adj_index_valid = 1; nh_grp->adj_index = adj_index; nh_grp->ecmp_size = ecmp_size; - err = mlxsw_sp_nexthop_group_mac_update(mlxsw_sp, nh_grp, true); + err = mlxsw_sp_nexthop_group_update(mlxsw_sp, nh_grp, true); if (err) { dev_warn(mlxsw_sp->bus_info->dev, "Failed to update neigh MAC in adjacency table.\n"); goto set_trap; @@ -1621,6 +2511,10 @@ mlxsw_sp_nexthop_group_refresh(struct mlxsw_sp *mlxsw_sp, dev_warn(mlxsw_sp->bus_info->dev, "Failed to mass-update adjacency index for nexthop group.\n"); goto set_trap; } + + /* Offload state within the group changed, so update the flags. */ + mlxsw_sp_nexthop_fib_entries_refresh(nh_grp); + return; set_trap: @@ -1640,9 +2534,9 @@ set_trap: static void __mlxsw_sp_nexthop_neigh_update(struct mlxsw_sp_nexthop *nh, bool removing) { - if (!removing && !nh->should_offload) + if (!removing) nh->should_offload = 1; - else if (removing && nh->offloaded) + else if (nh->offloaded) nh->should_offload = 0; nh->update = 1; } @@ -1684,7 +2578,6 @@ static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop *nh) { struct mlxsw_sp_neigh_entry *neigh_entry; - struct fib_nh *fib_nh = nh->key.fib_nh; struct neighbour *n; u8 nud_state, dead; int err; @@ -1693,13 +2586,14 @@ static int mlxsw_sp_nexthop_neigh_init(struct mlxsw_sp *mlxsw_sp, return 0; /* Take a reference of neigh here ensuring that neigh would - * not be detructed before the nexthop entry is finished. + * not be destructed before the nexthop entry is finished. * The reference is taken either in neigh_lookup() or * in neigh_create() in case n is not found. */ - n = neigh_lookup(&arp_tbl, &fib_nh->nh_gw, fib_nh->nh_dev); + n = neigh_lookup(nh->nh_grp->neigh_tbl, &nh->gw_addr, nh->rif->dev); if (!n) { - n = neigh_create(&arp_tbl, &fib_nh->nh_gw, fib_nh->nh_dev); + n = neigh_create(nh->nh_grp->neigh_tbl, &nh->gw_addr, + nh->rif->dev); if (IS_ERR(n)) return PTR_ERR(n); neigh_event_send(n, NULL); @@ -1761,18 +2655,131 @@ static void mlxsw_sp_nexthop_neigh_fini(struct mlxsw_sp *mlxsw_sp, neigh_release(n); } -static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_nexthop_group *nh_grp, - struct mlxsw_sp_nexthop *nh, - struct fib_nh *fib_nh) +static bool mlxsw_sp_netdev_ipip_type(const struct mlxsw_sp *mlxsw_sp, + const struct net_device *dev, + enum mlxsw_sp_ipip_type *p_type) +{ + struct mlxsw_sp_router *router = mlxsw_sp->router; + const struct mlxsw_sp_ipip_ops *ipip_ops; + enum mlxsw_sp_ipip_type ipipt; + + for (ipipt = 0; ipipt < MLXSW_SP_IPIP_TYPE_MAX; ++ipipt) { + ipip_ops = router->ipip_ops_arr[ipipt]; + if (dev->type == ipip_ops->dev_type) { + if (p_type) + *p_type = ipipt; + return true; + } + } + return false; +} + +static int mlxsw_sp_nexthop_ipip_init(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_sp_ipip_type ipipt, + struct mlxsw_sp_nexthop *nh, + struct net_device *ol_dev) +{ + if (!nh->nh_grp->gateway || nh->ipip_entry) + return 0; + + nh->ipip_entry = mlxsw_sp_ipip_entry_get(mlxsw_sp, ipipt, ol_dev); + if (IS_ERR(nh->ipip_entry)) + return PTR_ERR(nh->ipip_entry); + + __mlxsw_sp_nexthop_neigh_update(nh, false); + return 0; +} + +static void mlxsw_sp_nexthop_ipip_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) +{ + struct mlxsw_sp_ipip_entry *ipip_entry = nh->ipip_entry; + + if (!ipip_entry) + return; + + __mlxsw_sp_nexthop_neigh_update(nh, true); + mlxsw_sp_ipip_entry_put(mlxsw_sp, ipip_entry); + nh->ipip_entry = NULL; +} + +static bool mlxsw_sp_nexthop4_ipip_type(const struct mlxsw_sp *mlxsw_sp, + const struct fib_nh *fib_nh, + enum mlxsw_sp_ipip_type *p_ipipt) { struct net_device *dev = fib_nh->nh_dev; - struct in_device *in_dev; + + return dev && + fib_nh->nh_parent->fib_type == RTN_UNICAST && + mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, p_ipipt); +} + +static void mlxsw_sp_nexthop_type_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) +{ + switch (nh->type) { + case MLXSW_SP_NEXTHOP_TYPE_ETH: + mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh); + mlxsw_sp_nexthop_rif_fini(nh); + break; + case MLXSW_SP_NEXTHOP_TYPE_IPIP: + mlxsw_sp_nexthop_ipip_fini(mlxsw_sp, nh); + break; + } +} + +static int mlxsw_sp_nexthop4_type_init(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh, + struct fib_nh *fib_nh) +{ + struct mlxsw_sp_router *router = mlxsw_sp->router; + struct net_device *dev = fib_nh->nh_dev; + enum mlxsw_sp_ipip_type ipipt; struct mlxsw_sp_rif *rif; int err; + if (mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fib_nh, &ipipt) && + router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev, + MLXSW_SP_L3_PROTO_IPV4)) { + nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP; + return mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev); + } + + nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH; + rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); + if (!rif) + return 0; + + mlxsw_sp_nexthop_rif_init(nh, rif); + err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh); + if (err) + goto err_neigh_init; + + return 0; + +err_neigh_init: + mlxsw_sp_nexthop_rif_fini(nh); + return err; +} + +static void mlxsw_sp_nexthop4_type_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) +{ + mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh); +} + +static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group *nh_grp, + struct mlxsw_sp_nexthop *nh, + struct fib_nh *fib_nh) +{ + struct net_device *dev = fib_nh->nh_dev; + struct in_device *in_dev; + int err; + nh->nh_grp = nh_grp; nh->key.fib_nh = fib_nh; + memcpy(&nh->gw_addr, &fib_nh->nh_gw, sizeof(fib_nh->nh_gw)); err = mlxsw_sp_nexthop_insert(mlxsw_sp, nh); if (err) return err; @@ -1785,37 +2792,29 @@ static int mlxsw_sp_nexthop_init(struct mlxsw_sp *mlxsw_sp, fib_nh->nh_flags & RTNH_F_LINKDOWN) return 0; - rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); - if (!rif) - return 0; - mlxsw_sp_nexthop_rif_init(nh, rif); - - err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh); + err = mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh); if (err) goto err_nexthop_neigh_init; return 0; err_nexthop_neigh_init: - mlxsw_sp_nexthop_rif_fini(nh); mlxsw_sp_nexthop_remove(mlxsw_sp, nh); return err; } -static void mlxsw_sp_nexthop_fini(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_nexthop *nh) +static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) { - mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh); - mlxsw_sp_nexthop_rif_fini(nh); + mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh); mlxsw_sp_nexthop_remove(mlxsw_sp, nh); } -static void mlxsw_sp_nexthop_event(struct mlxsw_sp *mlxsw_sp, - unsigned long event, struct fib_nh *fib_nh) +static void mlxsw_sp_nexthop4_event(struct mlxsw_sp *mlxsw_sp, + unsigned long event, struct fib_nh *fib_nh) { struct mlxsw_sp_nexthop_key key; struct mlxsw_sp_nexthop *nh; - struct mlxsw_sp_rif *rif; if (mlxsw_sp->router->aborted) return; @@ -1825,18 +2824,12 @@ static void mlxsw_sp_nexthop_event(struct mlxsw_sp *mlxsw_sp, if (WARN_ON_ONCE(!nh)) return; - rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fib_nh->nh_dev); - if (!rif) - return; - switch (event) { case FIB_EVENT_NH_ADD: - mlxsw_sp_nexthop_rif_init(nh, rif); - mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh); + mlxsw_sp_nexthop4_type_init(mlxsw_sp, nh, fib_nh); break; case FIB_EVENT_NH_DEL: - mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh); - mlxsw_sp_nexthop_rif_fini(nh); + mlxsw_sp_nexthop4_type_fini(mlxsw_sp, nh); break; } @@ -1849,14 +2842,20 @@ static void mlxsw_sp_nexthop_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop *nh, *tmp; list_for_each_entry_safe(nh, tmp, &rif->nexthop_list, rif_list_node) { - mlxsw_sp_nexthop_neigh_fini(mlxsw_sp, nh); - mlxsw_sp_nexthop_rif_fini(nh); + mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh); mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh->nh_grp); } } +static bool mlxsw_sp_fi_is_gateway(const struct mlxsw_sp *mlxsw_sp, + const struct fib_info *fi) +{ + return fi->fib_nh->nh_scope == RT_SCOPE_LINK || + mlxsw_sp_nexthop4_ipip_type(mlxsw_sp, fi->fib_nh, NULL); +} + static struct mlxsw_sp_nexthop_group * -mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi) +mlxsw_sp_nexthop4_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi) { struct mlxsw_sp_nexthop_group *nh_grp; struct mlxsw_sp_nexthop *nh; @@ -1870,17 +2869,19 @@ mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi) nh_grp = kzalloc(alloc_size, GFP_KERNEL); if (!nh_grp) return ERR_PTR(-ENOMEM); + nh_grp->priv = fi; INIT_LIST_HEAD(&nh_grp->fib_list); - nh_grp->gateway = fi->fib_nh->nh_scope == RT_SCOPE_LINK; + nh_grp->neigh_tbl = &arp_tbl; + + nh_grp->gateway = mlxsw_sp_fi_is_gateway(mlxsw_sp, fi); nh_grp->count = fi->fib_nhs; - nh_grp->key.fi = fi; fib_info_hold(fi); for (i = 0; i < nh_grp->count; i++) { nh = &nh_grp->nexthops[i]; fib_nh = &fi->fib_nh[i]; - err = mlxsw_sp_nexthop_init(mlxsw_sp, nh_grp, nh, fib_nh); + err = mlxsw_sp_nexthop4_init(mlxsw_sp, nh_grp, nh, fib_nh); if (err) - goto err_nexthop_init; + goto err_nexthop4_init; } err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp); if (err) @@ -1889,19 +2890,19 @@ mlxsw_sp_nexthop_group_create(struct mlxsw_sp *mlxsw_sp, struct fib_info *fi) return nh_grp; err_nexthop_group_insert: -err_nexthop_init: +err_nexthop4_init: for (i--; i >= 0; i--) { nh = &nh_grp->nexthops[i]; - mlxsw_sp_nexthop_fini(mlxsw_sp, nh); + mlxsw_sp_nexthop4_fini(mlxsw_sp, nh); } - fib_info_put(nh_grp->key.fi); + fib_info_put(fi); kfree(nh_grp); return ERR_PTR(err); } static void -mlxsw_sp_nexthop_group_destroy(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_nexthop_group *nh_grp) +mlxsw_sp_nexthop4_group_destroy(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group *nh_grp) { struct mlxsw_sp_nexthop *nh; int i; @@ -1909,25 +2910,23 @@ mlxsw_sp_nexthop_group_destroy(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp); for (i = 0; i < nh_grp->count; i++) { nh = &nh_grp->nexthops[i]; - mlxsw_sp_nexthop_fini(mlxsw_sp, nh); + mlxsw_sp_nexthop4_fini(mlxsw_sp, nh); } mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); WARN_ON_ONCE(nh_grp->adj_index_valid); - fib_info_put(nh_grp->key.fi); + fib_info_put(mlxsw_sp_nexthop4_group_fi(nh_grp)); kfree(nh_grp); } -static int mlxsw_sp_nexthop_group_get(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, - struct fib_info *fi) +static int mlxsw_sp_nexthop4_group_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + struct fib_info *fi) { - struct mlxsw_sp_nexthop_group_key key; struct mlxsw_sp_nexthop_group *nh_grp; - key.fi = fi; - nh_grp = mlxsw_sp_nexthop_group_lookup(mlxsw_sp, key); + nh_grp = mlxsw_sp_nexthop4_group_lookup(mlxsw_sp, fi); if (!nh_grp) { - nh_grp = mlxsw_sp_nexthop_group_create(mlxsw_sp, fi); + nh_grp = mlxsw_sp_nexthop4_group_create(mlxsw_sp, fi); if (IS_ERR(nh_grp)) return PTR_ERR(nh_grp); } @@ -1936,15 +2935,25 @@ static int mlxsw_sp_nexthop_group_get(struct mlxsw_sp *mlxsw_sp, return 0; } -static void mlxsw_sp_nexthop_group_put(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry) +static void mlxsw_sp_nexthop4_group_put(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) { struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; list_del(&fib_entry->nexthop_group_node); if (!list_empty(&nh_grp->fib_list)) return; - mlxsw_sp_nexthop_group_destroy(mlxsw_sp, nh_grp); + mlxsw_sp_nexthop4_group_destroy(mlxsw_sp, nh_grp); +} + +static bool +mlxsw_sp_fib4_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry) +{ + struct mlxsw_sp_fib4_entry *fib4_entry; + + fib4_entry = container_of(fib_entry, struct mlxsw_sp_fib4_entry, + common); + return !fib4_entry->tos; } static bool @@ -1952,29 +2961,133 @@ mlxsw_sp_fib_entry_should_offload(const struct mlxsw_sp_fib_entry *fib_entry) { struct mlxsw_sp_nexthop_group *nh_group = fib_entry->nh_group; - if (fib_entry->params.tos) - return false; + switch (fib_entry->fib_node->fib->proto) { + case MLXSW_SP_L3_PROTO_IPV4: + if (!mlxsw_sp_fib4_entry_should_offload(fib_entry)) + return false; + break; + case MLXSW_SP_L3_PROTO_IPV6: + break; + } switch (fib_entry->type) { case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE: return !!nh_group->adj_index_valid; case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL: return !!nh_group->nh_rif; + case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP: + return true; default: return false; } } -static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) +static struct mlxsw_sp_nexthop * +mlxsw_sp_rt6_nexthop(struct mlxsw_sp_nexthop_group *nh_grp, + const struct mlxsw_sp_rt6 *mlxsw_sp_rt6) +{ + int i; + + for (i = 0; i < nh_grp->count; i++) { + struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i]; + struct rt6_info *rt = mlxsw_sp_rt6->rt; + + if (nh->rif && nh->rif->dev == rt->dst.dev && + ipv6_addr_equal((const struct in6_addr *) &nh->gw_addr, + &rt->rt6i_gateway)) + return nh; + continue; + } + + return NULL; +} + +static void +mlxsw_sp_fib4_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) +{ + struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; + int i; + + if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL || + fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP) { + nh_grp->nexthops->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD; + return; + } + + for (i = 0; i < nh_grp->count; i++) { + struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i]; + + if (nh->offloaded) + nh->key.fib_nh->nh_flags |= RTNH_F_OFFLOAD; + else + nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD; + } +} + +static void +mlxsw_sp_fib4_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) { - fib_entry->offloaded = true; + struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; + int i; + + for (i = 0; i < nh_grp->count; i++) { + struct mlxsw_sp_nexthop *nh = &nh_grp->nexthops[i]; + + nh->key.fib_nh->nh_flags &= ~RTNH_F_OFFLOAD; + } +} + +static void +mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + + fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry, + common); + + if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) { + list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6, + list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD; + return; + } + list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { + struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; + struct mlxsw_sp_nexthop *nh; + + nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6); + if (nh && nh->offloaded) + mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD; + else + mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD; + } +} + +static void +mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + + fib6_entry = container_of(fib_entry, struct mlxsw_sp_fib6_entry, + common); + list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { + struct rt6_info *rt = mlxsw_sp_rt6->rt; + + rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD; + } +} + +static void mlxsw_sp_fib_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) +{ switch (fib_entry->fib_node->fib->proto) { case MLXSW_SP_L3_PROTO_IPV4: - fib_info_offload_inc(fib_entry->nh_group->key.fi); + mlxsw_sp_fib4_entry_offload_set(fib_entry); break; case MLXSW_SP_L3_PROTO_IPV6: - WARN_ON_ONCE(1); + mlxsw_sp_fib6_entry_offload_set(fib_entry); + break; } } @@ -1983,13 +3096,12 @@ mlxsw_sp_fib_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) { switch (fib_entry->fib_node->fib->proto) { case MLXSW_SP_L3_PROTO_IPV4: - fib_info_offload_dec(fib_entry->nh_group->key.fi); + mlxsw_sp_fib4_entry_offload_unset(fib_entry); break; case MLXSW_SP_L3_PROTO_IPV6: - WARN_ON_ONCE(1); + mlxsw_sp_fib6_entry_offload_unset(fib_entry); + break; } - - fib_entry->offloaded = false; } static void @@ -1998,17 +3110,13 @@ mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry, { switch (op) { case MLXSW_REG_RALUE_OP_WRITE_DELETE: - if (!fib_entry->offloaded) - return; return mlxsw_sp_fib_entry_offload_unset(fib_entry); case MLXSW_REG_RALUE_OP_WRITE_WRITE: if (err) return; - if (mlxsw_sp_fib_entry_should_offload(fib_entry) && - !fib_entry->offloaded) + if (mlxsw_sp_fib_entry_should_offload(fib_entry)) mlxsw_sp_fib_entry_offload_set(fib_entry); - else if (!mlxsw_sp_fib_entry_should_offload(fib_entry) && - fib_entry->offloaded) + else if (!mlxsw_sp_fib_entry_should_offload(fib_entry)) mlxsw_sp_fib_entry_offload_unset(fib_entry); return; default: @@ -2016,13 +3124,37 @@ mlxsw_sp_fib_entry_offload_refresh(struct mlxsw_sp_fib_entry *fib_entry, } } -static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, - enum mlxsw_reg_ralue_op op) +static void +mlxsw_sp_fib_entry_ralue_pack(char *ralue_pl, + const struct mlxsw_sp_fib_entry *fib_entry, + enum mlxsw_reg_ralue_op op) { - char ralue_pl[MLXSW_REG_RALUE_LEN]; struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib; - u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr; + enum mlxsw_reg_ralxx_protocol proto; + u32 *p_dip; + + proto = (enum mlxsw_reg_ralxx_protocol) fib->proto; + + switch (fib->proto) { + case MLXSW_SP_L3_PROTO_IPV4: + p_dip = (u32 *) fib_entry->fib_node->key.addr; + mlxsw_reg_ralue_pack4(ralue_pl, proto, op, fib->vr->id, + fib_entry->fib_node->key.prefix_len, + *p_dip); + break; + case MLXSW_SP_L3_PROTO_IPV6: + mlxsw_reg_ralue_pack6(ralue_pl, proto, op, fib->vr->id, + fib_entry->fib_node->key.prefix_len, + fib_entry->fib_node->key.addr); + break; + } +} + +static int mlxsw_sp_fib_entry_op_remote(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + enum mlxsw_reg_ralue_op op) +{ + char ralue_pl[MLXSW_REG_RALUE_LEN]; enum mlxsw_reg_ralue_trap_action trap_action; u16 trap_id = 0; u32 adjacency_index = 0; @@ -2041,24 +3173,19 @@ static int mlxsw_sp_fib_entry_op4_remote(struct mlxsw_sp *mlxsw_sp, trap_id = MLXSW_TRAP_ID_RTR_INGRESS0; } - mlxsw_reg_ralue_pack4(ralue_pl, - (enum mlxsw_reg_ralxx_protocol) fib->proto, op, - fib->vr->id, fib_entry->fib_node->key.prefix_len, - *p_dip); + mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op); mlxsw_reg_ralue_act_remote_pack(ralue_pl, trap_action, trap_id, adjacency_index, ecmp_size); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); } -static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, - enum mlxsw_reg_ralue_op op) +static int mlxsw_sp_fib_entry_op_local(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + enum mlxsw_reg_ralue_op op) { struct mlxsw_sp_rif *rif = fib_entry->nh_group->nh_rif; - struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib; enum mlxsw_reg_ralue_trap_action trap_action; char ralue_pl[MLXSW_REG_RALUE_LEN]; - u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr; u16 trap_id = 0; u16 rif_index = 0; @@ -2070,42 +3197,53 @@ static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp, trap_id = MLXSW_TRAP_ID_RTR_INGRESS0; } - mlxsw_reg_ralue_pack4(ralue_pl, - (enum mlxsw_reg_ralxx_protocol) fib->proto, op, - fib->vr->id, fib_entry->fib_node->key.prefix_len, - *p_dip); + mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op); mlxsw_reg_ralue_act_local_pack(ralue_pl, trap_action, trap_id, rif_index); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); } -static int mlxsw_sp_fib_entry_op4_trap(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, - enum mlxsw_reg_ralue_op op) +static int mlxsw_sp_fib_entry_op_trap(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + enum mlxsw_reg_ralue_op op) { - struct mlxsw_sp_fib *fib = fib_entry->fib_node->fib; char ralue_pl[MLXSW_REG_RALUE_LEN]; - u32 *p_dip = (u32 *) fib_entry->fib_node->key.addr; - mlxsw_reg_ralue_pack4(ralue_pl, - (enum mlxsw_reg_ralxx_protocol) fib->proto, op, - fib->vr->id, fib_entry->fib_node->key.prefix_len, - *p_dip); + mlxsw_sp_fib_entry_ralue_pack(ralue_pl, fib_entry, op); mlxsw_reg_ralue_act_ip2me_pack(ralue_pl); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); } -static int mlxsw_sp_fib_entry_op4(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, - enum mlxsw_reg_ralue_op op) +static int +mlxsw_sp_fib_entry_op_ipip_decap(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + enum mlxsw_reg_ralue_op op) +{ + struct mlxsw_sp_ipip_entry *ipip_entry = fib_entry->decap.ipip_entry; + const struct mlxsw_sp_ipip_ops *ipip_ops; + + if (WARN_ON(!ipip_entry)) + return -EINVAL; + + ipip_ops = mlxsw_sp->router->ipip_ops_arr[ipip_entry->ipipt]; + return ipip_ops->fib_entry_op(mlxsw_sp, ipip_entry, op, + fib_entry->decap.tunnel_index); +} + +static int __mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + enum mlxsw_reg_ralue_op op) { switch (fib_entry->type) { case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE: - return mlxsw_sp_fib_entry_op4_remote(mlxsw_sp, fib_entry, op); + return mlxsw_sp_fib_entry_op_remote(mlxsw_sp, fib_entry, op); case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL: - return mlxsw_sp_fib_entry_op4_local(mlxsw_sp, fib_entry, op); + return mlxsw_sp_fib_entry_op_local(mlxsw_sp, fib_entry, op); case MLXSW_SP_FIB_ENTRY_TYPE_TRAP: - return mlxsw_sp_fib_entry_op4_trap(mlxsw_sp, fib_entry, op); + return mlxsw_sp_fib_entry_op_trap(mlxsw_sp, fib_entry, op); + case MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP: + return mlxsw_sp_fib_entry_op_ipip_decap(mlxsw_sp, + fib_entry, op); } return -EINVAL; } @@ -2114,16 +3252,10 @@ static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_entry *fib_entry, enum mlxsw_reg_ralue_op op) { - int err = -EINVAL; + int err = __mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op); - switch (fib_entry->fib_node->fib->proto) { - case MLXSW_SP_L3_PROTO_IPV4: - err = mlxsw_sp_fib_entry_op4(mlxsw_sp, fib_entry, op); - break; - case MLXSW_SP_L3_PROTO_IPV6: - return err; - } mlxsw_sp_fib_entry_offload_refresh(fib_entry, op, err); + return err; } @@ -2146,11 +3278,23 @@ mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp, const struct fib_entry_notifier_info *fen_info, struct mlxsw_sp_fib_entry *fib_entry) { + union mlxsw_sp_l3addr dip = { .addr4 = htonl(fen_info->dst) }; + struct net_device *dev = fen_info->fi->fib_dev; + struct mlxsw_sp_ipip_entry *ipip_entry; struct fib_info *fi = fen_info->fi; switch (fen_info->type) { - case RTN_BROADCAST: /* fall through */ case RTN_LOCAL: + ipip_entry = mlxsw_sp_ipip_entry_find_by_decap(mlxsw_sp, dev, + MLXSW_SP_L3_PROTO_IPV4, dip); + if (ipip_entry) { + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP; + return mlxsw_sp_fib_entry_decap_init(mlxsw_sp, + fib_entry, + ipip_entry); + } + /* fall through */ + case RTN_BROADCAST: fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; return 0; case RTN_UNREACHABLE: /* fall through */ @@ -2163,82 +3307,87 @@ mlxsw_sp_fib4_entry_type_set(struct mlxsw_sp *mlxsw_sp, fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; return 0; case RTN_UNICAST: - if (fi->fib_nh->nh_scope != RT_SCOPE_LINK) - fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; - else + if (mlxsw_sp_fi_is_gateway(mlxsw_sp, fi)) fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE; + else + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; return 0; default: return -EINVAL; } } -static struct mlxsw_sp_fib_entry * +static struct mlxsw_sp_fib4_entry * mlxsw_sp_fib4_entry_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node, const struct fib_entry_notifier_info *fen_info) { + struct mlxsw_sp_fib4_entry *fib4_entry; struct mlxsw_sp_fib_entry *fib_entry; int err; - fib_entry = kzalloc(sizeof(*fib_entry), GFP_KERNEL); - if (!fib_entry) { - err = -ENOMEM; - goto err_fib_entry_alloc; - } + fib4_entry = kzalloc(sizeof(*fib4_entry), GFP_KERNEL); + if (!fib4_entry) + return ERR_PTR(-ENOMEM); + fib_entry = &fib4_entry->common; err = mlxsw_sp_fib4_entry_type_set(mlxsw_sp, fen_info, fib_entry); if (err) goto err_fib4_entry_type_set; - err = mlxsw_sp_nexthop_group_get(mlxsw_sp, fib_entry, fen_info->fi); + err = mlxsw_sp_nexthop4_group_get(mlxsw_sp, fib_entry, fen_info->fi); if (err) - goto err_nexthop_group_get; + goto err_nexthop4_group_get; - fib_entry->params.prio = fen_info->fi->fib_priority; - fib_entry->params.tb_id = fen_info->tb_id; - fib_entry->params.type = fen_info->type; - fib_entry->params.tos = fen_info->tos; + fib4_entry->prio = fen_info->fi->fib_priority; + fib4_entry->tb_id = fen_info->tb_id; + fib4_entry->type = fen_info->type; + fib4_entry->tos = fen_info->tos; fib_entry->fib_node = fib_node; - return fib_entry; + return fib4_entry; -err_nexthop_group_get: +err_nexthop4_group_get: err_fib4_entry_type_set: - kfree(fib_entry); -err_fib_entry_alloc: + kfree(fib4_entry); return ERR_PTR(err); } static void mlxsw_sp_fib4_entry_destroy(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry) + struct mlxsw_sp_fib4_entry *fib4_entry) { - mlxsw_sp_nexthop_group_put(mlxsw_sp, fib_entry); - kfree(fib_entry); + mlxsw_sp_nexthop4_group_put(mlxsw_sp, &fib4_entry->common); + kfree(fib4_entry); } -static struct mlxsw_sp_fib_node * -mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp, - const struct fib_entry_notifier_info *fen_info); - -static struct mlxsw_sp_fib_entry * +static struct mlxsw_sp_fib4_entry * mlxsw_sp_fib4_entry_lookup(struct mlxsw_sp *mlxsw_sp, const struct fib_entry_notifier_info *fen_info) { - struct mlxsw_sp_fib_entry *fib_entry; + struct mlxsw_sp_fib4_entry *fib4_entry; struct mlxsw_sp_fib_node *fib_node; + struct mlxsw_sp_fib *fib; + struct mlxsw_sp_vr *vr; - fib_node = mlxsw_sp_fib4_node_get(mlxsw_sp, fen_info); - if (IS_ERR(fib_node)) + vr = mlxsw_sp_vr_find(mlxsw_sp, fen_info->tb_id); + if (!vr) return NULL; + fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4); - list_for_each_entry(fib_entry, &fib_node->entry_list, list) { - if (fib_entry->params.tb_id == fen_info->tb_id && - fib_entry->params.tos == fen_info->tos && - fib_entry->params.type == fen_info->type && - fib_entry->nh_group->key.fi == fen_info->fi) { - return fib_entry; + fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst, + sizeof(fen_info->dst), + fen_info->dst_len); + if (!fib_node) + return NULL; + + list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) { + if (fib4_entry->tb_id == fen_info->tb_id && + fib4_entry->tos == fen_info->tos && + fib4_entry->type == fen_info->type && + mlxsw_sp_nexthop4_group_fi(fib4_entry->common.nh_group) == + fen_info->fi) { + return fib4_entry; } } @@ -2311,6 +3460,67 @@ mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node, struct mlxsw_sp_fib_entry, list) == fib_entry; } +static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib *fib, + struct mlxsw_sp_fib_node *fib_node) +{ + struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } }; + struct mlxsw_sp_lpm_tree *lpm_tree; + int err; + + /* Since the tree is shared between all virtual routers we must + * make sure it contains all the required prefix lengths. This + * can be computed by either adding the new prefix length to the + * existing prefix usage of a bound tree, or by aggregating the + * prefix lengths across all virtual routers and adding the new + * one as well. + */ + if (fib->lpm_tree) + mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, + &fib->lpm_tree->prefix_usage); + else + mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage); + mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len); + + lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, + fib->proto); + if (IS_ERR(lpm_tree)) + return PTR_ERR(lpm_tree); + + if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id) + return 0; + + err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree); + if (err) + return err; + + return 0; +} + +static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib *fib) +{ + struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } }; + struct mlxsw_sp_lpm_tree *lpm_tree; + + /* Aggregate prefix lengths across all virtual routers to make + * sure we only have used prefix lengths in the LPM tree. + */ + mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage); + lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, + fib->proto); + if (IS_ERR(lpm_tree)) + goto err_tree_get; + mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree); + +err_tree_get: + if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) + return; + mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib); + mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree); + fib->lpm_tree = NULL; +} + static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node) { unsigned char prefix_len = fib_node->key.prefix_len; @@ -2333,8 +3543,6 @@ static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node, struct mlxsw_sp_fib *fib) { - struct mlxsw_sp_prefix_usage req_prefix_usage; - struct mlxsw_sp_lpm_tree *lpm_tree; int err; err = mlxsw_sp_fib_node_insert(fib, fib_node); @@ -2342,33 +3550,15 @@ static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp, return err; fib_node->fib = fib; - mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &fib->prefix_usage); - mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len); - - if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) { - err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib, - &req_prefix_usage); - if (err) - goto err_tree_check; - } else { - lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, - fib->proto); - if (IS_ERR(lpm_tree)) - return PTR_ERR(lpm_tree); - fib->lpm_tree = lpm_tree; - err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib); - if (err) - goto err_tree_bind; - } + err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node); + if (err) + goto err_fib_lpm_tree_link; mlxsw_sp_fib_node_prefix_inc(fib_node); return 0; -err_tree_bind: - fib->lpm_tree = NULL; - mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); -err_tree_check: +err_fib_lpm_tree_link: fib_node->fib = NULL; mlxsw_sp_fib_node_remove(fib, fib_node); return err; @@ -2377,46 +3567,34 @@ err_tree_check: static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node) { - struct mlxsw_sp_lpm_tree *lpm_tree = fib_node->fib->lpm_tree; struct mlxsw_sp_fib *fib = fib_node->fib; mlxsw_sp_fib_node_prefix_dec(fib_node); - - if (mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) { - mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib); - fib->lpm_tree = NULL; - mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); - } else { - mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, fib, &fib->prefix_usage); - } - + mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib); fib_node->fib = NULL; mlxsw_sp_fib_node_remove(fib, fib_node); } static struct mlxsw_sp_fib_node * -mlxsw_sp_fib4_node_get(struct mlxsw_sp *mlxsw_sp, - const struct fib_entry_notifier_info *fen_info) +mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr, + size_t addr_len, unsigned char prefix_len, + enum mlxsw_sp_l3proto proto) { struct mlxsw_sp_fib_node *fib_node; struct mlxsw_sp_fib *fib; struct mlxsw_sp_vr *vr; int err; - vr = mlxsw_sp_vr_get(mlxsw_sp, fen_info->tb_id); + vr = mlxsw_sp_vr_get(mlxsw_sp, tb_id); if (IS_ERR(vr)) return ERR_CAST(vr); - fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV4); + fib = mlxsw_sp_vr_fib(vr, proto); - fib_node = mlxsw_sp_fib_node_lookup(fib, &fen_info->dst, - sizeof(fen_info->dst), - fen_info->dst_len); + fib_node = mlxsw_sp_fib_node_lookup(fib, addr, addr_len, prefix_len); if (fib_node) return fib_node; - fib_node = mlxsw_sp_fib_node_create(fib, &fen_info->dst, - sizeof(fen_info->dst), - fen_info->dst_len); + fib_node = mlxsw_sp_fib_node_create(fib, addr, addr_len, prefix_len); if (!fib_node) { err = -ENOMEM; goto err_fib_node_create; @@ -2435,8 +3613,8 @@ err_fib_node_create: return ERR_PTR(err); } -static void mlxsw_sp_fib4_node_put(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_node *fib_node) +static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_node *fib_node) { struct mlxsw_sp_vr *vr = fib_node->fib->vr; @@ -2447,95 +3625,100 @@ static void mlxsw_sp_fib4_node_put(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_vr_put(vr); } -static struct mlxsw_sp_fib_entry * +static struct mlxsw_sp_fib4_entry * mlxsw_sp_fib4_node_entry_find(const struct mlxsw_sp_fib_node *fib_node, - const struct mlxsw_sp_fib_entry_params *params) + const struct mlxsw_sp_fib4_entry *new4_entry) { - struct mlxsw_sp_fib_entry *fib_entry; + struct mlxsw_sp_fib4_entry *fib4_entry; - list_for_each_entry(fib_entry, &fib_node->entry_list, list) { - if (fib_entry->params.tb_id > params->tb_id) + list_for_each_entry(fib4_entry, &fib_node->entry_list, common.list) { + if (fib4_entry->tb_id > new4_entry->tb_id) continue; - if (fib_entry->params.tb_id != params->tb_id) + if (fib4_entry->tb_id != new4_entry->tb_id) break; - if (fib_entry->params.tos > params->tos) + if (fib4_entry->tos > new4_entry->tos) continue; - if (fib_entry->params.prio >= params->prio || - fib_entry->params.tos < params->tos) - return fib_entry; + if (fib4_entry->prio >= new4_entry->prio || + fib4_entry->tos < new4_entry->tos) + return fib4_entry; } return NULL; } -static int mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib_entry *fib_entry, - struct mlxsw_sp_fib_entry *new_entry) +static int +mlxsw_sp_fib4_node_list_append(struct mlxsw_sp_fib4_entry *fib4_entry, + struct mlxsw_sp_fib4_entry *new4_entry) { struct mlxsw_sp_fib_node *fib_node; - if (WARN_ON(!fib_entry)) + if (WARN_ON(!fib4_entry)) return -EINVAL; - fib_node = fib_entry->fib_node; - list_for_each_entry_from(fib_entry, &fib_node->entry_list, list) { - if (fib_entry->params.tb_id != new_entry->params.tb_id || - fib_entry->params.tos != new_entry->params.tos || - fib_entry->params.prio != new_entry->params.prio) + fib_node = fib4_entry->common.fib_node; + list_for_each_entry_from(fib4_entry, &fib_node->entry_list, + common.list) { + if (fib4_entry->tb_id != new4_entry->tb_id || + fib4_entry->tos != new4_entry->tos || + fib4_entry->prio != new4_entry->prio) break; } - list_add_tail(&new_entry->list, &fib_entry->list); + list_add_tail(&new4_entry->common.list, &fib4_entry->common.list); return 0; } static int -mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib_node *fib_node, - struct mlxsw_sp_fib_entry *new_entry, +mlxsw_sp_fib4_node_list_insert(struct mlxsw_sp_fib4_entry *new4_entry, bool replace, bool append) { - struct mlxsw_sp_fib_entry *fib_entry; + struct mlxsw_sp_fib_node *fib_node = new4_entry->common.fib_node; + struct mlxsw_sp_fib4_entry *fib4_entry; - fib_entry = mlxsw_sp_fib4_node_entry_find(fib_node, &new_entry->params); + fib4_entry = mlxsw_sp_fib4_node_entry_find(fib_node, new4_entry); if (append) - return mlxsw_sp_fib4_node_list_append(fib_entry, new_entry); - if (replace && WARN_ON(!fib_entry)) + return mlxsw_sp_fib4_node_list_append(fib4_entry, new4_entry); + if (replace && WARN_ON(!fib4_entry)) return -EINVAL; /* Insert new entry before replaced one, so that we can later * remove the second. */ - if (fib_entry) { - list_add_tail(&new_entry->list, &fib_entry->list); + if (fib4_entry) { + list_add_tail(&new4_entry->common.list, + &fib4_entry->common.list); } else { - struct mlxsw_sp_fib_entry *last; + struct mlxsw_sp_fib4_entry *last; - list_for_each_entry(last, &fib_node->entry_list, list) { - if (new_entry->params.tb_id > last->params.tb_id) + list_for_each_entry(last, &fib_node->entry_list, common.list) { + if (new4_entry->tb_id > last->tb_id) break; - fib_entry = last; + fib4_entry = last; } - if (fib_entry) - list_add(&new_entry->list, &fib_entry->list); + if (fib4_entry) + list_add(&new4_entry->common.list, + &fib4_entry->common.list); else - list_add(&new_entry->list, &fib_node->entry_list); + list_add(&new4_entry->common.list, + &fib_node->entry_list); } return 0; } static void -mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib_entry *fib_entry) +mlxsw_sp_fib4_node_list_remove(struct mlxsw_sp_fib4_entry *fib4_entry) { - list_del(&fib_entry->list); + list_del(&fib4_entry->common.list); } -static int -mlxsw_sp_fib4_node_entry_add(struct mlxsw_sp *mlxsw_sp, - const struct mlxsw_sp_fib_node *fib_node, - struct mlxsw_sp_fib_entry *fib_entry) +static int mlxsw_sp_fib_node_entry_add(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) { + struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; + if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry)) return 0; @@ -2552,11 +3735,11 @@ mlxsw_sp_fib4_node_entry_add(struct mlxsw_sp *mlxsw_sp, return mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry); } -static void -mlxsw_sp_fib4_node_entry_del(struct mlxsw_sp *mlxsw_sp, - const struct mlxsw_sp_fib_node *fib_node, - struct mlxsw_sp_fib_entry *fib_entry) +static void mlxsw_sp_fib_node_entry_del(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) { + struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; + if (!mlxsw_sp_fib_node_entry_is_first(fib_node, fib_entry)) return; @@ -2574,54 +3757,53 @@ mlxsw_sp_fib4_node_entry_del(struct mlxsw_sp *mlxsw_sp, } static int mlxsw_sp_fib4_node_entry_link(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, + struct mlxsw_sp_fib4_entry *fib4_entry, bool replace, bool append) { - struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; int err; - err = mlxsw_sp_fib4_node_list_insert(fib_node, fib_entry, replace, - append); + err = mlxsw_sp_fib4_node_list_insert(fib4_entry, replace, append); if (err) return err; - err = mlxsw_sp_fib4_node_entry_add(mlxsw_sp, fib_node, fib_entry); + err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib4_entry->common); if (err) - goto err_fib4_node_entry_add; + goto err_fib_node_entry_add; return 0; -err_fib4_node_entry_add: - mlxsw_sp_fib4_node_list_remove(fib_entry); +err_fib_node_entry_add: + mlxsw_sp_fib4_node_list_remove(fib4_entry); return err; } static void mlxsw_sp_fib4_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry) + struct mlxsw_sp_fib4_entry *fib4_entry) { - struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; + mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib4_entry->common); + mlxsw_sp_fib4_node_list_remove(fib4_entry); - mlxsw_sp_fib4_node_entry_del(mlxsw_sp, fib_node, fib_entry); - mlxsw_sp_fib4_node_list_remove(fib_entry); + if (fib4_entry->common.type == MLXSW_SP_FIB_ENTRY_TYPE_IPIP_DECAP) + mlxsw_sp_fib_entry_decap_fini(mlxsw_sp, &fib4_entry->common); } static void mlxsw_sp_fib4_entry_replace(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_fib_entry *fib_entry, + struct mlxsw_sp_fib4_entry *fib4_entry, bool replace) { - struct mlxsw_sp_fib_node *fib_node = fib_entry->fib_node; - struct mlxsw_sp_fib_entry *replaced; + struct mlxsw_sp_fib_node *fib_node = fib4_entry->common.fib_node; + struct mlxsw_sp_fib4_entry *replaced; if (!replace) return; /* We inserted the new entry before replaced one */ - replaced = list_next_entry(fib_entry, list); + replaced = list_next_entry(fib4_entry, common.list); mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, replaced); mlxsw_sp_fib4_entry_destroy(mlxsw_sp, replaced); - mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); } static int @@ -2629,76 +3811,774 @@ mlxsw_sp_router_fib4_add(struct mlxsw_sp *mlxsw_sp, const struct fib_entry_notifier_info *fen_info, bool replace, bool append) { - struct mlxsw_sp_fib_entry *fib_entry; + struct mlxsw_sp_fib4_entry *fib4_entry; struct mlxsw_sp_fib_node *fib_node; int err; if (mlxsw_sp->router->aborted) return 0; - fib_node = mlxsw_sp_fib4_node_get(mlxsw_sp, fen_info); + fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, fen_info->tb_id, + &fen_info->dst, sizeof(fen_info->dst), + fen_info->dst_len, + MLXSW_SP_L3_PROTO_IPV4); if (IS_ERR(fib_node)) { dev_warn(mlxsw_sp->bus_info->dev, "Failed to get FIB node\n"); return PTR_ERR(fib_node); } - fib_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info); - if (IS_ERR(fib_entry)) { + fib4_entry = mlxsw_sp_fib4_entry_create(mlxsw_sp, fib_node, fen_info); + if (IS_ERR(fib4_entry)) { dev_warn(mlxsw_sp->bus_info->dev, "Failed to create FIB entry\n"); - err = PTR_ERR(fib_entry); + err = PTR_ERR(fib4_entry); goto err_fib4_entry_create; } - err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib_entry, replace, + err = mlxsw_sp_fib4_node_entry_link(mlxsw_sp, fib4_entry, replace, append); if (err) { dev_warn(mlxsw_sp->bus_info->dev, "Failed to link FIB entry to node\n"); goto err_fib4_node_entry_link; } - mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib_entry, replace); + mlxsw_sp_fib4_entry_replace(mlxsw_sp, fib4_entry, replace); return 0; err_fib4_node_entry_link: - mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry); + mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); err_fib4_entry_create: - mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); return err; } static void mlxsw_sp_router_fib4_del(struct mlxsw_sp *mlxsw_sp, struct fib_entry_notifier_info *fen_info) { + struct mlxsw_sp_fib4_entry *fib4_entry; + struct mlxsw_sp_fib_node *fib_node; + + if (mlxsw_sp->router->aborted) + return; + + fib4_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info); + if (WARN_ON(!fib4_entry)) + return; + fib_node = fib4_entry->common.fib_node; + + mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry); + mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); +} + +static bool mlxsw_sp_fib6_rt_should_ignore(const struct rt6_info *rt) +{ + /* Packets with link-local destination IP arriving to the router + * are trapped to the CPU, so no need to program specific routes + * for them. + */ + if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_LINKLOCAL) + return true; + + /* Multicast routes aren't supported, so ignore them. Neighbour + * Discovery packets are specifically trapped. + */ + if (ipv6_addr_type(&rt->rt6i_dst.addr) & IPV6_ADDR_MULTICAST) + return true; + + /* Cloned routes are irrelevant in the forwarding path. */ + if (rt->rt6i_flags & RTF_CACHE) + return true; + + return false; +} + +static struct mlxsw_sp_rt6 *mlxsw_sp_rt6_create(struct rt6_info *rt) +{ + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + + mlxsw_sp_rt6 = kzalloc(sizeof(*mlxsw_sp_rt6), GFP_KERNEL); + if (!mlxsw_sp_rt6) + return ERR_PTR(-ENOMEM); + + /* In case of route replace, replaced route is deleted with + * no notification. Take reference to prevent accessing freed + * memory. + */ + mlxsw_sp_rt6->rt = rt; + rt6_hold(rt); + + return mlxsw_sp_rt6; +} + +#if IS_ENABLED(CONFIG_IPV6) +static void mlxsw_sp_rt6_release(struct rt6_info *rt) +{ + rt6_release(rt); +} +#else +static void mlxsw_sp_rt6_release(struct rt6_info *rt) +{ +} +#endif + +static void mlxsw_sp_rt6_destroy(struct mlxsw_sp_rt6 *mlxsw_sp_rt6) +{ + mlxsw_sp_rt6_release(mlxsw_sp_rt6->rt); + kfree(mlxsw_sp_rt6); +} + +static bool mlxsw_sp_fib6_rt_can_mp(const struct rt6_info *rt) +{ + /* RTF_CACHE routes are ignored */ + return (rt->rt6i_flags & (RTF_GATEWAY | RTF_ADDRCONF)) == RTF_GATEWAY; +} + +static struct rt6_info * +mlxsw_sp_fib6_entry_rt(const struct mlxsw_sp_fib6_entry *fib6_entry) +{ + return list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6, + list)->rt; +} + +static struct mlxsw_sp_fib6_entry * +mlxsw_sp_fib6_node_mp_entry_find(const struct mlxsw_sp_fib_node *fib_node, + const struct rt6_info *nrt, bool replace) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; + + if (!mlxsw_sp_fib6_rt_can_mp(nrt) || replace) + return NULL; + + list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { + struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry); + + /* RT6_TABLE_LOCAL and RT6_TABLE_MAIN share the same + * virtual router. + */ + if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id) + continue; + if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id) + break; + if (rt->rt6i_metric < nrt->rt6i_metric) + continue; + if (rt->rt6i_metric == nrt->rt6i_metric && + mlxsw_sp_fib6_rt_can_mp(rt)) + return fib6_entry; + if (rt->rt6i_metric > nrt->rt6i_metric) + break; + } + + return NULL; +} + +static struct mlxsw_sp_rt6 * +mlxsw_sp_fib6_entry_rt_find(const struct mlxsw_sp_fib6_entry *fib6_entry, + const struct rt6_info *rt) +{ + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + + list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { + if (mlxsw_sp_rt6->rt == rt) + return mlxsw_sp_rt6; + } + + return NULL; +} + +static bool mlxsw_sp_nexthop6_ipip_type(const struct mlxsw_sp *mlxsw_sp, + const struct rt6_info *rt, + enum mlxsw_sp_ipip_type *ret) +{ + return rt->dst.dev && + mlxsw_sp_netdev_ipip_type(mlxsw_sp, rt->dst.dev, ret); +} + +static int mlxsw_sp_nexthop6_type_init(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group *nh_grp, + struct mlxsw_sp_nexthop *nh, + const struct rt6_info *rt) +{ + struct mlxsw_sp_router *router = mlxsw_sp->router; + struct net_device *dev = rt->dst.dev; + enum mlxsw_sp_ipip_type ipipt; + struct mlxsw_sp_rif *rif; + int err; + + if (mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, &ipipt) && + router->ipip_ops_arr[ipipt]->can_offload(mlxsw_sp, dev, + MLXSW_SP_L3_PROTO_IPV6)) { + nh->type = MLXSW_SP_NEXTHOP_TYPE_IPIP; + return mlxsw_sp_nexthop_ipip_init(mlxsw_sp, ipipt, nh, dev); + } + + nh->type = MLXSW_SP_NEXTHOP_TYPE_ETH; + rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); + if (!rif) + return 0; + mlxsw_sp_nexthop_rif_init(nh, rif); + + err = mlxsw_sp_nexthop_neigh_init(mlxsw_sp, nh); + if (err) + goto err_nexthop_neigh_init; + + return 0; + +err_nexthop_neigh_init: + mlxsw_sp_nexthop_rif_fini(nh); + return err; +} + +static void mlxsw_sp_nexthop6_type_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) +{ + mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh); +} + +static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group *nh_grp, + struct mlxsw_sp_nexthop *nh, + const struct rt6_info *rt) +{ + struct net_device *dev = rt->dst.dev; + + nh->nh_grp = nh_grp; + memcpy(&nh->gw_addr, &rt->rt6i_gateway, sizeof(nh->gw_addr)); + + if (!dev) + return 0; + nh->ifindex = dev->ifindex; + + return mlxsw_sp_nexthop6_type_init(mlxsw_sp, nh_grp, nh, rt); +} + +static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) +{ + mlxsw_sp_nexthop6_type_fini(mlxsw_sp, nh); +} + +static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp, + const struct rt6_info *rt) +{ + return rt->rt6i_flags & RTF_GATEWAY || + mlxsw_sp_nexthop6_ipip_type(mlxsw_sp, rt, NULL); +} + +static struct mlxsw_sp_nexthop_group * +mlxsw_sp_nexthop6_group_create(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry) +{ + struct mlxsw_sp_nexthop_group *nh_grp; + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + struct mlxsw_sp_nexthop *nh; + size_t alloc_size; + int i = 0; + int err; + + alloc_size = sizeof(*nh_grp) + + fib6_entry->nrt6 * sizeof(struct mlxsw_sp_nexthop); + nh_grp = kzalloc(alloc_size, GFP_KERNEL); + if (!nh_grp) + return ERR_PTR(-ENOMEM); + INIT_LIST_HEAD(&nh_grp->fib_list); +#if IS_ENABLED(CONFIG_IPV6) + nh_grp->neigh_tbl = &nd_tbl; +#endif + mlxsw_sp_rt6 = list_first_entry(&fib6_entry->rt6_list, + struct mlxsw_sp_rt6, list); + nh_grp->gateway = mlxsw_sp_rt6_is_gateway(mlxsw_sp, mlxsw_sp_rt6->rt); + nh_grp->count = fib6_entry->nrt6; + for (i = 0; i < nh_grp->count; i++) { + struct rt6_info *rt = mlxsw_sp_rt6->rt; + + nh = &nh_grp->nexthops[i]; + err = mlxsw_sp_nexthop6_init(mlxsw_sp, nh_grp, nh, rt); + if (err) + goto err_nexthop6_init; + mlxsw_sp_rt6 = list_next_entry(mlxsw_sp_rt6, list); + } + + err = mlxsw_sp_nexthop_group_insert(mlxsw_sp, nh_grp); + if (err) + goto err_nexthop_group_insert; + + mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); + return nh_grp; + +err_nexthop_group_insert: +err_nexthop6_init: + for (i--; i >= 0; i--) { + nh = &nh_grp->nexthops[i]; + mlxsw_sp_nexthop6_fini(mlxsw_sp, nh); + } + kfree(nh_grp); + return ERR_PTR(err); +} + +static void +mlxsw_sp_nexthop6_group_destroy(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group *nh_grp) +{ + struct mlxsw_sp_nexthop *nh; + int i = nh_grp->count; + + mlxsw_sp_nexthop_group_remove(mlxsw_sp, nh_grp); + for (i--; i >= 0; i--) { + nh = &nh_grp->nexthops[i]; + mlxsw_sp_nexthop6_fini(mlxsw_sp, nh); + } + mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); + WARN_ON(nh_grp->adj_index_valid); + kfree(nh_grp); +} + +static int mlxsw_sp_nexthop6_group_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry) +{ + struct mlxsw_sp_nexthop_group *nh_grp; + + nh_grp = mlxsw_sp_nexthop6_group_lookup(mlxsw_sp, fib6_entry); + if (!nh_grp) { + nh_grp = mlxsw_sp_nexthop6_group_create(mlxsw_sp, fib6_entry); + if (IS_ERR(nh_grp)) + return PTR_ERR(nh_grp); + } + + list_add_tail(&fib6_entry->common.nexthop_group_node, + &nh_grp->fib_list); + fib6_entry->common.nh_group = nh_grp; + + return 0; +} + +static void mlxsw_sp_nexthop6_group_put(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry) +{ + struct mlxsw_sp_nexthop_group *nh_grp = fib_entry->nh_group; + + list_del(&fib_entry->nexthop_group_node); + if (!list_empty(&nh_grp->fib_list)) + return; + mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, nh_grp); +} + +static int +mlxsw_sp_nexthop6_group_update(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry) +{ + struct mlxsw_sp_nexthop_group *old_nh_grp = fib6_entry->common.nh_group; + int err; + + fib6_entry->common.nh_group = NULL; + list_del(&fib6_entry->common.nexthop_group_node); + + err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry); + if (err) + goto err_nexthop6_group_get; + + /* In case this entry is offloaded, then the adjacency index + * currently associated with it in the device's table is that + * of the old group. Start using the new one instead. + */ + err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common); + if (err) + goto err_fib_node_entry_add; + + if (list_empty(&old_nh_grp->fib_list)) + mlxsw_sp_nexthop6_group_destroy(mlxsw_sp, old_nh_grp); + + return 0; + +err_fib_node_entry_add: + mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common); +err_nexthop6_group_get: + list_add_tail(&fib6_entry->common.nexthop_group_node, + &old_nh_grp->fib_list); + fib6_entry->common.nh_group = old_nh_grp; + return err; +} + +static int +mlxsw_sp_fib6_entry_nexthop_add(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry, + struct rt6_info *rt) +{ + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + int err; + + mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt); + if (IS_ERR(mlxsw_sp_rt6)) + return PTR_ERR(mlxsw_sp_rt6); + + list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list); + fib6_entry->nrt6++; + + err = mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry); + if (err) + goto err_nexthop6_group_update; + + return 0; + +err_nexthop6_group_update: + fib6_entry->nrt6--; + list_del(&mlxsw_sp_rt6->list); + mlxsw_sp_rt6_destroy(mlxsw_sp_rt6); + return err; +} + +static void +mlxsw_sp_fib6_entry_nexthop_del(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry, + struct rt6_info *rt) +{ + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + + mlxsw_sp_rt6 = mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt); + if (WARN_ON(!mlxsw_sp_rt6)) + return; + + fib6_entry->nrt6--; + list_del(&mlxsw_sp_rt6->list); + mlxsw_sp_nexthop6_group_update(mlxsw_sp, fib6_entry); + mlxsw_sp_rt6_destroy(mlxsw_sp_rt6); +} + +static void mlxsw_sp_fib6_entry_type_set(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_entry *fib_entry, + const struct rt6_info *rt) +{ + /* Packets hitting RTF_REJECT routes need to be discarded by the + * stack. We can rely on their destination device not having a + * RIF (it's the loopback device) and can thus use action type + * local, which will cause them to be trapped with a lower + * priority than packets that need to be locally received. + */ + if (rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST)) + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP; + else if (rt->rt6i_flags & RTF_REJECT) + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; + else if (mlxsw_sp_rt6_is_gateway(mlxsw_sp, rt)) + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_REMOTE; + else + fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL; +} + +static void +mlxsw_sp_fib6_entry_rt_destroy_all(struct mlxsw_sp_fib6_entry *fib6_entry) +{ + struct mlxsw_sp_rt6 *mlxsw_sp_rt6, *tmp; + + list_for_each_entry_safe(mlxsw_sp_rt6, tmp, &fib6_entry->rt6_list, + list) { + fib6_entry->nrt6--; + list_del(&mlxsw_sp_rt6->list); + mlxsw_sp_rt6_destroy(mlxsw_sp_rt6); + } +} + +static struct mlxsw_sp_fib6_entry * +mlxsw_sp_fib6_entry_create(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_node *fib_node, + struct rt6_info *rt) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; struct mlxsw_sp_fib_entry *fib_entry; + struct mlxsw_sp_rt6 *mlxsw_sp_rt6; + int err; + + fib6_entry = kzalloc(sizeof(*fib6_entry), GFP_KERNEL); + if (!fib6_entry) + return ERR_PTR(-ENOMEM); + fib_entry = &fib6_entry->common; + + mlxsw_sp_rt6 = mlxsw_sp_rt6_create(rt); + if (IS_ERR(mlxsw_sp_rt6)) { + err = PTR_ERR(mlxsw_sp_rt6); + goto err_rt6_create; + } + + mlxsw_sp_fib6_entry_type_set(mlxsw_sp, fib_entry, mlxsw_sp_rt6->rt); + + INIT_LIST_HEAD(&fib6_entry->rt6_list); + list_add_tail(&mlxsw_sp_rt6->list, &fib6_entry->rt6_list); + fib6_entry->nrt6 = 1; + err = mlxsw_sp_nexthop6_group_get(mlxsw_sp, fib6_entry); + if (err) + goto err_nexthop6_group_get; + + fib_entry->fib_node = fib_node; + + return fib6_entry; + +err_nexthop6_group_get: + list_del(&mlxsw_sp_rt6->list); + mlxsw_sp_rt6_destroy(mlxsw_sp_rt6); +err_rt6_create: + kfree(fib6_entry); + return ERR_PTR(err); +} + +static void mlxsw_sp_fib6_entry_destroy(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry) +{ + mlxsw_sp_nexthop6_group_put(mlxsw_sp, &fib6_entry->common); + mlxsw_sp_fib6_entry_rt_destroy_all(fib6_entry); + WARN_ON(fib6_entry->nrt6); + kfree(fib6_entry); +} + +static struct mlxsw_sp_fib6_entry * +mlxsw_sp_fib6_node_entry_find(const struct mlxsw_sp_fib_node *fib_node, + const struct rt6_info *nrt, bool replace) +{ + struct mlxsw_sp_fib6_entry *fib6_entry, *fallback = NULL; + + list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { + struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(fib6_entry); + + if (rt->rt6i_table->tb6_id > nrt->rt6i_table->tb6_id) + continue; + if (rt->rt6i_table->tb6_id != nrt->rt6i_table->tb6_id) + break; + if (replace && rt->rt6i_metric == nrt->rt6i_metric) { + if (mlxsw_sp_fib6_rt_can_mp(rt) == + mlxsw_sp_fib6_rt_can_mp(nrt)) + return fib6_entry; + if (mlxsw_sp_fib6_rt_can_mp(nrt)) + fallback = fallback ?: fib6_entry; + } + if (rt->rt6i_metric > nrt->rt6i_metric) + return fallback ?: fib6_entry; + } + + return fallback; +} + +static int +mlxsw_sp_fib6_node_list_insert(struct mlxsw_sp_fib6_entry *new6_entry, + bool replace) +{ + struct mlxsw_sp_fib_node *fib_node = new6_entry->common.fib_node; + struct rt6_info *nrt = mlxsw_sp_fib6_entry_rt(new6_entry); + struct mlxsw_sp_fib6_entry *fib6_entry; + + fib6_entry = mlxsw_sp_fib6_node_entry_find(fib_node, nrt, replace); + + if (replace && WARN_ON(!fib6_entry)) + return -EINVAL; + + if (fib6_entry) { + list_add_tail(&new6_entry->common.list, + &fib6_entry->common.list); + } else { + struct mlxsw_sp_fib6_entry *last; + + list_for_each_entry(last, &fib_node->entry_list, common.list) { + struct rt6_info *rt = mlxsw_sp_fib6_entry_rt(last); + + if (nrt->rt6i_table->tb6_id > rt->rt6i_table->tb6_id) + break; + fib6_entry = last; + } + + if (fib6_entry) + list_add(&new6_entry->common.list, + &fib6_entry->common.list); + else + list_add(&new6_entry->common.list, + &fib_node->entry_list); + } + + return 0; +} + +static void +mlxsw_sp_fib6_node_list_remove(struct mlxsw_sp_fib6_entry *fib6_entry) +{ + list_del(&fib6_entry->common.list); +} + +static int mlxsw_sp_fib6_node_entry_link(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry, + bool replace) +{ + int err; + + err = mlxsw_sp_fib6_node_list_insert(fib6_entry, replace); + if (err) + return err; + + err = mlxsw_sp_fib_node_entry_add(mlxsw_sp, &fib6_entry->common); + if (err) + goto err_fib_node_entry_add; + + return 0; + +err_fib_node_entry_add: + mlxsw_sp_fib6_node_list_remove(fib6_entry); + return err; +} + +static void +mlxsw_sp_fib6_node_entry_unlink(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry) +{ + mlxsw_sp_fib_node_entry_del(mlxsw_sp, &fib6_entry->common); + mlxsw_sp_fib6_node_list_remove(fib6_entry); +} + +static struct mlxsw_sp_fib6_entry * +mlxsw_sp_fib6_entry_lookup(struct mlxsw_sp *mlxsw_sp, + const struct rt6_info *rt) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; + struct mlxsw_sp_fib_node *fib_node; + struct mlxsw_sp_fib *fib; + struct mlxsw_sp_vr *vr; + + vr = mlxsw_sp_vr_find(mlxsw_sp, rt->rt6i_table->tb6_id); + if (!vr) + return NULL; + fib = mlxsw_sp_vr_fib(vr, MLXSW_SP_L3_PROTO_IPV6); + + fib_node = mlxsw_sp_fib_node_lookup(fib, &rt->rt6i_dst.addr, + sizeof(rt->rt6i_dst.addr), + rt->rt6i_dst.plen); + if (!fib_node) + return NULL; + + list_for_each_entry(fib6_entry, &fib_node->entry_list, common.list) { + struct rt6_info *iter_rt = mlxsw_sp_fib6_entry_rt(fib6_entry); + + if (rt->rt6i_table->tb6_id == iter_rt->rt6i_table->tb6_id && + rt->rt6i_metric == iter_rt->rt6i_metric && + mlxsw_sp_fib6_entry_rt_find(fib6_entry, rt)) + return fib6_entry; + } + + return NULL; +} + +static void mlxsw_sp_fib6_entry_replace(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib6_entry *fib6_entry, + bool replace) +{ + struct mlxsw_sp_fib_node *fib_node = fib6_entry->common.fib_node; + struct mlxsw_sp_fib6_entry *replaced; + + if (!replace) + return; + + replaced = list_next_entry(fib6_entry, common.list); + + mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, replaced); + mlxsw_sp_fib6_entry_destroy(mlxsw_sp, replaced); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); +} + +static int mlxsw_sp_router_fib6_add(struct mlxsw_sp *mlxsw_sp, + struct rt6_info *rt, bool replace) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; + struct mlxsw_sp_fib_node *fib_node; + int err; + + if (mlxsw_sp->router->aborted) + return 0; + + if (rt->rt6i_src.plen) + return -EINVAL; + + if (mlxsw_sp_fib6_rt_should_ignore(rt)) + return 0; + + fib_node = mlxsw_sp_fib_node_get(mlxsw_sp, rt->rt6i_table->tb6_id, + &rt->rt6i_dst.addr, + sizeof(rt->rt6i_dst.addr), + rt->rt6i_dst.plen, + MLXSW_SP_L3_PROTO_IPV6); + if (IS_ERR(fib_node)) + return PTR_ERR(fib_node); + + /* Before creating a new entry, try to append route to an existing + * multipath entry. + */ + fib6_entry = mlxsw_sp_fib6_node_mp_entry_find(fib_node, rt, replace); + if (fib6_entry) { + err = mlxsw_sp_fib6_entry_nexthop_add(mlxsw_sp, fib6_entry, rt); + if (err) + goto err_fib6_entry_nexthop_add; + return 0; + } + + fib6_entry = mlxsw_sp_fib6_entry_create(mlxsw_sp, fib_node, rt); + if (IS_ERR(fib6_entry)) { + err = PTR_ERR(fib6_entry); + goto err_fib6_entry_create; + } + + err = mlxsw_sp_fib6_node_entry_link(mlxsw_sp, fib6_entry, replace); + if (err) + goto err_fib6_node_entry_link; + + mlxsw_sp_fib6_entry_replace(mlxsw_sp, fib6_entry, replace); + + return 0; + +err_fib6_node_entry_link: + mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); +err_fib6_entry_create: +err_fib6_entry_nexthop_add: + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); + return err; +} + +static void mlxsw_sp_router_fib6_del(struct mlxsw_sp *mlxsw_sp, + struct rt6_info *rt) +{ + struct mlxsw_sp_fib6_entry *fib6_entry; struct mlxsw_sp_fib_node *fib_node; if (mlxsw_sp->router->aborted) return; - fib_entry = mlxsw_sp_fib4_entry_lookup(mlxsw_sp, fen_info); - if (WARN_ON(!fib_entry)) + if (mlxsw_sp_fib6_rt_should_ignore(rt)) return; - fib_node = fib_entry->fib_node; - mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib_entry); - mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry); - mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node); + fib6_entry = mlxsw_sp_fib6_entry_lookup(mlxsw_sp, rt); + if (WARN_ON(!fib6_entry)) + return; + + /* If route is part of a multipath entry, but not the last one + * removed, then only reduce its nexthop group. + */ + if (!list_is_singular(&fib6_entry->rt6_list)) { + mlxsw_sp_fib6_entry_nexthop_del(mlxsw_sp, fib6_entry, rt); + return; + } + + fib_node = fib6_entry->common.fib_node; + + mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry); + mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); } -static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) +static int __mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp, + enum mlxsw_reg_ralxx_protocol proto, + u8 tree_id) { char ralta_pl[MLXSW_REG_RALTA_LEN]; char ralst_pl[MLXSW_REG_RALST_LEN]; int i, err; - mlxsw_reg_ralta_pack(ralta_pl, true, MLXSW_REG_RALXX_PROTOCOL_IPV4, - MLXSW_SP_LPM_TREE_MIN); + mlxsw_reg_ralta_pack(ralta_pl, true, proto, tree_id); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl); if (err) return err; - mlxsw_reg_ralst_pack(ralst_pl, 0xff, MLXSW_SP_LPM_TREE_MIN); + mlxsw_reg_ralst_pack(ralst_pl, 0xff, tree_id); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl); if (err) return err; @@ -2708,20 +4588,14 @@ static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) char raltb_pl[MLXSW_REG_RALTB_LEN]; char ralue_pl[MLXSW_REG_RALUE_LEN]; - if (!mlxsw_sp_vr_is_used(vr)) - continue; - - mlxsw_reg_raltb_pack(raltb_pl, vr->id, - MLXSW_REG_RALXX_PROTOCOL_IPV4, - MLXSW_SP_LPM_TREE_MIN); + mlxsw_reg_raltb_pack(raltb_pl, vr->id, proto, tree_id); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl); if (err) return err; - mlxsw_reg_ralue_pack4(ralue_pl, MLXSW_SP_L3_PROTO_IPV4, - MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0, - 0); + mlxsw_reg_ralue_pack(ralue_pl, proto, + MLXSW_REG_RALUE_OP_WRITE_WRITE, vr->id, 0); mlxsw_reg_ralue_act_ip2me_pack(ralue_pl); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl); @@ -2732,17 +4606,33 @@ static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) return 0; } +static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) +{ + enum mlxsw_reg_ralxx_protocol proto = MLXSW_REG_RALXX_PROTOCOL_IPV4; + int err; + + err = __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto, + MLXSW_SP_LPM_TREE_MIN); + if (err) + return err; + + proto = MLXSW_REG_RALXX_PROTOCOL_IPV6; + return __mlxsw_sp_router_set_abort_trap(mlxsw_sp, proto, + MLXSW_SP_LPM_TREE_MIN + 1); +} + static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node) { - struct mlxsw_sp_fib_entry *fib_entry, *tmp; + struct mlxsw_sp_fib4_entry *fib4_entry, *tmp; - list_for_each_entry_safe(fib_entry, tmp, &fib_node->entry_list, list) { - bool do_break = &tmp->list == &fib_node->entry_list; + list_for_each_entry_safe(fib4_entry, tmp, &fib_node->entry_list, + common.list) { + bool do_break = &tmp->common.list == &fib_node->entry_list; - mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib_entry); - mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib_entry); - mlxsw_sp_fib4_node_put(mlxsw_sp, fib_node); + mlxsw_sp_fib4_node_entry_unlink(mlxsw_sp, fib4_entry); + mlxsw_sp_fib4_entry_destroy(mlxsw_sp, fib4_entry); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); /* Break when entry list is empty and node was freed. * Otherwise, we'll access freed memory in the next * iteration. @@ -2752,6 +4642,23 @@ static void mlxsw_sp_fib4_node_flush(struct mlxsw_sp *mlxsw_sp, } } +static void mlxsw_sp_fib6_node_flush(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_fib_node *fib_node) +{ + struct mlxsw_sp_fib6_entry *fib6_entry, *tmp; + + list_for_each_entry_safe(fib6_entry, tmp, &fib_node->entry_list, + common.list) { + bool do_break = &tmp->common.list == &fib_node->entry_list; + + mlxsw_sp_fib6_node_entry_unlink(mlxsw_sp, fib6_entry); + mlxsw_sp_fib6_entry_destroy(mlxsw_sp, fib6_entry); + mlxsw_sp_fib_node_put(mlxsw_sp, fib_node); + if (do_break) + break; + } +} + static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fib_node *fib_node) { @@ -2760,7 +4667,7 @@ static void mlxsw_sp_fib_node_flush(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_fib4_node_flush(mlxsw_sp, fib_node); break; case MLXSW_SP_L3_PROTO_IPV6: - WARN_ON_ONCE(1); + mlxsw_sp_fib6_node_flush(mlxsw_sp, fib_node); break; } } @@ -2791,10 +4698,17 @@ static void mlxsw_sp_router_fib_flush(struct mlxsw_sp *mlxsw_sp) if (!mlxsw_sp_vr_is_used(vr)) continue; mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4); + + /* If virtual router was only used for IPv4, then it's no + * longer used. + */ + if (!mlxsw_sp_vr_is_used(vr)) + continue; + mlxsw_sp_vr_fib_flush(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6); } } -static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp) +static void mlxsw_sp_router_fib_abort(struct mlxsw_sp *mlxsw_sp) { int err; @@ -2811,6 +4725,7 @@ static void mlxsw_sp_router_fib4_abort(struct mlxsw_sp *mlxsw_sp) struct mlxsw_sp_fib_event_work { struct work_struct work; union { + struct fib6_entry_notifier_info fen6_info; struct fib_entry_notifier_info fen_info; struct fib_rule_notifier_info fr_info; struct fib_nh_notifier_info fnh_info; @@ -2819,7 +4734,7 @@ struct mlxsw_sp_fib_event_work { unsigned long event; }; -static void mlxsw_sp_router_fib_event_work(struct work_struct *work) +static void mlxsw_sp_router_fib4_event_work(struct work_struct *work) { struct mlxsw_sp_fib_event_work *fib_work = container_of(work, struct mlxsw_sp_fib_event_work, work); @@ -2839,7 +4754,7 @@ static void mlxsw_sp_router_fib_event_work(struct work_struct *work) err = mlxsw_sp_router_fib4_add(mlxsw_sp, &fib_work->fen_info, replace, append); if (err) - mlxsw_sp_router_fib4_abort(mlxsw_sp); + mlxsw_sp_router_fib_abort(mlxsw_sp); fib_info_put(fib_work->fen_info.fi); break; case FIB_EVENT_ENTRY_DEL: @@ -2850,13 +4765,13 @@ static void mlxsw_sp_router_fib_event_work(struct work_struct *work) case FIB_EVENT_RULE_DEL: rule = fib_work->fr_info.rule; if (!fib4_rule_default(rule) && !rule->l3mdev) - mlxsw_sp_router_fib4_abort(mlxsw_sp); + mlxsw_sp_router_fib_abort(mlxsw_sp); fib_rule_put(rule); break; case FIB_EVENT_NH_ADD: /* fall through */ case FIB_EVENT_NH_DEL: - mlxsw_sp_nexthop_event(mlxsw_sp, fib_work->event, - fib_work->fnh_info.fib_nh); + mlxsw_sp_nexthop4_event(mlxsw_sp, fib_work->event, + fib_work->fnh_info.fib_nh); fib_info_put(fib_work->fnh_info.fib_nh->nh_parent); break; } @@ -2864,6 +4779,87 @@ static void mlxsw_sp_router_fib_event_work(struct work_struct *work) kfree(fib_work); } +static void mlxsw_sp_router_fib6_event_work(struct work_struct *work) +{ + struct mlxsw_sp_fib_event_work *fib_work = + container_of(work, struct mlxsw_sp_fib_event_work, work); + struct mlxsw_sp *mlxsw_sp = fib_work->mlxsw_sp; + struct fib_rule *rule; + bool replace; + int err; + + rtnl_lock(); + switch (fib_work->event) { + case FIB_EVENT_ENTRY_REPLACE: /* fall through */ + case FIB_EVENT_ENTRY_ADD: + replace = fib_work->event == FIB_EVENT_ENTRY_REPLACE; + err = mlxsw_sp_router_fib6_add(mlxsw_sp, + fib_work->fen6_info.rt, replace); + if (err) + mlxsw_sp_router_fib_abort(mlxsw_sp); + mlxsw_sp_rt6_release(fib_work->fen6_info.rt); + break; + case FIB_EVENT_ENTRY_DEL: + mlxsw_sp_router_fib6_del(mlxsw_sp, fib_work->fen6_info.rt); + mlxsw_sp_rt6_release(fib_work->fen6_info.rt); + break; + case FIB_EVENT_RULE_ADD: /* fall through */ + case FIB_EVENT_RULE_DEL: + rule = fib_work->fr_info.rule; + if (!fib6_rule_default(rule) && !rule->l3mdev) + mlxsw_sp_router_fib_abort(mlxsw_sp); + fib_rule_put(rule); + break; + } + rtnl_unlock(); + kfree(fib_work); +} + +static void mlxsw_sp_router_fib4_event(struct mlxsw_sp_fib_event_work *fib_work, + struct fib_notifier_info *info) +{ + switch (fib_work->event) { + case FIB_EVENT_ENTRY_REPLACE: /* fall through */ + case FIB_EVENT_ENTRY_APPEND: /* fall through */ + case FIB_EVENT_ENTRY_ADD: /* fall through */ + case FIB_EVENT_ENTRY_DEL: + memcpy(&fib_work->fen_info, info, sizeof(fib_work->fen_info)); + /* Take referece on fib_info to prevent it from being + * freed while work is queued. Release it afterwards. + */ + fib_info_hold(fib_work->fen_info.fi); + break; + case FIB_EVENT_RULE_ADD: /* fall through */ + case FIB_EVENT_RULE_DEL: + memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info)); + fib_rule_get(fib_work->fr_info.rule); + break; + case FIB_EVENT_NH_ADD: /* fall through */ + case FIB_EVENT_NH_DEL: + memcpy(&fib_work->fnh_info, info, sizeof(fib_work->fnh_info)); + fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent); + break; + } +} + +static void mlxsw_sp_router_fib6_event(struct mlxsw_sp_fib_event_work *fib_work, + struct fib_notifier_info *info) +{ + switch (fib_work->event) { + case FIB_EVENT_ENTRY_REPLACE: /* fall through */ + case FIB_EVENT_ENTRY_ADD: /* fall through */ + case FIB_EVENT_ENTRY_DEL: + memcpy(&fib_work->fen6_info, info, sizeof(fib_work->fen6_info)); + rt6_hold(fib_work->fen6_info.rt); + break; + case FIB_EVENT_RULE_ADD: /* fall through */ + case FIB_EVENT_RULE_DEL: + memcpy(&fib_work->fr_info, info, sizeof(fib_work->fr_info)); + fib_rule_get(fib_work->fr_info.rule); + break; + } +} + /* Called with rcu_read_lock() */ static int mlxsw_sp_router_fib_event(struct notifier_block *nb, unsigned long event, void *ptr) @@ -2879,31 +4875,18 @@ static int mlxsw_sp_router_fib_event(struct notifier_block *nb, if (WARN_ON(!fib_work)) return NOTIFY_BAD; - INIT_WORK(&fib_work->work, mlxsw_sp_router_fib_event_work); router = container_of(nb, struct mlxsw_sp_router, fib_nb); fib_work->mlxsw_sp = router->mlxsw_sp; fib_work->event = event; - switch (event) { - case FIB_EVENT_ENTRY_REPLACE: /* fall through */ - case FIB_EVENT_ENTRY_APPEND: /* fall through */ - case FIB_EVENT_ENTRY_ADD: /* fall through */ - case FIB_EVENT_ENTRY_DEL: - memcpy(&fib_work->fen_info, ptr, sizeof(fib_work->fen_info)); - /* Take referece on fib_info to prevent it from being - * freed while work is queued. Release it afterwards. - */ - fib_info_hold(fib_work->fen_info.fi); + switch (info->family) { + case AF_INET: + INIT_WORK(&fib_work->work, mlxsw_sp_router_fib4_event_work); + mlxsw_sp_router_fib4_event(fib_work, info); break; - case FIB_EVENT_RULE_ADD: /* fall through */ - case FIB_EVENT_RULE_DEL: - memcpy(&fib_work->fr_info, ptr, sizeof(fib_work->fr_info)); - fib_rule_get(fib_work->fr_info.rule); - break; - case FIB_EVENT_NH_ADD: /* fall through */ - case FIB_EVENT_NH_DEL: - memcpy(&fib_work->fnh_info, ptr, sizeof(fib_work->fnh_info)); - fib_info_hold(fib_work->fnh_info.fib_nh->nh_parent); + case AF_INET6: + INIT_WORK(&fib_work->work, mlxsw_sp_router_fib6_event_work); + mlxsw_sp_router_fib6_event(fib_work, info); break; } @@ -2948,17 +4931,28 @@ static void mlxsw_sp_router_rif_gone_sync(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_neigh_rif_gone_sync(mlxsw_sp, rif); } -static bool mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, - const struct in_device *in_dev, - unsigned long event) +static bool +mlxsw_sp_rif_should_config(struct mlxsw_sp_rif *rif, struct net_device *dev, + unsigned long event) { + struct inet6_dev *inet6_dev; + bool addr_list_empty = true; + struct in_device *idev; + switch (event) { case NETDEV_UP: - if (!rif) - return true; - return false; + return rif == NULL; case NETDEV_DOWN: - if (rif && !in_dev->ifa_list && + idev = __in_dev_get_rtnl(dev); + if (idev && idev->ifa_list) + addr_list_empty = false; + + inet6_dev = __in6_dev_get(dev); + if (addr_list_empty && inet6_dev && + !list_empty(&inet6_dev->addr_list)) + addr_list_empty = false; + + if (rif && addr_list_empty && !netif_is_l3_slave(rif->dev)) return true; /* It is possible we already removed the RIF ourselves @@ -2977,7 +4971,10 @@ mlxsw_sp_dev_rif_type(const struct mlxsw_sp *mlxsw_sp, { enum mlxsw_sp_fid_type type; - /* RIF type is derived from the type of the underlying FID */ + if (mlxsw_sp_netdev_ipip_type(mlxsw_sp, dev, NULL)) + return MLXSW_SP_RIF_TYPE_IPIP_LB; + + /* Otherwise RIF type is derived from the type of the underlying FID. */ if (is_vlan_dev(dev) && netif_is_bridge_master(vlan_dev_real_dev(dev))) type = MLXSW_SP_FID_TYPE_8021Q; else if (netif_is_bridge_master(dev) && br_vlan_enabled(dev)) @@ -3036,6 +5033,16 @@ u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif) return rif->rif_index; } +u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *lb_rif) +{ + return lb_rif->common.rif_index; +} + +u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *lb_rif) +{ + return lb_rif->ul_vr_id; +} + int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif) { return rif->dev->ifindex; @@ -3047,9 +5054,9 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, { u32 tb_id = l3mdev_fib_table(params->dev); const struct mlxsw_sp_rif_ops *ops; + struct mlxsw_sp_fid *fid = NULL; enum mlxsw_sp_rif_type type; struct mlxsw_sp_rif *rif; - struct mlxsw_sp_fid *fid; struct mlxsw_sp_vr *vr; u16 rif_index; int err; @@ -3073,12 +5080,14 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, rif->mlxsw_sp = mlxsw_sp; rif->ops = ops; - fid = ops->fid_get(rif); - if (IS_ERR(fid)) { - err = PTR_ERR(fid); - goto err_fid_get; + if (ops->fid_get) { + fid = ops->fid_get(rif); + if (IS_ERR(fid)) { + err = PTR_ERR(fid); + goto err_fid_get; + } + rif->fid = fid; } - rif->fid = fid; if (ops->setup) ops->setup(rif, params); @@ -3087,22 +5096,15 @@ mlxsw_sp_rif_create(struct mlxsw_sp *mlxsw_sp, if (err) goto err_configure; - err = mlxsw_sp_rif_fdb_op(mlxsw_sp, params->dev->dev_addr, - mlxsw_sp_fid_index(fid), true); - if (err) - goto err_rif_fdb_op; - mlxsw_sp_rif_counters_alloc(rif); - mlxsw_sp_fid_rif_set(fid, rif); mlxsw_sp->router->rifs[rif_index] = rif; vr->rif_count++; return rif; -err_rif_fdb_op: - ops->deconfigure(rif); err_configure: - mlxsw_sp_fid_put(fid); + if (fid) + mlxsw_sp_fid_put(fid); err_fid_get: kfree(rif); err_rif_alloc: @@ -3123,12 +5125,11 @@ void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif) vr->rif_count--; mlxsw_sp->router->rifs[rif->rif_index] = NULL; - mlxsw_sp_fid_rif_set(fid, NULL); mlxsw_sp_rif_counters_free(rif); - mlxsw_sp_rif_fdb_op(mlxsw_sp, rif->dev->dev_addr, - mlxsw_sp_fid_index(fid), false); ops->deconfigure(rif); - mlxsw_sp_fid_put(fid); + if (fid) + /* Loopback RIFs are not associated with a FID. */ + mlxsw_sp_fid_put(fid); kfree(rif); mlxsw_sp_vr_put(vr); } @@ -3356,7 +5357,7 @@ int mlxsw_sp_inetaddr_event(struct notifier_block *unused, goto out; rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); - if (!mlxsw_sp_rif_should_config(rif, ifa->ifa_dev, event)) + if (!mlxsw_sp_rif_should_config(rif, dev, event)) goto out; err = __mlxsw_sp_inetaddr_event(dev, event); @@ -3364,6 +5365,61 @@ out: return notifier_from_errno(err); } +struct mlxsw_sp_inet6addr_event_work { + struct work_struct work; + struct net_device *dev; + unsigned long event; +}; + +static void mlxsw_sp_inet6addr_event_work(struct work_struct *work) +{ + struct mlxsw_sp_inet6addr_event_work *inet6addr_work = + container_of(work, struct mlxsw_sp_inet6addr_event_work, work); + struct net_device *dev = inet6addr_work->dev; + unsigned long event = inet6addr_work->event; + struct mlxsw_sp *mlxsw_sp; + struct mlxsw_sp_rif *rif; + + rtnl_lock(); + mlxsw_sp = mlxsw_sp_lower_get(dev); + if (!mlxsw_sp) + goto out; + + rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev); + if (!mlxsw_sp_rif_should_config(rif, dev, event)) + goto out; + + __mlxsw_sp_inetaddr_event(dev, event); +out: + rtnl_unlock(); + dev_put(dev); + kfree(inet6addr_work); +} + +/* Called with rcu_read_lock() */ +int mlxsw_sp_inet6addr_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct inet6_ifaddr *if6 = (struct inet6_ifaddr *) ptr; + struct mlxsw_sp_inet6addr_event_work *inet6addr_work; + struct net_device *dev = if6->idev->dev; + + if (!mlxsw_sp_port_dev_lower_find_rcu(dev)) + return NOTIFY_DONE; + + inet6addr_work = kzalloc(sizeof(*inet6addr_work), GFP_ATOMIC); + if (!inet6addr_work) + return NOTIFY_BAD; + + INIT_WORK(&inet6addr_work->work, mlxsw_sp_inet6addr_event_work); + inet6addr_work->dev = dev; + inet6addr_work->event = event; + dev_hold(dev); + mlxsw_core_schedule_work(&inet6addr_work->work); + + return NOTIFY_DONE; +} + static int mlxsw_sp_rif_edit(struct mlxsw_sp *mlxsw_sp, u16 rif_index, const char *mac, int mtu) { @@ -3501,8 +5557,8 @@ static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable) rif_subport = mlxsw_sp_rif_subport_rif(rif); mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_SP_IF, - rif->rif_index, rif->vr_id, rif->dev->mtu, - rif->dev->dev_addr); + rif->rif_index, rif->vr_id, rif->dev->mtu); + mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr); mlxsw_reg_ritr_sp_if_pack(ritr_pl, rif_subport->lag, rif_subport->lag ? rif_subport->lag_id : rif_subport->system_port, @@ -3513,11 +5569,32 @@ static int mlxsw_sp_rif_subport_op(struct mlxsw_sp_rif *rif, bool enable) static int mlxsw_sp_rif_subport_configure(struct mlxsw_sp_rif *rif) { - return mlxsw_sp_rif_subport_op(rif, true); + int err; + + err = mlxsw_sp_rif_subport_op(rif, true); + if (err) + return err; + + err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, + mlxsw_sp_fid_index(rif->fid), true); + if (err) + goto err_rif_fdb_op; + + mlxsw_sp_fid_rif_set(rif->fid, rif); + return 0; + +err_rif_fdb_op: + mlxsw_sp_rif_subport_op(rif, false); + return err; } static void mlxsw_sp_rif_subport_deconfigure(struct mlxsw_sp_rif *rif) { + struct mlxsw_sp_fid *fid = rif->fid; + + mlxsw_sp_fid_rif_set(fid, NULL); + mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, + mlxsw_sp_fid_index(fid), false); mlxsw_sp_rif_subport_op(rif, false); } @@ -3544,7 +5621,8 @@ static int mlxsw_sp_rif_vlan_fid_op(struct mlxsw_sp_rif *rif, char ritr_pl[MLXSW_REG_RITR_LEN]; mlxsw_reg_ritr_pack(ritr_pl, enable, type, rif->rif_index, rif->vr_id, - rif->dev->mtu, rif->dev->dev_addr); + rif->dev->mtu); + mlxsw_reg_ritr_mac_pack(ritr_pl, rif->dev->dev_addr); mlxsw_reg_ritr_fid_set(ritr_pl, type, vid_fid); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); @@ -3565,25 +5643,48 @@ static int mlxsw_sp_rif_vlan_configure(struct mlxsw_sp_rif *rif) if (err) return err; + err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, + mlxsw_sp_router_port(mlxsw_sp), true); + if (err) + goto err_fid_mc_flood_set; + err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, mlxsw_sp_router_port(mlxsw_sp), true); if (err) goto err_fid_bc_flood_set; + err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, + mlxsw_sp_fid_index(rif->fid), true); + if (err) + goto err_rif_fdb_op; + + mlxsw_sp_fid_rif_set(rif->fid, rif); return 0; +err_rif_fdb_op: + mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, + mlxsw_sp_router_port(mlxsw_sp), false); err_fid_bc_flood_set: + mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, + mlxsw_sp_router_port(mlxsw_sp), false); +err_fid_mc_flood_set: mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false); return err; } static void mlxsw_sp_rif_vlan_deconfigure(struct mlxsw_sp_rif *rif) { - struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; u16 vid = mlxsw_sp_fid_8021q_vid(rif->fid); + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; + struct mlxsw_sp_fid *fid = rif->fid; + mlxsw_sp_fid_rif_set(fid, NULL); + mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, + mlxsw_sp_fid_index(fid), false); mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, mlxsw_sp_router_port(mlxsw_sp), false); + mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, + mlxsw_sp_router_port(mlxsw_sp), false); mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_VLAN_IF, vid, false); } @@ -3614,25 +5715,48 @@ static int mlxsw_sp_rif_fid_configure(struct mlxsw_sp_rif *rif) if (err) return err; + err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, + mlxsw_sp_router_port(mlxsw_sp), true); + if (err) + goto err_fid_mc_flood_set; + err = mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, mlxsw_sp_router_port(mlxsw_sp), true); if (err) goto err_fid_bc_flood_set; + err = mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, + mlxsw_sp_fid_index(rif->fid), true); + if (err) + goto err_rif_fdb_op; + + mlxsw_sp_fid_rif_set(rif->fid, rif); return 0; +err_rif_fdb_op: + mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, + mlxsw_sp_router_port(mlxsw_sp), false); err_fid_bc_flood_set: + mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, + mlxsw_sp_router_port(mlxsw_sp), false); +err_fid_mc_flood_set: mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false); return err; } static void mlxsw_sp_rif_fid_deconfigure(struct mlxsw_sp_rif *rif) { - struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; u16 fid_index = mlxsw_sp_fid_index(rif->fid); + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; + struct mlxsw_sp_fid *fid = rif->fid; + mlxsw_sp_fid_rif_set(fid, NULL); + mlxsw_sp_rif_fdb_op(rif->mlxsw_sp, rif->dev->dev_addr, + mlxsw_sp_fid_index(fid), false); mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_BC, mlxsw_sp_router_port(mlxsw_sp), false); + mlxsw_sp_fid_flood_set(rif->fid, MLXSW_SP_FLOOD_TYPE_MC, + mlxsw_sp_router_port(mlxsw_sp), false); mlxsw_sp_rif_vlan_fid_op(rif, MLXSW_REG_RITR_FID_IF, fid_index, false); } @@ -3650,10 +5774,104 @@ static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_fid_ops = { .fid_get = mlxsw_sp_rif_fid_fid_get, }; +static struct mlxsw_sp_rif_ipip_lb * +mlxsw_sp_rif_ipip_lb_rif(struct mlxsw_sp_rif *rif) +{ + return container_of(rif, struct mlxsw_sp_rif_ipip_lb, common); +} + +static void +mlxsw_sp_rif_ipip_lb_setup(struct mlxsw_sp_rif *rif, + const struct mlxsw_sp_rif_params *params) +{ + struct mlxsw_sp_rif_params_ipip_lb *params_lb; + struct mlxsw_sp_rif_ipip_lb *rif_lb; + + params_lb = container_of(params, struct mlxsw_sp_rif_params_ipip_lb, + common); + rif_lb = mlxsw_sp_rif_ipip_lb_rif(rif); + rif_lb->lb_config = params_lb->lb_config; +} + +static int +mlxsw_sp_rif_ipip_lb_op(struct mlxsw_sp_rif_ipip_lb *lb_rif, + struct mlxsw_sp_vr *ul_vr, bool enable) +{ + struct mlxsw_sp_rif_ipip_lb_config lb_cf = lb_rif->lb_config; + struct mlxsw_sp_rif *rif = &lb_rif->common; + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; + char ritr_pl[MLXSW_REG_RITR_LEN]; + u32 saddr4; + + switch (lb_cf.ul_protocol) { + case MLXSW_SP_L3_PROTO_IPV4: + saddr4 = be32_to_cpu(lb_cf.saddr.addr4); + mlxsw_reg_ritr_pack(ritr_pl, enable, MLXSW_REG_RITR_LOOPBACK_IF, + rif->rif_index, rif->vr_id, rif->dev->mtu); + mlxsw_reg_ritr_loopback_ipip4_pack(ritr_pl, lb_cf.lb_ipipt, + MLXSW_REG_RITR_LOOPBACK_IPIP_OPTIONS_GRE_KEY_PRESET, + ul_vr->id, saddr4, lb_cf.okey); + break; + + case MLXSW_SP_L3_PROTO_IPV6: + return -EAFNOSUPPORT; + } + + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl); +} + +static int +mlxsw_sp_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif) +{ + struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif); + u32 ul_tb_id = mlxsw_sp_ipip_dev_ul_tb_id(rif->dev); + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; + struct mlxsw_sp_vr *ul_vr; + int err; + + ul_vr = mlxsw_sp_vr_get(mlxsw_sp, ul_tb_id); + if (IS_ERR(ul_vr)) + return PTR_ERR(ul_vr); + + err = mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, true); + if (err) + goto err_loopback_op; + + lb_rif->ul_vr_id = ul_vr->id; + ++ul_vr->rif_count; + return 0; + +err_loopback_op: + mlxsw_sp_vr_put(ul_vr); + return err; +} + +static void mlxsw_sp_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif) +{ + struct mlxsw_sp_rif_ipip_lb *lb_rif = mlxsw_sp_rif_ipip_lb_rif(rif); + struct mlxsw_sp *mlxsw_sp = rif->mlxsw_sp; + struct mlxsw_sp_vr *ul_vr; + + ul_vr = &mlxsw_sp->router->vrs[lb_rif->ul_vr_id]; + mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, false); + + --ul_vr->rif_count; + mlxsw_sp_vr_put(ul_vr); +} + +static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = { + .type = MLXSW_SP_RIF_TYPE_IPIP_LB, + .rif_size = sizeof(struct mlxsw_sp_rif_ipip_lb), + .setup = mlxsw_sp_rif_ipip_lb_setup, + .configure = mlxsw_sp_rif_ipip_lb_configure, + .deconfigure = mlxsw_sp_rif_ipip_lb_deconfigure, +}; + static const struct mlxsw_sp_rif_ops *mlxsw_sp_rif_ops_arr[] = { [MLXSW_SP_RIF_TYPE_SUBPORT] = &mlxsw_sp_rif_subport_ops, [MLXSW_SP_RIF_TYPE_VLAN] = &mlxsw_sp_rif_vlan_ops, [MLXSW_SP_RIF_TYPE_FID] = &mlxsw_sp_rif_fid_ops, + [MLXSW_SP_RIF_TYPE_IPIP_LB] = &mlxsw_sp_rif_ipip_lb_ops, }; static int mlxsw_sp_rifs_init(struct mlxsw_sp *mlxsw_sp) @@ -3681,6 +5899,18 @@ static void mlxsw_sp_rifs_fini(struct mlxsw_sp *mlxsw_sp) kfree(mlxsw_sp->router->rifs); } +static int mlxsw_sp_ipips_init(struct mlxsw_sp *mlxsw_sp) +{ + mlxsw_sp->router->ipip_ops_arr = mlxsw_sp_ipip_ops_arr; + INIT_LIST_HEAD(&mlxsw_sp->router->ipip_list); + return 0; +} + +static void mlxsw_sp_ipips_fini(struct mlxsw_sp *mlxsw_sp) +{ + WARN_ON(!list_empty(&mlxsw_sp->router->ipip_list)); +} + static void mlxsw_sp_router_fib_dump_flush(struct notifier_block *nb) { struct mlxsw_sp_router *router; @@ -3704,7 +5934,7 @@ static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp) return -EIO; max_rifs = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); - mlxsw_reg_rgcr_pack(rgcr_pl, true); + mlxsw_reg_rgcr_pack(rgcr_pl, true, true); mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, max_rifs); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl); if (err) @@ -3716,7 +5946,7 @@ static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp) { char rgcr_pl[MLXSW_REG_RGCR_LEN]; - mlxsw_reg_rgcr_pack(rgcr_pl, false); + mlxsw_reg_rgcr_pack(rgcr_pl, false, false); mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl); } @@ -3740,6 +5970,10 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp) if (err) goto err_rifs_init; + err = mlxsw_sp_ipips_init(mlxsw_sp); + if (err) + goto err_ipips_init; + err = rhashtable_init(&mlxsw_sp->router->nexthop_ht, &mlxsw_sp_nexthop_ht_params); if (err) @@ -3781,6 +6015,8 @@ err_lpm_init: err_nexthop_group_ht_init: rhashtable_destroy(&mlxsw_sp->router->nexthop_ht); err_nexthop_ht_init: + mlxsw_sp_ipips_fini(mlxsw_sp); +err_ipips_init: mlxsw_sp_rifs_fini(mlxsw_sp); err_rifs_init: __mlxsw_sp_router_fini(mlxsw_sp); @@ -3797,6 +6033,7 @@ void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp) mlxsw_sp_lpm_fini(mlxsw_sp); rhashtable_destroy(&mlxsw_sp->router->nexthop_group_ht); rhashtable_destroy(&mlxsw_sp->router->nexthop_ht); + mlxsw_sp_ipips_fini(mlxsw_sp); mlxsw_sp_rifs_fini(mlxsw_sp); __mlxsw_sp_router_fini(mlxsw_sp); kfree(mlxsw_sp->router); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h index a3e8d2b25148..345fcc4f38e9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.h @@ -36,15 +36,38 @@ #define _MLXSW_ROUTER_H_ #include "spectrum.h" +#include "reg.h" + +enum mlxsw_sp_l3proto { + MLXSW_SP_L3_PROTO_IPV4, + MLXSW_SP_L3_PROTO_IPV6, +}; + +union mlxsw_sp_l3addr { + __be32 addr4; + struct in6_addr addr6; +}; + +struct mlxsw_sp_rif_ipip_lb; +struct mlxsw_sp_rif_ipip_lb_config { + enum mlxsw_reg_ritr_loopback_ipip_type lb_ipipt; + u32 okey; + enum mlxsw_sp_l3proto ul_protocol; /* Underlay. */ + union mlxsw_sp_l3addr saddr; +}; enum mlxsw_sp_rif_counter_dir { MLXSW_SP_RIF_COUNTER_INGRESS, MLXSW_SP_RIF_COUNTER_EGRESS, }; +struct mlxsw_sp_neigh_entry; + struct mlxsw_sp_rif *mlxsw_sp_rif_by_index(const struct mlxsw_sp *mlxsw_sp, u16 rif_index); u16 mlxsw_sp_rif_index(const struct mlxsw_sp_rif *rif); +u16 mlxsw_sp_ipip_lb_rif_index(const struct mlxsw_sp_rif_ipip_lb *rif); +u16 mlxsw_sp_ipip_lb_ul_vr_id(const struct mlxsw_sp_rif_ipip_lb *rif); int mlxsw_sp_rif_dev_ifindex(const struct mlxsw_sp_rif *rif); int mlxsw_sp_rif_counter_value_get(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_rif *rif, @@ -56,5 +79,33 @@ void mlxsw_sp_rif_counter_free(struct mlxsw_sp *mlxsw_sp, int mlxsw_sp_rif_counter_alloc(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_rif *rif, enum mlxsw_sp_rif_counter_dir dir); +struct mlxsw_sp_neigh_entry * +mlxsw_sp_rif_neigh_next(struct mlxsw_sp_rif *rif, + struct mlxsw_sp_neigh_entry *neigh_entry); +int mlxsw_sp_neigh_entry_type(struct mlxsw_sp_neigh_entry *neigh_entry); +unsigned char * +mlxsw_sp_neigh_entry_ha(struct mlxsw_sp_neigh_entry *neigh_entry); +u32 mlxsw_sp_neigh4_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry); +struct in6_addr * +mlxsw_sp_neigh6_entry_dip(struct mlxsw_sp_neigh_entry *neigh_entry); + +#define mlxsw_sp_rif_neigh_for_each(neigh_entry, rif) \ + for (neigh_entry = mlxsw_sp_rif_neigh_next(rif, NULL); neigh_entry; \ + neigh_entry = mlxsw_sp_rif_neigh_next(rif, neigh_entry)) +int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry, + u64 *p_counter); +void +mlxsw_sp_neigh_entry_counter_update(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_neigh_entry *neigh_entry, + bool adding); +bool mlxsw_sp_neigh_ipv6_ignore(struct mlxsw_sp_neigh_entry *neigh_entry); +union mlxsw_sp_l3addr +mlxsw_sp_ipip_netdev_saddr(enum mlxsw_sp_l3proto proto, + const struct net_device *ol_dev); +union mlxsw_sp_l3addr +mlxsw_sp_ipip_netdev_daddr(enum mlxsw_sp_l3proto proto, + const struct net_device *ol_dev); +__be32 mlxsw_sp_ipip_netdev_daddr4(const struct net_device *ol_dev); #endif /* _MLXSW_ROUTER_H_*/ diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchib.c b/drivers/net/ethernet/mellanox/mlxsw/switchib.c index 74341fe0eb25..ab7a29846bfa 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchib.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchib.c @@ -497,7 +497,7 @@ static void mlxsw_sib_fini(struct mlxsw_core *mlxsw_core) mlxsw_sib_ports_remove(mlxsw_sib); } -static struct mlxsw_config_profile mlxsw_sib_config_profile = { +static const struct mlxsw_config_profile mlxsw_sib_config_profile = { .used_max_system_port = 1, .max_system_port = 48000, .used_max_ib_mc = 1, diff --git a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c index 3b0f72455681..f3c29bbf07e2 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/switchx2.c +++ b/drivers/net/ethernet/mellanox/mlxsw/switchx2.c @@ -1674,7 +1674,7 @@ static void mlxsw_sx_fini(struct mlxsw_core *mlxsw_core) mlxsw_sx_ports_remove(mlxsw_sx); } -static struct mlxsw_config_profile mlxsw_sx_config_profile = { +static const struct mlxsw_config_profile mlxsw_sx_config_profile = { .used_max_vepa_channels = 1, .max_vepa_channels = 0, .used_max_mid = 1, diff --git a/drivers/net/ethernet/mellanox/mlxsw/trap.h b/drivers/net/ethernet/mellanox/mlxsw/trap.h index 12b5ed58f3eb..f396a1fef633 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/trap.h +++ b/drivers/net/ethernet/mellanox/mlxsw/trap.h @@ -61,11 +61,33 @@ enum { MLXSW_TRAP_ID_MTUERROR = 0x52, MLXSW_TRAP_ID_TTLERROR = 0x53, MLXSW_TRAP_ID_LBERROR = 0x54, - MLXSW_TRAP_ID_OSPF = 0x55, + MLXSW_TRAP_ID_IPV4_OSPF = 0x55, MLXSW_TRAP_ID_IP2ME = 0x5F, + MLXSW_TRAP_ID_IPV6_UNSPECIFIED_ADDRESS = 0x60, + MLXSW_TRAP_ID_IPV6_LINK_LOCAL_DEST = 0x61, + MLXSW_TRAP_ID_IPV6_LINK_LOCAL_SRC = 0x62, + MLXSW_TRAP_ID_IPV6_ALL_NODES_LINK = 0x63, + MLXSW_TRAP_ID_IPV6_OSPF = 0x64, + MLXSW_TRAP_ID_IPV6_MLDV12_LISTENER_QUERY = 0x65, + MLXSW_TRAP_ID_IPV6_MLDV1_LISTENER_REPORT = 0x66, + MLXSW_TRAP_ID_IPV6_MLDV1_LISTENER_DONE = 0x67, + MLXSW_TRAP_ID_IPV6_MLDV2_LISTENER_REPORT = 0x68, + MLXSW_TRAP_ID_IPV6_DHCP = 0x69, + MLXSW_TRAP_ID_IPV6_ALL_ROUTERS_LINK = 0x6F, MLXSW_TRAP_ID_RTR_INGRESS0 = 0x70, - MLXSW_TRAP_ID_BGP_IPV4 = 0x88, + MLXSW_TRAP_ID_IPV4_BGP = 0x88, + MLXSW_TRAP_ID_IPV6_BGP = 0x89, + MLXSW_TRAP_ID_L3_IPV6_ROUTER_SOLICITATION = 0x8A, + MLXSW_TRAP_ID_L3_IPV6_ROUTER_ADVERTISMENT = 0x8B, + MLXSW_TRAP_ID_L3_IPV6_NEIGHBOR_SOLICITATION = 0x8C, + MLXSW_TRAP_ID_L3_IPV6_NEIGHBOR_ADVERTISMENT = 0x8D, + MLXSW_TRAP_ID_L3_IPV6_REDIRECTION = 0x8E, MLXSW_TRAP_ID_HOST_MISS_IPV4 = 0x90, + MLXSW_TRAP_ID_IPV6_MC_LINK_LOCAL_DEST = 0x91, + MLXSW_TRAP_ID_HOST_MISS_IPV6 = 0x92, + MLXSW_TRAP_ID_IPIP_DECAP_ERROR = 0xB1, + MLXSW_TRAP_ID_ROUTER_ALERT_IPV4 = 0xD6, + MLXSW_TRAP_ID_ROUTER_ALERT_IPV6 = 0xD7, MLXSW_TRAP_ID_ACL0 = 0x1C0, MLXSW_TRAP_ID_MAX = 0x1FF diff --git a/drivers/net/ethernet/moxa/moxart_ether.c b/drivers/net/ethernet/moxa/moxart_ether.c index c0d7d5eec7e7..2e4effa9fe45 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.c +++ b/drivers/net/ethernet/moxa/moxart_ether.c @@ -161,7 +161,7 @@ static void moxart_mac_setup_desc_ring(struct net_device *ndev) priv->rx_head = 0; - /* reset the MAC controller TX/RX desciptor base address */ + /* reset the MAC controller TX/RX descriptor base address */ writel(priv->tx_base, priv->base + REG_TXR_BASE_ADDRESS); writel(priv->rx_base, priv->base + REG_RXR_BASE_ADDRESS); } @@ -269,9 +269,8 @@ rx_next: priv->rx_head = rx_head; } - if (rx < budget) { + if (rx < budget) napi_complete_done(napi, rx); - } priv->reg_imr |= RPKT_FINISH_M; writel(priv->reg_imr, priv->base + REG_INTERRUPT_MASK); @@ -289,8 +288,8 @@ static int moxart_tx_queue_space(struct net_device *ndev) static void moxart_tx_finished(struct net_device *ndev) { struct moxart_mac_priv_t *priv = netdev_priv(ndev); - unsigned tx_head = priv->tx_head; - unsigned tx_tail = priv->tx_tail; + unsigned int tx_head = priv->tx_head; + unsigned int tx_tail = priv->tx_tail; while (tx_tail != tx_head) { dma_unmap_single(&ndev->dev, priv->tx_mapping[tx_tail], @@ -312,7 +311,7 @@ static void moxart_tx_finished(struct net_device *ndev) static irqreturn_t moxart_mac_interrupt(int irq, void *dev_id) { - struct net_device *ndev = (struct net_device *) dev_id; + struct net_device *ndev = (struct net_device *)dev_id; struct moxart_mac_priv_t *priv = netdev_priv(ndev); unsigned int ists = readl(priv->base + REG_INTERRUPT_STATUS); @@ -495,7 +494,7 @@ static int moxart_mac_probe(struct platform_device *pdev) priv->tx_desc_base = dma_alloc_coherent(NULL, TX_REG_DESC_SIZE * TX_DESC_NUM, &priv->tx_base, GFP_DMA | GFP_KERNEL); - if (priv->tx_desc_base == NULL) { + if (!priv->tx_desc_base) { ret = -ENOMEM; goto init_fail; } @@ -503,7 +502,7 @@ static int moxart_mac_probe(struct platform_device *pdev) priv->rx_desc_base = dma_alloc_coherent(NULL, RX_REG_DESC_SIZE * RX_DESC_NUM, &priv->rx_base, GFP_DMA | GFP_KERNEL); - if (priv->rx_desc_base == NULL) { + if (!priv->rx_desc_base) { ret = -ENOMEM; goto init_fail; } diff --git a/drivers/net/ethernet/moxa/moxart_ether.h b/drivers/net/ethernet/moxa/moxart_ether.h index 686b8957d5cf..bee608b547d1 100644 --- a/drivers/net/ethernet/moxa/moxart_ether.h +++ b/drivers/net/ethernet/moxa/moxart_ether.h @@ -55,17 +55,17 @@ #define RX_DESC2_ADDRESS_VIRT 4 #define TX_DESC_NUM 64 -#define TX_DESC_NUM_MASK (TX_DESC_NUM-1) +#define TX_DESC_NUM_MASK (TX_DESC_NUM - 1) #define TX_NEXT(N) (((N) + 1) & (TX_DESC_NUM_MASK)) #define TX_BUF_SIZE 1600 -#define TX_BUF_SIZE_MAX (TX_DESC1_BUF_SIZE_MASK+1) +#define TX_BUF_SIZE_MAX (TX_DESC1_BUF_SIZE_MASK + 1) #define TX_WAKE_THRESHOLD 16 #define RX_DESC_NUM 64 -#define RX_DESC_NUM_MASK (RX_DESC_NUM-1) +#define RX_DESC_NUM_MASK (RX_DESC_NUM - 1) #define RX_NEXT(N) (((N) + 1) & (RX_DESC_NUM_MASK)) #define RX_BUF_SIZE 1600 -#define RX_BUF_SIZE_MAX (RX_DESC1_BUF_SIZE_MASK+1) +#define RX_BUF_SIZE_MAX (RX_DESC1_BUF_SIZE_MASK + 1) #define REG_INTERRUPT_STATUS 0 #define REG_INTERRUPT_MASK 4 diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c index fd2ec36c6fa1..462eda926b1c 100644 --- a/drivers/net/ethernet/neterion/s2io.c +++ b/drivers/net/ethernet/neterion/s2io.c @@ -42,8 +42,6 @@ * aggregated as a single large packet * napi: This parameter used to enable/disable NAPI (polling Rx) * Possible values '1' for enable and '0' for disable. Default is '1' - * ufo: This parameter used to enable/disable UDP Fragmentation Offload(UFO) - * Possible values '1' for enable and '0' for disable. Default is '0' * vlan_tag_strip: This can be used to enable or disable vlan stripping. * Possible values '1' for enable , '0' for disable. * Default is '2' - which means disable in promisc mode @@ -453,7 +451,6 @@ S2IO_PARM_INT(lro_max_pkts, 0xFFFF); S2IO_PARM_INT(indicate_max_pkts, 0); S2IO_PARM_INT(napi, 1); -S2IO_PARM_INT(ufo, 0); S2IO_PARM_INT(vlan_tag_strip, NO_STRIP_IN_PROMISC); static unsigned int tx_fifo_len[MAX_TX_FIFOS] = @@ -4128,32 +4125,6 @@ static netdev_tx_t s2io_xmit(struct sk_buff *skb, struct net_device *dev) } frg_len = skb_headlen(skb); - if (offload_type == SKB_GSO_UDP) { - int ufo_size; - - ufo_size = s2io_udp_mss(skb); - ufo_size &= ~7; - txdp->Control_1 |= TXD_UFO_EN; - txdp->Control_1 |= TXD_UFO_MSS(ufo_size); - txdp->Control_1 |= TXD_BUFFER0_SIZE(8); -#ifdef __BIG_ENDIAN - /* both variants do cpu_to_be64(be32_to_cpu(...)) */ - fifo->ufo_in_band_v[put_off] = - (__force u64)skb_shinfo(skb)->ip6_frag_id; -#else - fifo->ufo_in_band_v[put_off] = - (__force u64)skb_shinfo(skb)->ip6_frag_id << 32; -#endif - txdp->Host_Control = (unsigned long)fifo->ufo_in_band_v; - txdp->Buffer_Pointer = pci_map_single(sp->pdev, - fifo->ufo_in_band_v, - sizeof(u64), - PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(sp->pdev, txdp->Buffer_Pointer)) - goto pci_map_failed; - txdp++; - } - txdp->Buffer_Pointer = pci_map_single(sp->pdev, skb->data, frg_len, PCI_DMA_TODEVICE); if (pci_dma_mapping_error(sp->pdev, txdp->Buffer_Pointer)) @@ -4161,8 +4132,6 @@ static netdev_tx_t s2io_xmit(struct sk_buff *skb, struct net_device *dev) txdp->Host_Control = (unsigned long)skb; txdp->Control_1 |= TXD_BUFFER0_SIZE(frg_len); - if (offload_type == SKB_GSO_UDP) - txdp->Control_1 |= TXD_UFO_EN; frg_cnt = skb_shinfo(skb)->nr_frags; /* For fragmented SKB. */ @@ -4177,14 +4146,9 @@ static netdev_tx_t s2io_xmit(struct sk_buff *skb, struct net_device *dev) skb_frag_size(frag), DMA_TO_DEVICE); txdp->Control_1 = TXD_BUFFER0_SIZE(skb_frag_size(frag)); - if (offload_type == SKB_GSO_UDP) - txdp->Control_1 |= TXD_UFO_EN; } txdp->Control_1 |= TXD_GATHER_CODE_LAST; - if (offload_type == SKB_GSO_UDP) - frg_cnt++; /* as Txd0 was used for inband header */ - tx_fifo = mac_control->tx_FIFO_start[queue]; val64 = fifo->list_info[put_off].list_phy_addr; writeq(val64, &tx_fifo->TxDL_Pointer); @@ -7910,11 +7874,6 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) NETIF_F_RXCSUM | NETIF_F_LRO; dev->features |= dev->hw_features | NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX; - if (sp->device_type & XFRAME_II_DEVICE) { - dev->hw_features |= NETIF_F_UFO; - if (ufo) - dev->features |= NETIF_F_UFO; - } if (sp->high_dma_flag == true) dev->features |= NETIF_F_HIGHDMA; dev->watchdog_timeo = WATCH_DOG_TIMEOUT; @@ -8147,10 +8106,6 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) DBG_PRINT(ERR_DBG, "%s: Large receive offload enabled\n", dev->name); - if (ufo) - DBG_PRINT(ERR_DBG, - "%s: UDP Fragmentation Offload(UFO) enabled\n", - dev->name); /* Initialize device name */ snprintf(sp->name, sizeof(sp->name), "%s Neterion %s", dev->name, sp->product_name); diff --git a/drivers/net/ethernet/netronome/Kconfig b/drivers/net/ethernet/netronome/Kconfig index 0e331e2f685a..ae0c46ba7546 100644 --- a/drivers/net/ethernet/netronome/Kconfig +++ b/drivers/net/ethernet/netronome/Kconfig @@ -29,6 +29,7 @@ config NFP_APP_FLOWER bool "NFP4000/NFP6000 TC Flower offload support" depends on NFP depends on NET_SWITCHDEV + default y ---help--- Enable driver support for TC Flower offload on NFP4000 and NFP6000. Say Y, if you are planning to make use of TC Flower offload diff --git a/drivers/net/ethernet/netronome/nfp/Makefile b/drivers/net/ethernet/netronome/nfp/Makefile index b8e1358868bd..96e579a15cbe 100644 --- a/drivers/net/ethernet/netronome/nfp/Makefile +++ b/drivers/net/ethernet/netronome/nfp/Makefile @@ -23,6 +23,7 @@ nfp-objs := \ nfp_net_ethtool.o \ nfp_net_main.o \ nfp_net_repr.o \ + nfp_net_sriov.o \ nfp_netvf_main.o \ nfp_port.o \ bpf/main.o \ diff --git a/drivers/net/ethernet/netronome/nfp/bpf/jit.c b/drivers/net/ethernet/netronome/nfp/bpf/jit.c index 8e57fda6b8b5..239dfbe8a0a1 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/jit.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/jit.c @@ -1238,6 +1238,16 @@ static int jge_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) return wrp_cmp_imm(nfp_prog, meta, BR_BHS, true); } +static int jlt_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ + return wrp_cmp_imm(nfp_prog, meta, BR_BHS, false); +} + +static int jle_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ + return wrp_cmp_imm(nfp_prog, meta, BR_BLO, true); +} + static int jset_imm(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) { const struct bpf_insn *insn = &meta->insn; @@ -1325,6 +1335,16 @@ static int jge_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) return wrp_cmp_reg(nfp_prog, meta, BR_BHS, true); } +static int jlt_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ + return wrp_cmp_reg(nfp_prog, meta, BR_BHS, false); +} + +static int jle_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) +{ + return wrp_cmp_reg(nfp_prog, meta, BR_BLO, true); +} + static int jset_reg(struct nfp_prog *nfp_prog, struct nfp_insn_meta *meta) { return wrp_test_reg(nfp_prog, meta, ALU_OP_AND, BR_BNE); @@ -1383,11 +1403,15 @@ static const instr_cb_t instr_cb[256] = { [BPF_JMP | BPF_JEQ | BPF_K] = jeq_imm, [BPF_JMP | BPF_JGT | BPF_K] = jgt_imm, [BPF_JMP | BPF_JGE | BPF_K] = jge_imm, + [BPF_JMP | BPF_JLT | BPF_K] = jlt_imm, + [BPF_JMP | BPF_JLE | BPF_K] = jle_imm, [BPF_JMP | BPF_JSET | BPF_K] = jset_imm, [BPF_JMP | BPF_JNE | BPF_K] = jne_imm, [BPF_JMP | BPF_JEQ | BPF_X] = jeq_reg, [BPF_JMP | BPF_JGT | BPF_X] = jgt_reg, [BPF_JMP | BPF_JGE | BPF_X] = jge_reg, + [BPF_JMP | BPF_JLT | BPF_X] = jlt_reg, + [BPF_JMP | BPF_JLE | BPF_X] = jle_reg, [BPF_JMP | BPF_JSET | BPF_X] = jset_reg, [BPF_JMP | BPF_JNE | BPF_X] = jne_reg, [BPF_JMP | BPF_EXIT] = goto_out, diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index afbdf5fd4e4f..be2cf10a2cd7 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -84,7 +84,7 @@ static const char *nfp_bpf_extra_cap(struct nfp_app *app, struct nfp_net *nn) } static int -nfp_bpf_vnic_init(struct nfp_app *app, struct nfp_net *nn, unsigned int id) +nfp_bpf_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id) { struct nfp_net_bpf_priv *priv; int ret; @@ -106,14 +106,14 @@ nfp_bpf_vnic_init(struct nfp_app *app, struct nfp_net *nn, unsigned int id) setup_timer(&priv->rx_filter_stats_timer, nfp_net_filter_stats_timer, (unsigned long)nn); - ret = nfp_app_nic_vnic_init(app, nn, id); + ret = nfp_app_nic_vnic_alloc(app, nn, id); if (ret) kfree(priv); return ret; } -static void nfp_bpf_vnic_clean(struct nfp_app *app, struct nfp_net *nn) +static void nfp_bpf_vnic_free(struct nfp_app *app, struct nfp_net *nn) { if (nn->dp.bpf_offload_xdp) nfp_bpf_xdp_offload(app, nn, NULL); @@ -121,23 +121,21 @@ static void nfp_bpf_vnic_clean(struct nfp_app *app, struct nfp_net *nn) } static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev, - u32 handle, __be16 proto, struct tc_to_netdev *tc) + enum tc_setup_type type, void *type_data) { + struct tc_cls_bpf_offload *cls_bpf = type_data; struct nfp_net *nn = netdev_priv(netdev); - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) - return -EOPNOTSUPP; - if (proto != htons(ETH_P_ALL)) + if (type != TC_SETUP_CLSBPF || !nfp_net_ebpf_capable(nn) || + !is_classid_clsact_ingress(cls_bpf->common.classid) || + cls_bpf->common.protocol != htons(ETH_P_ALL) || + cls_bpf->common.chain_index) return -EOPNOTSUPP; - if (tc->type == TC_SETUP_CLSBPF && nfp_net_ebpf_capable(nn)) { - if (!nn->dp.bpf_offload_xdp) - return nfp_net_bpf_offload(nn, tc->cls_bpf); - else - return -EBUSY; - } + if (nn->dp.bpf_offload_xdp) + return -EBUSY; - return -EINVAL; + return nfp_net_bpf_offload(nn, cls_bpf); } static bool nfp_bpf_tc_busy(struct nfp_app *app, struct nfp_net *nn) @@ -151,8 +149,8 @@ const struct nfp_app_type app_bpf = { .extra_cap = nfp_bpf_extra_cap, - .vnic_init = nfp_bpf_vnic_init, - .vnic_clean = nfp_bpf_vnic_clean, + .vnic_alloc = nfp_bpf_vnic_alloc, + .vnic_free = nfp_bpf_vnic_free, .setup_tc = nfp_bpf_setup_tc, .tc_busy = nfp_bpf_tc_busy, diff --git a/drivers/net/ethernet/netronome/nfp/bpf/offload.c b/drivers/net/ethernet/netronome/nfp/bpf/offload.c index 78d80a364edb..a88bb5bc0082 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/offload.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/offload.c @@ -115,14 +115,14 @@ nfp_net_bpf_get_act(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf) /* TC direct action */ if (cls_bpf->exts_integrated) { - if (tc_no_actions(cls_bpf->exts)) + if (!tcf_exts_has_actions(cls_bpf->exts)) return NN_ACT_DIRECT; return -EOPNOTSUPP; } /* TC legacy mode */ - if (!tc_single_action(cls_bpf->exts)) + if (!tcf_exts_has_one_action(cls_bpf->exts)) return -EOPNOTSUPP; tcf_exts_to_list(cls_bpf->exts, &actions); diff --git a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c index d696ba46f70a..5b783a91b115 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/verifier.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/verifier.c @@ -79,28 +79,32 @@ nfp_bpf_check_exit(struct nfp_prog *nfp_prog, const struct bpf_verifier_env *env) { const struct bpf_reg_state *reg0 = &env->cur_state.regs[0]; + u64 imm; if (nfp_prog->act == NN_ACT_XDP) return 0; - if (reg0->type != CONST_IMM) { - pr_info("unsupported exit state: %d, imm: %llx\n", - reg0->type, reg0->imm); + if (!(reg0->type == SCALAR_VALUE && tnum_is_const(reg0->var_off))) { + char tn_buf[48]; + + tnum_strn(tn_buf, sizeof(tn_buf), reg0->var_off); + pr_info("unsupported exit state: %d, var_off: %s\n", + reg0->type, tn_buf); return -EINVAL; } - if (nfp_prog->act != NN_ACT_DIRECT && - reg0->imm != 0 && (reg0->imm & ~0U) != ~0U) { + imm = reg0->var_off.value; + if (nfp_prog->act != NN_ACT_DIRECT && imm != 0 && (imm & ~0U) != ~0U) { pr_info("unsupported exit state: %d, imm: %llx\n", - reg0->type, reg0->imm); + reg0->type, imm); return -EINVAL; } - if (nfp_prog->act == NN_ACT_DIRECT && reg0->imm <= TC_ACT_REDIRECT && - reg0->imm != TC_ACT_SHOT && reg0->imm != TC_ACT_STOLEN && - reg0->imm != TC_ACT_QUEUED) { + if (nfp_prog->act == NN_ACT_DIRECT && imm <= TC_ACT_REDIRECT && + imm != TC_ACT_SHOT && imm != TC_ACT_STOLEN && + imm != TC_ACT_QUEUED) { pr_info("unsupported exit state: %d, imm: %llx\n", - reg0->type, reg0->imm); + reg0->type, imm); return -EINVAL; } diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c index b0837b58c3a1..c3ca05d10fe1 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.c +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.c @@ -34,10 +34,12 @@ #include <linux/bitfield.h> #include <linux/netdevice.h> #include <linux/skbuff.h> +#include <linux/workqueue.h> #include <net/dst_metadata.h> #include "main.h" #include "../nfpcore/nfp_cpp.h" +#include "../nfp_net.h" #include "../nfp_net_repr.h" #include "./cmsg.h" @@ -75,6 +77,39 @@ nfp_flower_cmsg_alloc(struct nfp_app *app, unsigned int size, return skb; } +struct sk_buff * +nfp_flower_cmsg_mac_repr_start(struct nfp_app *app, unsigned int num_ports) +{ + struct nfp_flower_cmsg_mac_repr *msg; + struct sk_buff *skb; + unsigned int size; + + size = sizeof(*msg) + num_ports * sizeof(msg->ports[0]); + skb = nfp_flower_cmsg_alloc(app, size, NFP_FLOWER_CMSG_TYPE_MAC_REPR); + if (!skb) + return NULL; + + msg = nfp_flower_cmsg_get_data(skb); + memset(msg->reserved, 0, sizeof(msg->reserved)); + msg->num_ports = num_ports; + + return skb; +} + +void +nfp_flower_cmsg_mac_repr_add(struct sk_buff *skb, unsigned int idx, + unsigned int nbi, unsigned int nbi_port, + unsigned int phys_port) +{ + struct nfp_flower_cmsg_mac_repr *msg; + + msg = nfp_flower_cmsg_get_data(skb); + msg->ports[idx].idx = idx; + msg->ports[idx].info = nbi & NFP_FLOWER_CMSG_MAC_REPR_NBI; + msg->ports[idx].nbi_port = nbi_port; + msg->ports[idx].phys_port = phys_port; +} + int nfp_flower_cmsg_portmod(struct nfp_repr *repr, bool carrier_ok) { struct nfp_flower_cmsg_portmod *msg; @@ -106,23 +141,33 @@ nfp_flower_cmsg_portmod_rx(struct nfp_app *app, struct sk_buff *skb) msg = nfp_flower_cmsg_get_data(skb); link = msg->info & NFP_FLOWER_CMSG_PORTMOD_INFO_LINK; + rtnl_lock(); rcu_read_lock(); netdev = nfp_app_repr_get(app, be32_to_cpu(msg->portnum)); + rcu_read_unlock(); if (!netdev) { nfp_flower_cmsg_warn(app, "ctrl msg for unknown port 0x%08x\n", be32_to_cpu(msg->portnum)); - rcu_read_unlock(); + rtnl_unlock(); return; } - if (link) + if (link) { + u16 mtu = be16_to_cpu(msg->mtu); + netif_carrier_on(netdev); - else + + /* An MTU of 0 from the firmware should be ignored */ + if (mtu) + dev_set_mtu(netdev, mtu); + } else { netif_carrier_off(netdev); - rcu_read_unlock(); + } + rtnl_unlock(); } -void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb) +static void +nfp_flower_cmsg_process_one_rx(struct nfp_app *app, struct sk_buff *skb) { struct nfp_flower_cmsg_hdr *cmsg_hdr; enum nfp_flower_cmsg_type_port type; @@ -146,8 +191,30 @@ void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb) default: nfp_flower_cmsg_warn(app, "Cannot handle invalid repr control type %u\n", type); + goto out; } + dev_consume_skb_any(skb); + return; out: dev_kfree_skb_any(skb); } + +void nfp_flower_cmsg_process_rx(struct work_struct *work) +{ + struct nfp_flower_priv *priv; + struct sk_buff *skb; + + priv = container_of(work, struct nfp_flower_priv, cmsg_work); + + while ((skb = skb_dequeue(&priv->cmsg_skbs))) + nfp_flower_cmsg_process_one_rx(priv->app, skb); +} + +void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb) +{ + struct nfp_flower_priv *priv = app->priv; + + skb_queue_tail(&priv->cmsg_skbs, skb); + schedule_work(&priv->cmsg_work); +} diff --git a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h index cf738de170ab..a2ec60344236 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/cmsg.h +++ b/drivers/net/ethernet/netronome/nfp/flower/cmsg.h @@ -247,12 +247,27 @@ struct nfp_flower_cmsg_hdr { enum nfp_flower_cmsg_type_port { NFP_FLOWER_CMSG_TYPE_FLOW_ADD = 0, NFP_FLOWER_CMSG_TYPE_FLOW_DEL = 2, + NFP_FLOWER_CMSG_TYPE_MAC_REPR = 7, NFP_FLOWER_CMSG_TYPE_PORT_MOD = 8, NFP_FLOWER_CMSG_TYPE_FLOW_STATS = 15, NFP_FLOWER_CMSG_TYPE_PORT_ECHO = 16, NFP_FLOWER_CMSG_TYPE_MAX = 32, }; +/* NFP_FLOWER_CMSG_TYPE_MAC_REPR */ +struct nfp_flower_cmsg_mac_repr { + u8 reserved[3]; + u8 num_ports; + struct { + u8 idx; + u8 info; + u8 nbi_port; + u8 phys_port; + } ports[0]; +}; + +#define NFP_FLOWER_CMSG_MAC_REPR_NBI GENMASK(1, 0) + /* NFP_FLOWER_CMSG_TYPE_PORT_MOD */ struct nfp_flower_cmsg_portmod { __be32 portnum; @@ -308,7 +323,14 @@ static inline void *nfp_flower_cmsg_get_data(struct sk_buff *skb) return (unsigned char *)skb->data + NFP_FLOWER_CMSG_HLEN; } +struct sk_buff * +nfp_flower_cmsg_mac_repr_start(struct nfp_app *app, unsigned int num_ports); +void +nfp_flower_cmsg_mac_repr_add(struct sk_buff *skb, unsigned int idx, + unsigned int nbi, unsigned int nbi_port, + unsigned int phys_port); int nfp_flower_cmsg_portmod(struct nfp_repr *repr, bool carrier_ok); +void nfp_flower_cmsg_process_rx(struct work_struct *work); void nfp_flower_cmsg_rx(struct nfp_app *app, struct sk_buff *skb); struct sk_buff * nfp_flower_cmsg_alloc(struct nfp_app *app, unsigned int size, diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.c b/drivers/net/ethernet/netronome/nfp/flower/main.c index 6a65c8b33807..91fe03617106 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.c +++ b/drivers/net/ethernet/netronome/nfp/flower/main.c @@ -127,6 +127,11 @@ nfp_flower_repr_netdev_stop(struct nfp_app *app, struct nfp_repr *repr) static void nfp_flower_sriov_disable(struct nfp_app *app) { + struct nfp_flower_priv *priv = app->priv; + + if (!priv->nn) + return; + nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_VF); } @@ -159,12 +164,18 @@ nfp_flower_spawn_vnic_reprs(struct nfp_app *app, goto err_reprs_clean; } + /* For now we only support 1 PF */ + WARN_ON(repr_type == NFP_REPR_TYPE_PF && i); + port = nfp_port_alloc(app, port_type, reprs->reprs[i]); if (repr_type == NFP_REPR_TYPE_PF) { port->pf_id = i; + port->vnic = priv->nn->dp.ctrl_bar; } else { - port->pf_id = 0; /* For now we only support 1 PF */ + port->pf_id = 0; port->vf_id = i; + port->vnic = + app->pf->vf_cfg_mem + i * NFP_NET_CFG_BAR_SZ; } eth_hw_addr_random(reprs->reprs[i]); @@ -197,32 +208,37 @@ err_reprs_clean: static int nfp_flower_sriov_enable(struct nfp_app *app, int num_vfs) { + struct nfp_flower_priv *priv = app->priv; + + if (!priv->nn) + return 0; + return nfp_flower_spawn_vnic_reprs(app, NFP_FLOWER_CMSG_PORT_VNIC_TYPE_VF, NFP_REPR_TYPE_VF, num_vfs); } -static void nfp_flower_stop(struct nfp_app *app) -{ - nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PF); - nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT); - -} - static int nfp_flower_spawn_phy_reprs(struct nfp_app *app, struct nfp_flower_priv *priv) { struct nfp_eth_table *eth_tbl = app->pf->eth_tbl; struct nfp_reprs *reprs, *old_reprs; + struct sk_buff *ctrl_skb; unsigned int i; int err; - reprs = nfp_reprs_alloc(eth_tbl->max_index + 1); - if (!reprs) + ctrl_skb = nfp_flower_cmsg_mac_repr_start(app, eth_tbl->count); + if (!ctrl_skb) return -ENOMEM; + reprs = nfp_reprs_alloc(eth_tbl->max_index + 1); + if (!reprs) { + err = -ENOMEM; + goto err_free_ctrl_skb; + } + for (i = 0; i < eth_tbl->count; i++) { - int phys_port = eth_tbl->ports[i].index; + unsigned int phys_port = eth_tbl->ports[i].index; struct nfp_port *port; u32 cmsg_port_id; @@ -255,6 +271,11 @@ nfp_flower_spawn_phy_reprs(struct nfp_app *app, struct nfp_flower_priv *priv) goto err_reprs_clean; } + nfp_flower_cmsg_mac_repr_add(ctrl_skb, i, + eth_tbl->ports[i].nbi, + eth_tbl->ports[i].base, + phys_port); + nfp_info(app->cpp, "Phys Port %d Representor(%s) created\n", phys_port, reprs->reprs[phys_port]->name); } @@ -265,37 +286,31 @@ nfp_flower_spawn_phy_reprs(struct nfp_app *app, struct nfp_flower_priv *priv) goto err_reprs_clean; } + /* The MAC_REPR control message should be sent after the MAC + * representors are registered using nfp_app_reprs_set(). This is + * because the firmware may respond with control messages for the + * MAC representors, f.e. to provide the driver with information + * about their state, and without registration the driver will drop + * any such messages. + */ + nfp_ctrl_tx(app->ctrl, ctrl_skb); + return 0; err_reprs_clean: nfp_reprs_clean_and_free(reprs); +err_free_ctrl_skb: + kfree_skb(ctrl_skb); return err; } -static int nfp_flower_start(struct nfp_app *app) -{ - int err; - - err = nfp_flower_spawn_phy_reprs(app, app->priv); - if (err) - return err; - - return nfp_flower_spawn_vnic_reprs(app, - NFP_FLOWER_CMSG_PORT_VNIC_TYPE_PF, - NFP_REPR_TYPE_PF, 1); -} - -static int nfp_flower_vnic_init(struct nfp_app *app, struct nfp_net *nn, - unsigned int id) +static int nfp_flower_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, + unsigned int id) { - struct nfp_flower_priv *priv = app->priv; - if (id > 0) { nfp_warn(app->cpp, "FlowerNIC doesn't support more than one data vNIC\n"); goto err_invalid_port; } - priv->nn = nn; - eth_hw_addr_random(nn->dp.netdev); netif_keep_dst(nn->dp.netdev); @@ -306,9 +321,59 @@ err_invalid_port: return PTR_ERR_OR_ZERO(nn->port); } +static void nfp_flower_vnic_clean(struct nfp_app *app, struct nfp_net *nn) +{ + struct nfp_flower_priv *priv = app->priv; + + if (app->pf->num_vfs) + nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_VF); + nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PF); + nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT); + + priv->nn = NULL; +} + +static int nfp_flower_vnic_init(struct nfp_app *app, struct nfp_net *nn) +{ + struct nfp_flower_priv *priv = app->priv; + int err; + + priv->nn = nn; + + err = nfp_flower_spawn_phy_reprs(app, app->priv); + if (err) + goto err_clear_nn; + + err = nfp_flower_spawn_vnic_reprs(app, + NFP_FLOWER_CMSG_PORT_VNIC_TYPE_PF, + NFP_REPR_TYPE_PF, 1); + if (err) + goto err_destroy_reprs_phy; + + if (app->pf->num_vfs) { + err = nfp_flower_spawn_vnic_reprs(app, + NFP_FLOWER_CMSG_PORT_VNIC_TYPE_VF, + NFP_REPR_TYPE_VF, + app->pf->num_vfs); + if (err) + goto err_destroy_reprs_pf; + } + + return 0; + +err_destroy_reprs_pf: + nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PF); +err_destroy_reprs_phy: + nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT); +err_clear_nn: + priv->nn = NULL; + return err; +} + static int nfp_flower_init(struct nfp_app *app) { const struct nfp_pf *pf = app->pf; + struct nfp_flower_priv *app_priv; u64 version; int err; @@ -339,10 +404,15 @@ static int nfp_flower_init(struct nfp_app *app) return -EINVAL; } - app->priv = vzalloc(sizeof(struct nfp_flower_priv)); - if (!app->priv) + app_priv = vzalloc(sizeof(struct nfp_flower_priv)); + if (!app_priv) return -ENOMEM; + app->priv = app_priv; + app_priv->app = app; + skb_queue_head_init(&app_priv->cmsg_skbs); + INIT_WORK(&app_priv->cmsg_work, nfp_flower_cmsg_process_rx); + err = nfp_flower_metadata_init(app); if (err) goto err_free_app_priv; @@ -356,6 +426,11 @@ err_free_app_priv: static void nfp_flower_clean(struct nfp_app *app) { + struct nfp_flower_priv *app_priv = app->priv; + + skb_queue_purge(&app_priv->cmsg_skbs); + flush_work(&app_priv->cmsg_work); + nfp_flower_metadata_cleanup(app); vfree(app->priv); app->priv = NULL; @@ -371,14 +446,13 @@ const struct nfp_app_type app_flower = { .init = nfp_flower_init, .clean = nfp_flower_clean, + .vnic_alloc = nfp_flower_vnic_alloc, .vnic_init = nfp_flower_vnic_init, + .vnic_clean = nfp_flower_vnic_clean, .repr_open = nfp_flower_repr_netdev_open, .repr_stop = nfp_flower_repr_netdev_stop, - .start = nfp_flower_start, - .stop = nfp_flower_stop, - .ctrl_msg_rx = nfp_flower_cmsg_rx, .sriov_enable = nfp_flower_sriov_enable, diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index 9e64c048e83f..c20dd00a1cae 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -38,8 +38,9 @@ #include <linux/hashtable.h> #include <linux/time64.h> #include <linux/types.h> +#include <net/pkt_cls.h> +#include <linux/workqueue.h> -struct tc_to_netdev; struct net_device; struct nfp_app; @@ -71,6 +72,7 @@ struct nfp_fl_stats_id { /** * struct nfp_flower_priv - Flower APP per-vNIC priv data + * @app: Back pointer to app * @nn: Pointer to vNIC * @mask_id_seed: Seed used for mask hash table * @flower_version: HW version of flower @@ -78,8 +80,11 @@ struct nfp_fl_stats_id { * @mask_ids: List of free mask ids * @mask_table: Hash table used to store masks * @flow_table: Hash table used to store flower rules + * @cmsg_work: Workqueue for control messages processing + * @cmsg_skbs: List of skbs for control message processing */ struct nfp_flower_priv { + struct nfp_app *app; struct nfp_net *nn; u32 mask_id_seed; u64 flower_version; @@ -87,6 +92,8 @@ struct nfp_flower_priv { struct nfp_fl_mask_id mask_ids; DECLARE_HASHTABLE(mask_table, NFP_FLOWER_MASK_HASH_BITS); DECLARE_HASHTABLE(flow_table, NFP_FLOWER_HASH_BITS); + struct work_struct cmsg_work; + struct sk_buff_head cmsg_skbs; }; struct nfp_fl_key_ls { @@ -135,7 +142,7 @@ int nfp_flower_metadata_init(struct nfp_app *app); void nfp_flower_metadata_cleanup(struct nfp_app *app); int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, - u32 handle, __be16 proto, struct tc_to_netdev *tc); + enum tc_setup_type type, void *type_data); int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow, struct nfp_fl_key_ls *key_ls, struct net_device *netdev, diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 74a96d6bb05c..d396183108f7 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -409,16 +409,15 @@ nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev, } int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, - u32 handle, __be16 proto, struct tc_to_netdev *tc) + enum tc_setup_type type, void *type_data) { - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) - return -EOPNOTSUPP; + struct tc_cls_flower_offload *cls_flower = type_data; - if (!eth_proto_is_802_3(proto)) + if (type != TC_SETUP_CLSFLOWER || + !is_classid_clsact_ingress(cls_flower->common.classid) || + !eth_proto_is_802_3(cls_flower->common.protocol) || + cls_flower->common.chain_index) return -EOPNOTSUPP; - if (tc->type != TC_SETUP_CLSFLOWER) - return -EINVAL; - - return nfp_flower_repr_offload(app, netdev, tc->cls_flower); + return nfp_flower_repr_offload(app, netdev, cls_flower); } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.c b/drivers/net/ethernet/netronome/nfp/nfp_app.c index c704c022574f..82c290763529 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.c @@ -38,6 +38,7 @@ #include "nfpcore/nfp_nffw.h" #include "nfp_app.h" #include "nfp_main.h" +#include "nfp_net.h" #include "nfp_net_repr.h" static const struct nfp_app_type *apps[] = { @@ -48,6 +49,25 @@ static const struct nfp_app_type *apps[] = { #endif }; +struct nfp_app *nfp_app_from_netdev(struct net_device *netdev) +{ + if (nfp_netdev_is_nfp_net(netdev)) { + struct nfp_net *nn = netdev_priv(netdev); + + return nn->app; + } + + if (nfp_netdev_is_nfp_repr(netdev)) { + struct nfp_repr *repr = netdev_priv(netdev); + + return repr->app; + } + + WARN(1, "Unknown netdev type for nfp_app\n"); + + return NULL; +} + const char *nfp_app_mip_name(struct nfp_app *app) { if (!app || !app->pf->mip) @@ -105,7 +125,7 @@ struct nfp_app *nfp_app_alloc(struct nfp_pf *pf, enum nfp_app_id id) return ERR_PTR(-EINVAL); } - if (WARN_ON(!apps[i]->name || !apps[i]->vnic_init)) + if (WARN_ON(!apps[i]->name || !apps[i]->vnic_alloc)) return ERR_PTR(-EINVAL); app = kzalloc(sizeof(*app), GFP_KERNEL); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h index 5d714e10d9a9..af640b5c2108 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h @@ -42,7 +42,6 @@ struct bpf_prog; struct net_device; struct pci_dev; struct sk_buff; -struct tc_to_netdev; struct sk_buff; struct nfp_app; struct nfp_cpp; @@ -70,8 +69,10 @@ extern const struct nfp_app_type app_flower; * @init: perform basic app checks and init * @clean: clean app state * @extra_cap: extra capabilities string - * @vnic_init: init vNICs (assign port types, etc.) - * @vnic_clean: clean up app's vNIC state + * @vnic_alloc: allocate vNICs (assign port types, etc.) + * @vnic_free: free up app's vNIC state + * @vnic_init: vNIC netdev was registered + * @vnic_clean: vNIC netdev about to be unregistered * @repr_open: representor netdev open callback * @repr_stop: representor netdev stop callback * @start: start application logic @@ -96,8 +97,10 @@ struct nfp_app_type { const char *(*extra_cap)(struct nfp_app *app, struct nfp_net *nn); - int (*vnic_init)(struct nfp_app *app, struct nfp_net *nn, - unsigned int id); + int (*vnic_alloc)(struct nfp_app *app, struct nfp_net *nn, + unsigned int id); + void (*vnic_free)(struct nfp_app *app, struct nfp_net *nn); + int (*vnic_init)(struct nfp_app *app, struct nfp_net *nn); void (*vnic_clean)(struct nfp_app *app, struct nfp_net *nn); int (*repr_open)(struct nfp_app *app, struct nfp_repr *repr); @@ -109,7 +112,7 @@ struct nfp_app_type { void (*ctrl_msg_rx)(struct nfp_app *app, struct sk_buff *skb); int (*setup_tc)(struct nfp_app *app, struct net_device *netdev, - u32 handle, __be16 proto, struct tc_to_netdev *tc); + enum tc_setup_type type, void *type_data); bool (*tc_busy)(struct nfp_app *app, struct nfp_net *nn); int (*xdp_offload)(struct nfp_app *app, struct nfp_net *nn, struct bpf_prog *prog); @@ -158,10 +161,23 @@ static inline void nfp_app_clean(struct nfp_app *app) app->type->clean(app); } -static inline int nfp_app_vnic_init(struct nfp_app *app, struct nfp_net *nn, - unsigned int id) +static inline int nfp_app_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, + unsigned int id) +{ + return app->type->vnic_alloc(app, nn, id); +} + +static inline void nfp_app_vnic_free(struct nfp_app *app, struct nfp_net *nn) +{ + if (app->type->vnic_free) + app->type->vnic_free(app, nn); +} + +static inline int nfp_app_vnic_init(struct nfp_app *app, struct nfp_net *nn) { - return app->type->vnic_init(app, nn, id); + if (!app->type->vnic_init) + return 0; + return app->type->vnic_init(app, nn); } static inline void nfp_app_vnic_clean(struct nfp_app *app, struct nfp_net *nn) @@ -238,12 +254,11 @@ static inline bool nfp_app_tc_busy(struct nfp_app *app, struct nfp_net *nn) static inline int nfp_app_setup_tc(struct nfp_app *app, struct net_device *netdev, - u32 handle, __be16 proto, - struct tc_to_netdev *tc) + enum tc_setup_type type, void *type_data) { if (!app || !app->type->setup_tc) return -EOPNOTSUPP; - return app->type->setup_tc(app, netdev, handle, proto, tc); + return app->type->setup_tc(app, netdev, type, type_data); } static inline int nfp_app_xdp_offload(struct nfp_app *app, struct nfp_net *nn, @@ -295,6 +310,8 @@ static inline struct net_device *nfp_app_repr_get(struct nfp_app *app, u32 id) return app->type->repr_get(app, id); } +struct nfp_app *nfp_app_from_netdev(struct net_device *netdev); + struct nfp_reprs * nfp_app_reprs_set(struct nfp_app *app, enum nfp_repr_type type, struct nfp_reprs *reprs); @@ -308,7 +325,7 @@ void nfp_app_free(struct nfp_app *app); /* Callbacks shared between apps */ -int nfp_app_nic_vnic_init(struct nfp_app *app, struct nfp_net *nn, - unsigned int id); +int nfp_app_nic_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, + unsigned int id); #endif diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app_nic.c b/drivers/net/ethernet/netronome/nfp/nfp_app_nic.c index 4e37c81f9eaf..2a2f2fbc8850 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app_nic.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_app_nic.c @@ -60,8 +60,8 @@ nfp_app_nic_vnic_init_phy_port(struct nfp_pf *pf, struct nfp_app *app, return nn->port->type == NFP_PORT_INVALID; } -int nfp_app_nic_vnic_init(struct nfp_app *app, struct nfp_net *nn, - unsigned int id) +int nfp_app_nic_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, + unsigned int id) { int err; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index 3f199db2002e..f055b1774d65 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -172,6 +172,21 @@ static int nfp_pcie_sriov_configure(struct pci_dev *pdev, int num_vfs) return nfp_pcie_sriov_enable(pdev, num_vfs); } +static const struct firmware * +nfp_net_fw_request(struct pci_dev *pdev, struct nfp_pf *pf, const char *name) +{ + const struct firmware *fw = NULL; + int err; + + err = request_firmware_direct(&fw, name, &pdev->dev); + nfp_info(pf->cpp, " %s: %s\n", + name, err ? "not found" : "found, loading..."); + if (err) + return NULL; + + return fw; +} + /** * nfp_net_fw_find() - Find the correct firmware image for netdev mode * @pdev: PCI Device structure @@ -182,13 +197,32 @@ static int nfp_pcie_sriov_configure(struct pci_dev *pdev, int num_vfs) static const struct firmware * nfp_net_fw_find(struct pci_dev *pdev, struct nfp_pf *pf) { - const struct firmware *fw = NULL; struct nfp_eth_table_port *port; + const struct firmware *fw; const char *fw_model; char fw_name[256]; - int spc, err = 0; - int i, j; + const u8 *serial; + u16 interface; + int spc, i, j; + nfp_info(pf->cpp, "Looking for firmware file in order of priority:\n"); + + /* First try to find a firmware image specific for this device */ + interface = nfp_cpp_interface(pf->cpp); + nfp_cpp_serial(pf->cpp, &serial); + sprintf(fw_name, "netronome/serial-%pMF-%02hhx-%02hhx.nffw", + serial, interface >> 8, interface & 0xff); + fw = nfp_net_fw_request(pdev, pf, fw_name); + if (fw) + return fw; + + /* Then try the PCI name */ + sprintf(fw_name, "netronome/pci-%s.nffw", pci_name(pdev)); + fw = nfp_net_fw_request(pdev, pf, fw_name); + if (fw) + return fw; + + /* Finally try the card type and media */ if (!pf->eth_tbl) { dev_err(&pdev->dev, "Error: can't identify media config\n"); return NULL; @@ -221,13 +255,7 @@ nfp_net_fw_find(struct pci_dev *pdev, struct nfp_pf *pf) if (spc <= 0) return NULL; - err = request_firmware(&fw, fw_name, &pdev->dev); - if (err) - return NULL; - - dev_info(&pdev->dev, "Loading FW image: %s\n", fw_name); - - return fw; + return nfp_net_fw_request(pdev, pf, fw_name); } /** diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index 6922410806db..be0ee59f2eb9 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -73,6 +73,8 @@ struct nfp_rtsym_table; * @mac_stats_mem: Pointer to mapped MAC stats area * @vf_cfg_bar: Pointer to the CPP area for the VF configuration BAR * @vf_cfg_mem: Pointer to mapped VF configuration area + * @vfcfg_tbl2_area: Pointer to the CPP area for the VF config table + * @vfcfg_tbl2: Pointer to mapped VF config table * @irq_entries: Array of MSI-X entries for all vNICs * @limit_vfs: Number of VFs supported by firmware (~0 for PCI limit) * @num_vfs: Number of SR-IOV VFs enabled @@ -107,6 +109,8 @@ struct nfp_pf { u8 __iomem *mac_stats_mem; struct nfp_cpp_area *vf_cfg_bar; u8 __iomem *vf_cfg_mem; + struct nfp_cpp_area *vfcfg_tbl2_area; + u8 __iomem *vfcfg_tbl2; struct msix_entry *irq_entries; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index b1fa77bd708b..d51d8237b984 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -573,7 +573,6 @@ struct nfp_net_dp { * @tx_bar: Pointer to mapped TX queues * @rx_bar: Pointer to mapped FL/RX queues * @debugfs_dir: Device directory in debugfs - * @ethtool_dump_flag: Ethtool dump flag * @vnic_list: Entry on device vNIC list * @pdev: Backpointer to PCI device * @app: APP handle if available @@ -640,7 +639,6 @@ struct nfp_net { u8 __iomem *rx_bar; struct dentry *debugfs_dir; - u32 ethtool_dump_flag; struct list_head vnic_list; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 66a09e490cf5..1c0187f0af51 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -71,6 +71,7 @@ #include "nfp_app.h" #include "nfp_net_ctrl.h" #include "nfp_net.h" +#include "nfp_net_sriov.h" #include "nfp_port.h" /** @@ -990,7 +991,7 @@ static void nfp_net_tx_complete(struct nfp_net_tx_ring *tx_ring) /* check for last gather fragment */ if (fidx == nr_frags - 1) - dev_kfree_skb_any(skb); + dev_consume_skb_any(skb); tx_ring->txbufs[idx].dma_addr = 0; tx_ring->txbufs[idx].skb = NULL; @@ -2659,6 +2660,7 @@ static int nfp_net_netdev_close(struct net_device *netdev) /* Step 2: Tell NFP */ nfp_net_clear_config_and_disable(nn); + nfp_port_configure(netdev, false); /* Step 3: Free resources */ @@ -2776,16 +2778,21 @@ static int nfp_net_netdev_open(struct net_device *netdev) goto err_free_all; /* Step 2: Configure the NFP + * - Ifup the physical interface if it exists * - Enable rings from 0 to tx_rings/rx_rings - 1. * - Write MAC address (in case it changed) * - Set the MTU * - Set the Freelist buffer size * - Enable the FW */ - err = nfp_net_set_config_and_enable(nn); + err = nfp_port_configure(netdev, true); if (err) goto err_free_all; + err = nfp_net_set_config_and_enable(nn); + if (err) + goto err_port_disable; + /* Step 3: Enable for kernel * - put some freelist descriptors on each RX ring * - enable NAPI on each ring @@ -2796,6 +2803,8 @@ static int nfp_net_netdev_open(struct net_device *netdev) return 0; +err_port_disable: + nfp_port_configure(netdev, false); err_free_all: nfp_net_close_free_all(nn); return err; @@ -3413,6 +3422,11 @@ const struct net_device_ops nfp_net_netdev_ops = { .ndo_get_stats64 = nfp_net_stat64, .ndo_vlan_rx_add_vid = nfp_net_vlan_rx_add_vid, .ndo_vlan_rx_kill_vid = nfp_net_vlan_rx_kill_vid, + .ndo_set_vf_mac = nfp_app_set_vf_mac, + .ndo_set_vf_vlan = nfp_app_set_vf_vlan, + .ndo_set_vf_spoofchk = nfp_app_set_vf_spoofchk, + .ndo_get_vf_config = nfp_app_get_vf_config, + .ndo_set_vf_link_state = nfp_app_set_vf_link_state, .ndo_setup_tc = nfp_port_setup_tc, .ndo_tx_timeout = nfp_net_tx_timeout, .ndo_set_rx_mode = nfp_net_set_rx_mode, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index e5e94e0746ec..b0a452ba9039 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -164,6 +164,7 @@ #define NFP_NET_CFG_UPDATE_BPF (0x1 << 10) /* BPF program load */ #define NFP_NET_CFG_UPDATE_MACADDR (0x1 << 11) /* MAC address change */ #define NFP_NET_CFG_UPDATE_MBOX (0x1 << 12) /* Mailbox update */ +#define NFP_NET_CFG_UPDATE_VF (0x1 << 13) /* VF settings change */ #define NFP_NET_CFG_UPDATE_ERR (0x1 << 31) /* A error occurred */ #define NFP_NET_CFG_TXRS_ENABLE 0x0008 #define NFP_NET_CFG_RXRS_ENABLE 0x0010 diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c index 40217ece5fcb..cf81cf95d1d8 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_debugfs.c @@ -125,7 +125,6 @@ static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data) struct nfp_net_tx_ring *tx_ring; struct nfp_net_tx_desc *txd; int d_rd_p, d_wr_p, txd_cnt; - struct sk_buff *skb; struct nfp_net *nn; int i; @@ -158,13 +157,15 @@ static int nfp_net_debugfs_tx_q_read(struct seq_file *file, void *data) txd->vals[0], txd->vals[1], txd->vals[2], txd->vals[3]); - skb = READ_ONCE(tx_ring->txbufs[i].skb); - if (skb) { - if (tx_ring == r_vec->tx_ring) + if (tx_ring == r_vec->tx_ring) { + struct sk_buff *skb = READ_ONCE(tx_ring->txbufs[i].skb); + + if (skb) seq_printf(file, " skb->head=%p skb->data=%p", skb->head, skb->data); - else - seq_printf(file, " frag=%p", skb); + } else { + seq_printf(file, " frag=%p", + READ_ONCE(tx_ring->txbufs[i].frag)); } if (tx_ring->txbufs[i].dma_addr) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 6e31355c3567..07969f06df10 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -59,82 +59,129 @@ enum nfp_dump_diag { NFP_DUMP_NSP_DIAG = 0, }; -/* Support for stats. Returns netdev, driver, and device stats */ -enum { NETDEV_ET_STATS, NFP_NET_DRV_ET_STATS, NFP_NET_DEV_ET_STATS }; -struct _nfp_net_et_stats { +struct nfp_et_stat { char name[ETH_GSTRING_LEN]; - int type; - int sz; int off; }; -#define NN_ET_NETDEV_STAT(m) NETDEV_ET_STATS, \ - FIELD_SIZEOF(struct net_device_stats, m), \ - offsetof(struct net_device_stats, m) -/* For stats in the control BAR (other than Q stats) */ -#define NN_ET_DEV_STAT(m) NFP_NET_DEV_ET_STATS, \ - sizeof(u64), \ - (m) -static const struct _nfp_net_et_stats nfp_net_et_stats[] = { - /* netdev stats */ - {"rx_packets", NN_ET_NETDEV_STAT(rx_packets)}, - {"tx_packets", NN_ET_NETDEV_STAT(tx_packets)}, - {"rx_bytes", NN_ET_NETDEV_STAT(rx_bytes)}, - {"tx_bytes", NN_ET_NETDEV_STAT(tx_bytes)}, - {"rx_errors", NN_ET_NETDEV_STAT(rx_errors)}, - {"tx_errors", NN_ET_NETDEV_STAT(tx_errors)}, - {"rx_dropped", NN_ET_NETDEV_STAT(rx_dropped)}, - {"tx_dropped", NN_ET_NETDEV_STAT(tx_dropped)}, - {"multicast", NN_ET_NETDEV_STAT(multicast)}, - {"collisions", NN_ET_NETDEV_STAT(collisions)}, - {"rx_over_errors", NN_ET_NETDEV_STAT(rx_over_errors)}, - {"rx_crc_errors", NN_ET_NETDEV_STAT(rx_crc_errors)}, - {"rx_frame_errors", NN_ET_NETDEV_STAT(rx_frame_errors)}, - {"rx_fifo_errors", NN_ET_NETDEV_STAT(rx_fifo_errors)}, - {"rx_missed_errors", NN_ET_NETDEV_STAT(rx_missed_errors)}, - {"tx_aborted_errors", NN_ET_NETDEV_STAT(tx_aborted_errors)}, - {"tx_carrier_errors", NN_ET_NETDEV_STAT(tx_carrier_errors)}, - {"tx_fifo_errors", NN_ET_NETDEV_STAT(tx_fifo_errors)}, +static const struct nfp_et_stat nfp_net_et_stats[] = { /* Stats from the device */ - {"dev_rx_discards", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_DISCARDS)}, - {"dev_rx_errors", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_ERRORS)}, - {"dev_rx_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_OCTETS)}, - {"dev_rx_uc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_UC_OCTETS)}, - {"dev_rx_mc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_MC_OCTETS)}, - {"dev_rx_bc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_BC_OCTETS)}, - {"dev_rx_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_FRAMES)}, - {"dev_rx_mc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_MC_FRAMES)}, - {"dev_rx_bc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_RX_BC_FRAMES)}, - - {"dev_tx_discards", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_DISCARDS)}, - {"dev_tx_errors", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_ERRORS)}, - {"dev_tx_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_OCTETS)}, - {"dev_tx_uc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_UC_OCTETS)}, - {"dev_tx_mc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_MC_OCTETS)}, - {"dev_tx_bc_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_BC_OCTETS)}, - {"dev_tx_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_FRAMES)}, - {"dev_tx_mc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_MC_FRAMES)}, - {"dev_tx_bc_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_TX_BC_FRAMES)}, - - {"bpf_pass_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP0_FRAMES)}, - {"bpf_pass_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP0_BYTES)}, + { "dev_rx_discards", NFP_NET_CFG_STATS_RX_DISCARDS }, + { "dev_rx_errors", NFP_NET_CFG_STATS_RX_ERRORS }, + { "dev_rx_bytes", NFP_NET_CFG_STATS_RX_OCTETS }, + { "dev_rx_uc_bytes", NFP_NET_CFG_STATS_RX_UC_OCTETS }, + { "dev_rx_mc_bytes", NFP_NET_CFG_STATS_RX_MC_OCTETS }, + { "dev_rx_bc_bytes", NFP_NET_CFG_STATS_RX_BC_OCTETS }, + { "dev_rx_pkts", NFP_NET_CFG_STATS_RX_FRAMES }, + { "dev_rx_mc_pkts", NFP_NET_CFG_STATS_RX_MC_FRAMES }, + { "dev_rx_bc_pkts", NFP_NET_CFG_STATS_RX_BC_FRAMES }, + + { "dev_tx_discards", NFP_NET_CFG_STATS_TX_DISCARDS }, + { "dev_tx_errors", NFP_NET_CFG_STATS_TX_ERRORS }, + { "dev_tx_bytes", NFP_NET_CFG_STATS_TX_OCTETS }, + { "dev_tx_uc_bytes", NFP_NET_CFG_STATS_TX_UC_OCTETS }, + { "dev_tx_mc_bytes", NFP_NET_CFG_STATS_TX_MC_OCTETS }, + { "dev_tx_bc_bytes", NFP_NET_CFG_STATS_TX_BC_OCTETS }, + { "dev_tx_pkts", NFP_NET_CFG_STATS_TX_FRAMES }, + { "dev_tx_mc_pkts", NFP_NET_CFG_STATS_TX_MC_FRAMES }, + { "dev_tx_bc_pkts", NFP_NET_CFG_STATS_TX_BC_FRAMES }, + + { "bpf_pass_pkts", NFP_NET_CFG_STATS_APP0_FRAMES }, + { "bpf_pass_bytes", NFP_NET_CFG_STATS_APP0_BYTES }, /* see comments in outro functions in nfp_bpf_jit.c to find out * how different BPF modes use app-specific counters */ - {"bpf_app1_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP1_FRAMES)}, - {"bpf_app1_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP1_BYTES)}, - {"bpf_app2_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP2_FRAMES)}, - {"bpf_app2_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP2_BYTES)}, - {"bpf_app3_pkts", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP3_FRAMES)}, - {"bpf_app3_bytes", NN_ET_DEV_STAT(NFP_NET_CFG_STATS_APP3_BYTES)}, + { "bpf_app1_pkts", NFP_NET_CFG_STATS_APP1_FRAMES }, + { "bpf_app1_bytes", NFP_NET_CFG_STATS_APP1_BYTES }, + { "bpf_app2_pkts", NFP_NET_CFG_STATS_APP2_FRAMES }, + { "bpf_app2_bytes", NFP_NET_CFG_STATS_APP2_BYTES }, + { "bpf_app3_pkts", NFP_NET_CFG_STATS_APP3_FRAMES }, + { "bpf_app3_bytes", NFP_NET_CFG_STATS_APP3_BYTES }, +}; + +static const struct nfp_et_stat nfp_mac_et_stats[] = { + { "rx_octets", NFP_MAC_STATS_RX_IN_OCTETS, }, + { "rx_frame_too_long_errors", + NFP_MAC_STATS_RX_FRAME_TOO_LONG_ERRORS, }, + { "rx_range_length_errors", NFP_MAC_STATS_RX_RANGE_LENGTH_ERRORS, }, + { "rx_vlan_reveive_ok", NFP_MAC_STATS_RX_VLAN_REVEIVE_OK, }, + { "rx_errors", NFP_MAC_STATS_RX_IN_ERRORS, }, + { "rx_broadcast_pkts", NFP_MAC_STATS_RX_IN_BROADCAST_PKTS, }, + { "rx_drop_events", NFP_MAC_STATS_RX_DROP_EVENTS, }, + { "rx_alignment_errors", NFP_MAC_STATS_RX_ALIGNMENT_ERRORS, }, + { "rx_pause_mac_ctrl_frames", + NFP_MAC_STATS_RX_PAUSE_MAC_CTRL_FRAMES, }, + { "rx_frames_received_ok", NFP_MAC_STATS_RX_FRAMES_RECEIVED_OK, }, + { "rx_frame_check_sequence_errors", + NFP_MAC_STATS_RX_FRAME_CHECK_SEQUENCE_ERRORS, }, + { "rx_unicast_pkts", NFP_MAC_STATS_RX_UNICAST_PKTS, }, + { "rx_multicast_pkts", NFP_MAC_STATS_RX_MULTICAST_PKTS, }, + { "rx_pkts", NFP_MAC_STATS_RX_PKTS, }, + { "rx_undersize_pkts", NFP_MAC_STATS_RX_UNDERSIZE_PKTS, }, + { "rx_pkts_64_octets", NFP_MAC_STATS_RX_PKTS_64_OCTETS, }, + { "rx_pkts_65_to_127_octets", + NFP_MAC_STATS_RX_PKTS_65_TO_127_OCTETS, }, + { "rx_pkts_128_to_255_octets", + NFP_MAC_STATS_RX_PKTS_128_TO_255_OCTETS, }, + { "rx_pkts_256_to_511_octets", + NFP_MAC_STATS_RX_PKTS_256_TO_511_OCTETS, }, + { "rx_pkts_512_to_1023_octets", + NFP_MAC_STATS_RX_PKTS_512_TO_1023_OCTETS, }, + { "rx_pkts_1024_to_1518_octets", + NFP_MAC_STATS_RX_PKTS_1024_TO_1518_OCTETS, }, + { "rx_pkts_1519_to_max_octets", + NFP_MAC_STATS_RX_PKTS_1519_TO_MAX_OCTETS, }, + { "rx_jabbers", NFP_MAC_STATS_RX_JABBERS, }, + { "rx_fragments", NFP_MAC_STATS_RX_FRAGMENTS, }, + { "rx_oversize_pkts", NFP_MAC_STATS_RX_OVERSIZE_PKTS, }, + { "rx_pause_frames_class0", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS0, }, + { "rx_pause_frames_class1", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS1, }, + { "rx_pause_frames_class2", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS2, }, + { "rx_pause_frames_class3", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS3, }, + { "rx_pause_frames_class4", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS4, }, + { "rx_pause_frames_class5", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS5, }, + { "rx_pause_frames_class6", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS6, }, + { "rx_pause_frames_class7", NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS7, }, + { "rx_mac_ctrl_frames_received", + NFP_MAC_STATS_RX_MAC_CTRL_FRAMES_RECEIVED, }, + { "rx_mac_head_drop", NFP_MAC_STATS_RX_MAC_HEAD_DROP, }, + { "tx_queue_drop", NFP_MAC_STATS_TX_QUEUE_DROP, }, + { "tx_octets", NFP_MAC_STATS_TX_OUT_OCTETS, }, + { "tx_vlan_transmitted_ok", NFP_MAC_STATS_TX_VLAN_TRANSMITTED_OK, }, + { "tx_errors", NFP_MAC_STATS_TX_OUT_ERRORS, }, + { "tx_broadcast_pkts", NFP_MAC_STATS_TX_BROADCAST_PKTS, }, + { "tx_pause_mac_ctrl_frames", + NFP_MAC_STATS_TX_PAUSE_MAC_CTRL_FRAMES, }, + { "tx_frames_transmitted_ok", + NFP_MAC_STATS_TX_FRAMES_TRANSMITTED_OK, }, + { "tx_unicast_pkts", NFP_MAC_STATS_TX_UNICAST_PKTS, }, + { "tx_multicast_pkts", NFP_MAC_STATS_TX_MULTICAST_PKTS, }, + { "tx_pkts_64_octets", NFP_MAC_STATS_TX_PKTS_64_OCTETS, }, + { "tx_pkts_65_to_127_octets", + NFP_MAC_STATS_TX_PKTS_65_TO_127_OCTETS, }, + { "tx_pkts_128_to_255_octets", + NFP_MAC_STATS_TX_PKTS_128_TO_255_OCTETS, }, + { "tx_pkts_256_to_511_octets", + NFP_MAC_STATS_TX_PKTS_256_TO_511_OCTETS, }, + { "tx_pkts_512_to_1023_octets", + NFP_MAC_STATS_TX_PKTS_512_TO_1023_OCTETS, }, + { "tx_pkts_1024_to_1518_octets", + NFP_MAC_STATS_TX_PKTS_1024_TO_1518_OCTETS, }, + { "tx_pkts_1519_to_max_octets", + NFP_MAC_STATS_TX_PKTS_1519_TO_MAX_OCTETS, }, + { "tx_pause_frames_class0", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS0, }, + { "tx_pause_frames_class1", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS1, }, + { "tx_pause_frames_class2", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS2, }, + { "tx_pause_frames_class3", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS3, }, + { "tx_pause_frames_class4", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS4, }, + { "tx_pause_frames_class5", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS5, }, + { "tx_pause_frames_class6", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS6, }, + { "tx_pause_frames_class7", NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS7, }, }; #define NN_ET_GLOBAL_STATS_LEN ARRAY_SIZE(nfp_net_et_stats) -#define NN_ET_RVEC_STATS_LEN (nn->dp.num_r_vecs * 3) +#define NN_ET_SWITCH_STATS_LEN 9 #define NN_ET_RVEC_GATHER_STATS 7 -#define NN_ET_QUEUE_STATS_LEN ((nn->dp.num_tx_rings + nn->dp.num_rx_rings) * 2) -#define NN_ET_STATS_LEN (NN_ET_GLOBAL_STATS_LEN + NN_ET_RVEC_GATHER_STATS + \ - NN_ET_RVEC_STATS_LEN + NN_ET_QUEUE_STATS_LEN) static void nfp_net_get_nspinfo(struct nfp_app *app, char *version) { @@ -147,34 +194,53 @@ static void nfp_net_get_nspinfo(struct nfp_app *app, char *version) if (IS_ERR(nsp)) return; - snprintf(version, ETHTOOL_FWVERS_LEN, "sp:%hu.%hu", + snprintf(version, ETHTOOL_FWVERS_LEN, "%hu.%hu", nfp_nsp_get_abi_ver_major(nsp), nfp_nsp_get_abi_ver_minor(nsp)); nfp_nsp_close(nsp); } -static void nfp_net_get_drvinfo(struct net_device *netdev, - struct ethtool_drvinfo *drvinfo) +static void +nfp_get_drvinfo(struct nfp_app *app, struct pci_dev *pdev, + const char *vnic_version, struct ethtool_drvinfo *drvinfo) { char nsp_version[ETHTOOL_FWVERS_LEN] = {}; - struct nfp_net *nn = netdev_priv(netdev); - strlcpy(drvinfo->driver, nn->pdev->driver->name, - sizeof(drvinfo->driver)); + strlcpy(drvinfo->driver, pdev->driver->name, sizeof(drvinfo->driver)); strlcpy(drvinfo->version, nfp_driver_version, sizeof(drvinfo->version)); - nfp_net_get_nspinfo(nn->app, nsp_version); + nfp_net_get_nspinfo(app, nsp_version); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), - "%d.%d.%d.%d %s %s %s", + "%s %s %s %s", vnic_version, nsp_version, + nfp_app_mip_name(app), nfp_app_name(app)); +} + +static void +nfp_net_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) +{ + char vnic_version[ETHTOOL_FWVERS_LEN] = {}; + struct nfp_net *nn = netdev_priv(netdev); + + snprintf(vnic_version, sizeof(vnic_version), "%d.%d.%d.%d", nn->fw_ver.resv, nn->fw_ver.class, - nn->fw_ver.major, nn->fw_ver.minor, nsp_version, - nfp_app_mip_name(nn->app), nfp_app_name(nn->app)); + nn->fw_ver.major, nn->fw_ver.minor); strlcpy(drvinfo->bus_info, pci_name(nn->pdev), sizeof(drvinfo->bus_info)); - drvinfo->n_stats = NN_ET_STATS_LEN; - drvinfo->regdump_len = NFP_NET_CFG_BAR_SZ; + nfp_get_drvinfo(nn->app, nn->pdev, vnic_version, drvinfo); +} + +static void +nfp_app_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) +{ + struct nfp_app *app; + + app = nfp_app_from_netdev(netdev); + if (!app) + return; + + nfp_get_drvinfo(app, app->pdev, "*", drvinfo); } /** @@ -346,132 +412,270 @@ static int nfp_net_set_ringparam(struct net_device *netdev, return nfp_net_set_ring_size(nn, rxd_cnt, txd_cnt); } -static void nfp_net_get_strings(struct net_device *netdev, - u32 stringset, u8 *data) +static __printf(2, 3) u8 *nfp_pr_et(u8 *data, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsnprintf(data, ETH_GSTRING_LEN, fmt, args); + va_end(args); + + return data + ETH_GSTRING_LEN; +} + +static unsigned int nfp_vnic_get_sw_stats_count(struct net_device *netdev) +{ + struct nfp_net *nn = netdev_priv(netdev); + + return NN_ET_RVEC_GATHER_STATS + nn->dp.num_r_vecs * 3; +} + +static u8 *nfp_vnic_get_sw_stats_strings(struct net_device *netdev, u8 *data) { struct nfp_net *nn = netdev_priv(netdev); - u8 *p = data; int i; - switch (stringset) { - case ETH_SS_STATS: - for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) { - memcpy(p, nfp_net_et_stats[i].name, ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - } - for (i = 0; i < nn->dp.num_r_vecs; i++) { - sprintf(p, "rvec_%u_rx_pkts", i); - p += ETH_GSTRING_LEN; - sprintf(p, "rvec_%u_tx_pkts", i); - p += ETH_GSTRING_LEN; - sprintf(p, "rvec_%u_tx_busy", i); - p += ETH_GSTRING_LEN; - } - strncpy(p, "hw_rx_csum_ok", ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - strncpy(p, "hw_rx_csum_inner_ok", ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - strncpy(p, "hw_rx_csum_err", ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - strncpy(p, "hw_tx_csum", ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - strncpy(p, "hw_tx_inner_csum", ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - strncpy(p, "tx_gather", ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - strncpy(p, "tx_lso", ETH_GSTRING_LEN); - p += ETH_GSTRING_LEN; - for (i = 0; i < nn->dp.num_tx_rings; i++) { - sprintf(p, "txq_%u_pkts", i); - p += ETH_GSTRING_LEN; - sprintf(p, "txq_%u_bytes", i); - p += ETH_GSTRING_LEN; - } - for (i = 0; i < nn->dp.num_rx_rings; i++) { - sprintf(p, "rxq_%u_pkts", i); - p += ETH_GSTRING_LEN; - sprintf(p, "rxq_%u_bytes", i); - p += ETH_GSTRING_LEN; - } - break; + for (i = 0; i < nn->dp.num_r_vecs; i++) { + data = nfp_pr_et(data, "rvec_%u_rx_pkts", i); + data = nfp_pr_et(data, "rvec_%u_tx_pkts", i); + data = nfp_pr_et(data, "rvec_%u_tx_busy", i); } + + data = nfp_pr_et(data, "hw_rx_csum_ok"); + data = nfp_pr_et(data, "hw_rx_csum_inner_ok"); + data = nfp_pr_et(data, "hw_rx_csum_err"); + data = nfp_pr_et(data, "hw_tx_csum"); + data = nfp_pr_et(data, "hw_tx_inner_csum"); + data = nfp_pr_et(data, "tx_gather"); + data = nfp_pr_et(data, "tx_lso"); + + return data; } -static void nfp_net_get_stats(struct net_device *netdev, - struct ethtool_stats *stats, u64 *data) +static u64 *nfp_vnic_get_sw_stats(struct net_device *netdev, u64 *data) { u64 gathered_stats[NN_ET_RVEC_GATHER_STATS] = {}; struct nfp_net *nn = netdev_priv(netdev); - struct rtnl_link_stats64 *netdev_stats; - struct rtnl_link_stats64 temp = {}; u64 tmp[NN_ET_RVEC_GATHER_STATS]; - u8 __iomem *io_p; - int i, j, k; - u8 *p; - - netdev_stats = dev_get_stats(netdev, &temp); - - for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) { - switch (nfp_net_et_stats[i].type) { - case NETDEV_ET_STATS: - p = (char *)netdev_stats + nfp_net_et_stats[i].off; - data[i] = nfp_net_et_stats[i].sz == sizeof(u64) ? - *(u64 *)p : *(u32 *)p; - break; - - case NFP_NET_DEV_ET_STATS: - io_p = nn->dp.ctrl_bar + nfp_net_et_stats[i].off; - data[i] = readq(io_p); - break; - } - } - for (j = 0; j < nn->dp.num_r_vecs; j++) { + unsigned int i, j; + + for (i = 0; i < nn->dp.num_r_vecs; i++) { unsigned int start; do { - start = u64_stats_fetch_begin(&nn->r_vecs[j].rx_sync); - data[i++] = nn->r_vecs[j].rx_pkts; - tmp[0] = nn->r_vecs[j].hw_csum_rx_ok; - tmp[1] = nn->r_vecs[j].hw_csum_rx_inner_ok; - tmp[2] = nn->r_vecs[j].hw_csum_rx_error; - } while (u64_stats_fetch_retry(&nn->r_vecs[j].rx_sync, start)); + start = u64_stats_fetch_begin(&nn->r_vecs[i].rx_sync); + *data++ = nn->r_vecs[i].rx_pkts; + tmp[0] = nn->r_vecs[i].hw_csum_rx_ok; + tmp[1] = nn->r_vecs[i].hw_csum_rx_inner_ok; + tmp[2] = nn->r_vecs[i].hw_csum_rx_error; + } while (u64_stats_fetch_retry(&nn->r_vecs[i].rx_sync, start)); do { - start = u64_stats_fetch_begin(&nn->r_vecs[j].tx_sync); - data[i++] = nn->r_vecs[j].tx_pkts; - data[i++] = nn->r_vecs[j].tx_busy; - tmp[3] = nn->r_vecs[j].hw_csum_tx; - tmp[4] = nn->r_vecs[j].hw_csum_tx_inner; - tmp[5] = nn->r_vecs[j].tx_gather; - tmp[6] = nn->r_vecs[j].tx_lso; - } while (u64_stats_fetch_retry(&nn->r_vecs[j].tx_sync, start)); - - for (k = 0; k < NN_ET_RVEC_GATHER_STATS; k++) - gathered_stats[k] += tmp[k]; + start = u64_stats_fetch_begin(&nn->r_vecs[i].tx_sync); + *data++ = nn->r_vecs[i].tx_pkts; + *data++ = nn->r_vecs[i].tx_busy; + tmp[3] = nn->r_vecs[i].hw_csum_tx; + tmp[4] = nn->r_vecs[i].hw_csum_tx_inner; + tmp[5] = nn->r_vecs[i].tx_gather; + tmp[6] = nn->r_vecs[i].tx_lso; + } while (u64_stats_fetch_retry(&nn->r_vecs[i].tx_sync, start)); + + for (j = 0; j < NN_ET_RVEC_GATHER_STATS; j++) + gathered_stats[j] += tmp[j]; } + for (j = 0; j < NN_ET_RVEC_GATHER_STATS; j++) - data[i++] = gathered_stats[j]; - for (j = 0; j < nn->dp.num_tx_rings; j++) { - io_p = nn->dp.ctrl_bar + NFP_NET_CFG_TXR_STATS(j); - data[i++] = readq(io_p); - io_p = nn->dp.ctrl_bar + NFP_NET_CFG_TXR_STATS(j) + 8; - data[i++] = readq(io_p); + *data++ = gathered_stats[j]; + + return data; +} + +static unsigned int +nfp_vnic_get_hw_stats_count(unsigned int rx_rings, unsigned int tx_rings) +{ + return NN_ET_GLOBAL_STATS_LEN + (rx_rings + tx_rings) * 2; +} + +static u8 * +nfp_vnic_get_hw_stats_strings(u8 *data, unsigned int rx_rings, + unsigned int tx_rings, bool repr) +{ + int swap_off, i; + + BUILD_BUG_ON(NN_ET_GLOBAL_STATS_LEN < NN_ET_SWITCH_STATS_LEN * 2); + /* If repr is true first add SWITCH_STATS_LEN and then subtract it + * effectively swapping the RX and TX statistics (giving us the RX + * and TX from perspective of the switch). + */ + swap_off = repr * NN_ET_SWITCH_STATS_LEN; + + for (i = 0; i < NN_ET_SWITCH_STATS_LEN; i++) + data = nfp_pr_et(data, nfp_net_et_stats[i + swap_off].name); + + for (i = NN_ET_SWITCH_STATS_LEN; i < NN_ET_SWITCH_STATS_LEN * 2; i++) + data = nfp_pr_et(data, nfp_net_et_stats[i - swap_off].name); + + for (i = NN_ET_SWITCH_STATS_LEN * 2; i < NN_ET_GLOBAL_STATS_LEN; i++) + data = nfp_pr_et(data, nfp_net_et_stats[i].name); + + for (i = 0; i < tx_rings; i++) { + data = nfp_pr_et(data, "txq_%u_pkts", i); + data = nfp_pr_et(data, "txq_%u_bytes", i); + } + + for (i = 0; i < rx_rings; i++) { + data = nfp_pr_et(data, "rxq_%u_pkts", i); + data = nfp_pr_et(data, "rxq_%u_bytes", i); + } + + return data; +} + +static u64 * +nfp_vnic_get_hw_stats(u64 *data, u8 __iomem *mem, + unsigned int rx_rings, unsigned int tx_rings) +{ + unsigned int i; + + for (i = 0; i < NN_ET_GLOBAL_STATS_LEN; i++) + *data++ = readq(mem + nfp_net_et_stats[i].off); + + for (i = 0; i < tx_rings; i++) { + *data++ = readq(mem + NFP_NET_CFG_TXR_STATS(i)); + *data++ = readq(mem + NFP_NET_CFG_TXR_STATS(i) + 8); + } + + for (i = 0; i < rx_rings; i++) { + *data++ = readq(mem + NFP_NET_CFG_RXR_STATS(i)); + *data++ = readq(mem + NFP_NET_CFG_RXR_STATS(i) + 8); } - for (j = 0; j < nn->dp.num_rx_rings; j++) { - io_p = nn->dp.ctrl_bar + NFP_NET_CFG_RXR_STATS(j); - data[i++] = readq(io_p); - io_p = nn->dp.ctrl_bar + NFP_NET_CFG_RXR_STATS(j) + 8; - data[i++] = readq(io_p); + + return data; +} + +static unsigned int nfp_mac_get_stats_count(struct net_device *netdev) +{ + struct nfp_port *port; + + port = nfp_port_from_netdev(netdev); + if (!__nfp_port_get_eth_port(port) || !port->eth_stats) + return 0; + + return ARRAY_SIZE(nfp_mac_et_stats); +} + +static u8 *nfp_mac_get_stats_strings(struct net_device *netdev, u8 *data) +{ + struct nfp_port *port; + unsigned int i; + + port = nfp_port_from_netdev(netdev); + if (!__nfp_port_get_eth_port(port) || !port->eth_stats) + return data; + + for (i = 0; i < ARRAY_SIZE(nfp_mac_et_stats); i++) + data = nfp_pr_et(data, "mac.%s", nfp_mac_et_stats[i].name); + + return data; +} + +static u64 *nfp_mac_get_stats(struct net_device *netdev, u64 *data) +{ + struct nfp_port *port; + unsigned int i; + + port = nfp_port_from_netdev(netdev); + if (!__nfp_port_get_eth_port(port) || !port->eth_stats) + return data; + + for (i = 0; i < ARRAY_SIZE(nfp_mac_et_stats); i++) + *data++ = readq(port->eth_stats + nfp_mac_et_stats[i].off); + + return data; +} + +static void nfp_net_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) +{ + struct nfp_net *nn = netdev_priv(netdev); + + switch (stringset) { + case ETH_SS_STATS: + data = nfp_vnic_get_sw_stats_strings(netdev, data); + data = nfp_vnic_get_hw_stats_strings(data, nn->dp.num_rx_rings, + nn->dp.num_tx_rings, + false); + data = nfp_mac_get_stats_strings(netdev, data); + break; } } +static void +nfp_net_get_stats(struct net_device *netdev, struct ethtool_stats *stats, + u64 *data) +{ + struct nfp_net *nn = netdev_priv(netdev); + + data = nfp_vnic_get_sw_stats(netdev, data); + data = nfp_vnic_get_hw_stats(data, nn->dp.ctrl_bar, + nn->dp.num_rx_rings, nn->dp.num_tx_rings); + data = nfp_mac_get_stats(netdev, data); +} + static int nfp_net_get_sset_count(struct net_device *netdev, int sset) { struct nfp_net *nn = netdev_priv(netdev); switch (sset) { case ETH_SS_STATS: - return NN_ET_STATS_LEN; + return nfp_vnic_get_sw_stats_count(netdev) + + nfp_vnic_get_hw_stats_count(nn->dp.num_rx_rings, + nn->dp.num_tx_rings) + + nfp_mac_get_stats_count(netdev); + default: + return -EOPNOTSUPP; + } +} + +static void nfp_port_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) +{ + struct nfp_port *port = nfp_port_from_netdev(netdev); + + switch (stringset) { + case ETH_SS_STATS: + if (nfp_port_is_vnic(port)) + data = nfp_vnic_get_hw_stats_strings(data, 0, 0, true); + else + data = nfp_mac_get_stats_strings(netdev, data); + break; + } +} + +static void +nfp_port_get_stats(struct net_device *netdev, struct ethtool_stats *stats, + u64 *data) +{ + struct nfp_port *port = nfp_port_from_netdev(netdev); + + if (nfp_port_is_vnic(port)) + data = nfp_vnic_get_hw_stats(data, port->vnic, 0, 0); + else + data = nfp_mac_get_stats(netdev, data); +} + +static int nfp_port_get_sset_count(struct net_device *netdev, int sset) +{ + struct nfp_port *port = nfp_port_from_netdev(netdev); + unsigned int count; + + switch (sset) { + case ETH_SS_STATS: + if (nfp_port_is_vnic(port)) + count = nfp_vnic_get_hw_stats_count(0, 0); + else + count = nfp_mac_get_stats_count(netdev); + return count; default: return -EOPNOTSUPP; } @@ -708,18 +912,18 @@ static int nfp_net_get_coalesce(struct net_device *netdev, /* Other debug dumps */ static int -nfp_dump_nsp_diag(struct nfp_net *nn, struct ethtool_dump *dump, void *buffer) +nfp_dump_nsp_diag(struct nfp_app *app, struct ethtool_dump *dump, void *buffer) { struct nfp_resource *res; int ret; - if (!nn->app) + if (!app) return -EOPNOTSUPP; dump->version = 1; dump->flag = NFP_DUMP_NSP_DIAG; - res = nfp_resource_acquire(nn->app->cpp, NFP_RESOURCE_NSP_DIAG); + res = nfp_resource_acquire(app->cpp, NFP_RESOURCE_NSP_DIAG); if (IS_ERR(res)) return PTR_ERR(res); @@ -729,7 +933,7 @@ nfp_dump_nsp_diag(struct nfp_net *nn, struct ethtool_dump *dump, void *buffer) goto exit_release; } - ret = nfp_cpp_read(nn->app->cpp, nfp_resource_cpp_id(res), + ret = nfp_cpp_read(app->cpp, nfp_resource_cpp_id(res), nfp_resource_address(res), buffer, dump->len); if (ret != dump->len) @@ -746,32 +950,30 @@ exit_release: return ret; } -static int nfp_net_set_dump(struct net_device *netdev, struct ethtool_dump *val) +static int nfp_app_set_dump(struct net_device *netdev, struct ethtool_dump *val) { - struct nfp_net *nn = netdev_priv(netdev); + struct nfp_app *app = nfp_app_from_netdev(netdev); - if (!nn->app) + if (!app) return -EOPNOTSUPP; if (val->flag != NFP_DUMP_NSP_DIAG) return -EINVAL; - nn->ethtool_dump_flag = val->flag; - return 0; } static int -nfp_net_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump) +nfp_app_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump) { - return nfp_dump_nsp_diag(netdev_priv(netdev), dump, NULL); + return nfp_dump_nsp_diag(nfp_app_from_netdev(netdev), dump, NULL); } static int -nfp_net_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, +nfp_app_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, void *buffer) { - return nfp_dump_nsp_diag(netdev_priv(netdev), dump, buffer); + return nfp_dump_nsp_diag(nfp_app_from_netdev(netdev), dump, buffer); } static int nfp_net_set_coalesce(struct net_device *netdev, @@ -928,9 +1130,9 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { .set_rxfh = nfp_net_set_rxfh, .get_regs_len = nfp_net_get_regs_len, .get_regs = nfp_net_get_regs, - .set_dump = nfp_net_set_dump, - .get_dump_flag = nfp_net_get_dump_flag, - .get_dump_data = nfp_net_get_dump_data, + .set_dump = nfp_app_set_dump, + .get_dump_flag = nfp_app_get_dump_flag, + .get_dump_data = nfp_app_get_dump_data, .get_coalesce = nfp_net_get_coalesce, .set_coalesce = nfp_net_set_coalesce, .get_channels = nfp_net_get_channels, @@ -939,6 +1141,17 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { .set_link_ksettings = nfp_net_set_link_ksettings, }; +const struct ethtool_ops nfp_port_ethtool_ops = { + .get_drvinfo = nfp_app_get_drvinfo, + .get_link = ethtool_op_get_link, + .get_strings = nfp_port_get_strings, + .get_ethtool_stats = nfp_port_get_stats, + .get_sset_count = nfp_port_get_sset_count, + .set_dump = nfp_app_set_dump, + .get_dump_flag = nfp_app_get_dump_flag, + .get_dump_data = nfp_app_get_dump_data, +}; + void nfp_net_set_ethtool_ops(struct net_device *netdev) { netdev->ethtool_ops = &nfp_net_ethtool_ops; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 34b985384d26..5abb9ba31e7d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -57,6 +57,7 @@ #include "nfpcore/nfp6000_pcie.h" #include "nfp_app.h" #include "nfp_net_ctrl.h" +#include "nfp_net_sriov.h" #include "nfp_net.h" #include "nfp_main.h" #include "nfp_port.h" @@ -160,6 +161,8 @@ nfp_net_pf_map_rtsym(struct nfp_pf *pf, const char *name, const char *sym_fmt, static void nfp_net_pf_free_vnic(struct nfp_pf *pf, struct nfp_net *nn) { + if (nfp_net_is_data_vnic(nn)) + nfp_app_vnic_free(pf->app, nn); nfp_port_free(nn->port); list_del(&nn->vnic_list); pf->num_vnics--; @@ -204,7 +207,7 @@ nfp_net_pf_alloc_vnic(struct nfp_pf *pf, bool needs_netdev, nn->stride_tx = stride; if (needs_netdev) { - err = nfp_app_vnic_init(pf->app, nn, id); + err = nfp_app_vnic_alloc(pf->app, nn, id); if (err) { nfp_net_free(nn); return ERR_PTR(err); @@ -242,8 +245,17 @@ nfp_net_pf_init_vnic(struct nfp_pf *pf, struct nfp_net *nn, unsigned int id) nfp_net_info(nn); + if (nfp_net_is_data_vnic(nn)) { + err = nfp_app_vnic_init(pf->app, nn); + if (err) + goto err_devlink_port_clean; + } + return 0; +err_devlink_port_clean: + if (nn->port) + nfp_devlink_port_unregister(nn->port); err_dfs_clean: nfp_net_debugfs_dir_clean(&nn->debugfs_dir); nfp_net_clean(nn); @@ -287,11 +299,12 @@ err_free_prev: static void nfp_net_pf_clean_vnic(struct nfp_pf *pf, struct nfp_net *nn) { + if (nfp_net_is_data_vnic(nn)) + nfp_app_vnic_clean(pf->app, nn); if (nn->port) nfp_devlink_port_unregister(nn->port); nfp_net_debugfs_dir_clean(&nn->debugfs_dir); nfp_net_clean(nn); - nfp_app_vnic_clean(pf->app, nn); } static int nfp_net_pf_alloc_irqs(struct nfp_pf *pf) @@ -388,7 +401,7 @@ nfp_net_pf_app_init(struct nfp_pf *pf, u8 __iomem *qc_bar, unsigned int stride) NFP_PF_CSR_SLICE_SIZE, &pf->ctrl_vnic_bar); if (IS_ERR(ctrl_bar)) { - nfp_err(pf->cpp, "Failed to find data vNIC memory symbol\n"); + nfp_err(pf->cpp, "Failed to find ctrl vNIC memory symbol\n"); err = PTR_ERR(ctrl_bar); goto err_app_clean; } @@ -456,10 +469,14 @@ static int nfp_net_pf_app_start(struct nfp_pf *pf) { int err; - err = nfp_app_start(pf->app, pf->ctrl_vnic); + err = nfp_net_pf_app_start_ctrl(pf); if (err) return err; + err = nfp_app_start(pf->app, pf->ctrl_vnic); + if (err) + goto err_ctrl_stop; + if (pf->num_vfs) { err = nfp_app_sriov_enable(pf->app, pf->num_vfs); if (err) @@ -470,6 +487,8 @@ static int nfp_net_pf_app_start(struct nfp_pf *pf) err_app_stop: nfp_app_stop(pf->app); +err_ctrl_stop: + nfp_net_pf_app_stop_ctrl(pf); return err; } @@ -478,10 +497,13 @@ static void nfp_net_pf_app_stop(struct nfp_pf *pf) if (pf->num_vfs) nfp_app_sriov_disable(pf->app); nfp_app_stop(pf->app); + nfp_net_pf_app_stop_ctrl(pf); } static void nfp_net_pci_unmap_mem(struct nfp_pf *pf) { + if (pf->vfcfg_tbl2_area) + nfp_cpp_area_release_free(pf->vfcfg_tbl2_area); if (pf->vf_cfg_bar) nfp_cpp_area_release_free(pf->vf_cfg_bar); if (pf->mac_stats_bar) @@ -497,7 +519,7 @@ static int nfp_net_pci_map_mem(struct nfp_pf *pf) int err; min_size = pf->max_data_vnics * NFP_PF_CSR_SLICE_SIZE; - mem = nfp_net_pf_map_rtsym(pf, "net.ctrl", "_pf%d_net_bar0", + mem = nfp_net_pf_map_rtsym(pf, "net.bar0", "_pf%d_net_bar0", min_size, &pf->data_vnic_bar); if (IS_ERR(mem)) { nfp_err(pf->cpp, "Failed to find data vNIC memory symbol\n"); @@ -528,17 +550,32 @@ static int nfp_net_pci_map_mem(struct nfp_pf *pf) pf->vf_cfg_mem = NULL; } + min_size = NFP_NET_VF_CFG_SZ * pf->limit_vfs + NFP_NET_VF_CFG_MB_SZ; + pf->vfcfg_tbl2 = nfp_net_pf_map_rtsym(pf, "net.vfcfg_tbl2", + "_pf%d_net_vf_cfg2", + min_size, &pf->vfcfg_tbl2_area); + if (IS_ERR(pf->vfcfg_tbl2)) { + if (PTR_ERR(pf->vfcfg_tbl2) != -ENOENT) { + err = PTR_ERR(pf->vfcfg_tbl2); + goto err_unmap_vf_cfg; + } + pf->vfcfg_tbl2 = NULL; + } + mem = nfp_cpp_map_area(pf->cpp, "net.qc", 0, 0, NFP_PCIE_QUEUE(0), NFP_QCP_QUEUE_AREA_SZ, &pf->qc_area); if (IS_ERR(mem)) { nfp_err(pf->cpp, "Failed to map Queue Controller area.\n"); err = PTR_ERR(mem); - goto err_unmap_vf_cfg; + goto err_unmap_vfcfg_tbl2; } return 0; +err_unmap_vfcfg_tbl2: + if (pf->vfcfg_tbl2_area) + nfp_cpp_area_release_free(pf->vfcfg_tbl2_area); err_unmap_vf_cfg: if (pf->vf_cfg_bar) nfp_cpp_area_release_free(pf->vf_cfg_bar); @@ -552,7 +589,7 @@ err_unmap_ctrl: static void nfp_net_pci_remove_finish(struct nfp_pf *pf) { - nfp_net_pf_app_stop_ctrl(pf); + nfp_net_pf_app_stop(pf); /* stop app first, to avoid double free of ctrl vNIC's ddir */ nfp_net_debugfs_dir_clean(&pf->ddir); @@ -683,7 +720,6 @@ int nfp_net_pci_probe(struct nfp_pf *pf) { struct nfp_net_fw_version fw_ver; u8 __iomem *ctrl_bar, *qc_bar; - struct nfp_net *nn; int stride; int err; @@ -698,7 +734,7 @@ int nfp_net_pci_probe(struct nfp_pf *pf) if (!pf->rtbl) { nfp_err(pf->cpp, "No %s, giving up.\n", pf->fw_loaded ? "symbol table" : "firmware found"); - return -EPROBE_DEFER; + return -EINVAL; } mutex_lock(&pf->lock); @@ -760,7 +796,7 @@ int nfp_net_pci_probe(struct nfp_pf *pf) if (err) goto err_free_vnics; - err = nfp_net_pf_app_start_ctrl(pf); + err = nfp_net_pf_app_start(pf); if (err) goto err_free_irqs; @@ -768,20 +804,12 @@ int nfp_net_pci_probe(struct nfp_pf *pf) if (err) goto err_stop_app; - err = nfp_net_pf_app_start(pf); - if (err) - goto err_clean_vnics; - mutex_unlock(&pf->lock); return 0; -err_clean_vnics: - list_for_each_entry(nn, &pf->vnics, vnic_list) - if (nfp_net_is_data_vnic(nn)) - nfp_net_pf_clean_vnic(pf, nn); err_stop_app: - nfp_net_pf_app_stop_ctrl(pf); + nfp_net_pf_app_stop(pf); err_free_irqs: nfp_net_pf_free_irqs(pf); err_free_vnics: @@ -805,8 +833,6 @@ void nfp_net_pci_remove(struct nfp_pf *pf) if (list_empty(&pf->vnics)) goto out; - nfp_net_pf_app_stop(pf); - list_for_each_entry(nn, &pf->vnics, vnic_list) if (nfp_net_is_data_vnic(nn)) nfp_net_pf_clean_vnic(pf, nn); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c index 8ec5474f4b18..d540a9dc77b3 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_repr.c @@ -43,6 +43,7 @@ #include "nfp_main.h" #include "nfp_net_ctrl.h" #include "nfp_net_repr.h" +#include "nfp_net_sriov.h" #include "nfp_port.h" static void @@ -78,12 +79,10 @@ void nfp_repr_inc_rx_stats(struct net_device *netdev, unsigned int len) } static void -nfp_repr_phy_port_get_stats64(const struct nfp_app *app, u8 phy_port, +nfp_repr_phy_port_get_stats64(struct nfp_port *port, struct rtnl_link_stats64 *stats) { - u8 __iomem *mem; - - mem = app->pf->mac_stats_mem + phy_port * NFP_MAC_STATS_SIZE; + u8 __iomem *mem = port->eth_stats; /* TX and RX stats are flipped as we are returning the stats as seen * at the switch port corresponding to the phys port. @@ -98,67 +97,38 @@ nfp_repr_phy_port_get_stats64(const struct nfp_app *app, u8 phy_port, } static void -nfp_repr_vf_get_stats64(const struct nfp_app *app, u8 vf, - struct rtnl_link_stats64 *stats) +nfp_repr_vnic_get_stats64(struct nfp_port *port, + struct rtnl_link_stats64 *stats) { - u8 __iomem *mem; - - mem = app->pf->vf_cfg_mem + vf * NFP_NET_CFG_BAR_SZ; - /* TX and RX stats are flipped as we are returning the stats as seen * at the switch port corresponding to the VF. */ - stats->tx_packets = readq(mem + NFP_NET_CFG_STATS_RX_FRAMES); - stats->tx_bytes = readq(mem + NFP_NET_CFG_STATS_RX_OCTETS); - stats->tx_dropped = readq(mem + NFP_NET_CFG_STATS_RX_DISCARDS); - - stats->rx_packets = readq(mem + NFP_NET_CFG_STATS_TX_FRAMES); - stats->rx_bytes = readq(mem + NFP_NET_CFG_STATS_TX_OCTETS); - stats->rx_dropped = readq(mem + NFP_NET_CFG_STATS_TX_DISCARDS); -} - -static void -nfp_repr_pf_get_stats64(const struct nfp_app *app, u8 pf, - struct rtnl_link_stats64 *stats) -{ - u8 __iomem *mem; - - if (pf) - return; + stats->tx_packets = readq(port->vnic + NFP_NET_CFG_STATS_RX_FRAMES); + stats->tx_bytes = readq(port->vnic + NFP_NET_CFG_STATS_RX_OCTETS); + stats->tx_dropped = readq(port->vnic + NFP_NET_CFG_STATS_RX_DISCARDS); - mem = nfp_cpp_area_iomem(app->pf->data_vnic_bar); - - stats->tx_packets = readq(mem + NFP_NET_CFG_STATS_RX_FRAMES); - stats->tx_bytes = readq(mem + NFP_NET_CFG_STATS_RX_OCTETS); - stats->tx_dropped = readq(mem + NFP_NET_CFG_STATS_RX_DISCARDS); - - stats->rx_packets = readq(mem + NFP_NET_CFG_STATS_TX_FRAMES); - stats->rx_bytes = readq(mem + NFP_NET_CFG_STATS_TX_OCTETS); - stats->rx_dropped = readq(mem + NFP_NET_CFG_STATS_TX_DISCARDS); + stats->rx_packets = readq(port->vnic + NFP_NET_CFG_STATS_TX_FRAMES); + stats->rx_bytes = readq(port->vnic + NFP_NET_CFG_STATS_TX_OCTETS); + stats->rx_dropped = readq(port->vnic + NFP_NET_CFG_STATS_TX_DISCARDS); } static void nfp_repr_get_stats64(struct net_device *netdev, struct rtnl_link_stats64 *stats) { struct nfp_repr *repr = netdev_priv(netdev); - struct nfp_eth_table_port *eth_port; - struct nfp_app *app = repr->app; if (WARN_ON(!repr->port)) return; switch (repr->port->type) { case NFP_PORT_PHYS_PORT: - eth_port = __nfp_port_get_eth_port(repr->port); - if (!eth_port) + if (!__nfp_port_get_eth_port(repr->port)) break; - nfp_repr_phy_port_get_stats64(app, eth_port->index, stats); + nfp_repr_phy_port_get_stats64(repr->port, stats); break; case NFP_PORT_PF_PORT: - nfp_repr_pf_get_stats64(app, repr->port->pf_id, stats); - break; case NFP_PORT_VF_PORT: - nfp_repr_vf_get_stats64(app, repr->port->vf_id, stats); + nfp_repr_vnic_get_stats64(repr->port, stats); default: break; } @@ -239,15 +209,34 @@ static netdev_tx_t nfp_repr_xmit(struct sk_buff *skb, struct net_device *netdev) static int nfp_repr_stop(struct net_device *netdev) { struct nfp_repr *repr = netdev_priv(netdev); + int err; + + err = nfp_app_repr_stop(repr->app, repr); + if (err) + return err; - return nfp_app_repr_stop(repr->app, repr); + nfp_port_configure(netdev, false); + return 0; } static int nfp_repr_open(struct net_device *netdev) { struct nfp_repr *repr = netdev_priv(netdev); + int err; + + err = nfp_port_configure(netdev, true); + if (err) + return err; - return nfp_app_repr_open(repr->app, repr); + err = nfp_app_repr_open(repr->app, repr); + if (err) + goto err_port_disable; + + return 0; + +err_port_disable: + nfp_port_configure(netdev, false); + return err; } const struct net_device_ops nfp_repr_netdev_ops = { @@ -259,6 +248,11 @@ const struct net_device_ops nfp_repr_netdev_ops = { .ndo_get_offload_stats = nfp_repr_get_offload_stats, .ndo_get_phys_port_name = nfp_port_get_phys_port_name, .ndo_setup_tc = nfp_port_setup_tc, + .ndo_set_vf_mac = nfp_app_set_vf_mac, + .ndo_set_vf_vlan = nfp_app_set_vf_vlan, + .ndo_set_vf_spoofchk = nfp_app_set_vf_spoofchk, + .ndo_get_vf_config = nfp_app_get_vf_config, + .ndo_set_vf_link_state = nfp_app_set_vf_link_state, }; static void nfp_repr_clean(struct nfp_repr *repr) @@ -301,6 +295,8 @@ int nfp_repr_init(struct nfp_app *app, struct net_device *netdev, repr->dst->u.port_info.lower_dev = pf_netdev; netdev->netdev_ops = &nfp_repr_netdev_ops; + netdev->ethtool_ops = &nfp_port_ethtool_ops; + SWITCHDEV_SET_OPS(netdev, &nfp_port_switchdev_ops); if (nfp_app_has_tc(app)) { diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.c b/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.c new file mode 100644 index 000000000000..e6d2e06b050c --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.c @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/bitfield.h> +#include <linux/errno.h> +#include <linux/etherdevice.h> +#include <linux/if_link.h> +#include <linux/if_ether.h> + +#include "nfpcore/nfp_cpp.h" +#include "nfp_app.h" +#include "nfp_main.h" +#include "nfp_net_ctrl.h" +#include "nfp_net.h" +#include "nfp_net_sriov.h" + +static int +nfp_net_sriov_check(struct nfp_app *app, int vf, u16 cap, const char *msg) +{ + u16 cap_vf; + + if (!app || !app->pf->vfcfg_tbl2) + return -EOPNOTSUPP; + + cap_vf = readw(app->pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_CAP); + if ((cap_vf & cap) != cap) { + nfp_warn(app->pf->cpp, "ndo_set_vf_%s not supported\n", msg); + return -EOPNOTSUPP; + } + + if (vf < 0 || vf >= app->pf->num_vfs) { + nfp_warn(app->pf->cpp, "invalid VF id %d\n", vf); + return -EINVAL; + } + + return 0; +} + +static int +nfp_net_sriov_update(struct nfp_app *app, int vf, u16 update, const char *msg) +{ + struct nfp_net *nn; + int ret; + + /* Write update info to mailbox in VF config symbol */ + writeb(vf, app->pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_VF_NUM); + writew(update, app->pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_UPD); + + nn = list_first_entry(&app->pf->vnics, struct nfp_net, vnic_list); + /* Signal VF reconfiguration */ + ret = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_VF); + if (ret) + return ret; + + ret = readw(app->pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_RET); + if (ret) + nfp_warn(app->pf->cpp, + "FW refused VF %s update with errno: %d\n", msg, ret); + return -ret; +} + +int nfp_app_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) +{ + struct nfp_app *app = nfp_app_from_netdev(netdev); + unsigned int vf_offset; + int err; + + err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_MAC, "mac"); + if (err) + return err; + + if (is_multicast_ether_addr(mac)) { + nfp_warn(app->pf->cpp, + "invalid Ethernet address %pM for VF id %d\n", + mac, vf); + return -EINVAL; + } + + /* Write MAC to VF entry in VF config symbol */ + vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ; + writel(get_unaligned_be32(mac), app->pf->vfcfg_tbl2 + vf_offset); + writew(get_unaligned_be16(mac + 4), + app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_MAC_LO); + + return nfp_net_sriov_update(app, vf, NFP_NET_VF_CFG_MB_UPD_MAC, "MAC"); +} + +int nfp_app_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos, + __be16 vlan_proto) +{ + struct nfp_app *app = nfp_app_from_netdev(netdev); + unsigned int vf_offset; + u16 vlan_tci; + int err; + + err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_VLAN, "vlan"); + if (err) + return err; + + if (vlan_proto != htons(ETH_P_8021Q)) + return -EOPNOTSUPP; + + if (vlan > 4095 || qos > 7) { + nfp_warn(app->pf->cpp, + "invalid vlan id or qos for VF id %d\n", vf); + return -EINVAL; + } + + /* Write VLAN tag to VF entry in VF config symbol */ + vlan_tci = FIELD_PREP(NFP_NET_VF_CFG_VLAN_VID, vlan) | + FIELD_PREP(NFP_NET_VF_CFG_VLAN_QOS, qos); + vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ; + writew(vlan_tci, app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_VLAN); + + return nfp_net_sriov_update(app, vf, NFP_NET_VF_CFG_MB_UPD_VLAN, + "vlan"); +} + +int nfp_app_set_vf_spoofchk(struct net_device *netdev, int vf, bool enable) +{ + struct nfp_app *app = nfp_app_from_netdev(netdev); + unsigned int vf_offset; + u8 vf_ctrl; + int err; + + err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_SPOOF, + "spoofchk"); + if (err) + return err; + + /* Write spoof check control bit to VF entry in VF config symbol */ + vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ + + NFP_NET_VF_CFG_CTRL; + vf_ctrl = readb(app->pf->vfcfg_tbl2 + vf_offset); + vf_ctrl &= ~NFP_NET_VF_CFG_CTRL_SPOOF; + vf_ctrl |= FIELD_PREP(NFP_NET_VF_CFG_CTRL_SPOOF, enable); + writeb(vf_ctrl, app->pf->vfcfg_tbl2 + vf_offset); + + return nfp_net_sriov_update(app, vf, NFP_NET_VF_CFG_MB_UPD_SPOOF, + "spoofchk"); +} + +int nfp_app_set_vf_link_state(struct net_device *netdev, int vf, + int link_state) +{ + struct nfp_app *app = nfp_app_from_netdev(netdev); + unsigned int vf_offset; + u8 vf_ctrl; + int err; + + err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_LINK_STATE, + "link_state"); + if (err) + return err; + + switch (link_state) { + case IFLA_VF_LINK_STATE_AUTO: + case IFLA_VF_LINK_STATE_ENABLE: + case IFLA_VF_LINK_STATE_DISABLE: + break; + default: + return -EINVAL; + } + + /* Write link state to VF entry in VF config symbol */ + vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ + + NFP_NET_VF_CFG_CTRL; + vf_ctrl = readb(app->pf->vfcfg_tbl2 + vf_offset); + vf_ctrl &= ~NFP_NET_VF_CFG_CTRL_LINK_STATE; + vf_ctrl |= FIELD_PREP(NFP_NET_VF_CFG_CTRL_LINK_STATE, link_state); + writeb(vf_ctrl, app->pf->vfcfg_tbl2 + vf_offset); + + return nfp_net_sriov_update(app, vf, NFP_NET_VF_CFG_MB_UPD_LINK_STATE, + "link state"); +} + +int nfp_app_get_vf_config(struct net_device *netdev, int vf, + struct ifla_vf_info *ivi) +{ + struct nfp_app *app = nfp_app_from_netdev(netdev); + unsigned int vf_offset; + u16 vlan_tci; + u32 mac_hi; + u16 mac_lo; + u8 flags; + int err; + + err = nfp_net_sriov_check(app, vf, 0, ""); + if (err) + return err; + + vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ; + + mac_hi = readl(app->pf->vfcfg_tbl2 + vf_offset); + mac_lo = readw(app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_MAC_LO); + + flags = readb(app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_CTRL); + vlan_tci = readw(app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_VLAN); + + memset(ivi, 0, sizeof(*ivi)); + ivi->vf = vf; + + put_unaligned_be32(mac_hi, &ivi->mac[0]); + put_unaligned_be16(mac_lo, &ivi->mac[4]); + + ivi->vlan = FIELD_GET(NFP_NET_VF_CFG_VLAN_VID, vlan_tci); + ivi->qos = FIELD_GET(NFP_NET_VF_CFG_VLAN_QOS, vlan_tci); + + ivi->spoofchk = FIELD_GET(NFP_NET_VF_CFG_CTRL_SPOOF, flags); + ivi->linkstate = FIELD_GET(NFP_NET_VF_CFG_CTRL_LINK_STATE, flags); + + return 0; +} diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h b/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h new file mode 100644 index 000000000000..e9df9d1eab8e --- /dev/null +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_sriov.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2017 Netronome Systems, Inc. + * + * This software is dual licensed under the GNU General License Version 2, + * June 1991 as shown in the file COPYING in the top-level directory of this + * source tree or the BSD 2-Clause License provided below. You have the + * option to license this software under the complete terms of either license. + * + * The BSD 2-Clause License: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _NFP_NET_SRIOV_H_ +#define _NFP_NET_SRIOV_H_ + +/** + * SRIOV VF configuration. + * The configuration memory begins with a mailbox region for communication with + * the firmware followed by individual VF entries. + */ +#define NFP_NET_VF_CFG_SZ 16 +#define NFP_NET_VF_CFG_MB_SZ 16 + +/* VF config mailbox */ +#define NFP_NET_VF_CFG_MB 0x0 +#define NFP_NET_VF_CFG_MB_CAP 0x0 +#define NFP_NET_VF_CFG_MB_CAP_MAC (0x1 << 0) +#define NFP_NET_VF_CFG_MB_CAP_VLAN (0x1 << 1) +#define NFP_NET_VF_CFG_MB_CAP_SPOOF (0x1 << 2) +#define NFP_NET_VF_CFG_MB_CAP_LINK_STATE (0x1 << 3) +#define NFP_NET_VF_CFG_MB_RET 0x2 +#define NFP_NET_VF_CFG_MB_UPD 0x4 +#define NFP_NET_VF_CFG_MB_UPD_MAC (0x1 << 0) +#define NFP_NET_VF_CFG_MB_UPD_VLAN (0x1 << 1) +#define NFP_NET_VF_CFG_MB_UPD_SPOOF (0x1 << 2) +#define NFP_NET_VF_CFG_MB_UPD_LINK_STATE (0x1 << 3) +#define NFP_NET_VF_CFG_MB_VF_NUM 0x7 + +/* VF config entry + * MAC_LO is set that the MAC address can be read in a single 6 byte read + * by the NFP + */ +#define NFP_NET_VF_CFG_MAC 0x0 +#define NFP_NET_VF_CFG_MAC_HI 0x0 +#define NFP_NET_VF_CFG_MAC_LO 0x6 +#define NFP_NET_VF_CFG_CTRL 0x4 +#define NFP_NET_VF_CFG_CTRL_SPOOF 0x4 +#define NFP_NET_VF_CFG_CTRL_LINK_STATE 0x3 +#define NFP_NET_VF_CFG_LS_MODE_AUTO 0 +#define NFP_NET_VF_CFG_LS_MODE_ENABLE 1 +#define NFP_NET_VF_CFG_LS_MODE_DISABLE 2 +#define NFP_NET_VF_CFG_VLAN 0x8 +#define NFP_NET_VF_CFG_VLAN_QOS 0xe000 +#define NFP_NET_VF_CFG_VLAN_VID 0x0fff + +int nfp_app_set_vf_mac(struct net_device *netdev, int vf, u8 *mac); +int nfp_app_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos, + __be16 vlan_proto); +int nfp_app_set_vf_spoofchk(struct net_device *netdev, int vf, bool setting); +int nfp_app_set_vf_link_state(struct net_device *netdev, int vf, + int link_state); +int nfp_app_get_vf_config(struct net_device *netdev, int vf, + struct ifla_vf_info *ivi); + +#endif /* _NFP_NET_SRIOV_H_ */ diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.c b/drivers/net/ethernet/netronome/nfp/nfp_port.c index e42644dbb865..34a6e035fe9a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.c @@ -88,19 +88,16 @@ const struct switchdev_ops nfp_port_switchdev_ops = { .switchdev_port_attr_get = nfp_port_attr_get, }; -int nfp_port_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, + void *type_data) { struct nfp_port *port; - if (chain_index) - return -EOPNOTSUPP; - port = nfp_port_from_netdev(netdev); if (!port) return -EOPNOTSUPP; - return nfp_app_setup_tc(port->app, netdev, handle, proto, tc); + return nfp_app_setup_tc(port->app, netdev, type, type_data); } struct nfp_port * @@ -181,6 +178,33 @@ nfp_port_get_phys_port_name(struct net_device *netdev, char *name, size_t len) return 0; } +/** + * nfp_port_configure() - helper to set the interface configured bit + * @netdev: net_device instance + * @configed: Desired state + * + * Helper to set the ifup/ifdown state on the PHY only if there is a physical + * interface associated with the netdev. + * + * Return: + * 0 - configuration successful (or no change); + * -ERRNO - configuration failed. + */ +int nfp_port_configure(struct net_device *netdev, bool configed) +{ + struct nfp_eth_table_port *eth_port; + struct nfp_port *port; + int err; + + port = nfp_port_from_netdev(netdev); + eth_port = __nfp_port_get_eth_port(port); + if (!eth_port) + return 0; + + err = nfp_eth_set_configured(port->app->cpp, eth_port->index, configed); + return err < 0 && err != -EOPNOTSUPP ? err : 0; +} + int nfp_port_init_phy_port(struct nfp_pf *pf, struct nfp_app *app, struct nfp_port *port, unsigned int id) { @@ -201,6 +225,9 @@ int nfp_port_init_phy_port(struct nfp_pf *pf, struct nfp_app *app, port->eth_port = &pf->eth_tbl->ports[id]; port->eth_id = pf->eth_tbl->ports[id].index; + if (pf->mac_stats_mem) + port->eth_stats = + pf->mac_stats_mem + port->eth_id * NFP_MAC_STATS_SIZE; return 0; } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index a33d22e18f94..51dcb9c603ee 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -36,7 +36,6 @@ #include <net/devlink.h> -struct tc_to_netdev; struct net_device; struct nfp_app; struct nfp_pf; @@ -77,8 +76,10 @@ enum nfp_port_flags { * @dl_port: devlink port structure * @eth_id: for %NFP_PORT_PHYS_PORT port ID in NFP enumeration scheme * @eth_port: for %NFP_PORT_PHYS_PORT translated ETH Table port entry + * @eth_stats: for %NFP_PORT_PHYS_PORT MAC stats if available * @pf_id: for %NFP_PORT_PF_PORT, %NFP_PORT_VF_PORT ID of the PCI PF (0-3) * @vf_id: for %NFP_PORT_VF_PORT ID of the PCI VF within @pf_id + * @vnic: for %NFP_PORT_PF_PORT, %NFP_PORT_VF_PORT vNIC ctrl memory * @port_list: entry on pf's list of ports */ struct nfp_port { @@ -96,21 +97,29 @@ struct nfp_port { struct { unsigned int eth_id; struct nfp_eth_table_port *eth_port; + u8 __iomem *eth_stats; }; /* NFP_PORT_PF_PORT, NFP_PORT_VF_PORT */ struct { unsigned int pf_id; unsigned int vf_id; + u8 __iomem *vnic; }; }; struct list_head port_list; }; +extern const struct ethtool_ops nfp_port_ethtool_ops; extern const struct switchdev_ops nfp_port_switchdev_ops; -int nfp_port_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc); +int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, + void *type_data); + +static inline bool nfp_port_is_vnic(const struct nfp_port *port) +{ + return port->type == NFP_PORT_PF_PORT || port->type == NFP_PORT_VF_PORT; +} struct nfp_port *nfp_port_from_netdev(struct net_device *netdev); struct nfp_port * @@ -120,6 +129,7 @@ struct nfp_eth_table_port *nfp_port_get_eth_port(struct nfp_port *port); int nfp_port_get_phys_port_name(struct net_device *netdev, char *name, size_t len); +int nfp_port_configure(struct net_device *netdev, bool configed); struct nfp_port * nfp_port_alloc(struct nfp_app *app, enum nfp_port_type type, @@ -144,31 +154,32 @@ void nfp_devlink_port_unregister(struct nfp_port *port); #define NFP_MAC_STATS_SIZE 0x0200 #define NFP_MAC_STATS_RX_IN_OCTETS (NFP_MAC_STATS_BASE + 0x000) + /* unused 0x008 */ #define NFP_MAC_STATS_RX_FRAME_TOO_LONG_ERRORS (NFP_MAC_STATS_BASE + 0x010) #define NFP_MAC_STATS_RX_RANGE_LENGTH_ERRORS (NFP_MAC_STATS_BASE + 0x018) #define NFP_MAC_STATS_RX_VLAN_REVEIVE_OK (NFP_MAC_STATS_BASE + 0x020) #define NFP_MAC_STATS_RX_IN_ERRORS (NFP_MAC_STATS_BASE + 0x028) #define NFP_MAC_STATS_RX_IN_BROADCAST_PKTS (NFP_MAC_STATS_BASE + 0x030) -#define NFP_MAC_STATS_RX_STATS_DROP_EVENTS (NFP_MAC_STATS_BASE + 0x038) +#define NFP_MAC_STATS_RX_DROP_EVENTS (NFP_MAC_STATS_BASE + 0x038) #define NFP_MAC_STATS_RX_ALIGNMENT_ERRORS (NFP_MAC_STATS_BASE + 0x040) #define NFP_MAC_STATS_RX_PAUSE_MAC_CTRL_FRAMES (NFP_MAC_STATS_BASE + 0x048) #define NFP_MAC_STATS_RX_FRAMES_RECEIVED_OK (NFP_MAC_STATS_BASE + 0x050) #define NFP_MAC_STATS_RX_FRAME_CHECK_SEQUENCE_ERRORS (NFP_MAC_STATS_BASE + 0x058) #define NFP_MAC_STATS_RX_UNICAST_PKTS (NFP_MAC_STATS_BASE + 0x060) #define NFP_MAC_STATS_RX_MULTICAST_PKTS (NFP_MAC_STATS_BASE + 0x068) -#define NFP_MAC_STATS_RX_STATS_PKTS (NFP_MAC_STATS_BASE + 0x070) -#define NFP_MAC_STATS_RX_STATS_UNDERSIZE_PKTS (NFP_MAC_STATS_BASE + 0x078) -#define NFP_MAC_STATS_RX_STATS_PKTS_64_OCTETS (NFP_MAC_STATS_BASE + 0x080) -#define NFP_MAC_STATS_RX_STATS_PKTS_65_TO_127_OCTETS (NFP_MAC_STATS_BASE + 0x088) -#define NFP_MAC_STATS_RX_STATS_PKTS_512_TO_1023_OCTETS (NFP_MAC_STATS_BASE + 0x090) -#define NFP_MAC_STATS_RX_STATS_PKTS_1024_TO_1518_OCTETS (NFP_MAC_STATS_BASE + 0x098) -#define NFP_MAC_STATS_RX_STATS_JABBERS (NFP_MAC_STATS_BASE + 0x0a0) -#define NFP_MAC_STATS_RX_STATS_FRAGMENTS (NFP_MAC_STATS_BASE + 0x0a8) +#define NFP_MAC_STATS_RX_PKTS (NFP_MAC_STATS_BASE + 0x070) +#define NFP_MAC_STATS_RX_UNDERSIZE_PKTS (NFP_MAC_STATS_BASE + 0x078) +#define NFP_MAC_STATS_RX_PKTS_64_OCTETS (NFP_MAC_STATS_BASE + 0x080) +#define NFP_MAC_STATS_RX_PKTS_65_TO_127_OCTETS (NFP_MAC_STATS_BASE + 0x088) +#define NFP_MAC_STATS_RX_PKTS_512_TO_1023_OCTETS (NFP_MAC_STATS_BASE + 0x090) +#define NFP_MAC_STATS_RX_PKTS_1024_TO_1518_OCTETS (NFP_MAC_STATS_BASE + 0x098) +#define NFP_MAC_STATS_RX_JABBERS (NFP_MAC_STATS_BASE + 0x0a0) +#define NFP_MAC_STATS_RX_FRAGMENTS (NFP_MAC_STATS_BASE + 0x0a8) #define NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS2 (NFP_MAC_STATS_BASE + 0x0b0) #define NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS3 (NFP_MAC_STATS_BASE + 0x0b8) -#define NFP_MAC_STATS_RX_STATS_PKTS_128_TO_255_OCTETS (NFP_MAC_STATS_BASE + 0x0c0) -#define NFP_MAC_STATS_RX_STATS_PKTS_256_TO_511_OCTETS (NFP_MAC_STATS_BASE + 0x0c8) -#define NFP_MAC_STATS_RX_STATS_PKTS_1519_TO_MAX_OCTETS (NFP_MAC_STATS_BASE + 0x0d0) +#define NFP_MAC_STATS_RX_PKTS_128_TO_255_OCTETS (NFP_MAC_STATS_BASE + 0x0c0) +#define NFP_MAC_STATS_RX_PKTS_256_TO_511_OCTETS (NFP_MAC_STATS_BASE + 0x0c8) +#define NFP_MAC_STATS_RX_PKTS_1519_TO_MAX_OCTETS (NFP_MAC_STATS_BASE + 0x0d0) #define NFP_MAC_STATS_RX_OVERSIZE_PKTS (NFP_MAC_STATS_BASE + 0x0d8) #define NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS0 (NFP_MAC_STATS_BASE + 0x0e0) #define NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS1 (NFP_MAC_STATS_BASE + 0x0e8) @@ -178,9 +189,12 @@ void nfp_devlink_port_unregister(struct nfp_port *port); #define NFP_MAC_STATS_RX_PAUSE_FRAMES_CLASS7 (NFP_MAC_STATS_BASE + 0x108) #define NFP_MAC_STATS_RX_MAC_CTRL_FRAMES_RECEIVED (NFP_MAC_STATS_BASE + 0x110) #define NFP_MAC_STATS_RX_MAC_HEAD_DROP (NFP_MAC_STATS_BASE + 0x118) - + /* unused 0x120 */ + /* unused 0x128 */ + /* unused 0x130 */ #define NFP_MAC_STATS_TX_QUEUE_DROP (NFP_MAC_STATS_BASE + 0x138) #define NFP_MAC_STATS_TX_OUT_OCTETS (NFP_MAC_STATS_BASE + 0x140) + /* unused 0x148 */ #define NFP_MAC_STATS_TX_VLAN_TRANSMITTED_OK (NFP_MAC_STATS_BASE + 0x150) #define NFP_MAC_STATS_TX_OUT_ERRORS (NFP_MAC_STATS_BASE + 0x158) #define NFP_MAC_STATS_TX_BROADCAST_PKTS (NFP_MAC_STATS_BASE + 0x160) @@ -192,8 +206,16 @@ void nfp_devlink_port_unregister(struct nfp_port *port); #define NFP_MAC_STATS_TX_UNICAST_PKTS (NFP_MAC_STATS_BASE + 0x190) #define NFP_MAC_STATS_TX_MULTICAST_PKTS (NFP_MAC_STATS_BASE + 0x198) #define NFP_MAC_STATS_TX_PKTS_65_TO_127_OCTETS (NFP_MAC_STATS_BASE + 0x1a0) -#define NFP_MAC_STATS_TX_PKTS_127_TO_512_OCTETS (NFP_MAC_STATS_BASE + 0x1a8) -#define NFP_MAC_STATS_TX_PKTS_128_TO_1518_OCTETS (NFP_MAC_STATS_BASE + 0x1b0) -#define NFP_MAC_STATS_TX_PKTS_1518_TO_MAX_OCTETS (NFP_MAC_STATS_BASE + 0x1b8) +#define NFP_MAC_STATS_TX_PKTS_128_TO_255_OCTETS (NFP_MAC_STATS_BASE + 0x1a8) +#define NFP_MAC_STATS_TX_PKTS_1024_TO_1518_OCTETS (NFP_MAC_STATS_BASE + 0x1b0) +#define NFP_MAC_STATS_TX_PKTS_1519_TO_MAX_OCTETS (NFP_MAC_STATS_BASE + 0x1b8) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS0 (NFP_MAC_STATS_BASE + 0x1c0) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS1 (NFP_MAC_STATS_BASE + 0x1c8) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS4 (NFP_MAC_STATS_BASE + 0x1d0) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS5 (NFP_MAC_STATS_BASE + 0x1d8) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS2 (NFP_MAC_STATS_BASE + 0x1e0) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS3 (NFP_MAC_STATS_BASE + 0x1e8) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS6 (NFP_MAC_STATS_BASE + 0x1f0) +#define NFP_MAC_STATS_TX_PAUSE_FRAMES_CLASS7 (NFP_MAC_STATS_BASE + 0x1f8) #endif diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index c2bc36e8649f..f6f7c085f8e0 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -391,7 +391,10 @@ int nfp_eth_config_commit_end(struct nfp_nsp *nsp) * Enable or disable PHY module (this usually means setting the TX lanes * disable bits). * - * Return: 0 or -ERRNO. + * Return: + * 0 - configuration successful; + * 1 - no changes were needed; + * -ERRNO - configuration failed. */ int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable) { @@ -427,7 +430,10 @@ int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable) * * Set the ifup/ifdown state on the PHY. * - * Return: 0 or -ERRNO. + * Return: + * 0 - configuration successful; + * 1 - no changes were needed; + * -ERRNO - configuration failed. */ int nfp_eth_set_configured(struct nfp_cpp *cpp, unsigned int idx, bool configed) { @@ -439,6 +445,14 @@ int nfp_eth_set_configured(struct nfp_cpp *cpp, unsigned int idx, bool configed) if (IS_ERR(nsp)) return PTR_ERR(nsp); + /* Older ABI versions did support this feature, however this has only + * been reliable since ABI 20. + */ + if (nfp_nsp_get_abi_ver_minor(nsp) < 20) { + nfp_eth_config_cleanup_end(nsp); + return -EOPNOTSUPP; + } + entries = nfp_nsp_config_entries(nsp); /* Check if we are already in requested state */ diff --git a/drivers/net/ethernet/netronome/nfp/nic/main.c b/drivers/net/ethernet/netronome/nfp/nic/main.c index 520684242b7d..d5b587fccaa3 100644 --- a/drivers/net/ethernet/netronome/nfp/nic/main.c +++ b/drivers/net/ethernet/netronome/nfp/nic/main.c @@ -49,10 +49,22 @@ static int nfp_nic_init(struct nfp_app *app) return 0; } +static int nfp_nic_sriov_enable(struct nfp_app *app, int num_vfs) +{ + return 0; +} + +static void nfp_nic_sriov_disable(struct nfp_app *app) +{ +} + const struct nfp_app_type app_nic = { .id = NFP_APP_CORE_NIC, .name = "nic", .init = nfp_nic_init, - .vnic_init = nfp_app_nic_vnic_init, + .vnic_alloc = nfp_app_nic_vnic_alloc, + + .sriov_enable = nfp_nic_sriov_enable, + .sriov_disable = nfp_nic_sriov_disable, }; diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index aa912f43e15f..994a83a1f0a5 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -5629,9 +5629,8 @@ static int nv_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) setup_timer(&np->oom_kick, nv_do_rx_refill, (unsigned long)dev); setup_timer(&np->nic_poll, nv_do_nic_poll, (unsigned long)dev); - init_timer_deferrable(&np->stats_poll); - np->stats_poll.data = (unsigned long) dev; - np->stats_poll.function = nv_do_stats_poll; /* timer handler */ + setup_deferrable_timer(&np->stats_poll, nv_do_stats_poll, + (unsigned long)dev); err = pci_enable_device(pci_dev); if (err) diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 827de838389f..f2e8de607119 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -2828,7 +2828,7 @@ netxen_show_bridged_mode(struct device *dev, return sprintf(buf, "%d\n", bridged_mode); } -static struct device_attribute dev_attr_bridged_mode = { +static const struct device_attribute dev_attr_bridged_mode = { .attr = {.name = "bridged_mode", .mode = (S_IRUGO | S_IWUSR)}, .show = netxen_show_bridged_mode, .store = netxen_store_bridged_mode, @@ -2860,7 +2860,7 @@ netxen_show_diag_mode(struct device *dev, !!(adapter->flags & NETXEN_NIC_DIAG_ENABLED)); } -static struct device_attribute dev_attr_diag_mode = { +static const struct device_attribute dev_attr_diag_mode = { .attr = {.name = "diag_mode", .mode = (S_IRUGO | S_IWUSR)}, .show = netxen_show_diag_mode, .store = netxen_store_diag_mode, diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index 6c87bed13bd2..58a689fb04db 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -1684,6 +1684,8 @@ int qed_hw_init(struct qed_dev *cdev, struct qed_hw_init_params *p_params) "Load request was sent. Load code: 0x%x\n", load_code); + qed_mcp_set_capabilities(p_hwfn, p_hwfn->p_main_ptt); + qed_reset_mb_shadow(p_hwfn, p_hwfn->p_main_ptt); p_hwfn->first_on_engine = (load_code == @@ -2472,6 +2474,7 @@ static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { u32 port_cfg_addr, link_temp, nvm_cfg_addr, device_capabilities; u32 nvm_cfg1_offset, mf_mode, addr, generic_cont0, core_cfg; + struct qed_mcp_link_capabilities *p_caps; struct qed_mcp_link_params *link; /* Read global nvm_cfg address */ @@ -2534,6 +2537,7 @@ static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) /* Read default link configuration */ link = &p_hwfn->mcp_info->link_input; + p_caps = &p_hwfn->mcp_info->link_capabilities; port_cfg_addr = MCP_REG_SCRATCH + nvm_cfg1_offset + offsetof(struct nvm_cfg1, port[MFW_PORT(p_hwfn)]); link_temp = qed_rd(p_hwfn, p_ptt, @@ -2588,10 +2592,45 @@ static int qed_hw_get_nvm_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) NVM_CFG1_PORT_DRV_FLOW_CONTROL_TX); link->loopback_mode = 0; - DP_VERBOSE(p_hwfn, NETIF_MSG_LINK, - "Read default link: Speed 0x%08x, Adv. Speed 0x%08x, AN: 0x%02x, PAUSE AN: 0x%02x\n", - link->speed.forced_speed, link->speed.advertised_speeds, - link->speed.autoneg, link->pause.autoneg); + if (p_hwfn->mcp_info->capabilities & FW_MB_PARAM_FEATURE_SUPPORT_EEE) { + link_temp = qed_rd(p_hwfn, p_ptt, port_cfg_addr + + offsetof(struct nvm_cfg1_port, ext_phy)); + link_temp &= NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_MASK; + link_temp >>= NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_OFFSET; + p_caps->default_eee = QED_MCP_EEE_ENABLED; + link->eee.enable = true; + switch (link_temp) { + case NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_DISABLED: + p_caps->default_eee = QED_MCP_EEE_DISABLED; + link->eee.enable = false; + break; + case NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_BALANCED: + p_caps->eee_lpi_timer = EEE_TX_TIMER_USEC_BALANCED_TIME; + break; + case NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_AGGRESSIVE: + p_caps->eee_lpi_timer = + EEE_TX_TIMER_USEC_AGGRESSIVE_TIME; + break; + case NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_LOW_LATENCY: + p_caps->eee_lpi_timer = EEE_TX_TIMER_USEC_LATENCY_TIME; + break; + } + + link->eee.tx_lpi_timer = p_caps->eee_lpi_timer; + link->eee.tx_lpi_enable = link->eee.enable; + link->eee.adv_caps = QED_EEE_1G_ADV | QED_EEE_10G_ADV; + } else { + p_caps->default_eee = QED_MCP_EEE_UNSUPPORTED; + } + + DP_VERBOSE(p_hwfn, + NETIF_MSG_LINK, + "Read default link: Speed 0x%08x, Adv. Speed 0x%08x, AN: 0x%02x, PAUSE AN: 0x%02x EEE: %02x [%08x usec]\n", + link->speed.forced_speed, + link->speed.advertised_speeds, + link->speed.autoneg, + link->pause.autoneg, + p_caps->default_eee, p_caps->eee_lpi_timer); /* Read Multi-function information from shmem */ addr = MCP_REG_SCRATCH + nvm_cfg1_offset + @@ -2751,6 +2790,27 @@ static void qed_hw_info_port_num(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) qed_hw_info_port_num_ah(p_hwfn, p_ptt); } +static void qed_get_eee_caps(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + struct qed_mcp_link_capabilities *p_caps; + u32 eee_status; + + p_caps = &p_hwfn->mcp_info->link_capabilities; + if (p_caps->default_eee == QED_MCP_EEE_UNSUPPORTED) + return; + + p_caps->eee_speed_caps = 0; + eee_status = qed_rd(p_hwfn, p_ptt, p_hwfn->mcp_info->port_addr + + offsetof(struct public_port, eee_status)); + eee_status = (eee_status & EEE_SUPPORTED_SPEED_MASK) >> + EEE_SUPPORTED_SPEED_OFFSET; + + if (eee_status & EEE_1G_SUPPORTED) + p_caps->eee_speed_caps |= QED_EEE_1G_ADV; + if (eee_status & EEE_10G_ADV) + p_caps->eee_speed_caps |= QED_EEE_10G_ADV; +} + static int qed_get_hw_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, @@ -2767,6 +2827,8 @@ qed_get_hw_info(struct qed_hwfn *p_hwfn, qed_hw_info_port_num(p_hwfn, p_ptt); + qed_mcp_get_capabilities(p_hwfn, p_ptt); + qed_hw_get_nvm_info(p_hwfn, p_ptt); rc = qed_int_igu_read_cam(p_hwfn, p_ptt); @@ -2785,6 +2847,8 @@ qed_get_hw_info(struct qed_hwfn *p_hwfn, p_hwfn->mcp_info->func_info.ovlan; qed_mcp_cmd_port_init(p_hwfn, p_ptt); + + qed_get_eee_caps(p_hwfn, p_ptt); } if (qed_mcp_is_init(p_hwfn)) { @@ -3630,7 +3694,7 @@ static int qed_set_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, } p_coal_timeset = p_eth_qzone; - memset(p_coal_timeset, 0, eth_qzone_size); + memset(p_eth_qzone, 0, eth_qzone_size); SET_FIELD(p_coal_timeset->value, COALESCING_TIMESET_TIMESET, timeset); SET_FIELD(p_coal_timeset->value, COALESCING_TIMESET_VALID, 1); qed_memcpy_to(p_hwfn, p_ptt, hw_addr, p_eth_qzone, eth_qzone_size); @@ -3638,12 +3702,46 @@ static int qed_set_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, return 0; } -int qed_set_rxq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - u16 coalesce, u16 qid, u16 sb_id) +int qed_set_queue_coalesce(u16 rx_coal, u16 tx_coal, void *p_handle) +{ + struct qed_queue_cid *p_cid = p_handle; + struct qed_hwfn *p_hwfn; + struct qed_ptt *p_ptt; + int rc = 0; + + p_hwfn = p_cid->p_owner; + + if (IS_VF(p_hwfn->cdev)) + return qed_vf_pf_set_coalesce(p_hwfn, rx_coal, tx_coal, p_cid); + + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) + return -EAGAIN; + + if (rx_coal) { + rc = qed_set_rxq_coalesce(p_hwfn, p_ptt, rx_coal, p_cid); + if (rc) + goto out; + p_hwfn->cdev->rx_coalesce_usecs = rx_coal; + } + + if (tx_coal) { + rc = qed_set_txq_coalesce(p_hwfn, p_ptt, tx_coal, p_cid); + if (rc) + goto out; + p_hwfn->cdev->tx_coalesce_usecs = tx_coal; + } +out: + qed_ptt_release(p_hwfn, p_ptt); + return rc; +} + +int qed_set_rxq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u16 coalesce, struct qed_queue_cid *p_cid) { struct ustorm_eth_queue_zone eth_qzone; u8 timeset, timer_res; - u16 fw_qid = 0; u32 address; int rc; @@ -3660,32 +3758,29 @@ int qed_set_rxq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, } timeset = (u8)(coalesce >> timer_res); - rc = qed_fw_l2_queue(p_hwfn, qid, &fw_qid); - if (rc) - return rc; - - rc = qed_int_set_timer_res(p_hwfn, p_ptt, timer_res, sb_id, false); + rc = qed_int_set_timer_res(p_hwfn, p_ptt, timer_res, + p_cid->sb_igu_id, false); if (rc) goto out; - address = BAR0_MAP_REG_USDM_RAM + USTORM_ETH_QUEUE_ZONE_OFFSET(fw_qid); + address = BAR0_MAP_REG_USDM_RAM + + USTORM_ETH_QUEUE_ZONE_OFFSET(p_cid->abs.queue_id); rc = qed_set_coalesce(p_hwfn, p_ptt, address, ð_qzone, sizeof(struct ustorm_eth_queue_zone), timeset); if (rc) goto out; - p_hwfn->cdev->rx_coalesce_usecs = coalesce; out: return rc; } -int qed_set_txq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - u16 coalesce, u16 qid, u16 sb_id) +int qed_set_txq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u16 coalesce, struct qed_queue_cid *p_cid) { struct xstorm_eth_queue_zone eth_qzone; u8 timeset, timer_res; - u16 fw_qid = 0; u32 address; int rc; @@ -3702,22 +3797,16 @@ int qed_set_txq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, } timeset = (u8)(coalesce >> timer_res); - rc = qed_fw_l2_queue(p_hwfn, qid, &fw_qid); - if (rc) - return rc; - - rc = qed_int_set_timer_res(p_hwfn, p_ptt, timer_res, sb_id, true); + rc = qed_int_set_timer_res(p_hwfn, p_ptt, timer_res, + p_cid->sb_igu_id, true); if (rc) goto out; - address = BAR0_MAP_REG_XSDM_RAM + XSTORM_ETH_QUEUE_ZONE_OFFSET(fw_qid); + address = BAR0_MAP_REG_XSDM_RAM + + XSTORM_ETH_QUEUE_ZONE_OFFSET(p_cid->abs.queue_id); rc = qed_set_coalesce(p_hwfn, p_ptt, address, ð_qzone, sizeof(struct xstorm_eth_queue_zone), timeset); - if (rc) - goto out; - - p_hwfn->cdev->tx_coalesce_usecs = coalesce; out: return rc; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h index 1f1df1bf127c..defdda1ffaa2 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev_api.h +++ b/drivers/net/ethernet/qlogic/qed/qed_dev_api.h @@ -443,38 +443,35 @@ int qed_final_cleanup(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u16 id, bool is_vf); /** - * @brief qed_set_rxq_coalesce - Configure coalesce parameters for an Rx queue - * The fact that we can configure coalescing to up to 511, but on varying - * accuracy [the bigger the value the less accurate] up to a mistake of 3usec - * for the highest values. + * @brief qed_get_queue_coalesce - Retrieve coalesce value for a given queue. * * @param p_hwfn - * @param p_ptt - * @param coalesce - Coalesce value in micro seconds. - * @param qid - Queue index. - * @param qid - SB Id + * @param p_coal - store coalesce value read from the hardware. + * @param p_handle * * @return int - */ -int qed_set_rxq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - u16 coalesce, u16 qid, u16 sb_id); + **/ +int qed_get_queue_coalesce(struct qed_hwfn *p_hwfn, u16 *coal, void *handle); /** - * @brief qed_set_txq_coalesce - Configure coalesce parameters for a Tx queue - * While the API allows setting coalescing per-qid, all tx queues sharing a - * SB should be in same range [i.e., either 0-0x7f, 0x80-0xff or 0x100-0x1ff] - * otherwise configuration would break. + * @brief qed_set_queue_coalesce - Configure coalesce parameters for Rx and + * Tx queue. The fact that we can configure coalescing to up to 511, but on + * varying accuracy [the bigger the value the less accurate] up to a mistake + * of 3usec for the highest values. + * While the API allows setting coalescing per-qid, all queues sharing a SB + * should be in same range [i.e., either 0-0x7f, 0x80-0xff or 0x100-0x1ff] + * otherwise configuration would break. * - * @param p_hwfn - * @param p_ptt - * @param coalesce - Coalesce value in micro seconds. - * @param qid - Queue index. - * @param qid - SB Id + * + * @param rx_coal - Rx Coalesce value in micro seconds. + * @param tx_coal - TX Coalesce value in micro seconds. + * @param p_handle * * @return int - */ -int qed_set_txq_coalesce(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, - u16 coalesce, u16 qid, u16 sb_id); + **/ +int +qed_set_queue_coalesce(u16 rx_coal, u16 tx_coal, void *p_handle); + const char *qed_hw_get_resc_name(enum qed_resources res_id); #endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index 31fb0bffa098..3427fe7049b5 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -10825,6 +10825,17 @@ struct eth_phy_cfg { #define ETH_LOOPBACK_EXT (3) #define ETH_LOOPBACK_MAC (4) + u32 eee_cfg; +#define EEE_CFG_EEE_ENABLED BIT(0) +#define EEE_CFG_TX_LPI BIT(1) +#define EEE_CFG_ADV_SPEED_1G BIT(2) +#define EEE_CFG_ADV_SPEED_10G BIT(3) +#define EEE_TX_TIMER_USEC_MASK (0xfffffff0) +#define EEE_TX_TIMER_USEC_OFFSET 4 +#define EEE_TX_TIMER_USEC_BALANCED_TIME (0xa00) +#define EEE_TX_TIMER_USEC_AGGRESSIVE_TIME (0x100) +#define EEE_TX_TIMER_USEC_LATENCY_TIME (0x6000) + u32 feature_config_flags; #define ETH_EEE_MODE_ADV_LPI (1 << 0) }; @@ -11242,6 +11253,25 @@ struct public_port { u32 wol_pkt_len; u32 wol_pkt_details; struct dcb_dscp_map dcb_dscp_map; + + u32 eee_status; +#define EEE_ACTIVE_BIT BIT(0) +#define EEE_LD_ADV_STATUS_MASK 0x000000f0 +#define EEE_LD_ADV_STATUS_OFFSET 4 +#define EEE_1G_ADV BIT(1) +#define EEE_10G_ADV BIT(2) +#define EEE_LP_ADV_STATUS_MASK 0x00000f00 +#define EEE_LP_ADV_STATUS_OFFSET 8 +#define EEE_SUPPORTED_SPEED_MASK 0x0000f000 +#define EEE_SUPPORTED_SPEED_OFFSET 12 +#define EEE_1G_SUPPORTED BIT(1) +#define EEE_10G_SUPPORTED BIT(2) + + u32 eee_remote; +#define EEE_REMOTE_TW_TX_MASK 0x0000ffff +#define EEE_REMOTE_TW_TX_OFFSET 0 +#define EEE_REMOTE_TW_RX_MASK 0xffff0000 +#define EEE_REMOTE_TW_RX_OFFSET 16 }; struct public_func { @@ -11570,6 +11600,9 @@ struct public_drv_mb { #define DRV_MSG_CODE_GET_PF_RDMA_PROTOCOL 0x002b0000 #define DRV_MSG_CODE_OS_WOL 0x002e0000 +#define DRV_MSG_CODE_FEATURE_SUPPORT 0x00300000 +#define DRV_MSG_CODE_GET_MFW_FEATURE_SUPPORT 0x00310000 + #define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff u32 drv_mb_param; @@ -11653,6 +11686,10 @@ struct public_drv_mb { #define DRV_MB_PARAM_BIST_TEST_IMAGE_INDEX_SHIFT 8 #define DRV_MB_PARAM_BIST_TEST_IMAGE_INDEX_MASK 0x0000FF00 +#define DRV_MB_PARAM_FEATURE_SUPPORT_PORT_MASK 0x0000FFFF +#define DRV_MB_PARAM_FEATURE_SUPPORT_PORT_OFFSET 0 +#define DRV_MB_PARAM_FEATURE_SUPPORT_PORT_EEE 0x00000002 + u32 fw_mb_header; #define FW_MSG_CODE_MASK 0xffff0000 #define FW_MSG_CODE_UNSUPPORTED 0x00000000 @@ -11696,6 +11733,9 @@ struct public_drv_mb { #define FW_MB_PARAM_GET_PF_RDMA_IWARP 0x2 #define FW_MB_PARAM_GET_PF_RDMA_BOTH 0x3 +/* get MFW feature support response */ +#define FW_MB_PARAM_FEATURE_SUPPORT_EEE 0x00000002 + #define FW_MB_PARAM_LOAD_DONE_DID_EFUSE_ERROR (1 << 0) u32 drv_pulse_mb; @@ -11891,7 +11931,16 @@ struct nvm_cfg1_port { #define NVM_CFG1_PORT_DRV_FLOW_CONTROL_TX 0x4 u32 phy_cfg; u32 mgmt_traffic; + u32 ext_phy; + /* EEE power saving mode */ +#define NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_MASK 0x00FF0000 +#define NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_OFFSET 16 +#define NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_DISABLED 0x0 +#define NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_BALANCED 0x1 +#define NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_AGGRESSIVE 0x2 +#define NVM_CFG1_PORT_EEE_POWER_SAVING_MODE_LOW_LATENCY 0x3 + u32 mba_cfg1; u32 mba_cfg2; u32 vf_cfg; diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.c b/drivers/net/ethernet/qlogic/qed/qed_l2.c index 0ba5ec8a9814..085338990f49 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.c +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.c @@ -2047,6 +2047,106 @@ qed_configure_rfs_ntuple_filter(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, return qed_spq_post(p_hwfn, p_ent, NULL); } +int qed_get_rxq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_queue_cid *p_cid, u16 *p_rx_coal) +{ + u32 coalesce, address, is_valid; + struct cau_sb_entry sb_entry; + u8 timer_res; + int rc; + + rc = qed_dmae_grc2host(p_hwfn, p_ptt, CAU_REG_SB_VAR_MEMORY + + p_cid->sb_igu_id * sizeof(u64), + (u64)(uintptr_t)&sb_entry, 2, 0); + if (rc) { + DP_ERR(p_hwfn, "dmae_grc2host failed %d\n", rc); + return rc; + } + + timer_res = GET_FIELD(sb_entry.params, CAU_SB_ENTRY_TIMER_RES0); + + address = BAR0_MAP_REG_USDM_RAM + + USTORM_ETH_QUEUE_ZONE_OFFSET(p_cid->abs.queue_id); + coalesce = qed_rd(p_hwfn, p_ptt, address); + + is_valid = GET_FIELD(coalesce, COALESCING_TIMESET_VALID); + if (!is_valid) + return -EINVAL; + + coalesce = GET_FIELD(coalesce, COALESCING_TIMESET_TIMESET); + *p_rx_coal = (u16)(coalesce << timer_res); + + return 0; +} + +int qed_get_txq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_queue_cid *p_cid, u16 *p_tx_coal) +{ + u32 coalesce, address, is_valid; + struct cau_sb_entry sb_entry; + u8 timer_res; + int rc; + + rc = qed_dmae_grc2host(p_hwfn, p_ptt, CAU_REG_SB_VAR_MEMORY + + p_cid->sb_igu_id * sizeof(u64), + (u64)(uintptr_t)&sb_entry, 2, 0); + if (rc) { + DP_ERR(p_hwfn, "dmae_grc2host failed %d\n", rc); + return rc; + } + + timer_res = GET_FIELD(sb_entry.params, CAU_SB_ENTRY_TIMER_RES1); + + address = BAR0_MAP_REG_XSDM_RAM + + XSTORM_ETH_QUEUE_ZONE_OFFSET(p_cid->abs.queue_id); + coalesce = qed_rd(p_hwfn, p_ptt, address); + + is_valid = GET_FIELD(coalesce, COALESCING_TIMESET_VALID); + if (!is_valid) + return -EINVAL; + + coalesce = GET_FIELD(coalesce, COALESCING_TIMESET_TIMESET); + *p_tx_coal = (u16)(coalesce << timer_res); + + return 0; +} + +int qed_get_queue_coalesce(struct qed_hwfn *p_hwfn, u16 *p_coal, void *handle) +{ + struct qed_queue_cid *p_cid = handle; + struct qed_ptt *p_ptt; + int rc = 0; + + if (IS_VF(p_hwfn->cdev)) { + rc = qed_vf_pf_get_coalesce(p_hwfn, p_coal, p_cid); + if (rc) + DP_NOTICE(p_hwfn, "Unable to read queue coalescing\n"); + + return rc; + } + + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) + return -EAGAIN; + + if (p_cid->b_is_rx) { + rc = qed_get_rxq_coalesce(p_hwfn, p_ptt, p_cid, p_coal); + if (rc) + goto out; + } else { + rc = qed_get_txq_coalesce(p_hwfn, p_ptt, p_cid, p_coal); + if (rc) + goto out; + } + +out: + qed_ptt_release(p_hwfn, p_ptt); + + return rc; +} + static int qed_fill_eth_dev_info(struct qed_dev *cdev, struct qed_dev_eth_info *info) { @@ -2696,6 +2796,20 @@ static int qed_ntuple_arfs_filter_config(struct qed_dev *cdev, void *cookie, return rc; } +static int qed_get_coalesce(struct qed_dev *cdev, u16 *coal, void *handle) +{ + struct qed_queue_cid *p_cid = handle; + struct qed_hwfn *p_hwfn; + int rc; + + p_hwfn = p_cid->p_owner; + rc = qed_get_queue_coalesce(p_hwfn, coal, handle); + if (rc) + DP_NOTICE(p_hwfn, "Unable to read queue coalescing\n"); + + return rc; +} + static int qed_fp_cqe_completion(struct qed_dev *dev, u8 rss_id, struct eth_slow_path_rx_cqe *cqe) { @@ -2739,6 +2853,7 @@ static const struct qed_eth_ops qed_eth_ops_pass = { .tunn_config = &qed_tunn_configure, .ntuple_filter_config = &qed_ntuple_arfs_filter_config, .configure_arfs_searcher = &qed_configure_arfs_searcher, + .get_coalesce = &qed_get_coalesce, }; const struct qed_eth_ops *qed_get_eth_ops(void) diff --git a/drivers/net/ethernet/qlogic/qed/qed_l2.h b/drivers/net/ethernet/qlogic/qed/qed_l2.h index f8f09aadced7..cc1f248551c9 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_l2.h +++ b/drivers/net/ethernet/qlogic/qed/qed_l2.h @@ -400,4 +400,20 @@ qed_eth_txq_start_ramrod(struct qed_hwfn *p_hwfn, u8 qed_mcast_bin_from_mac(u8 *mac); -#endif /* _QED_L2_H */ +int qed_set_rxq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u16 coalesce, struct qed_queue_cid *p_cid); + +int qed_set_txq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u16 coalesce, struct qed_queue_cid *p_cid); + +int qed_get_rxq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_queue_cid *p_cid, u16 *p_hw_coal); + +int qed_get_txq_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_queue_cid *p_cid, u16 *p_hw_coal); + +#endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index b11399606990..27832885a87f 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -954,9 +954,7 @@ static int qed_slowpath_start(struct qed_dev *cdev, struct qed_tunnel_info tunn_info; const u8 *data = NULL; struct qed_hwfn *hwfn; -#ifdef CONFIG_RFS_ACCEL struct qed_ptt *p_ptt; -#endif int rc = -EINVAL; if (qed_iov_wq_start(cdev)) @@ -972,7 +970,6 @@ static int qed_slowpath_start(struct qed_dev *cdev, goto err; } -#ifdef CONFIG_RFS_ACCEL if (cdev->num_hwfns == 1) { p_ptt = qed_ptt_acquire(QED_LEADING_HWFN(cdev)); if (p_ptt) { @@ -983,7 +980,6 @@ static int qed_slowpath_start(struct qed_dev *cdev, goto err; } } -#endif } cdev->rx_coalesce_usecs = QED_DEFAULT_RX_USECS; @@ -1091,12 +1087,10 @@ err: if (IS_PF(cdev)) release_firmware(cdev->firmware); -#ifdef CONFIG_RFS_ACCEL if (IS_PF(cdev) && (cdev->num_hwfns == 1) && QED_LEADING_HWFN(cdev)->p_arfs_ptt) qed_ptt_release(QED_LEADING_HWFN(cdev), QED_LEADING_HWFN(cdev)->p_arfs_ptt); -#endif qed_iov_wq_stop(cdev, false); @@ -1111,11 +1105,9 @@ static int qed_slowpath_stop(struct qed_dev *cdev) qed_ll2_dealloc_if(cdev); if (IS_PF(cdev)) { -#ifdef CONFIG_RFS_ACCEL if (cdev->num_hwfns == 1) qed_ptt_release(QED_LEADING_HWFN(cdev), QED_LEADING_HWFN(cdev)->p_arfs_ptt); -#endif qed_free_stream_mem(cdev); if (IS_QED_ETH_IF(cdev)) qed_sriov_disable(cdev, true); @@ -1305,6 +1297,10 @@ static int qed_set_link(struct qed_dev *cdev, struct qed_link_params *params) } } + if (params->override_flags & QED_LINK_OVERRIDE_EEE_CONFIG) + memcpy(&link_params->eee, ¶ms->eee, + sizeof(link_params->eee)); + rc = qed_mcp_set_link(hwfn, ptt, params->link_up); qed_ptt_release(hwfn, ptt); @@ -1491,6 +1487,21 @@ static void qed_fill_link(struct qed_hwfn *hwfn, if (link.partner_adv_pause == QED_LINK_PARTNER_ASYMMETRIC_PAUSE || link.partner_adv_pause == QED_LINK_PARTNER_BOTH_PAUSE) if_link->lp_caps |= QED_LM_Asym_Pause_BIT; + + if (link_caps.default_eee == QED_MCP_EEE_UNSUPPORTED) { + if_link->eee_supported = false; + } else { + if_link->eee_supported = true; + if_link->eee_active = link.eee_active; + if_link->sup_caps = link_caps.eee_speed_caps; + /* MFW clears adv_caps on eee disable; use configured value */ + if_link->eee.adv_caps = link.eee_adv_caps ? link.eee_adv_caps : + params.eee.adv_caps; + if_link->eee.lp_adv_caps = link.eee_lp_adv_caps; + if_link->eee.enable = params.eee.enable; + if_link->eee.tx_lpi_enable = params.eee.tx_lpi_enable; + if_link->eee.tx_lpi_timer = params.eee.tx_lpi_timer; + } } static void qed_get_current_link(struct qed_dev *cdev, @@ -1557,36 +1568,10 @@ static int qed_nvm_get_image(struct qed_dev *cdev, enum qed_nvm_images type, return rc; } -static void qed_get_coalesce(struct qed_dev *cdev, u16 *rx_coal, u16 *tx_coal) -{ - *rx_coal = cdev->rx_coalesce_usecs; - *tx_coal = cdev->tx_coalesce_usecs; -} - static int qed_set_coalesce(struct qed_dev *cdev, u16 rx_coal, u16 tx_coal, - u16 qid, u16 sb_id) + void *handle) { - struct qed_hwfn *hwfn; - struct qed_ptt *ptt; - int hwfn_index; - int status = 0; - - hwfn_index = qid % cdev->num_hwfns; - hwfn = &cdev->hwfns[hwfn_index]; - ptt = qed_ptt_acquire(hwfn); - if (!ptt) - return -EAGAIN; - - status = qed_set_rxq_coalesce(hwfn, ptt, rx_coal, - qid / cdev->num_hwfns, sb_id); - if (status) - goto out; - status = qed_set_txq_coalesce(hwfn, ptt, tx_coal, - qid / cdev->num_hwfns, sb_id); -out: - qed_ptt_release(hwfn, ptt); - - return status; + return qed_set_queue_coalesce(rx_coal, tx_coal, handle); } static int qed_set_led(struct qed_dev *cdev, enum qed_led_mode mode) @@ -1735,7 +1720,6 @@ const struct qed_common_ops qed_common_ops_pass = { .chain_alloc = &qed_chain_alloc, .chain_free = &qed_chain_free, .nvm_get_image = &qed_nvm_get_image, - .get_coalesce = &qed_get_coalesce, .set_coalesce = &qed_set_coalesce, .set_led = &qed_set_led, .update_drv_state = &qed_update_drv_state, diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 3eb241657368..376485d99357 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -1097,6 +1097,31 @@ static void qed_mcp_handle_transceiver_change(struct qed_hwfn *p_hwfn, DP_NOTICE(p_hwfn, "Transceiver is unplugged.\n"); } +static void qed_mcp_read_eee_config(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_mcp_link_state *p_link) +{ + u32 eee_status, val; + + p_link->eee_adv_caps = 0; + p_link->eee_lp_adv_caps = 0; + eee_status = qed_rd(p_hwfn, + p_ptt, + p_hwfn->mcp_info->port_addr + + offsetof(struct public_port, eee_status)); + p_link->eee_active = !!(eee_status & EEE_ACTIVE_BIT); + val = (eee_status & EEE_LD_ADV_STATUS_MASK) >> EEE_LD_ADV_STATUS_OFFSET; + if (val & EEE_1G_ADV) + p_link->eee_adv_caps |= QED_EEE_1G_ADV; + if (val & EEE_10G_ADV) + p_link->eee_adv_caps |= QED_EEE_10G_ADV; + val = (eee_status & EEE_LP_ADV_STATUS_MASK) >> EEE_LP_ADV_STATUS_OFFSET; + if (val & EEE_1G_ADV) + p_link->eee_lp_adv_caps |= QED_EEE_1G_ADV; + if (val & EEE_10G_ADV) + p_link->eee_lp_adv_caps |= QED_EEE_10G_ADV; +} + static void qed_mcp_handle_link_change(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, bool b_reset) { @@ -1228,6 +1253,9 @@ static void qed_mcp_handle_link_change(struct qed_hwfn *p_hwfn, p_link->sfp_tx_fault = !!(status & LINK_STATUS_SFP_TX_FAULT); + if (p_hwfn->mcp_info->capabilities & FW_MB_PARAM_FEATURE_SUPPORT_EEE) + qed_mcp_read_eee_config(p_hwfn, p_ptt, p_link); + qed_link_update(p_hwfn); out: spin_unlock_bh(&p_hwfn->mcp_info->link_lock); @@ -1251,6 +1279,19 @@ int qed_mcp_set_link(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, bool b_up) phy_cfg.pause |= (params->pause.forced_tx) ? ETH_PAUSE_TX : 0; phy_cfg.adv_speed = params->speed.advertised_speeds; phy_cfg.loopback_mode = params->loopback_mode; + if (p_hwfn->mcp_info->capabilities & FW_MB_PARAM_FEATURE_SUPPORT_EEE) { + if (params->eee.enable) + phy_cfg.eee_cfg |= EEE_CFG_EEE_ENABLED; + if (params->eee.tx_lpi_enable) + phy_cfg.eee_cfg |= EEE_CFG_TX_LPI; + if (params->eee.adv_caps & QED_EEE_1G_ADV) + phy_cfg.eee_cfg |= EEE_CFG_ADV_SPEED_1G; + if (params->eee.adv_caps & QED_EEE_10G_ADV) + phy_cfg.eee_cfg |= EEE_CFG_ADV_SPEED_10G; + phy_cfg.eee_cfg |= (params->eee.tx_lpi_timer << + EEE_TX_TIMER_USEC_OFFSET) & + EEE_TX_TIMER_USEC_MASK; + } p_hwfn->b_drv_link_init = b_up; @@ -2822,3 +2863,28 @@ void qed_mcp_resc_lock_default_init(struct qed_resc_lock_params *p_lock, p_unlock->resource = resource; } } + +int qed_mcp_get_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + u32 mcp_resp; + int rc; + + rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_GET_MFW_FEATURE_SUPPORT, + 0, &mcp_resp, &p_hwfn->mcp_info->capabilities); + if (!rc) + DP_VERBOSE(p_hwfn, (QED_MSG_SP | NETIF_MSG_PROBE), + "MFW supported features: %08x\n", + p_hwfn->mcp_info->capabilities); + + return rc; +} + +int qed_mcp_set_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) +{ + u32 mcp_resp, mcp_param, features; + + features = DRV_MB_PARAM_FEATURE_SUPPORT_PORT_EEE; + + return qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_FEATURE_SUPPORT, + features, &mcp_resp, &mcp_param); +} diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index af03b3651411..c7ec2395d1ce 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -53,15 +53,25 @@ struct qed_mcp_link_pause_params { bool forced_tx; }; +enum qed_mcp_eee_mode { + QED_MCP_EEE_DISABLED, + QED_MCP_EEE_ENABLED, + QED_MCP_EEE_UNSUPPORTED +}; + struct qed_mcp_link_params { - struct qed_mcp_link_speed_params speed; - struct qed_mcp_link_pause_params pause; - u32 loopback_mode; + struct qed_mcp_link_speed_params speed; + struct qed_mcp_link_pause_params pause; + u32 loopback_mode; + struct qed_link_eee_params eee; }; struct qed_mcp_link_capabilities { u32 speed_capabilities; bool default_speed_autoneg; + enum qed_mcp_eee_mode default_eee; + u32 eee_lpi_timer; + u8 eee_speed_caps; }; struct qed_mcp_link_state { @@ -102,6 +112,9 @@ struct qed_mcp_link_state { u8 partner_adv_pause; bool sfp_tx_fault; + bool eee_active; + u8 eee_adv_caps; + u8 eee_lp_adv_caps; }; struct qed_mcp_function_info { @@ -546,6 +559,9 @@ struct qed_mcp_info { u8 *mfw_mb_shadow; u16 mfw_mb_length; u32 mcp_hist; + + /* Capabilties negotiated with the MFW */ + u32 capabilities; }; struct qed_mcp_mb_params { @@ -925,5 +941,20 @@ void qed_mcp_resc_lock_default_init(struct qed_resc_lock_params *p_lock, struct qed_resc_unlock_params *p_unlock, enum qed_resc_lock resource, bool b_is_permanent); +/** + * @brief Learn of supported MFW features; To be done during early init + * + * @param p_hwfn + * @param p_ptt + */ +int qed_mcp_get_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); +/** + * @brief Inform MFW of set of features supported by driver. Should be done + * inside the content of the LOAD_REQ. + * + * @param p_hwfn + * @param p_ptt + */ +int qed_mcp_set_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); #endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.c b/drivers/net/ethernet/qlogic/qed/qed_sriov.c index 2cfd3bd9a031..3f40b1de7957 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.c +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.c @@ -3400,6 +3400,157 @@ static void qed_iov_vf_mbx_release(struct qed_hwfn *p_hwfn, length, status); } +static void qed_iov_vf_pf_get_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *p_vf) +{ + struct qed_iov_vf_mbx *mbx = &p_vf->vf_mbx; + struct pfvf_read_coal_resp_tlv *p_resp; + struct vfpf_read_coal_req_tlv *req; + u8 status = PFVF_STATUS_FAILURE; + struct qed_vf_queue *p_queue; + struct qed_queue_cid *p_cid; + u16 coal = 0, qid, i; + bool b_is_rx; + int rc = 0; + + mbx->offset = (u8 *)mbx->reply_virt; + req = &mbx->req_virt->read_coal_req; + + qid = req->qid; + b_is_rx = req->is_rx ? true : false; + + if (b_is_rx) { + if (!qed_iov_validate_rxq(p_hwfn, p_vf, qid, + QED_IOV_VALIDATE_Q_ENABLE)) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF[%d]: Invalid Rx queue_id = %d\n", + p_vf->abs_vf_id, qid); + goto send_resp; + } + + p_cid = qed_iov_get_vf_rx_queue_cid(&p_vf->vf_queues[qid]); + rc = qed_get_rxq_coalesce(p_hwfn, p_ptt, p_cid, &coal); + if (rc) + goto send_resp; + } else { + if (!qed_iov_validate_txq(p_hwfn, p_vf, qid, + QED_IOV_VALIDATE_Q_ENABLE)) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF[%d]: Invalid Tx queue_id = %d\n", + p_vf->abs_vf_id, qid); + goto send_resp; + } + for (i = 0; i < MAX_QUEUES_PER_QZONE; i++) { + p_queue = &p_vf->vf_queues[qid]; + if ((!p_queue->cids[i].p_cid) || + (!p_queue->cids[i].b_is_tx)) + continue; + + p_cid = p_queue->cids[i].p_cid; + + rc = qed_get_txq_coalesce(p_hwfn, p_ptt, p_cid, &coal); + if (rc) + goto send_resp; + break; + } + } + + status = PFVF_STATUS_SUCCESS; + +send_resp: + p_resp = qed_add_tlv(p_hwfn, &mbx->offset, CHANNEL_TLV_COALESCE_READ, + sizeof(*p_resp)); + p_resp->coal = coal; + + qed_add_tlv(p_hwfn, &mbx->offset, CHANNEL_TLV_LIST_END, + sizeof(struct channel_list_end_tlv)); + + qed_iov_send_response(p_hwfn, p_ptt, p_vf, sizeof(*p_resp), status); +} + +static void qed_iov_vf_pf_set_coalesce(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct qed_vf_info *vf) +{ + struct qed_iov_vf_mbx *mbx = &vf->vf_mbx; + struct vfpf_update_coalesce *req; + u8 status = PFVF_STATUS_FAILURE; + struct qed_queue_cid *p_cid; + u16 rx_coal, tx_coal; + int rc = 0, i; + u16 qid; + + req = &mbx->req_virt->update_coalesce; + + rx_coal = req->rx_coal; + tx_coal = req->tx_coal; + qid = req->qid; + + if (!qed_iov_validate_rxq(p_hwfn, vf, qid, + QED_IOV_VALIDATE_Q_ENABLE) && rx_coal) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF[%d]: Invalid Rx queue_id = %d\n", + vf->abs_vf_id, qid); + goto out; + } + + if (!qed_iov_validate_txq(p_hwfn, vf, qid, + QED_IOV_VALIDATE_Q_ENABLE) && tx_coal) { + DP_VERBOSE(p_hwfn, QED_MSG_IOV, + "VF[%d]: Invalid Tx queue_id = %d\n", + vf->abs_vf_id, qid); + goto out; + } + + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF[%d]: Setting coalesce for VF rx_coal = %d, tx_coal = %d at queue = %d\n", + vf->abs_vf_id, rx_coal, tx_coal, qid); + + if (rx_coal) { + p_cid = qed_iov_get_vf_rx_queue_cid(&vf->vf_queues[qid]); + + rc = qed_set_rxq_coalesce(p_hwfn, p_ptt, rx_coal, p_cid); + if (rc) { + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF[%d]: Unable to set rx queue = %d coalesce\n", + vf->abs_vf_id, vf->vf_queues[qid].fw_rx_qid); + goto out; + } + vf->rx_coal = rx_coal; + } + + if (tx_coal) { + struct qed_vf_queue *p_queue = &vf->vf_queues[qid]; + + for (i = 0; i < MAX_QUEUES_PER_QZONE; i++) { + if (!p_queue->cids[i].p_cid) + continue; + + if (!p_queue->cids[i].b_is_tx) + continue; + + rc = qed_set_txq_coalesce(p_hwfn, p_ptt, tx_coal, + p_queue->cids[i].p_cid); + + if (rc) { + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "VF[%d]: Unable to set tx queue coalesce\n", + vf->abs_vf_id); + goto out; + } + } + vf->tx_coal = tx_coal; + } + + status = PFVF_STATUS_SUCCESS; +out: + qed_iov_prepare_resp(p_hwfn, p_ptt, vf, CHANNEL_TLV_COALESCE_UPDATE, + sizeof(struct pfvf_def_resp_tlv), status); +} static int qed_iov_vf_flr_poll_dorq(struct qed_hwfn *p_hwfn, struct qed_vf_info *p_vf, struct qed_ptt *p_ptt) @@ -3725,6 +3876,12 @@ static void qed_iov_process_mbx_req(struct qed_hwfn *p_hwfn, case CHANNEL_TLV_UPDATE_TUNN_PARAM: qed_iov_vf_mbx_update_tunn_param(p_hwfn, p_ptt, p_vf); break; + case CHANNEL_TLV_COALESCE_UPDATE: + qed_iov_vf_pf_set_coalesce(p_hwfn, p_ptt, p_vf); + break; + case CHANNEL_TLV_COALESCE_READ: + qed_iov_vf_pf_get_coalesce(p_hwfn, p_ptt, p_vf); + break; } } else if (qed_iov_tlv_supported(mbx->first_tlv.tl.type)) { DP_VERBOSE(p_hwfn, QED_MSG_IOV, diff --git a/drivers/net/ethernet/qlogic/qed/qed_sriov.h b/drivers/net/ethernet/qlogic/qed/qed_sriov.h index c2e44bce398c..3955929ba892 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_sriov.h +++ b/drivers/net/ethernet/qlogic/qed/qed_sriov.h @@ -217,6 +217,9 @@ struct qed_vf_info { u8 num_rxqs; u8 num_txqs; + u16 rx_coal; + u16 tx_coal; + u8 num_sbs; u8 num_mac_filters; diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.c b/drivers/net/ethernet/qlogic/qed/qed_vf.c index 1926d1ed439f..91b5e9f02a62 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.c +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.c @@ -1343,6 +1343,81 @@ exit: return rc; } +int qed_vf_pf_get_coalesce(struct qed_hwfn *p_hwfn, + u16 *p_coal, struct qed_queue_cid *p_cid) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct pfvf_read_coal_resp_tlv *resp; + struct vfpf_read_coal_req_tlv *req; + int rc; + + /* clear mailbox and prep header tlv */ + req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_COALESCE_READ, sizeof(*req)); + req->qid = p_cid->rel.queue_id; + req->is_rx = p_cid->b_is_rx ? 1 : 0; + + qed_add_tlv(p_hwfn, &p_iov->offset, CHANNEL_TLV_LIST_END, + sizeof(struct channel_list_end_tlv)); + resp = &p_iov->pf2vf_reply->read_coal_resp; + + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + if (rc) + goto exit; + + if (resp->hdr.status != PFVF_STATUS_SUCCESS) + goto exit; + + *p_coal = resp->coal; +exit: + qed_vf_pf_req_end(p_hwfn, rc); + + return rc; +} + +int +qed_vf_pf_set_coalesce(struct qed_hwfn *p_hwfn, + u16 rx_coal, u16 tx_coal, struct qed_queue_cid *p_cid) +{ + struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; + struct vfpf_update_coalesce *req; + struct pfvf_def_resp_tlv *resp; + int rc; + + /* clear mailbox and prep header tlv */ + req = qed_vf_pf_prep(p_hwfn, CHANNEL_TLV_COALESCE_UPDATE, sizeof(*req)); + + req->rx_coal = rx_coal; + req->tx_coal = tx_coal; + req->qid = p_cid->rel.queue_id; + + DP_VERBOSE(p_hwfn, + QED_MSG_IOV, + "Setting coalesce rx_coal = %d, tx_coal = %d at queue = %d\n", + rx_coal, tx_coal, req->qid); + + /* add list termination tlv */ + qed_add_tlv(p_hwfn, &p_iov->offset, CHANNEL_TLV_LIST_END, + sizeof(struct channel_list_end_tlv)); + + resp = &p_iov->pf2vf_reply->default_resp; + rc = qed_send_msg2pf(p_hwfn, &resp->hdr.status, sizeof(*resp)); + if (rc) + goto exit; + + if (resp->hdr.status != PFVF_STATUS_SUCCESS) + goto exit; + + if (rx_coal) + p_hwfn->cdev->rx_coalesce_usecs = rx_coal; + + if (tx_coal) + p_hwfn->cdev->tx_coalesce_usecs = tx_coal; + +exit: + qed_vf_pf_req_end(p_hwfn, rc); + return rc; +} + u16 qed_vf_get_igu_sb_id(struct qed_hwfn *p_hwfn, u16 sb_id) { struct qed_vf_iov *p_iov = p_hwfn->vf_iov_info; diff --git a/drivers/net/ethernet/qlogic/qed/qed_vf.h b/drivers/net/ethernet/qlogic/qed/qed_vf.h index 34d9b882a780..97d44dfb38ca 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_vf.h +++ b/drivers/net/ethernet/qlogic/qed/qed_vf.h @@ -497,6 +497,27 @@ struct tlv_buffer_size { u8 tlv_buffer[TLV_BUFFER_SIZE]; }; +struct vfpf_update_coalesce { + struct vfpf_first_tlv first_tlv; + u16 rx_coal; + u16 tx_coal; + u16 qid; + u8 padding[2]; +}; + +struct vfpf_read_coal_req_tlv { + struct vfpf_first_tlv first_tlv; + u16 qid; + u8 is_rx; + u8 padding[5]; +}; + +struct pfvf_read_coal_resp_tlv { + struct pfvf_tlv hdr; + u16 coal; + u8 padding[6]; +}; + union vfpf_tlvs { struct vfpf_first_tlv first_tlv; struct vfpf_acquire_tlv acquire; @@ -509,7 +530,8 @@ union vfpf_tlvs { struct vfpf_vport_update_tlv vport_update; struct vfpf_ucast_filter_tlv ucast_filter; struct vfpf_update_tunn_param_tlv tunn_param_update; - struct channel_list_end_tlv list_end; + struct vfpf_update_coalesce update_coalesce; + struct vfpf_read_coal_req_tlv read_coal_req; struct tlv_buffer_size tlv_buf_size; }; @@ -519,6 +541,7 @@ union pfvf_tlvs { struct tlv_buffer_size tlv_buf_size; struct pfvf_start_queue_resp_tlv queue_start; struct pfvf_update_tunn_param_tlv tunn_param_resp; + struct pfvf_read_coal_resp_tlv read_coal_resp; }; enum qed_bulletin_bit { @@ -624,8 +647,9 @@ enum { CHANNEL_TLV_VPORT_UPDATE_ACCEPT_ANY_VLAN, CHANNEL_TLV_VPORT_UPDATE_SGE_TPA, CHANNEL_TLV_UPDATE_TUNN_PARAM, - CHANNEL_TLV_RESERVED, + CHANNEL_TLV_COALESCE_UPDATE, CHANNEL_TLV_QID, + CHANNEL_TLV_COALESCE_READ, CHANNEL_TLV_MAX, /* Required for iterating over vport-update tlvs. @@ -677,6 +701,31 @@ struct qed_vf_iov { bool b_doorbell_bar; }; +/** + * @brief VF - Set Rx/Tx coalesce per VF's relative queue. + * Coalesce value '0' will omit the configuration. + * + * @param p_hwfn + * @param rx_coal - coalesce value in micro second for rx queue + * @param tx_coal - coalesce value in micro second for tx queue + * @param p_cid - queue cid + * + **/ +int qed_vf_pf_set_coalesce(struct qed_hwfn *p_hwfn, + u16 rx_coal, + u16 tx_coal, struct qed_queue_cid *p_cid); + +/** + * @brief VF - Get coalesce per VF's relative queue. + * + * @param p_hwfn + * @param p_coal - coalesce value in micro second for VF queues. + * @param p_cid - queue cid + * + **/ +int qed_vf_pf_get_coalesce(struct qed_hwfn *p_hwfn, + u16 *p_coal, struct qed_queue_cid *p_cid); + #ifdef CONFIG_QED_SRIOV /** * @brief Read the VF bulletin and act on it if needed diff --git a/drivers/net/ethernet/qlogic/qede/qede.h b/drivers/net/ethernet/qlogic/qede/qede.h index 4dfb238221f9..adb700512baa 100644 --- a/drivers/net/ethernet/qlogic/qede/qede.h +++ b/drivers/net/ethernet/qlogic/qede/qede.h @@ -160,6 +160,8 @@ struct qede_rdma_dev { struct qede_ptp; +#define QEDE_RFS_MAX_FLTR 256 + struct qede_dev { struct qed_dev *cdev; struct net_device *ndev; @@ -241,9 +243,7 @@ struct qede_dev { u16 vxlan_dst_port; u16 geneve_dst_port; -#ifdef CONFIG_RFS_ACCEL struct qede_arfs *arfs; -#endif bool wol_enabled; struct qede_rdma_dev rdma_info; @@ -447,16 +447,21 @@ struct qede_fastpath { #ifdef CONFIG_RFS_ACCEL int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, u16 rxq_index, u32 flow_id); +#define QEDE_SP_ARFS_CONFIG 4 +#define QEDE_SP_TASK_POLL_DELAY (5 * HZ) +#endif + void qede_process_arfs_filters(struct qede_dev *edev, bool free_fltr); void qede_poll_for_freeing_arfs_filters(struct qede_dev *edev); void qede_arfs_filter_op(void *dev, void *filter, u8 fw_rc); void qede_free_arfs(struct qede_dev *edev); int qede_alloc_arfs(struct qede_dev *edev); - -#define QEDE_SP_ARFS_CONFIG 4 -#define QEDE_SP_TASK_POLL_DELAY (5 * HZ) -#define QEDE_RFS_MAX_FLTR 256 -#endif +int qede_add_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info); +int qede_del_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info); +int qede_get_cls_rule_entry(struct qede_dev *edev, struct ethtool_rxnfc *cmd); +int qede_get_cls_rule_all(struct qede_dev *edev, struct ethtool_rxnfc *info, + u32 *rule_locs); +int qede_get_arfs_filter_count(struct qede_dev *edev); struct qede_reload_args { void (*func)(struct qede_dev *edev, struct qede_reload_args *args); diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 6a03d3e66cff..dae741270022 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -702,24 +702,62 @@ static u32 qede_get_link(struct net_device *dev) static int qede_get_coalesce(struct net_device *dev, struct ethtool_coalesce *coal) { + void *rx_handle = NULL, *tx_handle = NULL; struct qede_dev *edev = netdev_priv(dev); - u16 rxc, txc; + u16 rx_coal, tx_coal, i, rc = 0; + struct qede_fastpath *fp; + + rx_coal = QED_DEFAULT_RX_USECS; + tx_coal = QED_DEFAULT_TX_USECS; memset(coal, 0, sizeof(struct ethtool_coalesce)); - edev->ops->common->get_coalesce(edev->cdev, &rxc, &txc); - coal->rx_coalesce_usecs = rxc; - coal->tx_coalesce_usecs = txc; + __qede_lock(edev); + if (edev->state == QEDE_STATE_OPEN) { + for_each_queue(i) { + fp = &edev->fp_array[i]; - return 0; + if (fp->type & QEDE_FASTPATH_RX) { + rx_handle = fp->rxq->handle; + break; + } + } + + rc = edev->ops->get_coalesce(edev->cdev, &rx_coal, rx_handle); + if (rc) { + DP_INFO(edev, "Read Rx coalesce error\n"); + goto out; + } + + for_each_queue(i) { + fp = &edev->fp_array[i]; + if (fp->type & QEDE_FASTPATH_TX) { + tx_handle = fp->txq->handle; + break; + } + } + + rc = edev->ops->get_coalesce(edev->cdev, &tx_coal, tx_handle); + if (rc) + DP_INFO(edev, "Read Tx coalesce error\n"); + } + +out: + __qede_unlock(edev); + + coal->rx_coalesce_usecs = rx_coal; + coal->tx_coalesce_usecs = tx_coal; + + return rc; } static int qede_set_coalesce(struct net_device *dev, struct ethtool_coalesce *coal) { struct qede_dev *edev = netdev_priv(dev); + struct qede_fastpath *fp; int i, rc = 0; - u16 rxc, txc, sb_id; + u16 rxc, txc; if (!netif_running(dev)) { DP_INFO(edev, "Interface is down\n"); @@ -730,21 +768,36 @@ static int qede_set_coalesce(struct net_device *dev, coal->tx_coalesce_usecs > QED_COALESCE_MAX) { DP_INFO(edev, "Can't support requested %s coalesce value [max supported value %d]\n", - coal->rx_coalesce_usecs > QED_COALESCE_MAX ? "rx" - : "tx", - QED_COALESCE_MAX); + coal->rx_coalesce_usecs > QED_COALESCE_MAX ? "rx" : + "tx", QED_COALESCE_MAX); return -EINVAL; } rxc = (u16)coal->rx_coalesce_usecs; txc = (u16)coal->tx_coalesce_usecs; for_each_queue(i) { - sb_id = edev->fp_array[i].sb_info->igu_sb_id; - rc = edev->ops->common->set_coalesce(edev->cdev, rxc, txc, - (u16)i, sb_id); - if (rc) { - DP_INFO(edev, "Set coalesce error, rc = %d\n", rc); - return rc; + fp = &edev->fp_array[i]; + + if (edev->fp_array[i].type & QEDE_FASTPATH_RX) { + rc = edev->ops->common->set_coalesce(edev->cdev, + rxc, 0, + fp->rxq->handle); + if (rc) { + DP_INFO(edev, + "Set RX coalesce error, rc = %d\n", rc); + return rc; + } + } + + if (edev->fp_array[i].type & QEDE_FASTPATH_TX) { + rc = edev->ops->common->set_coalesce(edev->cdev, + 0, txc, + fp->txq->handle); + if (rc) { + DP_INFO(edev, + "Set TX coalesce error, rc = %d\n", rc); + return rc; + } } } @@ -1045,20 +1098,34 @@ static int qede_get_rss_flags(struct qede_dev *edev, struct ethtool_rxnfc *info) } static int qede_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, - u32 *rules __always_unused) + u32 *rule_locs) { struct qede_dev *edev = netdev_priv(dev); + int rc = 0; switch (info->cmd) { case ETHTOOL_GRXRINGS: info->data = QEDE_RSS_COUNT(edev); - return 0; + break; case ETHTOOL_GRXFH: - return qede_get_rss_flags(edev, info); + rc = qede_get_rss_flags(edev, info); + break; + case ETHTOOL_GRXCLSRLCNT: + info->rule_cnt = qede_get_arfs_filter_count(edev); + info->data = QEDE_RFS_MAX_FLTR; + break; + case ETHTOOL_GRXCLSRULE: + rc = qede_get_cls_rule_entry(edev, info); + break; + case ETHTOOL_GRXCLSRLALL: + rc = qede_get_cls_rule_all(edev, info, rule_locs); + break; default: DP_ERR(edev, "Command parameters not supported\n"); - return -EOPNOTSUPP; + rc = -EOPNOTSUPP; } + + return rc; } static int qede_set_rss_flags(struct qede_dev *edev, struct ethtool_rxnfc *info) @@ -1168,14 +1235,24 @@ static int qede_set_rss_flags(struct qede_dev *edev, struct ethtool_rxnfc *info) static int qede_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info) { struct qede_dev *edev = netdev_priv(dev); + int rc; switch (info->cmd) { case ETHTOOL_SRXFH: - return qede_set_rss_flags(edev, info); + rc = qede_set_rss_flags(edev, info); + break; + case ETHTOOL_SRXCLSRLINS: + rc = qede_add_cls_rule(edev, info); + break; + case ETHTOOL_SRXCLSRLDEL: + rc = qede_del_cls_rule(edev, info); + break; default: DP_INFO(edev, "Command parameters not supported\n"); - return -EOPNOTSUPP; + rc = -EOPNOTSUPP; } + + return rc; } static u32 qede_get_rxfh_indir_size(struct net_device *dev) @@ -1607,6 +1684,87 @@ static int qede_get_tunable(struct net_device *dev, return 0; } +static int qede_get_eee(struct net_device *dev, struct ethtool_eee *edata) +{ + struct qede_dev *edev = netdev_priv(dev); + struct qed_link_output current_link; + + memset(¤t_link, 0, sizeof(current_link)); + edev->ops->common->get_link(edev->cdev, ¤t_link); + + if (!current_link.eee_supported) { + DP_INFO(edev, "EEE is not supported\n"); + return -EOPNOTSUPP; + } + + if (current_link.eee.adv_caps & QED_EEE_1G_ADV) + edata->advertised = ADVERTISED_1000baseT_Full; + if (current_link.eee.adv_caps & QED_EEE_10G_ADV) + edata->advertised |= ADVERTISED_10000baseT_Full; + if (current_link.sup_caps & QED_EEE_1G_ADV) + edata->supported = ADVERTISED_1000baseT_Full; + if (current_link.sup_caps & QED_EEE_10G_ADV) + edata->supported |= ADVERTISED_10000baseT_Full; + if (current_link.eee.lp_adv_caps & QED_EEE_1G_ADV) + edata->lp_advertised = ADVERTISED_1000baseT_Full; + if (current_link.eee.lp_adv_caps & QED_EEE_10G_ADV) + edata->lp_advertised |= ADVERTISED_10000baseT_Full; + + edata->tx_lpi_timer = current_link.eee.tx_lpi_timer; + edata->eee_enabled = current_link.eee.enable; + edata->tx_lpi_enabled = current_link.eee.tx_lpi_enable; + edata->eee_active = current_link.eee_active; + + return 0; +} + +static int qede_set_eee(struct net_device *dev, struct ethtool_eee *edata) +{ + struct qede_dev *edev = netdev_priv(dev); + struct qed_link_output current_link; + struct qed_link_params params; + + if (!edev->ops->common->can_link_change(edev->cdev)) { + DP_INFO(edev, "Link settings are not allowed to be changed\n"); + return -EOPNOTSUPP; + } + + memset(¤t_link, 0, sizeof(current_link)); + edev->ops->common->get_link(edev->cdev, ¤t_link); + + if (!current_link.eee_supported) { + DP_INFO(edev, "EEE is not supported\n"); + return -EOPNOTSUPP; + } + + memset(¶ms, 0, sizeof(params)); + params.override_flags |= QED_LINK_OVERRIDE_EEE_CONFIG; + + if (!(edata->advertised & (ADVERTISED_1000baseT_Full | + ADVERTISED_10000baseT_Full)) || + ((edata->advertised & (ADVERTISED_1000baseT_Full | + ADVERTISED_10000baseT_Full)) != + edata->advertised)) { + DP_VERBOSE(edev, QED_MSG_DEBUG, + "Invalid advertised capabilities %d\n", + edata->advertised); + return -EINVAL; + } + + if (edata->advertised & ADVERTISED_1000baseT_Full) + params.eee.adv_caps = QED_EEE_1G_ADV; + if (edata->advertised & ADVERTISED_10000baseT_Full) + params.eee.adv_caps |= QED_EEE_10G_ADV; + params.eee.enable = edata->eee_enabled; + params.eee.tx_lpi_enable = edata->tx_lpi_enabled; + params.eee.tx_lpi_timer = edata->tx_lpi_timer; + + params.link_up = true; + edev->ops->common->set_link(edev->cdev, ¶ms); + + return 0; +} + static const struct ethtool_ops qede_ethtool_ops = { .get_link_ksettings = qede_get_link_ksettings, .set_link_ksettings = qede_set_link_ksettings, @@ -1640,6 +1798,9 @@ static const struct ethtool_ops qede_ethtool_ops = { .get_channels = qede_get_channels, .set_channels = qede_set_channels, .self_test = qede_self_test, + .get_eee = qede_get_eee, + .set_eee = qede_set_eee, + .get_tunable = qede_get_tunable, .set_tunable = qede_set_tunable, }; @@ -1650,6 +1811,8 @@ static const struct ethtool_ops qede_vf_ethtool_ops = { .get_msglevel = qede_get_msglevel, .set_msglevel = qede_set_msglevel, .get_link = qede_get_link, + .get_coalesce = qede_get_coalesce, + .set_coalesce = qede_set_coalesce, .get_ringparam = qede_get_ringparam, .set_ringparam = qede_set_ringparam, .get_strings = qede_get_strings, diff --git a/drivers/net/ethernet/qlogic/qede/qede_filter.c b/drivers/net/ethernet/qlogic/qede/qede_filter.c index f939db5bac5f..f79e36e4060a 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_filter.c +++ b/drivers/net/ethernet/qlogic/qede/qede_filter.c @@ -38,7 +38,6 @@ #include <linux/qed/qed_if.h> #include "qede.h" -#ifdef CONFIG_RFS_ACCEL struct qede_arfs_tuple { union { __be32 src_ipv4; @@ -76,10 +75,12 @@ struct qede_arfs_fltr_node { u16 next_rxq_id; bool filter_op; bool used; + u8 fw_rc; struct hlist_node node; }; struct qede_arfs { +#define QEDE_ARFS_BUCKET_HEAD(edev, idx) (&(edev)->arfs->arfs_hl_head[idx]) #define QEDE_ARFS_POLL_COUNT 100 #define QEDE_RFS_FLW_BITSHIFT (4) #define QEDE_RFS_FLW_MASK ((1 << QEDE_RFS_FLW_BITSHIFT) - 1) @@ -121,11 +122,56 @@ qede_free_arfs_filter(struct qede_dev *edev, struct qede_arfs_fltr_node *fltr) kfree(fltr); } +static int +qede_enqueue_fltr_and_config_searcher(struct qede_dev *edev, + struct qede_arfs_fltr_node *fltr, + u16 bucket_idx) +{ + fltr->mapping = dma_map_single(&edev->pdev->dev, fltr->data, + fltr->buf_len, DMA_TO_DEVICE); + if (dma_mapping_error(&edev->pdev->dev, fltr->mapping)) { + DP_NOTICE(edev, "Failed to map DMA memory for rule\n"); + qede_free_arfs_filter(edev, fltr); + return -ENOMEM; + } + + INIT_HLIST_NODE(&fltr->node); + hlist_add_head(&fltr->node, + QEDE_ARFS_BUCKET_HEAD(edev, bucket_idx)); + edev->arfs->filter_count++; + + if (edev->arfs->filter_count == 1 && !edev->arfs->enable) { + edev->ops->configure_arfs_searcher(edev->cdev, true); + edev->arfs->enable = true; + } + + return 0; +} + +static void +qede_dequeue_fltr_and_config_searcher(struct qede_dev *edev, + struct qede_arfs_fltr_node *fltr) +{ + hlist_del(&fltr->node); + dma_unmap_single(&edev->pdev->dev, fltr->mapping, + fltr->buf_len, DMA_TO_DEVICE); + + qede_free_arfs_filter(edev, fltr); + edev->arfs->filter_count--; + + if (!edev->arfs->filter_count && edev->arfs->enable) { + edev->arfs->enable = false; + edev->ops->configure_arfs_searcher(edev->cdev, false); + } +} + void qede_arfs_filter_op(void *dev, void *filter, u8 fw_rc) { struct qede_arfs_fltr_node *fltr = filter; struct qede_dev *edev = dev; + fltr->fw_rc = fw_rc; + if (fw_rc) { DP_NOTICE(edev, "Failed arfs filter configuration fw_rc=%d, flow_id=%d, sw_id=%d, src_port=%d, dst_port=%d, rxq=%d\n", @@ -185,18 +231,17 @@ void qede_process_arfs_filters(struct qede_dev *edev, bool free_fltr) if ((!test_bit(QEDE_FLTR_VALID, &fltr->state) && !fltr->used) || free_fltr) { - hlist_del(&fltr->node); - dma_unmap_single(&edev->pdev->dev, - fltr->mapping, - fltr->buf_len, DMA_TO_DEVICE); - qede_free_arfs_filter(edev, fltr); - edev->arfs->filter_count--; + qede_dequeue_fltr_and_config_searcher(edev, + fltr); } else { - if ((rps_may_expire_flow(edev->ndev, - fltr->rxq_id, - fltr->flow_id, - fltr->sw_id) || del) && - !free_fltr) + bool flow_exp = false; +#ifdef CONFIG_RFS_ACCEL + flow_exp = rps_may_expire_flow(edev->ndev, + fltr->rxq_id, + fltr->flow_id, + fltr->sw_id); +#endif + if ((flow_exp || del) && !free_fltr) qede_configure_arfs_fltr(edev, fltr, fltr->rxq_id, false); @@ -213,10 +258,12 @@ void qede_process_arfs_filters(struct qede_dev *edev, bool free_fltr) edev->arfs->enable = false; edev->ops->configure_arfs_searcher(edev->cdev, false); } +#ifdef CONFIG_RFS_ACCEL } else { set_bit(QEDE_SP_ARFS_CONFIG, &edev->sp_flags); schedule_delayed_work(&edev->sp_task, QEDE_SP_TASK_POLL_DELAY); +#endif } spin_unlock_bh(&edev->arfs->arfs_list_lock); @@ -258,25 +305,26 @@ int qede_alloc_arfs(struct qede_dev *edev) spin_lock_init(&edev->arfs->arfs_list_lock); for (i = 0; i <= QEDE_RFS_FLW_MASK; i++) - INIT_HLIST_HEAD(&edev->arfs->arfs_hl_head[i]); + INIT_HLIST_HEAD(QEDE_ARFS_BUCKET_HEAD(edev, i)); - edev->ndev->rx_cpu_rmap = alloc_irq_cpu_rmap(QEDE_RSS_COUNT(edev)); - if (!edev->ndev->rx_cpu_rmap) { + edev->arfs->arfs_fltr_bmap = vzalloc(BITS_TO_LONGS(QEDE_RFS_MAX_FLTR) * + sizeof(long)); + if (!edev->arfs->arfs_fltr_bmap) { vfree(edev->arfs); edev->arfs = NULL; return -ENOMEM; } - edev->arfs->arfs_fltr_bmap = vzalloc(BITS_TO_LONGS(QEDE_RFS_MAX_FLTR) * - sizeof(long)); - if (!edev->arfs->arfs_fltr_bmap) { - free_irq_cpu_rmap(edev->ndev->rx_cpu_rmap); - edev->ndev->rx_cpu_rmap = NULL; +#ifdef CONFIG_RFS_ACCEL + edev->ndev->rx_cpu_rmap = alloc_irq_cpu_rmap(QEDE_RSS_COUNT(edev)); + if (!edev->ndev->rx_cpu_rmap) { + vfree(edev->arfs->arfs_fltr_bmap); + edev->arfs->arfs_fltr_bmap = NULL; vfree(edev->arfs); edev->arfs = NULL; return -ENOMEM; } - +#endif return 0; } @@ -285,16 +333,19 @@ void qede_free_arfs(struct qede_dev *edev) if (!edev->arfs) return; +#ifdef CONFIG_RFS_ACCEL if (edev->ndev->rx_cpu_rmap) free_irq_cpu_rmap(edev->ndev->rx_cpu_rmap); edev->ndev->rx_cpu_rmap = NULL; +#endif vfree(edev->arfs->arfs_fltr_bmap); edev->arfs->arfs_fltr_bmap = NULL; vfree(edev->arfs); edev->arfs = NULL; } +#ifdef CONFIG_RFS_ACCEL static bool qede_compare_ip_addr(struct qede_arfs_fltr_node *tpos, const struct sk_buff *skb) { @@ -394,9 +445,8 @@ int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, spin_lock_bh(&edev->arfs->arfs_list_lock); - n = qede_arfs_htbl_key_search(&edev->arfs->arfs_hl_head[tbl_idx], + n = qede_arfs_htbl_key_search(QEDE_ARFS_BUCKET_HEAD(edev, tbl_idx), skb, ports[0], ports[1], ip_proto); - if (n) { /* Filter match */ n->next_rxq_id = rxq_index; @@ -448,23 +498,9 @@ int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, n->tuple.ip_proto = ip_proto; memcpy(n->data + ETH_HLEN, skb->data, skb_headlen(skb)); - n->mapping = dma_map_single(&edev->pdev->dev, n->data, - n->buf_len, DMA_TO_DEVICE); - if (dma_mapping_error(&edev->pdev->dev, n->mapping)) { - DP_NOTICE(edev, "Failed to map DMA memory for arfs\n"); - qede_free_arfs_filter(edev, n); - rc = -ENOMEM; + rc = qede_enqueue_fltr_and_config_searcher(edev, n, tbl_idx); + if (rc) goto ret_unlock; - } - - INIT_HLIST_NODE(&n->node); - hlist_add_head(&n->node, &edev->arfs->arfs_hl_head[tbl_idx]); - edev->arfs->filter_count++; - - if (edev->arfs->filter_count == 1 && !edev->arfs->enable) { - edev->ops->configure_arfs_searcher(edev->cdev, true); - edev->arfs->enable = true; - } qede_configure_arfs_fltr(edev, n, n->rxq_id, true); @@ -472,6 +508,7 @@ int qede_rx_flow_steer(struct net_device *dev, const struct sk_buff *skb, set_bit(QEDE_SP_ARFS_CONFIG, &edev->sp_flags); schedule_delayed_work(&edev->sp_task, 0); + return n->sw_id; ret_unlock: @@ -1263,3 +1300,371 @@ void qede_config_rx_mode(struct net_device *ndev) out: kfree(uc_macs); } + +static struct qede_arfs_fltr_node * +qede_get_arfs_fltr_by_loc(struct hlist_head *head, u32 location) +{ + struct qede_arfs_fltr_node *fltr; + + hlist_for_each_entry(fltr, head, node) + if (location == fltr->sw_id) + return fltr; + + return NULL; +} + +static bool +qede_compare_user_flow_ips(struct qede_arfs_fltr_node *tpos, + struct ethtool_rx_flow_spec *fsp, + __be16 proto) +{ + if (proto == htons(ETH_P_IP)) { + struct ethtool_tcpip4_spec *ip; + + ip = &fsp->h_u.tcp_ip4_spec; + + if (tpos->tuple.src_ipv4 == ip->ip4src && + tpos->tuple.dst_ipv4 == ip->ip4dst) + return true; + else + return false; + } else { + struct ethtool_tcpip6_spec *ip6; + struct in6_addr *src; + + ip6 = &fsp->h_u.tcp_ip6_spec; + src = &tpos->tuple.src_ipv6; + + if (!memcmp(src, &ip6->ip6src, sizeof(struct in6_addr)) && + !memcmp(&tpos->tuple.dst_ipv6, &ip6->ip6dst, + sizeof(struct in6_addr))) + return true; + else + return false; + } + return false; +} + +int qede_get_cls_rule_all(struct qede_dev *edev, struct ethtool_rxnfc *info, + u32 *rule_locs) +{ + struct qede_arfs_fltr_node *fltr; + struct hlist_head *head; + int cnt = 0, rc = 0; + + info->data = QEDE_RFS_MAX_FLTR; + + __qede_lock(edev); + + if (!edev->arfs) { + rc = -EPERM; + goto unlock; + } + + head = QEDE_ARFS_BUCKET_HEAD(edev, 0); + + hlist_for_each_entry(fltr, head, node) { + if (cnt == info->rule_cnt) { + rc = -EMSGSIZE; + goto unlock; + } + + rule_locs[cnt] = fltr->sw_id; + cnt++; + } + + info->rule_cnt = cnt; + +unlock: + __qede_unlock(edev); + return rc; +} + +int qede_get_cls_rule_entry(struct qede_dev *edev, struct ethtool_rxnfc *cmd) +{ + struct ethtool_rx_flow_spec *fsp = &cmd->fs; + struct qede_arfs_fltr_node *fltr = NULL; + int rc = 0; + + cmd->data = QEDE_RFS_MAX_FLTR; + + __qede_lock(edev); + + if (!edev->arfs) { + rc = -EPERM; + goto unlock; + } + + fltr = qede_get_arfs_fltr_by_loc(QEDE_ARFS_BUCKET_HEAD(edev, 0), + fsp->location); + if (!fltr) { + DP_NOTICE(edev, "Rule not found - location=0x%x\n", + fsp->location); + rc = -EINVAL; + goto unlock; + } + + if (fltr->tuple.eth_proto == htons(ETH_P_IP)) { + if (fltr->tuple.ip_proto == IPPROTO_TCP) + fsp->flow_type = TCP_V4_FLOW; + else + fsp->flow_type = UDP_V4_FLOW; + + fsp->h_u.tcp_ip4_spec.psrc = fltr->tuple.src_port; + fsp->h_u.tcp_ip4_spec.pdst = fltr->tuple.dst_port; + fsp->h_u.tcp_ip4_spec.ip4src = fltr->tuple.src_ipv4; + fsp->h_u.tcp_ip4_spec.ip4dst = fltr->tuple.dst_ipv4; + } else { + if (fltr->tuple.ip_proto == IPPROTO_TCP) + fsp->flow_type = TCP_V6_FLOW; + else + fsp->flow_type = UDP_V6_FLOW; + fsp->h_u.tcp_ip6_spec.psrc = fltr->tuple.src_port; + fsp->h_u.tcp_ip6_spec.pdst = fltr->tuple.dst_port; + memcpy(&fsp->h_u.tcp_ip6_spec.ip6src, + &fltr->tuple.src_ipv6, sizeof(struct in6_addr)); + memcpy(&fsp->h_u.tcp_ip6_spec.ip6dst, + &fltr->tuple.dst_ipv6, sizeof(struct in6_addr)); + } + + fsp->ring_cookie = fltr->rxq_id; + +unlock: + __qede_unlock(edev); + return rc; +} + +static int +qede_validate_and_check_flow_exist(struct qede_dev *edev, + struct ethtool_rx_flow_spec *fsp, + int *min_hlen) +{ + __be16 src_port = 0x0, dst_port = 0x0; + struct qede_arfs_fltr_node *fltr; + struct hlist_node *temp; + struct hlist_head *head; + __be16 eth_proto; + u8 ip_proto; + + if (fsp->location >= QEDE_RFS_MAX_FLTR || + fsp->ring_cookie >= QEDE_RSS_COUNT(edev)) + return -EINVAL; + + if (fsp->flow_type == TCP_V4_FLOW) { + *min_hlen += sizeof(struct iphdr) + + sizeof(struct tcphdr); + eth_proto = htons(ETH_P_IP); + ip_proto = IPPROTO_TCP; + } else if (fsp->flow_type == UDP_V4_FLOW) { + *min_hlen += sizeof(struct iphdr) + + sizeof(struct udphdr); + eth_proto = htons(ETH_P_IP); + ip_proto = IPPROTO_UDP; + } else if (fsp->flow_type == TCP_V6_FLOW) { + *min_hlen += sizeof(struct ipv6hdr) + + sizeof(struct tcphdr); + eth_proto = htons(ETH_P_IPV6); + ip_proto = IPPROTO_TCP; + } else if (fsp->flow_type == UDP_V6_FLOW) { + *min_hlen += sizeof(struct ipv6hdr) + + sizeof(struct udphdr); + eth_proto = htons(ETH_P_IPV6); + ip_proto = IPPROTO_UDP; + } else { + DP_NOTICE(edev, "Unsupported flow type = 0x%x\n", + fsp->flow_type); + return -EPROTONOSUPPORT; + } + + if (eth_proto == htons(ETH_P_IP)) { + src_port = fsp->h_u.tcp_ip4_spec.psrc; + dst_port = fsp->h_u.tcp_ip4_spec.pdst; + } else { + src_port = fsp->h_u.tcp_ip6_spec.psrc; + dst_port = fsp->h_u.tcp_ip6_spec.pdst; + } + + head = QEDE_ARFS_BUCKET_HEAD(edev, 0); + hlist_for_each_entry_safe(fltr, temp, head, node) { + if ((fltr->tuple.ip_proto == ip_proto && + fltr->tuple.eth_proto == eth_proto && + qede_compare_user_flow_ips(fltr, fsp, eth_proto) && + fltr->tuple.src_port == src_port && + fltr->tuple.dst_port == dst_port) || + fltr->sw_id == fsp->location) + return -EEXIST; + } + + return 0; +} + +static int +qede_poll_arfs_filter_config(struct qede_dev *edev, + struct qede_arfs_fltr_node *fltr) +{ + int count = QEDE_ARFS_POLL_COUNT; + + while (fltr->used && count) { + msleep(20); + count--; + } + + if (count == 0 || fltr->fw_rc) { + qede_dequeue_fltr_and_config_searcher(edev, fltr); + return -EIO; + } + + return fltr->fw_rc; +} + +int qede_add_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info) +{ + struct ethtool_rx_flow_spec *fsp = &info->fs; + struct qede_arfs_fltr_node *n; + int min_hlen = ETH_HLEN, rc; + struct ethhdr *eth; + struct iphdr *ip; + __be16 *ports; + + __qede_lock(edev); + + if (!edev->arfs) { + rc = -EPERM; + goto unlock; + } + + rc = qede_validate_and_check_flow_exist(edev, fsp, &min_hlen); + if (rc) + goto unlock; + + n = kzalloc(sizeof(*n), GFP_KERNEL); + if (!n) { + rc = -ENOMEM; + goto unlock; + } + + n->data = kzalloc(min_hlen, GFP_KERNEL); + if (!n->data) { + kfree(n); + rc = -ENOMEM; + goto unlock; + } + + n->sw_id = fsp->location; + set_bit(n->sw_id, edev->arfs->arfs_fltr_bmap); + n->buf_len = min_hlen; + n->rxq_id = fsp->ring_cookie; + n->next_rxq_id = n->rxq_id; + eth = (struct ethhdr *)n->data; + + if (info->fs.flow_type == TCP_V4_FLOW || + info->fs.flow_type == UDP_V4_FLOW) { + ports = (__be16 *)(n->data + ETH_HLEN + + sizeof(struct iphdr)); + eth->h_proto = htons(ETH_P_IP); + n->tuple.eth_proto = htons(ETH_P_IP); + n->tuple.src_ipv4 = info->fs.h_u.tcp_ip4_spec.ip4src; + n->tuple.dst_ipv4 = info->fs.h_u.tcp_ip4_spec.ip4dst; + n->tuple.src_port = info->fs.h_u.tcp_ip4_spec.psrc; + n->tuple.dst_port = info->fs.h_u.tcp_ip4_spec.pdst; + ports[0] = n->tuple.src_port; + ports[1] = n->tuple.dst_port; + ip = (struct iphdr *)(n->data + ETH_HLEN); + ip->saddr = info->fs.h_u.tcp_ip4_spec.ip4src; + ip->daddr = info->fs.h_u.tcp_ip4_spec.ip4dst; + ip->version = 0x4; + ip->ihl = 0x5; + + if (info->fs.flow_type == TCP_V4_FLOW) { + n->tuple.ip_proto = IPPROTO_TCP; + ip->protocol = IPPROTO_TCP; + } else { + n->tuple.ip_proto = IPPROTO_UDP; + ip->protocol = IPPROTO_UDP; + } + ip->tot_len = cpu_to_be16(min_hlen - ETH_HLEN); + } else { + struct ipv6hdr *ip6; + + ip6 = (struct ipv6hdr *)(n->data + ETH_HLEN); + ports = (__be16 *)(n->data + ETH_HLEN + + sizeof(struct ipv6hdr)); + eth->h_proto = htons(ETH_P_IPV6); + n->tuple.eth_proto = htons(ETH_P_IPV6); + memcpy(&n->tuple.src_ipv6, &info->fs.h_u.tcp_ip6_spec.ip6src, + sizeof(struct in6_addr)); + memcpy(&n->tuple.dst_ipv6, &info->fs.h_u.tcp_ip6_spec.ip6dst, + sizeof(struct in6_addr)); + n->tuple.src_port = info->fs.h_u.tcp_ip6_spec.psrc; + n->tuple.dst_port = info->fs.h_u.tcp_ip6_spec.pdst; + ports[0] = n->tuple.src_port; + ports[1] = n->tuple.dst_port; + memcpy(&ip6->saddr, &n->tuple.src_ipv6, + sizeof(struct in6_addr)); + memcpy(&ip6->daddr, &n->tuple.dst_ipv6, + sizeof(struct in6_addr)); + ip6->version = 0x6; + + if (info->fs.flow_type == TCP_V6_FLOW) { + n->tuple.ip_proto = IPPROTO_TCP; + ip6->nexthdr = NEXTHDR_TCP; + ip6->payload_len = cpu_to_be16(sizeof(struct tcphdr)); + } else { + n->tuple.ip_proto = IPPROTO_UDP; + ip6->nexthdr = NEXTHDR_UDP; + ip6->payload_len = cpu_to_be16(sizeof(struct udphdr)); + } + } + + rc = qede_enqueue_fltr_and_config_searcher(edev, n, 0); + if (rc) + goto unlock; + + qede_configure_arfs_fltr(edev, n, n->rxq_id, true); + rc = qede_poll_arfs_filter_config(edev, n); +unlock: + __qede_unlock(edev); + return rc; +} + +int qede_del_cls_rule(struct qede_dev *edev, struct ethtool_rxnfc *info) +{ + struct ethtool_rx_flow_spec *fsp = &info->fs; + struct qede_arfs_fltr_node *fltr = NULL; + int rc = -EPERM; + + __qede_lock(edev); + if (!edev->arfs) + goto unlock; + + fltr = qede_get_arfs_fltr_by_loc(QEDE_ARFS_BUCKET_HEAD(edev, 0), + fsp->location); + if (!fltr) + goto unlock; + + qede_configure_arfs_fltr(edev, fltr, fltr->rxq_id, false); + + rc = qede_poll_arfs_filter_config(edev, fltr); + if (rc == 0) + qede_dequeue_fltr_and_config_searcher(edev, fltr); + +unlock: + __qede_unlock(edev); + return rc; +} + +int qede_get_arfs_filter_count(struct qede_dev *edev) +{ + int count = 0; + + __qede_lock(edev); + + if (!edev->arfs) + goto unlock; + + count = edev->arfs->filter_count; + +unlock: + __qede_unlock(edev); + return count; +} diff --git a/drivers/net/ethernet/qlogic/qede/qede_main.c b/drivers/net/ethernet/qlogic/qede/qede_main.c index 06ca13dd9ddb..e5ee9f274a71 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_main.c +++ b/drivers/net/ethernet/qlogic/qede/qede_main.c @@ -873,9 +873,7 @@ static void qede_update_pf_params(struct qed_dev *cdev) */ pf_params.eth_pf_params.num_vf_cons = 48; -#ifdef CONFIG_RFS_ACCEL pf_params.eth_pf_params.num_arfs_filters = QEDE_RFS_MAX_FLTR; -#endif qed_ops->common->update_pf_params(cdev, &pf_params); } @@ -1984,12 +1982,12 @@ static void qede_unload(struct qede_dev *edev, enum qede_unload_mode mode, qede_vlan_mark_nonconfigured(edev); edev->ops->fastpath_stop(edev->cdev); -#ifdef CONFIG_RFS_ACCEL + if (!IS_VF(edev) && edev->dev_info.common.num_hwfns == 1) { qede_poll_for_freeing_arfs_filters(edev); qede_free_arfs(edev); } -#endif + /* Release the interrupts */ qede_sync_free_irqs(edev); edev->ops->common->set_fp_int(edev->cdev, 0); @@ -2041,13 +2039,12 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode, if (rc) goto err2; -#ifdef CONFIG_RFS_ACCEL if (!IS_VF(edev) && edev->dev_info.common.num_hwfns == 1) { rc = qede_alloc_arfs(edev); if (rc) DP_NOTICE(edev, "aRFS memory allocation failed\n"); } -#endif + qede_napi_add_enable(edev); DP_INFO(edev, "Napi added and enabled\n"); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c index be41e4c77b65..c48a0e2d4d7e 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c @@ -592,13 +592,9 @@ qlcnic_receive_peg_ready(struct qlcnic_adapter *adapter) } while (--retries); - if (!retries) { - dev_err(&adapter->pdev->dev, "Receive Peg initialization not " - "complete, state: 0x%x.\n", val); - return -EIO; - } - - return 0; + dev_err(&adapter->pdev->dev, "Receive Peg initialization not complete, state: 0x%x.\n", + val); + return -EIO; } int diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c index 0844b7c75767..afa10a163da1 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c @@ -1285,7 +1285,7 @@ flash_temp: int qlcnic_dump_fw(struct qlcnic_adapter *adapter) { struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; - static const struct qlcnic_dump_operations *fw_dump_ops; + const struct qlcnic_dump_operations *fw_dump_ops; struct qlcnic_83xx_dump_template_hdr *hdr_83xx; u32 entry_offset, dump, no_entries, buf_offset = 0; int i, k, ops_cnt, ops_index, dump_size = 0; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index 73027a6c06c7..287d89dd086f 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -1174,19 +1174,19 @@ static ssize_t qlcnic_83xx_sysfs_flash_write_handler(struct file *filp, return size; } -static struct device_attribute dev_attr_bridged_mode = { +static const struct device_attribute dev_attr_bridged_mode = { .attr = {.name = "bridged_mode", .mode = (S_IRUGO | S_IWUSR)}, .show = qlcnic_show_bridged_mode, .store = qlcnic_store_bridged_mode, }; -static struct device_attribute dev_attr_diag_mode = { +static const struct device_attribute dev_attr_diag_mode = { .attr = {.name = "diag_mode", .mode = (S_IRUGO | S_IWUSR)}, .show = qlcnic_show_diag_mode, .store = qlcnic_store_diag_mode, }; -static struct device_attribute dev_attr_beacon = { +static const struct device_attribute dev_attr_beacon = { .attr = {.name = "beacon", .mode = (S_IRUGO | S_IWUSR)}, .show = qlcnic_show_beacon, .store = qlcnic_store_beacon, @@ -1248,7 +1248,7 @@ static const struct bin_attribute bin_attr_pm_config = { .write = qlcnic_sysfs_write_pm_config, }; -static struct bin_attribute bin_attr_flash = { +static const struct bin_attribute bin_attr_flash = { .attr = {.name = "flash", .mode = (S_IRUGO | S_IWUSR)}, .size = 0, .read = qlcnic_83xx_sysfs_flash_read_handler, diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c index e3223f2fe2ff..fe2599b83d09 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_dbg.c @@ -144,42 +144,23 @@ static int ql_get_serdes_regs(struct ql_adapter *qdev, xaui_direct_valid = xaui_indirect_valid = 1; /* The XAUI needs to be read out per port */ - if (qdev->func & 1) { - /* We are NIC 2 */ - status = ql_read_other_func_serdes_reg(qdev, - XG_SERDES_XAUI_HSS_PCS_START, &temp); - if (status) - temp = XG_SERDES_ADDR_XAUI_PWR_DOWN; - if ((temp & XG_SERDES_ADDR_XAUI_PWR_DOWN) == - XG_SERDES_ADDR_XAUI_PWR_DOWN) - xaui_indirect_valid = 0; + status = ql_read_other_func_serdes_reg(qdev, + XG_SERDES_XAUI_HSS_PCS_START, &temp); + if (status) + temp = XG_SERDES_ADDR_XAUI_PWR_DOWN; - status = ql_read_serdes_reg(qdev, - XG_SERDES_XAUI_HSS_PCS_START, &temp); - if (status) - temp = XG_SERDES_ADDR_XAUI_PWR_DOWN; + if ((temp & XG_SERDES_ADDR_XAUI_PWR_DOWN) == + XG_SERDES_ADDR_XAUI_PWR_DOWN) + xaui_indirect_valid = 0; - if ((temp & XG_SERDES_ADDR_XAUI_PWR_DOWN) == - XG_SERDES_ADDR_XAUI_PWR_DOWN) - xaui_direct_valid = 0; - } else { - /* We are NIC 1 */ - status = ql_read_other_func_serdes_reg(qdev, - XG_SERDES_XAUI_HSS_PCS_START, &temp); - if (status) - temp = XG_SERDES_ADDR_XAUI_PWR_DOWN; - if ((temp & XG_SERDES_ADDR_XAUI_PWR_DOWN) == - XG_SERDES_ADDR_XAUI_PWR_DOWN) - xaui_indirect_valid = 0; + status = ql_read_serdes_reg(qdev, XG_SERDES_XAUI_HSS_PCS_START, &temp); - status = ql_read_serdes_reg(qdev, - XG_SERDES_XAUI_HSS_PCS_START, &temp); - if (status) - temp = XG_SERDES_ADDR_XAUI_PWR_DOWN; - if ((temp & XG_SERDES_ADDR_XAUI_PWR_DOWN) == - XG_SERDES_ADDR_XAUI_PWR_DOWN) - xaui_direct_valid = 0; - } + if (status) + temp = XG_SERDES_ADDR_XAUI_PWR_DOWN; + + if ((temp & XG_SERDES_ADDR_XAUI_PWR_DOWN) == + XG_SERDES_ADDR_XAUI_PWR_DOWN) + xaui_direct_valid = 0; /* * XFI register is shared so only need to read one diff --git a/drivers/net/ethernet/qualcomm/Kconfig b/drivers/net/ethernet/qualcomm/Kconfig index 877675a27b9f..f5200712718d 100644 --- a/drivers/net/ethernet/qualcomm/Kconfig +++ b/drivers/net/ethernet/qualcomm/Kconfig @@ -59,4 +59,6 @@ config QCOM_EMAC low power, Receive-Side Scaling (RSS), and IEEE 1588-2008 Precision Clock Synchronization Protocol. +source "drivers/net/ethernet/qualcomm/rmnet/Kconfig" + endif # NET_VENDOR_QUALCOMM diff --git a/drivers/net/ethernet/qualcomm/Makefile b/drivers/net/ethernet/qualcomm/Makefile index 92fa7c4da90a..1847350f48a7 100644 --- a/drivers/net/ethernet/qualcomm/Makefile +++ b/drivers/net/ethernet/qualcomm/Makefile @@ -9,3 +9,5 @@ obj-$(CONFIG_QCA7000_UART) += qcauart.o qcauart-objs := qca_uart.o obj-y += emac/ + +obj-$(CONFIG_RMNET) += rmnet/ diff --git a/drivers/net/ethernet/qualcomm/rmnet/Kconfig b/drivers/net/ethernet/qualcomm/rmnet/Kconfig new file mode 100644 index 000000000000..6e2587af47a4 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/Kconfig @@ -0,0 +1,12 @@ +# +# RMNET MAP driver +# + +menuconfig RMNET + tristate "RmNet MAP driver" + default n + ---help--- + If you select this, you will enable the RMNET module which is used + for handling data in the multiplexing and aggregation protocol (MAP) + format in the embedded data path. RMNET devices can be attached to + any IP mode physical device. diff --git a/drivers/net/ethernet/qualcomm/rmnet/Makefile b/drivers/net/ethernet/qualcomm/rmnet/Makefile new file mode 100644 index 000000000000..01bddf207cac --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the RMNET module +# + +rmnet-y := rmnet_config.o +rmnet-y += rmnet_vnd.o +rmnet-y += rmnet_handlers.o +rmnet-y += rmnet_map_data.o +rmnet-y += rmnet_map_command.o +obj-$(CONFIG_RMNET) += rmnet.o diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c new file mode 100644 index 000000000000..98f22551eb45 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.c @@ -0,0 +1,356 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * RMNET configuration engine + * + */ + +#include <net/sock.h> +#include <linux/module.h> +#include <linux/netlink.h> +#include <linux/netdevice.h> +#include "rmnet_config.h" +#include "rmnet_handlers.h" +#include "rmnet_vnd.h" +#include "rmnet_private.h" + +/* Locking scheme - + * The shared resource which needs to be protected is realdev->rx_handler_data. + * For the writer path, this is using rtnl_lock(). The writer paths are + * rmnet_newlink(), rmnet_dellink() and rmnet_force_unassociate_device(). These + * paths are already called with rtnl_lock() acquired in. There is also an + * ASSERT_RTNL() to ensure that we are calling with rtnl acquired. For + * dereference here, we will need to use rtnl_dereference(). Dev list writing + * needs to happen with rtnl_lock() acquired for netdev_master_upper_dev_link(). + * For the reader path, the real_dev->rx_handler_data is called in the TX / RX + * path. We only need rcu_read_lock() for these scenarios. In these cases, + * the rcu_read_lock() is held in __dev_queue_xmit() and + * netif_receive_skb_internal(), so readers need to use rcu_dereference_rtnl() + * to get the relevant information. For dev list reading, we again acquire + * rcu_read_lock() in rmnet_dellink() for netdev_master_upper_dev_get_rcu(). + * We also use unregister_netdevice_many() to free all rmnet devices in + * rmnet_force_unassociate_device() so we dont lose the rtnl_lock() and free in + * same context. + */ + +/* Local Definitions and Declarations */ + +struct rmnet_walk_data { + struct net_device *real_dev; + struct list_head *head; + struct rmnet_port *port; +}; + +static int rmnet_is_real_dev_registered(const struct net_device *real_dev) +{ + rx_handler_func_t *rx_handler; + + rx_handler = rcu_dereference(real_dev->rx_handler); + return (rx_handler == rmnet_rx_handler); +} + +/* Needs rtnl lock */ +static struct rmnet_port* +rmnet_get_port_rtnl(const struct net_device *real_dev) +{ + return rtnl_dereference(real_dev->rx_handler_data); +} + +static struct rmnet_endpoint* +rmnet_get_endpoint(struct net_device *dev, int config_id) +{ + struct rmnet_endpoint *ep; + struct rmnet_port *port; + + if (!rmnet_is_real_dev_registered(dev)) { + ep = rmnet_vnd_get_endpoint(dev); + } else { + port = rmnet_get_port_rtnl(dev); + + ep = &port->muxed_ep[config_id]; + } + + return ep; +} + +static int rmnet_unregister_real_device(struct net_device *real_dev, + struct rmnet_port *port) +{ + if (port->nr_rmnet_devs) + return -EINVAL; + + kfree(port); + + netdev_rx_handler_unregister(real_dev); + + /* release reference on real_dev */ + dev_put(real_dev); + + netdev_dbg(real_dev, "Removed from rmnet\n"); + return 0; +} + +static int rmnet_register_real_device(struct net_device *real_dev) +{ + struct rmnet_port *port; + int rc; + + ASSERT_RTNL(); + + if (rmnet_is_real_dev_registered(real_dev)) + return 0; + + port = kzalloc(sizeof(*port), GFP_ATOMIC); + if (!port) + return -ENOMEM; + + port->dev = real_dev; + rc = netdev_rx_handler_register(real_dev, rmnet_rx_handler, port); + if (rc) { + kfree(port); + return -EBUSY; + } + + /* hold on to real dev for MAP data */ + dev_hold(real_dev); + + netdev_dbg(real_dev, "registered with rmnet\n"); + return 0; +} + +static void rmnet_set_endpoint_config(struct net_device *dev, + u8 mux_id, u8 rmnet_mode, + struct net_device *egress_dev) +{ + struct rmnet_endpoint *ep; + + netdev_dbg(dev, "id %d mode %d dev %s\n", + mux_id, rmnet_mode, egress_dev->name); + + ep = rmnet_get_endpoint(dev, mux_id); + /* This config is cleared on every set, so its ok to not + * clear it on a device delete. + */ + memset(ep, 0, sizeof(struct rmnet_endpoint)); + ep->rmnet_mode = rmnet_mode; + ep->egress_dev = egress_dev; + ep->mux_id = mux_id; +} + +static int rmnet_newlink(struct net *src_net, struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + int ingress_format = RMNET_INGRESS_FORMAT_DEMUXING | + RMNET_INGRESS_FORMAT_DEAGGREGATION | + RMNET_INGRESS_FORMAT_MAP; + int egress_format = RMNET_EGRESS_FORMAT_MUXING | + RMNET_EGRESS_FORMAT_MAP; + struct net_device *real_dev; + int mode = RMNET_EPMODE_VND; + struct rmnet_port *port; + int err = 0; + u16 mux_id; + + real_dev = __dev_get_by_index(src_net, nla_get_u32(tb[IFLA_LINK])); + if (!real_dev || !dev) + return -ENODEV; + + if (!data[IFLA_VLAN_ID]) + return -EINVAL; + + mux_id = nla_get_u16(data[IFLA_VLAN_ID]); + + err = rmnet_register_real_device(real_dev); + if (err) + goto err0; + + port = rmnet_get_port_rtnl(real_dev); + err = rmnet_vnd_newlink(mux_id, dev, port, real_dev); + if (err) + goto err1; + + err = netdev_master_upper_dev_link(dev, real_dev, NULL, NULL); + if (err) + goto err2; + + netdev_dbg(dev, "data format [ingress 0x%08X] [egress 0x%08X]\n", + ingress_format, egress_format); + port->egress_data_format = egress_format; + port->ingress_data_format = ingress_format; + + rmnet_set_endpoint_config(real_dev, mux_id, mode, dev); + rmnet_set_endpoint_config(dev, mux_id, mode, real_dev); + return 0; + +err2: + rmnet_vnd_dellink(mux_id, port); +err1: + rmnet_unregister_real_device(real_dev, port); +err0: + return err; +} + +static void rmnet_dellink(struct net_device *dev, struct list_head *head) +{ + struct net_device *real_dev; + struct rmnet_port *port; + u8 mux_id; + + rcu_read_lock(); + real_dev = netdev_master_upper_dev_get_rcu(dev); + rcu_read_unlock(); + + if (!real_dev || !rmnet_is_real_dev_registered(real_dev)) + return; + + port = rmnet_get_port_rtnl(real_dev); + + mux_id = rmnet_vnd_get_mux(dev); + rmnet_vnd_dellink(mux_id, port); + netdev_upper_dev_unlink(dev, real_dev); + rmnet_unregister_real_device(real_dev, port); + + unregister_netdevice_queue(dev, head); +} + +static int rmnet_dev_walk_unreg(struct net_device *rmnet_dev, void *data) +{ + struct rmnet_walk_data *d = data; + u8 mux_id; + + mux_id = rmnet_vnd_get_mux(rmnet_dev); + + rmnet_vnd_dellink(mux_id, d->port); + netdev_upper_dev_unlink(rmnet_dev, d->real_dev); + unregister_netdevice_queue(rmnet_dev, d->head); + + return 0; +} + +static void rmnet_force_unassociate_device(struct net_device *dev) +{ + struct net_device *real_dev = dev; + struct rmnet_walk_data d; + struct rmnet_port *port; + LIST_HEAD(list); + + if (!rmnet_is_real_dev_registered(real_dev)) + return; + + ASSERT_RTNL(); + + d.real_dev = real_dev; + d.head = &list; + + port = rmnet_get_port_rtnl(dev); + d.port = port; + + rcu_read_lock(); + netdev_walk_all_lower_dev_rcu(real_dev, rmnet_dev_walk_unreg, &d); + rcu_read_unlock(); + unregister_netdevice_many(&list); + + rmnet_unregister_real_device(real_dev, port); +} + +static int rmnet_config_notify_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct net_device *dev = netdev_notifier_info_to_dev(data); + + if (!dev) + return NOTIFY_DONE; + + switch (event) { + case NETDEV_UNREGISTER: + netdev_dbg(dev, "Kernel unregister\n"); + rmnet_force_unassociate_device(dev); + break; + + default: + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block rmnet_dev_notifier __read_mostly = { + .notifier_call = rmnet_config_notify_cb, +}; + +static int rmnet_rtnl_validate(struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + u16 mux_id; + + if (!data || !data[IFLA_VLAN_ID]) + return -EINVAL; + + mux_id = nla_get_u16(data[IFLA_VLAN_ID]); + if (mux_id > (RMNET_MAX_LOGICAL_EP - 1)) + return -ERANGE; + + return 0; +} + +static size_t rmnet_get_size(const struct net_device *dev) +{ + return nla_total_size(2); /* IFLA_VLAN_ID */ +} + +struct rtnl_link_ops rmnet_link_ops __read_mostly = { + .kind = "rmnet", + .maxtype = __IFLA_VLAN_MAX, + .priv_size = sizeof(struct rmnet_priv), + .setup = rmnet_vnd_setup, + .validate = rmnet_rtnl_validate, + .newlink = rmnet_newlink, + .dellink = rmnet_dellink, + .get_size = rmnet_get_size, +}; + +/* Needs either rcu_read_lock() or rtnl lock */ +struct rmnet_port *rmnet_get_port(struct net_device *real_dev) +{ + if (rmnet_is_real_dev_registered(real_dev)) + return rcu_dereference_rtnl(real_dev->rx_handler_data); + else + return NULL; +} + +/* Startup/Shutdown */ + +static int __init rmnet_init(void) +{ + int rc; + + rc = register_netdevice_notifier(&rmnet_dev_notifier); + if (rc != 0) + return rc; + + rc = rtnl_link_register(&rmnet_link_ops); + if (rc != 0) { + unregister_netdevice_notifier(&rmnet_dev_notifier); + return rc; + } + return rc; +} + +static void __exit rmnet_exit(void) +{ + unregister_netdevice_notifier(&rmnet_dev_notifier); + rtnl_link_unregister(&rmnet_link_ops); +} + +module_init(rmnet_init) +module_exit(rmnet_exit) +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h new file mode 100644 index 000000000000..dde4e9f14f4a --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_config.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * RMNET Data configuration engine + * + */ + +#include <linux/skbuff.h> + +#ifndef _RMNET_CONFIG_H_ +#define _RMNET_CONFIG_H_ + +#define RMNET_MAX_LOGICAL_EP 255 + +/* Information about the next device to deliver the packet to. + * Exact usage of this parameter depends on the rmnet_mode. + */ +struct rmnet_endpoint { + u8 rmnet_mode; + u8 mux_id; + struct net_device *egress_dev; +}; + +/* One instance of this structure is instantiated for each real_dev associated + * with rmnet. + */ +struct rmnet_port { + struct net_device *dev; + struct rmnet_endpoint local_ep; + struct rmnet_endpoint muxed_ep[RMNET_MAX_LOGICAL_EP]; + u32 ingress_data_format; + u32 egress_data_format; + struct net_device *rmnet_devices[RMNET_MAX_LOGICAL_EP]; + u8 nr_rmnet_devs; +}; + +extern struct rtnl_link_ops rmnet_link_ops; + +struct rmnet_priv { + struct rmnet_endpoint local_ep; + u8 mux_id; + struct net_device *real_dev; +}; + +struct rmnet_port *rmnet_get_port(struct net_device *real_dev); + +#endif /* _RMNET_CONFIG_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c new file mode 100644 index 000000000000..540c7622dcb1 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.c @@ -0,0 +1,271 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * RMNET Data ingress/egress handler + * + */ + +#include <linux/netdevice.h> +#include <linux/netdev_features.h> +#include "rmnet_private.h" +#include "rmnet_config.h" +#include "rmnet_vnd.h" +#include "rmnet_map.h" +#include "rmnet_handlers.h" + +#define RMNET_IP_VERSION_4 0x40 +#define RMNET_IP_VERSION_6 0x60 + +/* Helper Functions */ + +static void rmnet_set_skb_proto(struct sk_buff *skb) +{ + switch (skb->data[0] & 0xF0) { + case RMNET_IP_VERSION_4: + skb->protocol = htons(ETH_P_IP); + break; + case RMNET_IP_VERSION_6: + skb->protocol = htons(ETH_P_IPV6); + break; + default: + skb->protocol = htons(ETH_P_MAP); + break; + } +} + +/* Generic handler */ + +static rx_handler_result_t +rmnet_bridge_handler(struct sk_buff *skb, struct rmnet_endpoint *ep) +{ + if (!ep->egress_dev) + kfree_skb(skb); + else + rmnet_egress_handler(skb, ep); + + return RX_HANDLER_CONSUMED; +} + +static rx_handler_result_t +rmnet_deliver_skb(struct sk_buff *skb, struct rmnet_endpoint *ep) +{ + switch (ep->rmnet_mode) { + case RMNET_EPMODE_NONE: + return RX_HANDLER_PASS; + + case RMNET_EPMODE_BRIDGE: + return rmnet_bridge_handler(skb, ep); + + case RMNET_EPMODE_VND: + skb_reset_transport_header(skb); + skb_reset_network_header(skb); + rmnet_vnd_rx_fixup(skb, skb->dev); + + skb->pkt_type = PACKET_HOST; + skb_set_mac_header(skb, 0); + netif_receive_skb(skb); + return RX_HANDLER_CONSUMED; + + default: + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } +} + +static rx_handler_result_t +rmnet_ingress_deliver_packet(struct sk_buff *skb, + struct rmnet_port *port) +{ + if (!port) { + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } + + skb->dev = port->local_ep.egress_dev; + + return rmnet_deliver_skb(skb, &port->local_ep); +} + +/* MAP handler */ + +static rx_handler_result_t +__rmnet_map_ingress_handler(struct sk_buff *skb, + struct rmnet_port *port) +{ + struct rmnet_endpoint *ep; + u8 mux_id; + u16 len; + + if (RMNET_MAP_GET_CD_BIT(skb)) { + if (port->ingress_data_format + & RMNET_INGRESS_FORMAT_MAP_COMMANDS) + return rmnet_map_command(skb, port); + + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } + + mux_id = RMNET_MAP_GET_MUX_ID(skb); + len = RMNET_MAP_GET_LENGTH(skb) - RMNET_MAP_GET_PAD(skb); + + if (mux_id >= RMNET_MAX_LOGICAL_EP) { + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } + + ep = &port->muxed_ep[mux_id]; + + if (port->ingress_data_format & RMNET_INGRESS_FORMAT_DEMUXING) + skb->dev = ep->egress_dev; + + /* Subtract MAP header */ + skb_pull(skb, sizeof(struct rmnet_map_header)); + skb_trim(skb, len); + rmnet_set_skb_proto(skb); + return rmnet_deliver_skb(skb, ep); +} + +static rx_handler_result_t +rmnet_map_ingress_handler(struct sk_buff *skb, + struct rmnet_port *port) +{ + struct sk_buff *skbn; + int rc; + + if (port->ingress_data_format & RMNET_INGRESS_FORMAT_DEAGGREGATION) { + while ((skbn = rmnet_map_deaggregate(skb)) != NULL) + __rmnet_map_ingress_handler(skbn, port); + + consume_skb(skb); + rc = RX_HANDLER_CONSUMED; + } else { + rc = __rmnet_map_ingress_handler(skb, port); + } + + return rc; +} + +static int rmnet_map_egress_handler(struct sk_buff *skb, + struct rmnet_port *port, + struct rmnet_endpoint *ep, + struct net_device *orig_dev) +{ + int required_headroom, additional_header_len; + struct rmnet_map_header *map_header; + + additional_header_len = 0; + required_headroom = sizeof(struct rmnet_map_header); + + if (skb_headroom(skb) < required_headroom) { + if (pskb_expand_head(skb, required_headroom, 0, GFP_KERNEL)) + return RMNET_MAP_CONSUMED; + } + + map_header = rmnet_map_add_map_header(skb, additional_header_len, 0); + if (!map_header) + return RMNET_MAP_CONSUMED; + + if (port->egress_data_format & RMNET_EGRESS_FORMAT_MUXING) { + if (ep->mux_id == 0xff) + map_header->mux_id = 0; + else + map_header->mux_id = ep->mux_id; + } + + skb->protocol = htons(ETH_P_MAP); + + return RMNET_MAP_SUCCESS; +} + +/* Ingress / Egress Entry Points */ + +/* Processes packet as per ingress data format for receiving device. Logical + * endpoint is determined from packet inspection. Packet is then sent to the + * egress device listed in the logical endpoint configuration. + */ +rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb) +{ + struct rmnet_port *port; + struct sk_buff *skb = *pskb; + struct net_device *dev; + int rc; + + if (!skb) + return RX_HANDLER_CONSUMED; + + dev = skb->dev; + port = rmnet_get_port(dev); + + if (port->ingress_data_format & RMNET_INGRESS_FORMAT_MAP) { + rc = rmnet_map_ingress_handler(skb, port); + } else { + switch (ntohs(skb->protocol)) { + case ETH_P_MAP: + if (port->local_ep.rmnet_mode == + RMNET_EPMODE_BRIDGE) { + rc = rmnet_ingress_deliver_packet(skb, port); + } else { + kfree_skb(skb); + rc = RX_HANDLER_CONSUMED; + } + break; + + case ETH_P_IP: + case ETH_P_IPV6: + rc = rmnet_ingress_deliver_packet(skb, port); + break; + + default: + rc = RX_HANDLER_PASS; + } + } + + return rc; +} + +/* Modifies packet as per logical endpoint configuration and egress data format + * for egress device configured in logical endpoint. Packet is then transmitted + * on the egress device. + */ +void rmnet_egress_handler(struct sk_buff *skb, + struct rmnet_endpoint *ep) +{ + struct net_device *orig_dev; + struct rmnet_port *port; + + orig_dev = skb->dev; + skb->dev = ep->egress_dev; + + port = rmnet_get_port(skb->dev); + if (!port) { + kfree_skb(skb); + return; + } + + if (port->egress_data_format & RMNET_EGRESS_FORMAT_MAP) { + switch (rmnet_map_egress_handler(skb, port, ep, orig_dev)) { + case RMNET_MAP_CONSUMED: + return; + + case RMNET_MAP_SUCCESS: + break; + + default: + kfree_skb(skb); + return; + } + } + + if (ep->rmnet_mode == RMNET_EPMODE_VND) + rmnet_vnd_tx_fixup(skb, orig_dev); + + dev_queue_xmit(skb); +} diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h new file mode 100644 index 000000000000..f2638cf5693c --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_handlers.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2013, 2016-2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * RMNET Data ingress/egress handler + * + */ + +#ifndef _RMNET_HANDLERS_H_ +#define _RMNET_HANDLERS_H_ + +#include "rmnet_config.h" + +void rmnet_egress_handler(struct sk_buff *skb, + struct rmnet_endpoint *ep); + +rx_handler_result_t rmnet_rx_handler(struct sk_buff **pskb); + +#endif /* _RMNET_HANDLERS_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h new file mode 100644 index 000000000000..ce2302c25b12 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map.h @@ -0,0 +1,86 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _RMNET_MAP_H_ +#define _RMNET_MAP_H_ + +struct rmnet_map_control_command { + u8 command_name; + u8 cmd_type:2; + u8 reserved:6; + u16 reserved2; + u32 transaction_id; + union { + struct { + u16 ip_family:2; + u16 reserved:14; + u16 flow_control_seq_num; + u32 qos_id; + } flow_control; + u8 data[0]; + }; +} __aligned(1); + +enum rmnet_map_results { + RMNET_MAP_SUCCESS, + RMNET_MAP_CONSUMED, + RMNET_MAP_GENERAL_FAILURE, + RMNET_MAP_NOT_ENABLED, + RMNET_MAP_FAILED_AGGREGATION, + RMNET_MAP_FAILED_MUX +}; + +enum rmnet_map_commands { + RMNET_MAP_COMMAND_NONE, + RMNET_MAP_COMMAND_FLOW_DISABLE, + RMNET_MAP_COMMAND_FLOW_ENABLE, + /* These should always be the last 2 elements */ + RMNET_MAP_COMMAND_UNKNOWN, + RMNET_MAP_COMMAND_ENUM_LENGTH +}; + +struct rmnet_map_header { + u8 pad_len:6; + u8 reserved_bit:1; + u8 cd_bit:1; + u8 mux_id; + u16 pkt_len; +} __aligned(1); + +#define RMNET_MAP_GET_MUX_ID(Y) (((struct rmnet_map_header *) \ + (Y)->data)->mux_id) +#define RMNET_MAP_GET_CD_BIT(Y) (((struct rmnet_map_header *) \ + (Y)->data)->cd_bit) +#define RMNET_MAP_GET_PAD(Y) (((struct rmnet_map_header *) \ + (Y)->data)->pad_len) +#define RMNET_MAP_GET_CMD_START(Y) ((struct rmnet_map_control_command *) \ + ((Y)->data + \ + sizeof(struct rmnet_map_header))) +#define RMNET_MAP_GET_LENGTH(Y) (ntohs(((struct rmnet_map_header *) \ + (Y)->data)->pkt_len)) + +#define RMNET_MAP_COMMAND_REQUEST 0 +#define RMNET_MAP_COMMAND_ACK 1 +#define RMNET_MAP_COMMAND_UNSUPPORTED 2 +#define RMNET_MAP_COMMAND_INVALID 3 + +#define RMNET_MAP_NO_PAD_BYTES 0 +#define RMNET_MAP_ADD_PAD_BYTES 1 + +u8 rmnet_map_demultiplex(struct sk_buff *skb); +struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb); +struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, + int hdrlen, int pad); +rx_handler_result_t rmnet_map_command(struct sk_buff *skb, + struct rmnet_port *port); + +#endif /* _RMNET_MAP_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c new file mode 100644 index 000000000000..d1ea5e21b982 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_command.c @@ -0,0 +1,106 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/netdevice.h> +#include "rmnet_config.h" +#include "rmnet_map.h" +#include "rmnet_private.h" +#include "rmnet_vnd.h" + +static u8 rmnet_map_do_flow_control(struct sk_buff *skb, + struct rmnet_port *rdinfo, + int enable) +{ + struct rmnet_map_control_command *cmd; + struct rmnet_endpoint *ep; + struct net_device *vnd; + u16 ip_family; + u16 fc_seq; + u32 qos_id; + u8 mux_id; + int r; + + mux_id = RMNET_MAP_GET_MUX_ID(skb); + cmd = RMNET_MAP_GET_CMD_START(skb); + + if (mux_id >= RMNET_MAX_LOGICAL_EP) { + kfree_skb(skb); + return RX_HANDLER_CONSUMED; + } + + ep = &rdinfo->muxed_ep[mux_id]; + vnd = ep->egress_dev; + + ip_family = cmd->flow_control.ip_family; + fc_seq = ntohs(cmd->flow_control.flow_control_seq_num); + qos_id = ntohl(cmd->flow_control.qos_id); + + /* Ignore the ip family and pass the sequence number for both v4 and v6 + * sequence. User space does not support creating dedicated flows for + * the 2 protocols + */ + r = rmnet_vnd_do_flow_control(vnd, enable); + if (r) { + kfree_skb(skb); + return RMNET_MAP_COMMAND_UNSUPPORTED; + } else { + return RMNET_MAP_COMMAND_ACK; + } +} + +static void rmnet_map_send_ack(struct sk_buff *skb, + unsigned char type) +{ + struct rmnet_map_control_command *cmd; + int xmit_status; + + skb->protocol = htons(ETH_P_MAP); + + cmd = RMNET_MAP_GET_CMD_START(skb); + cmd->cmd_type = type & 0x03; + + netif_tx_lock(skb->dev); + xmit_status = skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev); + netif_tx_unlock(skb->dev); +} + +/* Process MAP command frame and send N/ACK message as appropriate. Message cmd + * name is decoded here and appropriate handler is called. + */ +rx_handler_result_t rmnet_map_command(struct sk_buff *skb, + struct rmnet_port *port) +{ + struct rmnet_map_control_command *cmd; + unsigned char command_name; + unsigned char rc = 0; + + cmd = RMNET_MAP_GET_CMD_START(skb); + command_name = cmd->command_name; + + switch (command_name) { + case RMNET_MAP_COMMAND_FLOW_ENABLE: + rc = rmnet_map_do_flow_control(skb, port, 1); + break; + + case RMNET_MAP_COMMAND_FLOW_DISABLE: + rc = rmnet_map_do_flow_control(skb, port, 0); + break; + + default: + rc = RMNET_MAP_COMMAND_UNSUPPORTED; + kfree_skb(skb); + break; + } + if (rc == RMNET_MAP_COMMAND_ACK) + rmnet_map_send_ack(skb, rc); + return RX_HANDLER_CONSUMED; +} diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c new file mode 100644 index 000000000000..557c9bf1a469 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_map_data.c @@ -0,0 +1,104 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * RMNET Data MAP protocol + * + */ + +#include <linux/netdevice.h> +#include "rmnet_config.h" +#include "rmnet_map.h" +#include "rmnet_private.h" + +#define RMNET_MAP_DEAGGR_SPACING 64 +#define RMNET_MAP_DEAGGR_HEADROOM (RMNET_MAP_DEAGGR_SPACING / 2) + +/* Adds MAP header to front of skb->data + * Padding is calculated and set appropriately in MAP header. Mux ID is + * initialized to 0. + */ +struct rmnet_map_header *rmnet_map_add_map_header(struct sk_buff *skb, + int hdrlen, int pad) +{ + struct rmnet_map_header *map_header; + u32 padding, map_datalen; + u8 *padbytes; + + if (skb_headroom(skb) < sizeof(struct rmnet_map_header)) + return NULL; + + map_datalen = skb->len - hdrlen; + map_header = (struct rmnet_map_header *) + skb_push(skb, sizeof(struct rmnet_map_header)); + memset(map_header, 0, sizeof(struct rmnet_map_header)); + + if (pad == RMNET_MAP_NO_PAD_BYTES) { + map_header->pkt_len = htons(map_datalen); + return map_header; + } + + padding = ALIGN(map_datalen, 4) - map_datalen; + + if (padding == 0) + goto done; + + if (skb_tailroom(skb) < padding) + return NULL; + + padbytes = (u8 *)skb_put(skb, padding); + memset(padbytes, 0, padding); + +done: + map_header->pkt_len = htons(map_datalen + padding); + map_header->pad_len = padding & 0x3F; + + return map_header; +} + +/* Deaggregates a single packet + * A whole new buffer is allocated for each portion of an aggregated frame. + * Caller should keep calling deaggregate() on the source skb until 0 is + * returned, indicating that there are no more packets to deaggregate. Caller + * is responsible for freeing the original skb. + */ +struct sk_buff *rmnet_map_deaggregate(struct sk_buff *skb) +{ + struct rmnet_map_header *maph; + struct sk_buff *skbn; + u32 packet_len; + + if (skb->len == 0) + return NULL; + + maph = (struct rmnet_map_header *)skb->data; + packet_len = ntohs(maph->pkt_len) + sizeof(struct rmnet_map_header); + + if (((int)skb->len - (int)packet_len) < 0) + return NULL; + + skbn = alloc_skb(packet_len + RMNET_MAP_DEAGGR_SPACING, GFP_ATOMIC); + if (!skbn) + return NULL; + + skbn->dev = skb->dev; + skb_reserve(skbn, RMNET_MAP_DEAGGR_HEADROOM); + skb_put(skbn, packet_len); + memcpy(skbn->data, skb->data, packet_len); + skb_pull(skb, packet_len); + + /* Some hardware can send us empty frames. Catch them */ + if (ntohs(maph->pkt_len) == 0) { + kfree_skb(skb); + return NULL; + } + + return skbn; +} diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h new file mode 100644 index 000000000000..7967198fdd90 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_private.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2013-2014, 2016-2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _RMNET_PRIVATE_H_ +#define _RMNET_PRIVATE_H_ + +#define RMNET_MAX_PACKET_SIZE 16384 +#define RMNET_DFLT_PACKET_SIZE 1500 +#define RMNET_NEEDED_HEADROOM 16 +#define RMNET_TX_QUEUE_LEN 1000 + +/* Constants */ +#define RMNET_EGRESS_FORMAT__RESERVED__ BIT(0) +#define RMNET_EGRESS_FORMAT_MAP BIT(1) +#define RMNET_EGRESS_FORMAT_AGGREGATION BIT(2) +#define RMNET_EGRESS_FORMAT_MUXING BIT(3) +#define RMNET_EGRESS_FORMAT_MAP_CKSUMV3 BIT(4) +#define RMNET_EGRESS_FORMAT_MAP_CKSUMV4 BIT(5) + +#define RMNET_INGRESS_FIX_ETHERNET BIT(0) +#define RMNET_INGRESS_FORMAT_MAP BIT(1) +#define RMNET_INGRESS_FORMAT_DEAGGREGATION BIT(2) +#define RMNET_INGRESS_FORMAT_DEMUXING BIT(3) +#define RMNET_INGRESS_FORMAT_MAP_COMMANDS BIT(4) +#define RMNET_INGRESS_FORMAT_MAP_CKSUMV3 BIT(5) +#define RMNET_INGRESS_FORMAT_MAP_CKSUMV4 BIT(6) + +/* Pass the frame up the stack with no modifications to skb->dev */ +#define RMNET_EPMODE_NONE (0) +/* Replace skb->dev to a virtual rmnet device and pass up the stack */ +#define RMNET_EPMODE_VND (1) +/* Pass the frame directly to another device with dev_queue_xmit() */ +#define RMNET_EPMODE_BRIDGE (2) + +#endif /* _RMNET_PRIVATE_H_ */ diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c new file mode 100644 index 000000000000..7f90d5587653 --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.c @@ -0,0 +1,174 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * RMNET Data virtual network driver + * + */ + +#include <linux/etherdevice.h> +#include <linux/if_arp.h> +#include <net/pkt_sched.h> +#include "rmnet_config.h" +#include "rmnet_handlers.h" +#include "rmnet_private.h" +#include "rmnet_map.h" +#include "rmnet_vnd.h" + +/* RX/TX Fixup */ + +void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev) +{ + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; +} + +void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev) +{ + dev->stats.tx_packets++; + dev->stats.tx_bytes += skb->len; +} + +/* Network Device Operations */ + +static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct rmnet_priv *priv; + + priv = netdev_priv(dev); + if (priv->local_ep.egress_dev) { + rmnet_egress_handler(skb, &priv->local_ep); + } else { + dev->stats.tx_dropped++; + kfree_skb(skb); + } + return NETDEV_TX_OK; +} + +static int rmnet_vnd_change_mtu(struct net_device *rmnet_dev, int new_mtu) +{ + if (new_mtu < 0 || new_mtu > RMNET_MAX_PACKET_SIZE) + return -EINVAL; + + rmnet_dev->mtu = new_mtu; + return 0; +} + +static int rmnet_vnd_get_iflink(const struct net_device *dev) +{ + struct rmnet_priv *priv = netdev_priv(dev); + + return priv->real_dev->ifindex; +} + +static const struct net_device_ops rmnet_vnd_ops = { + .ndo_start_xmit = rmnet_vnd_start_xmit, + .ndo_change_mtu = rmnet_vnd_change_mtu, + .ndo_get_iflink = rmnet_vnd_get_iflink, +}; + +/* Called by kernel whenever a new rmnet<n> device is created. Sets MTU, + * flags, ARP type, needed headroom, etc... + */ +void rmnet_vnd_setup(struct net_device *rmnet_dev) +{ + rmnet_dev->netdev_ops = &rmnet_vnd_ops; + rmnet_dev->mtu = RMNET_DFLT_PACKET_SIZE; + rmnet_dev->needed_headroom = RMNET_NEEDED_HEADROOM; + random_ether_addr(rmnet_dev->dev_addr); + rmnet_dev->tx_queue_len = RMNET_TX_QUEUE_LEN; + + /* Raw IP mode */ + rmnet_dev->header_ops = NULL; /* No header */ + rmnet_dev->type = ARPHRD_RAWIP; + rmnet_dev->hard_header_len = 0; + rmnet_dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); + + rmnet_dev->needs_free_netdev = true; +} + +/* Exposed API */ + +int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, + struct rmnet_port *port, + struct net_device *real_dev) +{ + struct rmnet_priv *priv; + int rc; + + if (port->rmnet_devices[id]) + return -EINVAL; + + rc = register_netdevice(rmnet_dev); + if (!rc) { + port->rmnet_devices[id] = rmnet_dev; + port->nr_rmnet_devs++; + + rmnet_dev->rtnl_link_ops = &rmnet_link_ops; + + priv = netdev_priv(rmnet_dev); + priv->mux_id = id; + priv->real_dev = real_dev; + + netdev_dbg(rmnet_dev, "rmnet dev created\n"); + } + + return rc; +} + +int rmnet_vnd_dellink(u8 id, struct rmnet_port *port) +{ + if (id >= RMNET_MAX_LOGICAL_EP || !port->rmnet_devices[id]) + return -EINVAL; + + port->rmnet_devices[id] = NULL; + port->nr_rmnet_devs--; + return 0; +} + +u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev) +{ + struct rmnet_priv *priv; + + priv = netdev_priv(rmnet_dev); + return priv->mux_id; +} + +/* Gets the logical endpoint configuration for a RmNet virtual network device + * node. Caller should confirm that devices is a RmNet VND before calling. + */ +struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device *rmnet_dev) +{ + struct rmnet_priv *priv; + + if (!rmnet_dev) + return NULL; + + priv = netdev_priv(rmnet_dev); + + return &priv->local_ep; +} + +int rmnet_vnd_do_flow_control(struct net_device *rmnet_dev, int enable) +{ + netdev_dbg(rmnet_dev, "Setting VND TX queue state to %d\n", enable); + /* Although we expect similar number of enable/disable + * commands, optimize for the disable. That is more + * latency sensitive than enable + */ + if (unlikely(enable)) + netif_wake_queue(rmnet_dev); + else + netif_stop_queue(rmnet_dev); + + return 0; +} diff --git a/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h new file mode 100644 index 000000000000..8a4042f0f6bf --- /dev/null +++ b/drivers/net/ethernet/qualcomm/rmnet/rmnet_vnd.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * RMNET Data Virtual Network Device APIs + * + */ + +#ifndef _RMNET_VND_H_ +#define _RMNET_VND_H_ + +int rmnet_vnd_do_flow_control(struct net_device *dev, int enable); +struct rmnet_endpoint *rmnet_vnd_get_endpoint(struct net_device *dev); +int rmnet_vnd_newlink(u8 id, struct net_device *rmnet_dev, + struct rmnet_port *port, + struct net_device *real_dev); +int rmnet_vnd_dellink(u8 id, struct rmnet_port *port); +void rmnet_vnd_rx_fixup(struct sk_buff *skb, struct net_device *dev); +void rmnet_vnd_tx_fixup(struct sk_buff *skb, struct net_device *dev); +u8 rmnet_vnd_get_mux(struct net_device *rmnet_dev); +void rmnet_vnd_setup(struct net_device *dev); +#endif /* _RMNET_VND_H_ */ diff --git a/drivers/net/ethernet/renesas/ravb.h b/drivers/net/ethernet/renesas/ravb.h index 0525bd696d5d..96a27b00c90e 100644 --- a/drivers/net/ethernet/renesas/ravb.h +++ b/drivers/net/ethernet/renesas/ravb.h @@ -991,6 +991,7 @@ struct ravb_private { struct net_device *ndev; struct platform_device *pdev; void __iomem *addr; + struct clk *clk; struct mdiobb_ctrl mdiobb; u32 num_rx_ring[NUM_RX_QUEUE]; u32 num_tx_ring[NUM_TX_QUEUE]; @@ -1033,6 +1034,7 @@ struct ravb_private { unsigned no_avb_link:1; unsigned avb_link_active_low:1; + unsigned wol_enabled:1; }; static inline u32 ravb_read(struct net_device *ndev, enum ravb_reg reg) diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c index 5931e859876c..fdf30bfa403b 100644 --- a/drivers/net/ethernet/renesas/ravb_main.c +++ b/drivers/net/ethernet/renesas/ravb_main.c @@ -680,6 +680,9 @@ static void ravb_emac_interrupt_unlocked(struct net_device *ndev) ecsr = ravb_read(ndev, ECSR); ravb_write(ndev, ecsr, ECSR); /* clear interrupt */ + + if (ecsr & ECSR_MPD) + pm_wakeup_event(&priv->pdev->dev, 0); if (ecsr & ECSR_ICD) ndev->stats.tx_carrier_errors++; if (ecsr & ECSR_LCHNG) { @@ -1330,6 +1333,33 @@ static int ravb_get_ts_info(struct net_device *ndev, return 0; } +static void ravb_get_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct ravb_private *priv = netdev_priv(ndev); + + wol->supported = 0; + wol->wolopts = 0; + + if (priv->clk) { + wol->supported = WAKE_MAGIC; + wol->wolopts = priv->wol_enabled ? WAKE_MAGIC : 0; + } +} + +static int ravb_set_wol(struct net_device *ndev, struct ethtool_wolinfo *wol) +{ + struct ravb_private *priv = netdev_priv(ndev); + + if (!priv->clk || wol->wolopts & ~WAKE_MAGIC) + return -EOPNOTSUPP; + + priv->wol_enabled = !!(wol->wolopts & WAKE_MAGIC); + + device_set_wakeup_enable(&priv->pdev->dev, priv->wol_enabled); + + return 0; +} + static const struct ethtool_ops ravb_ethtool_ops = { .nway_reset = ravb_nway_reset, .get_msglevel = ravb_get_msglevel, @@ -1343,6 +1373,8 @@ static const struct ethtool_ops ravb_ethtool_ops = { .get_ts_info = ravb_get_ts_info, .get_link_ksettings = ravb_get_link_ksettings, .set_link_ksettings = ravb_set_link_ksettings, + .get_wol = ravb_get_wol, + .set_wol = ravb_set_wol, }; static inline int ravb_hook_irq(unsigned int irq, irq_handler_t handler, @@ -2041,6 +2073,11 @@ static int ravb_probe(struct platform_device *pdev) priv->chip_id = chip_id; + /* Get clock, if not found that's OK but Wake-On-Lan is unavailable */ + priv->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->clk)) + priv->clk = NULL; + /* Set function */ ndev->netdev_ops = &ravb_netdev_ops; ndev->ethtool_ops = &ravb_ethtool_ops; @@ -2107,6 +2144,9 @@ static int ravb_probe(struct platform_device *pdev) if (error) goto out_napi_del; + if (priv->clk) + device_set_wakeup_capable(&pdev->dev, 1); + /* Print device information */ netdev_info(ndev, "Base address at %#x, %pM, IRQ %d.\n", (u32)ndev->base_addr, ndev->dev_addr, ndev->irq); @@ -2160,15 +2200,66 @@ static int ravb_remove(struct platform_device *pdev) return 0; } +static int ravb_wol_setup(struct net_device *ndev) +{ + struct ravb_private *priv = netdev_priv(ndev); + + /* Disable interrupts by clearing the interrupt masks. */ + ravb_write(ndev, 0, RIC0); + ravb_write(ndev, 0, RIC2); + ravb_write(ndev, 0, TIC); + + /* Only allow ECI interrupts */ + synchronize_irq(priv->emac_irq); + napi_disable(&priv->napi[RAVB_NC]); + napi_disable(&priv->napi[RAVB_BE]); + ravb_write(ndev, ECSIPR_MPDIP, ECSIPR); + + /* Enable MagicPacket */ + ravb_modify(ndev, ECMR, ECMR_MPDE, ECMR_MPDE); + + /* Increased clock usage so device won't be suspended */ + clk_enable(priv->clk); + + return enable_irq_wake(priv->emac_irq); +} + +static int ravb_wol_restore(struct net_device *ndev) +{ + struct ravb_private *priv = netdev_priv(ndev); + int ret; + + napi_enable(&priv->napi[RAVB_NC]); + napi_enable(&priv->napi[RAVB_BE]); + + /* Disable MagicPacket */ + ravb_modify(ndev, ECMR, ECMR_MPDE, 0); + + ret = ravb_close(ndev); + if (ret < 0) + return ret; + + /* Restore clock usage count */ + clk_disable(priv->clk); + + return disable_irq_wake(priv->emac_irq); +} + static int __maybe_unused ravb_suspend(struct device *dev) { struct net_device *ndev = dev_get_drvdata(dev); - int ret = 0; + struct ravb_private *priv = netdev_priv(ndev); + int ret; - if (netif_running(ndev)) { - netif_device_detach(ndev); + if (!netif_running(ndev)) + return 0; + + netif_device_detach(ndev); + + if (priv->wol_enabled) + ret = ravb_wol_setup(ndev); + else ret = ravb_close(ndev); - } return ret; } @@ -2179,6 +2270,33 @@ static int __maybe_unused ravb_resume(struct device *dev) struct ravb_private *priv = netdev_priv(ndev); int ret = 0; + if (priv->wol_enabled) { + /* Reduce the usecount of the clock to zero and then + * restore it to its original value. This is done to force + * the clock to be re-enabled which is a workaround + * for renesas-cpg-mssr driver which do not enable clocks + * when resuming from PSCI suspend/resume. + * + * Without this workaround the driver fails to communicate + * with the hardware if WoL was enabled when the system + * entered PSCI suspend. This is due to that if WoL is enabled + * we explicitly keep the clock from being turned off when + * suspending, but in PSCI sleep power is cut so the clock + * is disabled anyhow, the clock driver is not aware of this + * so the clock is not turned back on when resuming. + * + * TODO: once the renesas-cpg-mssr suspend/resume is working + * this clock dance should be removed. + */ + clk_disable(priv->clk); + clk_disable(priv->clk); + clk_enable(priv->clk); + clk_enable(priv->clk); + + /* Set reset mode to rearm the WoL logic */ + ravb_write(ndev, CCC_OPC_RESET, CCC); + } + /* All register have been reset to default values. * Restore all registers which where setup at probe time and * reopen device if it was running before system suspended. @@ -2202,6 +2320,11 @@ static int __maybe_unused ravb_resume(struct device *dev) ravb_write(ndev, priv->desc_bat_dma, DBAT); if (netif_running(ndev)) { + if (priv->wol_enabled) { + ret = ravb_wol_restore(ndev); + if (ret) + return ret; + } ret = ravb_open(ndev); if (ret < 0) return ret; diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c index d2dc0a8ef305..d2e88a30f57b 100644 --- a/drivers/net/ethernet/renesas/sh_eth.c +++ b/drivers/net/ethernet/renesas/sh_eth.c @@ -3402,7 +3402,7 @@ static const struct dev_pm_ops sh_eth_dev_pm_ops = { #define SH_ETH_PM_OPS NULL #endif -static struct platform_device_id sh_eth_id_table[] = { +static const struct platform_device_id sh_eth_id_table[] = { { "sh7619-ether", (kernel_ulong_t)&sh7619_data }, { "sh771x-ether", (kernel_ulong_t)&sh771x_data }, { "sh7724-ether", (kernel_ulong_t)&sh7724_data }, diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index b1e5c07099fa..fc8f8bdf6579 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -34,6 +34,7 @@ #include <net/netevent.h> #include <net/arp.h> #include <net/fib_rules.h> +#include <net/fib_notifier.h> #include <linux/io-64-nonatomic-lo-hi.h> #include <generated/utsrelease.h> @@ -2191,6 +2192,10 @@ static int rocker_router_fib_event(struct notifier_block *nb, { struct rocker *rocker = container_of(nb, struct rocker, fib_nb); struct rocker_fib_event_work *fib_work; + struct fib_notifier_info *info = ptr; + + if (info->family != AF_INET) + return NOTIFY_DONE; fib_work = kzalloc(sizeof(*fib_work), GFP_ATOMIC); if (WARN_ON(!fib_work)) diff --git a/drivers/net/ethernet/rocker/rocker_ofdpa.c b/drivers/net/ethernet/rocker/rocker_ofdpa.c index 600e30e8f0be..0653b70723a3 100644 --- a/drivers/net/ethernet/rocker/rocker_ofdpa.c +++ b/drivers/net/ethernet/rocker/rocker_ofdpa.c @@ -1177,7 +1177,7 @@ static int ofdpa_group_l2_fan_out(struct ofdpa_port *ofdpa_port, entry->group_id = group_id; entry->group_count = group_count; - entry->group_ids = kcalloc(flags, group_count, sizeof(u32)); + entry->group_ids = kcalloc(group_count, sizeof(u32), GFP_KERNEL); if (!entry->group_ids) { kfree(entry); return -ENOMEM; @@ -1456,7 +1456,7 @@ static int ofdpa_port_vlan_flood_group(struct ofdpa_port *ofdpa_port, int err = 0; int i; - group_ids = kcalloc(flags, port_count, sizeof(u32)); + group_ids = kcalloc(port_count, sizeof(u32), GFP_KERNEL); if (!group_ids) return -ENOMEM; @@ -2761,7 +2761,7 @@ static int ofdpa_fib4_add(struct rocker *rocker, fen_info->tb_id, 0); if (err) return err; - fib_info_offload_inc(fen_info->fi); + fen_info->fi->fib_nh->nh_flags |= RTNH_F_OFFLOAD; return 0; } @@ -2776,7 +2776,7 @@ static int ofdpa_fib4_del(struct rocker *rocker, ofdpa_port = ofdpa_port_dev_lower_find(fen_info->fi->fib_dev, rocker); if (!ofdpa_port) return 0; - fib_info_offload_dec(fen_info->fi); + fen_info->fi->fib_nh->nh_flags &= ~RTNH_F_OFFLOAD; return ofdpa_port_fib_ipv4(ofdpa_port, htonl(fen_info->dst), fen_info->dst_len, fen_info->fi, fen_info->tb_id, OFDPA_OP_FLAG_REMOVE); @@ -2803,7 +2803,7 @@ static void ofdpa_fib4_abort(struct rocker *rocker) rocker); if (!ofdpa_port) continue; - fib_info_offload_dec(flow_entry->fi); + flow_entry->fi->fib_nh->nh_flags &= ~RTNH_F_OFFLOAD; ofdpa_flow_tbl_del(ofdpa_port, OFDPA_OP_FLAG_REMOVE, flow_entry); } diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index fcea9371ab7f..d407adf59610 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -32,8 +32,8 @@ netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev); netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); -int efx_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc); +int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + void *type_data); unsigned int efx_tx_max_skb_descs(struct efx_nic *efx); extern unsigned int efx_piobuf_size; extern bool efx_separate_tx_channels; diff --git a/drivers/net/ethernet/sfc/falcon/efx.h b/drivers/net/ethernet/sfc/falcon/efx.h index e5a7a40cc8b6..4f3bb30661ea 100644 --- a/drivers/net/ethernet/sfc/falcon/efx.h +++ b/drivers/net/ethernet/sfc/falcon/efx.h @@ -32,8 +32,8 @@ netdev_tx_t ef4_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev); netdev_tx_t ef4_enqueue_skb(struct ef4_tx_queue *tx_queue, struct sk_buff *skb); void ef4_xmit_done(struct ef4_tx_queue *tx_queue, unsigned int index); -int ef4_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc); +int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + void *type_data); unsigned int ef4_tx_max_skb_descs(struct ef4_nic *efx); extern bool ef4_separate_tx_channels; diff --git a/drivers/net/ethernet/sfc/falcon/tx.c b/drivers/net/ethernet/sfc/falcon/tx.c index f1520a404ac6..6a75f4140a4b 100644 --- a/drivers/net/ethernet/sfc/falcon/tx.c +++ b/drivers/net/ethernet/sfc/falcon/tx.c @@ -425,24 +425,25 @@ void ef4_init_tx_queue_core_txq(struct ef4_tx_queue *tx_queue) efx->n_tx_channels : 0)); } -int ef4_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *ntc) +int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + void *type_data) { struct ef4_nic *efx = netdev_priv(net_dev); + struct tc_mqprio_qopt *mqprio = type_data; struct ef4_channel *channel; struct ef4_tx_queue *tx_queue; unsigned tc, num_tc; int rc; - if (ntc->type != TC_SETUP_MQPRIO) - return -EINVAL; + if (type != TC_SETUP_MQPRIO) + return -EOPNOTSUPP; - num_tc = ntc->mqprio->num_tc; + num_tc = mqprio->num_tc; if (ef4_nic_rev(efx) < EF4_REV_FALCON_B0 || num_tc > EF4_MAX_TX_TC) return -EINVAL; - ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; if (num_tc == net_dev->num_tc) return 0; diff --git a/drivers/net/ethernet/sfc/mcdi_port.c b/drivers/net/ethernet/sfc/mcdi_port.c index 990a63d7fcb7..c7407d129c7d 100644 --- a/drivers/net/ethernet/sfc/mcdi_port.c +++ b/drivers/net/ethernet/sfc/mcdi_port.c @@ -746,59 +746,171 @@ static const char *efx_mcdi_phy_test_name(struct efx_nic *efx, return NULL; } -#define SFP_PAGE_SIZE 128 -#define SFP_NUM_PAGES 2 -static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx, - struct ethtool_eeprom *ee, u8 *data) +#define SFP_PAGE_SIZE 128 +#define SFF_DIAG_TYPE_OFFSET 92 +#define SFF_DIAG_ADDR_CHANGE BIT(2) +#define SFF_8079_NUM_PAGES 2 +#define SFF_8472_NUM_PAGES 4 +#define SFF_8436_NUM_PAGES 5 +#define SFF_DMT_LEVEL_OFFSET 94 + +/** efx_mcdi_phy_get_module_eeprom_page() - Get a single page of module eeprom + * @efx: NIC context + * @page: EEPROM page number + * @data: Destination data pointer + * @offset: Offset in page to copy from in to data + * @space: Space available in data + * + * Return: + * >=0 - amount of data copied + * <0 - error + */ +static int efx_mcdi_phy_get_module_eeprom_page(struct efx_nic *efx, + unsigned int page, + u8 *data, ssize_t offset, + ssize_t space) { MCDI_DECLARE_BUF(outbuf, MC_CMD_GET_PHY_MEDIA_INFO_OUT_LENMAX); MCDI_DECLARE_BUF(inbuf, MC_CMD_GET_PHY_MEDIA_INFO_IN_LEN); size_t outlen; - int rc; unsigned int payload_len; - unsigned int space_remaining = ee->len; - unsigned int page; - unsigned int page_off; unsigned int to_copy; - u8 *user_data = data; + int rc; - BUILD_BUG_ON(SFP_PAGE_SIZE * SFP_NUM_PAGES != ETH_MODULE_SFF_8079_LEN); + if (offset > SFP_PAGE_SIZE) + return -EINVAL; - page_off = ee->offset % SFP_PAGE_SIZE; - page = ee->offset / SFP_PAGE_SIZE; + to_copy = min(space, SFP_PAGE_SIZE - offset); - while (space_remaining && (page < SFP_NUM_PAGES)) { - MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page); + MCDI_SET_DWORD(inbuf, GET_PHY_MEDIA_INFO_IN_PAGE, page); + rc = efx_mcdi_rpc_quiet(efx, MC_CMD_GET_PHY_MEDIA_INFO, + inbuf, sizeof(inbuf), + outbuf, sizeof(outbuf), + &outlen); - rc = efx_mcdi_rpc(efx, MC_CMD_GET_PHY_MEDIA_INFO, - inbuf, sizeof(inbuf), - outbuf, sizeof(outbuf), - &outlen); - if (rc) - return rc; + if (rc) + return rc; + + if (outlen < (MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST + + SFP_PAGE_SIZE)) + return -EIO; + + payload_len = MCDI_DWORD(outbuf, GET_PHY_MEDIA_INFO_OUT_DATALEN); + if (payload_len != SFP_PAGE_SIZE) + return -EIO; - if (outlen < (MC_CMD_GET_PHY_MEDIA_INFO_OUT_DATA_OFST + - SFP_PAGE_SIZE)) - return -EIO; + memcpy(data, MCDI_PTR(outbuf, GET_PHY_MEDIA_INFO_OUT_DATA) + offset, + to_copy); - payload_len = MCDI_DWORD(outbuf, - GET_PHY_MEDIA_INFO_OUT_DATALEN); - if (payload_len != SFP_PAGE_SIZE) - return -EIO; + return to_copy; +} - /* Copy as much as we can into data */ - payload_len -= page_off; - to_copy = (space_remaining < payload_len) ? - space_remaining : payload_len; +static int efx_mcdi_phy_get_module_eeprom_byte(struct efx_nic *efx, + unsigned int page, + u8 byte) +{ + int rc; + u8 data; - memcpy(user_data, - MCDI_PTR(outbuf, GET_PHY_MEDIA_INFO_OUT_DATA) + page_off, - to_copy); + rc = efx_mcdi_phy_get_module_eeprom_page(efx, page, &data, byte, 1); + if (rc == 1) + return data; + + return rc; +} + +static int efx_mcdi_phy_diag_type(struct efx_nic *efx) +{ + /* Page zero of the EEPROM includes the diagnostic type at byte 92. */ + return efx_mcdi_phy_get_module_eeprom_byte(efx, 0, + SFF_DIAG_TYPE_OFFSET); +} - space_remaining -= to_copy; - user_data += to_copy; - page_off = 0; - page++; +static int efx_mcdi_phy_sff_8472_level(struct efx_nic *efx) +{ + /* Page zero of the EEPROM includes the DMT level at byte 94. */ + return efx_mcdi_phy_get_module_eeprom_byte(efx, 0, + SFF_DMT_LEVEL_OFFSET); +} + +static u32 efx_mcdi_phy_module_type(struct efx_nic *efx) +{ + struct efx_mcdi_phy_data *phy_data = efx->phy_data; + + if (phy_data->media != MC_CMD_MEDIA_QSFP_PLUS) + return phy_data->media; + + /* A QSFP+ NIC may actually have an SFP+ module attached. + * The ID is page 0, byte 0. + */ + switch (efx_mcdi_phy_get_module_eeprom_byte(efx, 0, 0)) { + case 0x3: + return MC_CMD_MEDIA_SFP_PLUS; + case 0xc: + case 0xd: + return MC_CMD_MEDIA_QSFP_PLUS; + default: + return 0; + } +} + +static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx, + struct ethtool_eeprom *ee, u8 *data) +{ + int rc; + ssize_t space_remaining = ee->len; + unsigned int page_off; + bool ignore_missing; + int num_pages; + int page; + + switch (efx_mcdi_phy_module_type(efx)) { + case MC_CMD_MEDIA_SFP_PLUS: + num_pages = efx_mcdi_phy_sff_8472_level(efx) > 0 ? + SFF_8472_NUM_PAGES : SFF_8079_NUM_PAGES; + page = 0; + ignore_missing = false; + break; + case MC_CMD_MEDIA_QSFP_PLUS: + num_pages = SFF_8436_NUM_PAGES; + page = -1; /* We obtain the lower page by asking for -1. */ + ignore_missing = true; /* Ignore missing pages after page 0. */ + break; + default: + return -EOPNOTSUPP; + } + + page_off = ee->offset % SFP_PAGE_SIZE; + page += ee->offset / SFP_PAGE_SIZE; + + while (space_remaining && (page < num_pages)) { + rc = efx_mcdi_phy_get_module_eeprom_page(efx, page, + data, page_off, + space_remaining); + + if (rc > 0) { + space_remaining -= rc; + data += rc; + page_off = 0; + page++; + } else if (rc == 0) { + space_remaining = 0; + } else if (ignore_missing && (page > 0)) { + int intended_size = SFP_PAGE_SIZE - page_off; + + space_remaining -= intended_size; + if (space_remaining < 0) { + space_remaining = 0; + } else { + memset(data, 0, intended_size); + data += intended_size; + page_off = 0; + page++; + rc = 0; + } + } else { + return rc; + } } return 0; @@ -807,16 +919,42 @@ static int efx_mcdi_phy_get_module_eeprom(struct efx_nic *efx, static int efx_mcdi_phy_get_module_info(struct efx_nic *efx, struct ethtool_modinfo *modinfo) { - struct efx_mcdi_phy_data *phy_cfg = efx->phy_data; + int sff_8472_level; + int diag_type; - switch (phy_cfg->media) { + switch (efx_mcdi_phy_module_type(efx)) { case MC_CMD_MEDIA_SFP_PLUS: - modinfo->type = ETH_MODULE_SFF_8079; - modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; - return 0; + sff_8472_level = efx_mcdi_phy_sff_8472_level(efx); + + /* If we can't read the diagnostics level we have none. */ + if (sff_8472_level < 0) + return -EOPNOTSUPP; + + /* Check if this module requires the (unsupported) address + * change operation. + */ + diag_type = efx_mcdi_phy_diag_type(efx); + + if ((sff_8472_level == 0) || + (diag_type & SFF_DIAG_ADDR_CHANGE)) { + modinfo->type = ETH_MODULE_SFF_8079; + modinfo->eeprom_len = ETH_MODULE_SFF_8079_LEN; + } else { + modinfo->type = ETH_MODULE_SFF_8472; + modinfo->eeprom_len = ETH_MODULE_SFF_8472_LEN; + } + break; + + case MC_CMD_MEDIA_QSFP_PLUS: + modinfo->type = ETH_MODULE_SFF_8436; + modinfo->eeprom_len = ETH_MODULE_SFF_8436_LEN; + break; + default: return -EOPNOTSUPP; } + + return 0; } static const struct efx_phy_operations efx_mcdi_phy_ops = { diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 02d41eb4a8e9..32bf1fecf864 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -653,24 +653,25 @@ void efx_init_tx_queue_core_txq(struct efx_tx_queue *tx_queue) efx->n_tx_channels : 0)); } -int efx_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *ntc) +int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + void *type_data) { struct efx_nic *efx = netdev_priv(net_dev); + struct tc_mqprio_qopt *mqprio = type_data; struct efx_channel *channel; struct efx_tx_queue *tx_queue; unsigned tc, num_tc; int rc; - if (ntc->type != TC_SETUP_MQPRIO) - return -EINVAL; + if (type != TC_SETUP_MQPRIO) + return -EOPNOTSUPP; - num_tc = ntc->mqprio->num_tc; + num_tc = mqprio->num_tc; if (num_tc > EFX_MAX_TX_TC) return -EINVAL; - ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; if (num_tc == net_dev->num_tc) return 0; diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 85c0e41f8021..97035766c291 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -45,6 +45,15 @@ config DWMAC_GENERIC platform specific code to function or is using platform data for setup. +config DWMAC_ANARION + tristate "Adaptrum Anarion GMAC support" + default ARC + depends on OF && (ARC || COMPILE_TEST) + help + Support for Adaptrum Anarion GMAC Ethernet controller. + + This selects the Anarion SoC glue layer support for the stmmac driver. + config DWMAC_IPQ806X tristate "QCA IPQ806x DWMAC support" default ARCH_QCOM diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index fd4937a7fcab..238307fadcdb 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -7,6 +7,7 @@ stmmac-objs:= stmmac_main.o stmmac_ethtool.o stmmac_mdio.o ring_mode.o \ # Ordering matters. Generic driver must be last. obj-$(CONFIG_STMMAC_PLATFORM) += stmmac-platform.o +obj-$(CONFIG_DWMAC_ANARION) += dwmac-anarion.o obj-$(CONFIG_DWMAC_IPQ806X) += dwmac-ipq806x.o obj-$(CONFIG_DWMAC_LPC18XX) += dwmac-lpc18xx.o obj-$(CONFIG_DWMAC_MESON) += dwmac-meson.o dwmac-meson8b.o diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c new file mode 100644 index 000000000000..85ce80c600c7 --- /dev/null +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-anarion.c @@ -0,0 +1,152 @@ +/* + * Adaptrum Anarion DWMAC glue layer + * + * Copyright (C) 2017, Adaptrum, Inc. + * (Written by Alexandru Gagniuc <alex.g at adaptrum.com> for Adaptrum, Inc.) + * Licensed under the GPLv2 or (at your option) any later version. + */ + +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_net.h> +#include <linux/stmmac.h> + +#include "stmmac.h" +#include "stmmac_platform.h" + +#define GMAC_RESET_CONTROL_REG 0 +#define GMAC_SW_CONFIG_REG 4 +#define GMAC_CONFIG_INTF_SEL_MASK (0x7 << 0) +#define GMAC_CONFIG_INTF_RGMII (0x1 << 0) + +struct anarion_gmac { + uintptr_t ctl_block; + uint32_t phy_intf_sel; +}; + +static uint32_t gmac_read_reg(struct anarion_gmac *gmac, uint8_t reg) +{ + return readl((void *)(gmac->ctl_block + reg)); +}; + +static void gmac_write_reg(struct anarion_gmac *gmac, uint8_t reg, uint32_t val) +{ + writel(val, (void *)(gmac->ctl_block + reg)); +} + +static int anarion_gmac_init(struct platform_device *pdev, void *priv) +{ + uint32_t sw_config; + struct anarion_gmac *gmac = priv; + + /* Reset logic, configure interface mode, then release reset. SIMPLE! */ + gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 1); + + sw_config = gmac_read_reg(gmac, GMAC_SW_CONFIG_REG); + sw_config &= ~GMAC_CONFIG_INTF_SEL_MASK; + sw_config |= (gmac->phy_intf_sel & GMAC_CONFIG_INTF_SEL_MASK); + gmac_write_reg(gmac, GMAC_SW_CONFIG_REG, sw_config); + + gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 0); + + return 0; +} + +static void anarion_gmac_exit(struct platform_device *pdev, void *priv) +{ + struct anarion_gmac *gmac = priv; + + gmac_write_reg(gmac, GMAC_RESET_CONTROL_REG, 1); +} + +static struct anarion_gmac *anarion_config_dt(struct platform_device *pdev) +{ + int phy_mode; + struct resource *res; + void __iomem *ctl_block; + struct anarion_gmac *gmac; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + ctl_block = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ctl_block)) { + dev_err(&pdev->dev, "Cannot get reset region (%ld)!\n", + PTR_ERR(ctl_block)); + return ctl_block; + } + + gmac = devm_kzalloc(&pdev->dev, sizeof(*gmac), GFP_KERNEL); + if (!gmac) + return ERR_PTR(-ENOMEM); + + gmac->ctl_block = (uintptr_t)ctl_block; + + phy_mode = of_get_phy_mode(pdev->dev.of_node); + switch (phy_mode) { + case PHY_INTERFACE_MODE_RGMII: /* Fall through */ + case PHY_INTERFACE_MODE_RGMII_ID /* Fall through */: + case PHY_INTERFACE_MODE_RGMII_RXID: /* Fall through */ + case PHY_INTERFACE_MODE_RGMII_TXID: + gmac->phy_intf_sel = GMAC_CONFIG_INTF_RGMII; + break; + default: + dev_err(&pdev->dev, "Unsupported phy-mode (%d)\n", + phy_mode); + return ERR_PTR(-ENOTSUPP); + } + + return gmac; +} + +static int anarion_dwmac_probe(struct platform_device *pdev) +{ + int ret; + struct anarion_gmac *gmac; + struct plat_stmmacenet_data *plat_dat; + struct stmmac_resources stmmac_res; + + ret = stmmac_get_platform_resources(pdev, &stmmac_res); + if (ret) + return ret; + + gmac = anarion_config_dt(pdev); + if (IS_ERR(gmac)) + return PTR_ERR(gmac); + + plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); + if (IS_ERR(plat_dat)) + return PTR_ERR(plat_dat); + + plat_dat->init = anarion_gmac_init; + plat_dat->exit = anarion_gmac_exit; + anarion_gmac_init(pdev, gmac); + plat_dat->bsp_priv = gmac; + + ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); + if (ret) { + stmmac_remove_config_dt(pdev, plat_dat); + return ret; + } + + return 0; +} + +static const struct of_device_id anarion_dwmac_match[] = { + { .compatible = "adaptrum,anarion-gmac" }, + { } +}; +MODULE_DEVICE_TABLE(of, anarion_dwmac_match); + +static struct platform_driver anarion_dwmac_driver = { + .probe = anarion_dwmac_probe, + .remove = stmmac_pltfr_remove, + .driver = { + .name = "anarion-dwmac", + .pm = &stmmac_pltfr_pm_ops, + .of_match_table = anarion_dwmac_match, + }, +}; +module_platform_driver(anarion_dwmac_driver); + +MODULE_DESCRIPTION("Adaptrum Anarion DWMAC specific glue layer"); +MODULE_AUTHOR("Alexandru Gagniuc <mr.nuke.me@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c index 9685555932ea..4404650b32c5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c @@ -89,7 +89,7 @@ static int meson8b_init_clk(struct meson8b_dwmac *dwmac) char clk_name[32]; const char *clk_div_parents[1]; const char *mux_parent_names[MUX_CLK_NUM_PARENTS]; - static struct clk_div_table clk_25m_div_table[] = { + static const struct clk_div_table clk_25m_div_table[] = { { .val = 0, .div = 5 }, { .val = 1, .div = 10 }, { /* sentinel */ }, diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index f0df5193f047..99823f54696a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -41,6 +41,7 @@ struct rk_gmac_ops { void (*set_to_rmii)(struct rk_priv_data *bsp_priv); void (*set_rgmii_speed)(struct rk_priv_data *bsp_priv, int speed); void (*set_rmii_speed)(struct rk_priv_data *bsp_priv, int speed); + void (*integrated_phy_powerup)(struct rk_priv_data *bsp_priv); }; struct rk_priv_data { @@ -52,6 +53,7 @@ struct rk_priv_data { bool clk_enabled; bool clock_input; + bool integrated_phy; struct clk *clk_mac; struct clk *gmac_clkin; @@ -61,6 +63,9 @@ struct rk_priv_data { struct clk *clk_mac_refout; struct clk *aclk_mac; struct clk *pclk_mac; + struct clk *clk_phy; + + struct reset_control *phy_reset; int tx_delay; int rx_delay; @@ -81,6 +86,8 @@ struct rk_priv_data { #define RK3228_GRF_MAC_CON0 0x0900 #define RK3228_GRF_MAC_CON1 0x0904 +#define RK3228_GRF_CON_MUX 0x50 + /* RK3228_GRF_MAC_CON0 */ #define RK3228_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7) #define RK3228_GMAC_CLK_TX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 0) @@ -106,6 +113,9 @@ struct rk_priv_data { #define RK3228_GMAC_RXCLK_DLY_ENABLE GRF_BIT(1) #define RK3228_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(1) +/* RK3228_GRF_COM_MUX */ +#define RK3228_GRF_CON_MUX_GMAC_INTEGRATED_PHY GRF_BIT(15) + static void rk3228_set_to_rgmii(struct rk_priv_data *bsp_priv, int tx_delay, int rx_delay) { @@ -186,11 +196,18 @@ static void rk3228_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) dev_err(dev, "unknown speed value for RMII! speed=%d", speed); } +static void rk3228_integrated_phy_powerup(struct rk_priv_data *priv) +{ + regmap_write(priv->grf, RK3228_GRF_CON_MUX, + RK3228_GRF_CON_MUX_GMAC_INTEGRATED_PHY); +} + static const struct rk_gmac_ops rk3228_ops = { .set_to_rgmii = rk3228_set_to_rgmii, .set_to_rmii = rk3228_set_to_rmii, .set_rgmii_speed = rk3228_set_rgmii_speed, .set_rmii_speed = rk3228_set_rmii_speed, + .integrated_phy_powerup = rk3228_integrated_phy_powerup, }; #define RK3288_GRF_SOC_CON1 0x0248 @@ -306,6 +323,8 @@ static const struct rk_gmac_ops rk3288_ops = { #define RK3328_GRF_MAC_CON0 0x0900 #define RK3328_GRF_MAC_CON1 0x0904 +#define RK3328_GRF_MAC_CON2 0x0908 +#define RK3328_GRF_MACPHY_CON1 0xb04 /* RK3328_GRF_MAC_CON0 */ #define RK3328_GMAC_CLK_RX_DL_CFG(val) HIWORD_UPDATE(val, 0x7F, 7) @@ -332,6 +351,9 @@ static const struct rk_gmac_ops rk3288_ops = { #define RK3328_GMAC_RXCLK_DLY_ENABLE GRF_BIT(1) #define RK3328_GMAC_RXCLK_DLY_DISABLE GRF_CLR_BIT(0) +/* RK3328_GRF_MACPHY_CON1 */ +#define RK3328_MACPHY_RMII_MODE GRF_BIT(9) + static void rk3328_set_to_rgmii(struct rk_priv_data *bsp_priv, int tx_delay, int rx_delay) { @@ -356,18 +378,19 @@ static void rk3328_set_to_rgmii(struct rk_priv_data *bsp_priv, static void rk3328_set_to_rmii(struct rk_priv_data *bsp_priv) { struct device *dev = &bsp_priv->pdev->dev; + unsigned int reg; if (IS_ERR(bsp_priv->grf)) { dev_err(dev, "Missing rockchip,grf property\n"); return; } - regmap_write(bsp_priv->grf, RK3328_GRF_MAC_CON1, + reg = bsp_priv->integrated_phy ? RK3328_GRF_MAC_CON2 : + RK3328_GRF_MAC_CON1; + + regmap_write(bsp_priv->grf, reg, RK3328_GMAC_PHY_INTF_SEL_RMII | RK3328_GMAC_RMII_MODE); - - /* set MAC to RMII mode */ - regmap_write(bsp_priv->grf, RK3328_GRF_MAC_CON1, GRF_BIT(11)); } static void rk3328_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) @@ -395,29 +418,40 @@ static void rk3328_set_rgmii_speed(struct rk_priv_data *bsp_priv, int speed) static void rk3328_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) { struct device *dev = &bsp_priv->pdev->dev; + unsigned int reg; if (IS_ERR(bsp_priv->grf)) { dev_err(dev, "Missing rockchip,grf property\n"); return; } + reg = bsp_priv->integrated_phy ? RK3328_GRF_MAC_CON2 : + RK3328_GRF_MAC_CON1; + if (speed == 10) - regmap_write(bsp_priv->grf, RK3328_GRF_MAC_CON1, + regmap_write(bsp_priv->grf, reg, RK3328_GMAC_RMII_CLK_2_5M | RK3328_GMAC_SPEED_10M); else if (speed == 100) - regmap_write(bsp_priv->grf, RK3328_GRF_MAC_CON1, + regmap_write(bsp_priv->grf, reg, RK3328_GMAC_RMII_CLK_25M | RK3328_GMAC_SPEED_100M); else dev_err(dev, "unknown speed value for RMII! speed=%d", speed); } +static void rk3328_integrated_phy_powerup(struct rk_priv_data *priv) +{ + regmap_write(priv->grf, RK3328_GRF_MACPHY_CON1, + RK3328_MACPHY_RMII_MODE); +} + static const struct rk_gmac_ops rk3328_ops = { .set_to_rgmii = rk3328_set_to_rgmii, .set_to_rmii = rk3328_set_to_rmii, .set_rgmii_speed = rk3328_set_rgmii_speed, .set_rmii_speed = rk3328_set_rmii_speed, + .integrated_phy_powerup = rk3328_integrated_phy_powerup, }; #define RK3366_GRF_SOC_CON6 0x0418 @@ -753,9 +787,107 @@ static const struct rk_gmac_ops rk3399_ops = { .set_rmii_speed = rk3399_set_rmii_speed, }; -static int gmac_clk_init(struct rk_priv_data *bsp_priv) +#define RV1108_GRF_GMAC_CON0 0X0900 + +/* RV1108_GRF_GMAC_CON0 */ +#define RV1108_GMAC_PHY_INTF_SEL_RMII (GRF_CLR_BIT(4) | GRF_CLR_BIT(5) | \ + GRF_BIT(6)) +#define RV1108_GMAC_FLOW_CTRL GRF_BIT(3) +#define RV1108_GMAC_FLOW_CTRL_CLR GRF_CLR_BIT(3) +#define RV1108_GMAC_SPEED_10M GRF_CLR_BIT(2) +#define RV1108_GMAC_SPEED_100M GRF_BIT(2) +#define RV1108_GMAC_RMII_CLK_25M GRF_BIT(7) +#define RV1108_GMAC_RMII_CLK_2_5M GRF_CLR_BIT(7) + +static void rv1108_set_to_rmii(struct rk_priv_data *bsp_priv) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + regmap_write(bsp_priv->grf, RV1108_GRF_GMAC_CON0, + RV1108_GMAC_PHY_INTF_SEL_RMII); +} + +static void rv1108_set_rmii_speed(struct rk_priv_data *bsp_priv, int speed) +{ + struct device *dev = &bsp_priv->pdev->dev; + + if (IS_ERR(bsp_priv->grf)) { + dev_err(dev, "%s: Missing rockchip,grf property\n", __func__); + return; + } + + if (speed == 10) { + regmap_write(bsp_priv->grf, RV1108_GRF_GMAC_CON0, + RV1108_GMAC_RMII_CLK_2_5M | + RV1108_GMAC_SPEED_10M); + } else if (speed == 100) { + regmap_write(bsp_priv->grf, RV1108_GRF_GMAC_CON0, + RV1108_GMAC_RMII_CLK_25M | + RV1108_GMAC_SPEED_100M); + } else { + dev_err(dev, "unknown speed value for RMII! speed=%d", speed); + } +} + +static const struct rk_gmac_ops rv1108_ops = { + .set_to_rmii = rv1108_set_to_rmii, + .set_rmii_speed = rv1108_set_rmii_speed, +}; + +#define RK_GRF_MACPHY_CON0 0xb00 +#define RK_GRF_MACPHY_CON1 0xb04 +#define RK_GRF_MACPHY_CON2 0xb08 +#define RK_GRF_MACPHY_CON3 0xb0c + +#define RK_MACPHY_ENABLE GRF_BIT(0) +#define RK_MACPHY_DISABLE GRF_CLR_BIT(0) +#define RK_MACPHY_CFG_CLK_50M GRF_BIT(14) +#define RK_GMAC2PHY_RMII_MODE (GRF_BIT(6) | GRF_CLR_BIT(7)) +#define RK_GRF_CON2_MACPHY_ID HIWORD_UPDATE(0x1234, 0xffff, 0) +#define RK_GRF_CON3_MACPHY_ID HIWORD_UPDATE(0x35, 0x3f, 0) + +static void rk_gmac_integrated_phy_powerup(struct rk_priv_data *priv) +{ + if (priv->ops->integrated_phy_powerup) + priv->ops->integrated_phy_powerup(priv); + + regmap_write(priv->grf, RK_GRF_MACPHY_CON0, RK_MACPHY_CFG_CLK_50M); + regmap_write(priv->grf, RK_GRF_MACPHY_CON0, RK_GMAC2PHY_RMII_MODE); + + regmap_write(priv->grf, RK_GRF_MACPHY_CON2, RK_GRF_CON2_MACPHY_ID); + regmap_write(priv->grf, RK_GRF_MACPHY_CON3, RK_GRF_CON3_MACPHY_ID); + + if (priv->phy_reset) { + /* PHY needs to be disabled before trying to reset it */ + regmap_write(priv->grf, RK_GRF_MACPHY_CON0, RK_MACPHY_DISABLE); + if (priv->phy_reset) + reset_control_assert(priv->phy_reset); + usleep_range(10, 20); + if (priv->phy_reset) + reset_control_deassert(priv->phy_reset); + usleep_range(10, 20); + regmap_write(priv->grf, RK_GRF_MACPHY_CON0, RK_MACPHY_ENABLE); + msleep(30); + } +} + +static void rk_gmac_integrated_phy_powerdown(struct rk_priv_data *priv) +{ + regmap_write(priv->grf, RK_GRF_MACPHY_CON0, RK_MACPHY_DISABLE); + if (priv->phy_reset) + reset_control_assert(priv->phy_reset); +} + +static int rk_gmac_clk_init(struct plat_stmmacenet_data *plat) { + struct rk_priv_data *bsp_priv = plat->bsp_priv; struct device *dev = &bsp_priv->pdev->dev; + int ret; bsp_priv->clk_enabled = false; @@ -806,6 +938,16 @@ static int gmac_clk_init(struct rk_priv_data *bsp_priv) clk_set_rate(bsp_priv->clk_mac, 50000000); } + if (plat->phy_node && bsp_priv->integrated_phy) { + bsp_priv->clk_phy = of_clk_get(plat->phy_node, 0); + if (IS_ERR(bsp_priv->clk_phy)) { + ret = PTR_ERR(bsp_priv->clk_phy); + dev_err(dev, "Cannot get PHY clock: %d\n", ret); + return -EINVAL; + } + clk_set_rate(bsp_priv->clk_phy, 50000000); + } + return 0; } @@ -829,6 +971,9 @@ static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable) bsp_priv->clk_mac_refout); } + if (!IS_ERR(bsp_priv->clk_phy)) + clk_prepare_enable(bsp_priv->clk_phy); + if (!IS_ERR(bsp_priv->aclk_mac)) clk_prepare_enable(bsp_priv->aclk_mac); @@ -861,6 +1006,9 @@ static int gmac_clk_enable(struct rk_priv_data *bsp_priv, bool enable) bsp_priv->clk_mac_refout); } + if (!IS_ERR(bsp_priv->clk_phy)) + clk_disable_unprepare(bsp_priv->clk_phy); + if (!IS_ERR(bsp_priv->aclk_mac)) clk_disable_unprepare(bsp_priv->aclk_mac); @@ -905,6 +1053,7 @@ static int phy_power_on(struct rk_priv_data *bsp_priv, bool enable) } static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev, + struct plat_stmmacenet_data *plat, const struct rk_gmac_ops *ops) { struct rk_priv_data *bsp_priv; @@ -967,9 +1116,22 @@ static struct rk_priv_data *rk_gmac_setup(struct platform_device *pdev, bsp_priv->grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf"); - bsp_priv->pdev = pdev; - gmac_clk_init(bsp_priv); + if (plat->phy_node) { + bsp_priv->integrated_phy = of_property_read_bool(plat->phy_node, + "phy-is-integrated"); + if (bsp_priv->integrated_phy) { + bsp_priv->phy_reset = of_reset_control_get(plat->phy_node, NULL); + if (IS_ERR(bsp_priv->phy_reset)) { + dev_err(&pdev->dev, "No PHY reset control found.\n"); + bsp_priv->phy_reset = NULL; + } + } + } + dev_info(dev, "integrated PHY? (%s).\n", + bsp_priv->integrated_phy ? "yes" : "no"); + + bsp_priv->pdev = pdev; return bsp_priv; } @@ -1017,6 +1179,9 @@ static int rk_gmac_powerup(struct rk_priv_data *bsp_priv) pm_runtime_enable(dev); pm_runtime_get_sync(dev); + if (bsp_priv->integrated_phy) + rk_gmac_integrated_phy_powerup(bsp_priv); + return 0; } @@ -1024,6 +1189,9 @@ static void rk_gmac_powerdown(struct rk_priv_data *gmac) { struct device *dev = &gmac->pdev->dev; + if (gmac->integrated_phy) + rk_gmac_integrated_phy_powerdown(gmac); + pm_runtime_put_sync(dev); pm_runtime_disable(dev); @@ -1075,12 +1243,16 @@ static int rk_gmac_probe(struct platform_device *pdev) plat_dat->has_gmac = true; plat_dat->fix_mac_speed = rk_fix_speed; - plat_dat->bsp_priv = rk_gmac_setup(pdev, data); + plat_dat->bsp_priv = rk_gmac_setup(pdev, plat_dat, data); if (IS_ERR(plat_dat->bsp_priv)) { ret = PTR_ERR(plat_dat->bsp_priv); goto err_remove_config_dt; } + ret = rk_gmac_clk_init(plat_dat); + if (ret) + return ret; + ret = rk_gmac_powerup(plat_dat->bsp_priv); if (ret) goto err_remove_config_dt; @@ -1147,6 +1319,7 @@ static const struct of_device_id rk_gmac_dwmac_match[] = { { .compatible = "rockchip,rk3366-gmac", .data = &rk3366_ops }, { .compatible = "rockchip,rk3368-gmac", .data = &rk3368_ops }, { .compatible = "rockchip,rk3399-gmac", .data = &rk3399_ops }, + { .compatible = "rockchip,rv1108-gmac", .data = &rv1108_ops }, { } }; MODULE_DEVICE_TABLE(of, rk_gmac_dwmac_match); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index 72ec711fcba2..f5f37bfa1d58 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -248,9 +248,6 @@ int stmmac_mdio_register(struct net_device *ndev) found = 0; for (addr = 0; addr < PHY_MAX_ADDR; addr++) { struct phy_device *phydev = mdiobus_get_phy(new_bus, addr); - int act = 0; - char irq_num[4]; - char *irq_str; if (!phydev) continue; @@ -273,19 +270,6 @@ int stmmac_mdio_register(struct net_device *ndev) if (priv->plat->phy_addr == -1) priv->plat->phy_addr = addr; - act = (priv->plat->phy_addr == addr); - switch (phydev->irq) { - case PHY_POLL: - irq_str = "POLL"; - break; - case PHY_IGNORE_INTERRUPT: - irq_str = "IGNORE"; - break; - default: - sprintf(irq_num, "%d", phydev->irq); - irq_str = irq_num; - break; - } phy_attached_info(phydev); found = 1; } diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c index d71bd80c5b5b..e471a903c654 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ptp.c @@ -152,7 +152,7 @@ static int stmmac_enable(struct ptp_clock_info *ptp, } /* structure describing a PTP hardware clock */ -static struct ptp_clock_info stmmac_ptp_clock_ops = { +static const struct ptp_clock_info stmmac_ptp_clock_ops = { .owner = THIS_MODULE, .name = "stmmac_ptp_clock", .max_adj = 62500000, diff --git a/drivers/net/ethernet/sun/ldmvsw.c b/drivers/net/ethernet/sun/ldmvsw.c index 8603e397097e..5b56c24b6ed2 100644 --- a/drivers/net/ethernet/sun/ldmvsw.c +++ b/drivers/net/ethernet/sun/ldmvsw.c @@ -248,7 +248,7 @@ static struct net_device *vsw_alloc_netdev(u8 hwaddr[], dev->ethtool_ops = &vsw_ethtool_ops; dev->watchdog_timeo = VSW_TX_TIMEOUT; - dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG; + dev->hw_features = NETIF_F_HW_CSUM | NETIF_F_SG; dev->features = dev->hw_features; /* MTU range: 68 - 65535 */ diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c index 4bb04aaf9650..6a4e8e1bbd90 100644 --- a/drivers/net/ethernet/sun/niu.c +++ b/drivers/net/ethernet/sun/niu.c @@ -9221,8 +9221,7 @@ static int niu_get_of_props(struct niu *np) phy_type = of_get_property(dp, "phy-type", &prop_len); if (!phy_type) { - netdev_err(dev, "%s: OF node lacks phy-type property\n", - dp->full_name); + netdev_err(dev, "%pOF: OF node lacks phy-type property\n", dp); return -EINVAL; } @@ -9232,26 +9231,25 @@ static int niu_get_of_props(struct niu *np) strcpy(np->vpd.phy_type, phy_type); if (niu_phy_type_prop_decode(np, np->vpd.phy_type)) { - netdev_err(dev, "%s: Illegal phy string [%s]\n", - dp->full_name, np->vpd.phy_type); + netdev_err(dev, "%pOF: Illegal phy string [%s]\n", + dp, np->vpd.phy_type); return -EINVAL; } mac_addr = of_get_property(dp, "local-mac-address", &prop_len); if (!mac_addr) { - netdev_err(dev, "%s: OF node lacks local-mac-address property\n", - dp->full_name); + netdev_err(dev, "%pOF: OF node lacks local-mac-address property\n", + dp); return -EINVAL; } if (prop_len != dev->addr_len) { - netdev_err(dev, "%s: OF MAC address prop len (%d) is wrong\n", - dp->full_name, prop_len); + netdev_err(dev, "%pOF: OF MAC address prop len (%d) is wrong\n", + dp, prop_len); } memcpy(dev->dev_addr, mac_addr, dev->addr_len); if (!is_valid_ether_addr(&dev->dev_addr[0])) { - netdev_err(dev, "%s: OF MAC address is invalid\n", - dp->full_name); - netdev_err(dev, "%s: [ %pM ]\n", dp->full_name, dev->dev_addr); + netdev_err(dev, "%pOF: OF MAC address is invalid\n", dp); + netdev_err(dev, "%pOF: [ %pM ]\n", dp, dev->dev_addr); return -EINVAL; } @@ -10027,8 +10025,8 @@ static int niu_of_probe(struct platform_device *op) reg = of_get_property(op->dev.of_node, "reg", NULL); if (!reg) { - dev_err(&op->dev, "%s: No 'reg' property, aborting\n", - op->dev.of_node->full_name); + dev_err(&op->dev, "%pOF: No 'reg' property, aborting\n", + op->dev.of_node); return -ENODEV; } diff --git a/drivers/net/ethernet/sun/sunvnet.c b/drivers/net/ethernet/sun/sunvnet.c index 75b167e3fe98..0b95105f7060 100644 --- a/drivers/net/ethernet/sun/sunvnet.c +++ b/drivers/net/ethernet/sun/sunvnet.c @@ -312,7 +312,7 @@ static struct vnet *vnet_new(const u64 *local_mac, dev->watchdog_timeo = VNET_TX_TIMEOUT; dev->hw_features = NETIF_F_TSO | NETIF_F_GSO | NETIF_F_GSO_SOFTWARE | - NETIF_F_IP_CSUM | NETIF_F_SG; + NETIF_F_HW_CSUM | NETIF_F_SG; dev->features = dev->hw_features; /* MTU range: 68 - 65535 */ diff --git a/drivers/net/ethernet/sun/sunvnet_common.c b/drivers/net/ethernet/sun/sunvnet_common.c index 9e86833249d4..ecf456c7b6d1 100644 --- a/drivers/net/ethernet/sun/sunvnet_common.c +++ b/drivers/net/ethernet/sun/sunvnet_common.c @@ -303,7 +303,7 @@ static struct sk_buff *alloc_and_align_skb(struct net_device *dev, return skb; } -static inline void vnet_fullcsum(struct sk_buff *skb) +static inline void vnet_fullcsum_ipv4(struct sk_buff *skb) { struct iphdr *iph = ip_hdr(skb); int offset = skb_transport_offset(skb); @@ -335,6 +335,40 @@ static inline void vnet_fullcsum(struct sk_buff *skb) } } +#if IS_ENABLED(CONFIG_IPV6) +static inline void vnet_fullcsum_ipv6(struct sk_buff *skb) +{ + struct ipv6hdr *ip6h = ipv6_hdr(skb); + int offset = skb_transport_offset(skb); + + if (skb->protocol != htons(ETH_P_IPV6)) + return; + if (ip6h->nexthdr != IPPROTO_TCP && + ip6h->nexthdr != IPPROTO_UDP) + return; + skb->ip_summed = CHECKSUM_NONE; + skb->csum_level = 1; + skb->csum = 0; + if (ip6h->nexthdr == IPPROTO_TCP) { + struct tcphdr *ptcp = tcp_hdr(skb); + + ptcp->check = 0; + skb->csum = skb_checksum(skb, offset, skb->len - offset, 0); + ptcp->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, + skb->len - offset, IPPROTO_TCP, + skb->csum); + } else if (ip6h->nexthdr == IPPROTO_UDP) { + struct udphdr *pudp = udp_hdr(skb); + + pudp->check = 0; + skb->csum = skb_checksum(skb, offset, skb->len - offset, 0); + pudp->check = csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr, + skb->len - offset, IPPROTO_UDP, + skb->csum); + } +} +#endif + static int vnet_rx_one(struct vnet_port *port, struct vio_net_desc *desc) { struct net_device *dev = VNET_PORT_TO_NET_DEVICE(port); @@ -394,9 +428,14 @@ static int vnet_rx_one(struct vnet_port *port, struct vio_net_desc *desc) struct iphdr *iph = ip_hdr(skb); int ihl = iph->ihl * 4; - skb_reset_transport_header(skb); skb_set_transport_header(skb, ihl); - vnet_fullcsum(skb); + vnet_fullcsum_ipv4(skb); +#if IS_ENABLED(CONFIG_IPV6) + } else if (skb->protocol == htons(ETH_P_IPV6)) { + skb_set_transport_header(skb, + sizeof(struct ipv6hdr)); + vnet_fullcsum_ipv6(skb); +#endif } } if (dext->flags & VNET_PKT_HCK_IPV4_HDRCKSUM_OK) { @@ -1115,24 +1154,47 @@ static inline struct sk_buff *vnet_skb_shape(struct sk_buff *skb, int ncookies) if (skb->ip_summed == CHECKSUM_PARTIAL) start = skb_checksum_start_offset(skb); if (start) { - struct iphdr *iph = ip_hdr(nskb); int offset = start + nskb->csum_offset; + /* copy the headers, no csum here */ if (skb_copy_bits(skb, 0, nskb->data, start)) { dev_kfree_skb(nskb); dev_kfree_skb(skb); return NULL; } + + /* copy the rest, with csum calculation */ *(__sum16 *)(skb->data + offset) = 0; csum = skb_copy_and_csum_bits(skb, start, nskb->data + start, skb->len - start, 0); - if (iph->protocol == IPPROTO_TCP || - iph->protocol == IPPROTO_UDP) { - csum = csum_tcpudp_magic(iph->saddr, iph->daddr, - skb->len - start, - iph->protocol, csum); + + /* add in the header checksums */ + if (skb->protocol == htons(ETH_P_IP)) { + struct iphdr *iph = ip_hdr(nskb); + + if (iph->protocol == IPPROTO_TCP || + iph->protocol == IPPROTO_UDP) { + csum = csum_tcpudp_magic(iph->saddr, + iph->daddr, + skb->len - start, + iph->protocol, + csum); + } + } else if (skb->protocol == htons(ETH_P_IPV6)) { + struct ipv6hdr *ip6h = ipv6_hdr(nskb); + + if (ip6h->nexthdr == IPPROTO_TCP || + ip6h->nexthdr == IPPROTO_UDP) { + csum = csum_ipv6_magic(&ip6h->saddr, + &ip6h->daddr, + skb->len - start, + ip6h->nexthdr, + csum); + } } + + /* save the final result */ *(__sum16 *)(nskb->data + offset) = csum; nskb->ip_summed = CHECKSUM_NONE; @@ -1318,8 +1380,14 @@ int sunvnet_start_xmit_common(struct sk_buff *skb, struct net_device *dev, if (unlikely(!skb)) goto out_dropped; - if (skb->ip_summed == CHECKSUM_PARTIAL) - vnet_fullcsum(skb); + if (skb->ip_summed == CHECKSUM_PARTIAL) { + if (skb->protocol == htons(ETH_P_IP)) + vnet_fullcsum_ipv4(skb); +#if IS_ENABLED(CONFIG_IPV6) + else if (skb->protocol == htons(ETH_P_IPV6)) + vnet_fullcsum_ipv6(skb); +#endif + } dr = &port->vio.drings[VIO_DRIVER_TX_RING]; i = skb_get_queue_mapping(skb); diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c index 3b91257683bc..e1b55b8fb8e0 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-net.c @@ -17,6 +17,7 @@ #include <linux/netdevice.h> #include <linux/tcp.h> +#include <linux/interrupt.h> #include "dwc-xlgmac.h" #include "dwc-xlgmac-reg.h" diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index badd0a8caeb9..db8a4bcfc6c7 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -1321,8 +1321,8 @@ static void cpsw_slave_open(struct cpsw_slave *slave, struct cpsw_priv *priv) phy = of_phy_connect(priv->ndev, slave->data->phy_node, &cpsw_adjust_link, 0, slave->data->phy_if); if (!phy) { - dev_err(priv->dev, "phy \"%s\" not found on slave %d\n", - slave->data->phy_node->full_name, + dev_err(priv->dev, "phy \"%pOF\" not found on slave %d\n", + slave->data->phy_node, slave->slave_num); return; } @@ -2670,8 +2670,8 @@ static int cpsw_probe_dt(struct cpsw_platform_data *data, parp = of_get_property(slave_node, "phy_id", &lenp); if (slave_data->phy_node) { dev_dbg(&pdev->dev, - "slave[%d] using phy-handle=\"%s\"\n", - i, slave_data->phy_node->full_name); + "slave[%d] using phy-handle=\"%pOF\"\n", + i, slave_data->phy_node); } else if (of_phy_is_fixed_link(slave_node)) { /* In the case of a fixed PHY, the DT node associated * to the PHY is the Ethernet MAC DT node. @@ -2827,7 +2827,7 @@ static int cpsw_probe_dual_emac(struct cpsw_priv *priv) #define CPSW_QUIRK_IRQ BIT(0) -static struct platform_device_id cpsw_devtype[] = { +static const struct platform_device_id cpsw_devtype[] = { { /* keep it for existing comaptibles */ .name = "cpsw", diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index c2121d214f08..e7b76f6b4f67 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -298,7 +298,7 @@ static long cpts_overflow_check(struct ptp_clock_info *ptp) return (long)delay; } -static struct ptp_clock_info cpts_info = { +static const struct ptp_clock_info cpts_info = { .owner = THIS_MODULE, .name = "CTPS timer", .max_adj = 1000000, diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 64d5527feb2a..4bb561856af5 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -1480,8 +1480,8 @@ static int emac_dev_open(struct net_device *ndev) phydev = of_phy_connect(ndev, priv->phy_node, &emac_adjust_link, 0, 0); if (!phydev) { - dev_err(emac_dev, "could not connect to phy %s\n", - priv->phy_node->full_name); + dev_err(emac_dev, "could not connect to phy %pOF\n", + priv->phy_node); ret = -ENODEV; goto err; } diff --git a/drivers/net/ethernet/ti/davinci_mdio.c b/drivers/net/ethernet/ti/davinci_mdio.c index 33df340db1f1..3c33f4504d8e 100644 --- a/drivers/net/ethernet/ti/davinci_mdio.c +++ b/drivers/net/ethernet/ti/davinci_mdio.c @@ -159,8 +159,10 @@ static int davinci_mdio_reset(struct mii_bus *bus) /* dump hardware version info */ ver = __raw_readl(&data->regs->version); - dev_info(data->dev, "davinci mdio revision %d.%d\n", - (ver >> 8) & 0xff, ver & 0xff); + dev_info(data->dev, + "davinci mdio revision %d.%d, bus freq %ld\n", + (ver >> 8) & 0xff, ver & 0xff, + data->pdata.bus_freq); if (data->skip_scan) goto done; @@ -198,8 +200,10 @@ static inline int wait_for_user_access(struct davinci_mdio_data *data) return 0; reg = __raw_readl(®s->control); - if ((reg & CONTROL_IDLE) == 0) + if ((reg & CONTROL_IDLE) == 0) { + usleep_range(100, 200); continue; + } /* * An emac soft_reset may have clobbered the mdio controller's diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 9d52c3a78621..eb96a6913235 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1877,20 +1877,21 @@ static u16 netcp_select_queue(struct net_device *dev, struct sk_buff *skb, return 0; } -static int netcp_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +static int netcp_setup_tc(struct net_device *dev, enum tc_setup_type type, + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; u8 num_tc; int i; /* setup tc must be called under rtnl lock */ ASSERT_RTNL(); - if (tc->type != TC_SETUP_MQPRIO) - return -EINVAL; + if (type != TC_SETUP_MQPRIO) + return -EOPNOTSUPP; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - num_tc = tc->mqprio->num_tc; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + num_tc = mqprio->num_tc; /* Sanity-check the number of traffic classes requested */ if ((dev->real_num_tx_queues <= 1) || diff --git a/drivers/net/ethernet/tile/tilegx.c b/drivers/net/ethernet/tile/tilegx.c index aec95382ea5c..c00102b8145a 100644 --- a/drivers/net/ethernet/tile/tilegx.c +++ b/drivers/net/ethernet/tile/tilegx.c @@ -873,7 +873,7 @@ static int ptp_mpipe_enable(struct ptp_clock_info *ptp, return -EOPNOTSUPP; } -static struct ptp_clock_info ptp_mpipe_caps = { +static const struct ptp_clock_info ptp_mpipe_caps = { .owner = THIS_MODULE, .name = "mPIPE clock", .max_adj = 999999999, diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index d73da8afe08e..60abc9250f56 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -1089,7 +1089,7 @@ static int temac_of_probe(struct platform_device *op) lp->phy_node = of_parse_phandle(op->dev.of_node, "phy-handle", 0); if (lp->phy_node) - dev_dbg(lp->dev, "using PHY node %s (%p)\n", np->full_name, np); + dev_dbg(lp->dev, "using PHY node %pOF (%p)\n", np, np); /* Add the device attributes */ rc = sysfs_create_group(&lp->dev->kobj, &temac_attr_group); diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet.h b/drivers/net/ethernet/xilinx/xilinx_axienet.h index af27f7d1cbf3..5ef626331f85 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet.h +++ b/drivers/net/ethernet/xilinx/xilinx_axienet.h @@ -389,7 +389,7 @@ struct axidma_bd { * @dma_err_tasklet: Tasklet structure to process Axi DMA errors * @tx_irq: Axidma TX IRQ number * @rx_irq: Axidma RX IRQ number - * @phy_type: Phy type to identify between MII/GMII/RGMII/SGMII/1000 Base-X + * @phy_mode: Phy type to identify between MII/GMII/RGMII/SGMII/1000 Base-X * @options: AxiEthernet option word * @last_link: Phy link state in which the PHY was negotiated earlier * @features: Stores the extended features supported by the axienet hw @@ -432,7 +432,7 @@ struct axienet_local { int tx_irq; int rx_irq; - u32 phy_type; + phy_interface_t phy_mode; u32 options; /* Current options word */ u32 last_link; diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 33c595f4691d..e74e1e897864 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -531,11 +531,11 @@ static void axienet_adjust_link(struct net_device *ndev) link_state = phy->speed | (phy->duplex << 1) | phy->link; if (lp->last_link != link_state) { if ((phy->speed == SPEED_10) || (phy->speed == SPEED_100)) { - if (lp->phy_type == XAE_PHY_TYPE_1000BASE_X) + if (lp->phy_mode == PHY_INTERFACE_MODE_1000BASEX) setspeed = 0; } else { if ((phy->speed == SPEED_1000) && - (lp->phy_type == XAE_PHY_TYPE_MII)) + (lp->phy_mode == PHY_INTERFACE_MODE_MII)) setspeed = 0; } @@ -935,15 +935,8 @@ static int axienet_open(struct net_device *ndev) return ret; if (lp->phy_node) { - if (lp->phy_type == XAE_PHY_TYPE_GMII) { - phydev = of_phy_connect(lp->ndev, lp->phy_node, - axienet_adjust_link, 0, - PHY_INTERFACE_MODE_GMII); - } else if (lp->phy_type == XAE_PHY_TYPE_RGMII_2_0) { - phydev = of_phy_connect(lp->ndev, lp->phy_node, - axienet_adjust_link, 0, - PHY_INTERFACE_MODE_RGMII_ID); - } + phydev = of_phy_connect(lp->ndev, lp->phy_node, + axienet_adjust_link, 0, lp->phy_mode); if (!phydev) dev_err(lp->dev, "of_phy_connect() failed\n"); @@ -1539,7 +1532,38 @@ static int axienet_probe(struct platform_device *pdev) * the device-tree and accordingly set flags. */ of_property_read_u32(pdev->dev.of_node, "xlnx,rxmem", &lp->rxmem); - of_property_read_u32(pdev->dev.of_node, "xlnx,phy-type", &lp->phy_type); + + /* Start with the proprietary, and broken phy_type */ + ret = of_property_read_u32(pdev->dev.of_node, "xlnx,phy-type", &value); + if (!ret) { + netdev_warn(ndev, "Please upgrade your device tree binary blob to use phy-mode"); + switch (value) { + case XAE_PHY_TYPE_MII: + lp->phy_mode = PHY_INTERFACE_MODE_MII; + break; + case XAE_PHY_TYPE_GMII: + lp->phy_mode = PHY_INTERFACE_MODE_GMII; + break; + case XAE_PHY_TYPE_RGMII_2_0: + lp->phy_mode = PHY_INTERFACE_MODE_RGMII_ID; + break; + case XAE_PHY_TYPE_SGMII: + lp->phy_mode = PHY_INTERFACE_MODE_SGMII; + break; + case XAE_PHY_TYPE_1000BASE_X: + lp->phy_mode = PHY_INTERFACE_MODE_1000BASEX; + break; + default: + ret = -EINVAL; + goto free_netdev; + } + } else { + lp->phy_mode = of_get_phy_mode(pdev->dev.of_node); + if (lp->phy_mode < 0) { + ret = -EINVAL; + goto free_netdev; + } + } /* Find the DMA node, map the DMA registers, and decode the DMA IRQs */ np = of_parse_phandle(pdev->dev.of_node, "axistream-connected", 0); diff --git a/drivers/net/ethernet/xircom/xirc2ps_cs.c b/drivers/net/ethernet/xircom/xirc2ps_cs.c index f71883264cc0..fd5288ff53b5 100644 --- a/drivers/net/ethernet/xircom/xirc2ps_cs.c +++ b/drivers/net/ethernet/xircom/xirc2ps_cs.c @@ -1781,7 +1781,7 @@ static int __init setup_xirc2ps_cs(char *str) */ int ints[10] = { -1 }; - str = get_options(str, 9, ints); + str = get_options(str, ARRAY_SIZE(ints), ints); #define MAYBE_SET(X,Y) if (ints[0] >= Y && ints[Y] != -1) { X = ints[Y]; } MAYBE_SET(if_port, 3); |