summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/broadcom/bnge/bnge_netdev.c')
-rw-r--r--drivers/net/ethernet/broadcom/bnge/bnge_netdev.c1292
1 files changed, 1181 insertions, 111 deletions
diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
index 832eeb960bd2..a4288f0258f8 100644
--- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
+++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c
@@ -10,6 +10,9 @@
#include <linux/list.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
+#include <net/netdev_lock.h>
+#include <net/netdev_queues.h>
+#include <net/netdev_rx_queue.h>
#include <linux/etherdevice.h>
#include <linux/if.h>
#include <net/ip.h>
@@ -17,9 +20,11 @@
#include <net/page_pool/helpers.h>
#include "bnge.h"
+#include "bnge_hwrm.h"
#include "bnge_hwrm_lib.h"
#include "bnge_ethtool.h"
#include "bnge_rmem.h"
+#include "bnge_txrx.h"
#define BNGE_RING_TO_TC_OFF(bd, tx) \
((tx) % (bd)->tx_nr_rings_per_tc)
@@ -35,6 +40,10 @@ static void bnge_free_stats_mem(struct bnge_net *bn,
{
struct bnge_dev *bd = bn->bd;
+ kfree(stats->hw_masks);
+ stats->hw_masks = NULL;
+ kfree(stats->sw_stats);
+ stats->sw_stats = NULL;
if (stats->hw_stats) {
dma_free_coherent(bd->dev, stats->len, stats->hw_stats,
stats->hw_stats_map);
@@ -43,7 +52,7 @@ static void bnge_free_stats_mem(struct bnge_net *bn,
}
static int bnge_alloc_stats_mem(struct bnge_net *bn,
- struct bnge_stats_mem *stats)
+ struct bnge_stats_mem *stats, bool alloc_masks)
{
struct bnge_dev *bd = bn->bd;
@@ -52,7 +61,23 @@ static int bnge_alloc_stats_mem(struct bnge_net *bn,
if (!stats->hw_stats)
return -ENOMEM;
+ stats->sw_stats = kzalloc(stats->len, GFP_KERNEL);
+ if (!stats->sw_stats)
+ goto stats_mem_err;
+
+ if (alloc_masks) {
+ stats->hw_masks = kzalloc(stats->len, GFP_KERNEL);
+ if (!stats->hw_masks)
+ goto stats_mem_err;
+ }
+
+ u64_stats_init(&stats->syncp);
+
return 0;
+
+stats_mem_err:
+ bnge_free_stats_mem(bn, stats);
+ return -ENOMEM;
}
static void bnge_free_ring_stats(struct bnge_net *bn)
@@ -71,6 +96,107 @@ static void bnge_free_ring_stats(struct bnge_net *bn)
}
}
+static void bnge_fill_masks(u64 *mask_arr, u64 mask, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ mask_arr[i] = mask;
+}
+
+void bnge_copy_hw_masks(u64 *mask_arr, __le64 *hw_mask_arr, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ mask_arr[i] = le64_to_cpu(hw_mask_arr[i]);
+}
+
+static void bnge_init_stats(struct bnge_net *bn)
+{
+ struct bnge_napi *bnapi = bn->bnapi[0];
+ struct bnge_nq_ring_info *nqr;
+ struct bnge_stats_mem *stats;
+ struct bnge_dev *bd = bn->bd;
+ __le64 *rx_stats, *tx_stats;
+ int rc, rx_count, tx_count;
+ u64 *rx_masks, *tx_masks;
+ u8 flags;
+
+ nqr = &bnapi->nq_ring;
+ stats = &nqr->stats;
+ rc = bnge_hwrm_func_qstat_ext(bd, stats);
+ if (rc) {
+ u64 mask = (1ULL << 48) - 1;
+
+ bnge_fill_masks(stats->hw_masks, mask, stats->len / 8);
+ }
+
+ if (bn->flags & BNGE_FLAG_PORT_STATS) {
+ stats = &bn->port_stats;
+ rx_stats = stats->hw_stats;
+ rx_masks = stats->hw_masks;
+ rx_count = sizeof(struct rx_port_stats) / 8;
+ tx_stats = rx_stats + BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
+ tx_masks = rx_masks + BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
+ tx_count = sizeof(struct tx_port_stats) / 8;
+
+ flags = PORT_QSTATS_REQ_FLAGS_COUNTER_MASK;
+ rc = bnge_hwrm_port_qstats(bd, flags);
+ if (rc) {
+ u64 mask = (1ULL << 40) - 1;
+
+ bnge_fill_masks(rx_masks, mask, rx_count);
+ bnge_fill_masks(tx_masks, mask, tx_count);
+ } else {
+ bnge_copy_hw_masks(rx_masks, rx_stats, rx_count);
+ bnge_copy_hw_masks(tx_masks, tx_stats, tx_count);
+ }
+ bnge_hwrm_port_qstats(bd, 0);
+ }
+
+ if (bn->flags & BNGE_FLAG_PORT_STATS_EXT) {
+ stats = &bn->rx_port_stats_ext;
+ rx_stats = stats->hw_stats;
+ rx_masks = stats->hw_masks;
+ rx_count = sizeof(struct rx_port_stats_ext) / 8;
+ stats = &bn->tx_port_stats_ext;
+ tx_stats = stats->hw_stats;
+ tx_masks = stats->hw_masks;
+ tx_count = sizeof(struct tx_port_stats_ext) / 8;
+
+ flags = PORT_QSTATS_EXT_REQ_FLAGS_COUNTER_MASK;
+ rc = bnge_hwrm_port_qstats_ext(bd, flags);
+ if (rc) {
+ u64 mask = (1ULL << 40) - 1;
+
+ bnge_fill_masks(rx_masks, mask, rx_count);
+ if (tx_stats)
+ bnge_fill_masks(tx_masks, mask, tx_count);
+ } else {
+ bnge_copy_hw_masks(rx_masks, rx_stats, rx_count);
+ if (tx_stats)
+ bnge_copy_hw_masks(tx_masks, tx_stats,
+ tx_count);
+ }
+ bnge_hwrm_port_qstats_ext(bd, 0);
+ }
+}
+
+static void bnge_free_port_ext_stats(struct bnge_net *bn)
+{
+ bn->flags &= ~BNGE_FLAG_PORT_STATS_EXT;
+ bnge_free_stats_mem(bn, &bn->rx_port_stats_ext);
+ bnge_free_stats_mem(bn, &bn->tx_port_stats_ext);
+}
+
+static void bnge_free_port_stats(struct bnge_net *bn)
+{
+ bn->flags &= ~BNGE_FLAG_PORT_STATS;
+ bnge_free_stats_mem(bn, &bn->port_stats);
+ bnge_free_port_ext_stats(bn);
+}
+
static int bnge_alloc_ring_stats(struct bnge_net *bn)
{
struct bnge_dev *bd = bn->bd;
@@ -84,12 +210,13 @@ static int bnge_alloc_ring_stats(struct bnge_net *bn)
struct bnge_nq_ring_info *nqr = &bnapi->nq_ring;
nqr->stats.len = size;
- rc = bnge_alloc_stats_mem(bn, &nqr->stats);
+ rc = bnge_alloc_stats_mem(bn, &nqr->stats, !i);
if (rc)
goto err_free_ring_stats;
nqr->hw_stats_ctx_id = INVALID_STATS_CTX_ID;
}
+
return 0;
err_free_ring_stats:
@@ -97,6 +224,227 @@ err_free_ring_stats:
return rc;
}
+static void bnge_alloc_port_ext_stats(struct bnge_net *bn)
+{
+ struct bnge_dev *bd = bn->bd;
+ int rc;
+
+ if (!(bd->fw_cap & BNGE_FW_CAP_EXT_STATS_SUPPORTED))
+ return;
+
+ if (!bn->rx_port_stats_ext.hw_stats) {
+ bn->rx_port_stats_ext.len = sizeof(struct rx_port_stats_ext);
+ rc = bnge_alloc_stats_mem(bn, &bn->rx_port_stats_ext, true);
+ /* Extended stats are optional */
+ if (rc)
+ return;
+ }
+
+ if (!bn->tx_port_stats_ext.hw_stats) {
+ bn->tx_port_stats_ext.len = sizeof(struct tx_port_stats_ext);
+ rc = bnge_alloc_stats_mem(bn, &bn->tx_port_stats_ext, true);
+ /* Extended stats are optional */
+ if (rc) {
+ bnge_free_port_ext_stats(bn);
+ return;
+ }
+ }
+ bn->flags |= BNGE_FLAG_PORT_STATS_EXT;
+}
+
+static int bnge_alloc_port_stats(struct bnge_net *bn)
+{
+ int rc;
+
+ if (!bn->port_stats.hw_stats) {
+ bn->port_stats.len = BNGE_PORT_STATS_SIZE;
+ rc = bnge_alloc_stats_mem(bn, &bn->port_stats, true);
+ if (rc)
+ return rc;
+
+ bn->flags |= BNGE_FLAG_PORT_STATS;
+ }
+
+ bnge_alloc_port_ext_stats(bn);
+ return 0;
+}
+
+void __bnge_queue_sp_work(struct bnge_net *bn)
+{
+ queue_work(bn->bnge_pf_wq, &bn->sp_task);
+}
+
+static void bnge_queue_sp_work(struct bnge_net *bn, unsigned int event)
+{
+ set_bit(event, &bn->sp_event);
+ __bnge_queue_sp_work(bn);
+}
+
+static void bnge_timer(struct timer_list *t)
+{
+ struct bnge_net *bn = timer_container_of(bn, t, timer);
+ struct bnge_dev *bd = bn->bd;
+
+ if (!netif_running(bn->netdev) ||
+ !test_bit(BNGE_STATE_OPEN, &bd->state))
+ return;
+
+ if (READ_ONCE(bd->link_info.phy_retry)) {
+ if (time_after(jiffies, READ_ONCE(bd->link_info.phy_retry_expires))) {
+ WRITE_ONCE(bd->link_info.phy_retry, false);
+ netdev_warn(bn->netdev, "failed to update PHY settings after maximum retries.\n");
+ } else {
+ bnge_queue_sp_work(bn, BNGE_UPDATE_PHY_SP_EVENT);
+ }
+ }
+
+ if (BNGE_LINK_IS_UP(bd) && bn->stats_coal_ticks)
+ bnge_queue_sp_work(bn, BNGE_PERIODIC_STATS_SP_EVENT);
+
+ mod_timer(&bn->timer, jiffies + bn->current_interval);
+}
+
+static void bnge_add_one_ctr(u64 hw, u64 *sw, u64 mask)
+{
+ u64 sw_tmp, sw_val;
+
+ hw &= mask;
+ sw_val = READ_ONCE(*sw);
+ sw_tmp = (sw_val & ~mask) | hw;
+ if (hw < (sw_val & mask))
+ sw_tmp += mask + 1;
+ WRITE_ONCE(*sw, sw_tmp);
+}
+
+static void __bnge_accumulate_stats(__le64 *hw_stats, u64 *sw_stats, u64 *masks,
+ int count, struct u64_stats_sync *syncp)
+{
+ unsigned long flags;
+ int i;
+
+ flags = u64_stats_update_begin_irqsave(syncp);
+ for (i = 0; i < count; i++) {
+ u64 hw = le64_to_cpu(READ_ONCE(hw_stats[i]));
+
+ if (masks[i] == -1ULL)
+ WRITE_ONCE(sw_stats[i], hw);
+ else
+ bnge_add_one_ctr(hw, &sw_stats[i], masks[i]);
+ }
+ u64_stats_update_end_irqrestore(syncp, flags);
+}
+
+static void bnge_accumulate_stats(struct bnge_stats_mem *stats)
+{
+ if (!stats->hw_stats)
+ return;
+
+ __bnge_accumulate_stats(stats->hw_stats, stats->sw_stats,
+ stats->hw_masks, stats->len / 8, &stats->syncp);
+}
+
+static void bnge_accumulate_all_stats(struct bnge_dev *bd)
+{
+ struct bnge_net *bn = netdev_priv(bd->netdev);
+ struct bnge_stats_mem *ring0_stats = NULL;
+ int i;
+
+ for (i = 0; i < bd->nq_nr_rings; i++) {
+ struct bnge_napi *bnapi = bn->bnapi[i];
+ struct bnge_nq_ring_info *nqr;
+ struct bnge_stats_mem *stats;
+
+ nqr = &bnapi->nq_ring;
+ stats = &nqr->stats;
+
+ if (!ring0_stats)
+ ring0_stats = &bn->bnapi[0]->nq_ring.stats;
+
+ __bnge_accumulate_stats(stats->hw_stats, stats->sw_stats,
+ ring0_stats->hw_masks,
+ ring0_stats->len / 8, &stats->syncp);
+ }
+
+ if (bn->flags & BNGE_FLAG_PORT_STATS) {
+ struct bnge_stats_mem *stats = &bn->port_stats;
+ __le64 *hw_stats = stats->hw_stats;
+ u64 *sw_stats = stats->sw_stats;
+ u64 *masks = stats->hw_masks;
+ u16 cnt;
+
+ cnt = sizeof(struct rx_port_stats) / 8;
+ __bnge_accumulate_stats(hw_stats, sw_stats, masks, cnt,
+ &stats->syncp);
+
+ hw_stats += BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
+ sw_stats += BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
+ masks += BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
+ cnt = sizeof(struct tx_port_stats) / 8;
+ __bnge_accumulate_stats(hw_stats, sw_stats, masks, cnt,
+ &stats->syncp);
+ }
+
+ if (bn->flags & BNGE_FLAG_PORT_STATS_EXT) {
+ bnge_accumulate_stats(&bn->rx_port_stats_ext);
+ bnge_accumulate_stats(&bn->tx_port_stats_ext);
+ }
+}
+
+static void bnge_sp_task(struct work_struct *work)
+{
+ struct bnge_net *bn = container_of(work, struct bnge_net, sp_task);
+ bool speed_chng, cfg_chng, link_chng;
+ struct bnge_dev *bd = bn->bd;
+
+ netdev_lock(bn->netdev);
+ if (!test_bit(BNGE_STATE_OPEN, &bd->state)) {
+ netdev_unlock(bn->netdev);
+ return;
+ }
+
+ if (test_and_clear_bit(BNGE_PERIODIC_STATS_SP_EVENT, &bn->sp_event)) {
+ bnge_hwrm_port_qstats(bd, 0);
+ bnge_hwrm_port_qstats_ext(bd, 0);
+ bnge_accumulate_all_stats(bd);
+ }
+
+ if (test_and_clear_bit(BNGE_UPDATE_PHY_SP_EVENT, &bn->sp_event)) {
+ int rc;
+
+ rc = bnge_update_phy_setting(bn);
+ if (rc) {
+ netdev_warn(bn->netdev, "update PHY settings retry failed\n");
+ } else {
+ WRITE_ONCE(bd->link_info.phy_retry, false);
+ netdev_info(bn->netdev, "update PHY settings retry succeeded\n");
+ }
+ }
+
+ speed_chng = test_and_clear_bit(BNGE_LINK_SPEED_CHNG_SP_EVENT,
+ &bn->sp_event);
+ cfg_chng = test_and_clear_bit(BNGE_LINK_CFG_CHANGE_SP_EVENT,
+ &bn->sp_event);
+ link_chng = test_and_clear_bit(BNGE_LINK_CHNG_SP_EVENT,
+ &bn->sp_event);
+
+ if (speed_chng || cfg_chng || link_chng) {
+ int rc;
+
+ if (speed_chng)
+ bnge_hwrm_phy_qcaps(bd);
+
+ rc = bnge_update_link(bn, true);
+ if (rc)
+ netdev_err(bn->netdev, "SP task cannot update link (rc: %d)\n",
+ rc);
+
+ if (speed_chng || cfg_chng)
+ bnge_init_ethtool_link_settings(bn);
+ }
+
+ netdev_unlock(bn->netdev);
+}
+
static void bnge_free_nq_desc_arr(struct bnge_nq_ring_info *nqr)
{
struct bnge_ring_struct *ring = &nqr->ring_struct;
@@ -123,11 +471,11 @@ static void bnge_free_cp_desc_arr(struct bnge_cp_ring_info *cpr)
static int bnge_alloc_nq_desc_arr(struct bnge_nq_ring_info *nqr, int n)
{
- nqr->desc_ring = kcalloc(n, sizeof(*nqr->desc_ring), GFP_KERNEL);
+ nqr->desc_ring = kzalloc_objs(*nqr->desc_ring, n);
if (!nqr->desc_ring)
return -ENOMEM;
- nqr->desc_mapping = kcalloc(n, sizeof(*nqr->desc_mapping), GFP_KERNEL);
+ nqr->desc_mapping = kzalloc_objs(*nqr->desc_mapping, n);
if (!nqr->desc_mapping)
goto err_free_desc_ring;
return 0;
@@ -140,11 +488,11 @@ err_free_desc_ring:
static int bnge_alloc_cp_desc_arr(struct bnge_cp_ring_info *cpr, int n)
{
- cpr->desc_ring = kcalloc(n, sizeof(*cpr->desc_ring), GFP_KERNEL);
+ cpr->desc_ring = kzalloc_objs(*cpr->desc_ring, n);
if (!cpr->desc_ring)
return -ENOMEM;
- cpr->desc_mapping = kcalloc(n, sizeof(*cpr->desc_mapping), GFP_KERNEL);
+ cpr->desc_mapping = kzalloc_objs(*cpr->desc_mapping, n);
if (!cpr->desc_mapping)
goto err_free_desc_ring;
return 0;
@@ -283,8 +631,7 @@ static int bnge_alloc_nq_tree(struct bnge_net *bn)
tx = 1;
}
- nqr->cp_ring_arr = kcalloc(cp_count, sizeof(*cpr),
- GFP_KERNEL);
+ nqr->cp_ring_arr = kzalloc_objs(*cpr, cp_count);
if (!nqr->cp_ring_arr) {
rc = -ENOMEM;
goto err_free_nq_tree;
@@ -372,11 +719,37 @@ static void bnge_free_one_agg_ring_bufs(struct bnge_net *bn,
}
}
+static void bnge_free_one_tpa_info_data(struct bnge_net *bn,
+ struct bnge_rx_ring_info *rxr)
+{
+ int i;
+
+ for (i = 0; i < bn->max_tpa; i++) {
+ struct bnge_tpa_info *tpa_info = &rxr->rx_tpa[i];
+ u8 *data = tpa_info->data;
+
+ if (!data)
+ continue;
+
+ tpa_info->data = NULL;
+ page_pool_free_va(rxr->head_pool, data, false);
+ }
+}
+
static void bnge_free_one_rx_ring_pair_bufs(struct bnge_net *bn,
struct bnge_rx_ring_info *rxr)
{
+ struct bnge_tpa_idx_map *map;
+
+ if (rxr->rx_tpa)
+ bnge_free_one_tpa_info_data(bn, rxr);
+
bnge_free_one_rx_ring_bufs(bn, rxr);
bnge_free_one_agg_ring_bufs(bn, rxr);
+
+ map = rxr->rx_tpa_idx_map;
+ if (map)
+ memset(map->agg_idx_bmap, 0, sizeof(map->agg_idx_bmap));
}
static void bnge_free_rx_ring_pair_bufs(struct bnge_net *bn)
@@ -391,9 +764,116 @@ static void bnge_free_rx_ring_pair_bufs(struct bnge_net *bn)
bnge_free_one_rx_ring_pair_bufs(bn, &bn->rx_ring[i]);
}
+static void bnge_free_tx_skbs(struct bnge_net *bn)
+{
+ struct bnge_dev *bd = bn->bd;
+ u16 max_idx;
+ int i;
+
+ max_idx = bn->tx_nr_pages * TX_DESC_CNT;
+ for (i = 0; i < bd->tx_nr_rings; i++) {
+ struct bnge_tx_ring_info *txr = &bn->tx_ring[i];
+ int j;
+
+ if (!txr->tx_buf_ring)
+ continue;
+
+ for (j = 0; j < max_idx;) {
+ struct bnge_sw_tx_bd *tx_buf = &txr->tx_buf_ring[j];
+ struct sk_buff *skb;
+ int k, last;
+
+ skb = tx_buf->skb;
+ if (!skb) {
+ j++;
+ continue;
+ }
+
+ tx_buf->skb = NULL;
+
+ dma_unmap_single(bd->dev,
+ dma_unmap_addr(tx_buf, mapping),
+ skb_headlen(skb),
+ DMA_TO_DEVICE);
+
+ last = tx_buf->nr_frags;
+ j += 2;
+ for (k = 0; k < last; k++, j++) {
+ int ring_idx = j & bn->tx_ring_mask;
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[k];
+
+ tx_buf = &txr->tx_buf_ring[ring_idx];
+ dma_unmap_page(bd->dev,
+ dma_unmap_addr(tx_buf, mapping),
+ skb_frag_size(frag),
+ DMA_TO_DEVICE);
+ }
+ dev_kfree_skb(skb);
+ }
+ netdev_tx_reset_queue(netdev_get_tx_queue(bd->netdev, i));
+ }
+}
+
static void bnge_free_all_rings_bufs(struct bnge_net *bn)
{
bnge_free_rx_ring_pair_bufs(bn);
+ bnge_free_tx_skbs(bn);
+}
+
+static void bnge_free_tpa_info(struct bnge_net *bn)
+{
+ struct bnge_dev *bd = bn->bd;
+ int i, j;
+
+ for (i = 0; i < bd->rx_nr_rings; i++) {
+ struct bnge_rx_ring_info *rxr = &bn->rx_ring[i];
+
+ kfree(rxr->rx_tpa_idx_map);
+ rxr->rx_tpa_idx_map = NULL;
+ if (rxr->rx_tpa) {
+ for (j = 0; j < bn->max_tpa; j++) {
+ kfree(rxr->rx_tpa[j].agg_arr);
+ rxr->rx_tpa[j].agg_arr = NULL;
+ }
+ }
+ kfree(rxr->rx_tpa);
+ rxr->rx_tpa = NULL;
+ }
+}
+
+static int bnge_alloc_tpa_info(struct bnge_net *bn)
+{
+ struct bnge_dev *bd = bn->bd;
+ int i, j;
+
+ if (!bd->max_tpa_v2)
+ return 0;
+
+ bn->max_tpa = max_t(u16, bd->max_tpa_v2, MAX_TPA);
+ for (i = 0; i < bd->rx_nr_rings; i++) {
+ struct bnge_rx_ring_info *rxr = &bn->rx_ring[i];
+
+ rxr->rx_tpa = kzalloc_objs(struct bnge_tpa_info, bn->max_tpa);
+ if (!rxr->rx_tpa)
+ goto err_free_tpa_info;
+
+ for (j = 0; j < bn->max_tpa; j++) {
+ struct rx_agg_cmp *agg;
+
+ agg = kzalloc_objs(*agg, MAX_SKB_FRAGS);
+ if (!agg)
+ goto err_free_tpa_info;
+ rxr->rx_tpa[j].agg_arr = agg;
+ }
+ rxr->rx_tpa_idx_map = kzalloc_obj(*rxr->rx_tpa_idx_map);
+ if (!rxr->rx_tpa_idx_map)
+ goto err_free_tpa_info;
+ }
+ return 0;
+
+err_free_tpa_info:
+ bnge_free_tpa_info(bn);
+ return -ENOMEM;
}
static void bnge_free_rx_rings(struct bnge_net *bn)
@@ -401,6 +881,7 @@ static void bnge_free_rx_rings(struct bnge_net *bn)
struct bnge_dev *bd = bn->bd;
int i;
+ bnge_free_tpa_info(bn);
for (i = 0; i < bd->rx_nr_rings; i++) {
struct bnge_rx_ring_info *rxr = &bn->rx_ring[i];
struct bnge_ring_struct *ring;
@@ -525,6 +1006,12 @@ static int bnge_alloc_rx_rings(struct bnge_net *bn)
goto err_free_rx_rings;
}
}
+
+ if (bn->priv_flags & BNGE_NET_EN_TPA) {
+ rc = bnge_alloc_tpa_info(bn);
+ if (rc)
+ goto err_free_rx_rings;
+ }
return rc;
err_free_rx_rings:
@@ -667,8 +1154,7 @@ static int bnge_alloc_vnics(struct bnge_net *bn)
*/
num_vnics = 1;
- bn->vnic_info = kcalloc(num_vnics, sizeof(struct bnge_vnic_info),
- GFP_KERNEL);
+ bn->vnic_info = kzalloc_objs(struct bnge_vnic_info, num_vnics);
if (!bn->vnic_info)
return -ENOMEM;
@@ -695,9 +1181,7 @@ static int bnge_init_ring_grps(struct bnge_net *bn)
struct bnge_dev *bd = bn->bd;
int i;
- bn->grp_info = kcalloc(bd->nq_nr_rings,
- sizeof(struct bnge_ring_grp_info),
- GFP_KERNEL);
+ bn->grp_info = kzalloc_objs(struct bnge_ring_grp_info, bd->nq_nr_rings);
if (!bn->grp_info)
return -ENOMEM;
for (i = 0; i < bd->nq_nr_rings; i++) {
@@ -757,9 +1241,7 @@ static int bnge_alloc_core(struct bnge_net *bn)
nqr->ring_struct.ring_mem.flags = BNGE_RMEM_RING_PTE_FLAG;
}
- bn->rx_ring = kcalloc(bd->rx_nr_rings,
- sizeof(struct bnge_rx_ring_info),
- GFP_KERNEL);
+ bn->rx_ring = kzalloc_objs(struct bnge_rx_ring_info, bd->rx_nr_rings);
if (!bn->rx_ring)
goto err_free_core;
@@ -774,9 +1256,7 @@ static int bnge_alloc_core(struct bnge_net *bn)
bn->bnapi[i]->rx_ring = &bn->rx_ring[i];
}
- bn->tx_ring = kcalloc(bd->tx_nr_rings,
- sizeof(struct bnge_tx_ring_info),
- GFP_KERNEL);
+ bn->tx_ring = kzalloc_objs(struct bnge_tx_ring_info, bd->tx_nr_rings);
if (!bn->tx_ring)
goto err_free_core;
@@ -811,6 +1291,8 @@ static int bnge_alloc_core(struct bnge_net *bn)
if (rc)
goto err_free_core;
+ bnge_init_stats(bn);
+
rc = bnge_alloc_vnics(bn);
if (rc)
goto err_free_core;
@@ -846,16 +1328,23 @@ err_free_core:
return rc;
}
-u16 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr)
+u32 bnge_cp_ring_for_rx(struct bnge_rx_ring_info *rxr)
{
return rxr->rx_cpr->ring_struct.fw_ring_id;
}
-u16 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr)
+u32 bnge_cp_ring_for_tx(struct bnge_tx_ring_info *txr)
{
return txr->tx_cpr->ring_struct.fw_ring_id;
}
+static void bnge_db_nq_arm(struct bnge_net *bn,
+ struct bnge_db_info *db, u32 idx)
+{
+ bnge_writeq(bn->bd, db->db_key64 | DBR_TYPE_NQ_ARM |
+ DB_RING_IDX(db, idx), db->doorbell);
+}
+
static void bnge_db_nq(struct bnge_net *bn, struct bnge_db_info *db, u32 idx)
{
bnge_writeq(bn->bd, db->db_key64 | DBR_TYPE_NQ_MASK |
@@ -878,12 +1367,6 @@ static int bnge_cp_num_to_irq_num(struct bnge_net *bn, int n)
return nqr->ring_struct.map_idx;
}
-static irqreturn_t bnge_msix(int irq, void *dev_instance)
-{
- /* NAPI scheduling to be added in a future patch */
- return IRQ_HANDLED;
-}
-
static void bnge_init_nq_tree(struct bnge_net *bn)
{
struct bnge_dev *bd = bn->bd;
@@ -893,12 +1376,12 @@ static void bnge_init_nq_tree(struct bnge_net *bn)
struct bnge_nq_ring_info *nqr = &bn->bnapi[i]->nq_ring;
struct bnge_ring_struct *ring = &nqr->ring_struct;
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
for (j = 0; j < nqr->cp_ring_count; j++) {
struct bnge_cp_ring_info *cpr = &nqr->cp_ring_arr[j];
ring = &cpr->ring_struct;
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
}
}
}
@@ -925,9 +1408,9 @@ static netmem_ref __bnge_alloc_rx_netmem(struct bnge_net *bn,
return netmem;
}
-static u8 *__bnge_alloc_rx_frag(struct bnge_net *bn, dma_addr_t *mapping,
- struct bnge_rx_ring_info *rxr,
- gfp_t gfp)
+u8 *__bnge_alloc_rx_frag(struct bnge_net *bn, dma_addr_t *mapping,
+ struct bnge_rx_ring_info *rxr,
+ gfp_t gfp)
{
unsigned int offset;
struct page *page;
@@ -941,9 +1424,8 @@ static u8 *__bnge_alloc_rx_frag(struct bnge_net *bn, dma_addr_t *mapping,
return page_address(page) + offset;
}
-static int bnge_alloc_rx_data(struct bnge_net *bn,
- struct bnge_rx_ring_info *rxr,
- u16 prod, gfp_t gfp)
+int bnge_alloc_rx_data(struct bnge_net *bn, struct bnge_rx_ring_info *rxr,
+ u16 prod, gfp_t gfp)
{
struct bnge_sw_rx_bd *rx_buf = &rxr->rx_buf_ring[RING_RX(bn, prod)];
struct rx_bd *rxbd;
@@ -995,7 +1477,7 @@ static int bnge_alloc_one_rx_ring_bufs(struct bnge_net *bn,
return 0;
}
-static u16 bnge_find_next_agg_idx(struct bnge_rx_ring_info *rxr, u16 idx)
+u16 bnge_find_next_agg_idx(struct bnge_rx_ring_info *rxr, u16 idx)
{
u16 next, max = rxr->rx_agg_bmap_size;
@@ -1005,9 +1487,9 @@ static u16 bnge_find_next_agg_idx(struct bnge_rx_ring_info *rxr, u16 idx)
return next;
}
-static int bnge_alloc_rx_netmem(struct bnge_net *bn,
- struct bnge_rx_ring_info *rxr,
- u16 prod, gfp_t gfp)
+int bnge_alloc_rx_netmem(struct bnge_net *bn,
+ struct bnge_rx_ring_info *rxr,
+ u16 prod, gfp_t gfp)
{
struct bnge_sw_rx_agg_bd *rx_agg_buf;
u16 sw_prod = rxr->rx_sw_agg_prod;
@@ -1070,6 +1552,30 @@ err_free_one_agg_ring_bufs:
return -ENOMEM;
}
+static int bnge_alloc_one_tpa_info_data(struct bnge_net *bn,
+ struct bnge_rx_ring_info *rxr)
+{
+ dma_addr_t mapping;
+ u8 *data;
+ int i;
+
+ for (i = 0; i < bn->max_tpa; i++) {
+ data = __bnge_alloc_rx_frag(bn, &mapping, rxr,
+ GFP_KERNEL);
+ if (!data)
+ goto err_free_tpa_info_data;
+
+ rxr->rx_tpa[i].data = data;
+ rxr->rx_tpa[i].data_ptr = data + bn->rx_offset;
+ rxr->rx_tpa[i].mapping = mapping;
+ }
+ return 0;
+
+err_free_tpa_info_data:
+ bnge_free_one_tpa_info_data(bn, rxr);
+ return -ENOMEM;
+}
+
static int bnge_alloc_one_rx_ring_pair_bufs(struct bnge_net *bn, int ring_nr)
{
struct bnge_rx_ring_info *rxr = &bn->rx_ring[ring_nr];
@@ -1084,8 +1590,17 @@ static int bnge_alloc_one_rx_ring_pair_bufs(struct bnge_net *bn, int ring_nr)
if (rc)
goto err_free_one_rx_ring_bufs;
}
+
+ if (rxr->rx_tpa) {
+ rc = bnge_alloc_one_tpa_info_data(bn, rxr);
+ if (rc)
+ goto err_free_one_agg_ring_bufs;
+ }
+
return 0;
+err_free_one_agg_ring_bufs:
+ bnge_free_one_agg_ring_bufs(bn, rxr);
err_free_one_rx_ring_bufs:
bnge_free_one_rx_ring_bufs(bn, rxr);
return rc;
@@ -1123,7 +1638,7 @@ static void bnge_init_one_rx_ring_rxbd(struct bnge_net *bn,
ring = &rxr->rx_ring_struct;
bnge_init_rxbd_pages(ring, type);
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
}
static void bnge_init_one_agg_ring_rxbd(struct bnge_net *bn,
@@ -1133,7 +1648,7 @@ static void bnge_init_one_agg_ring_rxbd(struct bnge_net *bn,
u32 type;
ring = &rxr->rx_agg_ring_struct;
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
if (bnge_is_agg_reqd(bn->bd)) {
type = ((u32)BNGE_RX_PAGE_SIZE << RX_BD_LEN_SHIFT) |
RX_BD_TYPE_RX_AGG_BD | RX_BD_FLAGS_SOP;
@@ -1194,7 +1709,7 @@ static void bnge_init_tx_rings(struct bnge_net *bn)
struct bnge_tx_ring_info *txr = &bn->tx_ring[i];
struct bnge_ring_struct *ring = &txr->tx_ring_struct;
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
netif_queue_set_napi(bn->netdev, i, NETDEV_QUEUE_TYPE_TX,
&txr->bnapi->napi);
@@ -1353,7 +1868,7 @@ static int bnge_hwrm_rx_agg_ring_alloc(struct bnge_net *bn,
ring->fw_ring_id);
bnge_db_write(bn->bd, &rxr->rx_agg_db, rxr->rx_agg_prod);
bnge_db_write(bn->bd, &rxr->rx_db, rxr->rx_prod);
- bn->grp_info[grp_idx].agg_fw_ring_id = ring->fw_ring_id;
+ bn->grp_info[grp_idx].agg_fw_ring_id = (u16)ring->fw_ring_id;
return 0;
}
@@ -1372,7 +1887,7 @@ static int bnge_hwrm_rx_ring_alloc(struct bnge_net *bn,
return rc;
bnge_set_db(bn, &rxr->rx_db, type, map_idx, ring->fw_ring_id);
- bn->grp_info[map_idx].rx_fw_ring_id = ring->fw_ring_id;
+ bn->grp_info[map_idx].rx_fw_ring_id = (u16)ring->fw_ring_id;
return 0;
}
@@ -1402,7 +1917,7 @@ static int bnge_hwrm_ring_alloc(struct bnge_net *bn)
bnge_set_db(bn, &nqr->nq_db, type, map_idx, ring->fw_ring_id);
bnge_db_nq(bn, &nqr->nq_db, nqr->nq_raw_cons);
enable_irq(vector);
- bn->grp_info[i].nq_fw_ring_id = ring->fw_ring_id;
+ bn->grp_info[i].nq_fw_ring_id = (u16)ring->fw_ring_id;
if (!i) {
rc = bnge_hwrm_set_async_event_cr(bd, ring->fw_ring_id);
@@ -1472,15 +1987,13 @@ void bnge_fill_hw_rss_tbl(struct bnge_net *bn, struct bnge_vnic_info *vnic)
tbl_size = bnge_get_rxfh_indir_size(bd);
for (i = 0; i < tbl_size; i++) {
- u16 ring_id, j;
+ u32 j;
j = bd->rss_indir_tbl[i];
rxr = &bn->rx_ring[j];
- ring_id = rxr->rx_ring_struct.fw_ring_id;
- *ring_tbl++ = cpu_to_le16(ring_id);
- ring_id = bnge_cp_ring_for_rx(rxr);
- *ring_tbl++ = cpu_to_le16(ring_id);
+ *ring_tbl++ = cpu_to_le16(rxr->rx_ring_struct.fw_ring_id);
+ *ring_tbl++ = cpu_to_le16(bnge_cp_ring_for_rx(rxr));
}
}
@@ -1599,7 +2112,7 @@ static struct bnge_l2_filter *bnge_alloc_l2_filter(struct bnge_net *bn,
if (fltr)
return fltr;
- fltr = kzalloc(sizeof(*fltr), gfp);
+ fltr = kzalloc_obj(*fltr, gfp);
if (!fltr)
return ERR_PTR(-ENOMEM);
@@ -1632,16 +2145,16 @@ err_del_l2_filter:
return rc;
}
-static bool bnge_mc_list_updated(struct bnge_net *bn, u32 *rx_mask)
+static bool bnge_mc_list_updated(struct bnge_net *bn, u32 *rx_mask,
+ const struct netdev_hw_addr_list *mc)
{
struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT];
- struct net_device *dev = bn->netdev;
struct netdev_hw_addr *ha;
int mc_count = 0, off = 0;
bool update = false;
u8 *haddr;
- netdev_for_each_mc_addr(ha, dev) {
+ netdev_hw_addr_list_for_each(ha, mc) {
if (mc_count >= BNGE_MAX_MC_ADDRS) {
*rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST;
vnic->mc_list_count = 0;
@@ -1665,17 +2178,17 @@ static bool bnge_mc_list_updated(struct bnge_net *bn, u32 *rx_mask)
return update;
}
-static bool bnge_uc_list_updated(struct bnge_net *bn)
+static bool bnge_uc_list_updated(struct bnge_net *bn,
+ const struct netdev_hw_addr_list *uc)
{
struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT];
- struct net_device *dev = bn->netdev;
struct netdev_hw_addr *ha;
int off = 0;
- if (netdev_uc_count(dev) != (vnic->uc_filter_count - 1))
+ if (netdev_hw_addr_list_count(uc) != (vnic->uc_filter_count - 1))
return true;
- netdev_for_each_uc_addr(ha, dev) {
+ netdev_hw_addr_list_for_each(ha, uc) {
if (!ether_addr_equal(ha->addr, vnic->uc_list + off))
return true;
@@ -1689,18 +2202,14 @@ static bool bnge_promisc_ok(struct bnge_net *bn)
return true;
}
-static int bnge_cfg_def_vnic(struct bnge_net *bn)
+static int bnge_cfg_rx_mode(struct bnge_net *bn, struct netdev_hw_addr_list *uc,
+ bool uc_update, bool snapshot)
{
struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT];
struct net_device *dev = bn->netdev;
struct bnge_dev *bd = bn->bd;
struct netdev_hw_addr *ha;
int i, off = 0, rc;
- bool uc_update;
-
- netif_addr_lock_bh(dev);
- uc_update = bnge_uc_list_updated(bn);
- netif_addr_unlock_bh(dev);
if (!uc_update)
goto skip_uc;
@@ -1714,22 +2223,28 @@ static int bnge_cfg_def_vnic(struct bnge_net *bn)
vnic->uc_filter_count = 1;
- netif_addr_lock_bh(dev);
- if (netdev_uc_count(dev) > (BNGE_MAX_UC_ADDRS - 1)) {
+ if (!snapshot)
+ netif_addr_lock_bh(dev);
+ if (netdev_hw_addr_list_count(uc) > (BNGE_MAX_UC_ADDRS - 1)) {
vnic->rx_mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS;
} else {
- netdev_for_each_uc_addr(ha, dev) {
+ netdev_hw_addr_list_for_each(ha, uc) {
memcpy(vnic->uc_list + off, ha->addr, ETH_ALEN);
off += ETH_ALEN;
vnic->uc_filter_count++;
}
}
- netif_addr_unlock_bh(dev);
+ if (!snapshot)
+ netif_addr_unlock_bh(dev);
for (i = 1, off = 0; i < vnic->uc_filter_count; i++, off += ETH_ALEN) {
rc = bnge_hwrm_set_vnic_filter(bn, 0, i, vnic->uc_list + off);
if (rc) {
- netdev_err(dev, "HWRM vnic filter failure rc: %d\n", rc);
+ if (rc == -EAGAIN)
+ netdev_warn(dev, "FW busy while setting vnic filter, will retry\n");
+ else
+ netdev_err(dev, "HWRM vnic filter failure rc: %d\n",
+ rc);
vnic->uc_filter_count = i;
return rc;
}
@@ -1748,13 +2263,137 @@ skip_uc:
vnic->mc_list_count = 0;
rc = bnge_hwrm_cfa_l2_set_rx_mask(bd, vnic);
}
- if (rc)
- netdev_err(dev, "HWRM cfa l2 rx mask failure rc: %d\n",
- rc);
+ if (rc) {
+ if (rc == -EAGAIN) {
+ netdev_warn(dev, "FW busy while setting l2 rx mask in CFA, will retry\n");
+ vnic->rx_mask &= ~BNGE_RX_MASK_CFG_FLAGS;
+ } else {
+ netdev_err(dev, "HWRM CFA L2 rx mask failure rc: %d\n",
+ rc);
+ }
+ }
return rc;
}
+static int bnge_set_rx_mode(struct net_device *dev,
+ struct netdev_hw_addr_list *uc,
+ struct netdev_hw_addr_list *mc)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ struct bnge_vnic_info *vnic;
+ bool mc_update = false;
+ bool uc_update;
+ u32 mask;
+
+ if (!test_bit(BNGE_STATE_OPEN, &bn->bd->state))
+ return 0;
+
+ vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT];
+ mask = vnic->rx_mask;
+ mask &= ~BNGE_RX_MASK_CFG_FLAGS;
+
+ if (dev->flags & IFF_PROMISC)
+ mask |= CFA_L2_SET_RX_MASK_REQ_MASK_PROMISCUOUS;
+
+ uc_update = bnge_uc_list_updated(bn, uc);
+
+ if (dev->flags & IFF_BROADCAST)
+ mask |= CFA_L2_SET_RX_MASK_REQ_MASK_BCAST;
+ if (dev->flags & IFF_ALLMULTI) {
+ mask |= CFA_L2_SET_RX_MASK_REQ_MASK_ALL_MCAST;
+ vnic->mc_list_count = 0;
+ } else if (dev->flags & IFF_MULTICAST) {
+ mc_update = bnge_mc_list_updated(bn, &mask, mc);
+ }
+
+ if (mask != vnic->rx_mask || uc_update || mc_update) {
+ vnic->rx_mask = mask;
+ return bnge_cfg_rx_mode(bn, uc, uc_update, true);
+ }
+
+ return 0;
+}
+
+static void bnge_disable_int(struct bnge_net *bn)
+{
+ struct bnge_dev *bd = bn->bd;
+ int i;
+
+ if (!bn->bnapi)
+ return;
+
+ for (i = 0; i < bd->nq_nr_rings; i++) {
+ struct bnge_napi *bnapi = bn->bnapi[i];
+ struct bnge_nq_ring_info *nqr;
+ struct bnge_ring_struct *ring;
+
+ nqr = &bnapi->nq_ring;
+ ring = &nqr->ring_struct;
+
+ if (ring->fw_ring_id != INVALID_HW_RING_ID_32BIT)
+ bnge_db_nq(bn, &nqr->nq_db, nqr->nq_raw_cons);
+ }
+}
+
+static void bnge_disable_int_sync(struct bnge_net *bn)
+{
+ struct bnge_dev *bd = bn->bd;
+ int i;
+
+ bnge_disable_int(bn);
+ for (i = 0; i < bd->nq_nr_rings; i++) {
+ int map_idx = bnge_cp_num_to_irq_num(bn, i);
+
+ synchronize_irq(bd->irq_tbl[map_idx].vector);
+ }
+}
+
+static void bnge_enable_int(struct bnge_net *bn)
+{
+ struct bnge_dev *bd = bn->bd;
+ int i;
+
+ for (i = 0; i < bd->nq_nr_rings; i++) {
+ struct bnge_napi *bnapi = bn->bnapi[i];
+ struct bnge_nq_ring_info *nqr;
+
+ nqr = &bnapi->nq_ring;
+ bnge_db_nq_arm(bn, &nqr->nq_db, nqr->nq_raw_cons);
+ }
+}
+
+static void bnge_disable_napi(struct bnge_net *bn)
+{
+ struct bnge_dev *bd = bn->bd;
+ int i;
+
+ if (test_and_set_bit(BNGE_STATE_NAPI_DISABLED, &bn->state))
+ return;
+
+ for (i = 0; i < bd->nq_nr_rings; i++) {
+ struct bnge_napi *bnapi = bn->bnapi[i];
+
+ napi_disable_locked(&bnapi->napi);
+ }
+}
+
+static void bnge_enable_napi(struct bnge_net *bn)
+{
+ struct bnge_dev *bd = bn->bd;
+ int i;
+
+ clear_bit(BNGE_STATE_NAPI_DISABLED, &bn->state);
+ for (i = 0; i < bd->nq_nr_rings; i++) {
+ struct bnge_napi *bnapi = bn->bnapi[i];
+
+ bnapi->in_reset = false;
+ bnapi->tx_fault = 0;
+
+ napi_enable_locked(&bnapi->napi);
+ }
+}
+
static void bnge_hwrm_vnic_free(struct bnge_net *bn)
{
int i;
@@ -1808,7 +2447,7 @@ static void bnge_hwrm_rx_ring_free(struct bnge_net *bn,
u32 grp_idx = rxr->bnapi->index;
u32 cmpl_ring_id;
- if (ring->fw_ring_id == INVALID_HW_RING_ID)
+ if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT)
return;
cmpl_ring_id = bnge_cp_ring_for_rx(rxr);
@@ -1816,7 +2455,7 @@ static void bnge_hwrm_rx_ring_free(struct bnge_net *bn,
RING_FREE_REQ_RING_TYPE_RX,
close_path ? cmpl_ring_id :
INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
bn->grp_info[grp_idx].rx_fw_ring_id = INVALID_HW_RING_ID;
}
@@ -1828,14 +2467,14 @@ static void bnge_hwrm_rx_agg_ring_free(struct bnge_net *bn,
u32 grp_idx = rxr->bnapi->index;
u32 cmpl_ring_id;
- if (ring->fw_ring_id == INVALID_HW_RING_ID)
+ if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT)
return;
cmpl_ring_id = bnge_cp_ring_for_rx(rxr);
hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_RX_AGG,
close_path ? cmpl_ring_id :
INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
bn->grp_info[grp_idx].agg_fw_ring_id = INVALID_HW_RING_ID;
}
@@ -1846,14 +2485,14 @@ static void bnge_hwrm_tx_ring_free(struct bnge_net *bn,
struct bnge_ring_struct *ring = &txr->tx_ring_struct;
u32 cmpl_ring_id;
- if (ring->fw_ring_id == INVALID_HW_RING_ID)
+ if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT)
return;
cmpl_ring_id = close_path ? bnge_cp_ring_for_tx(txr) :
INVALID_HW_RING_ID;
hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_TX,
cmpl_ring_id);
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
}
static void bnge_hwrm_cp_ring_free(struct bnge_net *bn,
@@ -1862,12 +2501,12 @@ static void bnge_hwrm_cp_ring_free(struct bnge_net *bn,
struct bnge_ring_struct *ring;
ring = &cpr->ring_struct;
- if (ring->fw_ring_id == INVALID_HW_RING_ID)
+ if (ring->fw_ring_id == INVALID_HW_RING_ID_32BIT)
return;
hwrm_ring_free_send_msg(bn, ring, RING_FREE_REQ_RING_TYPE_L2_CMPL,
INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
}
static void bnge_hwrm_ring_free(struct bnge_net *bn, bool close_path)
@@ -1886,6 +2525,12 @@ static void bnge_hwrm_ring_free(struct bnge_net *bn, bool close_path)
bnge_hwrm_rx_agg_ring_free(bn, &bn->rx_ring[i], close_path);
}
+ /* The completion rings are about to be freed. After that the
+ * IRQ doorbell will not work anymore. So we need to disable
+ * IRQ here.
+ */
+ bnge_disable_int_sync(bn);
+
for (i = 0; i < bd->nq_nr_rings; i++) {
struct bnge_napi *bnapi = bn->bnapi[i];
struct bnge_nq_ring_info *nqr;
@@ -1897,11 +2542,11 @@ static void bnge_hwrm_ring_free(struct bnge_net *bn, bool close_path)
bnge_hwrm_cp_ring_free(bn, &nqr->cp_ring_arr[j]);
ring = &nqr->ring_struct;
- if (ring->fw_ring_id != INVALID_HW_RING_ID) {
+ if (ring->fw_ring_id != INVALID_HW_RING_ID_32BIT) {
hwrm_ring_free_send_msg(bn, ring,
RING_FREE_REQ_RING_TYPE_NQ,
INVALID_HW_RING_ID);
- ring->fw_ring_id = INVALID_HW_RING_ID;
+ ring->fw_ring_id = INVALID_HW_RING_ID_32BIT;
bn->grp_info[i].nq_fw_ring_id = INVALID_HW_RING_ID;
}
}
@@ -2015,6 +2660,27 @@ err_free_irq:
return rc;
}
+static int bnge_set_tpa(struct bnge_net *bn, bool set_tpa)
+{
+ u32 tpa_flags = 0;
+ int rc, i;
+
+ if (set_tpa)
+ tpa_flags = bn->priv_flags & BNGE_NET_EN_TPA;
+ else if (BNGE_NO_FW_ACCESS(bn->bd))
+ return 0;
+ for (i = 0; i < bn->nr_vnics; i++) {
+ rc = bnge_hwrm_vnic_set_tpa(bn->bd, &bn->vnic_info[i],
+ tpa_flags);
+ if (rc) {
+ netdev_err(bn->netdev, "hwrm vnic set tpa failure rc for vnic %d: %x\n",
+ i, rc);
+ return rc;
+ }
+ }
+ return 0;
+}
+
static int bnge_init_chip(struct bnge_net *bn)
{
struct bnge_vnic_info *vnic = &bn->vnic_info[BNGE_VNIC_DEFAULT];
@@ -2049,6 +2715,12 @@ static int bnge_init_chip(struct bnge_net *bn)
if (bd->rss_cap & BNGE_RSS_CAP_RSS_HASH_TYPE_DELTA)
bnge_hwrm_update_rss_hash_cfg(bn);
+ if (bn->priv_flags & BNGE_NET_EN_TPA) {
+ rc = bnge_set_tpa(bn, true);
+ if (rc)
+ goto err_out;
+ }
+
/* Filter for default vnic 0 */
rc = bnge_hwrm_set_vnic_filter(bn, 0, 0, bn->netdev->dev_addr);
if (rc) {
@@ -2071,13 +2743,17 @@ static int bnge_init_chip(struct bnge_net *bn)
} else if (bn->netdev->flags & IFF_MULTICAST) {
u32 mask = 0;
- bnge_mc_list_updated(bn, &mask);
+ bnge_mc_list_updated(bn, &mask, &bn->netdev->mc);
vnic->rx_mask |= mask;
}
- rc = bnge_cfg_def_vnic(bn);
- if (rc)
+ rc = bnge_cfg_rx_mode(bn, &bn->netdev->uc, true, false);
+ if (rc == -EAGAIN) {
+ netif_rx_mode_schedule_retry(bn->netdev);
+ rc = 0;
+ } else if (rc) {
goto err_out;
+ }
return 0;
err_out:
@@ -2085,16 +2761,6 @@ err_out:
return rc;
}
-static int bnge_napi_poll(struct napi_struct *napi, int budget)
-{
- int work_done = 0;
-
- /* defer NAPI implementation to next patch series */
- napi_complete_done(napi, work_done);
-
- return work_done;
-}
-
static void bnge_init_napi(struct bnge_net *bn)
{
struct bnge_dev *bd = bn->bd;
@@ -2154,13 +2820,67 @@ static int bnge_init_nic(struct bnge_net *bn)
err_free_ring_grps:
bnge_free_ring_grps(bn);
- return rc;
-
err_free_rx_ring_pair_bufs:
bnge_free_rx_ring_pair_bufs(bn);
return rc;
}
+static void bnge_tx_disable(struct bnge_net *bn)
+{
+ struct bnge_tx_ring_info *txr;
+ int i;
+
+ if (bn->tx_ring) {
+ for (i = 0; i < bn->bd->tx_nr_rings; i++) {
+ txr = &bn->tx_ring[i];
+ WRITE_ONCE(txr->dev_state, BNGE_DEV_STATE_CLOSING);
+ }
+ }
+ /* Make sure napi polls see @dev_state change */
+ synchronize_net();
+
+ if (!bn->netdev)
+ return;
+ /* Drop carrier first to prevent TX timeout */
+ netif_carrier_off(bn->netdev);
+ /* Stop all TX queues */
+ netif_tx_disable(bn->netdev);
+}
+
+static void bnge_tx_enable(struct bnge_net *bn)
+{
+ struct bnge_tx_ring_info *txr;
+ int i;
+
+ for (i = 0; i < bn->bd->tx_nr_rings; i++) {
+ txr = &bn->tx_ring[i];
+ WRITE_ONCE(txr->dev_state, 0);
+ }
+ /* Make sure napi polls see @dev_state change */
+ synchronize_net();
+ netif_tx_wake_all_queues(bn->netdev);
+ if (BNGE_LINK_IS_UP(bn->bd))
+ netif_carrier_on(bn->netdev);
+}
+
+static int bnge_hwrm_if_change(struct bnge_dev *bd, bool up)
+{
+ struct hwrm_func_drv_if_change_input *req;
+ int rc;
+
+ if (!(bd->fw_cap & BNGE_FW_CAP_IF_CHANGE))
+ return 0;
+
+ rc = bnge_hwrm_req_init(bd, req, HWRM_FUNC_DRV_IF_CHANGE);
+ if (rc)
+ return rc;
+
+ if (up)
+ req->flags = cpu_to_le32(FUNC_DRV_IF_CHANGE_REQ_FLAGS_UP);
+
+ return bnge_hwrm_req_send(bd, req);
+}
+
static int bnge_open_core(struct bnge_net *bn)
{
struct bnge_dev *bd = bn->bd;
@@ -2168,16 +2888,22 @@ static int bnge_open_core(struct bnge_net *bn)
netif_carrier_off(bn->netdev);
+ rc = bnge_hwrm_if_change(bd, true);
+ if (rc) {
+ netdev_err(bn->netdev, "bnge_hwrm_if_change err: %d\n", rc);
+ return rc;
+ }
+
rc = bnge_reserve_rings(bd);
if (rc) {
netdev_err(bn->netdev, "bnge_reserve_rings err: %d\n", rc);
- return rc;
+ goto err_if_change;
}
rc = bnge_alloc_core(bn);
if (rc) {
netdev_err(bn->netdev, "bnge_alloc_core err: %d\n", rc);
- return rc;
+ goto err_if_change;
}
bnge_init_napi(bn);
@@ -2192,7 +2918,31 @@ static int bnge_open_core(struct bnge_net *bn)
netdev_err(bn->netdev, "bnge_init_nic err: %d\n", rc);
goto err_free_irq;
}
+
+ bnge_enable_napi(bn);
+
+ rc = bnge_update_phy_setting(bn);
+ if (rc) {
+ netdev_warn(bn->netdev, "failed to update PHY settings (rc: %d)\n",
+ rc);
+ WRITE_ONCE(bd->link_info.phy_retry_expires, jiffies + 5 * HZ);
+ WRITE_ONCE(bd->link_info.phy_retry, true);
+ } else {
+ WRITE_ONCE(bd->link_info.phy_retry, false);
+ }
+
set_bit(BNGE_STATE_OPEN, &bd->state);
+ set_bit(BNGE_STATE_STATS_ENABLE, &bn->state);
+
+ bnge_enable_int(bn);
+
+ bnge_tx_enable(bn);
+
+ mod_timer(&bn->timer, jiffies + bn->current_interval);
+
+ /* Poll link status and check for SFP+ module status */
+ bnge_get_port_module_status(bn);
+
return 0;
err_free_irq:
@@ -2200,16 +2950,11 @@ err_free_irq:
err_del_napi:
bnge_del_napi(bn);
bnge_free_core(bn);
+err_if_change:
+ bnge_hwrm_if_change(bd, false);
return rc;
}
-static netdev_tx_t bnge_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
- dev_kfree_skb_any(skb);
-
- return NETDEV_TX_OK;
-}
-
static int bnge_open(struct net_device *dev)
{
struct bnge_net *bn = netdev_priv(dev);
@@ -2224,17 +2969,206 @@ static int bnge_open(struct net_device *dev)
static int bnge_shutdown_nic(struct bnge_net *bn)
{
- /* TODO: close_path = 0 until we make NAPI functional */
- bnge_hwrm_resource_free(bn, 0);
+ bnge_hwrm_resource_free(bn, 1);
return 0;
}
+static void bnge_add_prev_ring_stats64(struct bnge_net *bn,
+ struct rtnl_link_stats64 *stats)
+{
+ struct netdev_queue_stats_rx *rx_save = &bn->rxq_prv_stats;
+ struct netdev_queue_stats_tx *tx_save = &bn->txq_prv_stats;
+ struct rtnl_link_stats64 *stats64_save = &bn->prv_stats64;
+
+ stats->rx_packets += rx_save->packets;
+ stats->tx_packets += tx_save->packets;
+ stats->rx_bytes += rx_save->bytes;
+ stats->tx_bytes += tx_save->bytes;
+ stats->rx_missed_errors += rx_save->hw_drop_overruns;
+ stats->tx_dropped += tx_save->hw_drop_errors;
+
+ stats->multicast += stats64_save->multicast;
+}
+
+static void bnge_get_ring_stats64(struct bnge_dev *bd,
+ struct rtnl_link_stats64 *stats)
+{
+ struct bnge_net *bn = netdev_priv(bd->netdev);
+ int i;
+
+ for (i = 0; i < bd->nq_nr_rings; i++) {
+ struct bnge_napi *bnapi = bn->bnapi[i];
+ u64 tx_bytes, tx_packets, tx_dropped;
+ u64 multicast, rx_missed_errors;
+ struct bnge_nq_ring_info *nqr;
+ u64 rx_bytes, rx_packets;
+ unsigned int start;
+ u64 *sw;
+
+ nqr = &bnapi->nq_ring;
+ sw = nqr->stats.sw_stats;
+
+ do {
+ start = u64_stats_fetch_begin(&nqr->stats.syncp);
+
+ rx_packets = BNGE_GET_RING_STATS64(sw, rx_ucast_pkts);
+ rx_packets += BNGE_GET_RING_STATS64(sw, rx_mcast_pkts);
+ rx_packets += BNGE_GET_RING_STATS64(sw, rx_bcast_pkts);
+
+ tx_packets = BNGE_GET_RING_STATS64(sw, tx_ucast_pkts);
+ tx_packets += BNGE_GET_RING_STATS64(sw, tx_mcast_pkts);
+ tx_packets += BNGE_GET_RING_STATS64(sw, tx_bcast_pkts);
+
+ rx_bytes = BNGE_GET_RING_STATS64(sw, rx_ucast_bytes);
+ rx_bytes += BNGE_GET_RING_STATS64(sw, rx_mcast_bytes);
+ rx_bytes += BNGE_GET_RING_STATS64(sw, rx_bcast_bytes);
+
+ tx_bytes = BNGE_GET_RING_STATS64(sw, tx_ucast_bytes);
+ tx_bytes += BNGE_GET_RING_STATS64(sw, tx_mcast_bytes);
+ tx_bytes += BNGE_GET_RING_STATS64(sw, tx_bcast_bytes);
+
+ multicast = BNGE_GET_RING_STATS64(sw, rx_mcast_pkts);
+ rx_missed_errors =
+ BNGE_GET_RING_STATS64(sw, rx_discard_pkts);
+ tx_dropped =
+ BNGE_GET_RING_STATS64(sw, tx_error_pkts);
+ } while (u64_stats_fetch_retry(&nqr->stats.syncp, start));
+
+ stats->rx_packets += rx_packets;
+ stats->tx_packets += tx_packets;
+ stats->rx_bytes += rx_bytes;
+ stats->tx_bytes += tx_bytes;
+ stats->multicast += multicast;
+ stats->rx_missed_errors += rx_missed_errors;
+ stats->tx_dropped += tx_dropped;
+ }
+}
+
+static void bnge_get_port_stats64(struct bnge_net *bn,
+ struct rtnl_link_stats64 *stats)
+{
+ unsigned int start;
+ u64 *tx, *rx;
+
+ rx = bn->port_stats.sw_stats;
+ tx = bn->port_stats.sw_stats + BNGE_TX_PORT_STATS_BYTE_OFFSET / 8;
+
+ do {
+ start = u64_stats_fetch_begin(&bn->port_stats.syncp);
+
+ stats->rx_crc_errors =
+ BNGE_GET_RX_PORT_STATS64(rx, rx_fcs_err_frames);
+ stats->rx_frame_errors =
+ BNGE_GET_RX_PORT_STATS64(rx, rx_align_err_frames);
+ stats->rx_length_errors =
+ BNGE_GET_RX_PORT_STATS64(rx, rx_undrsz_frames) +
+ BNGE_GET_RX_PORT_STATS64(rx, rx_ovrsz_frames) +
+ BNGE_GET_RX_PORT_STATS64(rx, rx_runt_frames);
+ stats->rx_errors =
+ BNGE_GET_RX_PORT_STATS64(rx, rx_false_carrier_frames) +
+ BNGE_GET_RX_PORT_STATS64(rx, rx_jbr_frames);
+ stats->collisions =
+ BNGE_GET_TX_PORT_STATS64(tx, tx_total_collisions);
+ stats->tx_fifo_errors =
+ BNGE_GET_TX_PORT_STATS64(tx, tx_fifo_underruns);
+ stats->tx_errors = BNGE_GET_TX_PORT_STATS64(tx, tx_err);
+ } while (u64_stats_fetch_retry(&bn->port_stats.syncp, start));
+}
+
+static void bnge_fill_prev_stats64(struct bnge_net *bn,
+ struct rtnl_link_stats64 *stats)
+{
+ struct netdev_queue_stats_rx *rx_save = &bn->rxq_prv_stats;
+ struct netdev_queue_stats_tx *tx_save = &bn->txq_prv_stats;
+ struct rtnl_link_stats64 *stats64_save = &bn->prv_stats64;
+
+ stats->rx_packets = rx_save->packets;
+ stats->tx_packets = tx_save->packets;
+ stats->rx_bytes = rx_save->bytes;
+ stats->tx_bytes = tx_save->bytes;
+ stats->rx_missed_errors = rx_save->hw_drop_overruns;
+ stats->tx_dropped = tx_save->hw_drop_errors;
+ stats->multicast = stats64_save->multicast;
+}
+
+static void bnge_get_stats64(struct net_device *dev,
+ struct rtnl_link_stats64 *stats)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+
+ if (bn->flags & BNGE_FLAG_PORT_STATS)
+ bnge_get_port_stats64(bn, stats);
+
+ spin_lock_bh(&bn->stats_lock);
+ if (!test_bit(BNGE_STATE_STATS_ENABLE, &bn->state)) {
+ bnge_fill_prev_stats64(bn, stats);
+ spin_unlock_bh(&bn->stats_lock);
+ return;
+ }
+
+ bnge_get_ring_stats64(bn->bd, stats);
+ bnge_add_prev_ring_stats64(bn, stats);
+ spin_unlock_bh(&bn->stats_lock);
+}
+
+static void bnge_save_ring_stats(struct bnge_net *bn)
+{
+ struct netdev_queue_stats_rx *rx_save = &bn->rxq_prv_stats;
+ struct netdev_queue_stats_tx *tx_save = &bn->txq_prv_stats;
+ struct rtnl_link_stats64 *stats64_save = &bn->prv_stats64;
+ int i;
+
+ for (i = 0; i < bn->bd->nq_nr_rings; i++) {
+ struct bnge_napi *bnapi = bn->bnapi[i];
+ struct bnge_nq_ring_info *nqr;
+ u64 *sw;
+
+ nqr = &bnapi->nq_ring;
+ sw = nqr->stats.sw_stats;
+
+ rx_save->packets += BNGE_GET_RING_STATS64(sw, rx_ucast_pkts);
+ rx_save->packets += BNGE_GET_RING_STATS64(sw, rx_mcast_pkts);
+ rx_save->packets += BNGE_GET_RING_STATS64(sw, rx_bcast_pkts);
+ rx_save->bytes += BNGE_GET_RING_STATS64(sw, rx_ucast_bytes);
+ rx_save->bytes += BNGE_GET_RING_STATS64(sw, rx_mcast_bytes);
+ rx_save->bytes += BNGE_GET_RING_STATS64(sw, rx_bcast_bytes);
+ rx_save->hw_drop_overruns += BNGE_GET_RING_STATS64(sw, rx_discard_pkts);
+ rx_save->hw_drops += BNGE_GET_RING_STATS64(sw, rx_error_pkts) +
+ BNGE_GET_RING_STATS64(sw, rx_discard_pkts);
+
+ tx_save->packets += BNGE_GET_RING_STATS64(sw, tx_ucast_pkts);
+ tx_save->packets += BNGE_GET_RING_STATS64(sw, tx_mcast_pkts);
+ tx_save->packets += BNGE_GET_RING_STATS64(sw, tx_bcast_pkts);
+ tx_save->bytes += BNGE_GET_RING_STATS64(sw, tx_ucast_bytes);
+ tx_save->bytes += BNGE_GET_RING_STATS64(sw, tx_mcast_bytes);
+ tx_save->bytes += BNGE_GET_RING_STATS64(sw, tx_bcast_bytes);
+ tx_save->hw_drop_errors += BNGE_GET_RING_STATS64(sw, tx_error_pkts);
+ tx_save->hw_drops += BNGE_GET_RING_STATS64(sw, tx_discard_pkts) +
+ BNGE_GET_RING_STATS64(sw, tx_error_pkts);
+
+ stats64_save->multicast +=
+ BNGE_GET_RING_STATS64(sw, rx_mcast_pkts);
+ }
+}
+
static void bnge_close_core(struct bnge_net *bn)
{
struct bnge_dev *bd = bn->bd;
+ bnge_tx_disable(bn);
+
clear_bit(BNGE_STATE_OPEN, &bd->state);
+
+ timer_delete_sync(&bn->timer);
bnge_shutdown_nic(bn);
+ bnge_disable_napi(bn);
+
+ /* Save ring stats before shutdown */
+ spin_lock_bh(&bn->stats_lock);
+ bnge_save_ring_stats(bn);
+ clear_bit(BNGE_STATE_STATS_ENABLE, &bn->state);
+ spin_unlock_bh(&bn->stats_lock);
+
bnge_free_all_rings_bufs(bn);
bnge_free_irq(bn);
bnge_del_napi(bn);
@@ -2247,14 +3181,99 @@ static int bnge_close(struct net_device *dev)
struct bnge_net *bn = netdev_priv(dev);
bnge_close_core(bn);
+ bnge_hwrm_shutdown_link(bn->bd);
+ bnge_hwrm_if_change(bn->bd, false);
+ bn->sp_event = 0;
return 0;
}
+static void bnge_get_queue_stats_rx(struct net_device *dev, int i,
+ struct netdev_queue_stats_rx *stats)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ struct bnge_nq_ring_info *nqr;
+ u64 *sw;
+
+ if (!bn->bnapi)
+ return;
+
+ nqr = &bn->bnapi[i]->nq_ring;
+ sw = nqr->stats.sw_stats;
+
+ stats->packets = 0;
+ stats->packets += BNGE_GET_RING_STATS64(sw, rx_ucast_pkts);
+ stats->packets += BNGE_GET_RING_STATS64(sw, rx_mcast_pkts);
+ stats->packets += BNGE_GET_RING_STATS64(sw, rx_bcast_pkts);
+
+ stats->bytes = 0;
+ stats->bytes += BNGE_GET_RING_STATS64(sw, rx_ucast_bytes);
+ stats->bytes += BNGE_GET_RING_STATS64(sw, rx_mcast_bytes);
+ stats->bytes += BNGE_GET_RING_STATS64(sw, rx_bcast_bytes);
+
+ stats->hw_drop_overruns = BNGE_GET_RING_STATS64(sw, rx_discard_pkts);
+ stats->hw_drops = BNGE_GET_RING_STATS64(sw, rx_error_pkts) +
+ stats->hw_drop_overruns;
+}
+
+static void bnge_get_queue_stats_tx(struct net_device *dev, int i,
+ struct netdev_queue_stats_tx *stats)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+ struct bnge_napi *bnapi;
+ u64 *sw;
+
+ if (!bn->tx_ring)
+ return;
+
+ bnapi = bn->tx_ring[bn->tx_ring_map[i]].bnapi;
+ sw = bnapi->nq_ring.stats.sw_stats;
+
+ stats->packets = 0;
+ stats->packets += BNGE_GET_RING_STATS64(sw, tx_ucast_pkts);
+ stats->packets += BNGE_GET_RING_STATS64(sw, tx_mcast_pkts);
+ stats->packets += BNGE_GET_RING_STATS64(sw, tx_bcast_pkts);
+
+ stats->bytes = 0;
+ stats->bytes += BNGE_GET_RING_STATS64(sw, tx_ucast_bytes);
+ stats->bytes += BNGE_GET_RING_STATS64(sw, tx_mcast_bytes);
+ stats->bytes += BNGE_GET_RING_STATS64(sw, tx_bcast_bytes);
+
+ stats->hw_drop_errors = BNGE_GET_RING_STATS64(sw, tx_error_pkts);
+ stats->hw_drops = BNGE_GET_RING_STATS64(sw, tx_discard_pkts) +
+ stats->hw_drop_errors;
+}
+
+static void bnge_get_base_stats(struct net_device *dev,
+ struct netdev_queue_stats_rx *rx,
+ struct netdev_queue_stats_tx *tx)
+{
+ struct bnge_net *bn = netdev_priv(dev);
+
+ rx->packets = bn->rxq_prv_stats.packets;
+ rx->bytes = bn->rxq_prv_stats.bytes;
+ rx->hw_drops = bn->rxq_prv_stats.hw_drops;
+ rx->hw_drop_overruns = bn->rxq_prv_stats.hw_drop_overruns;
+
+ tx->packets = bn->txq_prv_stats.packets;
+ tx->bytes = bn->txq_prv_stats.bytes;
+ tx->hw_drops = bn->txq_prv_stats.hw_drops;
+ tx->hw_drop_errors = bn->txq_prv_stats.hw_drop_errors;
+}
+
+static const struct netdev_stat_ops bnge_stat_ops = {
+ .get_queue_stats_rx = bnge_get_queue_stats_rx,
+ .get_queue_stats_tx = bnge_get_queue_stats_tx,
+ .get_base_stats = bnge_get_base_stats,
+};
+
static const struct net_device_ops bnge_netdev_ops = {
.ndo_open = bnge_open,
.ndo_stop = bnge_close,
.ndo_start_xmit = bnge_start_xmit,
+ .ndo_get_stats64 = bnge_get_stats64,
+ .ndo_set_rx_mode_async = bnge_set_rx_mode,
+ .ndo_features_check = bnge_features_check,
};
static void bnge_init_mac_addr(struct bnge_dev *bd)
@@ -2295,7 +3314,6 @@ void bnge_set_ring_params(struct bnge_dev *bd)
rx_space = rx_size + ALIGN(NET_SKB_PAD, 8) +
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
- bn->rx_copy_thresh = BNGE_RX_COPY_THRESH;
ring_size = bn->rx_ring_size;
bn->rx_agg_ring_size = 0;
bn->rx_agg_nr_pages = 0;
@@ -2334,7 +3352,10 @@ void bnge_set_ring_params(struct bnge_dev *bd)
bn->rx_agg_ring_size = agg_ring_size;
bn->rx_agg_ring_mask = (bn->rx_agg_nr_pages * RX_DESC_CNT) - 1;
- rx_size = SKB_DATA_ALIGN(BNGE_RX_COPY_THRESH + NET_IP_ALIGN);
+ rx_size = max3(BNGE_DEFAULT_RX_COPYBREAK,
+ bn->rx_copybreak,
+ bn->netdev->cfg_pending->hds_thresh);
+ rx_size = SKB_DATA_ALIGN(rx_size + NET_IP_ALIGN);
rx_space = rx_size + NET_SKB_PAD +
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
}
@@ -2367,6 +3388,17 @@ void bnge_set_ring_params(struct bnge_dev *bd)
bn->cp_ring_mask = bn->cp_bit - 1;
}
+static void bnge_init_ring_params(struct bnge_net *bn)
+{
+ u32 rx_size;
+
+ bn->rx_copybreak = BNGE_DEFAULT_RX_COPYBREAK;
+ /* Try to fit 4 chunks into a 4k page */
+ rx_size = SZ_1K -
+ NET_SKB_PAD - SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
+ bn->netdev->cfg->hds_thresh = max(BNGE_DEFAULT_RX_COPYBREAK, rx_size);
+}
+
int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs)
{
struct net_device *netdev;
@@ -2382,6 +3414,7 @@ int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs)
bd->netdev = netdev;
netdev->netdev_ops = &bnge_netdev_ops;
+ netdev->stat_ops = &bnge_stat_ops;
bnge_set_ethtool_ops(netdev);
@@ -2451,25 +3484,51 @@ int bnge_netdev_alloc(struct bnge_dev *bd, int max_irqs)
if (bd->tso_max_segs)
netif_set_tso_max_segs(netdev, bd->tso_max_segs);
+ INIT_WORK(&bn->sp_task, bnge_sp_task);
+ timer_setup(&bn->timer, bnge_timer, 0);
+ bn->current_interval = BNGE_TIMER_INTERVAL;
+
+ bn->bnge_pf_wq = alloc_ordered_workqueue("bnge_pf_wq-%s", 0,
+ dev_name(bd->dev));
+ if (!bn->bnge_pf_wq) {
+ netdev_err(netdev, "Unable to create workqueue.\n");
+ rc = -ENOMEM;
+ goto err_netdev;
+ }
+
bn->rx_ring_size = BNGE_DEFAULT_RX_RING_SIZE;
bn->tx_ring_size = BNGE_DEFAULT_TX_RING_SIZE;
bn->rx_dir = DMA_FROM_DEVICE;
bnge_set_tpa_flags(bd);
+ bnge_init_ring_params(bn);
bnge_set_ring_params(bd);
bnge_init_l2_fltr_tbl(bn);
bnge_init_mac_addr(bd);
+ rc = bnge_probe_phy(bn, true);
+ if (rc)
+ goto err_free_workq;
+
+ rc = bnge_alloc_port_stats(bn);
+ if (rc)
+ goto err_free_workq;
+ spin_lock_init(&bn->stats_lock);
+
netdev->request_ops_lock = true;
rc = register_netdev(netdev);
if (rc) {
dev_err(bd->dev, "Register netdev failed rc: %d\n", rc);
- goto err_netdev;
+ goto err_free_port_stats;
}
return 0;
+err_free_port_stats:
+ bnge_free_port_stats(bn);
+err_free_workq:
+ destroy_workqueue(bn->bnge_pf_wq);
err_netdev:
free_netdev(netdev);
return rc;
@@ -2478,8 +3537,19 @@ err_netdev:
void bnge_netdev_free(struct bnge_dev *bd)
{
struct net_device *netdev = bd->netdev;
+ struct bnge_net *bn;
+
+ bn = netdev_priv(netdev);
unregister_netdev(netdev);
+
+ timer_shutdown_sync(&bn->timer);
+ cancel_work_sync(&bn->sp_task);
+ bn->sp_event = 0;
+ destroy_workqueue(bn->bnge_pf_wq);
+
+ bnge_free_port_stats(bn);
+
free_netdev(netdev);
bd->netdev = NULL;
}