Skip to content

Commit

Permalink
cxgbe(4): Add support for NIC suspend/resume and live reset.
Browse files Browse the repository at this point in the history
Add suspend/resume callbacks to the driver and a live reset built around
them.  This commit covers the basic NIC and future commits will expand
this functionality to other stateful parts of the chip.  Suspend and
resume operate on the chip (the t?nex nexus device) and affect all its
ports.  It is not possible to suspend/resume or reset individual ports.
All these operations can be performed on a running NIC.  A reset will
look like a link bounce to the networking stack.

Here are some ways to exercise this functionality:

 /* Manual suspend and resume. */
 # devctl suspend t6nex0
 # devctl resume t6nex0

 /* Manual reset. */
 # devctl reset t6nex0

 /* Manual reset with driver sysctl. */
 # sysctl dev.t6nex.0.reset=1

 /* Automatic adapter reset on any fatal error. */
 # hw.cxgbe.reset_on_fatal_err=1

Suspend disables the adapter (DMA, interrupts, and the port PHYs) and
marks the hardware as unavailable to the driver.  All ifnets associated
with the adapter are still visible to the kernel but operations that
require hardware interaction will fail with ENXIO.  All ifnets report
link-down while the adapter is suspended.

Resume will reattach to the card, reconfigure it as before, and recreate
the queues servicing the existing ifnets.  The ifnets are able to send
and receive traffic as soon as the link comes back up.

Reset is roughly the same as a suspend and a resume with at least one of
these events in between: D0->D3Hot->D0, FLR, PCIe link retrain.

(cherry picked from commit 83b5cda)
  • Loading branch information
np-2020 committed Oct 20, 2021
1 parent 8ec8f56 commit 0d6f2ab
Show file tree
Hide file tree
Showing 6 changed files with 1,393 additions and 306 deletions.
52 changes: 42 additions & 10 deletions sys/dev/cxgbe/adapter.h
Expand Up @@ -155,6 +155,7 @@ enum {
IS_VF = (1 << 7),
KERN_TLS_ON = (1 << 8), /* HW is configured for KERN_TLS */
CXGBE_BUSY = (1 << 9),
HW_OFF_LIMITS = (1 << 10), /* off limits to all except reset_thread */

/* port flags */
HAS_TRACEQ = (1 << 3),
Expand Down Expand Up @@ -945,13 +946,26 @@ struct adapter {
TAILQ_HEAD(, sge_fl) sfl;
struct callout sfl_callout;

struct mtx reg_lock; /* for indirect register access */
/*
* Driver code that can run when the adapter is suspended must use this
* lock or a synchronized_op and check for HW_OFF_LIMITS before
* accessing hardware.
*
* XXX: could be changed to rwlock. wlock in suspend/resume and for
* indirect register access, rlock everywhere else.
*/
struct mtx reg_lock;

struct memwin memwin[NUM_MEMWIN]; /* memory windows */

struct mtx tc_lock;
struct task tc_task;

struct task reset_task;
const void *reset_thread;
int num_resets;
int incarnation;

const char *last_op;
const void *last_op_thr;
int last_op_flags;
Expand Down Expand Up @@ -1041,24 +1055,34 @@ forwarding_intr_to_fwq(struct adapter *sc)
return (sc->intr_count == 1);
}

/* Works reliably inside a sync_op or with reg_lock held. */
static inline bool
hw_off_limits(struct adapter *sc)
{
return (__predict_false(sc->flags & HW_OFF_LIMITS));
}

static inline uint32_t
t4_read_reg(struct adapter *sc, uint32_t reg)
{

if (hw_off_limits(sc))
MPASS(curthread == sc->reset_thread);
return bus_space_read_4(sc->bt, sc->bh, reg);
}

static inline void
t4_write_reg(struct adapter *sc, uint32_t reg, uint32_t val)
{

if (hw_off_limits(sc))
MPASS(curthread == sc->reset_thread);
bus_space_write_4(sc->bt, sc->bh, reg, val);
}

static inline uint64_t
t4_read_reg64(struct adapter *sc, uint32_t reg)
{

if (hw_off_limits(sc))
MPASS(curthread == sc->reset_thread);
#ifdef __LP64__
return bus_space_read_8(sc->bt, sc->bh, reg);
#else
Expand All @@ -1071,7 +1095,8 @@ t4_read_reg64(struct adapter *sc, uint32_t reg)
static inline void
t4_write_reg64(struct adapter *sc, uint32_t reg, uint64_t val)
{

if (hw_off_limits(sc))
MPASS(curthread == sc->reset_thread);
#ifdef __LP64__
bus_space_write_8(sc->bt, sc->bh, reg, val);
#else
Expand All @@ -1083,42 +1108,49 @@ t4_write_reg64(struct adapter *sc, uint32_t reg, uint64_t val)
static inline void
t4_os_pci_read_cfg1(struct adapter *sc, int reg, uint8_t *val)
{

if (hw_off_limits(sc))
MPASS(curthread == sc->reset_thread);
*val = pci_read_config(sc->dev, reg, 1);
}

static inline void
t4_os_pci_write_cfg1(struct adapter *sc, int reg, uint8_t val)
{

if (hw_off_limits(sc))
MPASS(curthread == sc->reset_thread);
pci_write_config(sc->dev, reg, val, 1);
}

static inline void
t4_os_pci_read_cfg2(struct adapter *sc, int reg, uint16_t *val)
{

if (hw_off_limits(sc))
MPASS(curthread == sc->reset_thread);
*val = pci_read_config(sc->dev, reg, 2);
}

static inline void
t4_os_pci_write_cfg2(struct adapter *sc, int reg, uint16_t val)
{

if (hw_off_limits(sc))
MPASS(curthread == sc->reset_thread);
pci_write_config(sc->dev, reg, val, 2);
}

static inline void
t4_os_pci_read_cfg4(struct adapter *sc, int reg, uint32_t *val)
{

if (hw_off_limits(sc))
MPASS(curthread == sc->reset_thread);
*val = pci_read_config(sc->dev, reg, 4);
}

static inline void
t4_os_pci_write_cfg4(struct adapter *sc, int reg, uint32_t val)
{

if (hw_off_limits(sc))
MPASS(curthread == sc->reset_thread);
pci_write_config(sc->dev, reg, val, 4);
}

Expand Down
2 changes: 1 addition & 1 deletion sys/dev/cxgbe/t4_clip.c
Expand Up @@ -171,7 +171,7 @@ update_clip(struct adapter *sc, void *arg __unused)
if (begin_synchronized_op(sc, NULL, HOLD_LOCK, "t4clip"))
return;

if (mtx_initialized(&sc->clip_table_lock))
if (mtx_initialized(&sc->clip_table_lock) && !hw_off_limits(sc))
update_clip_table(sc);

end_synchronized_op(sc, LOCK_HELD);
Expand Down
36 changes: 29 additions & 7 deletions sys/dev/cxgbe/t4_filter.c
Expand Up @@ -522,6 +522,11 @@ set_filter_mode(struct adapter *sc, uint32_t mode)
if (rc)
return (rc);

if (hw_off_limits(sc)) {
rc = ENXIO;
goto done;
}

if (sc->tids.ftids_in_use > 0 || /* TCAM filters active */
sc->tids.hpftids_in_use > 0 || /* hi-pri TCAM filters active */
sc->tids.tids_in_use > 0) { /* TOE or hashfilters active */
Expand Down Expand Up @@ -568,6 +573,11 @@ set_filter_mask(struct adapter *sc, uint32_t mode)
if (rc)
return (rc);

if (hw_off_limits(sc)) {
rc = ENXIO;
goto done;
}

if (sc->tids.tids_in_use > 0) { /* TOE or hashfilters active */
rc = EBUSY;
goto done;
Expand All @@ -589,20 +599,27 @@ static inline uint64_t
get_filter_hits(struct adapter *sc, uint32_t tid)
{
uint32_t tcb_addr;
uint64_t hits;

tcb_addr = t4_read_reg(sc, A_TP_CMM_TCB_BASE) + tid * TCB_SIZE;

if (is_t4(sc)) {
uint64_t hits;
mtx_lock(&sc->reg_lock);
if (hw_off_limits(sc))
hits = 0;
else if (is_t4(sc)) {
uint64_t t;

read_via_memwin(sc, 0, tcb_addr + 16, (uint32_t *)&hits, 8);
return (be64toh(hits));
read_via_memwin(sc, 0, tcb_addr + 16, (uint32_t *)&t, 8);
hits = be64toh(t);
} else {
uint32_t hits;
uint32_t t;

read_via_memwin(sc, 0, tcb_addr + 24, &hits, 4);
return (be32toh(hits));
read_via_memwin(sc, 0, tcb_addr + 24, &t, 4);
hits = be32toh(t);
}
mtx_unlock(&sc->reg_lock);

return (hits);
}

int
Expand Down Expand Up @@ -961,6 +978,11 @@ set_filter(struct adapter *sc, struct t4_filter *t)
if (rc)
return (rc);

if (hw_off_limits(sc)) {
rc = ENXIO;
goto done;
}

if (!(sc->flags & FULL_INIT_DONE) && ((rc = adapter_init(sc)) != 0))
goto done;

Expand Down

0 comments on commit 0d6f2ab

Please sign in to comment.