From 676f45fb33bbbe462d6164043353ac138cb38b53 Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Wed, 10 Nov 2010 17:56:37 -0800 Subject: [PATCH] net: wireless: bcm4329: Fix Softap start/stop race conditions Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcm4329/dhd.h | 2 + drivers/net/wireless/bcm4329/dhd_linux.c | 54 +++++++++++++++++++----- drivers/net/wireless/bcm4329/wl_iw.c | 30 +++++++++++-- 3 files changed, 72 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/bcm4329/dhd.h b/drivers/net/wireless/bcm4329/dhd.h index 59aa9f10ab1f..8f95e576ca65 100644 --- a/drivers/net/wireless/bcm4329/dhd.h +++ b/drivers/net/wireless/bcm4329/dhd.h @@ -221,6 +221,8 @@ extern int dhd_os_wake_lock_timeout_enable(dhd_pub_t *pub); extern void dhd_os_start_lock(dhd_pub_t *pub); extern void dhd_os_start_unlock(dhd_pub_t *pub); +extern unsigned long dhd_os_spin_lock(dhd_pub_t *pub); +extern void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags); typedef struct dhd_if_event { uint8 ifidx; diff --git a/drivers/net/wireless/bcm4329/dhd_linux.c b/drivers/net/wireless/bcm4329/dhd_linux.c index 424f6b2d0a4b..80dbbff9b772 100644 --- a/drivers/net/wireless/bcm4329/dhd_linux.c +++ b/drivers/net/wireless/bcm4329/dhd_linux.c @@ -261,6 +261,8 @@ typedef struct dhd_info { struct tasklet_struct tasklet; spinlock_t sdlock; spinlock_t txqlock; + spinlock_t dhd_lock; + /* Thread based operation */ bool threads_only; struct semaphore sdsem; @@ -904,13 +906,18 @@ _dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr) #ifdef SOFTAP extern struct net_device *ap_net_dev; +/* semaphore that the soft AP CODE waits on */ +extern struct semaphore ap_eth_sema; #endif static void dhd_op_if(dhd_if_t *ifp) { - dhd_info_t *dhd; - int ret = 0, err = 0; + dhd_info_t *dhd; + int ret = 0, err = 0; +#ifdef SOFTAP + unsigned long flags; +#endif ASSERT(ifp && ifp->info && ifp->idx); /* Virtual interfaces only */ @@ -945,13 +952,12 @@ dhd_op_if(dhd_if_t *ifp) ret = -EOPNOTSUPP; } else { #ifdef SOFTAP - /* semaphore that the soft AP CODE waits on */ - extern struct semaphore ap_eth_sema; - + flags = dhd_os_spin_lock(&dhd->pub); /* save ptr to wl0.1 netdev for use in wl_iw.c */ ap_net_dev = ifp->net; /* signal to the SOFTAP 'sleeper' thread, wl0.1 is ready */ up(&ap_eth_sema); + dhd_os_spin_unlock(&dhd->pub, flags); #endif DHD_TRACE(("\n ==== pid:%x, net_device for if:%s created ===\n\n", current->pid, ifp->net->name)); @@ -980,8 +986,10 @@ dhd_op_if(dhd_if_t *ifp) dhd->iflist[ifp->idx] = NULL; MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); #ifdef SOFTAP + flags = dhd_os_spin_lock(&dhd->pub); if (ifp->net == ap_net_dev) ap_net_dev = NULL; /* NULL SOFTAP global as well */ + dhd_os_spin_unlock(&dhd->pub, flags); #endif /* SOFTAP */ } } @@ -993,6 +1001,7 @@ _dhd_sysioc_thread(void *data) int i; #ifdef SOFTAP bool in_ap = FALSE; + unsigned long flags; #endif DAEMONIZE("dhd_sysioc"); @@ -1004,7 +1013,9 @@ _dhd_sysioc_thread(void *data) if (dhd->iflist[i]) { DHD_TRACE(("%s: interface %d\n",__FUNCTION__, i)); #ifdef SOFTAP + flags = dhd_os_spin_lock(&dhd->pub); in_ap = (ap_net_dev != NULL); + dhd_os_spin_unlock(&dhd->pub, flags); #endif /* SOFTAP */ if (dhd->iflist[i]->state) dhd_op_if(dhd->iflist[i]); @@ -1039,6 +1050,7 @@ _dhd_sysioc_thread(void *data) dhd_os_wake_unlock(&dhd->pub); dhd_os_start_unlock(&dhd->pub); } + DHD_TRACE(("%s: stopped\n",__FUNCTION__)); complete_and_exit(&dhd->sysioc_exited, 0); } @@ -2048,6 +2060,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) /* Initialize the spinlocks */ spin_lock_init(&dhd->sdlock); spin_lock_init(&dhd->txqlock); + spin_lock_init(&dhd->dhd_lock); /* Initialize Wakelock stuff */ spin_lock_init(&dhd->wl_lock); @@ -2437,16 +2450,18 @@ dhd_detach(dhd_pub_t *dhdp) /* Attach and link in the iw */ wl_iw_detach(); #endif - - for (i = 1; i < DHD_MAX_IFS; i++) - if (dhd->iflist[i]) - dhd_del_if(dhd, i); - if (dhd->sysioc_pid >= 0) { KILL_PROC(dhd->sysioc_pid, SIGTERM); wait_for_completion(&dhd->sysioc_exited); } + for (i = 1; i < DHD_MAX_IFS; i++) + if (dhd->iflist[i]) { + dhd->iflist[i]->state = WLC_E_IF_DEL; + dhd->iflist[i]->idx = i; + dhd_op_if(dhd->iflist[i]); + } + ifp = dhd->iflist[0]; ASSERT(ifp); #if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31)) @@ -3264,3 +3279,22 @@ int net_os_wake_unlock(struct net_device *dev) ret = dhd_os_wake_unlock(&dhd->pub); return ret; } + +unsigned long dhd_os_spin_lock(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags = 0; + + if (dhd) + spin_lock_irqsave(&dhd->dhd_lock, flags); + + return flags; +} + +void dhd_os_spin_unlock(dhd_pub_t *pub, unsigned long flags) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + + if (dhd) + spin_unlock_irqrestore(&dhd->dhd_lock, flags); +} diff --git a/drivers/net/wireless/bcm4329/wl_iw.c b/drivers/net/wireless/bcm4329/wl_iw.c index c8a5033a304a..547c6cee0082 100644 --- a/drivers/net/wireless/bcm4329/wl_iw.c +++ b/drivers/net/wireless/bcm4329/wl_iw.c @@ -98,8 +98,10 @@ typedef const struct si_pub si_t; static struct net_device *priv_dev; static bool ap_cfg_running = FALSE; bool ap_fw_loaded = FALSE; +static long ap_cfg_pid = -1; struct net_device *ap_net_dev = NULL; struct semaphore ap_eth_sema; +static struct completion ap_cfg_exited; static int wl_iw_set_ap_security(struct net_device *dev, struct ap_profile *ap); static int wl_iw_softap_deassoc_stations(struct net_device *dev, u8 *mac); #endif @@ -5944,7 +5946,12 @@ exit_proc: static int thr_wait_for_2nd_eth_dev(void *data) { + struct net_device *dev = (struct net_device *)data; + wl_iw_t *iw; int ret = 0; + unsigned long flags; + + net_os_wake_lock(dev); DAEMONIZE("wl0_eth_wthread"); @@ -5956,9 +5963,12 @@ static int thr_wait_for_2nd_eth_dev(void *data) goto fail; } + iw = *(wl_iw_t **)netdev_priv(dev); + flags = dhd_os_spin_lock(iw->pub); if (!ap_net_dev) { WL_ERROR((" ap_net_dev is null !!!")); ret = -1; + dhd_os_spin_unlock(iw->pub, flags); goto fail; } @@ -5967,6 +5977,8 @@ static int thr_wait_for_2nd_eth_dev(void *data) ap_cfg_running = TRUE; + dhd_os_spin_unlock(iw->pub, flags); + bcm_mdelay(500); wl_iw_send_priv_event(priv_dev, "AP_SET_CFG_OK"); @@ -5974,6 +5986,9 @@ static int thr_wait_for_2nd_eth_dev(void *data) fail: WL_TRACE(("\n>%s, thread completed\n", __FUNCTION__)); + net_os_wake_unlock(dev); + + complete_and_exit(&ap_cfg_exited, 0); return ret; } #endif @@ -6218,8 +6233,10 @@ static int set_ap_cfg(struct net_device *dev, struct ap_profile *ap) goto fail; } if (ap_cfg_running == FALSE) { - kernel_thread(thr_wait_for_2nd_eth_dev, 0, 0); + init_completion(&ap_cfg_exited); + ap_cfg_pid = kernel_thread(thr_wait_for_2nd_eth_dev, dev, 0); } else { + ap_cfg_pid = -1; if (ap_net_dev == NULL) { WL_ERROR(("%s ERROR: ap_net_dev is NULL !!!\n", __FUNCTION__)); goto fail; @@ -6557,9 +6574,9 @@ static int iwpriv_softap_stop(struct net_device *dev, if ((ap_cfg_running == TRUE)) { #ifdef AP_ONLY - wl_iw_softap_deassoc_stations(dev, NULL); + wl_iw_softap_deassoc_stations(dev, NULL); #else - wl_iw_softap_deassoc_stations(ap_net_dev, NULL); + wl_iw_softap_deassoc_stations(ap_net_dev, NULL); if ((res = dev_iw_write_cfg1_bss_var(dev, 2)) < 0) WL_ERROR(("%s failed to del BSS err = %d", __FUNCTION__, res)); @@ -6689,9 +6706,14 @@ iwpriv_en_ap_bss( net_os_wake_lock(dev); - WL_TRACE(("%s: rcvd IWPRIV IOCTL: for dev:%s\n", __FUNCTION__, dev->name)); + WL_SOFTAP(("%s: rcvd IWPRIV IOCTL: for dev:%s\n", __FUNCTION__, dev->name)); #ifndef AP_ONLY + if (ap_cfg_pid >= 0) { + wait_for_completion(&ap_cfg_exited); + ap_cfg_pid = -1; + } + if ((res = wl_iw_set_ap_security(dev, &my_ap)) != 0) { WL_ERROR((" %s ERROR setting SOFTAP security in :%d\n", __FUNCTION__, res)); } -- 2.34.1