From 697f4a19e52cea8fbd17c8b764a352aab28ba9e1 Mon Sep 17 00:00:00 2001
From: Hauke Mehrtens <hauke@hauke-m.de>
Date: Mon, 11 Nov 2013 22:04:26 +0000
Subject: broadcom-wl: fix crash when starting multiple virtual interfaces

When enabling multiple VIFS, the driver sometimes crashes.  The frequency
of the crash increases as more VIFS are enabled.

Signed-off-by: Nathan Hintz <nlhintz@hotmail.com>

SVN-Revision: 38762
---
 .../patches/008-fix_virtual_interfaces.patch       | 123 ++++++++++++++++++++-
 .../broadcom-wl/patches/009-fix_compile_3_2.patch  |   4 +-
 .../patches/010-remove_irqf_samble_random.patch    |   2 +-
 .../broadcom-wl/patches/012-compat-3.10.patch      |   8 +-
 .../broadcom-wl/patches/013-interface-name.patch   |   2 +-
 .../patches/110-add_number_to_dev_name.patch       |   2 +-
 6 files changed, 131 insertions(+), 10 deletions(-)

(limited to 'package/kernel/broadcom-wl/patches')

diff --git a/package/kernel/broadcom-wl/patches/008-fix_virtual_interfaces.patch b/package/kernel/broadcom-wl/patches/008-fix_virtual_interfaces.patch
index 360593115b..23831df5ba 100644
--- a/package/kernel/broadcom-wl/patches/008-fix_virtual_interfaces.patch
+++ b/package/kernel/broadcom-wl/patches/008-fix_virtual_interfaces.patch
@@ -1,6 +1,117 @@
 --- a/driver/wl_linux.c
 +++ b/driver/wl_linux.c
-@@ -1545,6 +1545,8 @@ wl_add_if(wl_info_t *wl, struct wlc_if* 
+@@ -354,6 +354,7 @@ static int wl_read_proc(char *buffer, ch
+ static int wl_dump(wl_info_t *wl, struct bcmstrbuf *b);
+ #endif /* BCMDBG */
+ struct wl_if *wl_alloc_if(wl_info_t *wl, int iftype, uint unit, struct wlc_if* wlc_if);
++static void wl_link_if(wl_info_t *wl, wl_if_t *wlif);
+ static void wl_free_if(wl_info_t *wl, wl_if_t *wlif);
+ 
+ 
+@@ -566,6 +567,9 @@ wl_attach(uint16 vendor, uint16 device,
+ 	wl->dev = dev;
+ 	wl_if_setup(dev);
+ 
++	/* add the interface to the interface linked list */
++	wl_link_if(wl, wlif);
++
+ 	/* map chip registers (47xx: and sprom) */
+ 	dev->base_addr = regs;
+ 
+@@ -1106,10 +1110,14 @@ wl_free(wl_info_t *wl)
+ 			free_irq(wl->dev->irq, wl);
+ 	}
+ 
+-	if (wl->dev) {
+-		wl_free_if(wl, WL_DEV_IF(wl->dev));
+-		wl->dev = NULL;
++	/* free all interfaces */
++	while (wl->if_list) {
++        	if ((wl->if_list->dev != wl->dev) || wl->if_list->next == NULL)
++			wl_free_if(wl, wl->if_list);
++		else
++			wl_free_if(wl, wl->if_list->next);
+ 	}
++	wl->dev = NULL;
+ 
+ #ifdef TOE
+ 	wl_toe_detach(wl->toei);
+@@ -1355,10 +1363,12 @@ wl_txflowcontrol(wl_info_t *wl, bool sta
+ 
+ 	ASSERT(prio == ALLPRIO);
+ 	for (wlif = wl->if_list; wlif != NULL; wlif = wlif->next) {
+-		if (state == ON)
+-			netif_stop_queue(wlif->dev);
+-		else
+-			netif_wake_queue(wlif->dev);
++		if (wlif->dev_registed) {
++			if (state == ON)
++				netif_stop_queue(wlif->dev);
++			else
++				netif_wake_queue(wlif->dev);
++		}
+ 	}
+ }
+ 
+@@ -1398,7 +1408,6 @@ wl_alloc_if(wl_info_t *wl, int iftype, u
+ {
+ 	struct net_device *dev;
+ 	wl_if_t *wlif;
+-	wl_if_t *p;
+ 
+ 	dev = alloc_etherdev(sizeof(wl_if_t));
+ 	wlif = netdev_priv(dev);
+@@ -1411,9 +1420,13 @@ wl_alloc_if(wl_info_t *wl, int iftype, u
+ 	wlif->wlcif = wlcif;
+ 	wlif->subunit = subunit;
+ 
+-	/* match current flow control state */
+-	if (iftype != WL_IFTYPE_MON && wl->dev && netif_queue_stopped(wl->dev))
+-		netif_stop_queue(dev);
++	return wlif;
++}
++
++static void
++wl_link_if(wl_info_t *wl, wl_if_t *wlif)
++{
++	wl_if_t *p;
+ 
+ 	/* add the interface to the interface linked list */
+ 	if (wl->if_list == NULL)
+@@ -1424,7 +1437,6 @@ wl_alloc_if(wl_info_t *wl, int iftype, u
+ 			p = p->next;
+ 		p->next = wlif;
+ 	}
+-	return wlif;
+ }
+ 
+ static void
+@@ -1504,6 +1516,9 @@ _wl_add_if(wl_task_t *task)
+ 	wl_info_t *wl = wlif->wl;
+ 	struct net_device *dev = wlif->dev;
+ 
++	/* add the interface to the interface linked list */
++	wl_link_if(wl, wlif);
++
+ 	if (wlif->type == WL_IFTYPE_WDS)
+ 		dev->netdev_ops = &wl_wds_ops;
+ 
+@@ -1516,6 +1531,14 @@ _wl_add_if(wl_task_t *task)
+ 	}
+ 	wlif->dev_registed = TRUE;
+ 
++	/* match current flow control state */
++	if (wl->dev) {
++		if (netif_queue_stopped(wl->dev))
++			netif_stop_queue(dev);
++		else
++			netif_wake_queue(dev);
++	}
++
+ done:
+ 	MFREE(wl->osh, task, sizeof(wl_task_t));
+ 	atomic_dec(&wl->callbacks);
+@@ -1545,6 +1568,8 @@ wl_add_if(wl_info_t *wl, struct wlc_if*
  		return NULL;
  	}
  
@@ -9,3 +120,13 @@
  	sprintf(wlif->dev->name, "%s%d.%d", devname, wl->pub->unit, wlif->subunit);
  	if (remote)
  		bcopy(remote, &wlif->remote, ETHER_ADDR_LEN);
+@@ -2778,6 +2803,9 @@ wl_add_monitor(wl_task_t *task)
+ 	dev = wlif->dev;
+ 	wl->monitor = dev;
+ 
++	/* add the interface to the interface linked list */
++	wl_link_if(wl, wlif);
++
+ 	/* override some fields */
+ 	sprintf(dev->name, "prism%d", wl->pub->unit);
+ 	bcopy(wl->dev->dev_addr, dev->dev_addr, ETHER_ADDR_LEN);
diff --git a/package/kernel/broadcom-wl/patches/009-fix_compile_3_2.patch b/package/kernel/broadcom-wl/patches/009-fix_compile_3_2.patch
index 628f2fdf25..cb388a1125 100644
--- a/package/kernel/broadcom-wl/patches/009-fix_compile_3_2.patch
+++ b/package/kernel/broadcom-wl/patches/009-fix_compile_3_2.patch
@@ -1,6 +1,6 @@
 --- a/driver/wl_linux.c
 +++ b/driver/wl_linux.c
-@@ -462,6 +462,16 @@ wl_schedule_fn(wl_info_t *wl, void (*fn)
+@@ -463,6 +463,16 @@ wl_schedule_fn(wl_info_t *wl, void (*fn)
  }
  #endif /* DSLCPE_DELAY */
  
@@ -17,7 +17,7 @@
  #define WL_DEFAULT_OPS \
  	.ndo_open = wl_open, \
  	.ndo_stop = wl_close, \
-@@ -470,6 +480,7 @@ wl_schedule_fn(wl_info_t *wl, void (*fn)
+@@ -471,6 +481,7 @@ wl_schedule_fn(wl_info_t *wl, void (*fn)
  	.ndo_set_mac_address = wl_set_mac_address, \
  	.ndo_set_multicast_list = wl_set_multicast_list, \
  	.ndo_do_ioctl = wl_ioctl
diff --git a/package/kernel/broadcom-wl/patches/010-remove_irqf_samble_random.patch b/package/kernel/broadcom-wl/patches/010-remove_irqf_samble_random.patch
index 27b132d115..4bbbdcaaaa 100644
--- a/package/kernel/broadcom-wl/patches/010-remove_irqf_samble_random.patch
+++ b/package/kernel/broadcom-wl/patches/010-remove_irqf_samble_random.patch
@@ -1,6 +1,6 @@
 --- a/driver/wl_linux.c	2012-09-26 20:51:48.099454971 -0400
 +++ b/driver/wl_linux.c	2012-09-26 20:53:24.115453441 -0400
-@@ -691,7 +691,7 @@
+@@ -695,7 +695,7 @@
  	if (wl->bustype != JTAG_BUS)
  #endif	/* BCMJTAG */
  	{
diff --git a/package/kernel/broadcom-wl/patches/012-compat-3.10.patch b/package/kernel/broadcom-wl/patches/012-compat-3.10.patch
index 1ab853967a..e36028a634 100644
--- a/package/kernel/broadcom-wl/patches/012-compat-3.10.patch
+++ b/package/kernel/broadcom-wl/patches/012-compat-3.10.patch
@@ -9,7 +9,7 @@
  static int wl_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data);
  #endif /* defined(CONFIG_PROC_FS) */
  #ifdef BCMDBG
-@@ -516,7 +516,7 @@ wl_attach(uint16 vendor, uint16 device,
+@@ -517,7 +517,7 @@ wl_attach(uint16 vendor, uint16 device,
  	struct net_device *dev;
  	wl_if_t *wlif;
  	wl_info_t *wl;
@@ -18,7 +18,7 @@
  	char tmp[128];
  #endif
  	osl_t *osh;
-@@ -660,7 +660,7 @@ wl_attach(uint16 vendor, uint16 device,
+@@ -664,7 +664,7 @@ wl_attach(uint16 vendor, uint16 device,
  			WL_ERROR(("wl%d: Error setting MPC variable to 0\n", unit));
  		}
  	}
@@ -27,7 +27,7 @@
  	/* create /proc/net/wl<unit> */
  	sprintf(tmp, "net/wl%d", wl->pub->unit);
  	create_proc_read_entry(tmp, 0, 0, wl_read_proc, (void*)wl);
-@@ -806,7 +806,7 @@ wl_dbus_disconnect_cb(void *arg)
+@@ -810,7 +810,7 @@ wl_dbus_disconnect_cb(void *arg)
  }
  #endif /* BCMDBUS */
  
@@ -36,7 +36,7 @@
  static int
  wl_read_proc(char *buffer, char **start, off_t offset, int length, int *eof, void *data)
  {
-@@ -1141,7 +1141,7 @@ wl_free(wl_info_t *wl)
+@@ -1149,7 +1149,7 @@ wl_free(wl_info_t *wl)
  
  	/* free common resources */
  	if (wl->wlc) {
diff --git a/package/kernel/broadcom-wl/patches/013-interface-name.patch b/package/kernel/broadcom-wl/patches/013-interface-name.patch
index a19e3a9882..dbe1bdb89a 100644
--- a/package/kernel/broadcom-wl/patches/013-interface-name.patch
+++ b/package/kernel/broadcom-wl/patches/013-interface-name.patch
@@ -1,6 +1,6 @@
 --- a/driver/wl_linux.c
 +++ b/driver/wl_linux.c
-@@ -1560,7 +1560,7 @@ wl_add_if(wl_info_t *wl, struct wlc_if*
+@@ -1583,7 +1583,7 @@ wl_add_if(wl_info_t *wl, struct wlc_if*
  
  	wl_if_setup(wlif->dev);
  
diff --git a/package/kernel/broadcom-wl/patches/110-add_number_to_dev_name.patch b/package/kernel/broadcom-wl/patches/110-add_number_to_dev_name.patch
index 28917d912a..95f0beb73c 100644
--- a/package/kernel/broadcom-wl/patches/110-add_number_to_dev_name.patch
+++ b/package/kernel/broadcom-wl/patches/110-add_number_to_dev_name.patch
@@ -1,6 +1,6 @@
 --- a/driver/wl_linux.c
 +++ b/driver/wl_linux.c
-@@ -1416,7 +1416,7 @@ wl_alloc_if(wl_info_t *wl, int iftype, u
+@@ -1425,7 +1425,7 @@ wl_alloc_if(wl_info_t *wl, int iftype, u
  	dev = alloc_etherdev(sizeof(wl_if_t));
  	wlif = netdev_priv(dev);
  	bzero(wlif, sizeof(wl_if_t));
-- 
cgit v1.2.3