wlan: Add station id to mac address hash

Add hash table for station id to mac address
mapping. It avoids storing station id in (skb->data -1)
location between select_queue and hard_start_xmit
call which is required to enable GRO.
This hash is created in SAP/P2P-GO and IBSS mode only.

CRs-Fixed: 849908
Change-Id: I42d3bd8942e06088fee9fb08ea8ebb27353833e7
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index 8a6f62c..2cb2f3d 100755
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -6318,6 +6318,9 @@
    VOS_STATUS status = VOS_STATUS_E_FAILURE;
    long rc = 0;
 
+   spin_lock_init( &pAdapter->sta_hash_lock);
+   pAdapter->is_sta_id_hash_initialized = VOS_FALSE;
+
    INIT_COMPLETION(pAdapter->session_open_comp_var);
    sme_SetCurrDeviceMode(pHddCtx->hHal, pAdapter->device_mode);
    //Open a SME session for future operation
@@ -6929,6 +6932,15 @@
          if( VOS_STATUS_SUCCESS != status )
             goto err_free_netdev;
 
+         status = hdd_sta_id_hash_attach(pAdapter);
+         if (VOS_STATUS_SUCCESS != status)
+         {
+             hddLog(VOS_TRACE_LEVEL_FATAL,
+                    FL("failed to attach hash for session %d"), session_type);
+             hdd_deinit_adapter(pHddCtx, pAdapter, rtnl_held);
+             goto err_free_netdev;
+         }
+
          status = hdd_register_hostapd( pAdapter, rtnl_held );
          if( VOS_STATUS_SUCCESS != status )
          {
@@ -7207,6 +7219,7 @@
    union iwreq_data wrqu;
    v_U8_t retry = 0;
    long ret;
+   VOS_STATUS status;
 
    if (pHddCtx->isLogpInProgress) {
        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
@@ -7216,6 +7229,11 @@
 
    ENTER();
 
+   status = hdd_sta_id_hash_detach(pAdapter);
+   if (status != VOS_STATUS_SUCCESS)
+       hddLog(VOS_TRACE_LEVEL_ERROR,
+                 FL("sta id hash detach failed"));
+
    pScanInfo =  &pHddCtx->scan_info;
    switch(pAdapter->device_mode)
    {
@@ -7521,6 +7539,12 @@
       if(pAdapter->device_mode == WLAN_HDD_IBSS )
          hdd_ibss_deinit_tx_rx(pAdapter);
 
+      status = hdd_sta_id_hash_detach(pAdapter);
+      if (status != VOS_STATUS_SUCCESS)
+          hddLog(VOS_TRACE_LEVEL_ERROR,
+                 FL("sta id hash detach failed for session id %d"),
+                 pAdapter->sessionId);
+
       wlan_hdd_decr_active_session(pHddCtx, pAdapter->device_mode);
 
       if (test_bit(WMM_INIT_DONE, &pAdapter->event_flags))
@@ -11517,6 +11541,286 @@
     return status;
 }
 
+static int hdd_log2_ceil(unsigned value)
+{
+    /* need to switch to unsigned math so that negative values
+     * will right-shift towards 0 instead of -1
+     */
+    unsigned tmp = value;
+    int log2 = -1;
+
+    if (value == 0)
+        return 0;
+
+    while (tmp) {
+        log2++;
+        tmp >>= 1;
+    }
+    if (1U << log2 != value)
+        log2++;
+
+    return log2;
+}
+
+/**
+ * hdd_sta_id_hash_attach() - initialize sta id to macaddr hash
+ * @pAdapter: adapter handle
+ *
+ * Return: vos status
+ */
+VOS_STATUS hdd_sta_id_hash_attach(hdd_adapter_t *pAdapter)
+{
+    int hash_elem, log2, i;
+
+    spin_lock_bh( &pAdapter->sta_hash_lock);
+    if (pAdapter->is_sta_id_hash_initialized == VOS_TRUE) {
+        spin_unlock_bh( &pAdapter->sta_hash_lock);
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "%s: hash already attached for session id %d",
+                  __func__, pAdapter->sessionId);
+        return VOS_STATUS_SUCCESS;
+    }
+    spin_unlock_bh( &pAdapter->sta_hash_lock);
+
+    hash_elem = WLAN_MAX_STA_COUNT;
+    hash_elem *= HDD_STA_ID_HASH_MULTIPLIER;
+    log2 = hdd_log2_ceil(hash_elem);
+    hash_elem = 1 << log2;
+
+    pAdapter->sta_id_hash.mask = hash_elem - 1;
+    pAdapter->sta_id_hash.idx_bits = log2;
+    pAdapter->sta_id_hash.bins =
+        vos_mem_malloc(hash_elem *sizeof(hdd_list_t));
+    if (!pAdapter->sta_id_hash.bins) {
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "%s: malloc failed for session %d",
+                  __func__, pAdapter->sessionId);
+        return VOS_STATUS_E_NOMEM;
+    }
+
+    for (i = 0; i < hash_elem; i++)
+        hdd_list_init(&pAdapter->sta_id_hash.bins[i], WLAN_MAX_STA_COUNT);
+
+    spin_lock_bh( &pAdapter->sta_hash_lock);
+    pAdapter->is_sta_id_hash_initialized = VOS_TRUE;
+    spin_unlock_bh( &pAdapter->sta_hash_lock);
+    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+              "%s: Station ID Hash attached for session id %d",
+              __func__, pAdapter->sessionId);
+
+    return VOS_STATUS_SUCCESS;
+}
+
+/**
+ * hdd_sta_id_hash_detach() - deinit sta_id to macaddr hash
+ * @pAdapter: adapter handle
+ *
+ * Return: vos status
+ */
+VOS_STATUS hdd_sta_id_hash_detach(hdd_adapter_t *pAdapter)
+{
+    int hash_elem, i;
+    v_SIZE_t size;
+
+    spin_lock_bh( &pAdapter->sta_hash_lock);
+    if (pAdapter->is_sta_id_hash_initialized != VOS_TRUE) {
+        spin_unlock_bh( &pAdapter->sta_hash_lock);
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+                  "%s: hash not initialized for session id %d",
+                  __func__, pAdapter->sessionId);
+        return VOS_STATUS_SUCCESS;
+    }
+
+    pAdapter->is_sta_id_hash_initialized = VOS_FALSE;
+    spin_unlock_bh( &pAdapter->sta_hash_lock);
+
+    hash_elem = 1 << pAdapter->sta_id_hash.idx_bits;
+
+    /* free all station info*/
+    for (i = 0; i < hash_elem; i++) {
+        hdd_list_size(&pAdapter->sta_id_hash.bins[i], &size);
+        if (size != 0) {
+            VOS_STATUS status;
+            hdd_staid_hash_node_t *sta_info_node = NULL;
+            hdd_staid_hash_node_t *next_node = NULL;
+            status = hdd_list_peek_front ( &pAdapter->sta_id_hash.bins[i],
+                                           (hdd_list_node_t**) &sta_info_node );
+
+            while ( NULL != sta_info_node && VOS_STATUS_SUCCESS == status )
+            {
+                status = hdd_list_remove_node( &pAdapter->sta_id_hash.bins[i],
+                                               &sta_info_node->node);
+                vos_mem_free(sta_info_node);
+
+                status = hdd_list_peek_next (&pAdapter->sta_id_hash.bins[i],
+                                            (hdd_list_node_t*)sta_info_node,
+                                            (hdd_list_node_t**)&next_node);
+                sta_info_node = next_node;
+            }
+        }
+    }
+
+    vos_mem_free(pAdapter->sta_id_hash.bins);
+    VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_INFO,
+              "%s: Station ID Hash detached for session id %d",
+              __func__, pAdapter->sessionId);
+    return VOS_STATUS_SUCCESS;
+}
+
+/**
+ * hdd_sta_id_hash_calculate_index() - derive index from macaddr
+ * @pAdapter: adapter handle
+ * @mac_addr_in: input mac address
+ *
+ * Return: index derived from mac address
+ */
+int hdd_sta_id_hash_calculate_index(hdd_adapter_t *pAdapter,
+                               v_MACADDR_t *mac_addr_in)
+{
+    uint16 index;
+    struct hdd_align_mac_addr_t * mac_addr =
+                     (struct hdd_align_mac_addr_t *)mac_addr_in;
+
+    index = mac_addr->bytes_ab ^
+            mac_addr->bytes_cd ^ mac_addr->bytes_ef;
+    index ^= index >> pAdapter->sta_id_hash.idx_bits;
+    index &= pAdapter->sta_id_hash.mask;
+    return index;
+}
+
+/**
+ * hdd_sta_id_hash_add_entry() - add entry in hash
+ * @pAdapter: adapter handle
+ * @sta_id: station id
+ * @mac_addr: mac address
+ *
+ * Return: vos status
+ */
+VOS_STATUS hdd_sta_id_hash_add_entry(hdd_adapter_t *pAdapter,
+                                    v_U8_t sta_id, v_MACADDR_t *mac_addr)
+{
+    uint16 index;
+    hdd_staid_hash_node_t *sta_info_node = NULL;
+
+    spin_lock_bh( &pAdapter->sta_hash_lock);
+    if (pAdapter->is_sta_id_hash_initialized != VOS_TRUE) {
+        spin_unlock_bh( &pAdapter->sta_hash_lock);
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "%s: hash is not initialized for session id %d",
+                  __func__, pAdapter->sessionId);
+        return VOS_STATUS_E_FAILURE;
+    }
+
+    index = hdd_sta_id_hash_calculate_index(pAdapter, mac_addr);
+    sta_info_node = vos_mem_malloc(sizeof(hdd_staid_hash_node_t));
+    if (!sta_info_node) {
+        spin_unlock_bh( &pAdapter->sta_hash_lock);
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "%s: malloc failed", __func__);
+        return VOS_STATUS_E_NOMEM;
+    }
+
+    sta_info_node->sta_id = sta_id;
+    vos_mem_copy(&sta_info_node->mac_addr, mac_addr, sizeof(v_MACADDR_t));
+
+    hdd_list_insert_back ( &pAdapter->sta_id_hash.bins[index],
+                           (hdd_list_node_t*) sta_info_node );
+    spin_unlock_bh( &pAdapter->sta_hash_lock);
+    return VOS_STATUS_SUCCESS;
+}
+
+/**
+ * hdd_sta_id_hash_remove_entry() - remove entry from hash
+ * @pAdapter: adapter handle
+ * @sta_id: station id
+ * @mac_addr: mac address
+ *
+ * Return: vos status
+ */
+VOS_STATUS hdd_sta_id_hash_remove_entry(hdd_adapter_t *pAdapter,
+                                       v_U8_t sta_id, v_MACADDR_t *mac_addr)
+{
+    uint16 index;
+    VOS_STATUS status;
+    hdd_staid_hash_node_t *sta_info_node = NULL;
+    hdd_staid_hash_node_t *next_node = NULL;
+
+    spin_lock_bh( &pAdapter->sta_hash_lock);
+    if (pAdapter->is_sta_id_hash_initialized != VOS_TRUE) {
+        spin_unlock_bh( &pAdapter->sta_hash_lock);
+        VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_ERROR,
+                  "%s: hash is not initialized for session id %d",
+                  __func__, pAdapter->sessionId);
+        return VOS_STATUS_E_FAILURE;
+    }
+
+    index = hdd_sta_id_hash_calculate_index(pAdapter, mac_addr);
+    status = hdd_list_peek_front ( &pAdapter->sta_id_hash.bins[index],
+                                   (hdd_list_node_t**) &sta_info_node );
+
+    while ( NULL != sta_info_node && VOS_STATUS_SUCCESS == status )
+    {
+        if (sta_info_node->sta_id == sta_id) {
+            status = hdd_list_remove_node( &pAdapter->sta_id_hash.bins[index],
+                                       &sta_info_node->node);
+            vos_mem_free(sta_info_node);
+            break;
+        }
+        status = hdd_list_peek_next (&pAdapter->sta_id_hash.bins[index],
+                (hdd_list_node_t*)sta_info_node, (hdd_list_node_t**)&next_node);
+        sta_info_node = next_node;
+    }
+    spin_unlock_bh( &pAdapter->sta_hash_lock);
+    return status;
+}
+
+/**
+ * hdd_sta_id_find_from_mac_addr() - find sta id from mac address
+ * @pAdapter: adapter handle
+ * @mac_addr_in: mac address
+ *
+ * Return: station id
+ */
+int hdd_sta_id_find_from_mac_addr(hdd_adapter_t *pAdapter,
+                                  v_MACADDR_t *mac_addr_in)
+{
+    uint8 is_found = 0;
+    uint8 sta_id = HDD_WLAN_INVALID_STA_ID;
+    uint16 index;
+    VOS_STATUS status;
+    hdd_staid_hash_node_t *sta_info_node = NULL;
+    hdd_staid_hash_node_t *next_node = NULL;
+
+    spin_lock_bh( &pAdapter->sta_hash_lock);
+    if (pAdapter->is_sta_id_hash_initialized != VOS_TRUE) {
+        spin_unlock_bh( &pAdapter->sta_hash_lock);
+        hddLog(VOS_TRACE_LEVEL_ERROR,
+                  FL("hash is not initialized for session id %d"),
+                  pAdapter->sessionId);
+        return HDD_WLAN_INVALID_STA_ID;
+    }
+
+    index = hdd_sta_id_hash_calculate_index(pAdapter, mac_addr_in);
+    status = hdd_list_peek_front ( &pAdapter->sta_id_hash.bins[index],
+                                   (hdd_list_node_t**) &sta_info_node );
+
+    while ( NULL != sta_info_node && VOS_STATUS_SUCCESS == status )
+    {
+        if (vos_mem_compare(&sta_info_node->mac_addr,
+                            mac_addr_in, sizeof(v_MACADDR_t))) {
+            is_found = 1;
+            sta_id = sta_info_node->sta_id;
+            break;
+        }
+        status = hdd_list_peek_next (&pAdapter->sta_id_hash.bins[index],
+                                     (hdd_list_node_t*)sta_info_node,
+                                     (hdd_list_node_t**)&next_node);
+        sta_info_node = next_node;
+    }
+    spin_unlock_bh( &pAdapter->sta_hash_lock);
+    return sta_id;
+}
+
 //Register the module init/exit functions
 module_init(hdd_module_init);
 module_exit(hdd_module_exit);