Skip to contentSkip to conversation

WiMAX pull request

PATCH In review Inaky Perez-Gonzalez 1 participant 14 messages Nov 4, 2009 – Nov 5, 2009

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

cover WiMAX pull request

The following changes since commit 4a78fd9a736db4c871bc8b583d66b61c38abd299:
Inaky Perez-Gonzalez (1):
wimax/i2400m: fix oops caused by race condition when exiting USB kthreads

are available in the git repository at:

git://git.kernel.org/pub/scm/linux/kernel/git/inaky/wimax.git linux-2.6.33.y

Patches follow for reviewing convenience.

Inaky Perez-Gonzalez (12):
wimax/i2400m: make i2400m->bus_dev_{stop,start}() optional wimax/i2400m: SDIO: fix oops on reset when TXing on uninitialized data wimax/i2400m: implement passive mode as a module option wimax/i2400m: introduce i2400m_reset(), stopping TX and carrier wimax/i2400m: fix device getting stuck in IDLE mode wimax/i2400m: Fix USB timeout specifications (to ms from HZ) wimax/i2400m: correctly identify all iwmc3200-based SKUs wimax/i2400m: don't retry SDIO enable in probe() paths wimax/i2400m: handle USB stalls
wimax/i2400m: fix bad assignment of return value in i2400mu_tx_bulk_out wimax/i2400m: fix SDIO debugfs dentry name
wimax/i2400m: fix inverted value in i2400ms_bus_setup()

 drivers/net/wimax/i2400m/control.c     |   14 +++++-
 drivers/net/wimax/i2400m/debugfs.c     |    2 +-
 drivers/net/wimax/i2400m/driver.c      |   36 +++++++++++---
 drivers/net/wimax/i2400m/fw.c          |    4 +-
 drivers/net/wimax/i2400m/i2400m-sdio.h |   11 ++++
 drivers/net/wimax/i2400m/i2400m.h      |   15 +++---
 drivers/net/wimax/i2400m/netdev.c      |   25 +++++++--
 drivers/net/wimax/i2400m/rx.c          |    4 +-
 drivers/net/wimax/i2400m/sdio.c        |   81 ++++++++++++++------------------
 drivers/net/wimax/i2400m/usb-fw.c      |   25 +++++++++-
 drivers/net/wimax/i2400m/usb-rx.c      |   23 +++++++++-
 drivers/net/wimax/i2400m/usb-tx.c      |   24 +++++++++-
 drivers/net/wimax/i2400m/usb.c         |   61 +++++++++++++++++++++---
 13 files changed, 240 insertions(+), 85 deletions(-)
#1 lore raw

PATCH 1/12 wimax/i2400m: make i2400m->bus_dev_{stop,start}() optional

In coming commits, the i2400m SDIO driver will not use i2400m->bus_dev_stop().

Thus changed to check before calling, as an empty stub has more overhead than a call to check if the function pointer is non-NULL.

---
 drivers/net/wimax/i2400m/driver.c |   14 +++++++++-----
 drivers/net/wimax/i2400m/i2400m.h |   14 +++++++-------
 2 files changed, 16 insertions(+), 12 deletions(-)
drivers/net/wimax/i2400m/driver.c +9 -5
index cc900b9..cc58a86 100644
@@ -384,9 +384,11 @@ retry:
 		dev_err(dev, "cannot create workqueue\n");
 		goto error_create_workqueue;
 	}
-	result = i2400m->bus_dev_start(i2400m);
-	if (result < 0)
-		goto error_bus_dev_start;
+	if (i2400m->bus_dev_start) {
+		result = i2400m->bus_dev_start(i2400m);
+		if (result < 0)
+			goto error_bus_dev_start;
+	}
 	i2400m->ready = 1;
 	wmb();		/* see i2400m->ready's documentation  */
 	/* process pending reports from the device */
@@ -413,7 +415,8 @@ error_check_mac_addr:
 	wmb();		/* see i2400m->ready's documentation  */
 	flush_workqueue(i2400m->work_queue);
 error_fw_check:
-	i2400m->bus_dev_stop(i2400m);
+	if (i2400m->bus_dev_stop)
+		i2400m->bus_dev_stop(i2400m);
 error_bus_dev_start:
 	destroy_workqueue(i2400m->work_queue);
 error_create_workqueue:
@@ -480,7 +483,8 @@ void __i2400m_dev_stop(struct i2400m *i2400m)
 	wmb();		/* see i2400m->ready's documentation  */
 	flush_workqueue(i2400m->work_queue);
 
-	i2400m->bus_dev_stop(i2400m);
+	if (i2400m->bus_dev_stop)
+		i2400m->bus_dev_stop(i2400m);
 	destroy_workqueue(i2400m->work_queue);
 	i2400m_rx_release(i2400m);
 	i2400m_tx_release(i2400m);
drivers/net/wimax/i2400m/i2400m.h +7 -7
index 55bca43..5eee985 100644
@@ -245,19 +245,19 @@ struct i2400m_barker_db;
  *     all the host resources created to  handle communication with
  *     the device.
  *
- * @bus_dev_start: [fill] Function called by the bus-generic code
- *     [i2400m_dev_start()] to setup the bus-specific communications
- *     to the the device. See LIFE CYCLE above.
+ * @bus_dev_start: [optional fill] Function called by the bus-generic
+ *     code [i2400m_dev_start()] to do things needed to start the
+ *     device. See LIFE CYCLE above.
  *
  *     NOTE: Doesn't need to upload the firmware, as that is taken
  *     care of by the bus-generic code.
  *
- * @bus_dev_stop: [fill] Function called by the bus-generic code
- *     [i2400m_dev_stop()] to shutdown the bus-specific communications
- *     to the the device. See LIFE CYCLE above.
+ * @bus_dev_stop: [optional fill] Function called by the bus-generic
+ *     code [i2400m_dev_stop()] to do things needed for stopping the
+ *     device. See LIFE CYCLE above.
  *
  *     This function does not need to reset the device, just tear down
- *     all the host resources created to  handle communication with
+ *     all the host resources created to handle communication with
  *     the device.
  *
  * @bus_tx_kick: [fill] Function called by the bus-generic code to let
Signature · 1 line
1.6.2.5
#2 lore raw

PATCH 2/12 wimax/i2400m: SDIO: fix oops on reset when TXing on uninitialized data

In reply to Inaky Perez-Gonzalez

Currently the SDIO part of the TX resources were initialized/released with bus_dev_{start,stop}.

The generic code's TX subsystem is destroyed afterwards, so there is a window from the bus-TX destruction to the generic-TX destruction where the generic-TX code might call into bus-TX to do transactions.

The SDIO code cannot really cope with this (whereas in USB, how it is laid out, it correctly ignores it). In any case, it made no sense for the SDIO TX code to be in i2400m->bus_dev_start/stop(), so moved to i2400m->bus_setup/release(), which also takes care of the oops.

---
 drivers/net/wimax/i2400m/sdio.c |   43 ++++++++------------------------------
 1 files changed, 9 insertions(+), 34 deletions(-)
drivers/net/wimax/i2400m/sdio.c +9 -34
index 14e66f0..ec17892 100644
@@ -46,15 +46,6 @@
  * i2400ms_bus_reset()            Called by i2400m->bus_reset
  *   __i2400ms_reset()
  *     __i2400ms_send_barker()
- *
- * i2400ms_bus_dev_start()        Called by i2400m_dev_start() [who is
- *   i2400ms_tx_setup()           called by i2400m_setup()]
- *   i2400ms_rx_setup()
- *
- * i2400ms_bus_dev_stop()         Called by i2400m_dev_stop() [who is
- *   i2400ms_rx_release()         is called by i2400m_release()]
- *   i2400ms_tx_release()
- *
  */
 
 #include <linux/debugfs.h>
@@ -191,12 +182,17 @@ int i2400ms_bus_setup(struct i2400m *i2400m)
 		goto error_func_enable;
 	}
 
+	result = i2400ms_tx_setup(i2400ms);
+	if (result < 0)
+		goto error_tx_setup;
 	result = i2400ms_rx_setup(i2400ms);
 	if (result < 0)
 		goto error_rx_setup;
 	return 0;
 
 error_rx_setup:
+	i2400ms_tx_release(i2400ms);
+error_tx_setup:
 	sdio_claim_host(func);
 	sdio_disable_func(func);
 	sdio_release_host(func);
@@ -218,6 +214,7 @@ void i2400ms_bus_release(struct i2400m *i2400m)
 	struct sdio_func *func = i2400ms->func;
 
 	i2400ms_rx_release(i2400ms);
+	i2400ms_tx_release(i2400ms);
 	sdio_claim_host(func);
 	sdio_disable_func(func);
 	sdio_release_host(func);
@@ -235,36 +232,14 @@ void i2400ms_bus_release(struct i2400m *i2400m)
 static
 int i2400ms_bus_dev_start(struct i2400m *i2400m)
 {
-	int result;
 	struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m);
 	struct sdio_func *func = i2400ms->func;
 	struct device *dev = &func->dev;
 
 	d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
 	msleep(200);
-	result = i2400ms_tx_setup(i2400ms);
-	if (result < 0)
-		goto error_tx_setup;
-	d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
-	return result;
-
-error_tx_setup:
-	i2400ms_tx_release(i2400ms);
-	d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
-	return result;
-}
-
-
-static
-void i2400ms_bus_dev_stop(struct i2400m *i2400m)
-{
-	struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m);
-	struct sdio_func *func = i2400ms->func;
-	struct device *dev = &func->dev;
-
-	d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
-	i2400ms_tx_release(i2400ms);
-	d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
+	d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, 0);
+	return 0;
 }
 
 
@@ -506,7 +481,7 @@ int i2400ms_probe(struct sdio_func *func,
 	i2400m->bus_pl_size_max = I2400MS_PL_SIZE_MAX;
 	i2400m->bus_setup = i2400ms_bus_setup;
 	i2400m->bus_dev_start = i2400ms_bus_dev_start;
-	i2400m->bus_dev_stop = i2400ms_bus_dev_stop;
+	i2400m->bus_dev_stop = NULL;
 	i2400m->bus_release = i2400ms_bus_release;
 	i2400m->bus_tx_kick = i2400ms_bus_tx_kick;
 	i2400m->bus_reset = i2400ms_bus_reset;
Signature · 1 line
1.6.2.5
#3 lore raw

PATCH 3/12 wimax/i2400m: implement passive mode as a module option

In reply to Inaky Perez-Gonzalez

Some versions of the user space Intel WiMAX daemon need to have full control over the device initialization sequence. By setting the module option i2400.passive_mode to 1, the driver defers all device configuration and initialization to user space.

---
 drivers/net/wimax/i2400m/control.c |   10 ++++++++++
 1 files changed, 10 insertions(+), 0 deletions(-)
drivers/net/wimax/i2400m/control.c +10 -0
index 8fe70e7..b69fd88 100644
@@ -82,6 +82,13 @@
 #define D_SUBMODULE control
 #include "debug-levels.h"
 
+int i2400m_passive_mode;	/* 0 (passive mode disabled) by default */
+module_param_named(passive_mode, i2400m_passive_mode, int, 0644);
+MODULE_PARM_DESC(passive_mode,
+		 "If true, the driver will not do any device setup "
+		 "and leave it up to user space, who must be properly "
+		 "setup.");
+
 
 /*
  * Return if a TLV is of a give type and size
@@ -1335,6 +1342,8 @@ int i2400m_dev_initialize(struct i2400m *i2400m)
 	unsigned argc = 0;
 
 	d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
+	if (i2400m_passive_mode)
+		goto out_passive;
 	/* Disable idle mode? (enabled by default) */
 	if (i2400m_idle_mode_disabled) {
 		if (i2400m_le_v1_3(i2400m)) {
@@ -1377,6 +1386,7 @@ int i2400m_dev_initialize(struct i2400m *i2400m)
 	result = i2400m_set_init_config(i2400m, args, argc);
 	if (result < 0)
 		goto error;
+out_passive:
 	/*
 	 * Update state: Here it just calls a get state; parsing the
 	 * result (System State TLV and RF Status TLV [done in the rx
Signature · 1 line
1.6.2.5
#4 lore raw

PATCH 4/12 wimax/i2400m: introduce i2400m_reset(), stopping TX and carrier

In reply to Inaky Perez-Gonzalez

Currently the i2400m driver was resetting by just calling i2400m->bus_reset(). However, this was missing stopping the TX queue and downing the carrier. This was causing, for the corner case of the driver reseting a device that refuses to go out of idle mode, that a few packets would be queued and more than one reset would go through, making the recovery a wee bit messy.

To avoid introducing the same cleanup in all the bus-specific driver, introduced a i2400m_reset() function that takes care of house cleaning and then calling the bus-level reset implementation.

The bulk of the changes in all files are just to rename the call from i2400m->bus_reset() to i2400m_reset().

---
 drivers/net/wimax/i2400m/control.c |    4 ++--
 drivers/net/wimax/i2400m/debugfs.c |    2 +-
 drivers/net/wimax/i2400m/driver.c  |   22 ++++++++++++++++++++--
 drivers/net/wimax/i2400m/fw.c      |    4 ++--
 drivers/net/wimax/i2400m/i2400m.h  |    1 +
 drivers/net/wimax/i2400m/netdev.c  |    8 +++++---
 drivers/net/wimax/i2400m/rx.c      |    4 ++--
 drivers/net/wimax/i2400m/sdio.c    |    9 +--------
 drivers/net/wimax/i2400m/usb.c     |    2 +-
 9 files changed, 35 insertions(+), 21 deletions(-)
drivers/net/wimax/i2400m/control.c +2 -2
index b69fd88..9449455 100644
@@ -54,7 +54,7 @@
  *   i2400m_set_init_config()
  *   i2400m_cmd_get_state()
  * i2400m_dev_shutdown()        Called by i2400m_dev_stop()
- *   i2400m->bus_reset()
+ *   i2400m_reset()
  *
  * i2400m_{cmd,get,set}_*()
  *   i2400m_msg_to_dev()
@@ -343,7 +343,7 @@ void i2400m_report_tlv_system_state(struct i2400m *i2400m,
 		/* Huh? just in case, shut it down */
 		dev_err(dev, "HW BUG? unknown state %u: shutting down\n",
 			i2400m_state);
-		i2400m->bus_reset(i2400m, I2400M_RT_WARM);
+		i2400m_reset(i2400m, I2400M_RT_WARM);
 		break;
 	};
 	d_fnend(3, dev, "(i2400m %p ss %p [%u]) = void\n",
drivers/net/wimax/i2400m/debugfs.c +1 -1
index 9b81af3..b1aec3e 100644
@@ -214,7 +214,7 @@ int debugfs_i2400m_reset_set(void *data, u64 val)
 	case I2400M_RT_WARM:
 	case I2400M_RT_COLD:
 	case I2400M_RT_BUS:
-		result = i2400m->bus_reset(i2400m, rt);
+		result = i2400m_reset(i2400m, rt);
 		if (result >= 0)
 			result = 0;
 	default:
drivers/net/wimax/i2400m/driver.c +20 -2
index cc58a86..96a615f 100644
@@ -255,7 +255,7 @@ int i2400m_op_reset(struct wimax_dev *wimax_dev)
 	mutex_lock(&i2400m->init_mutex);
 	i2400m->reset_ctx = &ctx;
 	mutex_unlock(&i2400m->init_mutex);
-	result = i2400m->bus_reset(i2400m, I2400M_RT_WARM);
+	result = i2400m_reset(i2400m, I2400M_RT_WARM);
 	if (result < 0)
 		goto out;
 	result = wait_for_completion_timeout(&ctx.completion, 4*HZ);
@@ -710,7 +710,7 @@ out_unlock:
 	mutex_unlock(&i2400m->init_mutex);
 	if (result == -EUCLEAN) {
 		/* ops, need to clean up [w/ init_mutex not held] */
-		result = i2400m->bus_reset(i2400m, I2400M_RT_BUS);
+		result = i2400m_reset(i2400m, I2400M_RT_BUS);
 		if (result >= 0)
 			result = -ENODEV;
 	}
@@ -815,6 +815,24 @@ void i2400m_init(struct i2400m *i2400m)
 EXPORT_SYMBOL_GPL(i2400m_init);
 
 
+int i2400m_reset(struct i2400m *i2400m, enum i2400m_reset_type rt)
+{
+	struct net_device *net_dev = i2400m->wimax_dev.net_dev;
+
+	/*
+	 * Make sure we stop TXs and down the carrier before
+	 * resetting; this is needed to avoid things like
+	 * i2400m_wake_tx() scheduling stuff in parallel.
+	 */
+	if (net_dev->reg_state == NETREG_REGISTERED) {
+		netif_tx_disable(net_dev);
+		netif_carrier_off(net_dev);
+	}
+	return i2400m->bus_reset(i2400m, rt);
+}
+EXPORT_SYMBOL_GPL(i2400m_reset);
+
+
 /**
  * i2400m_setup - bus-generic setup function for the i2400m device
  *
drivers/net/wimax/i2400m/fw.c +2 -2
index fda54bf..64cdfeb 100644
@@ -130,7 +130,7 @@
  * i2400m_fw_dnload
  *   i2400m_bootrom_init
  *     i2400m_bm_cmd
- *     i2400m->bus_reset
+ *     i2400m_reset
  *   i2400m_dnload_init
  *     i2400m_dnload_init_signed
  *     i2400m_dnload_init_nonsigned
@@ -902,7 +902,7 @@ do_reboot:
 	d_printf(4, dev, "device reboot: reboot command [%d # left]\n",
 		 count);
 	if ((flags & I2400M_BRI_NO_REBOOT) == 0)
-		i2400m->bus_reset(i2400m, I2400M_RT_WARM);
+		i2400m_reset(i2400m, I2400M_RT_WARM);
 	result = i2400m_bm_cmd(i2400m, NULL, 0, &ack, sizeof(ack),
 			       I2400M_BM_CMD_RAW);
 	flags &= ~I2400M_BRI_NO_REBOOT;
drivers/net/wimax/i2400m/i2400m.h +1 -0
index 5eee985..04df9bb 100644
@@ -717,6 +717,7 @@ unsigned i2400m_brh_get_signature(const struct i2400m_bootrom_header *hdr)
  * Driver / device setup and internal functions
  */
 extern void i2400m_init(struct i2400m *);
+extern int i2400m_reset(struct i2400m *, enum i2400m_reset_type);
 extern void i2400m_netdev_setup(struct net_device *net_dev);
 extern int i2400m_sysfs_setup(struct device_driver *);
 extern void i2400m_sysfs_release(struct device_driver *);
drivers/net/wimax/i2400m/netdev.c +5 -3
index e7d1a51..f67af42 100644
@@ -171,8 +171,9 @@ void i2400m_wake_tx_work(struct work_struct *ws)
 		result = 0;
 	if (result < 0) {
 		dev_err(dev, "WAKE&TX: device didn't get out of idle: "
-			"%d\n", result);
-			goto error;
+			"%d - resetting\n", result);
+		i2400m_reset(i2400m, I2400M_RT_BUS);
+		goto error;
 	}
 	result = wait_event_timeout(i2400m->state_wq,
 				    i2400m->state != I2400M_SS_IDLE, 5 * HZ);
@@ -180,7 +181,8 @@ void i2400m_wake_tx_work(struct work_struct *ws)
 		result = -ETIMEDOUT;
 	if (result < 0) {
 		dev_err(dev, "WAKE&TX: error waiting for device to exit IDLE: "
-			"%d\n", result);
+			"%d - resetting\n", result);
+		i2400m_reset(i2400m, I2400M_RT_BUS);
 		goto error;
 	}
 	msleep(20);	/* device still needs some time or it drops it */
drivers/net/wimax/i2400m/rx.c +2 -2
index 64a44ca..e3d2a9d 100644
@@ -828,7 +828,7 @@ void i2400m_roq_queue(struct i2400m *i2400m, struct i2400m_roq *roq,
 		dev_err(dev, "SW BUG? queue nsn %d (lbn %u ws %u)\n",
 			nsn, lbn, roq->ws);
 		i2400m_roq_log_dump(i2400m, roq);
-		i2400m->bus_reset(i2400m, I2400M_RT_WARM);
+		i2400m_reset(i2400m, I2400M_RT_WARM);
 	} else {
 		__i2400m_roq_queue(i2400m, roq, skb, lbn, nsn);
 		i2400m_roq_log_add(i2400m, roq, I2400M_RO_TYPE_PACKET,
@@ -894,7 +894,7 @@ void i2400m_roq_queue_update_ws(struct i2400m *i2400m, struct i2400m_roq *roq,
 		dev_err(dev, "SW BUG? queue_update_ws nsn %u (sn %u ws %u)\n",
 			nsn, sn, roq->ws);
 		i2400m_roq_log_dump(i2400m, roq);
-		i2400m->bus_reset(i2400m, I2400M_RT_WARM);
+		i2400m_reset(i2400m, I2400M_RT_WARM);
 	} else {
 		/* if the queue is empty, don't bother as we'd queue
 		 * it and inmediately unqueue it -- just deliver it */
drivers/net/wimax/i2400m/sdio.c +1 -8
index ec17892..20ab22e 100644
@@ -43,7 +43,7 @@
  *     i2400m_release()
  *     free_netdev(net_dev)
  *
- * i2400ms_bus_reset()            Called by i2400m->bus_reset
+ * i2400ms_bus_reset()            Called by i2400m_reset
  *   __i2400ms_reset()
  *     __i2400ms_send_barker()
  */
@@ -342,13 +342,6 @@ int i2400ms_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt)
 					       sizeof(i2400m_COLD_BOOT_BARKER));
 	else if (rt == I2400M_RT_BUS) {
 do_bus_reset:
-		/* call netif_tx_disable() before sending IOE disable,
-		 * so that all the tx from network layer are stopped
-		 * while IOE is being reset. Make sure it is called
-		 * only after register_netdev() was issued.
-		 */
-		if (i2400m->wimax_dev.net_dev->reg_state == NETREG_REGISTERED)
-			netif_tx_disable(i2400m->wimax_dev.net_dev);
 
 		i2400ms_bus_release(i2400m);
 
drivers/net/wimax/i2400m/usb.c +1 -1
index 418db12..5e07940 100644
@@ -58,7 +58,7 @@
  *   i2400mu_rx_release()
  *   i2400mu_tx_release()
  *
- * i2400mu_bus_reset()            Called by i2400m->bus_reset
+ * i2400mu_bus_reset()            Called by i2400m_reset
  *   __i2400mu_reset()
  *     __i2400mu_send_barker()
  *   usb_reset_device()
Signature · 1 line
1.6.2.5
#5 lore raw

PATCH 5/12 wimax/i2400m: fix device getting stuck in IDLE mode

In reply to Inaky Perez-Gonzalez

The i2400m, when conected, will negotiate with the WiMAX basestation to put the link in IDLE mode when it is not being used. Upon RX/TX traffic, the link has to be restablished and that might require some crypto handshakes and maybe a DHCP renew.

This process might take up to 20 (!) seconds and in some cases we were seeing network watchdog warnings that weren't needed.

So the network watchdog timeout is updated to be slightly above that 20s threshold. As well, the driver itself will double check if the device is stuck in IDLE mode -- if that happens, the device will be reset (in this case the queue is also woken up to remove bogus--once the device is reset--warnings).

---
 drivers/net/wimax/i2400m/netdev.c |   17 ++++++++++++++---
 1 files changed, 14 insertions(+), 3 deletions(-)
drivers/net/wimax/i2400m/netdev.c +14 -3
index f67af42..599aa4e 100644
@@ -89,7 +89,10 @@ enum {
 	 * The MTU is 1400 or less
 	 */
 	I2400M_MAX_MTU = 1400,
-	I2400M_TX_TIMEOUT = HZ,
+	/* 20 secs? yep, this is the maximum timeout that the device
+	 * might take to get out of IDLE / negotiate it with the base
+	 * station. We add 1sec for good measure. */
+	I2400M_TX_TIMEOUT = 21 * HZ,
 	I2400M_TX_QLEN = 5,
 };
 
@@ -151,6 +154,7 @@ void i2400m_wake_tx_work(struct work_struct *ws)
 {
 	int result;
 	struct i2400m *i2400m = container_of(ws, struct i2400m, wake_tx_ws);
+	struct net_device *net_dev = i2400m->wimax_dev.net_dev;
 	struct device *dev = i2400m_dev(i2400m);
 	struct sk_buff *skb = i2400m->wake_tx_skb;
 	unsigned long flags;
@@ -166,6 +170,11 @@ void i2400m_wake_tx_work(struct work_struct *ws)
 		dev_err(dev, "WAKE&TX: skb dissapeared!\n");
 		goto out_put;
 	}
+	/* If we have, somehow, lost the connection after this was
+	 * queued, don't do anything; this might be the device got
+	 * reset or just disconnected. */
+	if (unlikely(!netif_carrier_ok(net_dev)))
+		goto out_kfree;
 	result = i2400m_cmd_exit_idle(i2400m);
 	if (result == -EILSEQ)
 		result = 0;
@@ -176,7 +185,8 @@ void i2400m_wake_tx_work(struct work_struct *ws)
 		goto error;
 	}
 	result = wait_event_timeout(i2400m->state_wq,
-				    i2400m->state != I2400M_SS_IDLE, 5 * HZ);
+				    i2400m->state != I2400M_SS_IDLE,
+				    net_dev->watchdog_timeo - HZ/2);
 	if (result == 0)
 		result = -ETIMEDOUT;
 	if (result < 0) {
@@ -187,8 +197,9 @@ void i2400m_wake_tx_work(struct work_struct *ws)
 	}
 	msleep(20);	/* device still needs some time or it drops it */
 	result = i2400m_tx(i2400m, skb->data, skb->len, I2400M_PT_DATA);
-	netif_wake_queue(i2400m->wimax_dev.net_dev);
 error:
+	netif_wake_queue(net_dev);
+out_kfree:
 	kfree_skb(skb);	/* refcount transferred by _hard_start_xmit() */
 out_put:
 	i2400m_put(i2400m);
Signature · 1 line
1.6.2.5
#6 lore raw

PATCH 6/12 wimax/i2400m: Fix USB timeout specifications (to ms from HZ)

In reply to Inaky Perez-Gonzalez

The USB code was incorrectly specifiying timeouts to be in jiffies vs msecs. On top of that, lower it to 200ms, as 1s is really too long (doesn't allow the watchdog to trip a reset if the device timesout too often).

---
 drivers/net/wimax/i2400m/usb-fw.c |    2 +-
 drivers/net/wimax/i2400m/usb-rx.c |    2 +-
 drivers/net/wimax/i2400m/usb-tx.c |    2 +-
 drivers/net/wimax/i2400m/usb.c    |    2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)
drivers/net/wimax/i2400m/usb-fw.c +1 -1
index b59aee0..8ec8b6d 100644
@@ -102,7 +102,7 @@ ssize_t i2400mu_tx_bulk_out(struct i2400mu *i2400mu, void *buf, size_t buf_size)
 	epd = usb_get_epd(i2400mu->usb_iface, i2400mu->endpoint_cfg.bulk_out);
 	pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
 retry:
-	result = usb_bulk_msg(i2400mu->usb_dev, pipe, buf, buf_size, &len, HZ);
+	result = usb_bulk_msg(i2400mu->usb_dev, pipe, buf, buf_size, &len, 200);
 	switch (result) {
 	case 0:
 		if (len != buf_size) {
drivers/net/wimax/i2400m/usb-rx.c +1 -1
index 245587f..22d127b 100644
@@ -214,7 +214,7 @@ retry:
 	}
 	result = usb_bulk_msg(
 		i2400mu->usb_dev, usb_pipe, rx_skb->data + rx_skb->len,
-		rx_size, &read_size, HZ);
+		rx_size, &read_size, 200);
 	usb_mark_last_busy(i2400mu->usb_dev);
 	switch (result) {
 	case 0:
drivers/net/wimax/i2400m/usb-tx.c +1 -1
index a3c46e9..6cdf003 100644
@@ -105,7 +105,7 @@ int i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg,
 	usb_pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
 retry:
 	result = usb_bulk_msg(i2400mu->usb_dev, usb_pipe,
-			      tx_msg, tx_msg_size, &sent_size, HZ);
+			      tx_msg, tx_msg_size, &sent_size, 200);
 	usb_mark_last_busy(i2400mu->usb_dev);
 	switch (result) {
 	case 0:
drivers/net/wimax/i2400m/usb.c +1 -1
index 5e07940..34df090 100644
@@ -173,7 +173,7 @@ int __i2400mu_send_barker(struct i2400mu *i2400mu,
 	pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
 	memcpy(buffer, barker, barker_size);
 	ret = usb_bulk_msg(i2400mu->usb_dev, pipe, buffer, barker_size,
-			   &actual_len, HZ);
+			   &actual_len, 200);
 	if (ret < 0) {
 		if (ret != -EINVAL)
 			dev_err(dev, "E: barker error: %d\n", ret);
Signature · 1 line
1.6.2.5
#7 lore raw

PATCH 7/12 wimax/i2400m: correctly identify all iwmc3200-based SKUs

In reply to Inaky Perez-Gonzalez

Different paths of the i2400m SDIO driver need to take care of a few SKU-specific quirks. For the ones that are common to to all the iwmc3200 based devices, introduce i2400ms->iwmc3200 [set in i2400ms_probe()], so it doesn't have to check against the list of iwmc3200 SKU IDs on each quirk site.

---
 drivers/net/wimax/i2400m/i2400m-sdio.h |    3 +++
 drivers/net/wimax/i2400m/sdio.c        |   16 +++++++++++++---
 2 files changed, 16 insertions(+), 3 deletions(-)
drivers/net/wimax/i2400m/i2400m-sdio.h +3 -0
index 18218a2..fba482c 100644
@@ -114,6 +114,9 @@ struct i2400ms {
 	wait_queue_head_t bm_wfa_wq;
 	int bm_wait_result;
 	size_t bm_ack_size;
+
+	/* Device is any of the iwmc3200 SKUs */
+	unsigned iwmc3200:1;
 };
 
 
drivers/net/wimax/i2400m/sdio.c +13 -3
index 20ab22e..079f900 100644
@@ -105,8 +105,9 @@ static const struct i2400m_poke_table i2400ms_pokes[] = {
  *     error (-ENODEV when it was unable to enable the function).
  */
 static
-int i2400ms_enable_function(struct sdio_func *func, unsigned maxtries)
+int i2400ms_enable_function(struct i2400ms *i2400ms, unsigned maxtries)
 {
+	struct sdio_func *func = i2400ms->func;
 	u64 timeout;
 	int err;
 	struct device *dev = &func->dev;
@@ -126,7 +127,7 @@ int i2400ms_enable_function(struct sdio_func *func, unsigned maxtries)
 		 * platforms (system hang). We explicitly overwrite
 		 * func->enable_timeout here to work around the issue.
 		 */
-		if (func->device == SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX)
+		if (i2400ms->iwmc3200)
 			func->enable_timeout = IWMC3200_IOR_TIMEOUT;
 		err = sdio_enable_func(func);
 		if (0 == err) {
@@ -176,7 +177,7 @@ int i2400ms_bus_setup(struct i2400m *i2400m)
 		goto error_set_blk_size;
 	}
 
-	result = i2400ms_enable_function(func, 1);
+	result = i2400ms_enable_function(i2400ms, 1);
 	if (result < 0) {
 		dev_err(dev, "Cannot enable SDIO function: %d\n", result);
 		goto error_func_enable;
@@ -487,6 +488,15 @@ int i2400ms_probe(struct sdio_func *func,
 	i2400m->bus_bm_mac_addr_impaired = 1;
 	i2400m->bus_bm_pokes_table = &i2400ms_pokes[0];
 
+	switch (func->device) {
+	case SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX:
+	case SDIO_DEVICE_ID_INTEL_IWMC3200WIMAX_2G5:
+		i2400ms->iwmc3200 = 1;
+		break;
+	default:
+		i2400ms->iwmc3200 = 0;
+	}
+
 	result = i2400m_setup(i2400m, I2400M_BRI_NO_REBOOT);
 	if (result < 0) {
 		dev_err(dev, "cannot setup device: %d\n", result);
Signature · 1 line
1.6.2.5
#8 lore raw

PATCH 8/12 wimax/i2400m: don't retry SDIO enable in probe() paths

In reply to Inaky Perez-Gonzalez

The iwmc3200 has a quirk where retrying SDIO enable during the probe() path causes bad interactions with the TOP function controller that causes a reset storm. The workaround is simply not to retry an SDIO enable in said path (and still do in the reset / reinitialization paths).

The driver does so by checking i2400ms->debugfs_dentry to see if it has been initialized; if not, it is in the probe() path. Document said fact in i2400ms->debugfs_entry.

---
 drivers/net/wimax/i2400m/i2400m-sdio.h |    8 ++++++++
 drivers/net/wimax/i2400m/sdio.c        |   13 ++++++++++++-
 2 files changed, 20 insertions(+), 1 deletions(-)
drivers/net/wimax/i2400m/i2400m-sdio.h +8 -0
index fba482c..b9c4bed 100644
@@ -100,6 +100,14 @@ enum {
  * @tx_workqueue: workqeueue used for data TX; we don't use the
  *     system's workqueue as that might cause deadlocks with code in
  *     the bus-generic driver.
+ *
+ * @debugfs_dentry: dentry for the SDIO specific debugfs files
+ *
+ *     Note this value is set to NULL upon destruction; this is
+ *     because some routinges use it to determine if we are inside the
+ *     probe() path or some other path. When debugfs is disabled,
+ *     creation sets the dentry to '(void*) -ENODEV', which is valid
+ *     for the test.
  */
 struct i2400ms {
 	struct i2400m i2400m;		/* FIRST! See doc */
drivers/net/wimax/i2400m/sdio.c +12 -1
index 079f900..e8ad85c 100644
@@ -159,6 +159,10 @@ function_enabled:
 /*
  * Setup minimal device communication infrastructure needed to at
  * least be able to update the firmware.
+ *
+ * Note the ugly trick: if we are in the probe path
+ * (i2400ms->debugfs_dentry == NULL), we only retry function
+ * enablement one, to avoid racing with the iwmc3200 top controller.
  */
 static
 int i2400ms_bus_setup(struct i2400m *i2400m)
@@ -168,6 +172,7 @@ int i2400ms_bus_setup(struct i2400m *i2400m)
 		container_of(i2400m, struct i2400ms, i2400m);
 	struct device *dev = i2400m_dev(i2400m);
 	struct sdio_func *func = i2400ms->func;
+	int retries;
 
 	sdio_claim_host(func);
 	result = sdio_set_block_size(func, I2400MS_BLK_SIZE);
@@ -177,7 +182,11 @@ int i2400ms_bus_setup(struct i2400m *i2400m)
 		goto error_set_blk_size;
 	}
 
-	result = i2400ms_enable_function(i2400ms, 1);
+	if (i2400ms->iwmc3200 && i2400ms->debugfs_dentry == NULL)
+		retries = 0;
+	else
+		retries = 1;
+	result = i2400ms_enable_function(i2400ms, retries);
 	if (result < 0) {
 		dev_err(dev, "Cannot enable SDIO function: %d\n", result);
 		goto error_func_enable;
@@ -415,6 +424,7 @@ int i2400ms_debugfs_add(struct i2400ms *i2400ms)
 
 error:
 	debugfs_remove_recursive(i2400ms->debugfs_dentry);
+	i2400ms->debugfs_dentry = NULL;
 	return result;
 }
 
@@ -531,6 +541,7 @@ void i2400ms_remove(struct sdio_func *func)
 
 	d_fnstart(3, dev, "SDIO func %p\n", func);
 	debugfs_remove_recursive(i2400ms->debugfs_dentry);
+	i2400ms->debugfs_dentry = NULL;
 	i2400m_release(i2400m);
 	sdio_set_drvdata(func, NULL);
 	free_netdev(net_dev);
Signature · 1 line
1.6.2.5
#9 lore raw

PATCH 9/12 wimax/i2400m: handle USB stalls

In reply to Inaky Perez-Gonzalez

When the device stalls, clear it and retry; if it keeps failing too often, reset the device.

This specially happens when running on virtual machines; the real hardware doesn't seem to trip on stalls too much, except for a few reports in the mailing list (still to be confirmed this is the cause, although it seems likely.

NOTE: it is not clear if the URB has to be resubmitted fully or start only at the offset of the first transaction sent. Can't find documentation to clarify one end or the other.

Tests that just resubmit the whole URB seemed to work in my environment.

---
 drivers/net/wimax/i2400m/usb-fw.c |   22 ++++++++++++++
 drivers/net/wimax/i2400m/usb-rx.c |   21 +++++++++++++
 drivers/net/wimax/i2400m/usb-tx.c |   22 ++++++++++++++
 drivers/net/wimax/i2400m/usb.c    |   57 +++++++++++++++++++++++++++++++++----
 4 files changed, 116 insertions(+), 6 deletions(-)
drivers/net/wimax/i2400m/usb-fw.c +22 -0
index 8ec8b6d..d8f6ce2 100644
@@ -113,6 +113,28 @@ retry:
 		}
 		result = len;
 		break;
+	case -EPIPE:
+		/*
+		 * Stall -- maybe the device is choking with our
+		 * requests. Clear it and give it some time. If they
+		 * happen to often, it might be another symptom, so we
+		 * reset.
+		 *
+		 * No error handling for usb_clear_halt(0; if it
+		 * works, the retry works; if it fails, this switch
+		 * does the error handling for us.
+		 */
+		if (edc_inc(&i2400mu->urb_edc,
+			    10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
+			dev_err(dev, "BM-CMD: too many stalls in "
+				"URB; resetting device\n");
+			usb_queue_reset_device(i2400mu->usb_iface);
+			/* fallthrough */
+		} else {
+			usb_clear_halt(i2400mu->usb_dev, pipe);
+			msleep(10);	/* give the device some time */
+			goto retry;
+		}
 	case -EINVAL:			/* while removing driver */
 	case -ENODEV:			/* dev disconnect ... */
 	case -ENOENT:			/* just ignore it */
drivers/net/wimax/i2400m/usb-rx.c +21 -0
index 22d127b..ba1b023 100644
@@ -222,6 +222,26 @@ retry:
 			goto retry;	/* ZLP, just resubmit */
 		skb_put(rx_skb, read_size);
 		break;
+	case -EPIPE:
+		/*
+		 * Stall -- maybe the device is choking with our
+		 * requests. Clear it and give it some time. If they
+		 * happen to often, it might be another symptom, so we
+		 * reset.
+		 *
+		 * No error handling for usb_clear_halt(0; if it
+		 * works, the retry works; if it fails, this switch
+		 * does the error handling for us.
+		 */
+		if (edc_inc(&i2400mu->urb_edc,
+			    10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
+			dev_err(dev, "BM-CMD: too many stalls in "
+				"URB; resetting device\n");
+			goto do_reset;
+		}
+		usb_clear_halt(i2400mu->usb_dev, usb_pipe);
+		msleep(10);	/* give the device some time */
+		goto retry;
 	case -EINVAL:			/* while removing driver */
 	case -ENODEV:			/* dev disconnect ... */
 	case -ENOENT:			/* just ignore it */
@@ -283,6 +303,7 @@ out:
 error_reset:
 	dev_err(dev, "RX: maximum errors in URB exceeded; "
 		"resetting device\n");
+do_reset:
 	usb_queue_reset_device(i2400mu->usb_iface);
 	rx_skb = ERR_PTR(result);
 	goto out;
drivers/net/wimax/i2400m/usb-tx.c +22 -0
index 6cdf003..c65b997 100644
@@ -115,6 +115,28 @@ retry:
 			result = -EIO;
 		}
 		break;
+	case -EPIPE:
+		/*
+		 * Stall -- maybe the device is choking with our
+		 * requests. Clear it and give it some time. If they
+		 * happen to often, it might be another symptom, so we
+		 * reset.
+		 *
+		 * No error handling for usb_clear_halt(0; if it
+		 * works, the retry works; if it fails, this switch
+		 * does the error handling for us.
+		 */
+		if (edc_inc(&i2400mu->urb_edc,
+			    10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
+			dev_err(dev, "BM-CMD: too many stalls in "
+				"URB; resetting device\n");
+			usb_queue_reset_device(i2400mu->usb_iface);
+			/* fallthrough */
+		} else {
+			usb_clear_halt(i2400mu->usb_dev, usb_pipe);
+			msleep(10);	/* give the device some time */
+			goto retry;
+		}
 	case -EINVAL:			/* while removing driver */
 	case -ENODEV:			/* dev disconnect ... */
 	case -ENOENT:			/* just ignore it */
drivers/net/wimax/i2400m/usb.c +51 -6

65 lines not on this page · raw message

Signature · 1 line
1.6.2.5
#10 lore raw

PATCH 10/12 wimax/i2400m: fix bad assignment of return value in i2400mu_tx_bulk_out

In reply to Inaky Perez-Gonzalez

The function was always setting the return value to the amount of bytes transferred, overwriting the error code in error paths.

---
 drivers/net/wimax/i2400m/usb-fw.c |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)
drivers/net/wimax/i2400m/usb-fw.c +0 -1
index d8f6ce2..ce6b993 100644
@@ -157,7 +157,6 @@ retry:
 			result);
 		goto retry;
 	}
-	result = len;
 	if (do_autopm)
 		usb_autopm_put_interface(i2400mu->usb_iface);
 	return result;
Signature · 1 line
1.6.2.5
#11 lore raw

PATCH 11/12 wimax/i2400m: fix SDIO debugfs dentry name

In reply to Inaky Perez-Gonzalez

The SDIO specific debugfs dentry was being misnamed "i2400m-usb" instead of "i2400m-sdio".

---
 drivers/net/wimax/i2400m/sdio.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
drivers/net/wimax/i2400m/sdio.c +1 -1
index e8ad85c..b06d526 100644
@@ -407,7 +407,7 @@ int i2400ms_debugfs_add(struct i2400ms *i2400ms)
 	int result;
 	struct dentry *dentry = i2400ms->i2400m.wimax_dev.debugfs_dentry;
 
-	dentry = debugfs_create_dir("i2400m-usb", dentry);
+	dentry = debugfs_create_dir("i2400m-sdio", dentry);
 	result = PTR_ERR(dentry);
 	if (IS_ERR(dentry)) {
 		if (result == -ENODEV)
Signature · 1 line
1.6.2.5
#12 lore raw

PATCH 12/12 wimax/i2400m: fix inverted value in i2400ms_bus_setup()

In reply to Inaky Perez-Gonzalez

Fix inverted setting of 'retries'; when we are in the probe() path, we should retry to enable the function only once; otherwise until it times out.

---
 drivers/net/wimax/i2400m/sdio.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)
drivers/net/wimax/i2400m/sdio.c +2 -2
index b06d526..76a50ac 100644
@@ -183,9 +183,9 @@ int i2400ms_bus_setup(struct i2400m *i2400m)
 	}
 
 	if (i2400ms->iwmc3200 && i2400ms->debugfs_dentry == NULL)
-		retries = 0;
-	else
 		retries = 1;
+	else
+		retries = 0;
 	result = i2400ms_enable_function(i2400ms, retries);
 	if (result < 0) {
 		dev_err(dev, "Cannot enable SDIO function: %d\n", result);
Signature · 1 line
1.6.2.5
#13 lore raw
In reply to Inaky Perez-Gonzalez
Quoted 3 lines · Inaky Perez-Gonzalez

The following changes since commit 4a78fd9a736db4c871bc8b583d66b61c38abd299:
Inaky Perez-Gonzalez (1):
wimax/i2400m: fix oops caused by race condition when exiting USB kthreads

Hmm, this is supposed to be batch #5 of the patch series, but somehow my scripts to break'em up took a life of their own and overwrote the subject. Sorry for that!