prima: Resolve deadlock during random mac address generation

SME global lock is acquired before processing scan response
message in SME layer. As part of processing this scan response,
scan done callback is invoked in HDD layer which further tries
to acquire mac spoof lock to request new random mac address.
If hdd_processSpoofMacAddrRequest is invoked in kernel context
at the same time, there is possibility of deadlock as
hdd_processSpoofMacAddrRequest tries to acquire mac spoof lock
first and then sme global lock. If this sme global lock is already
acquired during scan done callback and waiting for mac spoof lock
before requesting for new random mac address, then there is possibility
of deadlock. Fix this by handling random mac address generation as
part of work queue so that sme global lock can be released immediately
without waiting for mac spoof lock.

Change-Id: I6b3b481b82e04d8e31eda6710905c16ec77e526f
CRs-Fixed: 936165
diff --git a/CORE/HDD/inc/wlan_hdd_main.h b/CORE/HDD/inc/wlan_hdd_main.h
index 184ddfb..f86ceed 100644
--- a/CORE/HDD/inc/wlan_hdd_main.h
+++ b/CORE/HDD/inc/wlan_hdd_main.h
@@ -1560,6 +1560,9 @@
 #ifdef WLAN_FEATURE_OFFLOAD_PACKETS
     struct hdd_offloaded_packets_ctx op_ctx;
 #endif
+
+   /* work queue to defer mac spoofing */
+   struct delayed_work spoof_mac_addr_work;
 };
 
 
@@ -1675,7 +1678,7 @@
 tSirAbortScanStatus hdd_abort_mac_scan(hdd_context_t* pHddCtx,
                                        tANI_U8 sessionId,
                                        eCsrAbortReason reason);
-VOS_STATUS hdd_processSpoofMacAddrRequest(hdd_context_t *pHddCtx);
+void hdd_processSpoofMacAddrRequest(struct work_struct *work);
 void hdd_cleanup_actionframe( hdd_context_t *pHddCtx, hdd_adapter_t *pAdapter );
 
 void crda_regulatory_entry_default(v_U8_t *countryCode, int domain_id);
diff --git a/CORE/HDD/src/wlan_hdd_cfg80211.c b/CORE/HDD/src/wlan_hdd_cfg80211.c
index c3b2981..e934372 100644
--- a/CORE/HDD/src/wlan_hdd_cfg80211.c
+++ b/CORE/HDD/src/wlan_hdd_cfg80211.c
@@ -180,6 +180,8 @@
 #define MAC_ADDR_SPOOFING_FW_HOST_DISABLE           0
 #define MAC_ADDR_SPOOFING_FW_HOST_ENABLE            1
 #define MAC_ADDR_SPOOFING_FW_ENABLE_HOST_DISABLE    2
+#define MAC_ADDR_SPOOFING_DEFER_INTERVAL            10 //in ms
+
 
 static const u32 hdd_cipher_suites[] =
 {
@@ -5075,10 +5077,8 @@
             pHddCtx->spoofMacAddr.isEnabled = FALSE;
     }
 
-    if (VOS_STATUS_SUCCESS != hdd_processSpoofMacAddrRequest(pHddCtx))
-    {
-        hddLog(LOGE, FL("Failed to send Spoof Mac Addr to FW"));
-    }
+    schedule_delayed_work(&pHddCtx->spoof_mac_addr_work,
+                          msecs_to_jiffies(MAC_ADDR_SPOOFING_DEFER_INTERVAL));
 
     EXIT();
     return 0;
@@ -12645,7 +12645,9 @@
          ||  pHddCtx->spoofMacAddr.isReqDeferred)) {
         /* Generate new random mac addr for next scan */
         hddLog(VOS_TRACE_LEVEL_INFO, "scan completed - generate new spoof mac addr");
-        hdd_processSpoofMacAddrRequest(pHddCtx);
+
+        schedule_delayed_work(&pHddCtx->spoof_mac_addr_work,
+                           msecs_to_jiffies(MAC_ADDR_SPOOFING_DEFER_INTERVAL));
     }
 
 allow_suspend:
diff --git a/CORE/HDD/src/wlan_hdd_early_suspend.c b/CORE/HDD/src/wlan_hdd_early_suspend.c
index ca0119f..63f264a 100644
--- a/CORE/HDD/src/wlan_hdd_early_suspend.c
+++ b/CORE/HDD/src/wlan_hdd_early_suspend.c
@@ -1919,6 +1919,7 @@
         vos_timer_stop(&pHddCtx->tx_rx_trafficTmr);
    }
 
+   vos_flush_delayed_work(&pHddCtx->spoof_mac_addr_work);
    hdd_reset_all_adapters(pHddCtx);
    /* DeRegister with platform driver as client for Suspend/Resume */
    vosStatus = hddDeregisterPmOps(pHddCtx);
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index 2b15122..ccae302 100755
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -8917,6 +8917,8 @@
 
    hdd_close_all_adapters( pHddCtx );
 
+   vos_flush_delayed_work(&pHddCtx->spoof_mac_addr_work);
+
 free_hdd_ctx:
    /* free the power on lock from platform driver */
    if (free_riva_power_on_lock("wlan"))
@@ -9756,6 +9758,9 @@
 
    hdd_list_init( &pHddCtx->hddAdapters, MAX_NUMBER_OF_ADAPTERS );
 
+   vos_init_delayed_work(&pHddCtx->spoof_mac_addr_work,
+                                hdd_processSpoofMacAddrRequest);
+
 #ifdef FEATURE_WLAN_TDLS
    /* tdls_lock is initialized before an hdd_open_adapter ( which is
     * invoked by other instances also) to protect the concurrent
diff --git a/CORE/HDD/src/wlan_hdd_scan.c b/CORE/HDD/src/wlan_hdd_scan.c
index 709fdec..39e2f39 100644
--- a/CORE/HDD/src/wlan_hdd_scan.c
+++ b/CORE/HDD/src/wlan_hdd_scan.c
@@ -542,11 +542,16 @@
 
   --------------------------------------------------------------------------*/
 
-VOS_STATUS hdd_processSpoofMacAddrRequest(hdd_context_t *pHddCtx)
+void __hdd_processSpoofMacAddrRequest(struct work_struct *work)
 {
+    hdd_context_t *pHddCtx =
+        container_of(to_delayed_work(work), hdd_context_t, spoof_mac_addr_work);
 
     ENTER();
 
+    if (wlan_hdd_validate_context(pHddCtx))
+        return;
+
     mutex_lock(&pHddCtx->spoofMacAddr.macSpoofingLock);
 
     if (pHddCtx->spoofMacAddr.isEnabled) {
@@ -556,7 +561,7 @@
                 pHddCtx->spoofMacAddr.isEnabled = FALSE;
                 mutex_unlock(&pHddCtx->spoofMacAddr.macSpoofingLock);
                 hddLog(LOGE, FL("Failed to generate random Mac Addr"));
-                return VOS_STATUS_E_FAILURE;
+                return;
         }
     }
 
@@ -584,7 +589,14 @@
 
     EXIT();
 
-    return VOS_STATUS_SUCCESS;
+    return;
+}
+
+void hdd_processSpoofMacAddrRequest(struct work_struct *work)
+{
+    vos_ssr_protect(__func__);
+    __hdd_processSpoofMacAddrRequest(work);
+    vos_ssr_unprotect(__func__);
 }
 
 /**---------------------------------------------------------------------------