aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/libertas
diff options
context:
space:
mode:
authorAndres Salomon <dilinger@queued.net>2011-12-19 15:22:58 -0500
committerJohn W. Linville <linville@tuxdriver.com>2011-12-21 15:06:10 -0500
commitafbca95f95f2bf7283a72670c24c1f6de00b1cb5 (patch)
treeff83f8cec64d3936960ec166292c75f1b17e2952 /drivers/net/wireless/libertas
parent092fadb00c3958f2cbc560cf00489563bc929faa (diff)
libertas: clean up scan thread handling
The libertas scan thread expects priv->scan_req to be non-NULL. In theory, it should always be set. In practice, we've seen the following oops: [ 8363.067444] Unable to handle kernel NULL pointer dereference at virtual address 00000004 [ 8363.067490] pgd = c0004000 [ 8363.078393] [00000004] *pgd=00000000 [ 8363.086711] Internal error: Oops: 17 [#1] PREEMPT [ 8363.091375] Modules linked in: fuse libertas_sdio libertas psmouse mousedev ov7670 mmp_camera joydev videobuf2_core videobuf2_dma_sg videobuf2_memops [last unloaded: scsi_wait_scan] [ 8363.107490] CPU: 0 Not tainted (3.0.0-gf7ccc69 #671) [ 8363.112799] PC is at lbs_scan_worker+0x108/0x5a4 [libertas] [ 8363.118326] LR is at 0x0 [ 8363.120836] pc : [<bf03a854>] lr : [<00000000>] psr: 60000113 [ 8363.120845] sp : ee66bf48 ip : 00000000 fp : 00000000 [ 8363.120845] r10: ee2c2088 r9 : c04e2efc r8 : eef97005 [ 8363.132231] r7 : eee0716f r6 : ee2c02c0 r5 : ee2c2088 r4 : eee07160 [ 8363.137419] r3 : 00000000 r2 : a0000113 r1 : 00000001 r0 : eee07160 [ 8363.143896] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel [ 8363.157630] Control: 10c5387d Table: 2e754019 DAC: 00000015 [ 8363.163334] Process kworker/u:1 (pid: 25, stack limit = 0xee66a2f8) While I've not found a smoking gun, there are two places that raised red flags for me. The first is in _internal_start_scan, when we queue up a scan; we first queue the worker, and then set priv->scan_req. There's theoretically a 50mS delay which should be plenty, but doing things that way just seems racy (and not in the good way). The second is in the scan worker thread itself. Depending on the state of priv->scan_channel, we cancel pending scan runs and then requeue a run in 300mS. We then send the scan command down to the hardware, sleep, and if we get scan results for all the desired channels, we set priv->scan_req to NULL. However, it that's happened in less than 300mS, what happens with the pending scan run? This patch addresses both of those concerns. With the patch applied, we have not seen the oops in the past two weeks. Signed-off-by: Andres Salomon <dilinger@queued.net> Cc: stable@kernel.org Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/libertas')
-rw-r--r--drivers/net/wireless/libertas/cfg.c10
1 files changed, 6 insertions, 4 deletions
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index d1d84e0e30fc..a7cd311cb1b7 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -731,9 +731,11 @@ static void lbs_scan_worker(struct work_struct *work)
731 le16_to_cpu(scan_cmd->hdr.size), 731 le16_to_cpu(scan_cmd->hdr.size),
732 lbs_ret_scan, 0); 732 lbs_ret_scan, 0);
733 733
734 if (priv->scan_channel >= priv->scan_req->n_channels) 734 if (priv->scan_channel >= priv->scan_req->n_channels) {
735 /* Mark scan done */ 735 /* Mark scan done */
736 cancel_delayed_work(&priv->scan_work);
736 lbs_scan_done(priv); 737 lbs_scan_done(priv);
738 }
737 739
738 /* Restart network */ 740 /* Restart network */
739 if (carrier) 741 if (carrier)
@@ -762,12 +764,12 @@ static void _internal_start_scan(struct lbs_private *priv, bool internal,
762 request->n_ssids, request->n_channels, request->ie_len); 764 request->n_ssids, request->n_channels, request->ie_len);
763 765
764 priv->scan_channel = 0; 766 priv->scan_channel = 0;
765 queue_delayed_work(priv->work_thread, &priv->scan_work,
766 msecs_to_jiffies(50));
767
768 priv->scan_req = request; 767 priv->scan_req = request;
769 priv->internal_scan = internal; 768 priv->internal_scan = internal;
770 769
770 queue_delayed_work(priv->work_thread, &priv->scan_work,
771 msecs_to_jiffies(50));
772
771 lbs_deb_leave(LBS_DEB_CFG80211); 773 lbs_deb_leave(LBS_DEB_CFG80211);
772} 774}
773 775