/* SPDX-License-Identifier: BSD-3-Clause
- * Copyright(c) 2014-2018 Broadcom
+ * Copyright(c) 2014-2021 Broadcom
* All rights reserved.
*/
#include "bnxt.h"
#include "bnxt_cpr.h"
+#include "bnxt_filter.h"
#include "bnxt_hwrm.h"
#include "bnxt_rxq.h"
#include "bnxt_stats.h"
#include "bnxt_txq.h"
+#include "bnxt_vnic.h"
#include "hsi_struct_def_dpdk.h"
static const struct bnxt_xstats_name_off bnxt_rx_stats_strings[] = {
}
}
+static void bnxt_fill_rte_eth_stats(struct rte_eth_stats *stats,
+ struct bnxt_ring_stats *ring_stats,
+ unsigned int i, bool rx)
+{
+ if (rx) {
+ stats->q_ipackets[i] = ring_stats->rx_ucast_pkts;
+ stats->q_ipackets[i] += ring_stats->rx_mcast_pkts;
+ stats->q_ipackets[i] += ring_stats->rx_bcast_pkts;
+
+ stats->ipackets += stats->q_ipackets[i];
+
+ stats->q_ibytes[i] = ring_stats->rx_ucast_bytes;
+ stats->q_ibytes[i] += ring_stats->rx_mcast_bytes;
+ stats->q_ibytes[i] += ring_stats->rx_bcast_bytes;
+
+ stats->ibytes += stats->q_ibytes[i];
+
+ stats->q_errors[i] = ring_stats->rx_discard_pkts;
+ stats->q_errors[i] += ring_stats->rx_error_pkts;
+
+ stats->imissed += ring_stats->rx_discard_pkts;
+ stats->ierrors += ring_stats->rx_error_pkts;
+ } else {
+ stats->q_opackets[i] = ring_stats->tx_ucast_pkts;
+ stats->q_opackets[i] += ring_stats->tx_mcast_pkts;
+ stats->q_opackets[i] += ring_stats->tx_bcast_pkts;
+
+ stats->opackets += stats->q_opackets[i];
+
+ stats->q_obytes[i] = ring_stats->tx_ucast_bytes;
+ stats->q_obytes[i] += ring_stats->tx_mcast_bytes;
+ stats->q_obytes[i] += ring_stats->tx_bcast_bytes;
+
+ stats->obytes += stats->q_obytes[i];
+
+ stats->oerrors += ring_stats->tx_discard_pkts;
+ }
+}
+
int bnxt_stats_get_op(struct rte_eth_dev *eth_dev,
- struct rte_eth_stats *bnxt_stats)
+ struct rte_eth_stats *bnxt_stats)
{
int rc = 0;
unsigned int i;
for (i = 0; i < num_q_stats; i++) {
struct bnxt_rx_queue *rxq = bp->rx_queues[i];
struct bnxt_cp_ring_info *cpr = rxq->cp_ring;
+ struct bnxt_ring_stats ring_stats = {0};
+
+ if (!rxq->rx_started)
+ continue;
- rc = bnxt_hwrm_ctx_qstats(bp, cpr->hw_stats_ctx_id, i,
- bnxt_stats, 1);
+ rc = bnxt_hwrm_ring_stats(bp, cpr->hw_stats_ctx_id, i,
+ &ring_stats, true);
if (unlikely(rc))
return rc;
+
+ bnxt_fill_rte_eth_stats(bnxt_stats, &ring_stats, i, true);
bnxt_stats->rx_nombuf +=
rte_atomic64_read(&rxq->rx_mbuf_alloc_fail);
}
for (i = 0; i < num_q_stats; i++) {
struct bnxt_tx_queue *txq = bp->tx_queues[i];
struct bnxt_cp_ring_info *cpr = txq->cp_ring;
+ struct bnxt_ring_stats ring_stats = {0};
+
+ if (!txq->tx_started)
+ continue;
- rc = bnxt_hwrm_ctx_qstats(bp, cpr->hw_stats_ctx_id, i,
- bnxt_stats, 0);
+ rc = bnxt_hwrm_ring_stats(bp, cpr->hw_stats_ctx_id, i,
+ &ring_stats, false);
if (unlikely(rc))
return rc;
+
+ bnxt_fill_rte_eth_stats(bnxt_stats, &ring_stats, i, false);
}
- rc = bnxt_hwrm_func_qstats(bp, 0xffff, bnxt_stats, NULL);
return rc;
}
+static void bnxt_clear_prev_stat(struct bnxt *bp)
+{
+ /*
+ * Clear the cached values of stats returned by HW in the previous
+ * get operation.
+ */
+ memset(bp->prev_rx_ring_stats, 0, sizeof(struct bnxt_ring_stats) * bp->rx_cp_nr_rings);
+ memset(bp->prev_tx_ring_stats, 0, sizeof(struct bnxt_ring_stats) * bp->tx_cp_nr_rings);
+}
+
int bnxt_stats_reset_op(struct rte_eth_dev *eth_dev)
{
struct bnxt *bp = eth_dev->data->dev_private;
rte_atomic64_clear(&rxq->rx_mbuf_alloc_fail);
}
+ bnxt_clear_prev_stat(bp);
+
return ret;
}
+static void bnxt_fill_func_qstats(struct hwrm_func_qstats_output *func_qstats,
+ struct bnxt_ring_stats *ring_stats,
+ bool rx)
+{
+ if (rx) {
+ func_qstats->rx_ucast_pkts += ring_stats->rx_ucast_pkts;
+ func_qstats->rx_mcast_pkts += ring_stats->rx_mcast_pkts;
+ func_qstats->rx_bcast_pkts += ring_stats->rx_bcast_pkts;
+
+ func_qstats->rx_ucast_bytes += ring_stats->rx_ucast_bytes;
+ func_qstats->rx_mcast_bytes += ring_stats->rx_mcast_bytes;
+ func_qstats->rx_bcast_bytes += ring_stats->rx_bcast_bytes;
+
+ func_qstats->rx_discard_pkts += ring_stats->rx_discard_pkts;
+ func_qstats->rx_drop_pkts += ring_stats->rx_error_pkts;
+
+ func_qstats->rx_agg_pkts += ring_stats->rx_agg_pkts;
+ func_qstats->rx_agg_bytes += ring_stats->rx_agg_bytes;
+ func_qstats->rx_agg_events += ring_stats->rx_agg_events;
+ func_qstats->rx_agg_aborts += ring_stats->rx_agg_aborts;
+ } else {
+ func_qstats->tx_ucast_pkts += ring_stats->tx_ucast_pkts;
+ func_qstats->tx_mcast_pkts += ring_stats->tx_mcast_pkts;
+ func_qstats->tx_bcast_pkts += ring_stats->tx_bcast_pkts;
+
+ func_qstats->tx_ucast_bytes += ring_stats->tx_ucast_bytes;
+ func_qstats->tx_mcast_bytes += ring_stats->tx_mcast_bytes;
+ func_qstats->tx_bcast_bytes += ring_stats->tx_bcast_bytes;
+
+ func_qstats->tx_drop_pkts += ring_stats->tx_error_pkts;
+ func_qstats->tx_discard_pkts += ring_stats->tx_discard_pkts;
+ }
+}
+
int bnxt_dev_xstats_get_op(struct rte_eth_dev *eth_dev,
struct rte_eth_xstat *xstats, unsigned int n)
{
if (rc)
return rc;
- if (xstats == NULL)
- return 0;
+ stat_count = RTE_DIM(bnxt_rx_stats_strings) +
+ RTE_DIM(bnxt_tx_stats_strings) +
+ RTE_DIM(bnxt_func_stats_strings) +
+ RTE_DIM(bnxt_rx_ext_stats_strings) +
+ RTE_DIM(bnxt_tx_ext_stats_strings) +
+ bnxt_flow_stats_cnt(bp);
- memset(xstats, 0, sizeof(*xstats));
+ if (n < stat_count || xstats == NULL)
+ return stat_count;
+
+ for (i = 0; i < bp->rx_cp_nr_rings; i++) {
+ struct bnxt_rx_queue *rxq = bp->rx_queues[i];
+ struct bnxt_cp_ring_info *cpr = rxq->cp_ring;
+ struct bnxt_ring_stats ring_stats = {0};
+
+ if (!rxq->rx_started)
+ continue;
+
+ rc = bnxt_hwrm_ring_stats(bp, cpr->hw_stats_ctx_id, i,
+ &ring_stats, true);
+ if (unlikely(rc))
+ return rc;
+
+ bnxt_fill_func_qstats(&func_qstats, &ring_stats, true);
+ }
+
+ for (i = 0; i < bp->tx_cp_nr_rings; i++) {
+ struct bnxt_tx_queue *txq = bp->tx_queues[i];
+ struct bnxt_cp_ring_info *cpr = txq->cp_ring;
+ struct bnxt_ring_stats ring_stats = {0};
+
+ if (!txq->tx_started)
+ continue;
+
+ rc = bnxt_hwrm_ring_stats(bp, cpr->hw_stats_ctx_id, i,
+ &ring_stats, false);
+ if (unlikely(rc))
+ return rc;
+
+ bnxt_fill_func_qstats(&func_qstats, &ring_stats, false);
+ }
- bnxt_hwrm_func_qstats(bp, 0xffff, NULL, &func_qstats);
bnxt_hwrm_port_qstats(bp);
bnxt_hwrm_ext_port_qstats(bp);
rx_port_stats_ext_cnt = RTE_MIN(RTE_DIM(bnxt_rx_ext_stats_strings),
(bp->fw_tx_port_stats_ext_size /
stat_size));
- count = RTE_DIM(bnxt_rx_stats_strings) +
- RTE_DIM(bnxt_tx_stats_strings) +
- RTE_DIM(bnxt_func_stats_strings) +
- RTE_DIM(bnxt_rx_ext_stats_strings) +
- RTE_DIM(bnxt_tx_ext_stats_strings);
- stat_count = count;
-
- if (n < count)
- return count;
+ memset(xstats, 0, sizeof(*xstats) * n);
count = 0;
for (i = 0; i < RTE_DIM(bnxt_rx_stats_strings); i++) {
for (i = 0; i < RTE_DIM(bnxt_func_stats_strings); i++) {
xstats[count].id = count;
- xstats[count].value =
- rte_le_to_cpu_64(((uint64_t *)&func_qstats)[i]);
+ xstats[count].value = *(uint64_t *)((char *)&func_qstats +
+ bnxt_func_stats_strings[i].offset);
count++;
}
-
for (i = 0; i < rx_port_stats_ext_cnt; i++) {
uint64_t *rx_stats_ext = (uint64_t *)bp->hw_rx_port_stats_ext;
xstats[count].value = rte_le_to_cpu_64
(*(uint64_t *)((char *)tx_stats_ext +
bnxt_tx_ext_stats_strings[i].offset));
-
count++;
}
+ if (bp->fw_cap & BNXT_FW_CAP_ADV_FLOW_COUNTERS &&
+ bp->fw_cap & BNXT_FW_CAP_ADV_FLOW_MGMT &&
+ BNXT_FLOW_XSTATS_EN(bp)) {
+ int j;
+
+ i = 0;
+ for (j = 0; j < bp->max_vnics; j++) {
+ struct bnxt_filter_info *filter;
+ struct bnxt_vnic_info *vnic;
+ struct rte_flow *flow;
+
+ vnic = &bp->vnic_info[j];
+ if (vnic && vnic->fw_vnic_id == INVALID_VNIC_ID)
+ continue;
+
+ if (STAILQ_EMPTY(&vnic->flow_list))
+ continue;
+
+ STAILQ_FOREACH(flow, &vnic->flow_list, next) {
+ if (!flow || !flow->filter)
+ continue;
+
+ filter = flow->filter;
+ xstats[count].id = count;
+ xstats[count].value =
+ filter->hw_stats.bytes;
+ count++;
+ xstats[count].id = count;
+ xstats[count].value =
+ filter->hw_stats.packets;
+ count++;
+ if (++i > bp->max_l2_ctx)
+ break;
+ }
+ if (i > bp->max_l2_ctx)
+ break;
+ }
+ }
+
return stat_count;
}
+int bnxt_flow_stats_cnt(struct bnxt *bp)
+{
+ if (bp->fw_cap & BNXT_FW_CAP_ADV_FLOW_COUNTERS &&
+ bp->fw_cap & BNXT_FW_CAP_ADV_FLOW_MGMT &&
+ BNXT_FLOW_XSTATS_EN(bp)) {
+ struct bnxt_xstats_name_off flow_bytes[bp->max_l2_ctx];
+ struct bnxt_xstats_name_off flow_pkts[bp->max_l2_ctx];
+
+ return RTE_DIM(flow_bytes) + RTE_DIM(flow_pkts);
+ }
+
+ return 0;
+}
+
int bnxt_dev_xstats_get_names_op(struct rte_eth_dev *eth_dev,
- struct rte_eth_xstat_name *xstats_names,
- __rte_unused unsigned int limit)
+ struct rte_eth_xstat_name *xstats_names,
+ unsigned int size)
{
+ struct bnxt *bp = (struct bnxt *)eth_dev->data->dev_private;
const unsigned int stat_cnt = RTE_DIM(bnxt_rx_stats_strings) +
RTE_DIM(bnxt_tx_stats_strings) +
RTE_DIM(bnxt_func_stats_strings) +
RTE_DIM(bnxt_rx_ext_stats_strings) +
- RTE_DIM(bnxt_tx_ext_stats_strings);
- struct bnxt *bp = (struct bnxt *)eth_dev->data->dev_private;
- unsigned int i, count;
+ RTE_DIM(bnxt_tx_ext_stats_strings) +
+ bnxt_flow_stats_cnt(bp);
+ unsigned int i, count = 0;
int rc;
rc = is_bnxt_in_error(bp);
if (rc)
return rc;
- if (xstats_names != NULL) {
- count = 0;
+ if (xstats_names == NULL || size < stat_cnt)
+ return stat_cnt;
- for (i = 0; i < RTE_DIM(bnxt_rx_stats_strings); i++) {
- strlcpy(xstats_names[count].name,
- bnxt_rx_stats_strings[i].name,
- sizeof(xstats_names[count].name));
- count++;
- }
+ for (i = 0; i < RTE_DIM(bnxt_rx_stats_strings); i++) {
+ strlcpy(xstats_names[count].name,
+ bnxt_rx_stats_strings[i].name,
+ sizeof(xstats_names[count].name));
+ count++;
+ }
- for (i = 0; i < RTE_DIM(bnxt_tx_stats_strings); i++) {
- strlcpy(xstats_names[count].name,
- bnxt_tx_stats_strings[i].name,
- sizeof(xstats_names[count].name));
- count++;
- }
+ for (i = 0; i < RTE_DIM(bnxt_tx_stats_strings); i++) {
+ strlcpy(xstats_names[count].name,
+ bnxt_tx_stats_strings[i].name,
+ sizeof(xstats_names[count].name));
+ count++;
+ }
- for (i = 0; i < RTE_DIM(bnxt_func_stats_strings); i++) {
- strlcpy(xstats_names[count].name,
- bnxt_func_stats_strings[i].name,
- sizeof(xstats_names[count].name));
- count++;
- }
+ for (i = 0; i < RTE_DIM(bnxt_func_stats_strings); i++) {
+ strlcpy(xstats_names[count].name,
+ bnxt_func_stats_strings[i].name,
+ sizeof(xstats_names[count].name));
+ count++;
+ }
- for (i = 0; i < RTE_DIM(bnxt_rx_ext_stats_strings); i++) {
- strlcpy(xstats_names[count].name,
- bnxt_rx_ext_stats_strings[i].name,
- sizeof(xstats_names[count].name));
+ for (i = 0; i < RTE_DIM(bnxt_rx_ext_stats_strings); i++) {
+ strlcpy(xstats_names[count].name,
+ bnxt_rx_ext_stats_strings[i].name,
+ sizeof(xstats_names[count].name));
+ count++;
+ }
+
+ for (i = 0; i < RTE_DIM(bnxt_tx_ext_stats_strings); i++) {
+ strlcpy(xstats_names[count].name,
+ bnxt_tx_ext_stats_strings[i].name,
+ sizeof(xstats_names[count].name));
+
+ count++;
+ }
+
+ if (bp->fw_cap & BNXT_FW_CAP_ADV_FLOW_COUNTERS &&
+ bp->fw_cap & BNXT_FW_CAP_ADV_FLOW_MGMT &&
+ BNXT_FLOW_XSTATS_EN(bp)) {
+ for (i = 0; i < bp->max_l2_ctx; i++) {
+ char buf[RTE_ETH_XSTATS_NAME_SIZE];
+
+ sprintf(buf, "flow_%d_bytes", i);
+ strlcpy(xstats_names[count].name, buf,
+ sizeof(xstats_names[count].name));
count++;
- }
- for (i = 0; i < RTE_DIM(bnxt_tx_ext_stats_strings); i++) {
- strlcpy(xstats_names[count].name,
- bnxt_tx_ext_stats_strings[i].name,
+ sprintf(buf, "flow_%d_packets", i);
+ strlcpy(xstats_names[count].name, buf,
sizeof(xstats_names[count].name));
count++;
}
-
}
+
return stat_cnt;
}
PMD_DRV_LOG(ERR, "Failed to reset xstats: %s\n",
strerror(-ret));
+ bnxt_clear_prev_stat(bp);
+
return ret;
}
-int bnxt_dev_xstats_get_by_id_op(struct rte_eth_dev *dev, const uint64_t *ids,
- uint64_t *values, unsigned int limit)
+/* Update the input context memory with the flow counter IDs
+ * of the flows that we are interested in.
+ * Also, update the output tables with the current local values
+ * since that is what will be used by FW to accumulate
+ */
+static void bnxt_update_fc_pre_qstat(uint32_t *in_tbl,
+ uint64_t *out_tbl,
+ struct bnxt_filter_info *filter,
+ uint32_t *ptbl_cnt)
{
- const unsigned int stat_cnt = RTE_DIM(bnxt_rx_stats_strings) +
- RTE_DIM(bnxt_tx_stats_strings) +
- RTE_DIM(bnxt_func_stats_strings) +
- RTE_DIM(bnxt_rx_ext_stats_strings) +
- RTE_DIM(bnxt_tx_ext_stats_strings);
- struct bnxt *bp = dev->data->dev_private;
- struct rte_eth_xstat xstats[stat_cnt];
- uint64_t values_copy[stat_cnt];
- uint16_t i;
- int rc;
-
- rc = is_bnxt_in_error(bp);
- if (rc)
- return rc;
+ uint32_t in_tbl_cnt = *ptbl_cnt;
- if (!ids)
- return bnxt_dev_xstats_get_op(dev, xstats, stat_cnt);
+ in_tbl[in_tbl_cnt] = filter->flow_id;
+ out_tbl[2 * in_tbl_cnt] = filter->hw_stats.packets;
+ out_tbl[2 * in_tbl_cnt + 1] = filter->hw_stats.bytes;
+ in_tbl_cnt++;
+ *ptbl_cnt = in_tbl_cnt;
+}
- bnxt_dev_xstats_get_by_id_op(dev, NULL, values_copy, stat_cnt);
- for (i = 0; i < limit; i++) {
- if (ids[i] >= stat_cnt) {
- PMD_DRV_LOG(ERR, "id value isn't valid");
- return -EINVAL;
- }
- values[i] = values_copy[ids[i]];
- }
- return stat_cnt;
+/* Post issuing counter_qstats cmd, update the driver's local stat
+ * entries with the values DMA-ed by FW in the output table
+ */
+static void bnxt_update_fc_post_qstat(struct bnxt_filter_info *filter,
+ uint64_t *out_tbl,
+ uint32_t out_tbl_idx)
+{
+ filter->hw_stats.packets = out_tbl[2 * out_tbl_idx];
+ filter->hw_stats.bytes = out_tbl[(2 * out_tbl_idx) + 1];
}
-int bnxt_dev_xstats_get_names_by_id_op(struct rte_eth_dev *dev,
- struct rte_eth_xstat_name *xstats_names,
- const uint64_t *ids, unsigned int limit)
+static int bnxt_update_fc_tbl(struct bnxt *bp, uint16_t ctr,
+ struct bnxt_filter_info *en_tbl[],
+ uint16_t in_flow_cnt)
{
- const unsigned int stat_cnt = RTE_DIM(bnxt_rx_stats_strings) +
- RTE_DIM(bnxt_tx_stats_strings) +
- RTE_DIM(bnxt_func_stats_strings) +
- RTE_DIM(bnxt_rx_ext_stats_strings) +
- RTE_DIM(bnxt_tx_ext_stats_strings);
- struct rte_eth_xstat_name xstats_names_copy[stat_cnt];
- struct bnxt *bp = dev->data->dev_private;
- uint16_t i;
- int rc;
+ uint32_t *in_rx_tbl;
+ uint64_t *out_rx_tbl;
+ uint32_t in_rx_tbl_cnt = 0;
+ uint32_t out_rx_tbl_cnt = 0;
+ int i, rc = 0;
+
+ in_rx_tbl = (uint32_t *)bp->flow_stat->rx_fc_in_tbl.va;
+ out_rx_tbl = (uint64_t *)bp->flow_stat->rx_fc_out_tbl.va;
+
+ for (i = 0; i < in_flow_cnt; i++) {
+ if (!en_tbl[i])
+ continue;
+
+ /* Currently only ingress/Rx flows are supported anyway. */
+ bnxt_update_fc_pre_qstat(in_rx_tbl, out_rx_tbl,
+ en_tbl[i], &in_rx_tbl_cnt);
+ }
- rc = is_bnxt_in_error(bp);
- if (rc)
- return rc;
+ /* Currently only ingress/Rx flows are supported */
+ if (in_rx_tbl_cnt) {
+ rc = bnxt_hwrm_cfa_counter_qstats(bp, BNXT_DIR_RX, ctr,
+ in_rx_tbl_cnt);
+ if (rc)
+ return rc;
+ }
- if (!ids)
- return bnxt_dev_xstats_get_names_op(dev, xstats_names,
- stat_cnt);
- bnxt_dev_xstats_get_names_by_id_op(dev, xstats_names_copy, NULL,
- stat_cnt);
+ for (i = 0; i < in_flow_cnt; i++) {
+ if (!en_tbl[i])
+ continue;
- for (i = 0; i < limit; i++) {
- if (ids[i] >= stat_cnt) {
- PMD_DRV_LOG(ERR, "id value isn't valid");
- return -EINVAL;
+ /* Currently only ingress/Rx flows are supported */
+ bnxt_update_fc_post_qstat(en_tbl[i], out_rx_tbl,
+ out_rx_tbl_cnt);
+ out_rx_tbl_cnt++;
+ }
+
+ return rc;
+}
+
+/* Walks through the list which has all the flows
+ * requesting for explicit flow counters.
+ */
+int bnxt_flow_stats_req(struct bnxt *bp)
+{
+ int i;
+ int rc = 0;
+ struct rte_flow *flow;
+ uint16_t in_flow_tbl_cnt = 0;
+ struct bnxt_vnic_info *vnic = NULL;
+ struct bnxt_filter_info *valid_en_tbl[bp->flow_stat->max_fc];
+ uint16_t counter_type = CFA_COUNTER_CFG_IN_COUNTER_TYPE_FC;
+
+ bnxt_acquire_flow_lock(bp);
+ for (i = 0; i < bp->max_vnics; i++) {
+ vnic = &bp->vnic_info[i];
+ if (vnic && vnic->fw_vnic_id == INVALID_VNIC_ID)
+ continue;
+
+ if (STAILQ_EMPTY(&vnic->flow_list))
+ continue;
+
+ STAILQ_FOREACH(flow, &vnic->flow_list, next) {
+ if (!flow || !flow->filter)
+ continue;
+
+ valid_en_tbl[in_flow_tbl_cnt++] = flow->filter;
+ if (in_flow_tbl_cnt >= bp->flow_stat->max_fc) {
+ rc = bnxt_update_fc_tbl(bp, counter_type,
+ valid_en_tbl,
+ in_flow_tbl_cnt);
+ if (rc)
+ goto err;
+ in_flow_tbl_cnt = 0;
+ continue;
+ }
}
- strcpy(xstats_names[i].name,
- xstats_names_copy[ids[i]].name);
}
- return stat_cnt;
+
+ if (!in_flow_tbl_cnt) {
+ bnxt_release_flow_lock(bp);
+ goto out;
+ }
+
+ rc = bnxt_update_fc_tbl(bp, counter_type, valid_en_tbl,
+ in_flow_tbl_cnt);
+ if (!rc) {
+ bnxt_release_flow_lock(bp);
+ return 0;
+ }
+
+err:
+ /* If cmd fails once, no need of
+ * invoking again every second
+ */
+ bnxt_release_flow_lock(bp);
+ bnxt_cancel_fc_thread(bp);
+out:
+ return rc;
}