Files
vpp/src/plugins/dev_octeon/port.c
T
Alok Mishra d8022139eb octeon: fix pause flow control for lbk/sdp devices
Pause frame flow control is not supported for LBK (Loopback) and
SDP (System DPI Packet Interface Unit) devices.
This patch skips the pause flow configuration for these devices.

Type: fix
Fixes: 53239b4

Signed-off-by: Alok Mishra <almishra@marvell.com>
Change-Id: I3096fcef9df4ad59d64bfabb83f91f13813128a8
2024-09-26 08:20:22 +00:00

680 lines
17 KiB
C

/* SPDX-License-Identifier: Apache-2.0
* Copyright (c) 2023 Cisco Systems, Inc.
*/
#include <vnet/vnet.h>
#include <vnet/dev/dev.h>
#include <vnet/dev/counters.h>
#include <dev_octeon/octeon.h>
#include <dev_octeon/common.h>
#include <vnet/ethernet/ethernet.h>
#define OCT_FLOW_PREALLOC_SIZE 1
#define OCT_FLOW_MAX_PRIORITY 7
#define OCT_ETH_LINK_SPEED_100G 100000 /**< 100 Gbps */
VLIB_REGISTER_LOG_CLASS (oct_log, static) = {
.class_name = "octeon",
.subclass_name = "port",
};
static const u8 default_rss_key[] = {
0xfe, 0xed, 0x0b, 0xad, 0xfe, 0xed, 0x0b, 0xad, 0xad, 0x0b, 0xed, 0xfe,
0xad, 0x0b, 0xed, 0xfe, 0x13, 0x57, 0x9b, 0xef, 0x24, 0x68, 0xac, 0x0e,
0x91, 0x72, 0x53, 0x11, 0x82, 0x64, 0x20, 0x44, 0x12, 0xef, 0x34, 0xcd,
0x56, 0xbc, 0x78, 0x9a, 0x9a, 0x78, 0xbc, 0x56, 0xcd, 0x34, 0xef, 0x12
};
static const u32 default_rss_flowkey =
(FLOW_KEY_TYPE_IPV4 | FLOW_KEY_TYPE_IPV6 | FLOW_KEY_TYPE_TCP |
FLOW_KEY_TYPE_UDP | FLOW_KEY_TYPE_SCTP);
static const u64 rxq_cfg =
ROC_NIX_LF_RX_CFG_DIS_APAD | ROC_NIX_LF_RX_CFG_IP6_UDP_OPT |
ROC_NIX_LF_RX_CFG_L2_LEN_ERR | ROC_NIX_LF_RX_CFG_DROP_RE |
ROC_NIX_LF_RX_CFG_CSUM_OL4 | ROC_NIX_LF_RX_CFG_CSUM_IL4 |
ROC_NIX_LF_RX_CFG_LEN_OL3 | ROC_NIX_LF_RX_CFG_LEN_OL4 |
ROC_NIX_LF_RX_CFG_LEN_IL3 | ROC_NIX_LF_RX_CFG_LEN_IL4;
static vnet_dev_rv_t
oct_roc_err (vnet_dev_t *dev, int rv, char *fmt, ...)
{
u8 *s = 0;
va_list va;
va_start (va, fmt);
s = va_format (s, fmt, &va);
va_end (va);
log_err (dev, "%v - ROC error %s (%d)", s, roc_error_msg_get (rv), rv);
vec_free (s);
return VNET_DEV_ERR_INTERNAL;
}
vnet_dev_rv_t
oct_port_pause_flow_control_init (vlib_main_t *vm, vnet_dev_port_t *port)
{
vnet_dev_t *dev = port->dev;
oct_device_t *cd = vnet_dev_get_data (dev);
struct roc_nix *nix = cd->nix;
struct roc_nix_fc_cfg fc_cfg;
struct roc_nix_sq *sq;
struct roc_nix_cq *cq;
struct roc_nix_rq *rq;
int rrv;
/* pause flow control is not supported on SDP/LBK devices */
if (roc_nix_is_sdp (nix) || roc_nix_is_lbk (nix))
{
log_notice (dev,
"pause flow control is not supported on SDP/LBK devices");
return VNET_DEV_OK;
}
fc_cfg.type = ROC_NIX_FC_RXCHAN_CFG;
fc_cfg.rxchan_cfg.enable = true;
rrv = roc_nix_fc_config_set (nix, &fc_cfg);
if (rrv)
return oct_roc_err (dev, rrv, "roc_nix_fc_config_set failed");
memset (&fc_cfg, 0, sizeof (struct roc_nix_fc_cfg));
fc_cfg.type = ROC_NIX_FC_RQ_CFG;
fc_cfg.rq_cfg.enable = true;
fc_cfg.rq_cfg.tc = 0;
foreach_vnet_dev_port_rx_queue (rxq, port)
{
oct_rxq_t *crq = vnet_dev_get_rx_queue_data (rxq);
rq = &crq->rq;
cq = &crq->cq;
fc_cfg.rq_cfg.rq = rq->qid;
fc_cfg.rq_cfg.cq_drop = cq->drop_thresh;
rrv = roc_nix_fc_config_set (nix, &fc_cfg);
if (rrv)
return oct_roc_err (dev, rrv, "roc_nix_fc_config_set failed");
}
memset (&fc_cfg, 0, sizeof (struct roc_nix_fc_cfg));
fc_cfg.type = ROC_NIX_FC_TM_CFG;
fc_cfg.tm_cfg.tc = 0;
fc_cfg.tm_cfg.enable = true;
foreach_vnet_dev_port_tx_queue (txq, port)
{
oct_txq_t *ctq = vnet_dev_get_tx_queue_data (txq);
sq = &ctq->sq;
fc_cfg.tm_cfg.sq = sq->qid;
rrv = roc_nix_fc_config_set (nix, &fc_cfg);
if (rrv)
return oct_roc_err (dev, rrv, "roc_nix_fc_config_set failed");
}
/* By default, enable pause flow control */
rrv = roc_nix_fc_mode_set (nix, ROC_NIX_FC_FULL);
if (rrv)
return oct_roc_err (dev, rrv, "roc_nix_fc_mode_set failed");
return VNET_DEV_OK;
}
vnet_dev_rv_t
oct_port_init (vlib_main_t *vm, vnet_dev_port_t *port)
{
vnet_dev_t *dev = port->dev;
oct_device_t *cd = vnet_dev_get_data (dev);
oct_port_t *cp = vnet_dev_get_port_data (port);
u8 mac_addr[PLT_ETHER_ADDR_LEN];
struct roc_nix *nix = cd->nix;
vnet_dev_rv_t rv;
int rrv;
log_debug (dev, "port init: port %u", port->port_id);
if ((rrv = roc_nix_lf_alloc (nix, port->intf.num_rx_queues,
port->intf.num_tx_queues, rxq_cfg)))
{
oct_port_deinit (vm, port);
return oct_roc_err (
dev, rrv,
"roc_nix_lf_alloc(nb_rxq = %u, nb_txq = %d, rxq_cfg=0x%lx) failed",
port->intf.num_rx_queues, port->intf.num_tx_queues, rxq_cfg);
}
cp->lf_allocated = 1;
if (!roc_nix_is_vf_or_sdp (nix))
{
if ((rrv = roc_nix_npc_mac_addr_get (nix, mac_addr)))
{
oct_port_deinit (vm, port);
return oct_roc_err (dev, rrv, "roc_nix_npc_mac_addr_get failed");
}
/* Sync MAC address to CGX/RPM table */
if ((rrv = roc_nix_mac_addr_set (nix, mac_addr)))
{
oct_port_deinit (vm, port);
return oct_roc_err (dev, rrv, "roc_nix_mac_addr_set failed");
}
}
if ((rrv = roc_nix_tm_init (nix)))
{
oct_port_deinit (vm, port);
return oct_roc_err (dev, rrv, "roc_nix_tm_init() failed");
}
cp->tm_initialized = 1;
if ((rrv = roc_nix_tm_hierarchy_enable (nix, ROC_NIX_TM_DEFAULT,
/* xmit_enable */ 0)))
{
oct_port_deinit (vm, port);
return oct_roc_err (dev, rrv, "roc_nix_tm_hierarchy_enable() failed");
}
if ((rrv = roc_nix_rss_default_setup (nix, default_rss_flowkey)))
{
oct_port_deinit (vm, port);
return oct_roc_err (dev, rrv, "roc_nix_rss_default_setup() failed");
}
roc_nix_rss_key_set (nix, default_rss_key);
cp->npc.roc_nix = nix;
cp->npc.flow_prealloc_size = OCT_FLOW_PREALLOC_SIZE;
cp->npc.flow_max_priority = OCT_FLOW_MAX_PRIORITY;
if ((rrv = roc_npc_init (&cp->npc)))
{
oct_port_deinit (vm, port);
return oct_roc_err (dev, rrv, "roc_npc_init() failed");
}
cp->npc_initialized = 1;
foreach_vnet_dev_port_rx_queue (q, port)
if (q->enabled)
if ((rv = oct_rxq_init (vm, q)))
{
oct_port_deinit (vm, port);
return rv;
}
foreach_vnet_dev_port_tx_queue (q, port)
if (q->enabled)
if ((rv = oct_txq_init (vm, q)))
{
oct_port_deinit (vm, port);
return rv;
}
oct_port_add_counters (vm, port);
if ((rrv = roc_nix_mac_mtu_set (nix, port->max_rx_frame_size)))
{
rv = oct_roc_err (dev, rrv, "roc_nix_mac_mtu_set() failed");
return rv;
}
/* Configure pause frame flow control*/
if ((rv = oct_port_pause_flow_control_init (vm, port)))
{
oct_port_deinit (vm, port);
return rv;
}
return VNET_DEV_OK;
}
void
oct_port_deinit (vlib_main_t *vm, vnet_dev_port_t *port)
{
vnet_dev_t *dev = port->dev;
oct_device_t *cd = vnet_dev_get_data (dev);
oct_port_t *cp = vnet_dev_get_port_data (port);
struct roc_nix *nix = cd->nix;
int rrv;
foreach_vnet_dev_port_rx_queue (q, port)
oct_rxq_deinit (vm, q);
foreach_vnet_dev_port_tx_queue (q, port)
oct_txq_deinit (vm, q);
if (cp->npc_initialized)
{
if ((rrv = roc_npc_fini (&cp->npc)))
oct_roc_err (dev, rrv, "roc_npc_fini() failed");
cp->npc_initialized = 0;
}
if (cp->tm_initialized)
{
roc_nix_tm_fini (nix);
cp->tm_initialized = 0;
}
if (cp->lf_allocated)
{
if ((rrv = roc_nix_lf_free (nix)))
oct_roc_err (dev, rrv, "roc_nix_lf_free() failed");
cp->lf_allocated = 0;
}
}
void
oct_port_poll (vlib_main_t *vm, vnet_dev_port_t *port)
{
vnet_dev_t *dev = port->dev;
oct_device_t *cd = vnet_dev_get_data (dev);
struct roc_nix *nix = cd->nix;
struct roc_nix_link_info link_info = {};
vnet_dev_port_state_changes_t changes = {};
int rrv;
if (oct_port_get_stats (vm, port))
return;
foreach_vnet_dev_port_rx_queue (q, port)
{
if (oct_rxq_get_stats (vm, port, q))
return;
}
foreach_vnet_dev_port_tx_queue (q, port)
{
if (oct_txq_get_stats (vm, port, q))
return;
}
if (roc_nix_is_lbk (nix) || roc_nix_is_sdp (nix))
{
link_info.status = 1;
link_info.full_duplex = 1;
link_info.autoneg = 0;
link_info.speed = OCT_ETH_LINK_SPEED_100G;
}
else
{
rrv = roc_nix_mac_link_info_get (nix, &link_info);
if (rrv)
return;
}
if (cd->status != link_info.status)
{
changes.change.link_state = 1;
changes.link_state = link_info.status;
cd->status = link_info.status;
}
if (cd->full_duplex != link_info.full_duplex)
{
changes.change.link_duplex = 1;
changes.full_duplex = link_info.full_duplex;
cd->full_duplex = link_info.full_duplex;
}
if (cd->speed != link_info.speed)
{
changes.change.link_speed = 1;
/* Convert to Kbps */
changes.link_speed = link_info.speed * 1000;
cd->speed = link_info.speed;
}
if (changes.change.any == 0)
return;
log_debug (dev,
"status %u full_duplex %u speed %u port %u lmac_type_id %u "
"fec %u aautoneg %u",
link_info.status, link_info.full_duplex, link_info.speed,
link_info.port, link_info.lmac_type_id, link_info.fec,
link_info.autoneg);
vnet_dev_port_state_change (vm, port, changes);
}
vnet_dev_rv_t
oct_rxq_start (vlib_main_t *vm, vnet_dev_rx_queue_t *rxq)
{
vnet_dev_t *dev = rxq->port->dev;
oct_rxq_t *crq = vnet_dev_get_rx_queue_data (rxq);
u32 buffer_indices[rxq->size], n_alloc;
u8 bpi = vnet_dev_get_rx_queue_buffer_pool_index (rxq);
int rrv;
n_alloc = vlib_buffer_alloc_from_pool (vm, buffer_indices, rxq->size, bpi);
for (int i = 0; i < n_alloc; i++)
roc_npa_aura_op_free (
crq->aura_handle, 0,
pointer_to_uword (vlib_get_buffer (vm, buffer_indices[i])) -
crq->hdr_off);
crq->n_enq = n_alloc;
if (roc_npa_aura_op_available (crq->aura_handle) != rxq->size)
log_warn (rxq->port->dev, "rx queue %u aura not filled completelly",
rxq->queue_id);
if ((rrv = roc_nix_rq_ena_dis (&crq->rq, 1)))
return oct_roc_err (dev, rrv, "roc_nix_rq_ena_dis() failed");
return VNET_DEV_OK;
}
void
oct_rxq_stop (vlib_main_t *vm, vnet_dev_rx_queue_t *rxq)
{
vnet_dev_t *dev = rxq->port->dev;
oct_rxq_t *crq = vnet_dev_get_rx_queue_data (rxq);
int rrv;
u32 n;
if ((rrv = roc_nix_rq_ena_dis (&crq->rq, 0)))
oct_roc_err (dev, rrv, "roc_nix_rq_ena_dis() failed");
n = oct_aura_free_all_buffers (vm, crq->aura_handle, crq->hdr_off);
if (crq->n_enq - n > 0)
log_err (dev, "%u buffers leaked on rx queue %u stop", crq->n_enq - n,
rxq->queue_id);
else
log_debug (dev, "%u buffers freed from rx queue %u", n, rxq->queue_id);
crq->n_enq = 0;
}
void
oct_txq_stop (vlib_main_t *vm, vnet_dev_tx_queue_t *txq)
{
vnet_dev_t *dev = txq->port->dev;
oct_txq_t *ctq = vnet_dev_get_tx_queue_data (txq);
oct_npa_batch_alloc_cl128_t *cl;
u32 n, off = ctq->hdr_off;
n = oct_aura_free_all_buffers (vm, ctq->aura_handle, off);
ctq->n_enq -= n;
if (ctq->n_enq > 0 && ctq->ba_num_cl > 0)
for (n = ctq->ba_num_cl, cl = ctq->ba_buffer + ctq->ba_first_cl; n;
cl++, n--)
{
oct_npa_batch_alloc_status_t st;
st.as_u64 = __atomic_load_n (cl->iova, __ATOMIC_ACQUIRE);
if (st.status.ccode != ALLOC_CCODE_INVAL)
for (u32 i = 0; i < st.status.count; i++)
{
vlib_buffer_t *b = (vlib_buffer_t *) (cl->iova[i] + off);
vlib_buffer_free_one (vm, vlib_get_buffer_index (vm, b));
ctq->n_enq--;
}
}
if (ctq->n_enq > 0)
log_err (dev, "%u buffers leaked on tx queue %u stop", ctq->n_enq,
txq->queue_id);
else
log_debug (dev, "%u buffers freed from tx queue %u", n, txq->queue_id);
ctq->n_enq = 0;
}
vnet_dev_rv_t
oct_port_start (vlib_main_t *vm, vnet_dev_port_t *port)
{
vnet_dev_t *dev = port->dev;
oct_device_t *cd = vnet_dev_get_data (dev);
struct roc_nix *nix = cd->nix;
struct roc_nix_eeprom_info eeprom_info = {};
vnet_dev_rv_t rv;
int rrv;
log_debug (port->dev, "port start: port %u", port->port_id);
foreach_vnet_dev_port_rx_queue (q, port)
if ((rv = oct_rxq_start (vm, q)) != VNET_DEV_OK)
goto done;
foreach_vnet_dev_port_tx_queue (q, port)
{
oct_txq_t *ctq = vnet_dev_get_tx_queue_data (q);
ctq->n_enq = 0;
}
if ((rrv = roc_nix_npc_rx_ena_dis (nix, true)))
{
rv = oct_roc_err (dev, rrv, "roc_nix_npc_rx_ena_dis() failed");
goto done;
}
vnet_dev_poll_port_add (vm, port, 0.5, oct_port_poll);
if (roc_nix_eeprom_info_get (nix, &eeprom_info) == 0)
{
log_debug (dev, "sff_id %u data %U", eeprom_info.sff_id, format_hexdump,
eeprom_info.buf, sizeof (eeprom_info.buf));
}
done:
if (rv != VNET_DEV_OK)
oct_port_stop (vm, port);
return VNET_DEV_OK;
}
void
oct_port_stop (vlib_main_t *vm, vnet_dev_port_t *port)
{
vnet_dev_t *dev = port->dev;
oct_device_t *cd = vnet_dev_get_data (dev);
struct roc_nix *nix = cd->nix;
int rrv;
log_debug (port->dev, "port stop: port %u", port->port_id);
vnet_dev_poll_port_remove (vm, port, oct_port_poll);
rrv = roc_nix_npc_rx_ena_dis (nix, false);
if (rrv)
{
oct_roc_err (dev, rrv, "roc_nix_npc_rx_ena_dis() failed");
return;
}
foreach_vnet_dev_port_rx_queue (q, port)
oct_rxq_stop (vm, q);
foreach_vnet_dev_port_tx_queue (q, port)
oct_txq_stop (vm, q);
vnet_dev_port_state_change (vm, port,
(vnet_dev_port_state_changes_t){
.change.link_state = 1,
.change.link_speed = 1,
.link_speed = 0,
.link_state = 0,
});
/* Update the device status */
cd->status = 0;
cd->speed = 0;
}
vnet_dev_rv_t
oct_validate_config_promisc_mode (vnet_dev_port_t *port, int enable)
{
vnet_dev_t *dev = port->dev;
oct_device_t *cd = vnet_dev_get_data (dev);
struct roc_nix *nix = cd->nix;
if (roc_nix_is_sdp (nix) || roc_nix_is_lbk (nix))
return VNET_DEV_ERR_UNSUPPORTED_DEVICE;
return VNET_DEV_OK;
}
vnet_dev_rv_t
oct_op_config_promisc_mode (vlib_main_t *vm, vnet_dev_port_t *port, int enable)
{
vnet_dev_t *dev = port->dev;
oct_device_t *cd = vnet_dev_get_data (dev);
struct roc_nix *nix = cd->nix;
int rv;
rv = roc_nix_npc_promisc_ena_dis (nix, enable);
if (rv)
{
return oct_roc_err (dev, rv, "roc_nix_npc_promisc_ena_dis failed");
}
if (!roc_nix_is_pf (nix))
return VNET_DEV_OK;
rv = roc_nix_mac_promisc_mode_enable (nix, enable);
if (rv)
{
return oct_roc_err (dev, rv,
"roc_nix_mac_promisc_mode_enable(%s) failed",
enable ? "true" : "false");
}
return VNET_DEV_OK;
}
static vnet_dev_rv_t
oct_port_add_del_eth_addr (vlib_main_t *vm, vnet_dev_port_t *port,
vnet_dev_hw_addr_t *addr, int is_add,
int is_primary)
{
vnet_dev_t *dev = port->dev;
oct_device_t *cd = vnet_dev_get_data (dev);
struct roc_nix *nix = cd->nix;
vnet_dev_rv_t rv = VNET_DEV_OK;
i32 rrv;
if (is_primary)
{
if (is_add)
{
/* Update mac address at NPC */
rrv = roc_nix_npc_mac_addr_set (nix, (u8 *) addr);
if (rrv)
rv = oct_roc_err (dev, rrv, "roc_nix_npc_mac_addr_set() failed");
/* Update mac address at CGX for PFs only */
if (!roc_nix_is_vf_or_sdp (nix))
{
rrv = roc_nix_mac_addr_set (nix, (u8 *) addr);
if (rrv)
{
/* Rollback to previous mac address */
roc_nix_npc_mac_addr_set (nix,
(u8 *) &port->primary_hw_addr);
rv = oct_roc_err (dev, rrv, "roc_nix_mac_addr_set() failed");
}
}
}
}
return rv;
}
vnet_dev_rv_t
oct_op_config_max_rx_len (vlib_main_t *vm, vnet_dev_port_t *port,
u32 rx_frame_size)
{
vnet_dev_t *dev = port->dev;
oct_device_t *cd = vnet_dev_get_data (dev);
struct roc_nix *nix = cd->nix;
vnet_dev_rv_t rv = VNET_DEV_OK;
i32 rrv;
rrv = roc_nix_mac_max_rx_len_set (nix, rx_frame_size);
if (rrv)
rv = oct_roc_err (dev, rrv, "roc_nix_mac_max_rx_len_set() failed");
return rv;
}
vnet_dev_rv_t
oct_port_cfg_change_validate (vlib_main_t *vm, vnet_dev_port_t *port,
vnet_dev_port_cfg_change_req_t *req)
{
vnet_dev_rv_t rv = VNET_DEV_OK;
switch (req->type)
{
case VNET_DEV_PORT_CFG_MAX_RX_FRAME_SIZE:
if (port->started)
rv = VNET_DEV_ERR_PORT_STARTED;
break;
case VNET_DEV_PORT_CFG_PROMISC_MODE:
rv = oct_validate_config_promisc_mode (port, req->promisc);
break;
case VNET_DEV_PORT_CFG_CHANGE_PRIMARY_HW_ADDR:
case VNET_DEV_PORT_CFG_ADD_SECONDARY_HW_ADDR:
case VNET_DEV_PORT_CFG_REMOVE_SECONDARY_HW_ADDR:
break;
case VNET_DEV_PORT_CFG_ADD_RX_FLOW:
case VNET_DEV_PORT_CFG_DEL_RX_FLOW:
case VNET_DEV_PORT_CFG_GET_RX_FLOW_COUNTER:
case VNET_DEV_PORT_CFG_RESET_RX_FLOW_COUNTER:
rv = oct_flow_validate_params (vm, port, req->type, req->flow_index,
req->private_data);
break;
default:
rv = VNET_DEV_ERR_NOT_SUPPORTED;
};
return rv;
}
vnet_dev_rv_t
oct_port_cfg_change (vlib_main_t *vm, vnet_dev_port_t *port,
vnet_dev_port_cfg_change_req_t *req)
{
vnet_dev_rv_t rv = VNET_DEV_OK;
switch (req->type)
{
case VNET_DEV_PORT_CFG_PROMISC_MODE:
rv = oct_op_config_promisc_mode (vm, port, req->promisc);
break;
case VNET_DEV_PORT_CFG_CHANGE_PRIMARY_HW_ADDR:
rv = oct_port_add_del_eth_addr (vm, port, &req->addr,
/* is_add */ 1,
/* is_primary */ 1);
break;
case VNET_DEV_PORT_CFG_ADD_SECONDARY_HW_ADDR:
break;
case VNET_DEV_PORT_CFG_REMOVE_SECONDARY_HW_ADDR:
break;
case VNET_DEV_PORT_CFG_MAX_RX_FRAME_SIZE:
rv = oct_op_config_max_rx_len (vm, port, req->max_rx_frame_size);
break;
case VNET_DEV_PORT_CFG_ADD_RX_FLOW:
case VNET_DEV_PORT_CFG_DEL_RX_FLOW:
case VNET_DEV_PORT_CFG_GET_RX_FLOW_COUNTER:
case VNET_DEV_PORT_CFG_RESET_RX_FLOW_COUNTER:
rv = oct_flow_ops_fn (vm, port, req->type, req->flow_index,
req->private_data);
break;
default:
return VNET_DEV_ERR_NOT_SUPPORTED;
};
return rv;
}