Improve cxgb(4)'s behaviour when faced with temporarily "bouncy" links:
- Run the adapter's tick at 1Hz and remove link state checks from it. Instead, have each port check its link state. Delay the check so that it takes place slightly after the driver is notified of a change in link state. This is a cheap way to debounce these notifications if many are received in rapid succession. POLL_LINK_1ST_TIME flag can also be eliminated as a side effect of these changes. - Do not reset the PHY when link goes down. - Clear port's link_fault flag if the PHY indicates link is down. - get_link_status_r should leave speed and duplex alone when link is down. MFC after: 1 month
This commit is contained in:
parent
2c32b50248
commit
bd1a9fbad6
@ -297,6 +297,9 @@ static int get_link_status_r(struct cphy *phy, int *link_ok, int *speed,
|
||||
if (err)
|
||||
return err;
|
||||
*link_ok = (stat0 & stat1 & (stat2 >> 12)) & 1;
|
||||
|
||||
if (*link_ok == 0)
|
||||
return (0);
|
||||
}
|
||||
if (speed)
|
||||
*speed = SPEED_10000;
|
||||
@ -1947,8 +1950,6 @@ static int ael2020_intr_enable(struct cphy *phy)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
phy->caps |= POLL_LINK_1ST_TIME;
|
||||
|
||||
/* enable standard Link Alarm Status Interrupts */
|
||||
err = t3_phy_lasi_intr_enable(phy);
|
||||
if (err)
|
||||
|
@ -60,7 +60,6 @@ enum {
|
||||
/* skip 25 */
|
||||
SUPPORTED_MISC_IRQ = 1 << 26,
|
||||
SUPPORTED_IRQ = (SUPPORTED_LINK_IRQ | SUPPORTED_MISC_IRQ),
|
||||
POLL_LINK_1ST_TIME = 1 << 27
|
||||
};
|
||||
|
||||
enum { /* adapter interrupt-maintained statistics */
|
||||
|
@ -1530,6 +1530,9 @@ void t3_link_changed(adapter_t *adapter, int port_id)
|
||||
|
||||
phy->ops->get_link_status(phy, &link_ok, &speed, &duplex, &fc);
|
||||
|
||||
if (link_ok == 0)
|
||||
pi->link_fault = LF_NO;
|
||||
|
||||
if (lc->requested_fc & PAUSE_AUTONEG)
|
||||
fc &= lc->requested_fc;
|
||||
else
|
||||
@ -1608,12 +1611,8 @@ void t3_link_changed(adapter_t *adapter, int port_id)
|
||||
F_XGM_INT, 0);
|
||||
}
|
||||
|
||||
if (!link_fault) {
|
||||
if (is_10G(adapter))
|
||||
pi->phy.ops->power_down(&pi->phy, 1);
|
||||
if (!link_fault)
|
||||
t3_mac_disable(mac, MAC_DIRECTION_RX);
|
||||
t3_link_start(phy, mac, lc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure Tx FIFO continues to drain, even as rxen is left
|
||||
@ -2160,13 +2159,14 @@ static int mac_intr_handler(adapter_t *adap, unsigned int idx)
|
||||
mac->stats.xaui_pcs_ctc_err++;
|
||||
if (cause & F_XAUIPCSALIGNCHANGE)
|
||||
mac->stats.xaui_pcs_align_change++;
|
||||
if (cause & F_XGM_INT) {
|
||||
t3_set_reg_field(adap,
|
||||
A_XGM_INT_ENABLE + mac->offset,
|
||||
F_XGM_INT, 0);
|
||||
if (cause & F_XGM_INT &
|
||||
t3_read_reg(adap, A_XGM_INT_ENABLE + mac->offset)) {
|
||||
t3_set_reg_field(adap, A_XGM_INT_ENABLE + mac->offset,
|
||||
F_XGM_INT, 0);
|
||||
|
||||
/* link fault suspected */
|
||||
pi->link_fault = LF_MAYBE;
|
||||
t3_os_link_intr(pi);
|
||||
}
|
||||
|
||||
t3_write_reg(adap, A_XGM_INT_CAUSE + mac->offset, cause);
|
||||
@ -2194,7 +2194,7 @@ static int phy_intr_handler(adapter_t *adapter)
|
||||
int phy_cause = p->phy.ops->intr_handler(&p->phy);
|
||||
|
||||
if (phy_cause & cphy_cause_link_change)
|
||||
t3_link_changed(adapter, i);
|
||||
t3_os_link_intr(p);
|
||||
if (phy_cause & cphy_cause_fifo_error)
|
||||
p->phy.fifo_errors++;
|
||||
if (phy_cause & cphy_cause_module_change)
|
||||
|
@ -106,6 +106,8 @@ struct port_info {
|
||||
int link_fault;
|
||||
|
||||
uint8_t hw_addr[ETHER_ADDR_LEN];
|
||||
struct callout link_check_ch;
|
||||
struct task link_check_task;
|
||||
struct task timer_reclaim_task;
|
||||
struct cdev *port_cdev;
|
||||
|
||||
@ -494,6 +496,7 @@ adap2pinfo(struct adapter *adap, int idx)
|
||||
int t3_os_find_pci_capability(adapter_t *adapter, int cap);
|
||||
int t3_os_pci_save_state(struct adapter *adapter);
|
||||
int t3_os_pci_restore_state(struct adapter *adapter);
|
||||
void t3_os_link_intr(struct port_info *);
|
||||
void t3_os_link_changed(adapter_t *adapter, int port_id, int link_status,
|
||||
int speed, int duplex, int fc, int mac_was_reset);
|
||||
void t3_os_phymod_changed(struct adapter *adap, int port_id);
|
||||
@ -527,10 +530,6 @@ int t3_get_desc(const struct sge_qset *qs, unsigned int qnum, unsigned int idx,
|
||||
unsigned char *data);
|
||||
void t3_update_qset_coalesce(struct sge_qset *qs, const struct qset_params *p);
|
||||
|
||||
#define CXGB_TICKS(a) ((a)->params.linkpoll_period ? \
|
||||
(hz * (a)->params.linkpoll_period) / 10 : \
|
||||
(a)->params.stats_update_period * hz)
|
||||
|
||||
/*
|
||||
* XXX figure out how we can return this to being private to sge
|
||||
*/
|
||||
|
@ -97,6 +97,8 @@ static int setup_sge_qsets(adapter_t *);
|
||||
static void cxgb_async_intr(void *);
|
||||
static void cxgb_tick_handler(void *, int);
|
||||
static void cxgb_tick(void *);
|
||||
static void link_check_callout(void *);
|
||||
static void check_link_status(void *, int);
|
||||
static void setup_rss(adapter_t *sc);
|
||||
static int alloc_filters(struct adapter *);
|
||||
static int setup_hw_filters(struct adapter *);
|
||||
@ -670,7 +672,7 @@ cxgb_controller_attach(device_t dev)
|
||||
sc->params.vpd.port_type[2], sc->params.vpd.port_type[3]);
|
||||
|
||||
device_printf(sc->dev, "Firmware Version %s\n", &sc->fw_version[0]);
|
||||
callout_reset(&sc->cxgb_tick_ch, CXGB_TICKS(sc), cxgb_tick, sc);
|
||||
callout_reset(&sc->cxgb_tick_ch, hz, cxgb_tick, sc);
|
||||
t3_add_attach_sysctls(sc);
|
||||
out:
|
||||
if (error)
|
||||
@ -1007,6 +1009,9 @@ cxgb_port_attach(device_t dev)
|
||||
device_get_unit(device_get_parent(dev)), p->port_id);
|
||||
PORT_LOCK_INIT(p, p->lockbuf);
|
||||
|
||||
callout_init(&p->link_check_ch, CALLOUT_MPSAFE);
|
||||
TASK_INIT(&p->link_check_task, 0, check_link_status, p);
|
||||
|
||||
/* Allocate an ifnet object and set it up */
|
||||
ifp = p->ifp = if_alloc(IFT_ETHER);
|
||||
if (ifp == NULL) {
|
||||
@ -1827,8 +1832,6 @@ cxgb_init_locked(struct port_info *p)
|
||||
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
|
||||
PORT_UNLOCK(p);
|
||||
|
||||
t3_link_changed(sc, p->port_id);
|
||||
|
||||
for (i = p->first_qset; i < p->first_qset + p->nqsets; i++) {
|
||||
struct sge_qset *qs = &sc->sge.qs[i];
|
||||
struct sge_txq *txq = &qs->txq[TXQ_ETH];
|
||||
@ -1839,6 +1842,9 @@ cxgb_init_locked(struct port_info *p)
|
||||
|
||||
/* all ok */
|
||||
setbit(&sc->open_device_map, p->port_id);
|
||||
callout_reset(&p->link_check_ch,
|
||||
p->phy.caps & SUPPORTED_LINK_IRQ ? hz * 3 : hz / 4,
|
||||
link_check_callout, p);
|
||||
|
||||
done:
|
||||
if (may_sleep) {
|
||||
@ -1914,6 +1920,9 @@ cxgb_uninit_synchronized(struct port_info *pi)
|
||||
taskqueue_drain(sc->tq, &sc->slow_intr_task);
|
||||
taskqueue_drain(sc->tq, &sc->tick_task);
|
||||
|
||||
callout_drain(&pi->link_check_ch);
|
||||
taskqueue_drain(sc->tq, &pi->link_check_task);
|
||||
|
||||
PORT_LOCK(pi);
|
||||
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
|
||||
|
||||
@ -2274,33 +2283,42 @@ cxgb_async_intr(void *data)
|
||||
taskqueue_enqueue(sc->tq, &sc->slow_intr_task);
|
||||
}
|
||||
|
||||
static inline int
|
||||
link_poll_needed(struct port_info *p)
|
||||
static void
|
||||
link_check_callout(void *arg)
|
||||
{
|
||||
struct cphy *phy = &p->phy;
|
||||
struct port_info *pi = arg;
|
||||
struct adapter *sc = pi->adapter;
|
||||
|
||||
if (phy->caps & POLL_LINK_1ST_TIME) {
|
||||
p->phy.caps &= ~POLL_LINK_1ST_TIME;
|
||||
return (1);
|
||||
}
|
||||
if (!isset(&sc->open_device_map, pi->port_id))
|
||||
return;
|
||||
|
||||
return (p->link_fault || !(phy->caps & SUPPORTED_LINK_IRQ));
|
||||
taskqueue_enqueue(sc->tq, &pi->link_check_task);
|
||||
}
|
||||
|
||||
static void
|
||||
check_link_status(adapter_t *sc)
|
||||
check_link_status(void *arg, int pending)
|
||||
{
|
||||
int i;
|
||||
struct port_info *pi = arg;
|
||||
struct adapter *sc = pi->adapter;
|
||||
|
||||
for (i = 0; i < (sc)->params.nports; ++i) {
|
||||
struct port_info *p = &sc->port[i];
|
||||
if (!isset(&sc->open_device_map, pi->port_id))
|
||||
return;
|
||||
|
||||
if (!isset(&sc->open_device_map, p->port_id))
|
||||
continue;
|
||||
t3_link_changed(sc, pi->port_id);
|
||||
|
||||
if (link_poll_needed(p))
|
||||
t3_link_changed(sc, i);
|
||||
}
|
||||
if (pi->link_fault || !(pi->phy.caps & SUPPORTED_LINK_IRQ))
|
||||
callout_reset(&pi->link_check_ch, hz, link_check_callout, pi);
|
||||
}
|
||||
|
||||
void
|
||||
t3_os_link_intr(struct port_info *pi)
|
||||
{
|
||||
/*
|
||||
* Schedule a link check in the near future. If the link is flapping
|
||||
* rapidly we'll keep resetting the callout and delaying the check until
|
||||
* things stabilize a bit.
|
||||
*/
|
||||
callout_reset(&pi->link_check_ch, hz / 4, link_check_callout, pi);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2352,7 +2370,7 @@ cxgb_tick(void *arg)
|
||||
return;
|
||||
|
||||
taskqueue_enqueue(sc->tq, &sc->tick_task);
|
||||
callout_reset(&sc->cxgb_tick_ch, CXGB_TICKS(sc), cxgb_tick, sc);
|
||||
callout_reset(&sc->cxgb_tick_ch, hz, cxgb_tick, sc);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2366,8 +2384,6 @@ cxgb_tick_handler(void *arg, int count)
|
||||
if (sc->flags & CXGB_SHUTDOWN || !(sc->flags & FULL_INIT_DONE))
|
||||
return;
|
||||
|
||||
check_link_status(sc);
|
||||
|
||||
if (p->rev == T3_REV_B2 && p->nports < 4 && sc->open_device_map)
|
||||
check_t3b2_mac(sc);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user