diff options
author | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2009-10-06 23:34:13 -0400 |
---|---|---|
committer | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2009-10-19 02:56:18 -0400 |
commit | b9ee95010bee6c0e17d18bc9d9c0cfab6e8cb73a (patch) | |
tree | d1fd6706e054bb337d3ac79e96e32d2ec5145047 /drivers/net/wimax | |
parent | 5eeae35b9a2e304fc4ae3d9eed63afeea23b482c (diff) |
wimax/i2400m: fix deadlock: don't do BUS reset under i2400m->init_mutex
Since the addition of the pre/post reset handlers, it became clear
that we cannot do a I2400M-RT-BUS type reset while holding the
init_mutex, as in the case of USB, it will deadlock when trying to
call i2400m_pre_reset().
Thus, the following changes:
- clarify the fact that calling bus_reset() w/ I2400M_RT_BUS while
holding init_mutex is a no-no.
- i2400m_dev_reset_handle() will do a BUS reset to recover a gone
device after unlocking init_mutex.
- in the USB reset implementation, when cold and warm reset fails,
fallback to QUEUING a usb reset, not executing a USB reset, so it
happens from another context and does not deadlock.
Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
Diffstat (limited to 'drivers/net/wimax')
-rw-r--r-- | drivers/net/wimax/i2400m/driver.c | 10 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/i2400m.h | 3 | ||||
-rw-r--r-- | drivers/net/wimax/i2400m/usb.c | 14 |
3 files changed, 20 insertions, 7 deletions
diff --git a/drivers/net/wimax/i2400m/driver.c b/drivers/net/wimax/i2400m/driver.c index 810eda7dbdba..10673af5a7f0 100644 --- a/drivers/net/wimax/i2400m/driver.c +++ b/drivers/net/wimax/i2400m/driver.c | |||
@@ -765,9 +765,7 @@ void __i2400m_dev_reset_handle(struct work_struct *ws) | |||
765 | wmb(); /* see i2400m->updown's documentation */ | 765 | wmb(); /* see i2400m->updown's documentation */ |
766 | dev_err(dev, "%s: cannot start the device: %d\n", | 766 | dev_err(dev, "%s: cannot start the device: %d\n", |
767 | reason, result); | 767 | reason, result); |
768 | result = i2400m->bus_reset(i2400m, I2400M_RT_BUS); | 768 | result = -EUCLEAN; |
769 | if (result >= 0) | ||
770 | result = -ENODEV; | ||
771 | } | 769 | } |
772 | out_unlock: | 770 | out_unlock: |
773 | if (i2400m->reset_ctx) { | 771 | if (i2400m->reset_ctx) { |
@@ -775,6 +773,12 @@ out_unlock: | |||
775 | complete(&ctx->completion); | 773 | complete(&ctx->completion); |
776 | } | 774 | } |
777 | mutex_unlock(&i2400m->init_mutex); | 775 | mutex_unlock(&i2400m->init_mutex); |
776 | if (result == -EUCLEAN) { | ||
777 | /* ops, need to clean up [w/ init_mutex not held] */ | ||
778 | result = i2400m->bus_reset(i2400m, I2400M_RT_BUS); | ||
779 | if (result >= 0) | ||
780 | result = -ENODEV; | ||
781 | } | ||
778 | out: | 782 | out: |
779 | i2400m_put(i2400m); | 783 | i2400m_put(i2400m); |
780 | kfree(iw); | 784 | kfree(iw); |
diff --git a/drivers/net/wimax/i2400m/i2400m.h b/drivers/net/wimax/i2400m/i2400m.h index 8fc8a0ca5126..f5ed7d5cee47 100644 --- a/drivers/net/wimax/i2400m/i2400m.h +++ b/drivers/net/wimax/i2400m/i2400m.h | |||
@@ -281,6 +281,9 @@ struct i2400m_barker_db; | |||
281 | * process, so it cannot rely on common infrastructure being laid | 281 | * process, so it cannot rely on common infrastructure being laid |
282 | * out. | 282 | * out. |
283 | * | 283 | * |
284 | * IMPORTANT: don't call reset on RT_BUS with i2400m->init_mutex | ||
285 | * held, as the .pre/.post reset handlers will deadlock. | ||
286 | * | ||
284 | * @bus_bm_retries: [fill] How many times shall a firmware upload / | 287 | * @bus_bm_retries: [fill] How many times shall a firmware upload / |
285 | * device initialization be retried? Different models of the same | 288 | * device initialization be retried? Different models of the same |
286 | * device might need different values, hence it is set by the | 289 | * device might need different values, hence it is set by the |
diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c index 8b246cc498b1..418db12b0cd7 100644 --- a/drivers/net/wimax/i2400m/usb.c +++ b/drivers/net/wimax/i2400m/usb.c | |||
@@ -254,7 +254,6 @@ int i2400mu_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt) | |||
254 | sizeof(i2400m_COLD_BOOT_BARKER), | 254 | sizeof(i2400m_COLD_BOOT_BARKER), |
255 | i2400mu->endpoint_cfg.reset_cold); | 255 | i2400mu->endpoint_cfg.reset_cold); |
256 | else if (rt == I2400M_RT_BUS) { | 256 | else if (rt == I2400M_RT_BUS) { |
257 | do_bus_reset: | ||
258 | result = usb_reset_device(i2400mu->usb_dev); | 257 | result = usb_reset_device(i2400mu->usb_dev); |
259 | switch (result) { | 258 | switch (result) { |
260 | case 0: | 259 | case 0: |
@@ -262,7 +261,7 @@ do_bus_reset: | |||
262 | case -ENODEV: | 261 | case -ENODEV: |
263 | case -ENOENT: | 262 | case -ENOENT: |
264 | case -ESHUTDOWN: | 263 | case -ESHUTDOWN: |
265 | result = rt == I2400M_RT_WARM ? -ENODEV : 0; | 264 | result = 0; |
266 | break; /* We assume the device is disconnected */ | 265 | break; /* We assume the device is disconnected */ |
267 | default: | 266 | default: |
268 | dev_err(dev, "USB reset failed (%d), giving up!\n", | 267 | dev_err(dev, "USB reset failed (%d), giving up!\n", |
@@ -275,10 +274,17 @@ do_bus_reset: | |||
275 | if (result < 0 | 274 | if (result < 0 |
276 | && result != -EINVAL /* device is gone */ | 275 | && result != -EINVAL /* device is gone */ |
277 | && rt != I2400M_RT_BUS) { | 276 | && rt != I2400M_RT_BUS) { |
277 | /* | ||
278 | * Things failed -- resort to lower level reset, that | ||
279 | * we queue in another context; the reason for this is | ||
280 | * that the pre and post reset functionality requires | ||
281 | * the i2400m->init_mutex; RT_WARM and RT_COLD can | ||
282 | * come from areas where i2400m->init_mutex is taken. | ||
283 | */ | ||
278 | dev_err(dev, "%s reset failed (%d); trying USB reset\n", | 284 | dev_err(dev, "%s reset failed (%d); trying USB reset\n", |
279 | rt == I2400M_RT_WARM ? "warm" : "cold", result); | 285 | rt == I2400M_RT_WARM ? "warm" : "cold", result); |
280 | rt = I2400M_RT_BUS; | 286 | usb_queue_reset_device(i2400mu->usb_iface); |
281 | goto do_bus_reset; | 287 | result = -ENODEV; |
282 | } | 288 | } |
283 | d_fnend(3, dev, "(i2400m %p rt %u) = %d\n", i2400m, rt, result); | 289 | d_fnend(3, dev, "(i2400m %p rt %u) = %d\n", i2400m, rt, result); |
284 | return result; | 290 | return result; |