mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
eth: fbnic: Add protection against pause storm
Add protection against TX pause storms. A pause storm occurs when a device fails to send received packets up to the stack. When a pause storm is detected (pause state persists beyond the configured timeout), the device stops sending the pause frames and begins dropping packets instead of back-pressuring. The timeout is configurable via ethtool tunable (pfc-prevention-tout) with a maximum value of 10485ms, and the default value of 500ms. Once the device transitions to the storm-detected state, the service task periodically attempts recovery, returning the device to normal operation to handle any subsequent pause storm episodes. Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: Mohsin Bashir <mohsin.bashr@gmail.com> Link: https://patch.msgid.link/20260302230149.1580195-4-mohsin.bashr@gmail.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
parent
817de93c34
commit
9b7c8728f5
|
|
@ -98,6 +98,9 @@ struct fbnic_dev {
|
|||
|
||||
/* MDIO bus for PHYs */
|
||||
struct mii_bus *mdio_bus;
|
||||
|
||||
/* In units of ms since API supports values in ms */
|
||||
u16 ps_timeout;
|
||||
};
|
||||
|
||||
/* Reserve entry 0 in the MSI-X "others" array until we have filled all
|
||||
|
|
|
|||
|
|
@ -230,6 +230,7 @@ enum {
|
|||
#define FBNIC_INTR_MSIX_CTRL_VECTOR_MASK CSR_GENMASK(7, 0)
|
||||
#define FBNIC_INTR_MSIX_CTRL_ENABLE CSR_BIT(31)
|
||||
enum {
|
||||
FBNIC_INTR_MSIX_CTRL_RXB_IDX = 7,
|
||||
FBNIC_INTR_MSIX_CTRL_PCS_IDX = 34,
|
||||
};
|
||||
|
||||
|
|
@ -560,6 +561,11 @@ enum {
|
|||
#define FBNIC_RXB_DROP_THLD_CNT 8
|
||||
#define FBNIC_RXB_DROP_THLD_ON CSR_GENMASK(12, 0)
|
||||
#define FBNIC_RXB_DROP_THLD_OFF CSR_GENMASK(25, 13)
|
||||
#define FBNIC_RXB_PAUSE_STORM(n) (0x08019 + (n)) /* 0x20064 + 4*n */
|
||||
#define FBNIC_RXB_PAUSE_STORM_CNT 4
|
||||
#define FBNIC_RXB_PAUSE_STORM_FORCE_NORMAL CSR_BIT(20)
|
||||
#define FBNIC_RXB_PAUSE_STORM_THLD_TIME CSR_GENMASK(19, 0)
|
||||
#define FBNIC_RXB_PAUSE_STORM_UNIT_WR 0x0801d /* 0x20074 */
|
||||
#define FBNIC_RXB_ECN_THLD(n) (0x0801e + (n)) /* 0x20078 + 4*n */
|
||||
#define FBNIC_RXB_ECN_THLD_CNT 8
|
||||
#define FBNIC_RXB_ECN_THLD_ON CSR_GENMASK(12, 0)
|
||||
|
|
@ -596,6 +602,9 @@ enum {
|
|||
#define FBNIC_RXB_INTF_CREDIT_MASK2 CSR_GENMASK(11, 8)
|
||||
#define FBNIC_RXB_INTF_CREDIT_MASK3 CSR_GENMASK(15, 12)
|
||||
|
||||
#define FBNIC_RXB_ERR_INTR_STS 0x08050 /* 0x20140 */
|
||||
#define FBNIC_RXB_ERR_INTR_STS_PS CSR_GENMASK(15, 12)
|
||||
#define FBNIC_RXB_ERR_INTR_MASK 0x08052 /* 0x20148 */
|
||||
#define FBNIC_RXB_PAUSE_EVENT_CNT(n) (0x08053 + (n)) /* 0x2014c + 4*n */
|
||||
#define FBNIC_RXB_DROP_FRMS_STS(n) (0x08057 + (n)) /* 0x2015c + 4*n */
|
||||
#define FBNIC_RXB_DROP_BYTES_STS_L(n) \
|
||||
|
|
@ -636,6 +645,7 @@ enum {
|
|||
|
||||
#define FBNIC_RXB_PBUF_FIFO_LEVEL(n) (0x0811d + (n)) /* 0x20474 + 4*n */
|
||||
|
||||
#define FBNIC_RXB_PAUSE_STORM_UNIT_RD 0x08125 /* 0x20494 */
|
||||
#define FBNIC_RXB_INTEGRITY_ERR(n) (0x0812f + (n)) /* 0x204bc + 4*n */
|
||||
#define FBNIC_RXB_MAC_ERR(n) (0x08133 + (n)) /* 0x204cc + 4*n */
|
||||
#define FBNIC_RXB_PARSER_ERR(n) (0x08137 + (n)) /* 0x204dc + 4*n */
|
||||
|
|
|
|||
|
|
@ -1641,6 +1641,47 @@ static void fbnic_get_ts_stats(struct net_device *netdev,
|
|||
}
|
||||
}
|
||||
|
||||
static int fbnic_get_tunable(struct net_device *netdev,
|
||||
const struct ethtool_tunable *tun,
|
||||
void *data)
|
||||
{
|
||||
struct fbnic_net *fbn = netdev_priv(netdev);
|
||||
int err = 0;
|
||||
|
||||
switch (tun->id) {
|
||||
case ETHTOOL_PFC_PREVENTION_TOUT:
|
||||
*(u16 *)data = fbn->fbd->ps_timeout;
|
||||
break;
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int fbnic_set_tunable(struct net_device *netdev,
|
||||
const struct ethtool_tunable *tun,
|
||||
const void *data)
|
||||
{
|
||||
struct fbnic_net *fbn = netdev_priv(netdev);
|
||||
int err;
|
||||
|
||||
switch (tun->id) {
|
||||
case ETHTOOL_PFC_PREVENTION_TOUT: {
|
||||
u16 ps_timeout = *(u16 *)data;
|
||||
|
||||
err = fbnic_mac_ps_protect_to_config(fbn->fbd, ps_timeout);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
err = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int
|
||||
fbnic_get_module_eeprom_by_page(struct net_device *netdev,
|
||||
const struct ethtool_module_eeprom *page_data,
|
||||
|
|
@ -1915,6 +1956,8 @@ static const struct ethtool_ops fbnic_ethtool_ops = {
|
|||
.set_channels = fbnic_set_channels,
|
||||
.get_ts_info = fbnic_get_ts_info,
|
||||
.get_ts_stats = fbnic_get_ts_stats,
|
||||
.get_tunable = fbnic_get_tunable,
|
||||
.set_tunable = fbnic_set_tunable,
|
||||
.get_link_ksettings = fbnic_phylink_ethtool_ksettings_get,
|
||||
.get_fec_stats = fbnic_get_fec_stats,
|
||||
.get_fecparam = fbnic_phylink_get_fecparam,
|
||||
|
|
|
|||
|
|
@ -170,6 +170,8 @@ int fbnic_mac_request_irq(struct fbnic_dev *fbd)
|
|||
fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_PCS_IDX),
|
||||
FBNIC_PCS_MSIX_ENTRY | FBNIC_INTR_MSIX_CTRL_ENABLE);
|
||||
|
||||
fbnic_wr32(fbd, FBNIC_INTR_MSIX_CTRL(FBNIC_INTR_MSIX_CTRL_RXB_IDX), 0);
|
||||
|
||||
fbd->mac_msix_vector = vector;
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ static void fbnic_mac_init_qm(struct fbnic_dev *fbd)
|
|||
#define FBNIC_DROP_EN_MASK 0x7d
|
||||
#define FBNIC_PAUSE_EN_MASK 0x14
|
||||
#define FBNIC_ECN_EN_MASK 0x10
|
||||
#define FBNIC_PS_EN_MASK 0x01
|
||||
|
||||
struct fbnic_fifo_config {
|
||||
unsigned int addr;
|
||||
|
|
@ -420,6 +421,14 @@ static void __fbnic_mac_stat_rd64(struct fbnic_dev *fbd, bool reset, u32 reg,
|
|||
#define fbnic_mac_stat_rd64(fbd, reset, __stat, __CSR) \
|
||||
__fbnic_mac_stat_rd64(fbd, reset, FBNIC_##__CSR##_L, &(__stat))
|
||||
|
||||
bool fbnic_mac_check_tx_pause(struct fbnic_dev *fbd)
|
||||
{
|
||||
u32 command_config;
|
||||
|
||||
command_config = rd32(fbd, FBNIC_MAC_COMMAND_CONFIG);
|
||||
return !(command_config & FBNIC_MAC_COMMAND_CONFIG_TX_PAUSE_DIS);
|
||||
}
|
||||
|
||||
static void fbnic_mac_tx_pause_config(struct fbnic_dev *fbd, bool tx_pause)
|
||||
{
|
||||
u32 rxb_pause_ctrl;
|
||||
|
|
@ -434,6 +443,49 @@ static void fbnic_mac_tx_pause_config(struct fbnic_dev *fbd, bool tx_pause)
|
|||
wr32(fbd, FBNIC_RXB_PAUSE_DROP_CTRL, rxb_pause_ctrl);
|
||||
}
|
||||
|
||||
static void
|
||||
fbnic_mac_ps_protect_to_reset(struct fbnic_dev *fbd, u16 timeout_ms)
|
||||
{
|
||||
wr32(fbd, FBNIC_RXB_PAUSE_STORM_UNIT_WR, FBNIC_RXB_PS_CLK_DIV);
|
||||
|
||||
wr32(fbd, FBNIC_RXB_PAUSE_STORM(FBNIC_RXB_INTF_NET),
|
||||
FIELD_PREP(FBNIC_RXB_PAUSE_STORM_THLD_TIME,
|
||||
FBNIC_MAC_RXB_PS_TO(timeout_ms)) |
|
||||
FBNIC_RXB_PAUSE_STORM_FORCE_NORMAL);
|
||||
wrfl(fbd);
|
||||
wr32(fbd, FBNIC_RXB_PAUSE_STORM(FBNIC_RXB_INTF_NET),
|
||||
FIELD_PREP(FBNIC_RXB_PAUSE_STORM_THLD_TIME,
|
||||
FBNIC_MAC_RXB_PS_TO(timeout_ms)));
|
||||
}
|
||||
|
||||
static void
|
||||
fbnic_mac_ps_protect_config(struct fbnic_dev *fbd, bool ps_protect)
|
||||
{
|
||||
u16 timeout;
|
||||
u32 reg;
|
||||
|
||||
ps_protect = ps_protect && fbd->ps_timeout;
|
||||
timeout = ps_protect ? fbd->ps_timeout : FBNIC_MAC_PS_TO_DEFAULT_MS;
|
||||
|
||||
fbnic_mac_ps_protect_to_reset(fbd, timeout);
|
||||
|
||||
reg = rd32(fbd, FBNIC_RXB_PAUSE_DROP_CTRL);
|
||||
reg &= ~FBNIC_RXB_PAUSE_DROP_CTRL_PS_ENABLE;
|
||||
reg |= FIELD_PREP(FBNIC_RXB_PAUSE_DROP_CTRL_PS_ENABLE, ps_protect);
|
||||
wr32(fbd, FBNIC_RXB_PAUSE_DROP_CTRL, reg);
|
||||
|
||||
/* Clear any pending interrupt status first */
|
||||
wr32(fbd, FBNIC_RXB_ERR_INTR_STS,
|
||||
FIELD_PREP(FBNIC_RXB_ERR_INTR_STS_PS, FBNIC_PS_EN_MASK));
|
||||
|
||||
/* Unmask the Network to Host PS interrupt if tx_pause is on */
|
||||
reg = rd32(fbd, FBNIC_RXB_ERR_INTR_MASK);
|
||||
reg |= FBNIC_RXB_ERR_INTR_STS_PS;
|
||||
if (ps_protect)
|
||||
reg &= ~FBNIC_RXB_ERR_INTR_STS_PS;
|
||||
wr32(fbd, FBNIC_RXB_ERR_INTR_MASK, reg);
|
||||
}
|
||||
|
||||
static int fbnic_mac_get_link_event(struct fbnic_dev *fbd)
|
||||
{
|
||||
u32 intr_mask = rd32(fbd, FBNIC_SIG_PCS_INTR_STS);
|
||||
|
|
@ -658,6 +710,7 @@ static void fbnic_mac_link_up_asic(struct fbnic_dev *fbd,
|
|||
u32 cmd_cfg, mac_ctrl;
|
||||
|
||||
fbnic_mac_tx_pause_config(fbd, tx_pause);
|
||||
fbnic_mac_ps_protect_config(fbd, tx_pause);
|
||||
|
||||
cmd_cfg = __fbnic_mac_cmd_config_asic(fbd, tx_pause, rx_pause);
|
||||
mac_ctrl = rd32(fbd, FBNIC_SIG_MAC_IN0);
|
||||
|
|
@ -918,3 +971,46 @@ int fbnic_mac_init(struct fbnic_dev *fbd)
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fbnic_mac_ps_protect_to_config(struct fbnic_dev *fbd, u16 timeout_ms)
|
||||
{
|
||||
u16 old_timeout_ms = fbd->ps_timeout;
|
||||
|
||||
if (timeout_ms == old_timeout_ms)
|
||||
return 0;
|
||||
|
||||
if (timeout_ms == PFC_STORM_PREVENTION_AUTO)
|
||||
timeout_ms = FBNIC_MAC_PS_TO_DEFAULT_MS;
|
||||
|
||||
if (timeout_ms > FBNIC_MAC_PS_TO_MAX_MS)
|
||||
return -EINVAL;
|
||||
|
||||
fbd->ps_timeout = timeout_ms;
|
||||
|
||||
if (!fbnic_mac_check_tx_pause(fbd))
|
||||
return 0;
|
||||
|
||||
if (timeout_ms == 0)
|
||||
fbnic_mac_ps_protect_config(fbd, false);
|
||||
else if (old_timeout_ms == 0)
|
||||
fbnic_mac_ps_protect_config(fbd, true);
|
||||
else
|
||||
fbnic_mac_ps_protect_to_reset(fbd, fbd->ps_timeout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fbnic_mac_ps_protect_handler(struct fbnic_dev *fbd)
|
||||
{
|
||||
u32 rxb_err_sts = rd32(fbd, FBNIC_RXB_ERR_INTR_STS);
|
||||
|
||||
/* Check if pause storm interrupt for network was triggered */
|
||||
if (rxb_err_sts & FIELD_PREP(FBNIC_RXB_ERR_INTR_STS_PS,
|
||||
FBNIC_PS_EN_MASK)) {
|
||||
/* Write 1 to clear the interrupt status first */
|
||||
wr32(fbd, FBNIC_RXB_ERR_INTR_STS,
|
||||
FIELD_PREP(FBNIC_RXB_ERR_INTR_STS_PS, FBNIC_PS_EN_MASK));
|
||||
|
||||
fbnic_mac_ps_protect_to_reset(fbd, fbd->ps_timeout);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,30 @@
|
|||
|
||||
struct fbnic_dev;
|
||||
|
||||
/* The RXB clock runs at 600 MHZ in the ASIC and the PAUSE_STORM_UNIT_WR
|
||||
* is 10us granularity, so set the clock to 6000 (0x1770)
|
||||
*/
|
||||
#define FBNIC_RXB_PS_CLK_DIV 0x1770
|
||||
|
||||
/* Convert milliseconds to pause storm timeout units (10us granularity) */
|
||||
#define FBNIC_MAC_RXB_PS_TO(ms) ((ms) * 100)
|
||||
|
||||
/* Convert pause storm timeout units (10us granularity) to milliseconds */
|
||||
#define FBNIC_MAC_RXB_PS_TO_MS(ps) ((ps) / 100)
|
||||
|
||||
/* Set the default timer to 500ms, which should be longer than any
|
||||
* reasonable period of continuous pausing. The service task, which runs
|
||||
* once per second, periodically resets the pause storm trigger.
|
||||
*
|
||||
* As a result, on a functioning system, if pause continues, we enforce
|
||||
* a duty cycle determined by the configured pause storm timeout (50%
|
||||
* default). A crashed system will not have the service task and therefore
|
||||
* pause will remain disabled until reboot recovery.
|
||||
*/
|
||||
#define FBNIC_MAC_PS_TO_DEFAULT_MS 500
|
||||
#define FBNIC_MAC_PS_TO_MAX_MS \
|
||||
FBNIC_MAC_RXB_PS_TO_MS(FIELD_MAX(FBNIC_RXB_PAUSE_STORM_THLD_TIME))
|
||||
|
||||
#define FBNIC_MAX_JUMBO_FRAME_SIZE 9742
|
||||
|
||||
/* States loosely based on section 136.8.11.7.5 of IEEE 802.3-2022 Ethernet
|
||||
|
|
@ -119,4 +143,7 @@ struct fbnic_mac {
|
|||
|
||||
int fbnic_mac_init(struct fbnic_dev *fbd);
|
||||
void fbnic_mac_get_fw_settings(struct fbnic_dev *fbd, u8 *aui, u8 *fec);
|
||||
int fbnic_mac_ps_protect_to_config(struct fbnic_dev *fbd, u16 timeout);
|
||||
void fbnic_mac_ps_protect_handler(struct fbnic_dev *fbd);
|
||||
bool fbnic_mac_check_tx_pause(struct fbnic_dev *fbd);
|
||||
#endif /* _FBNIC_MAC_H_ */
|
||||
|
|
|
|||
|
|
@ -220,6 +220,9 @@ static void fbnic_service_task(struct work_struct *work)
|
|||
|
||||
fbnic_get_hw_stats32(fbd);
|
||||
|
||||
if (fbd->ps_timeout && fbnic_mac_check_tx_pause(fbd))
|
||||
fbnic_mac_ps_protect_handler(fbd);
|
||||
|
||||
fbnic_fw_check_heartbeat(fbd);
|
||||
|
||||
fbnic_health_check(fbd);
|
||||
|
|
@ -296,6 +299,8 @@ static int fbnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
|||
/* Populate driver with hardware-specific info and handlers */
|
||||
fbd->max_num_queues = info->max_num_queues;
|
||||
|
||||
fbd->ps_timeout = FBNIC_MAC_PS_TO_DEFAULT_MS;
|
||||
|
||||
pci_set_master(pdev);
|
||||
pci_save_state(pdev);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user