Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 27b044a8 authored by Michael Albaugh's avatar Michael Albaugh Committed by Roland Dreier
Browse files

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: default avatarBryan O'Sullivan <bryan.osullivan@qlogic.com>
Signed-off-by: default avatarRoland Dreier <rolandd@cisco.com>
parent 9783ab40
Loading
Loading
Loading
Loading
+34 −11
Original line number Diff line number Diff line
@@ -216,6 +216,20 @@ static int bringup_link(struct ipath_devdata *dd)
	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,
			   struct ipath_portdata **pdp)
{
@@ -271,20 +285,16 @@ static int init_chip_first(struct ipath_devdata *dd,
		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 "
			      "0, failing\n");
		ret = -ENOMEM;
		goto done;
	}
	pd = dd->ipath_pd[0];
	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_pd[0] = pd;

	dd->ipath_rcvtidcnt =
		ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidcnt);
	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
	 * re-init, the simplest way to handle this is to free
	 * existing, and re-allocate.
	 * Need to re-create rest of port 0 portdata as well.
	 */
	if (reinit) {
		struct ipath_portdata *pd = dd->ipath_pd[0];
		dd->ipath_pd[0] = NULL;
		/* Alloc and init new ipath_portdata for port0,
		 * Then free old pd. Could lead to fragmentation, but also
		 * 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);
	ret = ipath_create_rcvhdrq(dd, pd);
+1 −1
Original line number Diff line number Diff line
@@ -207,7 +207,7 @@ void ipath_get_faststats(unsigned long opaque)
	 * don't access the chip while running diags, or memory diags can
	 * fail
	 */
	if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT) ||
	if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_INITTED) ||
	    ipath_diag_inuse)
		/* but re-arm the timer, for diags case; won't hurt other */
		goto done;