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/i2400m/usb.c | |
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/i2400m/usb.c')
-rw-r--r-- | drivers/net/wimax/i2400m/usb.c | 14 |
1 files changed, 10 insertions, 4 deletions
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; |