diff options
author | Michael Albaugh <Michael.Albaugh@QLogic.com> | 2007-03-15 17:45:08 -0400 |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2007-04-18 23:20:58 -0400 |
commit | 27b044a815df7d4530bc68560796680ed588070c (patch) | |
tree | e84d4fe91961ae24ac2d72f12eac368b9b7c29e8 | |
parent | 9783ab405844202b452ac673677e6c8f8c9a6a99 (diff) |
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>
-rw-r--r-- | drivers/infiniband/hw/ipath/ipath_init_chip.c | 45 | ||||
-rw-r--r-- | drivers/infiniband/hw/ipath/ipath_stats.c | 2 |
2 files changed, 35 insertions, 12 deletions
diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c index 72caa9f091f6..7045ba689494 100644 --- a/drivers/infiniband/hw/ipath/ipath_init_chip.c +++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c | |||
@@ -216,6 +216,20 @@ static int bringup_link(struct ipath_devdata *dd) | |||
216 | return ret; | 216 | return ret; |
217 | } | 217 | } |
218 | 218 | ||
219 | static struct ipath_portdata *create_portdata0(struct ipath_devdata *dd) | ||
220 | { | ||
221 | struct ipath_portdata *pd = NULL; | ||
222 | |||
223 | pd = kzalloc(sizeof(*pd), GFP_KERNEL); | ||
224 | if (pd) { | ||
225 | pd->port_dd = dd; | ||
226 | pd->port_cnt = 1; | ||
227 | /* The port 0 pkey table is used by the layer interface. */ | ||
228 | pd->port_pkeys[0] = IPATH_DEFAULT_P_KEY; | ||
229 | } | ||
230 | return pd; | ||
231 | } | ||
232 | |||
219 | static int init_chip_first(struct ipath_devdata *dd, | 233 | static int init_chip_first(struct ipath_devdata *dd, |
220 | struct ipath_portdata **pdp) | 234 | struct ipath_portdata **pdp) |
221 | { | 235 | { |
@@ -271,20 +285,16 @@ static int init_chip_first(struct ipath_devdata *dd, | |||
271 | goto done; | 285 | goto done; |
272 | } | 286 | } |
273 | 287 | ||
274 | dd->ipath_pd[0] = kzalloc(sizeof(*pd), GFP_KERNEL); | 288 | pd = create_portdata0(dd); |
275 | 289 | ||
276 | if (!dd->ipath_pd[0]) { | 290 | if (!pd) { |
277 | ipath_dev_err(dd, "Unable to allocate portdata for port " | 291 | ipath_dev_err(dd, "Unable to allocate portdata for port " |
278 | "0, failing\n"); | 292 | "0, failing\n"); |
279 | ret = -ENOMEM; | 293 | ret = -ENOMEM; |
280 | goto done; | 294 | goto done; |
281 | } | 295 | } |
282 | pd = dd->ipath_pd[0]; | 296 | dd->ipath_pd[0] = pd; |
283 | pd->port_dd = dd; | 297 | |
284 | pd->port_port = 0; | ||
285 | pd->port_cnt = 1; | ||
286 | /* The port 0 pkey table is used by the layer interface. */ | ||
287 | pd->port_pkeys[0] = IPATH_DEFAULT_P_KEY; | ||
288 | dd->ipath_rcvtidcnt = | 298 | dd->ipath_rcvtidcnt = |
289 | ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidcnt); | 299 | ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidcnt); |
290 | dd->ipath_rcvtidbase = | 300 | dd->ipath_rcvtidbase = |
@@ -838,11 +848,24 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit) | |||
838 | * Set up the port 0 (kernel) rcvhdr q and egr TIDs. If doing | 848 | * Set up the port 0 (kernel) rcvhdr q and egr TIDs. If doing |
839 | * re-init, the simplest way to handle this is to free | 849 | * re-init, the simplest way to handle this is to free |
840 | * existing, and re-allocate. | 850 | * existing, and re-allocate. |
851 | * Need to re-create rest of port 0 portdata as well. | ||
841 | */ | 852 | */ |
842 | if (reinit) { | 853 | if (reinit) { |
843 | struct ipath_portdata *pd = dd->ipath_pd[0]; | 854 | /* Alloc and init new ipath_portdata for port0, |
844 | dd->ipath_pd[0] = NULL; | 855 | * Then free old pd. Could lead to fragmentation, but also |
845 | ipath_free_pddata(dd, pd); | 856 | * makes later support for hot-swap easier. |
857 | */ | ||
858 | struct ipath_portdata *npd; | ||
859 | npd = create_portdata0(dd); | ||
860 | if (npd) { | ||
861 | ipath_free_pddata(dd, pd); | ||
862 | dd->ipath_pd[0] = pd = npd; | ||
863 | } else { | ||
864 | ipath_dev_err(dd, "Unable to allocate portdata for" | ||
865 | " port 0, failing\n"); | ||
866 | ret = -ENOMEM; | ||
867 | goto done; | ||
868 | } | ||
846 | } | 869 | } |
847 | dd->ipath_f_tidtemplate(dd); | 870 | dd->ipath_f_tidtemplate(dd); |
848 | ret = ipath_create_rcvhdrq(dd, pd); | 871 | ret = ipath_create_rcvhdrq(dd, pd); |
diff --git a/drivers/infiniband/hw/ipath/ipath_stats.c b/drivers/infiniband/hw/ipath/ipath_stats.c index a627342a969c..9307f7187ca5 100644 --- a/drivers/infiniband/hw/ipath/ipath_stats.c +++ b/drivers/infiniband/hw/ipath/ipath_stats.c | |||
@@ -207,7 +207,7 @@ void ipath_get_faststats(unsigned long opaque) | |||
207 | * don't access the chip while running diags, or memory diags can | 207 | * don't access the chip while running diags, or memory diags can |
208 | * fail | 208 | * fail |
209 | */ | 209 | */ |
210 | if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT) || | 210 | if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_INITTED) || |
211 | ipath_diag_inuse) | 211 | ipath_diag_inuse) |
212 | /* but re-arm the timer, for diags case; won't hurt other */ | 212 | /* but re-arm the timer, for diags case; won't hurt other */ |
213 | goto done; | 213 | goto done; |