4daf12f442
Presently, HW is programmed only to receive packets from LPB pool. Making all packets received from LPB pool. But, CNXK HW supports two pools, - SPB -> packets with smaller size (less than 4K) - LPB -> packets with bigger size (greater than 4K) Patch enables multiple mempool capability, pool is selected based on the packet's length. So, basically, PMD programs HW for receiving packets from both SPB and LPB pools based on the packet's length. This is achieved by enabling rx multiple mempool offload, RTE_ETH_RX_OFFLOAD_MUL_MEMPOOL. This allows the application to send more than one pool(in our case two) to the driver, with different segment(packet) lengths, which helps the driver to configure both pools based on segment lengths. This is often useful for saving the memory where the application can create a different pool to steer the specific size of the packet, thus enabling effective use of memory. Signed-off-by: Hanumanth Pothula <hpothula@marvell.com>
1240 lines
31 KiB
C
1240 lines
31 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(C) 2021 Marvell.
|
|
*/
|
|
|
|
#include <cnxk_ethdev.h>
|
|
|
|
int
|
|
cnxk_nix_info_get(struct rte_eth_dev *eth_dev, struct rte_eth_dev_info *devinfo)
|
|
{
|
|
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(eth_dev);
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
int max_rx_pktlen;
|
|
|
|
max_rx_pktlen = (roc_nix_max_pkt_len(&dev->nix) + RTE_ETHER_CRC_LEN -
|
|
CNXK_NIX_MAX_VTAG_ACT_SIZE);
|
|
|
|
devinfo->min_rx_bufsize = NIX_MIN_HW_FRS + RTE_ETHER_CRC_LEN;
|
|
devinfo->max_rx_pktlen = max_rx_pktlen;
|
|
devinfo->max_rx_queues = RTE_MAX_QUEUES_PER_PORT;
|
|
devinfo->max_tx_queues = RTE_MAX_QUEUES_PER_PORT;
|
|
devinfo->max_mac_addrs = dev->max_mac_entries;
|
|
devinfo->max_vfs = pci_dev->max_vfs;
|
|
devinfo->max_mtu = devinfo->max_rx_pktlen -
|
|
(RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN);
|
|
devinfo->min_mtu = devinfo->min_rx_bufsize - CNXK_NIX_L2_OVERHEAD;
|
|
|
|
devinfo->rx_offload_capa = dev->rx_offload_capa;
|
|
devinfo->tx_offload_capa = dev->tx_offload_capa;
|
|
devinfo->rx_queue_offload_capa = 0;
|
|
devinfo->tx_queue_offload_capa = 0;
|
|
|
|
devinfo->reta_size = dev->nix.reta_sz;
|
|
devinfo->hash_key_size = ROC_NIX_RSS_KEY_LEN;
|
|
devinfo->flow_type_rss_offloads = CNXK_NIX_RSS_OFFLOAD;
|
|
|
|
devinfo->default_rxconf = (struct rte_eth_rxconf){
|
|
.rx_drop_en = 0,
|
|
.offloads = 0,
|
|
};
|
|
|
|
devinfo->default_txconf = (struct rte_eth_txconf){
|
|
.offloads = 0,
|
|
};
|
|
|
|
devinfo->default_rxportconf = (struct rte_eth_dev_portconf){
|
|
.ring_size = CNXK_NIX_RX_DEFAULT_RING_SZ,
|
|
};
|
|
|
|
devinfo->rx_desc_lim = (struct rte_eth_desc_lim){
|
|
.nb_max = UINT16_MAX,
|
|
.nb_min = CNXK_NIX_RX_MIN_DESC,
|
|
.nb_align = CNXK_NIX_RX_MIN_DESC_ALIGN,
|
|
.nb_seg_max = CNXK_NIX_RX_NB_SEG_MAX,
|
|
.nb_mtu_seg_max = CNXK_NIX_RX_NB_SEG_MAX,
|
|
};
|
|
devinfo->rx_desc_lim.nb_max =
|
|
RTE_ALIGN_MUL_FLOOR(devinfo->rx_desc_lim.nb_max,
|
|
CNXK_NIX_RX_MIN_DESC_ALIGN);
|
|
|
|
devinfo->tx_desc_lim = (struct rte_eth_desc_lim){
|
|
.nb_max = UINT16_MAX,
|
|
.nb_min = 1,
|
|
.nb_align = 1,
|
|
.nb_seg_max = CNXK_NIX_TX_NB_SEG_MAX,
|
|
.nb_mtu_seg_max = CNXK_NIX_TX_NB_SEG_MAX,
|
|
};
|
|
|
|
devinfo->speed_capa = dev->speed_capa;
|
|
devinfo->dev_capa = RTE_ETH_DEV_CAPA_RUNTIME_RX_QUEUE_SETUP |
|
|
RTE_ETH_DEV_CAPA_RUNTIME_TX_QUEUE_SETUP |
|
|
RTE_ETH_DEV_CAPA_FLOW_RULE_KEEP;
|
|
|
|
devinfo->max_rx_mempools = CNXK_NIX_NUM_POOLS_MAX;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_rx_burst_mode_get(struct rte_eth_dev *eth_dev, uint16_t queue_id,
|
|
struct rte_eth_burst_mode *mode)
|
|
{
|
|
ssize_t bytes = 0, str_size = RTE_ETH_BURST_MODE_INFO_SIZE, rc;
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
const struct burst_info {
|
|
uint64_t flags;
|
|
const char *output;
|
|
} rx_offload_map[] = {
|
|
{RTE_ETH_RX_OFFLOAD_VLAN_STRIP, " VLAN Strip,"},
|
|
{RTE_ETH_RX_OFFLOAD_IPV4_CKSUM, " Inner IPv4 Checksum,"},
|
|
{RTE_ETH_RX_OFFLOAD_UDP_CKSUM, " UDP Checksum,"},
|
|
{RTE_ETH_RX_OFFLOAD_TCP_CKSUM, " TCP Checksum,"},
|
|
{RTE_ETH_RX_OFFLOAD_TCP_LRO, " TCP LRO,"},
|
|
{RTE_ETH_RX_OFFLOAD_QINQ_STRIP, " QinQ VLAN Strip,"},
|
|
{RTE_ETH_RX_OFFLOAD_OUTER_IPV4_CKSUM, " Outer IPv4 Checksum,"},
|
|
{RTE_ETH_RX_OFFLOAD_MACSEC_STRIP, " MACsec Strip,"},
|
|
{RTE_ETH_RX_OFFLOAD_VLAN_FILTER, " VLAN Filter,"},
|
|
{RTE_ETH_RX_OFFLOAD_VLAN_EXTEND, " VLAN Extend,"},
|
|
{RTE_ETH_RX_OFFLOAD_SCATTER, " Scattered,"},
|
|
{RTE_ETH_RX_OFFLOAD_TIMESTAMP, " Timestamp,"},
|
|
{RTE_ETH_RX_OFFLOAD_SECURITY, " Security,"},
|
|
{RTE_ETH_RX_OFFLOAD_KEEP_CRC, " Keep CRC,"},
|
|
{RTE_ETH_RX_OFFLOAD_SCTP_CKSUM, " SCTP,"},
|
|
{RTE_ETH_RX_OFFLOAD_OUTER_UDP_CKSUM, " Outer UDP Checksum,"},
|
|
{RTE_ETH_RX_OFFLOAD_RSS_HASH, " RSS,"}
|
|
};
|
|
static const char *const burst_mode[] = {"Vector Neon, Rx Offloads:",
|
|
"Scalar, Rx Offloads:"
|
|
};
|
|
uint32_t i;
|
|
|
|
PLT_SET_USED(queue_id);
|
|
|
|
/* Update burst mode info */
|
|
rc = rte_strscpy(mode->info + bytes, burst_mode[dev->scalar_ena],
|
|
str_size - bytes);
|
|
if (rc < 0)
|
|
goto done;
|
|
|
|
bytes += rc;
|
|
|
|
/* Update Rx offload info */
|
|
for (i = 0; i < RTE_DIM(rx_offload_map); i++) {
|
|
if (dev->rx_offloads & rx_offload_map[i].flags) {
|
|
rc = rte_strscpy(mode->info + bytes,
|
|
rx_offload_map[i].output,
|
|
str_size - bytes);
|
|
if (rc < 0)
|
|
goto done;
|
|
|
|
bytes += rc;
|
|
}
|
|
}
|
|
|
|
done:
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_tx_burst_mode_get(struct rte_eth_dev *eth_dev, uint16_t queue_id,
|
|
struct rte_eth_burst_mode *mode)
|
|
{
|
|
ssize_t bytes = 0, str_size = RTE_ETH_BURST_MODE_INFO_SIZE, rc;
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
const struct burst_info {
|
|
uint64_t flags;
|
|
const char *output;
|
|
} tx_offload_map[] = {
|
|
{RTE_ETH_TX_OFFLOAD_VLAN_INSERT, " VLAN Insert,"},
|
|
{RTE_ETH_TX_OFFLOAD_IPV4_CKSUM, " Inner IPv4 Checksum,"},
|
|
{RTE_ETH_TX_OFFLOAD_UDP_CKSUM, " UDP Checksum,"},
|
|
{RTE_ETH_TX_OFFLOAD_TCP_CKSUM, " TCP Checksum,"},
|
|
{RTE_ETH_TX_OFFLOAD_SCTP_CKSUM, " SCTP Checksum,"},
|
|
{RTE_ETH_TX_OFFLOAD_TCP_TSO, " TCP TSO,"},
|
|
{RTE_ETH_TX_OFFLOAD_UDP_TSO, " UDP TSO,"},
|
|
{RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM, " Outer IPv4 Checksum,"},
|
|
{RTE_ETH_TX_OFFLOAD_QINQ_INSERT, " QinQ VLAN Insert,"},
|
|
{RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO, " VXLAN Tunnel TSO,"},
|
|
{RTE_ETH_TX_OFFLOAD_GRE_TNL_TSO, " GRE Tunnel TSO,"},
|
|
{RTE_ETH_TX_OFFLOAD_IPIP_TNL_TSO, " IP-in-IP Tunnel TSO,"},
|
|
{RTE_ETH_TX_OFFLOAD_GENEVE_TNL_TSO, " Geneve Tunnel TSO,"},
|
|
{RTE_ETH_TX_OFFLOAD_MACSEC_INSERT, " MACsec Insert,"},
|
|
{RTE_ETH_TX_OFFLOAD_MT_LOCKFREE, " Multi Thread Lockless Tx,"},
|
|
{RTE_ETH_TX_OFFLOAD_MULTI_SEGS, " Scattered,"},
|
|
{RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE, " H/W MBUF Free,"},
|
|
{RTE_ETH_TX_OFFLOAD_SECURITY, " Security,"},
|
|
{RTE_ETH_TX_OFFLOAD_UDP_TNL_TSO, " UDP Tunnel TSO,"},
|
|
{RTE_ETH_TX_OFFLOAD_IP_TNL_TSO, " IP Tunnel TSO,"},
|
|
{RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM, " Outer UDP Checksum,"},
|
|
{RTE_ETH_TX_OFFLOAD_SEND_ON_TIMESTAMP, " Timestamp,"}
|
|
};
|
|
static const char *const burst_mode[] = {"Vector Neon, Tx Offloads:",
|
|
"Scalar, Tx Offloads:"
|
|
};
|
|
uint32_t i;
|
|
|
|
PLT_SET_USED(queue_id);
|
|
|
|
/* Update burst mode info */
|
|
rc = rte_strscpy(mode->info + bytes, burst_mode[dev->scalar_ena],
|
|
str_size - bytes);
|
|
if (rc < 0)
|
|
goto done;
|
|
|
|
bytes += rc;
|
|
|
|
/* Update Tx offload info */
|
|
for (i = 0; i < RTE_DIM(tx_offload_map); i++) {
|
|
if (dev->tx_offloads & tx_offload_map[i].flags) {
|
|
rc = rte_strscpy(mode->info + bytes,
|
|
tx_offload_map[i].output,
|
|
str_size - bytes);
|
|
if (rc < 0)
|
|
goto done;
|
|
|
|
bytes += rc;
|
|
}
|
|
}
|
|
|
|
done:
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_flow_ctrl_get(struct rte_eth_dev *eth_dev,
|
|
struct rte_eth_fc_conf *fc_conf)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
enum rte_eth_fc_mode mode_map[] = {
|
|
RTE_ETH_FC_NONE, RTE_ETH_FC_RX_PAUSE,
|
|
RTE_ETH_FC_TX_PAUSE, RTE_ETH_FC_FULL
|
|
};
|
|
struct roc_nix *nix = &dev->nix;
|
|
int mode;
|
|
|
|
mode = roc_nix_fc_mode_get(nix);
|
|
if (mode < 0)
|
|
return mode;
|
|
|
|
memset(fc_conf, 0, sizeof(struct rte_eth_fc_conf));
|
|
fc_conf->mode = mode_map[mode];
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
nix_fc_cq_config_set(struct cnxk_eth_dev *dev, uint16_t qid, bool enable)
|
|
{
|
|
struct roc_nix *nix = &dev->nix;
|
|
struct roc_nix_fc_cfg fc_cfg;
|
|
struct roc_nix_cq *cq;
|
|
struct roc_nix_rq *rq;
|
|
|
|
memset(&fc_cfg, 0, sizeof(struct roc_nix_fc_cfg));
|
|
rq = &dev->rqs[qid];
|
|
cq = &dev->cqs[qid];
|
|
fc_cfg.type = ROC_NIX_FC_RQ_CFG;
|
|
fc_cfg.rq_cfg.enable = enable;
|
|
fc_cfg.rq_cfg.tc = 0;
|
|
fc_cfg.rq_cfg.rq = qid;
|
|
fc_cfg.rq_cfg.pool = rq->aura_handle;
|
|
fc_cfg.rq_cfg.cq_drop = cq->drop_thresh;
|
|
|
|
return roc_nix_fc_config_set(nix, &fc_cfg);
|
|
}
|
|
|
|
int
|
|
cnxk_nix_flow_ctrl_set(struct rte_eth_dev *eth_dev,
|
|
struct rte_eth_fc_conf *fc_conf)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
enum roc_nix_fc_mode mode_map[] = {
|
|
ROC_NIX_FC_NONE, ROC_NIX_FC_RX,
|
|
ROC_NIX_FC_TX, ROC_NIX_FC_FULL
|
|
};
|
|
struct rte_eth_dev_data *data = eth_dev->data;
|
|
struct cnxk_fc_cfg *fc = &dev->fc_cfg;
|
|
struct roc_nix *nix = &dev->nix;
|
|
struct cnxk_eth_rxq_sp *rxq;
|
|
struct cnxk_eth_txq_sp *txq;
|
|
uint8_t rx_pause, tx_pause;
|
|
int rc, i;
|
|
|
|
if (roc_nix_is_sdp(nix))
|
|
return 0;
|
|
|
|
if (fc_conf->high_water || fc_conf->low_water || fc_conf->pause_time ||
|
|
fc_conf->mac_ctrl_frame_fwd || fc_conf->autoneg) {
|
|
plt_info("Only MODE configuration is supported");
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
rx_pause = (fc_conf->mode == RTE_ETH_FC_FULL) ||
|
|
(fc_conf->mode == RTE_ETH_FC_RX_PAUSE);
|
|
tx_pause = (fc_conf->mode == RTE_ETH_FC_FULL) ||
|
|
(fc_conf->mode == RTE_ETH_FC_TX_PAUSE);
|
|
|
|
if (fc_conf->mode == fc->mode) {
|
|
fc->rx_pause = rx_pause;
|
|
fc->tx_pause = tx_pause;
|
|
return 0;
|
|
}
|
|
|
|
/* Check if TX pause frame is already enabled or not */
|
|
if (fc->tx_pause ^ tx_pause) {
|
|
if (roc_model_is_cn96_ax() && data->dev_started) {
|
|
/* On Ax, CQ should be in disabled state
|
|
* while setting flow control configuration.
|
|
*/
|
|
plt_info("Stop the port=%d for setting flow control",
|
|
data->port_id);
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < data->nb_rx_queues; i++) {
|
|
struct roc_nix_fc_cfg fc_cfg;
|
|
|
|
memset(&fc_cfg, 0, sizeof(struct roc_nix_fc_cfg));
|
|
rxq = ((struct cnxk_eth_rxq_sp *)data->rx_queues[i]) -
|
|
1;
|
|
rxq->tx_pause = !!tx_pause;
|
|
rc = nix_fc_cq_config_set(dev, rxq->qid, !!tx_pause);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
/* Check if RX pause frame is enabled or not */
|
|
if (fc->rx_pause ^ rx_pause) {
|
|
for (i = 0; i < data->nb_tx_queues; i++) {
|
|
struct roc_nix_fc_cfg fc_cfg;
|
|
|
|
memset(&fc_cfg, 0, sizeof(struct roc_nix_fc_cfg));
|
|
txq = ((struct cnxk_eth_txq_sp *)data->tx_queues[i]) -
|
|
1;
|
|
fc_cfg.type = ROC_NIX_FC_TM_CFG;
|
|
fc_cfg.tm_cfg.sq = txq->qid;
|
|
fc_cfg.tm_cfg.enable = !!rx_pause;
|
|
rc = roc_nix_fc_config_set(nix, &fc_cfg);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
}
|
|
|
|
rc = roc_nix_fc_mode_set(nix, mode_map[fc_conf->mode]);
|
|
if (rc)
|
|
return rc;
|
|
|
|
fc->rx_pause = rx_pause;
|
|
fc->tx_pause = tx_pause;
|
|
fc->mode = fc_conf->mode;
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_priority_flow_ctrl_queue_info_get(struct rte_eth_dev *eth_dev,
|
|
struct rte_eth_pfc_queue_info *pfc_info)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
|
|
pfc_info->tc_max = roc_nix_chan_count_get(&dev->nix);
|
|
pfc_info->mode_capa = RTE_ETH_FC_FULL;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_priority_flow_ctrl_queue_config(struct rte_eth_dev *eth_dev,
|
|
struct rte_eth_pfc_queue_conf *pfc_conf)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct roc_nix *nix = &dev->nix;
|
|
enum rte_eth_fc_mode mode;
|
|
uint8_t en, tc;
|
|
uint16_t qid;
|
|
int rc = 0;
|
|
|
|
if (dev->fc_cfg.mode != RTE_ETH_FC_NONE) {
|
|
plt_err("Disable Flow Control before configuring PFC");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
if (roc_nix_is_sdp(nix) || roc_nix_is_lbk(nix)) {
|
|
plt_nix_dbg("Prio flow ctrl config is not allowed on SDP/LBK");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
mode = pfc_conf->mode;
|
|
|
|
/* Perform Tx pause configuration on RQ */
|
|
qid = pfc_conf->tx_pause.rx_qid;
|
|
if (qid < eth_dev->data->nb_rx_queues) {
|
|
en = (mode == RTE_ETH_FC_FULL) || (mode == RTE_ETH_FC_TX_PAUSE);
|
|
tc = pfc_conf->tx_pause.tc;
|
|
rc = nix_priority_flow_ctrl_rq_conf(eth_dev, qid, en, tc);
|
|
}
|
|
|
|
/* Perform Rx pause configuration on SQ */
|
|
qid = pfc_conf->rx_pause.tx_qid;
|
|
if (qid < eth_dev->data->nb_tx_queues) {
|
|
en = (mode == RTE_ETH_FC_FULL) || (mode == RTE_ETH_FC_RX_PAUSE);
|
|
tc = pfc_conf->rx_pause.tc;
|
|
rc |= nix_priority_flow_ctrl_sq_conf(eth_dev, qid, en, tc);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_flow_ops_get(struct rte_eth_dev *eth_dev,
|
|
const struct rte_flow_ops **ops)
|
|
{
|
|
RTE_SET_USED(eth_dev);
|
|
|
|
*ops = &cnxk_flow_ops;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_mac_addr_set(struct rte_eth_dev *eth_dev, struct rte_ether_addr *addr)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct roc_nix *nix = &dev->nix;
|
|
int rc;
|
|
|
|
/* Update mac address at NPC */
|
|
rc = roc_nix_npc_mac_addr_set(nix, addr->addr_bytes);
|
|
if (rc)
|
|
goto exit;
|
|
|
|
/* Update mac address at CGX for PFs only */
|
|
if (!roc_nix_is_vf_or_sdp(nix)) {
|
|
rc = roc_nix_mac_addr_set(nix, addr->addr_bytes);
|
|
if (rc) {
|
|
/* Rollback to previous mac address */
|
|
roc_nix_npc_mac_addr_set(nix, dev->mac_addr);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
/* Update mac address to cnxk ethernet device */
|
|
rte_memcpy(dev->mac_addr, addr->addr_bytes, RTE_ETHER_ADDR_LEN);
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_mac_addr_add(struct rte_eth_dev *eth_dev, struct rte_ether_addr *addr,
|
|
uint32_t index, uint32_t pool)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct roc_nix *nix = &dev->nix;
|
|
int rc;
|
|
|
|
PLT_SET_USED(index);
|
|
PLT_SET_USED(pool);
|
|
|
|
rc = roc_nix_mac_addr_add(nix, addr->addr_bytes);
|
|
if (rc < 0) {
|
|
plt_err("Failed to add mac address, rc=%d", rc);
|
|
return rc;
|
|
}
|
|
|
|
/* Enable promiscuous mode at NIX level */
|
|
roc_nix_npc_promisc_ena_dis(nix, true);
|
|
dev->dmac_filter_enable = true;
|
|
eth_dev->data->promiscuous = false;
|
|
dev->dmac_filter_count++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
cnxk_nix_mac_addr_del(struct rte_eth_dev *eth_dev, uint32_t index)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct roc_nix *nix = &dev->nix;
|
|
int rc;
|
|
|
|
rc = roc_nix_mac_addr_del(nix, index);
|
|
if (rc)
|
|
plt_err("Failed to delete mac address, rc=%d", rc);
|
|
|
|
dev->dmac_filter_count--;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_mtu_set(struct rte_eth_dev *eth_dev, uint16_t mtu)
|
|
{
|
|
uint32_t old_frame_size, frame_size = mtu + CNXK_NIX_L2_OVERHEAD;
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct rte_eth_dev_data *data = eth_dev->data;
|
|
struct roc_nix *nix = &dev->nix;
|
|
int rc = -EINVAL;
|
|
uint32_t buffsz;
|
|
|
|
frame_size += CNXK_NIX_TIMESYNC_RX_OFFSET * dev->ptp_en;
|
|
|
|
/* Check if MTU is within the allowed range */
|
|
if ((frame_size - RTE_ETHER_CRC_LEN) < NIX_MIN_HW_FRS) {
|
|
plt_err("MTU is lesser than minimum");
|
|
goto exit;
|
|
}
|
|
|
|
if ((frame_size - RTE_ETHER_CRC_LEN) >
|
|
((uint32_t)roc_nix_max_pkt_len(nix))) {
|
|
plt_err("MTU is greater than maximum");
|
|
goto exit;
|
|
}
|
|
|
|
buffsz = data->min_rx_buf_size - RTE_PKTMBUF_HEADROOM;
|
|
old_frame_size = data->mtu + CNXK_NIX_L2_OVERHEAD;
|
|
|
|
/* Refuse MTU that requires the support of scattered packets
|
|
* when this feature has not been enabled before.
|
|
*/
|
|
if (data->dev_started && frame_size > buffsz &&
|
|
!(dev->rx_offloads & RTE_ETH_RX_OFFLOAD_SCATTER)) {
|
|
plt_err("Scatter offload is not enabled for mtu");
|
|
goto exit;
|
|
}
|
|
|
|
/* Check <seg size> * <max_seg> >= max_frame */
|
|
if ((dev->rx_offloads & RTE_ETH_RX_OFFLOAD_SCATTER) &&
|
|
frame_size > (buffsz * CNXK_NIX_RX_NB_SEG_MAX)) {
|
|
plt_err("Greater than maximum supported packet length");
|
|
goto exit;
|
|
}
|
|
|
|
frame_size -= RTE_ETHER_CRC_LEN;
|
|
|
|
/* Update mtu on Tx */
|
|
rc = roc_nix_mac_mtu_set(nix, frame_size);
|
|
if (rc) {
|
|
plt_err("Failed to set MTU, rc=%d", rc);
|
|
goto exit;
|
|
}
|
|
|
|
/* Sync same frame size on Rx */
|
|
rc = roc_nix_mac_max_rx_len_set(nix, frame_size);
|
|
if (rc) {
|
|
/* Rollback to older mtu */
|
|
roc_nix_mac_mtu_set(nix,
|
|
old_frame_size - RTE_ETHER_CRC_LEN);
|
|
plt_err("Failed to max Rx frame length, rc=%d", rc);
|
|
goto exit;
|
|
}
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_promisc_enable(struct rte_eth_dev *eth_dev)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct roc_nix *nix = &dev->nix;
|
|
int rc = 0;
|
|
|
|
if (roc_nix_is_vf_or_sdp(nix))
|
|
return rc;
|
|
|
|
rc = roc_nix_npc_promisc_ena_dis(nix, true);
|
|
if (rc) {
|
|
plt_err("Failed to setup promisc mode in npc, rc=%d(%s)", rc,
|
|
roc_error_msg_get(rc));
|
|
return rc;
|
|
}
|
|
|
|
rc = roc_nix_mac_promisc_mode_enable(nix, true);
|
|
if (rc) {
|
|
plt_err("Failed to setup promisc mode in mac, rc=%d(%s)", rc,
|
|
roc_error_msg_get(rc));
|
|
roc_nix_npc_promisc_ena_dis(nix, false);
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_promisc_disable(struct rte_eth_dev *eth_dev)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct roc_nix *nix = &dev->nix;
|
|
int rc = 0;
|
|
|
|
if (roc_nix_is_vf_or_sdp(nix))
|
|
return rc;
|
|
|
|
rc = roc_nix_npc_promisc_ena_dis(nix, dev->dmac_filter_enable);
|
|
if (rc) {
|
|
plt_err("Failed to setup promisc mode in npc, rc=%d(%s)", rc,
|
|
roc_error_msg_get(rc));
|
|
return rc;
|
|
}
|
|
|
|
rc = roc_nix_mac_promisc_mode_enable(nix, false);
|
|
if (rc) {
|
|
plt_err("Failed to setup promisc mode in mac, rc=%d(%s)", rc,
|
|
roc_error_msg_get(rc));
|
|
roc_nix_npc_promisc_ena_dis(nix, !dev->dmac_filter_enable);
|
|
return rc;
|
|
}
|
|
|
|
dev->dmac_filter_enable = false;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_allmulticast_enable(struct rte_eth_dev *eth_dev)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
|
|
return roc_nix_npc_mcast_config(&dev->nix, true,
|
|
eth_dev->data->promiscuous);
|
|
}
|
|
|
|
int
|
|
cnxk_nix_allmulticast_disable(struct rte_eth_dev *eth_dev)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
|
|
return roc_nix_npc_mcast_config(&dev->nix, false,
|
|
eth_dev->data->promiscuous);
|
|
}
|
|
|
|
int
|
|
cnxk_nix_set_link_up(struct rte_eth_dev *eth_dev)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct roc_nix *nix = &dev->nix;
|
|
int rc, i;
|
|
|
|
if (roc_nix_is_vf_or_sdp(nix))
|
|
return -ENOTSUP;
|
|
|
|
rc = roc_nix_mac_link_state_set(nix, true);
|
|
if (rc)
|
|
goto exit;
|
|
|
|
/* Start tx queues */
|
|
for (i = 0; i < eth_dev->data->nb_tx_queues; i++) {
|
|
rc = cnxk_nix_tx_queue_start(eth_dev, i);
|
|
if (rc)
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_set_link_down(struct rte_eth_dev *eth_dev)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct roc_nix *nix = &dev->nix;
|
|
int rc, i;
|
|
|
|
if (roc_nix_is_vf_or_sdp(nix))
|
|
return -ENOTSUP;
|
|
|
|
/* Stop tx queues */
|
|
for (i = 0; i < eth_dev->data->nb_tx_queues; i++) {
|
|
rc = cnxk_nix_tx_queue_stop(eth_dev, i);
|
|
if (rc)
|
|
goto exit;
|
|
}
|
|
|
|
rc = roc_nix_mac_link_state_set(nix, false);
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_get_module_info(struct rte_eth_dev *eth_dev,
|
|
struct rte_eth_dev_module_info *modinfo)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct roc_nix_eeprom_info eeprom_info = {0};
|
|
struct roc_nix *nix = &dev->nix;
|
|
int rc;
|
|
|
|
rc = roc_nix_eeprom_info_get(nix, &eeprom_info);
|
|
if (rc)
|
|
return rc;
|
|
|
|
modinfo->type = eeprom_info.sff_id;
|
|
modinfo->eeprom_len = ROC_NIX_EEPROM_SIZE;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_get_module_eeprom(struct rte_eth_dev *eth_dev,
|
|
struct rte_dev_eeprom_info *info)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct roc_nix_eeprom_info eeprom_info = {0};
|
|
struct roc_nix *nix = &dev->nix;
|
|
int rc = -EINVAL;
|
|
|
|
if (!info->data || !info->length ||
|
|
(info->offset + info->length > ROC_NIX_EEPROM_SIZE))
|
|
return rc;
|
|
|
|
rc = roc_nix_eeprom_info_get(nix, &eeprom_info);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rte_memcpy(info->data, eeprom_info.buf + info->offset, info->length);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_rx_queue_intr_enable(struct rte_eth_dev *eth_dev, uint16_t rx_queue_id)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
|
|
roc_nix_rx_queue_intr_enable(&dev->nix, rx_queue_id);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_rx_queue_intr_disable(struct rte_eth_dev *eth_dev,
|
|
uint16_t rx_queue_id)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
|
|
roc_nix_rx_queue_intr_disable(&dev->nix, rx_queue_id);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_pool_ops_supported(struct rte_eth_dev *eth_dev, const char *pool)
|
|
{
|
|
RTE_SET_USED(eth_dev);
|
|
|
|
if (!strcmp(pool, rte_mbuf_platform_mempool_ops()))
|
|
return 0;
|
|
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_fw_version_get(struct rte_eth_dev *eth_dev, char *fw_version,
|
|
size_t fw_size)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
const char *str = roc_npc_profile_name_get(&dev->npc);
|
|
uint32_t size = strlen(str) + 1;
|
|
|
|
if (fw_size > size)
|
|
fw_size = size;
|
|
|
|
rte_strlcpy(fw_version, str, fw_size);
|
|
|
|
if (fw_size < size)
|
|
return size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
cnxk_nix_rxq_info_get(struct rte_eth_dev *eth_dev, uint16_t qid,
|
|
struct rte_eth_rxq_info *qinfo)
|
|
{
|
|
void *rxq = eth_dev->data->rx_queues[qid];
|
|
struct cnxk_eth_rxq_sp *rxq_sp = cnxk_eth_rxq_to_sp(rxq);
|
|
|
|
memset(qinfo, 0, sizeof(*qinfo));
|
|
|
|
qinfo->mp = rxq_sp->qconf.mp;
|
|
qinfo->scattered_rx = eth_dev->data->scattered_rx;
|
|
qinfo->nb_desc = rxq_sp->qconf.nb_desc;
|
|
|
|
memcpy(&qinfo->conf, &rxq_sp->qconf.conf.rx, sizeof(qinfo->conf));
|
|
}
|
|
|
|
void
|
|
cnxk_nix_txq_info_get(struct rte_eth_dev *eth_dev, uint16_t qid,
|
|
struct rte_eth_txq_info *qinfo)
|
|
{
|
|
void *txq = eth_dev->data->tx_queues[qid];
|
|
struct cnxk_eth_txq_sp *txq_sp = cnxk_eth_txq_to_sp(txq);
|
|
|
|
memset(qinfo, 0, sizeof(*qinfo));
|
|
|
|
qinfo->nb_desc = txq_sp->qconf.nb_desc;
|
|
|
|
memcpy(&qinfo->conf, &txq_sp->qconf.conf.tx, sizeof(qinfo->conf));
|
|
}
|
|
|
|
uint32_t
|
|
cnxk_nix_rx_queue_count(void *rxq)
|
|
{
|
|
struct cnxk_eth_rxq_sp *rxq_sp = cnxk_eth_rxq_to_sp(rxq);
|
|
struct roc_nix *nix = &rxq_sp->dev->nix;
|
|
uint32_t head, tail;
|
|
|
|
roc_nix_cq_head_tail_get(nix, rxq_sp->qid, &head, &tail);
|
|
return (tail - head) % (rxq_sp->qconf.nb_desc);
|
|
}
|
|
|
|
static inline int
|
|
nix_offset_has_packet(uint32_t head, uint32_t tail, uint16_t offset, bool is_rx)
|
|
{
|
|
/* Check given offset(queue index) has packet filled/xmit by HW
|
|
* in case of Rx or Tx.
|
|
* Also, checks for wrap around case.
|
|
*/
|
|
return ((tail > head && offset <= tail && offset >= head) ||
|
|
(head > tail && (offset >= head || offset <= tail))) ?
|
|
is_rx :
|
|
!is_rx;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_rx_descriptor_status(void *rxq, uint16_t offset)
|
|
{
|
|
struct cnxk_eth_rxq_sp *rxq_sp = cnxk_eth_rxq_to_sp(rxq);
|
|
struct roc_nix *nix = &rxq_sp->dev->nix;
|
|
uint32_t head, tail;
|
|
|
|
if (rxq_sp->qconf.nb_desc <= offset)
|
|
return -EINVAL;
|
|
|
|
roc_nix_cq_head_tail_get(nix, rxq_sp->qid, &head, &tail);
|
|
|
|
if (nix_offset_has_packet(head, tail, offset, 1))
|
|
return RTE_ETH_RX_DESC_DONE;
|
|
else
|
|
return RTE_ETH_RX_DESC_AVAIL;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_tx_descriptor_status(void *txq, uint16_t offset)
|
|
{
|
|
struct cnxk_eth_txq_sp *txq_sp = cnxk_eth_txq_to_sp(txq);
|
|
struct roc_nix *nix = &txq_sp->dev->nix;
|
|
uint32_t head = 0, tail = 0;
|
|
|
|
if (txq_sp->qconf.nb_desc <= offset)
|
|
return -EINVAL;
|
|
|
|
roc_nix_sq_head_tail_get(nix, txq_sp->qid, &head, &tail);
|
|
|
|
if (nix_offset_has_packet(head, tail, offset, 0))
|
|
return RTE_ETH_TX_DESC_DONE;
|
|
else
|
|
return RTE_ETH_TX_DESC_FULL;
|
|
}
|
|
|
|
/* It is a NOP for cnxk as HW frees the buffer on xmit */
|
|
int
|
|
cnxk_nix_tx_done_cleanup(void *txq, uint32_t free_cnt)
|
|
{
|
|
RTE_SET_USED(txq);
|
|
RTE_SET_USED(free_cnt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_dev_get_reg(struct rte_eth_dev *eth_dev, struct rte_dev_reg_info *regs)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct roc_nix *nix = &dev->nix;
|
|
uint64_t *data = regs->data;
|
|
int rc = -ENOTSUP;
|
|
|
|
if (data == NULL) {
|
|
rc = roc_nix_lf_get_reg_count(nix);
|
|
if (rc > 0) {
|
|
regs->length = rc;
|
|
regs->width = 8;
|
|
rc = 0;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
if (!regs->length ||
|
|
regs->length == (uint32_t)roc_nix_lf_get_reg_count(nix))
|
|
return roc_nix_lf_reg_dump(nix, data);
|
|
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_reta_update(struct rte_eth_dev *eth_dev,
|
|
struct rte_eth_rss_reta_entry64 *reta_conf,
|
|
uint16_t reta_size)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
uint16_t reta[ROC_NIX_RSS_RETA_MAX];
|
|
struct roc_nix *nix = &dev->nix;
|
|
int i, j, rc = -EINVAL, idx = 0;
|
|
|
|
if (reta_size != dev->nix.reta_sz) {
|
|
plt_err("Size of hash lookup table configured (%d) does not "
|
|
"match the number hardware can supported (%d)",
|
|
reta_size, dev->nix.reta_sz);
|
|
goto fail;
|
|
}
|
|
|
|
roc_nix_rss_reta_get(nix, 0, reta);
|
|
|
|
/* Copy RETA table */
|
|
for (i = 0; i < (int)(dev->nix.reta_sz / RTE_ETH_RETA_GROUP_SIZE); i++) {
|
|
for (j = 0; j < RTE_ETH_RETA_GROUP_SIZE; j++) {
|
|
if ((reta_conf[i].mask >> j) & 0x01)
|
|
reta[idx] = reta_conf[i].reta[j];
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
return roc_nix_rss_reta_set(nix, 0, reta);
|
|
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_reta_query(struct rte_eth_dev *eth_dev,
|
|
struct rte_eth_rss_reta_entry64 *reta_conf,
|
|
uint16_t reta_size)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
uint16_t reta[ROC_NIX_RSS_RETA_MAX];
|
|
struct roc_nix *nix = &dev->nix;
|
|
int rc = -EINVAL, i, j, idx = 0;
|
|
|
|
if (reta_size != dev->nix.reta_sz) {
|
|
plt_err("Size of hash lookup table configured (%d) does not "
|
|
"match the number hardware can supported (%d)",
|
|
reta_size, dev->nix.reta_sz);
|
|
goto fail;
|
|
}
|
|
|
|
rc = roc_nix_rss_reta_get(nix, 0, reta);
|
|
if (rc)
|
|
goto fail;
|
|
|
|
/* Copy RETA table */
|
|
for (i = 0; i < (int)(dev->nix.reta_sz / RTE_ETH_RETA_GROUP_SIZE); i++) {
|
|
for (j = 0; j < RTE_ETH_RETA_GROUP_SIZE; j++) {
|
|
if ((reta_conf[i].mask >> j) & 0x01)
|
|
reta_conf[i].reta[j] = reta[idx];
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_eth_dev_priv_dump(struct rte_eth_dev *eth_dev, FILE *file)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct roc_nix *roc_nix = &dev->nix;
|
|
int i;
|
|
|
|
roc_nix_dump(roc_nix, file);
|
|
|
|
for (i = 0; i < eth_dev->data->nb_rx_queues; i++)
|
|
roc_nix_rq_dump(&dev->rqs[i], file);
|
|
|
|
for (i = 0; i < eth_dev->data->nb_rx_queues; i++)
|
|
roc_nix_cq_dump(&dev->cqs[i], file);
|
|
|
|
for (i = 0; i < eth_dev->data->nb_tx_queues; i++)
|
|
roc_nix_sq_dump(&dev->sqs[i], file);
|
|
|
|
roc_nix_queues_ctx_dump(roc_nix, file);
|
|
|
|
roc_nix_tm_dump(roc_nix, file);
|
|
|
|
roc_nix_inl_dev_dump(NULL, file);
|
|
|
|
roc_nix_inl_outb_cpt_lfs_dump(roc_nix, file);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_rss_hash_update(struct rte_eth_dev *eth_dev,
|
|
struct rte_eth_rss_conf *rss_conf)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct roc_nix *nix = &dev->nix;
|
|
uint8_t rss_hash_level;
|
|
uint32_t flowkey_cfg;
|
|
int rc = -EINVAL;
|
|
uint8_t alg_idx;
|
|
|
|
if (rss_conf->rss_key && rss_conf->rss_key_len != ROC_NIX_RSS_KEY_LEN) {
|
|
plt_err("Hash key size mismatch %d vs %d",
|
|
rss_conf->rss_key_len, ROC_NIX_RSS_KEY_LEN);
|
|
goto fail;
|
|
}
|
|
|
|
if (rss_conf->rss_key)
|
|
roc_nix_rss_key_set(nix, rss_conf->rss_key);
|
|
|
|
rss_hash_level = RTE_ETH_RSS_LEVEL(rss_conf->rss_hf);
|
|
if (rss_hash_level)
|
|
rss_hash_level -= 1;
|
|
flowkey_cfg =
|
|
cnxk_rss_ethdev_to_nix(dev, rss_conf->rss_hf, rss_hash_level);
|
|
|
|
rc = roc_nix_rss_flowkey_set(nix, &alg_idx, flowkey_cfg,
|
|
ROC_NIX_RSS_GROUP_DEFAULT,
|
|
ROC_NIX_RSS_MCAM_IDX_DEFAULT);
|
|
if (rc) {
|
|
plt_err("Failed to set RSS hash function rc=%d", rc);
|
|
return rc;
|
|
}
|
|
|
|
fail:
|
|
return rc;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_rss_hash_conf_get(struct rte_eth_dev *eth_dev,
|
|
struct rte_eth_rss_conf *rss_conf)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
|
|
if (rss_conf->rss_key)
|
|
roc_nix_rss_key_get(&dev->nix, rss_conf->rss_key);
|
|
|
|
rss_conf->rss_key_len = ROC_NIX_RSS_KEY_LEN;
|
|
rss_conf->rss_hf = dev->ethdev_rss_hf;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
cnxk_nix_mc_addr_list_configure(struct rte_eth_dev *eth_dev,
|
|
struct rte_ether_addr *mc_addr_set,
|
|
uint32_t nb_mc_addr)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct rte_eth_dev_data *data = eth_dev->data;
|
|
struct rte_ether_addr null_mac_addr;
|
|
struct roc_nix *nix = &dev->nix;
|
|
int rc, index;
|
|
uint32_t i;
|
|
|
|
memset(&null_mac_addr, 0, sizeof(null_mac_addr));
|
|
|
|
/* All configured multicast filters should be flushed first */
|
|
for (i = 0; i < dev->max_mac_entries; i++) {
|
|
if (rte_is_multicast_ether_addr(&data->mac_addrs[i])) {
|
|
rc = roc_nix_mac_addr_del(nix, i);
|
|
if (rc) {
|
|
plt_err("Failed to flush mcast address, rc=%d",
|
|
rc);
|
|
return rc;
|
|
}
|
|
|
|
dev->dmac_filter_count--;
|
|
/* Update address in NIC data structure */
|
|
rte_ether_addr_copy(&null_mac_addr,
|
|
&data->mac_addrs[i]);
|
|
}
|
|
}
|
|
|
|
if (!mc_addr_set || !nb_mc_addr)
|
|
return 0;
|
|
|
|
/* Check for available space */
|
|
if (nb_mc_addr >
|
|
((uint32_t)(dev->max_mac_entries - dev->dmac_filter_count))) {
|
|
plt_err("No space is available to add multicast filters");
|
|
return -ENOSPC;
|
|
}
|
|
|
|
/* Multicast addresses are to be installed */
|
|
for (i = 0; i < nb_mc_addr; i++) {
|
|
index = roc_nix_mac_addr_add(nix, mc_addr_set[i].addr_bytes);
|
|
if (index < 0) {
|
|
plt_err("Failed to add mcast mac address, rc=%d",
|
|
index);
|
|
return index;
|
|
}
|
|
|
|
dev->dmac_filter_count++;
|
|
/* Update address in NIC data structure */
|
|
rte_ether_addr_copy(&mc_addr_set[i], &data->mac_addrs[index]);
|
|
}
|
|
|
|
roc_nix_npc_promisc_ena_dis(nix, true);
|
|
dev->dmac_filter_enable = true;
|
|
eth_dev->data->promiscuous = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
nix_priority_flow_ctrl_rq_conf(struct rte_eth_dev *eth_dev, uint16_t qid,
|
|
uint8_t tx_pause, uint8_t tc)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct rte_eth_dev_data *data = eth_dev->data;
|
|
struct cnxk_pfc_cfg *pfc = &dev->pfc_cfg;
|
|
struct roc_nix *nix = &dev->nix;
|
|
struct roc_nix_pfc_cfg pfc_cfg;
|
|
struct roc_nix_fc_cfg fc_cfg;
|
|
struct cnxk_eth_rxq_sp *rxq;
|
|
enum roc_nix_fc_mode mode;
|
|
struct roc_nix_rq *rq;
|
|
struct roc_nix_cq *cq;
|
|
int rc;
|
|
|
|
if (roc_model_is_cn96_ax() && data->dev_started) {
|
|
/* On Ax, CQ should be in disabled state
|
|
* while setting flow control configuration.
|
|
*/
|
|
plt_info("Stop the port=%d for setting flow control",
|
|
data->port_id);
|
|
return 0;
|
|
}
|
|
|
|
if (data->rx_queues == NULL)
|
|
return -EINVAL;
|
|
|
|
if (qid >= eth_dev->data->nb_rx_queues)
|
|
return -ENOTSUP;
|
|
|
|
/* Configure RQ */
|
|
rxq = ((struct cnxk_eth_rxq_sp *)data->rx_queues[qid]) - 1;
|
|
rq = &dev->rqs[qid];
|
|
cq = &dev->cqs[qid];
|
|
|
|
memset(&fc_cfg, 0, sizeof(struct roc_nix_fc_cfg));
|
|
fc_cfg.type = ROC_NIX_FC_RQ_CFG;
|
|
fc_cfg.rq_cfg.tc = tc;
|
|
fc_cfg.rq_cfg.enable = !!tx_pause;
|
|
fc_cfg.rq_cfg.rq = rq->qid;
|
|
fc_cfg.rq_cfg.pool = rxq->qconf.mp->pool_id;
|
|
fc_cfg.rq_cfg.cq_drop = cq->drop_thresh;
|
|
rc = roc_nix_fc_config_set(nix, &fc_cfg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
if (rxq->tx_pause != tx_pause) {
|
|
if (tx_pause)
|
|
pfc->tx_pause_en++;
|
|
else
|
|
pfc->tx_pause_en--;
|
|
}
|
|
|
|
rxq->tx_pause = !!tx_pause;
|
|
rxq->tc = tc;
|
|
|
|
/* Skip if PFC already enabled in mac */
|
|
if (pfc->tx_pause_en > 1)
|
|
return 0;
|
|
|
|
/* Configure MAC block */
|
|
pfc->class_en = pfc->tx_pause_en ? 0xFF : 0x0;
|
|
|
|
if (pfc->rx_pause_en)
|
|
mode = pfc->tx_pause_en ? ROC_NIX_FC_FULL : ROC_NIX_FC_RX;
|
|
else
|
|
mode = pfc->tx_pause_en ? ROC_NIX_FC_TX : ROC_NIX_FC_NONE;
|
|
|
|
memset(&pfc_cfg, 0, sizeof(struct roc_nix_pfc_cfg));
|
|
pfc_cfg.mode = mode;
|
|
pfc_cfg.tc = pfc->class_en;
|
|
return roc_nix_pfc_mode_set(nix, &pfc_cfg);
|
|
}
|
|
|
|
int
|
|
nix_priority_flow_ctrl_sq_conf(struct rte_eth_dev *eth_dev, uint16_t qid,
|
|
uint8_t rx_pause, uint8_t tc)
|
|
{
|
|
struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev);
|
|
struct rte_eth_dev_data *data = eth_dev->data;
|
|
struct cnxk_pfc_cfg *pfc = &dev->pfc_cfg;
|
|
struct roc_nix *nix = &dev->nix;
|
|
struct roc_nix_pfc_cfg pfc_cfg;
|
|
struct roc_nix_fc_cfg fc_cfg;
|
|
struct cnxk_eth_txq_sp *txq;
|
|
enum roc_nix_fc_mode mode;
|
|
struct roc_nix_sq *sq;
|
|
int rc;
|
|
|
|
if (data->tx_queues == NULL)
|
|
return -EINVAL;
|
|
|
|
if (qid >= eth_dev->data->nb_tx_queues)
|
|
return -ENOTSUP;
|
|
|
|
/* Check if RX pause frame is enabled or not and
|
|
* confirm user requested for PFC.
|
|
*/
|
|
if (!pfc->rx_pause_en && rx_pause) {
|
|
if ((roc_nix_tm_tree_type_get(nix) == ROC_NIX_TM_DEFAULT) &&
|
|
eth_dev->data->nb_tx_queues > 1) {
|
|
/*
|
|
* Disabled xmit will be enabled when
|
|
* new topology is available.
|
|
*/
|
|
rc = roc_nix_tm_hierarchy_disable(nix);
|
|
if (rc)
|
|
goto exit;
|
|
|
|
rc = roc_nix_tm_pfc_prepare_tree(nix);
|
|
if (rc)
|
|
goto exit;
|
|
|
|
rc = roc_nix_tm_hierarchy_enable(nix, ROC_NIX_TM_PFC,
|
|
true);
|
|
if (rc)
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
txq = ((struct cnxk_eth_txq_sp *)data->tx_queues[qid]) - 1;
|
|
sq = &dev->sqs[txq->qid];
|
|
memset(&fc_cfg, 0, sizeof(struct roc_nix_fc_cfg));
|
|
fc_cfg.type = ROC_NIX_FC_TM_CFG;
|
|
fc_cfg.tm_cfg.sq = sq->qid;
|
|
fc_cfg.tm_cfg.tc = tc;
|
|
fc_cfg.tm_cfg.enable = !!rx_pause;
|
|
rc = roc_nix_fc_config_set(nix, &fc_cfg);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* Maintaining a count for SQs which are configured for PFC. This is
|
|
* required to handle disabling of a particular SQ without affecting
|
|
* PFC on other SQs.
|
|
*/
|
|
if (!fc_cfg.tm_cfg.enable && sq->tc != ROC_NIX_PFC_CLASS_INVALID) {
|
|
sq->tc = ROC_NIX_PFC_CLASS_INVALID;
|
|
pfc->rx_pause_en--;
|
|
} else if (fc_cfg.tm_cfg.enable &&
|
|
sq->tc == ROC_NIX_PFC_CLASS_INVALID) {
|
|
sq->tc = tc;
|
|
pfc->rx_pause_en++;
|
|
}
|
|
|
|
if (pfc->rx_pause_en > 1)
|
|
goto exit;
|
|
|
|
if (pfc->tx_pause_en)
|
|
mode = pfc->rx_pause_en ? ROC_NIX_FC_FULL : ROC_NIX_FC_TX;
|
|
else
|
|
mode = pfc->rx_pause_en ? ROC_NIX_FC_RX : ROC_NIX_FC_NONE;
|
|
|
|
memset(&pfc_cfg, 0, sizeof(struct roc_nix_pfc_cfg));
|
|
pfc_cfg.mode = mode;
|
|
pfc_cfg.tc = pfc->class_en;
|
|
rc = roc_nix_pfc_mode_set(nix, &pfc_cfg);
|
|
exit:
|
|
return rc;
|
|
}
|