aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wimax/i2400m/usb.c
diff options
context:
space:
mode:
authorInaky Perez-Gonzalez <inaky@linux.intel.com>2009-10-06 23:34:13 -0400
committerInaky Perez-Gonzalez <inaky@linux.intel.com>2009-10-19 02:56:18 -0400
commitb9ee95010bee6c0e17d18bc9d9c0cfab6e8cb73a (patch)
treed1fd6706e054bb337d3ac79e96e32d2ec5145047 /drivers/net/wimax/i2400m/usb.c
parent5eeae35b9a2e304fc4ae3d9eed63afeea23b482c (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.c14
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) {
257do_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;