|
@@ -1,11 +1,34 @@
|
|
|
/* QLogic qede NIC Driver
|
|
|
-* Copyright (c) 2015 QLogic Corporation
|
|
|
-*
|
|
|
-* This software is available 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.
|
|
|
-*/
|
|
|
-
|
|
|
+ * Copyright (c) 2015-2017 QLogic Corporation
|
|
|
+ *
|
|
|
+ * 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/module.h>
|
|
|
#include <linux/pci.h>
|
|
|
#include <linux/version.h>
|
|
@@ -36,6 +59,7 @@
|
|
|
#include <linux/random.h>
|
|
|
#include <net/ip6_checksum.h>
|
|
|
#include <linux/bitops.h>
|
|
|
+#include <linux/vmalloc.h>
|
|
|
#include <linux/qed/qede_roce.h>
|
|
|
#include "qede.h"
|
|
|
|
|
@@ -154,8 +178,12 @@ static int qede_sriov_configure(struct pci_dev *pdev, int num_vfs_param)
|
|
|
{
|
|
|
struct qede_dev *edev = netdev_priv(pci_get_drvdata(pdev));
|
|
|
struct qed_dev_info *qed_info = &edev->dev_info.common;
|
|
|
+ struct qed_update_vport_params *vport_params;
|
|
|
int rc;
|
|
|
|
|
|
+ vport_params = vzalloc(sizeof(*vport_params));
|
|
|
+ if (!vport_params)
|
|
|
+ return -ENOMEM;
|
|
|
DP_VERBOSE(edev, QED_MSG_IOV, "Requested %d VFs\n", num_vfs_param);
|
|
|
|
|
|
rc = edev->ops->iov->configure(edev->cdev, num_vfs_param);
|
|
@@ -163,15 +191,13 @@ static int qede_sriov_configure(struct pci_dev *pdev, int num_vfs_param)
|
|
|
/* Enable/Disable Tx switching for PF */
|
|
|
if ((rc == num_vfs_param) && netif_running(edev->ndev) &&
|
|
|
qed_info->mf_mode != QED_MF_NPAR && qed_info->tx_switching) {
|
|
|
- struct qed_update_vport_params params;
|
|
|
-
|
|
|
- memset(¶ms, 0, sizeof(params));
|
|
|
- params.vport_id = 0;
|
|
|
- params.update_tx_switching_flg = 1;
|
|
|
- params.tx_switching_flg = num_vfs_param ? 1 : 0;
|
|
|
- edev->ops->vport_update(edev->cdev, ¶ms);
|
|
|
+ vport_params->vport_id = 0;
|
|
|
+ vport_params->update_tx_switching_flg = 1;
|
|
|
+ vport_params->tx_switching_flg = num_vfs_param ? 1 : 0;
|
|
|
+ edev->ops->vport_update(edev->cdev, vport_params);
|
|
|
}
|
|
|
|
|
|
+ vfree(vport_params);
|
|
|
return rc;
|
|
|
}
|
|
|
#endif
|
|
@@ -187,18 +213,6 @@ static struct pci_driver qede_pci_driver = {
|
|
|
#endif
|
|
|
};
|
|
|
|
|
|
-static void qede_force_mac(void *dev, u8 *mac, bool forced)
|
|
|
-{
|
|
|
- struct qede_dev *edev = dev;
|
|
|
-
|
|
|
- /* MAC hints take effect only if we haven't set one already */
|
|
|
- if (is_valid_ether_addr(edev->ndev->dev_addr) && !forced)
|
|
|
- return;
|
|
|
-
|
|
|
- ether_addr_copy(edev->ndev->dev_addr, mac);
|
|
|
- ether_addr_copy(edev->primary_mac, mac);
|
|
|
-}
|
|
|
-
|
|
|
static struct qed_eth_cb_ops qede_ll_ops = {
|
|
|
{
|
|
|
.link_update = qede_link_update,
|
|
@@ -294,1673 +308,38 @@ static void __exit qede_cleanup(void)
|
|
|
module_init(qede_init);
|
|
|
module_exit(qede_cleanup);
|
|
|
|
|
|
-/* -------------------------------------------------------------------------
|
|
|
- * START OF FAST-PATH
|
|
|
- * -------------------------------------------------------------------------
|
|
|
- */
|
|
|
-
|
|
|
-/* Unmap the data and free skb */
|
|
|
-static int qede_free_tx_pkt(struct qede_dev *edev,
|
|
|
- struct qede_tx_queue *txq, int *len)
|
|
|
-{
|
|
|
- u16 idx = txq->sw_tx_cons & NUM_TX_BDS_MAX;
|
|
|
- struct sk_buff *skb = txq->sw_tx_ring.skbs[idx].skb;
|
|
|
- struct eth_tx_1st_bd *first_bd;
|
|
|
- struct eth_tx_bd *tx_data_bd;
|
|
|
- int bds_consumed = 0;
|
|
|
- int nbds;
|
|
|
- bool data_split = txq->sw_tx_ring.skbs[idx].flags & QEDE_TSO_SPLIT_BD;
|
|
|
- int i, split_bd_len = 0;
|
|
|
-
|
|
|
- if (unlikely(!skb)) {
|
|
|
- DP_ERR(edev,
|
|
|
- "skb is null for txq idx=%d txq->sw_tx_cons=%d txq->sw_tx_prod=%d\n",
|
|
|
- idx, txq->sw_tx_cons, txq->sw_tx_prod);
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- *len = skb->len;
|
|
|
-
|
|
|
- first_bd = (struct eth_tx_1st_bd *)qed_chain_consume(&txq->tx_pbl);
|
|
|
-
|
|
|
- bds_consumed++;
|
|
|
-
|
|
|
- nbds = first_bd->data.nbds;
|
|
|
-
|
|
|
- if (data_split) {
|
|
|
- struct eth_tx_bd *split = (struct eth_tx_bd *)
|
|
|
- qed_chain_consume(&txq->tx_pbl);
|
|
|
- split_bd_len = BD_UNMAP_LEN(split);
|
|
|
- bds_consumed++;
|
|
|
- }
|
|
|
- dma_unmap_single(&edev->pdev->dev, BD_UNMAP_ADDR(first_bd),
|
|
|
- BD_UNMAP_LEN(first_bd) + split_bd_len, DMA_TO_DEVICE);
|
|
|
-
|
|
|
- /* Unmap the data of the skb frags */
|
|
|
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++, bds_consumed++) {
|
|
|
- tx_data_bd = (struct eth_tx_bd *)
|
|
|
- qed_chain_consume(&txq->tx_pbl);
|
|
|
- dma_unmap_page(&edev->pdev->dev, BD_UNMAP_ADDR(tx_data_bd),
|
|
|
- BD_UNMAP_LEN(tx_data_bd), DMA_TO_DEVICE);
|
|
|
- }
|
|
|
-
|
|
|
- while (bds_consumed++ < nbds)
|
|
|
- qed_chain_consume(&txq->tx_pbl);
|
|
|
-
|
|
|
- /* Free skb */
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
- txq->sw_tx_ring.skbs[idx].skb = NULL;
|
|
|
- txq->sw_tx_ring.skbs[idx].flags = 0;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/* Unmap the data and free skb when mapping failed during start_xmit */
|
|
|
-static void qede_free_failed_tx_pkt(struct qede_tx_queue *txq,
|
|
|
- struct eth_tx_1st_bd *first_bd,
|
|
|
- int nbd, bool data_split)
|
|
|
-{
|
|
|
- u16 idx = txq->sw_tx_prod & NUM_TX_BDS_MAX;
|
|
|
- struct sk_buff *skb = txq->sw_tx_ring.skbs[idx].skb;
|
|
|
- struct eth_tx_bd *tx_data_bd;
|
|
|
- int i, split_bd_len = 0;
|
|
|
-
|
|
|
- /* Return prod to its position before this skb was handled */
|
|
|
- qed_chain_set_prod(&txq->tx_pbl,
|
|
|
- le16_to_cpu(txq->tx_db.data.bd_prod), first_bd);
|
|
|
-
|
|
|
- first_bd = (struct eth_tx_1st_bd *)qed_chain_produce(&txq->tx_pbl);
|
|
|
-
|
|
|
- if (data_split) {
|
|
|
- struct eth_tx_bd *split = (struct eth_tx_bd *)
|
|
|
- qed_chain_produce(&txq->tx_pbl);
|
|
|
- split_bd_len = BD_UNMAP_LEN(split);
|
|
|
- nbd--;
|
|
|
- }
|
|
|
-
|
|
|
- dma_unmap_single(txq->dev, BD_UNMAP_ADDR(first_bd),
|
|
|
- BD_UNMAP_LEN(first_bd) + split_bd_len, DMA_TO_DEVICE);
|
|
|
-
|
|
|
- /* Unmap the data of the skb frags */
|
|
|
- for (i = 0; i < nbd; i++) {
|
|
|
- tx_data_bd = (struct eth_tx_bd *)
|
|
|
- qed_chain_produce(&txq->tx_pbl);
|
|
|
- if (tx_data_bd->nbytes)
|
|
|
- dma_unmap_page(txq->dev,
|
|
|
- BD_UNMAP_ADDR(tx_data_bd),
|
|
|
- BD_UNMAP_LEN(tx_data_bd), DMA_TO_DEVICE);
|
|
|
- }
|
|
|
-
|
|
|
- /* Return again prod to its position before this skb was handled */
|
|
|
- qed_chain_set_prod(&txq->tx_pbl,
|
|
|
- le16_to_cpu(txq->tx_db.data.bd_prod), first_bd);
|
|
|
-
|
|
|
- /* Free skb */
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
- txq->sw_tx_ring.skbs[idx].skb = NULL;
|
|
|
- txq->sw_tx_ring.skbs[idx].flags = 0;
|
|
|
-}
|
|
|
-
|
|
|
-static u32 qede_xmit_type(struct sk_buff *skb, int *ipv6_ext)
|
|
|
-{
|
|
|
- u32 rc = XMIT_L4_CSUM;
|
|
|
- __be16 l3_proto;
|
|
|
-
|
|
|
- if (skb->ip_summed != CHECKSUM_PARTIAL)
|
|
|
- return XMIT_PLAIN;
|
|
|
-
|
|
|
- l3_proto = vlan_get_protocol(skb);
|
|
|
- if (l3_proto == htons(ETH_P_IPV6) &&
|
|
|
- (ipv6_hdr(skb)->nexthdr == NEXTHDR_IPV6))
|
|
|
- *ipv6_ext = 1;
|
|
|
-
|
|
|
- if (skb->encapsulation) {
|
|
|
- rc |= XMIT_ENC;
|
|
|
- if (skb_is_gso(skb)) {
|
|
|
- unsigned short gso_type = skb_shinfo(skb)->gso_type;
|
|
|
-
|
|
|
- if ((gso_type & SKB_GSO_UDP_TUNNEL_CSUM) ||
|
|
|
- (gso_type & SKB_GSO_GRE_CSUM))
|
|
|
- rc |= XMIT_ENC_GSO_L4_CSUM;
|
|
|
-
|
|
|
- rc |= XMIT_LSO;
|
|
|
- return rc;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (skb_is_gso(skb))
|
|
|
- rc |= XMIT_LSO;
|
|
|
-
|
|
|
- return rc;
|
|
|
-}
|
|
|
-
|
|
|
-static void qede_set_params_for_ipv6_ext(struct sk_buff *skb,
|
|
|
- struct eth_tx_2nd_bd *second_bd,
|
|
|
- struct eth_tx_3rd_bd *third_bd)
|
|
|
-{
|
|
|
- u8 l4_proto;
|
|
|
- u16 bd2_bits1 = 0, bd2_bits2 = 0;
|
|
|
-
|
|
|
- bd2_bits1 |= (1 << ETH_TX_DATA_2ND_BD_IPV6_EXT_SHIFT);
|
|
|
-
|
|
|
- bd2_bits2 |= ((((u8 *)skb_transport_header(skb) - skb->data) >> 1) &
|
|
|
- ETH_TX_DATA_2ND_BD_L4_HDR_START_OFFSET_W_MASK)
|
|
|
- << ETH_TX_DATA_2ND_BD_L4_HDR_START_OFFSET_W_SHIFT;
|
|
|
-
|
|
|
- bd2_bits1 |= (ETH_L4_PSEUDO_CSUM_CORRECT_LENGTH <<
|
|
|
- ETH_TX_DATA_2ND_BD_L4_PSEUDO_CSUM_MODE_SHIFT);
|
|
|
-
|
|
|
- if (vlan_get_protocol(skb) == htons(ETH_P_IPV6))
|
|
|
- l4_proto = ipv6_hdr(skb)->nexthdr;
|
|
|
- else
|
|
|
- l4_proto = ip_hdr(skb)->protocol;
|
|
|
-
|
|
|
- if (l4_proto == IPPROTO_UDP)
|
|
|
- bd2_bits1 |= 1 << ETH_TX_DATA_2ND_BD_L4_UDP_SHIFT;
|
|
|
-
|
|
|
- if (third_bd)
|
|
|
- third_bd->data.bitfields |=
|
|
|
- cpu_to_le16(((tcp_hdrlen(skb) / 4) &
|
|
|
- ETH_TX_DATA_3RD_BD_TCP_HDR_LEN_DW_MASK) <<
|
|
|
- ETH_TX_DATA_3RD_BD_TCP_HDR_LEN_DW_SHIFT);
|
|
|
-
|
|
|
- second_bd->data.bitfields1 = cpu_to_le16(bd2_bits1);
|
|
|
- second_bd->data.bitfields2 = cpu_to_le16(bd2_bits2);
|
|
|
-}
|
|
|
-
|
|
|
-static int map_frag_to_bd(struct qede_tx_queue *txq,
|
|
|
- skb_frag_t *frag, struct eth_tx_bd *bd)
|
|
|
-{
|
|
|
- dma_addr_t mapping;
|
|
|
-
|
|
|
- /* Map skb non-linear frag data for DMA */
|
|
|
- mapping = skb_frag_dma_map(txq->dev, frag, 0,
|
|
|
- skb_frag_size(frag), DMA_TO_DEVICE);
|
|
|
- if (unlikely(dma_mapping_error(txq->dev, mapping)))
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- /* Setup the data pointer of the frag data */
|
|
|
- BD_SET_UNMAP_ADDR_LEN(bd, mapping, skb_frag_size(frag));
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static u16 qede_get_skb_hlen(struct sk_buff *skb, bool is_encap_pkt)
|
|
|
-{
|
|
|
- if (is_encap_pkt)
|
|
|
- return (skb_inner_transport_header(skb) +
|
|
|
- inner_tcp_hdrlen(skb) - skb->data);
|
|
|
- else
|
|
|
- return (skb_transport_header(skb) +
|
|
|
- tcp_hdrlen(skb) - skb->data);
|
|
|
-}
|
|
|
-
|
|
|
-/* +2 for 1st BD for headers and 2nd BD for headlen (if required) */
|
|
|
-#if ((MAX_SKB_FRAGS + 2) > ETH_TX_MAX_BDS_PER_NON_LSO_PACKET)
|
|
|
-static bool qede_pkt_req_lin(struct sk_buff *skb, u8 xmit_type)
|
|
|
-{
|
|
|
- int allowed_frags = ETH_TX_MAX_BDS_PER_NON_LSO_PACKET - 1;
|
|
|
-
|
|
|
- if (xmit_type & XMIT_LSO) {
|
|
|
- int hlen;
|
|
|
-
|
|
|
- hlen = qede_get_skb_hlen(skb, xmit_type & XMIT_ENC);
|
|
|
-
|
|
|
- /* linear payload would require its own BD */
|
|
|
- if (skb_headlen(skb) > hlen)
|
|
|
- allowed_frags--;
|
|
|
- }
|
|
|
-
|
|
|
- return (skb_shinfo(skb)->nr_frags > allowed_frags);
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-static inline void qede_update_tx_producer(struct qede_tx_queue *txq)
|
|
|
-{
|
|
|
- /* wmb makes sure that the BDs data is updated before updating the
|
|
|
- * producer, otherwise FW may read old data from the BDs.
|
|
|
- */
|
|
|
- wmb();
|
|
|
- barrier();
|
|
|
- writel(txq->tx_db.raw, txq->doorbell_addr);
|
|
|
-
|
|
|
- /* mmiowb is needed to synchronize doorbell writes from more than one
|
|
|
- * processor. It guarantees that the write arrives to the device before
|
|
|
- * the queue lock is released and another start_xmit is called (possibly
|
|
|
- * on another CPU). Without this barrier, the next doorbell can bypass
|
|
|
- * this doorbell. This is applicable to IA64/Altix systems.
|
|
|
- */
|
|
|
- mmiowb();
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_xdp_xmit(struct qede_dev *edev, struct qede_fastpath *fp,
|
|
|
- struct sw_rx_data *metadata, u16 padding, u16 length)
|
|
|
-{
|
|
|
- struct qede_tx_queue *txq = fp->xdp_tx;
|
|
|
- u16 idx = txq->sw_tx_prod & NUM_TX_BDS_MAX;
|
|
|
- struct eth_tx_1st_bd *first_bd;
|
|
|
-
|
|
|
- if (!qed_chain_get_elem_left(&txq->tx_pbl)) {
|
|
|
- txq->stopped_cnt++;
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
-
|
|
|
- first_bd = (struct eth_tx_1st_bd *)qed_chain_produce(&txq->tx_pbl);
|
|
|
-
|
|
|
- memset(first_bd, 0, sizeof(*first_bd));
|
|
|
- first_bd->data.bd_flags.bitfields =
|
|
|
- BIT(ETH_TX_1ST_BD_FLAGS_START_BD_SHIFT);
|
|
|
- first_bd->data.bitfields |=
|
|
|
- (length & ETH_TX_DATA_1ST_BD_PKT_LEN_MASK) <<
|
|
|
- ETH_TX_DATA_1ST_BD_PKT_LEN_SHIFT;
|
|
|
- first_bd->data.nbds = 1;
|
|
|
-
|
|
|
- /* We can safely ignore the offset, as it's 0 for XDP */
|
|
|
- BD_SET_UNMAP_ADDR_LEN(first_bd, metadata->mapping + padding, length);
|
|
|
-
|
|
|
- /* Synchronize the buffer back to device, as program [probably]
|
|
|
- * has changed it.
|
|
|
- */
|
|
|
- dma_sync_single_for_device(&edev->pdev->dev,
|
|
|
- metadata->mapping + padding,
|
|
|
- length, PCI_DMA_TODEVICE);
|
|
|
-
|
|
|
- txq->sw_tx_ring.pages[idx] = metadata->data;
|
|
|
- txq->sw_tx_prod++;
|
|
|
-
|
|
|
- /* Mark the fastpath for future XDP doorbell */
|
|
|
- fp->xdp_xmit = 1;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/* Main transmit function */
|
|
|
-static netdev_tx_t qede_start_xmit(struct sk_buff *skb,
|
|
|
- struct net_device *ndev)
|
|
|
-{
|
|
|
- struct qede_dev *edev = netdev_priv(ndev);
|
|
|
- struct netdev_queue *netdev_txq;
|
|
|
- struct qede_tx_queue *txq;
|
|
|
- struct eth_tx_1st_bd *first_bd;
|
|
|
- struct eth_tx_2nd_bd *second_bd = NULL;
|
|
|
- struct eth_tx_3rd_bd *third_bd = NULL;
|
|
|
- struct eth_tx_bd *tx_data_bd = NULL;
|
|
|
- u16 txq_index;
|
|
|
- u8 nbd = 0;
|
|
|
- dma_addr_t mapping;
|
|
|
- int rc, frag_idx = 0, ipv6_ext = 0;
|
|
|
- u8 xmit_type;
|
|
|
- u16 idx;
|
|
|
- u16 hlen;
|
|
|
- bool data_split = false;
|
|
|
-
|
|
|
- /* Get tx-queue context and netdev index */
|
|
|
- txq_index = skb_get_queue_mapping(skb);
|
|
|
- WARN_ON(txq_index >= QEDE_TSS_COUNT(edev));
|
|
|
- txq = edev->fp_array[edev->fp_num_rx + txq_index].txq;
|
|
|
- netdev_txq = netdev_get_tx_queue(ndev, txq_index);
|
|
|
-
|
|
|
- WARN_ON(qed_chain_get_elem_left(&txq->tx_pbl) < (MAX_SKB_FRAGS + 1));
|
|
|
-
|
|
|
- xmit_type = qede_xmit_type(skb, &ipv6_ext);
|
|
|
-
|
|
|
-#if ((MAX_SKB_FRAGS + 2) > ETH_TX_MAX_BDS_PER_NON_LSO_PACKET)
|
|
|
- if (qede_pkt_req_lin(skb, xmit_type)) {
|
|
|
- if (skb_linearize(skb)) {
|
|
|
- DP_NOTICE(edev,
|
|
|
- "SKB linearization failed - silently dropping this SKB\n");
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
- return NETDEV_TX_OK;
|
|
|
- }
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
- /* Fill the entry in the SW ring and the BDs in the FW ring */
|
|
|
- idx = txq->sw_tx_prod & NUM_TX_BDS_MAX;
|
|
|
- txq->sw_tx_ring.skbs[idx].skb = skb;
|
|
|
- first_bd = (struct eth_tx_1st_bd *)
|
|
|
- qed_chain_produce(&txq->tx_pbl);
|
|
|
- memset(first_bd, 0, sizeof(*first_bd));
|
|
|
- first_bd->data.bd_flags.bitfields =
|
|
|
- 1 << ETH_TX_1ST_BD_FLAGS_START_BD_SHIFT;
|
|
|
-
|
|
|
- /* Map skb linear data for DMA and set in the first BD */
|
|
|
- mapping = dma_map_single(txq->dev, skb->data,
|
|
|
- skb_headlen(skb), DMA_TO_DEVICE);
|
|
|
- if (unlikely(dma_mapping_error(txq->dev, mapping))) {
|
|
|
- DP_NOTICE(edev, "SKB mapping failed\n");
|
|
|
- qede_free_failed_tx_pkt(txq, first_bd, 0, false);
|
|
|
- qede_update_tx_producer(txq);
|
|
|
- return NETDEV_TX_OK;
|
|
|
- }
|
|
|
- nbd++;
|
|
|
- BD_SET_UNMAP_ADDR_LEN(first_bd, mapping, skb_headlen(skb));
|
|
|
-
|
|
|
- /* In case there is IPv6 with extension headers or LSO we need 2nd and
|
|
|
- * 3rd BDs.
|
|
|
- */
|
|
|
- if (unlikely((xmit_type & XMIT_LSO) | ipv6_ext)) {
|
|
|
- second_bd = (struct eth_tx_2nd_bd *)
|
|
|
- qed_chain_produce(&txq->tx_pbl);
|
|
|
- memset(second_bd, 0, sizeof(*second_bd));
|
|
|
-
|
|
|
- nbd++;
|
|
|
- third_bd = (struct eth_tx_3rd_bd *)
|
|
|
- qed_chain_produce(&txq->tx_pbl);
|
|
|
- memset(third_bd, 0, sizeof(*third_bd));
|
|
|
-
|
|
|
- nbd++;
|
|
|
- /* We need to fill in additional data in second_bd... */
|
|
|
- tx_data_bd = (struct eth_tx_bd *)second_bd;
|
|
|
- }
|
|
|
-
|
|
|
- if (skb_vlan_tag_present(skb)) {
|
|
|
- first_bd->data.vlan = cpu_to_le16(skb_vlan_tag_get(skb));
|
|
|
- first_bd->data.bd_flags.bitfields |=
|
|
|
- 1 << ETH_TX_1ST_BD_FLAGS_VLAN_INSERTION_SHIFT;
|
|
|
- }
|
|
|
-
|
|
|
- /* Fill the parsing flags & params according to the requested offload */
|
|
|
- if (xmit_type & XMIT_L4_CSUM) {
|
|
|
- /* We don't re-calculate IP checksum as it is already done by
|
|
|
- * the upper stack
|
|
|
- */
|
|
|
- first_bd->data.bd_flags.bitfields |=
|
|
|
- 1 << ETH_TX_1ST_BD_FLAGS_L4_CSUM_SHIFT;
|
|
|
-
|
|
|
- if (xmit_type & XMIT_ENC) {
|
|
|
- first_bd->data.bd_flags.bitfields |=
|
|
|
- 1 << ETH_TX_1ST_BD_FLAGS_IP_CSUM_SHIFT;
|
|
|
- first_bd->data.bitfields |=
|
|
|
- 1 << ETH_TX_DATA_1ST_BD_TUNN_FLAG_SHIFT;
|
|
|
- }
|
|
|
-
|
|
|
- /* Legacy FW had flipped behavior in regard to this bit -
|
|
|
- * I.e., needed to set to prevent FW from touching encapsulated
|
|
|
- * packets when it didn't need to.
|
|
|
- */
|
|
|
- if (unlikely(txq->is_legacy))
|
|
|
- first_bd->data.bitfields ^=
|
|
|
- 1 << ETH_TX_DATA_1ST_BD_TUNN_FLAG_SHIFT;
|
|
|
-
|
|
|
- /* If the packet is IPv6 with extension header, indicate that
|
|
|
- * to FW and pass few params, since the device cracker doesn't
|
|
|
- * support parsing IPv6 with extension header/s.
|
|
|
- */
|
|
|
- if (unlikely(ipv6_ext))
|
|
|
- qede_set_params_for_ipv6_ext(skb, second_bd, third_bd);
|
|
|
- }
|
|
|
-
|
|
|
- if (xmit_type & XMIT_LSO) {
|
|
|
- first_bd->data.bd_flags.bitfields |=
|
|
|
- (1 << ETH_TX_1ST_BD_FLAGS_LSO_SHIFT);
|
|
|
- third_bd->data.lso_mss =
|
|
|
- cpu_to_le16(skb_shinfo(skb)->gso_size);
|
|
|
-
|
|
|
- if (unlikely(xmit_type & XMIT_ENC)) {
|
|
|
- first_bd->data.bd_flags.bitfields |=
|
|
|
- 1 << ETH_TX_1ST_BD_FLAGS_TUNN_IP_CSUM_SHIFT;
|
|
|
-
|
|
|
- if (xmit_type & XMIT_ENC_GSO_L4_CSUM) {
|
|
|
- u8 tmp = ETH_TX_1ST_BD_FLAGS_TUNN_L4_CSUM_SHIFT;
|
|
|
-
|
|
|
- first_bd->data.bd_flags.bitfields |= 1 << tmp;
|
|
|
- }
|
|
|
- hlen = qede_get_skb_hlen(skb, true);
|
|
|
- } else {
|
|
|
- first_bd->data.bd_flags.bitfields |=
|
|
|
- 1 << ETH_TX_1ST_BD_FLAGS_IP_CSUM_SHIFT;
|
|
|
- hlen = qede_get_skb_hlen(skb, false);
|
|
|
- }
|
|
|
-
|
|
|
- /* @@@TBD - if will not be removed need to check */
|
|
|
- third_bd->data.bitfields |=
|
|
|
- cpu_to_le16((1 << ETH_TX_DATA_3RD_BD_HDR_NBD_SHIFT));
|
|
|
-
|
|
|
- /* Make life easier for FW guys who can't deal with header and
|
|
|
- * data on same BD. If we need to split, use the second bd...
|
|
|
- */
|
|
|
- if (unlikely(skb_headlen(skb) > hlen)) {
|
|
|
- DP_VERBOSE(edev, NETIF_MSG_TX_QUEUED,
|
|
|
- "TSO split header size is %d (%x:%x)\n",
|
|
|
- first_bd->nbytes, first_bd->addr.hi,
|
|
|
- first_bd->addr.lo);
|
|
|
-
|
|
|
- mapping = HILO_U64(le32_to_cpu(first_bd->addr.hi),
|
|
|
- le32_to_cpu(first_bd->addr.lo)) +
|
|
|
- hlen;
|
|
|
-
|
|
|
- BD_SET_UNMAP_ADDR_LEN(tx_data_bd, mapping,
|
|
|
- le16_to_cpu(first_bd->nbytes) -
|
|
|
- hlen);
|
|
|
-
|
|
|
- /* this marks the BD as one that has no
|
|
|
- * individual mapping
|
|
|
- */
|
|
|
- txq->sw_tx_ring.skbs[idx].flags |= QEDE_TSO_SPLIT_BD;
|
|
|
-
|
|
|
- first_bd->nbytes = cpu_to_le16(hlen);
|
|
|
-
|
|
|
- tx_data_bd = (struct eth_tx_bd *)third_bd;
|
|
|
- data_split = true;
|
|
|
- }
|
|
|
- } else {
|
|
|
- first_bd->data.bitfields |=
|
|
|
- (skb->len & ETH_TX_DATA_1ST_BD_PKT_LEN_MASK) <<
|
|
|
- ETH_TX_DATA_1ST_BD_PKT_LEN_SHIFT;
|
|
|
- }
|
|
|
-
|
|
|
- /* Handle fragmented skb */
|
|
|
- /* special handle for frags inside 2nd and 3rd bds.. */
|
|
|
- while (tx_data_bd && frag_idx < skb_shinfo(skb)->nr_frags) {
|
|
|
- rc = map_frag_to_bd(txq,
|
|
|
- &skb_shinfo(skb)->frags[frag_idx],
|
|
|
- tx_data_bd);
|
|
|
- if (rc) {
|
|
|
- qede_free_failed_tx_pkt(txq, first_bd, nbd, data_split);
|
|
|
- qede_update_tx_producer(txq);
|
|
|
- return NETDEV_TX_OK;
|
|
|
- }
|
|
|
-
|
|
|
- if (tx_data_bd == (struct eth_tx_bd *)second_bd)
|
|
|
- tx_data_bd = (struct eth_tx_bd *)third_bd;
|
|
|
- else
|
|
|
- tx_data_bd = NULL;
|
|
|
-
|
|
|
- frag_idx++;
|
|
|
- }
|
|
|
-
|
|
|
- /* map last frags into 4th, 5th .... */
|
|
|
- for (; frag_idx < skb_shinfo(skb)->nr_frags; frag_idx++, nbd++) {
|
|
|
- tx_data_bd = (struct eth_tx_bd *)
|
|
|
- qed_chain_produce(&txq->tx_pbl);
|
|
|
-
|
|
|
- memset(tx_data_bd, 0, sizeof(*tx_data_bd));
|
|
|
-
|
|
|
- rc = map_frag_to_bd(txq,
|
|
|
- &skb_shinfo(skb)->frags[frag_idx],
|
|
|
- tx_data_bd);
|
|
|
- if (rc) {
|
|
|
- qede_free_failed_tx_pkt(txq, first_bd, nbd, data_split);
|
|
|
- qede_update_tx_producer(txq);
|
|
|
- return NETDEV_TX_OK;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* update the first BD with the actual num BDs */
|
|
|
- first_bd->data.nbds = nbd;
|
|
|
-
|
|
|
- netdev_tx_sent_queue(netdev_txq, skb->len);
|
|
|
-
|
|
|
- skb_tx_timestamp(skb);
|
|
|
-
|
|
|
- /* Advance packet producer only before sending the packet since mapping
|
|
|
- * of pages may fail.
|
|
|
- */
|
|
|
- txq->sw_tx_prod++;
|
|
|
-
|
|
|
- /* 'next page' entries are counted in the producer value */
|
|
|
- txq->tx_db.data.bd_prod =
|
|
|
- cpu_to_le16(qed_chain_get_prod_idx(&txq->tx_pbl));
|
|
|
-
|
|
|
- if (!skb->xmit_more || netif_xmit_stopped(netdev_txq))
|
|
|
- qede_update_tx_producer(txq);
|
|
|
-
|
|
|
- if (unlikely(qed_chain_get_elem_left(&txq->tx_pbl)
|
|
|
- < (MAX_SKB_FRAGS + 1))) {
|
|
|
- if (skb->xmit_more)
|
|
|
- qede_update_tx_producer(txq);
|
|
|
-
|
|
|
- netif_tx_stop_queue(netdev_txq);
|
|
|
- txq->stopped_cnt++;
|
|
|
- DP_VERBOSE(edev, NETIF_MSG_TX_QUEUED,
|
|
|
- "Stop queue was called\n");
|
|
|
- /* paired memory barrier is in qede_tx_int(), we have to keep
|
|
|
- * ordering of set_bit() in netif_tx_stop_queue() and read of
|
|
|
- * fp->bd_tx_cons
|
|
|
- */
|
|
|
- smp_mb();
|
|
|
-
|
|
|
- if (qed_chain_get_elem_left(&txq->tx_pbl)
|
|
|
- >= (MAX_SKB_FRAGS + 1) &&
|
|
|
- (edev->state == QEDE_STATE_OPEN)) {
|
|
|
- netif_tx_wake_queue(netdev_txq);
|
|
|
- DP_VERBOSE(edev, NETIF_MSG_TX_QUEUED,
|
|
|
- "Wake queue was called\n");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return NETDEV_TX_OK;
|
|
|
-}
|
|
|
-
|
|
|
-int qede_txq_has_work(struct qede_tx_queue *txq)
|
|
|
-{
|
|
|
- u16 hw_bd_cons;
|
|
|
-
|
|
|
- /* Tell compiler that consumer and producer can change */
|
|
|
- barrier();
|
|
|
- hw_bd_cons = le16_to_cpu(*txq->hw_cons_ptr);
|
|
|
- if (qed_chain_get_cons_idx(&txq->tx_pbl) == hw_bd_cons + 1)
|
|
|
- return 0;
|
|
|
-
|
|
|
- return hw_bd_cons != qed_chain_get_cons_idx(&txq->tx_pbl);
|
|
|
-}
|
|
|
-
|
|
|
-static void qede_xdp_tx_int(struct qede_dev *edev, struct qede_tx_queue *txq)
|
|
|
-{
|
|
|
- struct eth_tx_1st_bd *bd;
|
|
|
- u16 hw_bd_cons;
|
|
|
-
|
|
|
- hw_bd_cons = le16_to_cpu(*txq->hw_cons_ptr);
|
|
|
- barrier();
|
|
|
-
|
|
|
- while (hw_bd_cons != qed_chain_get_cons_idx(&txq->tx_pbl)) {
|
|
|
- bd = (struct eth_tx_1st_bd *)qed_chain_consume(&txq->tx_pbl);
|
|
|
-
|
|
|
- dma_unmap_single(&edev->pdev->dev, BD_UNMAP_ADDR(bd),
|
|
|
- PAGE_SIZE, DMA_BIDIRECTIONAL);
|
|
|
- __free_page(txq->sw_tx_ring.pages[txq->sw_tx_cons &
|
|
|
- NUM_TX_BDS_MAX]);
|
|
|
-
|
|
|
- txq->sw_tx_cons++;
|
|
|
- txq->xmit_pkts++;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_tx_int(struct qede_dev *edev, struct qede_tx_queue *txq)
|
|
|
-{
|
|
|
- struct netdev_queue *netdev_txq;
|
|
|
- u16 hw_bd_cons;
|
|
|
- unsigned int pkts_compl = 0, bytes_compl = 0;
|
|
|
- int rc;
|
|
|
-
|
|
|
- netdev_txq = netdev_get_tx_queue(edev->ndev, txq->index);
|
|
|
-
|
|
|
- hw_bd_cons = le16_to_cpu(*txq->hw_cons_ptr);
|
|
|
- barrier();
|
|
|
-
|
|
|
- while (hw_bd_cons != qed_chain_get_cons_idx(&txq->tx_pbl)) {
|
|
|
- int len = 0;
|
|
|
-
|
|
|
- rc = qede_free_tx_pkt(edev, txq, &len);
|
|
|
- if (rc) {
|
|
|
- DP_NOTICE(edev, "hw_bd_cons = %d, chain_cons=%d\n",
|
|
|
- hw_bd_cons,
|
|
|
- qed_chain_get_cons_idx(&txq->tx_pbl));
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- bytes_compl += len;
|
|
|
- pkts_compl++;
|
|
|
- txq->sw_tx_cons++;
|
|
|
- txq->xmit_pkts++;
|
|
|
- }
|
|
|
-
|
|
|
- netdev_tx_completed_queue(netdev_txq, pkts_compl, bytes_compl);
|
|
|
-
|
|
|
- /* Need to make the tx_bd_cons update visible to start_xmit()
|
|
|
- * before checking for netif_tx_queue_stopped(). Without the
|
|
|
- * memory barrier, there is a small possibility that
|
|
|
- * start_xmit() will miss it and cause the queue to be stopped
|
|
|
- * forever.
|
|
|
- * On the other hand we need an rmb() here to ensure the proper
|
|
|
- * ordering of bit testing in the following
|
|
|
- * netif_tx_queue_stopped(txq) call.
|
|
|
- */
|
|
|
- smp_mb();
|
|
|
-
|
|
|
- if (unlikely(netif_tx_queue_stopped(netdev_txq))) {
|
|
|
- /* Taking tx_lock is needed to prevent reenabling the queue
|
|
|
- * while it's empty. This could have happen if rx_action() gets
|
|
|
- * suspended in qede_tx_int() after the condition before
|
|
|
- * netif_tx_wake_queue(), while tx_action (qede_start_xmit()):
|
|
|
- *
|
|
|
- * stops the queue->sees fresh tx_bd_cons->releases the queue->
|
|
|
- * sends some packets consuming the whole queue again->
|
|
|
- * stops the queue
|
|
|
- */
|
|
|
-
|
|
|
- __netif_tx_lock(netdev_txq, smp_processor_id());
|
|
|
-
|
|
|
- if ((netif_tx_queue_stopped(netdev_txq)) &&
|
|
|
- (edev->state == QEDE_STATE_OPEN) &&
|
|
|
- (qed_chain_get_elem_left(&txq->tx_pbl)
|
|
|
- >= (MAX_SKB_FRAGS + 1))) {
|
|
|
- netif_tx_wake_queue(netdev_txq);
|
|
|
- DP_VERBOSE(edev, NETIF_MSG_TX_DONE,
|
|
|
- "Wake queue was called\n");
|
|
|
- }
|
|
|
-
|
|
|
- __netif_tx_unlock(netdev_txq);
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-bool qede_has_rx_work(struct qede_rx_queue *rxq)
|
|
|
-{
|
|
|
- u16 hw_comp_cons, sw_comp_cons;
|
|
|
-
|
|
|
- /* Tell compiler that status block fields can change */
|
|
|
- barrier();
|
|
|
-
|
|
|
- hw_comp_cons = le16_to_cpu(*rxq->hw_cons_ptr);
|
|
|
- sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring);
|
|
|
-
|
|
|
- return hw_comp_cons != sw_comp_cons;
|
|
|
-}
|
|
|
-
|
|
|
-static inline void qede_rx_bd_ring_consume(struct qede_rx_queue *rxq)
|
|
|
-{
|
|
|
- qed_chain_consume(&rxq->rx_bd_ring);
|
|
|
- rxq->sw_rx_cons++;
|
|
|
-}
|
|
|
-
|
|
|
-/* This function reuses the buffer(from an offset) from
|
|
|
- * consumer index to producer index in the bd ring
|
|
|
- */
|
|
|
-static inline void qede_reuse_page(struct qede_rx_queue *rxq,
|
|
|
- struct sw_rx_data *curr_cons)
|
|
|
-{
|
|
|
- struct eth_rx_bd *rx_bd_prod = qed_chain_produce(&rxq->rx_bd_ring);
|
|
|
- struct sw_rx_data *curr_prod;
|
|
|
- dma_addr_t new_mapping;
|
|
|
-
|
|
|
- curr_prod = &rxq->sw_rx_ring[rxq->sw_rx_prod & NUM_RX_BDS_MAX];
|
|
|
- *curr_prod = *curr_cons;
|
|
|
-
|
|
|
- new_mapping = curr_prod->mapping + curr_prod->page_offset;
|
|
|
-
|
|
|
- rx_bd_prod->addr.hi = cpu_to_le32(upper_32_bits(new_mapping));
|
|
|
- rx_bd_prod->addr.lo = cpu_to_le32(lower_32_bits(new_mapping));
|
|
|
-
|
|
|
- rxq->sw_rx_prod++;
|
|
|
- curr_cons->data = NULL;
|
|
|
-}
|
|
|
+static int qede_open(struct net_device *ndev);
|
|
|
+static int qede_close(struct net_device *ndev);
|
|
|
|
|
|
-/* In case of allocation failures reuse buffers
|
|
|
- * from consumer index to produce buffers for firmware
|
|
|
- */
|
|
|
-void qede_recycle_rx_bd_ring(struct qede_rx_queue *rxq, u8 count)
|
|
|
+void qede_fill_by_demand_stats(struct qede_dev *edev)
|
|
|
{
|
|
|
- struct sw_rx_data *curr_cons;
|
|
|
+ struct qed_eth_stats stats;
|
|
|
|
|
|
- for (; count > 0; count--) {
|
|
|
- curr_cons = &rxq->sw_rx_ring[rxq->sw_rx_cons & NUM_RX_BDS_MAX];
|
|
|
- qede_reuse_page(rxq, curr_cons);
|
|
|
- qede_rx_bd_ring_consume(rxq);
|
|
|
- }
|
|
|
-}
|
|
|
+ edev->ops->get_vport_stats(edev->cdev, &stats);
|
|
|
+ edev->stats.no_buff_discards = stats.no_buff_discards;
|
|
|
+ edev->stats.packet_too_big_discard = stats.packet_too_big_discard;
|
|
|
+ edev->stats.ttl0_discard = stats.ttl0_discard;
|
|
|
+ edev->stats.rx_ucast_bytes = stats.rx_ucast_bytes;
|
|
|
+ edev->stats.rx_mcast_bytes = stats.rx_mcast_bytes;
|
|
|
+ edev->stats.rx_bcast_bytes = stats.rx_bcast_bytes;
|
|
|
+ edev->stats.rx_ucast_pkts = stats.rx_ucast_pkts;
|
|
|
+ edev->stats.rx_mcast_pkts = stats.rx_mcast_pkts;
|
|
|
+ edev->stats.rx_bcast_pkts = stats.rx_bcast_pkts;
|
|
|
+ edev->stats.mftag_filter_discards = stats.mftag_filter_discards;
|
|
|
+ edev->stats.mac_filter_discards = stats.mac_filter_discards;
|
|
|
|
|
|
-static int qede_alloc_rx_buffer(struct qede_rx_queue *rxq)
|
|
|
-{
|
|
|
- struct sw_rx_data *sw_rx_data;
|
|
|
- struct eth_rx_bd *rx_bd;
|
|
|
- dma_addr_t mapping;
|
|
|
- struct page *data;
|
|
|
-
|
|
|
- data = alloc_pages(GFP_ATOMIC, 0);
|
|
|
- if (unlikely(!data))
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- /* Map the entire page as it would be used
|
|
|
- * for multiple RX buffer segment size mapping.
|
|
|
- */
|
|
|
- mapping = dma_map_page(rxq->dev, data, 0,
|
|
|
- PAGE_SIZE, rxq->data_direction);
|
|
|
- if (unlikely(dma_mapping_error(rxq->dev, mapping))) {
|
|
|
- __free_page(data);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
-
|
|
|
- sw_rx_data = &rxq->sw_rx_ring[rxq->sw_rx_prod & NUM_RX_BDS_MAX];
|
|
|
- sw_rx_data->page_offset = 0;
|
|
|
- sw_rx_data->data = data;
|
|
|
- sw_rx_data->mapping = mapping;
|
|
|
-
|
|
|
- /* Advance PROD and get BD pointer */
|
|
|
- rx_bd = (struct eth_rx_bd *)qed_chain_produce(&rxq->rx_bd_ring);
|
|
|
- WARN_ON(!rx_bd);
|
|
|
- rx_bd->addr.hi = cpu_to_le32(upper_32_bits(mapping));
|
|
|
- rx_bd->addr.lo = cpu_to_le32(lower_32_bits(mapping));
|
|
|
-
|
|
|
- rxq->sw_rx_prod++;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static inline int qede_realloc_rx_buffer(struct qede_rx_queue *rxq,
|
|
|
- struct sw_rx_data *curr_cons)
|
|
|
-{
|
|
|
- /* Move to the next segment in the page */
|
|
|
- curr_cons->page_offset += rxq->rx_buf_seg_size;
|
|
|
-
|
|
|
- if (curr_cons->page_offset == PAGE_SIZE) {
|
|
|
- if (unlikely(qede_alloc_rx_buffer(rxq))) {
|
|
|
- /* Since we failed to allocate new buffer
|
|
|
- * current buffer can be used again.
|
|
|
- */
|
|
|
- curr_cons->page_offset -= rxq->rx_buf_seg_size;
|
|
|
-
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
-
|
|
|
- dma_unmap_page(rxq->dev, curr_cons->mapping,
|
|
|
- PAGE_SIZE, rxq->data_direction);
|
|
|
- } else {
|
|
|
- /* Increment refcount of the page as we don't want
|
|
|
- * network stack to take the ownership of the page
|
|
|
- * which can be recycled multiple times by the driver.
|
|
|
- */
|
|
|
- page_ref_inc(curr_cons->data);
|
|
|
- qede_reuse_page(rxq, curr_cons);
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-void qede_update_rx_prod(struct qede_dev *edev, struct qede_rx_queue *rxq)
|
|
|
-{
|
|
|
- u16 bd_prod = qed_chain_get_prod_idx(&rxq->rx_bd_ring);
|
|
|
- u16 cqe_prod = qed_chain_get_prod_idx(&rxq->rx_comp_ring);
|
|
|
- struct eth_rx_prod_data rx_prods = {0};
|
|
|
-
|
|
|
- /* Update producers */
|
|
|
- rx_prods.bd_prod = cpu_to_le16(bd_prod);
|
|
|
- rx_prods.cqe_prod = cpu_to_le16(cqe_prod);
|
|
|
-
|
|
|
- /* Make sure that the BD and SGE data is updated before updating the
|
|
|
- * producers since FW might read the BD/SGE right after the producer
|
|
|
- * is updated.
|
|
|
- */
|
|
|
- wmb();
|
|
|
-
|
|
|
- internal_ram_wr(rxq->hw_rxq_prod_addr, sizeof(rx_prods),
|
|
|
- (u32 *)&rx_prods);
|
|
|
-
|
|
|
- /* mmiowb is needed to synchronize doorbell writes from more than one
|
|
|
- * processor. It guarantees that the write arrives to the device before
|
|
|
- * the napi lock is released and another qede_poll is called (possibly
|
|
|
- * on another CPU). Without this barrier, the next doorbell can bypass
|
|
|
- * this doorbell. This is applicable to IA64/Altix systems.
|
|
|
- */
|
|
|
- mmiowb();
|
|
|
-}
|
|
|
-
|
|
|
-static void qede_get_rxhash(struct sk_buff *skb, u8 bitfields, __le32 rss_hash)
|
|
|
-{
|
|
|
- enum pkt_hash_types hash_type = PKT_HASH_TYPE_NONE;
|
|
|
- enum rss_hash_type htype;
|
|
|
- u32 hash = 0;
|
|
|
-
|
|
|
- htype = GET_FIELD(bitfields, ETH_FAST_PATH_RX_REG_CQE_RSS_HASH_TYPE);
|
|
|
- if (htype) {
|
|
|
- hash_type = ((htype == RSS_HASH_TYPE_IPV4) ||
|
|
|
- (htype == RSS_HASH_TYPE_IPV6)) ?
|
|
|
- PKT_HASH_TYPE_L3 : PKT_HASH_TYPE_L4;
|
|
|
- hash = le32_to_cpu(rss_hash);
|
|
|
- }
|
|
|
- skb_set_hash(skb, hash, hash_type);
|
|
|
-}
|
|
|
-
|
|
|
-static void qede_set_skb_csum(struct sk_buff *skb, u8 csum_flag)
|
|
|
-{
|
|
|
- skb_checksum_none_assert(skb);
|
|
|
-
|
|
|
- if (csum_flag & QEDE_CSUM_UNNECESSARY)
|
|
|
- skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
|
-
|
|
|
- if (csum_flag & QEDE_TUNN_CSUM_UNNECESSARY)
|
|
|
- skb->csum_level = 1;
|
|
|
-}
|
|
|
-
|
|
|
-static inline void qede_skb_receive(struct qede_dev *edev,
|
|
|
- struct qede_fastpath *fp,
|
|
|
- struct qede_rx_queue *rxq,
|
|
|
- struct sk_buff *skb, u16 vlan_tag)
|
|
|
-{
|
|
|
- if (vlan_tag)
|
|
|
- __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
|
|
|
-
|
|
|
- napi_gro_receive(&fp->napi, skb);
|
|
|
- fp->rxq->rcv_pkts++;
|
|
|
-}
|
|
|
-
|
|
|
-static void qede_set_gro_params(struct qede_dev *edev,
|
|
|
- struct sk_buff *skb,
|
|
|
- struct eth_fast_path_rx_tpa_start_cqe *cqe)
|
|
|
-{
|
|
|
- u16 parsing_flags = le16_to_cpu(cqe->pars_flags.flags);
|
|
|
-
|
|
|
- if (((parsing_flags >> PARSING_AND_ERR_FLAGS_L3TYPE_SHIFT) &
|
|
|
- PARSING_AND_ERR_FLAGS_L3TYPE_MASK) == 2)
|
|
|
- skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
|
|
|
- else
|
|
|
- skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
|
|
|
-
|
|
|
- skb_shinfo(skb)->gso_size = __le16_to_cpu(cqe->len_on_first_bd) -
|
|
|
- cqe->header_len;
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_fill_frag_skb(struct qede_dev *edev,
|
|
|
- struct qede_rx_queue *rxq,
|
|
|
- u8 tpa_agg_index, u16 len_on_bd)
|
|
|
-{
|
|
|
- struct sw_rx_data *current_bd = &rxq->sw_rx_ring[rxq->sw_rx_cons &
|
|
|
- NUM_RX_BDS_MAX];
|
|
|
- struct qede_agg_info *tpa_info = &rxq->tpa_info[tpa_agg_index];
|
|
|
- struct sk_buff *skb = tpa_info->skb;
|
|
|
-
|
|
|
- if (unlikely(tpa_info->state != QEDE_AGG_STATE_START))
|
|
|
- goto out;
|
|
|
-
|
|
|
- /* Add one frag and update the appropriate fields in the skb */
|
|
|
- skb_fill_page_desc(skb, tpa_info->frag_id++,
|
|
|
- current_bd->data, current_bd->page_offset,
|
|
|
- len_on_bd);
|
|
|
-
|
|
|
- if (unlikely(qede_realloc_rx_buffer(rxq, current_bd))) {
|
|
|
- /* Incr page ref count to reuse on allocation failure
|
|
|
- * so that it doesn't get freed while freeing SKB.
|
|
|
- */
|
|
|
- page_ref_inc(current_bd->data);
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- qed_chain_consume(&rxq->rx_bd_ring);
|
|
|
- rxq->sw_rx_cons++;
|
|
|
-
|
|
|
- skb->data_len += len_on_bd;
|
|
|
- skb->truesize += rxq->rx_buf_seg_size;
|
|
|
- skb->len += len_on_bd;
|
|
|
-
|
|
|
- return 0;
|
|
|
-
|
|
|
-out:
|
|
|
- tpa_info->state = QEDE_AGG_STATE_ERROR;
|
|
|
- qede_recycle_rx_bd_ring(rxq, 1);
|
|
|
-
|
|
|
- return -ENOMEM;
|
|
|
-}
|
|
|
-
|
|
|
-static void qede_tpa_start(struct qede_dev *edev,
|
|
|
- struct qede_rx_queue *rxq,
|
|
|
- struct eth_fast_path_rx_tpa_start_cqe *cqe)
|
|
|
-{
|
|
|
- struct qede_agg_info *tpa_info = &rxq->tpa_info[cqe->tpa_agg_index];
|
|
|
- struct eth_rx_bd *rx_bd_cons = qed_chain_consume(&rxq->rx_bd_ring);
|
|
|
- struct eth_rx_bd *rx_bd_prod = qed_chain_produce(&rxq->rx_bd_ring);
|
|
|
- struct sw_rx_data *replace_buf = &tpa_info->buffer;
|
|
|
- dma_addr_t mapping = tpa_info->buffer_mapping;
|
|
|
- struct sw_rx_data *sw_rx_data_cons;
|
|
|
- struct sw_rx_data *sw_rx_data_prod;
|
|
|
-
|
|
|
- sw_rx_data_cons = &rxq->sw_rx_ring[rxq->sw_rx_cons & NUM_RX_BDS_MAX];
|
|
|
- sw_rx_data_prod = &rxq->sw_rx_ring[rxq->sw_rx_prod & NUM_RX_BDS_MAX];
|
|
|
-
|
|
|
- /* Use pre-allocated replacement buffer - we can't release the agg.
|
|
|
- * start until its over and we don't want to risk allocation failing
|
|
|
- * here, so re-allocate when aggregation will be over.
|
|
|
- */
|
|
|
- sw_rx_data_prod->mapping = replace_buf->mapping;
|
|
|
-
|
|
|
- sw_rx_data_prod->data = replace_buf->data;
|
|
|
- rx_bd_prod->addr.hi = cpu_to_le32(upper_32_bits(mapping));
|
|
|
- rx_bd_prod->addr.lo = cpu_to_le32(lower_32_bits(mapping));
|
|
|
- sw_rx_data_prod->page_offset = replace_buf->page_offset;
|
|
|
-
|
|
|
- rxq->sw_rx_prod++;
|
|
|
-
|
|
|
- /* move partial skb from cons to pool (don't unmap yet)
|
|
|
- * save mapping, incase we drop the packet later on.
|
|
|
- */
|
|
|
- tpa_info->buffer = *sw_rx_data_cons;
|
|
|
- mapping = HILO_U64(le32_to_cpu(rx_bd_cons->addr.hi),
|
|
|
- le32_to_cpu(rx_bd_cons->addr.lo));
|
|
|
-
|
|
|
- tpa_info->buffer_mapping = mapping;
|
|
|
- rxq->sw_rx_cons++;
|
|
|
-
|
|
|
- /* set tpa state to start only if we are able to allocate skb
|
|
|
- * for this aggregation, otherwise mark as error and aggregation will
|
|
|
- * be dropped
|
|
|
- */
|
|
|
- tpa_info->skb = netdev_alloc_skb(edev->ndev,
|
|
|
- le16_to_cpu(cqe->len_on_first_bd));
|
|
|
- if (unlikely(!tpa_info->skb)) {
|
|
|
- DP_NOTICE(edev, "Failed to allocate SKB for gro\n");
|
|
|
- tpa_info->state = QEDE_AGG_STATE_ERROR;
|
|
|
- goto cons_buf;
|
|
|
- }
|
|
|
-
|
|
|
- /* Start filling in the aggregation info */
|
|
|
- skb_put(tpa_info->skb, le16_to_cpu(cqe->len_on_first_bd));
|
|
|
- tpa_info->frag_id = 0;
|
|
|
- tpa_info->state = QEDE_AGG_STATE_START;
|
|
|
-
|
|
|
- /* Store some information from first CQE */
|
|
|
- tpa_info->start_cqe_placement_offset = cqe->placement_offset;
|
|
|
- tpa_info->start_cqe_bd_len = le16_to_cpu(cqe->len_on_first_bd);
|
|
|
- if ((le16_to_cpu(cqe->pars_flags.flags) >>
|
|
|
- PARSING_AND_ERR_FLAGS_TAG8021QEXIST_SHIFT) &
|
|
|
- PARSING_AND_ERR_FLAGS_TAG8021QEXIST_MASK)
|
|
|
- tpa_info->vlan_tag = le16_to_cpu(cqe->vlan_tag);
|
|
|
- else
|
|
|
- tpa_info->vlan_tag = 0;
|
|
|
-
|
|
|
- qede_get_rxhash(tpa_info->skb, cqe->bitfields, cqe->rss_hash);
|
|
|
-
|
|
|
- /* This is needed in order to enable forwarding support */
|
|
|
- qede_set_gro_params(edev, tpa_info->skb, cqe);
|
|
|
-
|
|
|
-cons_buf: /* We still need to handle bd_len_list to consume buffers */
|
|
|
- if (likely(cqe->ext_bd_len_list[0]))
|
|
|
- qede_fill_frag_skb(edev, rxq, cqe->tpa_agg_index,
|
|
|
- le16_to_cpu(cqe->ext_bd_len_list[0]));
|
|
|
-
|
|
|
- if (unlikely(cqe->ext_bd_len_list[1])) {
|
|
|
- DP_ERR(edev,
|
|
|
- "Unlikely - got a TPA aggregation with more than one ext_bd_len_list entry in the TPA start\n");
|
|
|
- tpa_info->state = QEDE_AGG_STATE_ERROR;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-#ifdef CONFIG_INET
|
|
|
-static void qede_gro_ip_csum(struct sk_buff *skb)
|
|
|
-{
|
|
|
- const struct iphdr *iph = ip_hdr(skb);
|
|
|
- struct tcphdr *th;
|
|
|
-
|
|
|
- skb_set_transport_header(skb, sizeof(struct iphdr));
|
|
|
- th = tcp_hdr(skb);
|
|
|
-
|
|
|
- th->check = ~tcp_v4_check(skb->len - skb_transport_offset(skb),
|
|
|
- iph->saddr, iph->daddr, 0);
|
|
|
-
|
|
|
- tcp_gro_complete(skb);
|
|
|
-}
|
|
|
-
|
|
|
-static void qede_gro_ipv6_csum(struct sk_buff *skb)
|
|
|
-{
|
|
|
- struct ipv6hdr *iph = ipv6_hdr(skb);
|
|
|
- struct tcphdr *th;
|
|
|
-
|
|
|
- skb_set_transport_header(skb, sizeof(struct ipv6hdr));
|
|
|
- th = tcp_hdr(skb);
|
|
|
-
|
|
|
- th->check = ~tcp_v6_check(skb->len - skb_transport_offset(skb),
|
|
|
- &iph->saddr, &iph->daddr, 0);
|
|
|
- tcp_gro_complete(skb);
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-static void qede_gro_receive(struct qede_dev *edev,
|
|
|
- struct qede_fastpath *fp,
|
|
|
- struct sk_buff *skb,
|
|
|
- u16 vlan_tag)
|
|
|
-{
|
|
|
- /* FW can send a single MTU sized packet from gro flow
|
|
|
- * due to aggregation timeout/last segment etc. which
|
|
|
- * is not expected to be a gro packet. If a skb has zero
|
|
|
- * frags then simply push it in the stack as non gso skb.
|
|
|
- */
|
|
|
- if (unlikely(!skb->data_len)) {
|
|
|
- skb_shinfo(skb)->gso_type = 0;
|
|
|
- skb_shinfo(skb)->gso_size = 0;
|
|
|
- goto send_skb;
|
|
|
- }
|
|
|
-
|
|
|
-#ifdef CONFIG_INET
|
|
|
- if (skb_shinfo(skb)->gso_size) {
|
|
|
- skb_reset_network_header(skb);
|
|
|
-
|
|
|
- switch (skb->protocol) {
|
|
|
- case htons(ETH_P_IP):
|
|
|
- qede_gro_ip_csum(skb);
|
|
|
- break;
|
|
|
- case htons(ETH_P_IPV6):
|
|
|
- qede_gro_ipv6_csum(skb);
|
|
|
- break;
|
|
|
- default:
|
|
|
- DP_ERR(edev,
|
|
|
- "Error: FW GRO supports only IPv4/IPv6, not 0x%04x\n",
|
|
|
- ntohs(skb->protocol));
|
|
|
- }
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
-send_skb:
|
|
|
- skb_record_rx_queue(skb, fp->rxq->rxq_id);
|
|
|
- qede_skb_receive(edev, fp, fp->rxq, skb, vlan_tag);
|
|
|
-}
|
|
|
-
|
|
|
-static inline void qede_tpa_cont(struct qede_dev *edev,
|
|
|
- struct qede_rx_queue *rxq,
|
|
|
- struct eth_fast_path_rx_tpa_cont_cqe *cqe)
|
|
|
-{
|
|
|
- int i;
|
|
|
-
|
|
|
- for (i = 0; cqe->len_list[i]; i++)
|
|
|
- qede_fill_frag_skb(edev, rxq, cqe->tpa_agg_index,
|
|
|
- le16_to_cpu(cqe->len_list[i]));
|
|
|
-
|
|
|
- if (unlikely(i > 1))
|
|
|
- DP_ERR(edev,
|
|
|
- "Strange - TPA cont with more than a single len_list entry\n");
|
|
|
-}
|
|
|
-
|
|
|
-static void qede_tpa_end(struct qede_dev *edev,
|
|
|
- struct qede_fastpath *fp,
|
|
|
- struct eth_fast_path_rx_tpa_end_cqe *cqe)
|
|
|
-{
|
|
|
- struct qede_rx_queue *rxq = fp->rxq;
|
|
|
- struct qede_agg_info *tpa_info;
|
|
|
- struct sk_buff *skb;
|
|
|
- int i;
|
|
|
-
|
|
|
- tpa_info = &rxq->tpa_info[cqe->tpa_agg_index];
|
|
|
- skb = tpa_info->skb;
|
|
|
-
|
|
|
- for (i = 0; cqe->len_list[i]; i++)
|
|
|
- qede_fill_frag_skb(edev, rxq, cqe->tpa_agg_index,
|
|
|
- le16_to_cpu(cqe->len_list[i]));
|
|
|
- if (unlikely(i > 1))
|
|
|
- DP_ERR(edev,
|
|
|
- "Strange - TPA emd with more than a single len_list entry\n");
|
|
|
-
|
|
|
- if (unlikely(tpa_info->state != QEDE_AGG_STATE_START))
|
|
|
- goto err;
|
|
|
-
|
|
|
- /* Sanity */
|
|
|
- if (unlikely(cqe->num_of_bds != tpa_info->frag_id + 1))
|
|
|
- DP_ERR(edev,
|
|
|
- "Strange - TPA had %02x BDs, but SKB has only %d frags\n",
|
|
|
- cqe->num_of_bds, tpa_info->frag_id);
|
|
|
- if (unlikely(skb->len != le16_to_cpu(cqe->total_packet_len)))
|
|
|
- DP_ERR(edev,
|
|
|
- "Strange - total packet len [cqe] is %4x but SKB has len %04x\n",
|
|
|
- le16_to_cpu(cqe->total_packet_len), skb->len);
|
|
|
-
|
|
|
- memcpy(skb->data,
|
|
|
- page_address(tpa_info->buffer.data) +
|
|
|
- tpa_info->start_cqe_placement_offset +
|
|
|
- tpa_info->buffer.page_offset, tpa_info->start_cqe_bd_len);
|
|
|
-
|
|
|
- /* Finalize the SKB */
|
|
|
- skb->protocol = eth_type_trans(skb, edev->ndev);
|
|
|
- skb->ip_summed = CHECKSUM_UNNECESSARY;
|
|
|
-
|
|
|
- /* tcp_gro_complete() will copy NAPI_GRO_CB(skb)->count
|
|
|
- * to skb_shinfo(skb)->gso_segs
|
|
|
- */
|
|
|
- NAPI_GRO_CB(skb)->count = le16_to_cpu(cqe->num_of_coalesced_segs);
|
|
|
-
|
|
|
- qede_gro_receive(edev, fp, skb, tpa_info->vlan_tag);
|
|
|
-
|
|
|
- tpa_info->state = QEDE_AGG_STATE_NONE;
|
|
|
-
|
|
|
- return;
|
|
|
-err:
|
|
|
- tpa_info->state = QEDE_AGG_STATE_NONE;
|
|
|
- dev_kfree_skb_any(tpa_info->skb);
|
|
|
- tpa_info->skb = NULL;
|
|
|
-}
|
|
|
-
|
|
|
-static bool qede_tunn_exist(u16 flag)
|
|
|
-{
|
|
|
- return !!(flag & (PARSING_AND_ERR_FLAGS_TUNNELEXIST_MASK <<
|
|
|
- PARSING_AND_ERR_FLAGS_TUNNELEXIST_SHIFT));
|
|
|
-}
|
|
|
-
|
|
|
-static u8 qede_check_tunn_csum(u16 flag)
|
|
|
-{
|
|
|
- u16 csum_flag = 0;
|
|
|
- u8 tcsum = 0;
|
|
|
-
|
|
|
- if (flag & (PARSING_AND_ERR_FLAGS_TUNNELL4CHKSMWASCALCULATED_MASK <<
|
|
|
- PARSING_AND_ERR_FLAGS_TUNNELL4CHKSMWASCALCULATED_SHIFT))
|
|
|
- csum_flag |= PARSING_AND_ERR_FLAGS_TUNNELL4CHKSMERROR_MASK <<
|
|
|
- PARSING_AND_ERR_FLAGS_TUNNELL4CHKSMERROR_SHIFT;
|
|
|
-
|
|
|
- if (flag & (PARSING_AND_ERR_FLAGS_L4CHKSMWASCALCULATED_MASK <<
|
|
|
- PARSING_AND_ERR_FLAGS_L4CHKSMWASCALCULATED_SHIFT)) {
|
|
|
- csum_flag |= PARSING_AND_ERR_FLAGS_L4CHKSMERROR_MASK <<
|
|
|
- PARSING_AND_ERR_FLAGS_L4CHKSMERROR_SHIFT;
|
|
|
- tcsum = QEDE_TUNN_CSUM_UNNECESSARY;
|
|
|
- }
|
|
|
-
|
|
|
- csum_flag |= PARSING_AND_ERR_FLAGS_TUNNELIPHDRERROR_MASK <<
|
|
|
- PARSING_AND_ERR_FLAGS_TUNNELIPHDRERROR_SHIFT |
|
|
|
- PARSING_AND_ERR_FLAGS_IPHDRERROR_MASK <<
|
|
|
- PARSING_AND_ERR_FLAGS_IPHDRERROR_SHIFT;
|
|
|
-
|
|
|
- if (csum_flag & flag)
|
|
|
- return QEDE_CSUM_ERROR;
|
|
|
-
|
|
|
- return QEDE_CSUM_UNNECESSARY | tcsum;
|
|
|
-}
|
|
|
-
|
|
|
-static u8 qede_check_notunn_csum(u16 flag)
|
|
|
-{
|
|
|
- u16 csum_flag = 0;
|
|
|
- u8 csum = 0;
|
|
|
-
|
|
|
- if (flag & (PARSING_AND_ERR_FLAGS_L4CHKSMWASCALCULATED_MASK <<
|
|
|
- PARSING_AND_ERR_FLAGS_L4CHKSMWASCALCULATED_SHIFT)) {
|
|
|
- csum_flag |= PARSING_AND_ERR_FLAGS_L4CHKSMERROR_MASK <<
|
|
|
- PARSING_AND_ERR_FLAGS_L4CHKSMERROR_SHIFT;
|
|
|
- csum = QEDE_CSUM_UNNECESSARY;
|
|
|
- }
|
|
|
-
|
|
|
- csum_flag |= PARSING_AND_ERR_FLAGS_IPHDRERROR_MASK <<
|
|
|
- PARSING_AND_ERR_FLAGS_IPHDRERROR_SHIFT;
|
|
|
-
|
|
|
- if (csum_flag & flag)
|
|
|
- return QEDE_CSUM_ERROR;
|
|
|
-
|
|
|
- return csum;
|
|
|
-}
|
|
|
-
|
|
|
-static u8 qede_check_csum(u16 flag)
|
|
|
-{
|
|
|
- if (!qede_tunn_exist(flag))
|
|
|
- return qede_check_notunn_csum(flag);
|
|
|
- else
|
|
|
- return qede_check_tunn_csum(flag);
|
|
|
-}
|
|
|
-
|
|
|
-static bool qede_pkt_is_ip_fragmented(struct eth_fast_path_rx_reg_cqe *cqe,
|
|
|
- u16 flag)
|
|
|
-{
|
|
|
- u8 tun_pars_flg = cqe->tunnel_pars_flags.flags;
|
|
|
-
|
|
|
- if ((tun_pars_flg & (ETH_TUNNEL_PARSING_FLAGS_IPV4_FRAGMENT_MASK <<
|
|
|
- ETH_TUNNEL_PARSING_FLAGS_IPV4_FRAGMENT_SHIFT)) ||
|
|
|
- (flag & (PARSING_AND_ERR_FLAGS_IPV4FRAG_MASK <<
|
|
|
- PARSING_AND_ERR_FLAGS_IPV4FRAG_SHIFT)))
|
|
|
- return true;
|
|
|
-
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-/* Return true iff packet is to be passed to stack */
|
|
|
-static bool qede_rx_xdp(struct qede_dev *edev,
|
|
|
- struct qede_fastpath *fp,
|
|
|
- struct qede_rx_queue *rxq,
|
|
|
- struct bpf_prog *prog,
|
|
|
- struct sw_rx_data *bd,
|
|
|
- struct eth_fast_path_rx_reg_cqe *cqe)
|
|
|
-{
|
|
|
- u16 len = le16_to_cpu(cqe->len_on_first_bd);
|
|
|
- struct xdp_buff xdp;
|
|
|
- enum xdp_action act;
|
|
|
-
|
|
|
- xdp.data = page_address(bd->data) + cqe->placement_offset;
|
|
|
- xdp.data_end = xdp.data + len;
|
|
|
-
|
|
|
- /* Queues always have a full reset currently, so for the time
|
|
|
- * being until there's atomic program replace just mark read
|
|
|
- * side for map helpers.
|
|
|
- */
|
|
|
- rcu_read_lock();
|
|
|
- act = bpf_prog_run_xdp(prog, &xdp);
|
|
|
- rcu_read_unlock();
|
|
|
-
|
|
|
- if (act == XDP_PASS)
|
|
|
- return true;
|
|
|
-
|
|
|
- /* Count number of packets not to be passed to stack */
|
|
|
- rxq->xdp_no_pass++;
|
|
|
-
|
|
|
- switch (act) {
|
|
|
- case XDP_TX:
|
|
|
- /* We need the replacement buffer before transmit. */
|
|
|
- if (qede_alloc_rx_buffer(rxq)) {
|
|
|
- qede_recycle_rx_bd_ring(rxq, 1);
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- /* Now if there's a transmission problem, we'd still have to
|
|
|
- * throw current buffer, as replacement was already allocated.
|
|
|
- */
|
|
|
- if (qede_xdp_xmit(edev, fp, bd, cqe->placement_offset, len)) {
|
|
|
- dma_unmap_page(rxq->dev, bd->mapping,
|
|
|
- PAGE_SIZE, DMA_BIDIRECTIONAL);
|
|
|
- __free_page(bd->data);
|
|
|
- }
|
|
|
-
|
|
|
- /* Regardless, we've consumed an Rx BD */
|
|
|
- qede_rx_bd_ring_consume(rxq);
|
|
|
- return false;
|
|
|
-
|
|
|
- default:
|
|
|
- bpf_warn_invalid_xdp_action(act);
|
|
|
- case XDP_ABORTED:
|
|
|
- case XDP_DROP:
|
|
|
- qede_recycle_rx_bd_ring(rxq, cqe->bd_num);
|
|
|
- }
|
|
|
-
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-static struct sk_buff *qede_rx_allocate_skb(struct qede_dev *edev,
|
|
|
- struct qede_rx_queue *rxq,
|
|
|
- struct sw_rx_data *bd, u16 len,
|
|
|
- u16 pad)
|
|
|
-{
|
|
|
- unsigned int offset = bd->page_offset;
|
|
|
- struct skb_frag_struct *frag;
|
|
|
- struct page *page = bd->data;
|
|
|
- unsigned int pull_len;
|
|
|
- struct sk_buff *skb;
|
|
|
- unsigned char *va;
|
|
|
-
|
|
|
- /* Allocate a new SKB with a sufficient large header len */
|
|
|
- skb = netdev_alloc_skb(edev->ndev, QEDE_RX_HDR_SIZE);
|
|
|
- if (unlikely(!skb))
|
|
|
- return NULL;
|
|
|
-
|
|
|
- /* Copy data into SKB - if it's small, we can simply copy it and
|
|
|
- * re-use the already allcoated & mapped memory.
|
|
|
- */
|
|
|
- if (len + pad <= edev->rx_copybreak) {
|
|
|
- memcpy(skb_put(skb, len),
|
|
|
- page_address(page) + pad + offset, len);
|
|
|
- qede_reuse_page(rxq, bd);
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- frag = &skb_shinfo(skb)->frags[0];
|
|
|
-
|
|
|
- skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
|
|
|
- page, pad + offset, len, rxq->rx_buf_seg_size);
|
|
|
-
|
|
|
- va = skb_frag_address(frag);
|
|
|
- pull_len = eth_get_headlen(va, QEDE_RX_HDR_SIZE);
|
|
|
-
|
|
|
- /* Align the pull_len to optimize memcpy */
|
|
|
- memcpy(skb->data, va, ALIGN(pull_len, sizeof(long)));
|
|
|
-
|
|
|
- /* Correct the skb & frag sizes offset after the pull */
|
|
|
- skb_frag_size_sub(frag, pull_len);
|
|
|
- frag->page_offset += pull_len;
|
|
|
- skb->data_len -= pull_len;
|
|
|
- skb->tail += pull_len;
|
|
|
-
|
|
|
- if (unlikely(qede_realloc_rx_buffer(rxq, bd))) {
|
|
|
- /* Incr page ref count to reuse on allocation failure so
|
|
|
- * that it doesn't get freed while freeing SKB [as its
|
|
|
- * already mapped there].
|
|
|
- */
|
|
|
- page_ref_inc(page);
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
-out:
|
|
|
- /* We've consumed the first BD and prepared an SKB */
|
|
|
- qede_rx_bd_ring_consume(rxq);
|
|
|
- return skb;
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_rx_build_jumbo(struct qede_dev *edev,
|
|
|
- struct qede_rx_queue *rxq,
|
|
|
- struct sk_buff *skb,
|
|
|
- struct eth_fast_path_rx_reg_cqe *cqe,
|
|
|
- u16 first_bd_len)
|
|
|
-{
|
|
|
- u16 pkt_len = le16_to_cpu(cqe->pkt_len);
|
|
|
- struct sw_rx_data *bd;
|
|
|
- u16 bd_cons_idx;
|
|
|
- u8 num_frags;
|
|
|
-
|
|
|
- pkt_len -= first_bd_len;
|
|
|
-
|
|
|
- /* We've already used one BD for the SKB. Now take care of the rest */
|
|
|
- for (num_frags = cqe->bd_num - 1; num_frags > 0; num_frags--) {
|
|
|
- u16 cur_size = pkt_len > rxq->rx_buf_size ? rxq->rx_buf_size :
|
|
|
- pkt_len;
|
|
|
-
|
|
|
- if (unlikely(!cur_size)) {
|
|
|
- DP_ERR(edev,
|
|
|
- "Still got %d BDs for mapping jumbo, but length became 0\n",
|
|
|
- num_frags);
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- /* We need a replacement buffer for each BD */
|
|
|
- if (unlikely(qede_alloc_rx_buffer(rxq)))
|
|
|
- goto out;
|
|
|
-
|
|
|
- /* Now that we've allocated the replacement buffer,
|
|
|
- * we can safely consume the next BD and map it to the SKB.
|
|
|
- */
|
|
|
- bd_cons_idx = rxq->sw_rx_cons & NUM_RX_BDS_MAX;
|
|
|
- bd = &rxq->sw_rx_ring[bd_cons_idx];
|
|
|
- qede_rx_bd_ring_consume(rxq);
|
|
|
-
|
|
|
- dma_unmap_page(rxq->dev, bd->mapping,
|
|
|
- PAGE_SIZE, DMA_FROM_DEVICE);
|
|
|
-
|
|
|
- skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags++,
|
|
|
- bd->data, 0, cur_size);
|
|
|
-
|
|
|
- skb->truesize += PAGE_SIZE;
|
|
|
- skb->data_len += cur_size;
|
|
|
- skb->len += cur_size;
|
|
|
- pkt_len -= cur_size;
|
|
|
- }
|
|
|
-
|
|
|
- if (unlikely(pkt_len))
|
|
|
- DP_ERR(edev,
|
|
|
- "Mapped all BDs of jumbo, but still have %d bytes\n",
|
|
|
- pkt_len);
|
|
|
-
|
|
|
-out:
|
|
|
- return num_frags;
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_rx_process_tpa_cqe(struct qede_dev *edev,
|
|
|
- struct qede_fastpath *fp,
|
|
|
- struct qede_rx_queue *rxq,
|
|
|
- union eth_rx_cqe *cqe,
|
|
|
- enum eth_rx_cqe_type type)
|
|
|
-{
|
|
|
- switch (type) {
|
|
|
- case ETH_RX_CQE_TYPE_TPA_START:
|
|
|
- qede_tpa_start(edev, rxq, &cqe->fast_path_tpa_start);
|
|
|
- return 0;
|
|
|
- case ETH_RX_CQE_TYPE_TPA_CONT:
|
|
|
- qede_tpa_cont(edev, rxq, &cqe->fast_path_tpa_cont);
|
|
|
- return 0;
|
|
|
- case ETH_RX_CQE_TYPE_TPA_END:
|
|
|
- qede_tpa_end(edev, fp, &cqe->fast_path_tpa_end);
|
|
|
- return 1;
|
|
|
- default:
|
|
|
- return 0;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_rx_process_cqe(struct qede_dev *edev,
|
|
|
- struct qede_fastpath *fp,
|
|
|
- struct qede_rx_queue *rxq)
|
|
|
-{
|
|
|
- struct bpf_prog *xdp_prog = READ_ONCE(rxq->xdp_prog);
|
|
|
- struct eth_fast_path_rx_reg_cqe *fp_cqe;
|
|
|
- u16 len, pad, bd_cons_idx, parse_flag;
|
|
|
- enum eth_rx_cqe_type cqe_type;
|
|
|
- union eth_rx_cqe *cqe;
|
|
|
- struct sw_rx_data *bd;
|
|
|
- struct sk_buff *skb;
|
|
|
- __le16 flags;
|
|
|
- u8 csum_flag;
|
|
|
-
|
|
|
- /* Get the CQE from the completion ring */
|
|
|
- cqe = (union eth_rx_cqe *)qed_chain_consume(&rxq->rx_comp_ring);
|
|
|
- cqe_type = cqe->fast_path_regular.type;
|
|
|
-
|
|
|
- /* Process an unlikely slowpath event */
|
|
|
- if (unlikely(cqe_type == ETH_RX_CQE_TYPE_SLOW_PATH)) {
|
|
|
- struct eth_slow_path_rx_cqe *sp_cqe;
|
|
|
-
|
|
|
- sp_cqe = (struct eth_slow_path_rx_cqe *)cqe;
|
|
|
- edev->ops->eth_cqe_completion(edev->cdev, fp->id, sp_cqe);
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- /* Handle TPA cqes */
|
|
|
- if (cqe_type != ETH_RX_CQE_TYPE_REGULAR)
|
|
|
- return qede_rx_process_tpa_cqe(edev, fp, rxq, cqe, cqe_type);
|
|
|
-
|
|
|
- /* Get the data from the SW ring; Consume it only after it's evident
|
|
|
- * we wouldn't recycle it.
|
|
|
- */
|
|
|
- bd_cons_idx = rxq->sw_rx_cons & NUM_RX_BDS_MAX;
|
|
|
- bd = &rxq->sw_rx_ring[bd_cons_idx];
|
|
|
-
|
|
|
- fp_cqe = &cqe->fast_path_regular;
|
|
|
- len = le16_to_cpu(fp_cqe->len_on_first_bd);
|
|
|
- pad = fp_cqe->placement_offset;
|
|
|
-
|
|
|
- /* Run eBPF program if one is attached */
|
|
|
- if (xdp_prog)
|
|
|
- if (!qede_rx_xdp(edev, fp, rxq, xdp_prog, bd, fp_cqe))
|
|
|
- return 1;
|
|
|
-
|
|
|
- /* If this is an error packet then drop it */
|
|
|
- flags = cqe->fast_path_regular.pars_flags.flags;
|
|
|
- parse_flag = le16_to_cpu(flags);
|
|
|
-
|
|
|
- csum_flag = qede_check_csum(parse_flag);
|
|
|
- if (unlikely(csum_flag == QEDE_CSUM_ERROR)) {
|
|
|
- if (qede_pkt_is_ip_fragmented(fp_cqe, parse_flag)) {
|
|
|
- rxq->rx_ip_frags++;
|
|
|
- } else {
|
|
|
- DP_NOTICE(edev,
|
|
|
- "CQE has error, flags = %x, dropping incoming packet\n",
|
|
|
- parse_flag);
|
|
|
- rxq->rx_hw_errors++;
|
|
|
- qede_recycle_rx_bd_ring(rxq, fp_cqe->bd_num);
|
|
|
- return 0;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* Basic validation passed; Need to prepare an SKB. This would also
|
|
|
- * guarantee to finally consume the first BD upon success.
|
|
|
- */
|
|
|
- skb = qede_rx_allocate_skb(edev, rxq, bd, len, pad);
|
|
|
- if (!skb) {
|
|
|
- rxq->rx_alloc_errors++;
|
|
|
- qede_recycle_rx_bd_ring(rxq, fp_cqe->bd_num);
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- /* In case of Jumbo packet, several PAGE_SIZEd buffers will be pointed
|
|
|
- * by a single cqe.
|
|
|
- */
|
|
|
- if (fp_cqe->bd_num > 1) {
|
|
|
- u16 unmapped_frags = qede_rx_build_jumbo(edev, rxq, skb,
|
|
|
- fp_cqe, len);
|
|
|
-
|
|
|
- if (unlikely(unmapped_frags > 0)) {
|
|
|
- qede_recycle_rx_bd_ring(rxq, unmapped_frags);
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
- return 0;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* The SKB contains all the data. Now prepare meta-magic */
|
|
|
- skb->protocol = eth_type_trans(skb, edev->ndev);
|
|
|
- qede_get_rxhash(skb, fp_cqe->bitfields, fp_cqe->rss_hash);
|
|
|
- qede_set_skb_csum(skb, csum_flag);
|
|
|
- skb_record_rx_queue(skb, rxq->rxq_id);
|
|
|
-
|
|
|
- /* SKB is prepared - pass it to stack */
|
|
|
- qede_skb_receive(edev, fp, rxq, skb, le16_to_cpu(fp_cqe->vlan_tag));
|
|
|
-
|
|
|
- return 1;
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_rx_int(struct qede_fastpath *fp, int budget)
|
|
|
-{
|
|
|
- struct qede_rx_queue *rxq = fp->rxq;
|
|
|
- struct qede_dev *edev = fp->edev;
|
|
|
- u16 hw_comp_cons, sw_comp_cons;
|
|
|
- int work_done = 0;
|
|
|
-
|
|
|
- hw_comp_cons = le16_to_cpu(*rxq->hw_cons_ptr);
|
|
|
- sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring);
|
|
|
-
|
|
|
- /* Memory barrier to prevent the CPU from doing speculative reads of CQE
|
|
|
- * / BD in the while-loop before reading hw_comp_cons. If the CQE is
|
|
|
- * read before it is written by FW, then FW writes CQE and SB, and then
|
|
|
- * the CPU reads the hw_comp_cons, it will use an old CQE.
|
|
|
- */
|
|
|
- rmb();
|
|
|
-
|
|
|
- /* Loop to complete all indicated BDs */
|
|
|
- while ((sw_comp_cons != hw_comp_cons) && (work_done < budget)) {
|
|
|
- qede_rx_process_cqe(edev, fp, rxq);
|
|
|
- qed_chain_recycle_consumed(&rxq->rx_comp_ring);
|
|
|
- sw_comp_cons = qed_chain_get_cons_idx(&rxq->rx_comp_ring);
|
|
|
- work_done++;
|
|
|
- }
|
|
|
-
|
|
|
- /* Update producers */
|
|
|
- qede_update_rx_prod(edev, rxq);
|
|
|
-
|
|
|
- return work_done;
|
|
|
-}
|
|
|
-
|
|
|
-static bool qede_poll_is_more_work(struct qede_fastpath *fp)
|
|
|
-{
|
|
|
- qed_sb_update_sb_idx(fp->sb_info);
|
|
|
-
|
|
|
- /* *_has_*_work() reads the status block, thus we need to ensure that
|
|
|
- * status block indices have been actually read (qed_sb_update_sb_idx)
|
|
|
- * prior to this check (*_has_*_work) so that we won't write the
|
|
|
- * "newer" value of the status block to HW (if there was a DMA right
|
|
|
- * after qede_has_rx_work and if there is no rmb, the memory reading
|
|
|
- * (qed_sb_update_sb_idx) may be postponed to right before *_ack_sb).
|
|
|
- * In this case there will never be another interrupt until there is
|
|
|
- * another update of the status block, while there is still unhandled
|
|
|
- * work.
|
|
|
- */
|
|
|
- rmb();
|
|
|
-
|
|
|
- if (likely(fp->type & QEDE_FASTPATH_RX))
|
|
|
- if (qede_has_rx_work(fp->rxq))
|
|
|
- return true;
|
|
|
-
|
|
|
- if (fp->type & QEDE_FASTPATH_XDP)
|
|
|
- if (qede_txq_has_work(fp->xdp_tx))
|
|
|
- return true;
|
|
|
-
|
|
|
- if (likely(fp->type & QEDE_FASTPATH_TX))
|
|
|
- if (qede_txq_has_work(fp->txq))
|
|
|
- return true;
|
|
|
-
|
|
|
- return false;
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_poll(struct napi_struct *napi, int budget)
|
|
|
-{
|
|
|
- struct qede_fastpath *fp = container_of(napi, struct qede_fastpath,
|
|
|
- napi);
|
|
|
- struct qede_dev *edev = fp->edev;
|
|
|
- int rx_work_done = 0;
|
|
|
-
|
|
|
- if (likely(fp->type & QEDE_FASTPATH_TX) && qede_txq_has_work(fp->txq))
|
|
|
- qede_tx_int(edev, fp->txq);
|
|
|
-
|
|
|
- if ((fp->type & QEDE_FASTPATH_XDP) && qede_txq_has_work(fp->xdp_tx))
|
|
|
- qede_xdp_tx_int(edev, fp->xdp_tx);
|
|
|
-
|
|
|
- rx_work_done = (likely(fp->type & QEDE_FASTPATH_RX) &&
|
|
|
- qede_has_rx_work(fp->rxq)) ?
|
|
|
- qede_rx_int(fp, budget) : 0;
|
|
|
- if (rx_work_done < budget) {
|
|
|
- if (!qede_poll_is_more_work(fp)) {
|
|
|
- napi_complete(napi);
|
|
|
-
|
|
|
- /* Update and reenable interrupts */
|
|
|
- qed_sb_ack(fp->sb_info, IGU_INT_ENABLE, 1);
|
|
|
- } else {
|
|
|
- rx_work_done = budget;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (fp->xdp_xmit) {
|
|
|
- u16 xdp_prod = qed_chain_get_prod_idx(&fp->xdp_tx->tx_pbl);
|
|
|
-
|
|
|
- fp->xdp_xmit = 0;
|
|
|
- fp->xdp_tx->tx_db.data.bd_prod = cpu_to_le16(xdp_prod);
|
|
|
- qede_update_tx_producer(fp->xdp_tx);
|
|
|
- }
|
|
|
-
|
|
|
- return rx_work_done;
|
|
|
-}
|
|
|
-
|
|
|
-static irqreturn_t qede_msix_fp_int(int irq, void *fp_cookie)
|
|
|
-{
|
|
|
- struct qede_fastpath *fp = fp_cookie;
|
|
|
-
|
|
|
- qed_sb_ack(fp->sb_info, IGU_INT_DISABLE, 0 /*do not update*/);
|
|
|
-
|
|
|
- napi_schedule_irqoff(&fp->napi);
|
|
|
- return IRQ_HANDLED;
|
|
|
-}
|
|
|
-
|
|
|
-/* -------------------------------------------------------------------------
|
|
|
- * END OF FAST-PATH
|
|
|
- * -------------------------------------------------------------------------
|
|
|
- */
|
|
|
-
|
|
|
-static int qede_open(struct net_device *ndev);
|
|
|
-static int qede_close(struct net_device *ndev);
|
|
|
-static int qede_set_mac_addr(struct net_device *ndev, void *p);
|
|
|
-static void qede_set_rx_mode(struct net_device *ndev);
|
|
|
-static void qede_config_rx_mode(struct net_device *ndev);
|
|
|
-
|
|
|
-static int qede_set_ucast_rx_mac(struct qede_dev *edev,
|
|
|
- enum qed_filter_xcast_params_type opcode,
|
|
|
- unsigned char mac[ETH_ALEN])
|
|
|
-{
|
|
|
- struct qed_filter_params filter_cmd;
|
|
|
-
|
|
|
- memset(&filter_cmd, 0, sizeof(filter_cmd));
|
|
|
- filter_cmd.type = QED_FILTER_TYPE_UCAST;
|
|
|
- filter_cmd.filter.ucast.type = opcode;
|
|
|
- filter_cmd.filter.ucast.mac_valid = 1;
|
|
|
- ether_addr_copy(filter_cmd.filter.ucast.mac, mac);
|
|
|
-
|
|
|
- return edev->ops->filter_config(edev->cdev, &filter_cmd);
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_set_ucast_rx_vlan(struct qede_dev *edev,
|
|
|
- enum qed_filter_xcast_params_type opcode,
|
|
|
- u16 vid)
|
|
|
-{
|
|
|
- struct qed_filter_params filter_cmd;
|
|
|
-
|
|
|
- memset(&filter_cmd, 0, sizeof(filter_cmd));
|
|
|
- filter_cmd.type = QED_FILTER_TYPE_UCAST;
|
|
|
- filter_cmd.filter.ucast.type = opcode;
|
|
|
- filter_cmd.filter.ucast.vlan_valid = 1;
|
|
|
- filter_cmd.filter.ucast.vlan = vid;
|
|
|
-
|
|
|
- return edev->ops->filter_config(edev->cdev, &filter_cmd);
|
|
|
-}
|
|
|
-
|
|
|
-void qede_fill_by_demand_stats(struct qede_dev *edev)
|
|
|
-{
|
|
|
- struct qed_eth_stats stats;
|
|
|
-
|
|
|
- edev->ops->get_vport_stats(edev->cdev, &stats);
|
|
|
- edev->stats.no_buff_discards = stats.no_buff_discards;
|
|
|
- edev->stats.packet_too_big_discard = stats.packet_too_big_discard;
|
|
|
- edev->stats.ttl0_discard = stats.ttl0_discard;
|
|
|
- edev->stats.rx_ucast_bytes = stats.rx_ucast_bytes;
|
|
|
- edev->stats.rx_mcast_bytes = stats.rx_mcast_bytes;
|
|
|
- edev->stats.rx_bcast_bytes = stats.rx_bcast_bytes;
|
|
|
- edev->stats.rx_ucast_pkts = stats.rx_ucast_pkts;
|
|
|
- edev->stats.rx_mcast_pkts = stats.rx_mcast_pkts;
|
|
|
- edev->stats.rx_bcast_pkts = stats.rx_bcast_pkts;
|
|
|
- edev->stats.mftag_filter_discards = stats.mftag_filter_discards;
|
|
|
- edev->stats.mac_filter_discards = stats.mac_filter_discards;
|
|
|
-
|
|
|
- edev->stats.tx_ucast_bytes = stats.tx_ucast_bytes;
|
|
|
- edev->stats.tx_mcast_bytes = stats.tx_mcast_bytes;
|
|
|
- edev->stats.tx_bcast_bytes = stats.tx_bcast_bytes;
|
|
|
- edev->stats.tx_ucast_pkts = stats.tx_ucast_pkts;
|
|
|
- edev->stats.tx_mcast_pkts = stats.tx_mcast_pkts;
|
|
|
- edev->stats.tx_bcast_pkts = stats.tx_bcast_pkts;
|
|
|
- edev->stats.tx_err_drop_pkts = stats.tx_err_drop_pkts;
|
|
|
- edev->stats.coalesced_pkts = stats.tpa_coalesced_pkts;
|
|
|
- edev->stats.coalesced_events = stats.tpa_coalesced_events;
|
|
|
- edev->stats.coalesced_aborts_num = stats.tpa_aborts_num;
|
|
|
- edev->stats.non_coalesced_pkts = stats.tpa_not_coalesced_pkts;
|
|
|
- edev->stats.coalesced_bytes = stats.tpa_coalesced_bytes;
|
|
|
+ edev->stats.tx_ucast_bytes = stats.tx_ucast_bytes;
|
|
|
+ edev->stats.tx_mcast_bytes = stats.tx_mcast_bytes;
|
|
|
+ edev->stats.tx_bcast_bytes = stats.tx_bcast_bytes;
|
|
|
+ edev->stats.tx_ucast_pkts = stats.tx_ucast_pkts;
|
|
|
+ edev->stats.tx_mcast_pkts = stats.tx_mcast_pkts;
|
|
|
+ edev->stats.tx_bcast_pkts = stats.tx_bcast_pkts;
|
|
|
+ edev->stats.tx_err_drop_pkts = stats.tx_err_drop_pkts;
|
|
|
+ edev->stats.coalesced_pkts = stats.tpa_coalesced_pkts;
|
|
|
+ edev->stats.coalesced_events = stats.tpa_coalesced_events;
|
|
|
+ edev->stats.coalesced_aborts_num = stats.tpa_aborts_num;
|
|
|
+ edev->stats.non_coalesced_pkts = stats.tpa_not_coalesced_pkts;
|
|
|
+ edev->stats.coalesced_bytes = stats.tpa_coalesced_bytes;
|
|
|
|
|
|
edev->stats.rx_64_byte_packets = stats.rx_64_byte_packets;
|
|
|
edev->stats.rx_65_to_127_byte_packets = stats.rx_65_to_127_byte_packets;
|
|
@@ -2010,530 +389,103 @@ void qede_fill_by_demand_stats(struct qede_dev *edev)
|
|
|
stats.tx_4096_to_9216_byte_packets;
|
|
|
edev->stats.tx_9217_to_16383_byte_packets =
|
|
|
stats.tx_9217_to_16383_byte_packets;
|
|
|
- edev->stats.tx_pause_frames = stats.tx_pause_frames;
|
|
|
- edev->stats.tx_pfc_frames = stats.tx_pfc_frames;
|
|
|
- edev->stats.tx_lpi_entry_count = stats.tx_lpi_entry_count;
|
|
|
- edev->stats.tx_total_collisions = stats.tx_total_collisions;
|
|
|
- edev->stats.brb_truncates = stats.brb_truncates;
|
|
|
- edev->stats.brb_discards = stats.brb_discards;
|
|
|
- edev->stats.tx_mac_ctrl_frames = stats.tx_mac_ctrl_frames;
|
|
|
-}
|
|
|
-
|
|
|
-static
|
|
|
-struct rtnl_link_stats64 *qede_get_stats64(struct net_device *dev,
|
|
|
- struct rtnl_link_stats64 *stats)
|
|
|
-{
|
|
|
- struct qede_dev *edev = netdev_priv(dev);
|
|
|
-
|
|
|
- qede_fill_by_demand_stats(edev);
|
|
|
-
|
|
|
- stats->rx_packets = edev->stats.rx_ucast_pkts +
|
|
|
- edev->stats.rx_mcast_pkts +
|
|
|
- edev->stats.rx_bcast_pkts;
|
|
|
- stats->tx_packets = edev->stats.tx_ucast_pkts +
|
|
|
- edev->stats.tx_mcast_pkts +
|
|
|
- edev->stats.tx_bcast_pkts;
|
|
|
-
|
|
|
- stats->rx_bytes = edev->stats.rx_ucast_bytes +
|
|
|
- edev->stats.rx_mcast_bytes +
|
|
|
- edev->stats.rx_bcast_bytes;
|
|
|
-
|
|
|
- stats->tx_bytes = edev->stats.tx_ucast_bytes +
|
|
|
- edev->stats.tx_mcast_bytes +
|
|
|
- edev->stats.tx_bcast_bytes;
|
|
|
-
|
|
|
- stats->tx_errors = edev->stats.tx_err_drop_pkts;
|
|
|
- stats->multicast = edev->stats.rx_mcast_pkts +
|
|
|
- edev->stats.rx_bcast_pkts;
|
|
|
-
|
|
|
- stats->rx_fifo_errors = edev->stats.no_buff_discards;
|
|
|
-
|
|
|
- stats->collisions = edev->stats.tx_total_collisions;
|
|
|
- stats->rx_crc_errors = edev->stats.rx_crc_errors;
|
|
|
- stats->rx_frame_errors = edev->stats.rx_align_errors;
|
|
|
-
|
|
|
- return stats;
|
|
|
-}
|
|
|
-
|
|
|
-#ifdef CONFIG_QED_SRIOV
|
|
|
-static int qede_get_vf_config(struct net_device *dev, int vfidx,
|
|
|
- struct ifla_vf_info *ivi)
|
|
|
-{
|
|
|
- struct qede_dev *edev = netdev_priv(dev);
|
|
|
-
|
|
|
- if (!edev->ops)
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- return edev->ops->iov->get_config(edev->cdev, vfidx, ivi);
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_set_vf_rate(struct net_device *dev, int vfidx,
|
|
|
- int min_tx_rate, int max_tx_rate)
|
|
|
-{
|
|
|
- struct qede_dev *edev = netdev_priv(dev);
|
|
|
-
|
|
|
- return edev->ops->iov->set_rate(edev->cdev, vfidx, min_tx_rate,
|
|
|
- max_tx_rate);
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_set_vf_spoofchk(struct net_device *dev, int vfidx, bool val)
|
|
|
-{
|
|
|
- struct qede_dev *edev = netdev_priv(dev);
|
|
|
-
|
|
|
- if (!edev->ops)
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- return edev->ops->iov->set_spoof(edev->cdev, vfidx, val);
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_set_vf_link_state(struct net_device *dev, int vfidx,
|
|
|
- int link_state)
|
|
|
-{
|
|
|
- struct qede_dev *edev = netdev_priv(dev);
|
|
|
-
|
|
|
- if (!edev->ops)
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- return edev->ops->iov->set_link_state(edev->cdev, vfidx, link_state);
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-static void qede_config_accept_any_vlan(struct qede_dev *edev, bool action)
|
|
|
-{
|
|
|
- struct qed_update_vport_params params;
|
|
|
- int rc;
|
|
|
-
|
|
|
- /* Proceed only if action actually needs to be performed */
|
|
|
- if (edev->accept_any_vlan == action)
|
|
|
- return;
|
|
|
-
|
|
|
- memset(¶ms, 0, sizeof(params));
|
|
|
-
|
|
|
- params.vport_id = 0;
|
|
|
- params.accept_any_vlan = action;
|
|
|
- params.update_accept_any_vlan_flg = 1;
|
|
|
-
|
|
|
- rc = edev->ops->vport_update(edev->cdev, ¶ms);
|
|
|
- if (rc) {
|
|
|
- DP_ERR(edev, "Failed to %s accept-any-vlan\n",
|
|
|
- action ? "enable" : "disable");
|
|
|
- } else {
|
|
|
- DP_INFO(edev, "%s accept-any-vlan\n",
|
|
|
- action ? "enabled" : "disabled");
|
|
|
- edev->accept_any_vlan = action;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
|
|
|
-{
|
|
|
- struct qede_dev *edev = netdev_priv(dev);
|
|
|
- struct qede_vlan *vlan, *tmp;
|
|
|
- int rc = 0;
|
|
|
-
|
|
|
- DP_VERBOSE(edev, NETIF_MSG_IFUP, "Adding vlan 0x%04x\n", vid);
|
|
|
-
|
|
|
- vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
|
|
|
- if (!vlan) {
|
|
|
- DP_INFO(edev, "Failed to allocate struct for vlan\n");
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
- INIT_LIST_HEAD(&vlan->list);
|
|
|
- vlan->vid = vid;
|
|
|
- vlan->configured = false;
|
|
|
-
|
|
|
- /* Verify vlan isn't already configured */
|
|
|
- list_for_each_entry(tmp, &edev->vlan_list, list) {
|
|
|
- if (tmp->vid == vlan->vid) {
|
|
|
- DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
|
|
|
- "vlan already configured\n");
|
|
|
- kfree(vlan);
|
|
|
- return -EEXIST;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* If interface is down, cache this VLAN ID and return */
|
|
|
- __qede_lock(edev);
|
|
|
- if (edev->state != QEDE_STATE_OPEN) {
|
|
|
- DP_VERBOSE(edev, NETIF_MSG_IFDOWN,
|
|
|
- "Interface is down, VLAN %d will be configured when interface is up\n",
|
|
|
- vid);
|
|
|
- if (vid != 0)
|
|
|
- edev->non_configured_vlans++;
|
|
|
- list_add(&vlan->list, &edev->vlan_list);
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- /* Check for the filter limit.
|
|
|
- * Note - vlan0 has a reserved filter and can be added without
|
|
|
- * worrying about quota
|
|
|
- */
|
|
|
- if ((edev->configured_vlans < edev->dev_info.num_vlan_filters) ||
|
|
|
- (vlan->vid == 0)) {
|
|
|
- rc = qede_set_ucast_rx_vlan(edev,
|
|
|
- QED_FILTER_XCAST_TYPE_ADD,
|
|
|
- vlan->vid);
|
|
|
- if (rc) {
|
|
|
- DP_ERR(edev, "Failed to configure VLAN %d\n",
|
|
|
- vlan->vid);
|
|
|
- kfree(vlan);
|
|
|
- goto out;
|
|
|
- }
|
|
|
- vlan->configured = true;
|
|
|
-
|
|
|
- /* vlan0 filter isn't consuming out of our quota */
|
|
|
- if (vlan->vid != 0)
|
|
|
- edev->configured_vlans++;
|
|
|
- } else {
|
|
|
- /* Out of quota; Activate accept-any-VLAN mode */
|
|
|
- if (!edev->non_configured_vlans)
|
|
|
- qede_config_accept_any_vlan(edev, true);
|
|
|
-
|
|
|
- edev->non_configured_vlans++;
|
|
|
- }
|
|
|
-
|
|
|
- list_add(&vlan->list, &edev->vlan_list);
|
|
|
-
|
|
|
-out:
|
|
|
- __qede_unlock(edev);
|
|
|
- return rc;
|
|
|
-}
|
|
|
-
|
|
|
-static void qede_del_vlan_from_list(struct qede_dev *edev,
|
|
|
- struct qede_vlan *vlan)
|
|
|
-{
|
|
|
- /* vlan0 filter isn't consuming out of our quota */
|
|
|
- if (vlan->vid != 0) {
|
|
|
- if (vlan->configured)
|
|
|
- edev->configured_vlans--;
|
|
|
- else
|
|
|
- edev->non_configured_vlans--;
|
|
|
- }
|
|
|
-
|
|
|
- list_del(&vlan->list);
|
|
|
- kfree(vlan);
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_configure_vlan_filters(struct qede_dev *edev)
|
|
|
-{
|
|
|
- int rc = 0, real_rc = 0, accept_any_vlan = 0;
|
|
|
- struct qed_dev_eth_info *dev_info;
|
|
|
- struct qede_vlan *vlan = NULL;
|
|
|
-
|
|
|
- if (list_empty(&edev->vlan_list))
|
|
|
- return 0;
|
|
|
-
|
|
|
- dev_info = &edev->dev_info;
|
|
|
-
|
|
|
- /* Configure non-configured vlans */
|
|
|
- list_for_each_entry(vlan, &edev->vlan_list, list) {
|
|
|
- if (vlan->configured)
|
|
|
- continue;
|
|
|
-
|
|
|
- /* We have used all our credits, now enable accept_any_vlan */
|
|
|
- if ((vlan->vid != 0) &&
|
|
|
- (edev->configured_vlans == dev_info->num_vlan_filters)) {
|
|
|
- accept_any_vlan = 1;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- DP_VERBOSE(edev, NETIF_MSG_IFUP, "Adding vlan %d\n", vlan->vid);
|
|
|
-
|
|
|
- rc = qede_set_ucast_rx_vlan(edev, QED_FILTER_XCAST_TYPE_ADD,
|
|
|
- vlan->vid);
|
|
|
- if (rc) {
|
|
|
- DP_ERR(edev, "Failed to configure VLAN %u\n",
|
|
|
- vlan->vid);
|
|
|
- real_rc = rc;
|
|
|
- continue;
|
|
|
- }
|
|
|
-
|
|
|
- vlan->configured = true;
|
|
|
- /* vlan0 filter doesn't consume our VLAN filter's quota */
|
|
|
- if (vlan->vid != 0) {
|
|
|
- edev->non_configured_vlans--;
|
|
|
- edev->configured_vlans++;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* enable accept_any_vlan mode if we have more VLANs than credits,
|
|
|
- * or remove accept_any_vlan mode if we've actually removed
|
|
|
- * a non-configured vlan, and all remaining vlans are truly configured.
|
|
|
- */
|
|
|
-
|
|
|
- if (accept_any_vlan)
|
|
|
- qede_config_accept_any_vlan(edev, true);
|
|
|
- else if (!edev->non_configured_vlans)
|
|
|
- qede_config_accept_any_vlan(edev, false);
|
|
|
-
|
|
|
- return real_rc;
|
|
|
+ edev->stats.tx_pause_frames = stats.tx_pause_frames;
|
|
|
+ edev->stats.tx_pfc_frames = stats.tx_pfc_frames;
|
|
|
+ edev->stats.tx_lpi_entry_count = stats.tx_lpi_entry_count;
|
|
|
+ edev->stats.tx_total_collisions = stats.tx_total_collisions;
|
|
|
+ edev->stats.brb_truncates = stats.brb_truncates;
|
|
|
+ edev->stats.brb_discards = stats.brb_discards;
|
|
|
+ edev->stats.tx_mac_ctrl_frames = stats.tx_mac_ctrl_frames;
|
|
|
}
|
|
|
|
|
|
-static int qede_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid)
|
|
|
+static
|
|
|
+struct rtnl_link_stats64 *qede_get_stats64(struct net_device *dev,
|
|
|
+ struct rtnl_link_stats64 *stats)
|
|
|
{
|
|
|
struct qede_dev *edev = netdev_priv(dev);
|
|
|
- struct qede_vlan *vlan = NULL;
|
|
|
- int rc = 0;
|
|
|
-
|
|
|
- DP_VERBOSE(edev, NETIF_MSG_IFDOWN, "Removing vlan 0x%04x\n", vid);
|
|
|
-
|
|
|
- /* Find whether entry exists */
|
|
|
- __qede_lock(edev);
|
|
|
- list_for_each_entry(vlan, &edev->vlan_list, list)
|
|
|
- if (vlan->vid == vid)
|
|
|
- break;
|
|
|
-
|
|
|
- if (!vlan || (vlan->vid != vid)) {
|
|
|
- DP_VERBOSE(edev, (NETIF_MSG_IFUP | NETIF_MSG_IFDOWN),
|
|
|
- "Vlan isn't configured\n");
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- if (edev->state != QEDE_STATE_OPEN) {
|
|
|
- /* As interface is already down, we don't have a VPORT
|
|
|
- * instance to remove vlan filter. So just update vlan list
|
|
|
- */
|
|
|
- DP_VERBOSE(edev, NETIF_MSG_IFDOWN,
|
|
|
- "Interface is down, removing VLAN from list only\n");
|
|
|
- qede_del_vlan_from_list(edev, vlan);
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- /* Remove vlan */
|
|
|
- if (vlan->configured) {
|
|
|
- rc = qede_set_ucast_rx_vlan(edev, QED_FILTER_XCAST_TYPE_DEL,
|
|
|
- vid);
|
|
|
- if (rc) {
|
|
|
- DP_ERR(edev, "Failed to remove VLAN %d\n", vid);
|
|
|
- goto out;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- qede_del_vlan_from_list(edev, vlan);
|
|
|
-
|
|
|
- /* We have removed a VLAN - try to see if we can
|
|
|
- * configure non-configured VLAN from the list.
|
|
|
- */
|
|
|
- rc = qede_configure_vlan_filters(edev);
|
|
|
-
|
|
|
-out:
|
|
|
- __qede_unlock(edev);
|
|
|
- return rc;
|
|
|
-}
|
|
|
|
|
|
-static void qede_vlan_mark_nonconfigured(struct qede_dev *edev)
|
|
|
-{
|
|
|
- struct qede_vlan *vlan = NULL;
|
|
|
+ qede_fill_by_demand_stats(edev);
|
|
|
|
|
|
- if (list_empty(&edev->vlan_list))
|
|
|
- return;
|
|
|
+ stats->rx_packets = edev->stats.rx_ucast_pkts +
|
|
|
+ edev->stats.rx_mcast_pkts +
|
|
|
+ edev->stats.rx_bcast_pkts;
|
|
|
+ stats->tx_packets = edev->stats.tx_ucast_pkts +
|
|
|
+ edev->stats.tx_mcast_pkts +
|
|
|
+ edev->stats.tx_bcast_pkts;
|
|
|
|
|
|
- list_for_each_entry(vlan, &edev->vlan_list, list) {
|
|
|
- if (!vlan->configured)
|
|
|
- continue;
|
|
|
+ stats->rx_bytes = edev->stats.rx_ucast_bytes +
|
|
|
+ edev->stats.rx_mcast_bytes +
|
|
|
+ edev->stats.rx_bcast_bytes;
|
|
|
|
|
|
- vlan->configured = false;
|
|
|
+ stats->tx_bytes = edev->stats.tx_ucast_bytes +
|
|
|
+ edev->stats.tx_mcast_bytes +
|
|
|
+ edev->stats.tx_bcast_bytes;
|
|
|
|
|
|
- /* vlan0 filter isn't consuming out of our quota */
|
|
|
- if (vlan->vid != 0) {
|
|
|
- edev->non_configured_vlans++;
|
|
|
- edev->configured_vlans--;
|
|
|
- }
|
|
|
+ stats->tx_errors = edev->stats.tx_err_drop_pkts;
|
|
|
+ stats->multicast = edev->stats.rx_mcast_pkts +
|
|
|
+ edev->stats.rx_bcast_pkts;
|
|
|
|
|
|
- DP_VERBOSE(edev, NETIF_MSG_IFDOWN,
|
|
|
- "marked vlan %d as non-configured\n", vlan->vid);
|
|
|
- }
|
|
|
+ stats->rx_fifo_errors = edev->stats.no_buff_discards;
|
|
|
|
|
|
- edev->accept_any_vlan = false;
|
|
|
-}
|
|
|
+ stats->collisions = edev->stats.tx_total_collisions;
|
|
|
+ stats->rx_crc_errors = edev->stats.rx_crc_errors;
|
|
|
+ stats->rx_frame_errors = edev->stats.rx_align_errors;
|
|
|
|
|
|
-static void qede_set_features_reload(struct qede_dev *edev,
|
|
|
- struct qede_reload_args *args)
|
|
|
-{
|
|
|
- edev->ndev->features = args->u.features;
|
|
|
+ return stats;
|
|
|
}
|
|
|
|
|
|
-int qede_set_features(struct net_device *dev, netdev_features_t features)
|
|
|
+#ifdef CONFIG_QED_SRIOV
|
|
|
+static int qede_get_vf_config(struct net_device *dev, int vfidx,
|
|
|
+ struct ifla_vf_info *ivi)
|
|
|
{
|
|
|
struct qede_dev *edev = netdev_priv(dev);
|
|
|
- netdev_features_t changes = features ^ dev->features;
|
|
|
- bool need_reload = false;
|
|
|
-
|
|
|
- /* No action needed if hardware GRO is disabled during driver load */
|
|
|
- if (changes & NETIF_F_GRO) {
|
|
|
- if (dev->features & NETIF_F_GRO)
|
|
|
- need_reload = !edev->gro_disable;
|
|
|
- else
|
|
|
- need_reload = edev->gro_disable;
|
|
|
- }
|
|
|
|
|
|
- if (need_reload) {
|
|
|
- struct qede_reload_args args;
|
|
|
-
|
|
|
- args.u.features = features;
|
|
|
- args.func = &qede_set_features_reload;
|
|
|
-
|
|
|
- /* Make sure that we definitely need to reload.
|
|
|
- * In case of an eBPF attached program, there will be no FW
|
|
|
- * aggregations, so no need to actually reload.
|
|
|
- */
|
|
|
- __qede_lock(edev);
|
|
|
- if (edev->xdp_prog)
|
|
|
- args.func(edev, &args);
|
|
|
- else
|
|
|
- qede_reload(edev, &args, true);
|
|
|
- __qede_unlock(edev);
|
|
|
-
|
|
|
- return 1;
|
|
|
- }
|
|
|
+ if (!edev->ops)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- return 0;
|
|
|
+ return edev->ops->iov->get_config(edev->cdev, vfidx, ivi);
|
|
|
}
|
|
|
|
|
|
-static void qede_udp_tunnel_add(struct net_device *dev,
|
|
|
- struct udp_tunnel_info *ti)
|
|
|
+static int qede_set_vf_rate(struct net_device *dev, int vfidx,
|
|
|
+ int min_tx_rate, int max_tx_rate)
|
|
|
{
|
|
|
struct qede_dev *edev = netdev_priv(dev);
|
|
|
- u16 t_port = ntohs(ti->port);
|
|
|
-
|
|
|
- switch (ti->type) {
|
|
|
- case UDP_TUNNEL_TYPE_VXLAN:
|
|
|
- if (edev->vxlan_dst_port)
|
|
|
- return;
|
|
|
-
|
|
|
- edev->vxlan_dst_port = t_port;
|
|
|
-
|
|
|
- DP_VERBOSE(edev, QED_MSG_DEBUG, "Added vxlan port=%d\n",
|
|
|
- t_port);
|
|
|
-
|
|
|
- set_bit(QEDE_SP_VXLAN_PORT_CONFIG, &edev->sp_flags);
|
|
|
- break;
|
|
|
- case UDP_TUNNEL_TYPE_GENEVE:
|
|
|
- if (edev->geneve_dst_port)
|
|
|
- return;
|
|
|
-
|
|
|
- edev->geneve_dst_port = t_port;
|
|
|
-
|
|
|
- DP_VERBOSE(edev, QED_MSG_DEBUG, "Added geneve port=%d\n",
|
|
|
- t_port);
|
|
|
- set_bit(QEDE_SP_GENEVE_PORT_CONFIG, &edev->sp_flags);
|
|
|
- break;
|
|
|
- default:
|
|
|
- return;
|
|
|
- }
|
|
|
|
|
|
- schedule_delayed_work(&edev->sp_task, 0);
|
|
|
+ return edev->ops->iov->set_rate(edev->cdev, vfidx, min_tx_rate,
|
|
|
+ max_tx_rate);
|
|
|
}
|
|
|
|
|
|
-static void qede_udp_tunnel_del(struct net_device *dev,
|
|
|
- struct udp_tunnel_info *ti)
|
|
|
+static int qede_set_vf_spoofchk(struct net_device *dev, int vfidx, bool val)
|
|
|
{
|
|
|
struct qede_dev *edev = netdev_priv(dev);
|
|
|
- u16 t_port = ntohs(ti->port);
|
|
|
-
|
|
|
- switch (ti->type) {
|
|
|
- case UDP_TUNNEL_TYPE_VXLAN:
|
|
|
- if (t_port != edev->vxlan_dst_port)
|
|
|
- return;
|
|
|
-
|
|
|
- edev->vxlan_dst_port = 0;
|
|
|
-
|
|
|
- DP_VERBOSE(edev, QED_MSG_DEBUG, "Deleted vxlan port=%d\n",
|
|
|
- t_port);
|
|
|
-
|
|
|
- set_bit(QEDE_SP_VXLAN_PORT_CONFIG, &edev->sp_flags);
|
|
|
- break;
|
|
|
- case UDP_TUNNEL_TYPE_GENEVE:
|
|
|
- if (t_port != edev->geneve_dst_port)
|
|
|
- return;
|
|
|
-
|
|
|
- edev->geneve_dst_port = 0;
|
|
|
|
|
|
- DP_VERBOSE(edev, QED_MSG_DEBUG, "Deleted geneve port=%d\n",
|
|
|
- t_port);
|
|
|
- set_bit(QEDE_SP_GENEVE_PORT_CONFIG, &edev->sp_flags);
|
|
|
- break;
|
|
|
- default:
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- schedule_delayed_work(&edev->sp_task, 0);
|
|
|
-}
|
|
|
-
|
|
|
-/* 8B udp header + 8B base tunnel header + 32B option length */
|
|
|
-#define QEDE_MAX_TUN_HDR_LEN 48
|
|
|
-
|
|
|
-static netdev_features_t qede_features_check(struct sk_buff *skb,
|
|
|
- struct net_device *dev,
|
|
|
- netdev_features_t features)
|
|
|
-{
|
|
|
- if (skb->encapsulation) {
|
|
|
- u8 l4_proto = 0;
|
|
|
-
|
|
|
- switch (vlan_get_protocol(skb)) {
|
|
|
- case htons(ETH_P_IP):
|
|
|
- l4_proto = ip_hdr(skb)->protocol;
|
|
|
- break;
|
|
|
- case htons(ETH_P_IPV6):
|
|
|
- l4_proto = ipv6_hdr(skb)->nexthdr;
|
|
|
- break;
|
|
|
- default:
|
|
|
- return features;
|
|
|
- }
|
|
|
-
|
|
|
- /* Disable offloads for geneve tunnels, as HW can't parse
|
|
|
- * the geneve header which has option length greater than 32B.
|
|
|
- */
|
|
|
- if ((l4_proto == IPPROTO_UDP) &&
|
|
|
- ((skb_inner_mac_header(skb) -
|
|
|
- skb_transport_header(skb)) > QEDE_MAX_TUN_HDR_LEN))
|
|
|
- return features & ~(NETIF_F_CSUM_MASK |
|
|
|
- NETIF_F_GSO_MASK);
|
|
|
- }
|
|
|
-
|
|
|
- return features;
|
|
|
-}
|
|
|
-
|
|
|
-static void qede_xdp_reload_func(struct qede_dev *edev,
|
|
|
- struct qede_reload_args *args)
|
|
|
-{
|
|
|
- struct bpf_prog *old;
|
|
|
+ if (!edev->ops)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- old = xchg(&edev->xdp_prog, args->u.new_prog);
|
|
|
- if (old)
|
|
|
- bpf_prog_put(old);
|
|
|
+ return edev->ops->iov->set_spoof(edev->cdev, vfidx, val);
|
|
|
}
|
|
|
|
|
|
-static int qede_xdp_set(struct qede_dev *edev, struct bpf_prog *prog)
|
|
|
+static int qede_set_vf_link_state(struct net_device *dev, int vfidx,
|
|
|
+ int link_state)
|
|
|
{
|
|
|
- struct qede_reload_args args;
|
|
|
-
|
|
|
- if (prog && prog->xdp_adjust_head) {
|
|
|
- DP_ERR(edev, "Does not support bpf_xdp_adjust_head()\n");
|
|
|
- return -EOPNOTSUPP;
|
|
|
- }
|
|
|
+ struct qede_dev *edev = netdev_priv(dev);
|
|
|
|
|
|
- /* If we're called, there was already a bpf reference increment */
|
|
|
- args.func = &qede_xdp_reload_func;
|
|
|
- args.u.new_prog = prog;
|
|
|
- qede_reload(edev, &args, false);
|
|
|
+ if (!edev->ops)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- return 0;
|
|
|
+ return edev->ops->iov->set_link_state(edev->cdev, vfidx, link_state);
|
|
|
}
|
|
|
|
|
|
-static int qede_xdp(struct net_device *dev, struct netdev_xdp *xdp)
|
|
|
+static int qede_set_vf_trust(struct net_device *dev, int vfidx, bool setting)
|
|
|
{
|
|
|
struct qede_dev *edev = netdev_priv(dev);
|
|
|
|
|
|
- switch (xdp->command) {
|
|
|
- case XDP_SETUP_PROG:
|
|
|
- return qede_xdp_set(edev, xdp->prog);
|
|
|
- case XDP_QUERY_PROG:
|
|
|
- xdp->prog_attached = !!edev->xdp_prog;
|
|
|
- return 0;
|
|
|
- default:
|
|
|
+ if (!edev->ops)
|
|
|
return -EINVAL;
|
|
|
- }
|
|
|
+
|
|
|
+ return edev->ops->iov->set_trust(edev->cdev, vfidx, setting);
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
static const struct net_device_ops qede_netdev_ops = {
|
|
|
.ndo_open = qede_open,
|
|
@@ -2546,6 +498,7 @@ static const struct net_device_ops qede_netdev_ops = {
|
|
|
#ifdef CONFIG_QED_SRIOV
|
|
|
.ndo_set_vf_mac = qede_set_vf_mac,
|
|
|
.ndo_set_vf_vlan = qede_set_vf_vlan,
|
|
|
+ .ndo_set_vf_trust = qede_set_vf_trust,
|
|
|
#endif
|
|
|
.ndo_vlan_rx_add_vid = qede_vlan_rx_add_vid,
|
|
|
.ndo_vlan_rx_kill_vid = qede_vlan_rx_kill_vid,
|
|
@@ -2814,7 +767,7 @@ static void qede_update_pf_params(struct qed_dev *cdev)
|
|
|
|
|
|
/* 64 rx + 64 tx + 64 XDP */
|
|
|
memset(&pf_params, 0, sizeof(struct qed_pf_params));
|
|
|
- pf_params.eth_pf_params.num_cons = 192;
|
|
|
+ pf_params.eth_pf_params.num_cons = (MAX_SB_PER_PF_MIMD - 1) * 3;
|
|
|
qed_ops->common->update_pf_params(cdev, &pf_params);
|
|
|
}
|
|
|
|
|
@@ -3215,8 +1168,9 @@ static int qede_alloc_mem_rxq(struct qede_dev *edev, struct qede_rx_queue *rxq)
|
|
|
goto err;
|
|
|
|
|
|
/* Allocate buffers for the Rx ring */
|
|
|
+ rxq->filled_buffers = 0;
|
|
|
for (i = 0; i < rxq->num_rx_buffers; i++) {
|
|
|
- rc = qede_alloc_rx_buffer(rxq);
|
|
|
+ rc = qede_alloc_rx_buffer(rxq, false);
|
|
|
if (rc) {
|
|
|
DP_ERR(edev,
|
|
|
"Rx buffers allocation failed at index %d\n", i);
|
|
@@ -3564,19 +1518,24 @@ static int qede_stop_txq(struct qede_dev *edev,
|
|
|
|
|
|
static int qede_stop_queues(struct qede_dev *edev)
|
|
|
{
|
|
|
- struct qed_update_vport_params vport_update_params;
|
|
|
+ struct qed_update_vport_params *vport_update_params;
|
|
|
struct qed_dev *cdev = edev->cdev;
|
|
|
struct qede_fastpath *fp;
|
|
|
int rc, i;
|
|
|
|
|
|
/* Disable the vport */
|
|
|
- memset(&vport_update_params, 0, sizeof(vport_update_params));
|
|
|
- vport_update_params.vport_id = 0;
|
|
|
- vport_update_params.update_vport_active_flg = 1;
|
|
|
- vport_update_params.vport_active_flg = 0;
|
|
|
- vport_update_params.update_rss_flg = 0;
|
|
|
+ vport_update_params = vzalloc(sizeof(*vport_update_params));
|
|
|
+ if (!vport_update_params)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ vport_update_params->vport_id = 0;
|
|
|
+ vport_update_params->update_vport_active_flg = 1;
|
|
|
+ vport_update_params->vport_active_flg = 0;
|
|
|
+ vport_update_params->update_rss_flg = 0;
|
|
|
+
|
|
|
+ rc = edev->ops->vport_update(cdev, vport_update_params);
|
|
|
+ vfree(vport_update_params);
|
|
|
|
|
|
- rc = edev->ops->vport_update(cdev, &vport_update_params);
|
|
|
if (rc) {
|
|
|
DP_ERR(edev, "Failed to update vport\n");
|
|
|
return rc;
|
|
@@ -3688,11 +1647,10 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats)
|
|
|
{
|
|
|
int vlan_removal_en = 1;
|
|
|
struct qed_dev *cdev = edev->cdev;
|
|
|
- struct qed_update_vport_params vport_update_params;
|
|
|
- struct qed_queue_start_common_params q_params;
|
|
|
struct qed_dev_info *qed_info = &edev->dev_info.common;
|
|
|
+ struct qed_update_vport_params *vport_update_params;
|
|
|
+ struct qed_queue_start_common_params q_params;
|
|
|
struct qed_start_vport_params start = {0};
|
|
|
- bool reset_rss_indir = false;
|
|
|
int rc, i;
|
|
|
|
|
|
if (!edev->num_queues) {
|
|
@@ -3701,6 +1659,10 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats)
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
+ vport_update_params = vzalloc(sizeof(*vport_update_params));
|
|
|
+ if (!vport_update_params)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
start.gro_enable = !edev->gro_disable;
|
|
|
start.mtu = edev->ndev->mtu;
|
|
|
start.vport_id = 0;
|
|
@@ -3712,7 +1674,7 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats)
|
|
|
|
|
|
if (rc) {
|
|
|
DP_ERR(edev, "Start V-PORT failed %d\n", rc);
|
|
|
- return rc;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
DP_VERBOSE(edev, NETIF_MSG_IFUP,
|
|
@@ -3748,7 +1710,7 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats)
|
|
|
if (rc) {
|
|
|
DP_ERR(edev, "Start RXQ #%d failed %d\n", i,
|
|
|
rc);
|
|
|
- return rc;
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
/* Use the return parameters */
|
|
@@ -3764,108 +1726,44 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats)
|
|
|
if (fp->type & QEDE_FASTPATH_XDP) {
|
|
|
rc = qede_start_txq(edev, fp, fp->xdp_tx, i, XDP_PI);
|
|
|
if (rc)
|
|
|
- return rc;
|
|
|
+ goto out;
|
|
|
|
|
|
fp->rxq->xdp_prog = bpf_prog_add(edev->xdp_prog, 1);
|
|
|
if (IS_ERR(fp->rxq->xdp_prog)) {
|
|
|
rc = PTR_ERR(fp->rxq->xdp_prog);
|
|
|
fp->rxq->xdp_prog = NULL;
|
|
|
- return rc;
|
|
|
+ goto out;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (fp->type & QEDE_FASTPATH_TX) {
|
|
|
rc = qede_start_txq(edev, fp, fp->txq, i, TX_PI(0));
|
|
|
if (rc)
|
|
|
- return rc;
|
|
|
+ goto out;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Prepare and send the vport enable */
|
|
|
- memset(&vport_update_params, 0, sizeof(vport_update_params));
|
|
|
- vport_update_params.vport_id = start.vport_id;
|
|
|
- vport_update_params.update_vport_active_flg = 1;
|
|
|
- vport_update_params.vport_active_flg = 1;
|
|
|
+ vport_update_params->vport_id = start.vport_id;
|
|
|
+ vport_update_params->update_vport_active_flg = 1;
|
|
|
+ vport_update_params->vport_active_flg = 1;
|
|
|
|
|
|
if ((qed_info->mf_mode == QED_MF_NPAR || pci_num_vf(edev->pdev)) &&
|
|
|
qed_info->tx_switching) {
|
|
|
- vport_update_params.update_tx_switching_flg = 1;
|
|
|
- vport_update_params.tx_switching_flg = 1;
|
|
|
+ vport_update_params->update_tx_switching_flg = 1;
|
|
|
+ vport_update_params->tx_switching_flg = 1;
|
|
|
}
|
|
|
|
|
|
- /* Fill struct with RSS params */
|
|
|
- if (QEDE_RSS_COUNT(edev) > 1) {
|
|
|
- vport_update_params.update_rss_flg = 1;
|
|
|
-
|
|
|
- /* Need to validate current RSS config uses valid entries */
|
|
|
- for (i = 0; i < QED_RSS_IND_TABLE_SIZE; i++) {
|
|
|
- if (edev->rss_params.rss_ind_table[i] >=
|
|
|
- QEDE_RSS_COUNT(edev)) {
|
|
|
- reset_rss_indir = true;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (!(edev->rss_params_inited & QEDE_RSS_INDIR_INITED) ||
|
|
|
- reset_rss_indir) {
|
|
|
- u16 val;
|
|
|
-
|
|
|
- for (i = 0; i < QED_RSS_IND_TABLE_SIZE; i++) {
|
|
|
- u16 indir_val;
|
|
|
-
|
|
|
- val = QEDE_RSS_COUNT(edev);
|
|
|
- indir_val = ethtool_rxfh_indir_default(i, val);
|
|
|
- edev->rss_params.rss_ind_table[i] = indir_val;
|
|
|
- }
|
|
|
- edev->rss_params_inited |= QEDE_RSS_INDIR_INITED;
|
|
|
- }
|
|
|
-
|
|
|
- if (!(edev->rss_params_inited & QEDE_RSS_KEY_INITED)) {
|
|
|
- netdev_rss_key_fill(edev->rss_params.rss_key,
|
|
|
- sizeof(edev->rss_params.rss_key));
|
|
|
- edev->rss_params_inited |= QEDE_RSS_KEY_INITED;
|
|
|
- }
|
|
|
-
|
|
|
- if (!(edev->rss_params_inited & QEDE_RSS_CAPS_INITED)) {
|
|
|
- edev->rss_params.rss_caps = QED_RSS_IPV4 |
|
|
|
- QED_RSS_IPV6 |
|
|
|
- QED_RSS_IPV4_TCP |
|
|
|
- QED_RSS_IPV6_TCP;
|
|
|
- edev->rss_params_inited |= QEDE_RSS_CAPS_INITED;
|
|
|
- }
|
|
|
-
|
|
|
- memcpy(&vport_update_params.rss_params, &edev->rss_params,
|
|
|
- sizeof(vport_update_params.rss_params));
|
|
|
- } else {
|
|
|
- memset(&vport_update_params.rss_params, 0,
|
|
|
- sizeof(vport_update_params.rss_params));
|
|
|
- }
|
|
|
+ qede_fill_rss_params(edev, &vport_update_params->rss_params,
|
|
|
+ &vport_update_params->update_rss_flg);
|
|
|
|
|
|
- rc = edev->ops->vport_update(cdev, &vport_update_params);
|
|
|
- if (rc) {
|
|
|
+ rc = edev->ops->vport_update(cdev, vport_update_params);
|
|
|
+ if (rc)
|
|
|
DP_ERR(edev, "Update V-PORT failed %d\n", rc);
|
|
|
- return rc;
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int qede_set_mcast_rx_mac(struct qede_dev *edev,
|
|
|
- enum qed_filter_xcast_params_type opcode,
|
|
|
- unsigned char *mac, int num_macs)
|
|
|
-{
|
|
|
- struct qed_filter_params filter_cmd;
|
|
|
- int i;
|
|
|
|
|
|
- memset(&filter_cmd, 0, sizeof(filter_cmd));
|
|
|
- filter_cmd.type = QED_FILTER_TYPE_MCAST;
|
|
|
- filter_cmd.filter.mcast.type = opcode;
|
|
|
- filter_cmd.filter.mcast.num = num_macs;
|
|
|
-
|
|
|
- for (i = 0; i < num_macs; i++, mac += ETH_ALEN)
|
|
|
- ether_addr_copy(filter_cmd.filter.mcast.mac[i], mac);
|
|
|
-
|
|
|
- return edev->ops->filter_config(edev->cdev, &filter_cmd);
|
|
|
+out:
|
|
|
+ vfree(vport_update_params);
|
|
|
+ return rc;
|
|
|
}
|
|
|
|
|
|
enum qede_unload_mode {
|
|
@@ -4097,192 +1995,3 @@ static void qede_link_update(void *dev, struct qed_link_output *link)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-static int qede_set_mac_addr(struct net_device *ndev, void *p)
|
|
|
-{
|
|
|
- struct qede_dev *edev = netdev_priv(ndev);
|
|
|
- struct sockaddr *addr = p;
|
|
|
- int rc;
|
|
|
-
|
|
|
- ASSERT_RTNL(); /* @@@TBD To be removed */
|
|
|
-
|
|
|
- DP_INFO(edev, "Set_mac_addr called\n");
|
|
|
-
|
|
|
- if (!is_valid_ether_addr(addr->sa_data)) {
|
|
|
- DP_NOTICE(edev, "The MAC address is not valid\n");
|
|
|
- return -EFAULT;
|
|
|
- }
|
|
|
-
|
|
|
- if (!edev->ops->check_mac(edev->cdev, addr->sa_data)) {
|
|
|
- DP_NOTICE(edev, "qed prevents setting MAC\n");
|
|
|
- return -EINVAL;
|
|
|
- }
|
|
|
-
|
|
|
- ether_addr_copy(ndev->dev_addr, addr->sa_data);
|
|
|
-
|
|
|
- if (!netif_running(ndev)) {
|
|
|
- DP_NOTICE(edev, "The device is currently down\n");
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- /* Remove the previous primary mac */
|
|
|
- rc = qede_set_ucast_rx_mac(edev, QED_FILTER_XCAST_TYPE_DEL,
|
|
|
- edev->primary_mac);
|
|
|
- if (rc)
|
|
|
- return rc;
|
|
|
-
|
|
|
- edev->ops->common->update_mac(edev->cdev, addr->sa_data);
|
|
|
-
|
|
|
- /* Add MAC filter according to the new unicast HW MAC address */
|
|
|
- ether_addr_copy(edev->primary_mac, ndev->dev_addr);
|
|
|
- return qede_set_ucast_rx_mac(edev, QED_FILTER_XCAST_TYPE_ADD,
|
|
|
- edev->primary_mac);
|
|
|
-}
|
|
|
-
|
|
|
-static int
|
|
|
-qede_configure_mcast_filtering(struct net_device *ndev,
|
|
|
- enum qed_filter_rx_mode_type *accept_flags)
|
|
|
-{
|
|
|
- struct qede_dev *edev = netdev_priv(ndev);
|
|
|
- unsigned char *mc_macs, *temp;
|
|
|
- struct netdev_hw_addr *ha;
|
|
|
- int rc = 0, mc_count;
|
|
|
- size_t size;
|
|
|
-
|
|
|
- size = 64 * ETH_ALEN;
|
|
|
-
|
|
|
- mc_macs = kzalloc(size, GFP_KERNEL);
|
|
|
- if (!mc_macs) {
|
|
|
- DP_NOTICE(edev,
|
|
|
- "Failed to allocate memory for multicast MACs\n");
|
|
|
- rc = -ENOMEM;
|
|
|
- goto exit;
|
|
|
- }
|
|
|
-
|
|
|
- temp = mc_macs;
|
|
|
-
|
|
|
- /* Remove all previously configured MAC filters */
|
|
|
- rc = qede_set_mcast_rx_mac(edev, QED_FILTER_XCAST_TYPE_DEL,
|
|
|
- mc_macs, 1);
|
|
|
- if (rc)
|
|
|
- goto exit;
|
|
|
-
|
|
|
- netif_addr_lock_bh(ndev);
|
|
|
-
|
|
|
- mc_count = netdev_mc_count(ndev);
|
|
|
- if (mc_count < 64) {
|
|
|
- netdev_for_each_mc_addr(ha, ndev) {
|
|
|
- ether_addr_copy(temp, ha->addr);
|
|
|
- temp += ETH_ALEN;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- netif_addr_unlock_bh(ndev);
|
|
|
-
|
|
|
- /* Check for all multicast @@@TBD resource allocation */
|
|
|
- if ((ndev->flags & IFF_ALLMULTI) ||
|
|
|
- (mc_count > 64)) {
|
|
|
- if (*accept_flags == QED_FILTER_RX_MODE_TYPE_REGULAR)
|
|
|
- *accept_flags = QED_FILTER_RX_MODE_TYPE_MULTI_PROMISC;
|
|
|
- } else {
|
|
|
- /* Add all multicast MAC filters */
|
|
|
- rc = qede_set_mcast_rx_mac(edev, QED_FILTER_XCAST_TYPE_ADD,
|
|
|
- mc_macs, mc_count);
|
|
|
- }
|
|
|
-
|
|
|
-exit:
|
|
|
- kfree(mc_macs);
|
|
|
- return rc;
|
|
|
-}
|
|
|
-
|
|
|
-static void qede_set_rx_mode(struct net_device *ndev)
|
|
|
-{
|
|
|
- struct qede_dev *edev = netdev_priv(ndev);
|
|
|
-
|
|
|
- set_bit(QEDE_SP_RX_MODE, &edev->sp_flags);
|
|
|
- schedule_delayed_work(&edev->sp_task, 0);
|
|
|
-}
|
|
|
-
|
|
|
-/* Must be called with qede_lock held */
|
|
|
-static void qede_config_rx_mode(struct net_device *ndev)
|
|
|
-{
|
|
|
- enum qed_filter_rx_mode_type accept_flags = QED_FILTER_TYPE_UCAST;
|
|
|
- struct qede_dev *edev = netdev_priv(ndev);
|
|
|
- struct qed_filter_params rx_mode;
|
|
|
- unsigned char *uc_macs, *temp;
|
|
|
- struct netdev_hw_addr *ha;
|
|
|
- int rc, uc_count;
|
|
|
- size_t size;
|
|
|
-
|
|
|
- netif_addr_lock_bh(ndev);
|
|
|
-
|
|
|
- uc_count = netdev_uc_count(ndev);
|
|
|
- size = uc_count * ETH_ALEN;
|
|
|
-
|
|
|
- uc_macs = kzalloc(size, GFP_ATOMIC);
|
|
|
- if (!uc_macs) {
|
|
|
- DP_NOTICE(edev, "Failed to allocate memory for unicast MACs\n");
|
|
|
- netif_addr_unlock_bh(ndev);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- temp = uc_macs;
|
|
|
- netdev_for_each_uc_addr(ha, ndev) {
|
|
|
- ether_addr_copy(temp, ha->addr);
|
|
|
- temp += ETH_ALEN;
|
|
|
- }
|
|
|
-
|
|
|
- netif_addr_unlock_bh(ndev);
|
|
|
-
|
|
|
- /* Configure the struct for the Rx mode */
|
|
|
- memset(&rx_mode, 0, sizeof(struct qed_filter_params));
|
|
|
- rx_mode.type = QED_FILTER_TYPE_RX_MODE;
|
|
|
-
|
|
|
- /* Remove all previous unicast secondary macs and multicast macs
|
|
|
- * (configrue / leave the primary mac)
|
|
|
- */
|
|
|
- rc = qede_set_ucast_rx_mac(edev, QED_FILTER_XCAST_TYPE_REPLACE,
|
|
|
- edev->primary_mac);
|
|
|
- if (rc)
|
|
|
- goto out;
|
|
|
-
|
|
|
- /* Check for promiscuous */
|
|
|
- if ((ndev->flags & IFF_PROMISC) ||
|
|
|
- (uc_count > edev->dev_info.num_mac_filters - 1)) {
|
|
|
- accept_flags = QED_FILTER_RX_MODE_TYPE_PROMISC;
|
|
|
- } else {
|
|
|
- /* Add MAC filters according to the unicast secondary macs */
|
|
|
- int i;
|
|
|
-
|
|
|
- temp = uc_macs;
|
|
|
- for (i = 0; i < uc_count; i++) {
|
|
|
- rc = qede_set_ucast_rx_mac(edev,
|
|
|
- QED_FILTER_XCAST_TYPE_ADD,
|
|
|
- temp);
|
|
|
- if (rc)
|
|
|
- goto out;
|
|
|
-
|
|
|
- temp += ETH_ALEN;
|
|
|
- }
|
|
|
-
|
|
|
- rc = qede_configure_mcast_filtering(ndev, &accept_flags);
|
|
|
- if (rc)
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- /* take care of VLAN mode */
|
|
|
- if (ndev->flags & IFF_PROMISC) {
|
|
|
- qede_config_accept_any_vlan(edev, true);
|
|
|
- } else if (!edev->non_configured_vlans) {
|
|
|
- /* It's possible that accept_any_vlan mode is set due to a
|
|
|
- * previous setting of IFF_PROMISC. If vlan credits are
|
|
|
- * sufficient, disable accept_any_vlan.
|
|
|
- */
|
|
|
- qede_config_accept_any_vlan(edev, false);
|
|
|
- }
|
|
|
-
|
|
|
- rx_mode.filter.accept_flags = accept_flags;
|
|
|
- edev->ops->filter_config(edev->cdev, &rx_mode);
|
|
|
-out:
|
|
|
- kfree(uc_macs);
|
|
|
-}
|