ioat: Drain/quiesce the device less racily

On detach and during a forced HW reset.

Sponsored by:	EMC / Isilon Storage Division
This commit is contained in:
Conrad Meyer 2015-10-29 04:16:39 +00:00
parent 79c1a0199f
commit 5f77bd3e24
2 changed files with 55 additions and 14 deletions

View file

@ -96,7 +96,7 @@ static inline struct ioat_softc *ioat_get(struct ioat_softc *,
static inline void ioat_put(struct ioat_softc *, enum ioat_ref_kind);
static inline void ioat_putn(struct ioat_softc *, uint32_t,
enum ioat_ref_kind);
static void ioat_drain(struct ioat_softc *);
static void ioat_drain_locked(struct ioat_softc *);
#define ioat_log_message(v, ...) do { \
if ((v) <= g_ioat_debug_level) { \
@ -271,6 +271,7 @@ ioat_attach(device_t device)
ioat_process_events(ioat);
ioat_setup_sysctl(device);
ioat->chan_idx = ioat_channel_index;
ioat_channel[ioat_channel_index++] = ioat;
ioat_test_attach();
@ -288,7 +289,13 @@ ioat_detach(device_t device)
ioat = DEVICE2SOFTC(device);
ioat_test_detach();
ioat_drain(ioat);
mtx_lock(IOAT_REFLK);
ioat->quiescing = TRUE;
ioat_channel[ioat->chan_idx] = NULL;
ioat_drain_locked(ioat);
mtx_unlock(IOAT_REFLK);
ioat_teardown_intr(ioat);
callout_drain(&ioat->timer);
@ -614,10 +621,16 @@ out:
bus_dmaengine_t
ioat_get_dmaengine(uint32_t index)
{
struct ioat_softc *sc;
if (index >= ioat_channel_index)
return (NULL);
return (&ioat_get(ioat_channel[index], IOAT_DMAENGINE_REF)->dmaengine);
sc = ioat_channel[index];
if (sc == NULL || sc->quiescing)
return (NULL);
return (&ioat_get(sc, IOAT_DMAENGINE_REF)->dmaengine);
}
void
@ -887,6 +900,10 @@ ioat_reserve_space(struct ioat_softc *ioat, uint32_t num_descs, int mflags)
error = EINVAL;
goto out;
}
if (ioat->quiescing) {
error = ENXIO;
goto out;
}
for (;;) {
if (ioat_get_ring_space(ioat) >= num_descs)
@ -1238,6 +1255,12 @@ ioat_reset_hw(struct ioat_softc *ioat)
uint64_t status;
uint32_t chanerr;
unsigned timeout;
int error;
mtx_lock(IOAT_REFLK);
ioat->quiescing = TRUE;
ioat_drain_locked(ioat);
mtx_unlock(IOAT_REFLK);
status = ioat_get_chansts(ioat);
if (is_ioat_active(status) || is_ioat_idle(status))
@ -1249,8 +1272,10 @@ ioat_reset_hw(struct ioat_softc *ioat)
DELAY(1000);
status = ioat_get_chansts(ioat);
}
if (timeout == 20)
return (ETIMEDOUT);
if (timeout == 20) {
error = ETIMEDOUT;
goto out;
}
KASSERT(ioat_get_active(ioat) == 0, ("active after quiesce"));
@ -1280,8 +1305,10 @@ ioat_reset_hw(struct ioat_softc *ioat)
/* Wait at most 20 ms */
for (timeout = 0; ioat_reset_pending(ioat) && timeout < 20; timeout++)
DELAY(1000);
if (timeout == 20)
return (ETIMEDOUT);
if (timeout == 20) {
error = ETIMEDOUT;
goto out;
}
if (ioat_model_resets_msix(ioat)) {
ioat_log_message(1, "device resets registers; restored\n");
@ -1294,13 +1321,16 @@ ioat_reset_hw(struct ioat_softc *ioat)
/* So this really shouldn't happen... */
ioat_log_message(0, "Device is active after a reset?\n");
ioat_write_chanctrl(ioat, IOAT_CHANCTRL_RUN);
return (0);
error = 0;
goto out;
}
chanerr = ioat_read_4(ioat, IOAT_CHANERR_OFFSET);
ioat_halted_debug(ioat, chanerr);
if (chanerr != 0)
return (EIO);
if (chanerr != 0) {
error = EIO;
goto out;
}
/*
* Bring device back online after reset. Writing CHAINADDR brings the
@ -1315,7 +1345,17 @@ ioat_reset_hw(struct ioat_softc *ioat)
ioat_write_chanctrl(ioat, IOAT_CHANCTRL_RUN);
ioat_write_chancmp(ioat, ioat->comp_update_bus_addr);
ioat_write_chainaddr(ioat, ioat->ring[0]->hw_desc_bus_addr);
return (ioat_start_channel(ioat));
error = 0;
out:
mtx_lock(IOAT_REFLK);
ioat->quiescing = FALSE;
mtx_unlock(IOAT_REFLK);
if (error == 0)
error = ioat_start_channel(ioat);
return (error);
}
static int
@ -1456,11 +1496,10 @@ ioat_put(struct ioat_softc *ioat, enum ioat_ref_kind kind)
}
static void
ioat_drain(struct ioat_softc *ioat)
ioat_drain_locked(struct ioat_softc *ioat)
{
mtx_lock(IOAT_REFLK);
mtx_assert(IOAT_REFLK, MA_OWNED);
while (ioat->refcnt > 0)
msleep(IOAT_REFLK, IOAT_REFLK, 0, "ioat_drain", 0);
mtx_unlock(IOAT_REFLK);
}

View file

@ -365,6 +365,7 @@ struct ioat_softc {
})
int version;
int chan_idx;
struct mtx submit_lock;
device_t device;
@ -389,6 +390,7 @@ struct ioat_softc {
struct callout timer;
boolean_t quiescing;
boolean_t is_resize_pending;
boolean_t is_completion_pending;
boolean_t is_reset_pending;