IB/ipath: Fix driver crash (in interrupt or during unload) after chip reset
Re-init of the kernel structures after a chip reset was leaving the portdata structure for port zero in an inconsistent state, and a pointer to it either stale (in re-init code) or NULL (in devdata) Fixing the order of operations on this struct, and the condition for interrupt access, prevents the crashes. Signed-off-by: Bryan O'Sullivan <bryan.osullivan@qlogic.com> Signed-off-by: Roland Dreier <rolandd@cisco.com>
This commit is contained in:
parent
9783ab4058
commit
27b044a815
|
@ -216,6 +216,20 @@ static int bringup_link(struct ipath_devdata *dd)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct ipath_portdata *create_portdata0(struct ipath_devdata *dd)
|
||||||
|
{
|
||||||
|
struct ipath_portdata *pd = NULL;
|
||||||
|
|
||||||
|
pd = kzalloc(sizeof(*pd), GFP_KERNEL);
|
||||||
|
if (pd) {
|
||||||
|
pd->port_dd = dd;
|
||||||
|
pd->port_cnt = 1;
|
||||||
|
/* The port 0 pkey table is used by the layer interface. */
|
||||||
|
pd->port_pkeys[0] = IPATH_DEFAULT_P_KEY;
|
||||||
|
}
|
||||||
|
return pd;
|
||||||
|
}
|
||||||
|
|
||||||
static int init_chip_first(struct ipath_devdata *dd,
|
static int init_chip_first(struct ipath_devdata *dd,
|
||||||
struct ipath_portdata **pdp)
|
struct ipath_portdata **pdp)
|
||||||
{
|
{
|
||||||
|
@ -271,20 +285,16 @@ static int init_chip_first(struct ipath_devdata *dd,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
dd->ipath_pd[0] = kzalloc(sizeof(*pd), GFP_KERNEL);
|
pd = create_portdata0(dd);
|
||||||
|
|
||||||
if (!dd->ipath_pd[0]) {
|
if (!pd) {
|
||||||
ipath_dev_err(dd, "Unable to allocate portdata for port "
|
ipath_dev_err(dd, "Unable to allocate portdata for port "
|
||||||
"0, failing\n");
|
"0, failing\n");
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
pd = dd->ipath_pd[0];
|
dd->ipath_pd[0] = pd;
|
||||||
pd->port_dd = dd;
|
|
||||||
pd->port_port = 0;
|
|
||||||
pd->port_cnt = 1;
|
|
||||||
/* The port 0 pkey table is used by the layer interface. */
|
|
||||||
pd->port_pkeys[0] = IPATH_DEFAULT_P_KEY;
|
|
||||||
dd->ipath_rcvtidcnt =
|
dd->ipath_rcvtidcnt =
|
||||||
ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidcnt);
|
ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidcnt);
|
||||||
dd->ipath_rcvtidbase =
|
dd->ipath_rcvtidbase =
|
||||||
|
@ -838,11 +848,24 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit)
|
||||||
* Set up the port 0 (kernel) rcvhdr q and egr TIDs. If doing
|
* Set up the port 0 (kernel) rcvhdr q and egr TIDs. If doing
|
||||||
* re-init, the simplest way to handle this is to free
|
* re-init, the simplest way to handle this is to free
|
||||||
* existing, and re-allocate.
|
* existing, and re-allocate.
|
||||||
|
* Need to re-create rest of port 0 portdata as well.
|
||||||
*/
|
*/
|
||||||
if (reinit) {
|
if (reinit) {
|
||||||
struct ipath_portdata *pd = dd->ipath_pd[0];
|
/* Alloc and init new ipath_portdata for port0,
|
||||||
dd->ipath_pd[0] = NULL;
|
* Then free old pd. Could lead to fragmentation, but also
|
||||||
ipath_free_pddata(dd, pd);
|
* makes later support for hot-swap easier.
|
||||||
|
*/
|
||||||
|
struct ipath_portdata *npd;
|
||||||
|
npd = create_portdata0(dd);
|
||||||
|
if (npd) {
|
||||||
|
ipath_free_pddata(dd, pd);
|
||||||
|
dd->ipath_pd[0] = pd = npd;
|
||||||
|
} else {
|
||||||
|
ipath_dev_err(dd, "Unable to allocate portdata for"
|
||||||
|
" port 0, failing\n");
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dd->ipath_f_tidtemplate(dd);
|
dd->ipath_f_tidtemplate(dd);
|
||||||
ret = ipath_create_rcvhdrq(dd, pd);
|
ret = ipath_create_rcvhdrq(dd, pd);
|
||||||
|
|
|
@ -207,7 +207,7 @@ void ipath_get_faststats(unsigned long opaque)
|
||||||
* don't access the chip while running diags, or memory diags can
|
* don't access the chip while running diags, or memory diags can
|
||||||
* fail
|
* fail
|
||||||
*/
|
*/
|
||||||
if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT) ||
|
if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_INITTED) ||
|
||||||
ipath_diag_inuse)
|
ipath_diag_inuse)
|
||||||
/* but re-arm the timer, for diags case; won't hurt other */
|
/* but re-arm the timer, for diags case; won't hurt other */
|
||||||
goto done;
|
goto done;
|
||||||
|
|
Loading…
Reference in New Issue