wlan: Recovery logic for bigger received packet

In current code, kernel crash if DXE copied more then VPKT_SIZE_BUFFER.
Rather then crashing kernel, we can recover by discarding
this packet. We are storing RXBD, PMI bytes, skb head and tail
in a global circular buffer before discarding this packet. When
DXE copies more then 4096 bytes, no point in recovering as it
can corrupt next packet. So crash only when packet lengh is
more then one page(4096 bytes).

Change-Id: I8a676ca6f41d1bbaf3f76b9eb7c1efbbadd5c2ec
CRs-Fixed: 968395
diff --git a/CORE/VOSS/inc/vos_packet.h b/CORE/VOSS/inc/vos_packet.h
index 2fe96d9..3aa0a33 100644
--- a/CORE/VOSS/inc/vos_packet.h
+++ b/CORE/VOSS/inc/vos_packet.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2014 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1134,4 +1134,40 @@
    void  *pskb,
    v_U8_t tracking_map
 );
+
+/**
+  @brief vos_get_pkt_head() - Get skb head pointer
+
+  @param
+       pPacket - the voss Packet to operate on
+  @return
+       v_PVOID_t - skb head pointer
+
+*/
+v_PVOID_t vos_get_pkt_head(vos_pkt_t *pPacket);
+
+/**
+
+  @brief vos_get_pkt_end() - Get skb end pointer
+
+  @param
+       pPacket - the voss Packet to operate on
+  @return
+       v_PVOID_t - skb end pointer
+
+*/
+v_PVOID_t vos_get_pkt_end(vos_pkt_t *pPacket);
+
+/**
+
+  @brief vos_recover_tail() - Recover corrupted tail of skb
+
+  @param
+       pPacket - the voss Packet to operate on
+  @return
+       v_VOID_t - None
+
+*/
+v_VOID_t vos_recover_tail(vos_pkt_t *pPacket);
+
 #endif  // !defined( __VOS_PKT_H )
diff --git a/CORE/VOSS/src/vos_packet.c b/CORE/VOSS/src/vos_packet.c
index 5dba8ac..43daf9f 100644
--- a/CORE/VOSS/src/vos_packet.c
+++ b/CORE/VOSS/src/vos_packet.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2014 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2016 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -59,6 +59,8 @@
 #define VOS_PKT_PROT_DHCP_CLI_PORT   68
 #define VOS_PKT_PROT_EAPOL_ETH_TYPE  0x888E
 #define VOS_PKT_PROT_ARP_ETH_TYPE    0x0806
+#define VOS_PKT_GET_HEAD(skb)        (skb->head)
+#define VOS_PKT_GET_END(skb)         (skb->end)
 
 /*--------------------------------------------------------------------------
   Type declarations
@@ -3097,6 +3099,87 @@
    /* Protocol type map */
    return pkt_proto_type;
 }
+
+v_PVOID_t vos_get_pkt_head(vos_pkt_t *pPacket)
+{
+   struct sk_buff *skb;
+
+   // Validate the parameter pointers
+   if (unlikely(NULL == pPacket))
+   {
+      VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
+                "VPKT [%d]: NULL pointer", __LINE__);
+      return NULL;
+   }
+
+   if ( VOS_STATUS_SUCCESS !=
+        vos_pkt_get_os_packet(pPacket, (void**)&skb, VOS_FALSE ))
+   {
+      VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
+                "OS-PKT [%d]: OS PKT pointer is NULL", __LINE__);
+      return NULL;
+   }
+
+   return VOS_PKT_GET_HEAD(skb);
+}
+
+v_PVOID_t vos_get_pkt_end(vos_pkt_t *pPacket)
+{
+   struct sk_buff *skb;
+
+    // Validate the parameter pointers
+   if (unlikely(NULL == pPacket))
+   {
+      VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
+                "VPKT [%d]: NULL pointer", __LINE__);
+      return NULL;
+   }
+
+   if ( VOS_STATUS_SUCCESS !=
+        vos_pkt_get_os_packet(pPacket, (void**)&skb, VOS_FALSE ))
+   {
+     VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
+                "OS-PKT [%d]: OS PKT pointer is NULL", __LINE__);
+     return NULL;
+   }
+
+ /* find end point if skb->end is an offset */
+#ifdef NET_SKBUFF_DATA_USES_OFFSET
+   return VOS_PKT_GET_HEAD(skb) + VOS_PKT_GET_END(skb);
+#else
+   return VOS_PKT_GET_END(skb);
+#endif
+}
+
+v_VOID_t vos_recover_tail(vos_pkt_t *pPacket)
+{
+   struct skb_shared_info *shinfo;
+   struct sk_buff *skb;
+
+   // Validate the parameter pointers
+   if (unlikely(NULL == pPacket))
+   {
+      VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
+                "VPKT [%d]: NULL pointer", __LINE__);
+      return;
+   }
+
+   if ( VOS_STATUS_SUCCESS !=
+        vos_pkt_get_os_packet(pPacket, (void**)&skb, VOS_FALSE ))
+   {
+      VOS_TRACE(VOS_MODULE_ID_VOSS, VOS_TRACE_LEVEL_FATAL,
+                "OS-PKT [%d]: OS PKT pointer is NULL", __LINE__);
+      return;
+   }
+
+   shinfo = skb_shinfo(skb);
+   memset(shinfo, 0, sizeof(struct skb_shared_info));
+   atomic_set(&shinfo->dataref, 1);
+   kmemcheck_annotate_variable(shinfo->destructor_arg);
+
+   return;
+}
+
 #ifdef VOS_PACKET_UNIT_TEST
 #include "vos_packet_test.c"
 #endif
diff --git a/CORE/WDI/TRP/DTS/src/wlan_qct_wdi_dts.c b/CORE/WDI/TRP/DTS/src/wlan_qct_wdi_dts.c
index 26b3ab8..c18bced 100644
--- a/CORE/WDI/TRP/DTS/src/wlan_qct_wdi_dts.c
+++ b/CORE/WDI/TRP/DTS/src/wlan_qct_wdi_dts.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2011-2016 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -76,6 +76,25 @@
    uint32 tputBpus;  //unit in Bytes per usec: round off to integral value
 }WDTS_RateInfo;
 
+#define WDTS_MAX_NUMBER_OF_RX_PKT 5
+#define WDTS_MAX_PAGE_SIZE 4096
+#define WDTS_MAX_RXDB_DATA_SIZE 128
+
+struct WDTS_RxPktInfo
+{
+    uint8 rx_bd[WDTS_MAX_RXDB_DATA_SIZE];
+    void *pFrame_head;
+    void *pFrame_tail;
+    uint32 pFrame_len;
+};
+
+static struct WDTS_PktInfoBuff
+{
+    struct WDTS_RxPktInfo PktInfo[WDTS_MAX_NUMBER_OF_RX_PKT];
+    uint32 current_count;
+    uint8 current_position;
+}WDTS_Pkt_Data_Buff = { .current_position = 0, .current_count = 0 };
+
 #define WDTS_MAX_RATE_NUM               137
 #define WDTS_MAX_11B_RATE_NUM           8
 #define MB_PER_SEC_TO_BYTES_PER_MSEC    13
@@ -516,6 +535,56 @@
 #endif
 }
 
+/* Store RXBD, skb lenght, skb head, and skb end offset to global buffer.
+ * This function should  be invoked when MPDU lenght + MPDU herader Offset
+ * if higher then 3872 bytes.
+ * Parameters:
+ * pFrame:Refernce to PAL frame.
+ * pBDHeader: BD header for PAL Frame.
+ * Return Value: v_VOID_t
+ *
+ */
+v_VOID_t
+WDTS_StoreMetaInfo(wpt_packet *pFrame, wpt_uint8 *pBDHeader)
+{
+    wpt_uint8  usMPDUHLen;
+    wpt_boolean usAsf, usAef, usLsf, usESF;
+    wpt_uint16 usMPDULen;
+    wpt_uint32 usPmiCmd24to25;
+    struct WDTS_RxPktInfo *current_data =
+           &WDTS_Pkt_Data_Buff.PktInfo[WDTS_Pkt_Data_Buff.current_position];
+
+    vos_mem_copy(current_data->rx_bd, (void*)wpalPacketGetRawBuf(pFrame),
+                                                       WDTS_MAX_RXDB_DATA_SIZE);
+
+    usMPDULen = (wpt_uint16)WDI_RX_BD_GET_MPDU_LEN(pBDHeader);
+    usMPDUHLen = (wpt_uint8)WDI_RX_BD_GET_MPDU_H_LEN(pBDHeader);
+    usAsf = (wpt_boolean)WDI_RX_BD_GET_ASF(pBDHeader);
+    usAef = (wpt_boolean)WDI_RX_BD_GET_AEF(pBDHeader);
+    usLsf = (wpt_boolean)WDI_RX_BD_GET_LSF(pBDHeader);
+    usESF = (wpt_boolean)WDI_RX_BD_GET_ESF(pBDHeader);
+    usPmiCmd24to25 = (wpt_uint32)WDI_RX_BD_GET_PMICMD_24TO25(pBDHeader);
+
+    current_data->pFrame_head = wpalGetOSPktHead(pFrame);
+    current_data->pFrame_tail = wpalGetOSPktend(pFrame);
+    current_data->pFrame_len = wpalPacketGetLength(pFrame);
+
+    WDTS_Pkt_Data_Buff.current_count++;
+
+    /* Dump packet info */
+    VOS_TRACE(VOS_MODULE_ID_WDI, VOS_TRACE_LEVEL_ERROR,
+                  "count: %d usMPDULen: 0x%x, usMPDUHLen: 0x%x, usAsf: %x,"
+                  "usAef: %x, usLsf: 0x%x, usESF: 0x%x, usPmiCmd24to25: 0x%x,"
+                  "skb_len: 0x%x",WDTS_Pkt_Data_Buff.current_count, usMPDULen,
+                  usMPDUHLen, usAsf, usAef, usLsf, usESF, usPmiCmd24to25,
+                  current_data->pFrame_len);
+
+    WDTS_Pkt_Data_Buff.current_position++;
+    if(WDTS_Pkt_Data_Buff.current_position >= WDTS_MAX_NUMBER_OF_RX_PKT)
+       WDTS_Pkt_Data_Buff.current_position = 0;
+
+    return;
+}
 
 /* DTS Rx packet function. 
  * This function should be invoked by the transport device to indicate 
@@ -607,18 +676,29 @@
   // Special handling for frames which contain logging information
   if (WDTS_CHANNEL_RX_LOG == channel)
   {
-      if(VPKT_SIZE_BUFFER_ALIGNED < (usMPDULen+ucMPDUHOffset)){
-        WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_FATAL,
+      if (VPKT_SIZE_BUFFER_ALIGNED < (usMPDULen+ucMPDUHOffset))
+      {
+          /* Size of the packet tranferred by the DMA engine is
+           * greater than the the memory allocated for the skb
+           * Recover the SKB  case of length is in same memory page
+           */
+          WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_FATAL,
                    "Invalid Frame size, might memory corrupted(%d+%d/%d)",
                    usMPDULen, ucMPDUHOffset, VPKT_SIZE_BUFFER_ALIGNED);
 
-        /* Size of the packet tranferred by the DMA engine is
-         * greater than the the memory allocated for the skb
-         */
-        WPAL_BUG(0);
+          // Store RXBD,  skb head, tail and skb lenght in circular buffer
+          WDTS_StoreMetaInfo(pFrame, pBDHeader);
 
-        wpalPacketFree(pFrame);
-        return eWLAN_PAL_STATUS_SUCCESS;
+          if ((usMPDULen+ucMPDUHOffset) <= WDTS_MAX_PAGE_SIZE)
+          {
+              wpalRecoverTail(pFrame);
+              wpalPacketFree(pFrame);
+          } else {
+              //Recovery may cause adjoining buffer corruption
+              WPAL_BUG(0);
+          }
+
+          return eWLAN_PAL_STATUS_SUCCESS;
       }
 
       /* Firmware should send the Header offset as length
@@ -662,19 +742,31 @@
         ucMPDUHOffset = usMPDUDOffset;
       }
 
-      if(VPKT_SIZE_BUFFER_ALIGNED < (usMPDULen+ucMPDUHOffset)){
-        WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_FATAL,
+      if (VPKT_SIZE_BUFFER_ALIGNED < (usMPDULen+ucMPDUHOffset))
+      {
+          /* Size of the packet tranferred by the DMA engine is
+           * greater than the the memory allocated for the skb
+           * Recover the SKB  case of length is in same memory page
+           */
+          WPAL_TRACE(eWLAN_MODULE_DAL_DATA, eWLAN_PAL_TRACE_LEVEL_FATAL,
                    "Invalid Frame size, might memory corrupted(%d+%d/%d)",
                    usMPDULen, ucMPDUHOffset, VPKT_SIZE_BUFFER_ALIGNED);
 
-        /* Size of the packet tranferred by the DMA engine is
-         * greater than the the memory allocated for the skb
-         */
-        WPAL_BUG(0);
+          // Store RXBD,  skb head, tail and skb lenght in circular buffer
+          WDTS_StoreMetaInfo(pFrame, pBDHeader);
 
-        wpalPacketFree(pFrame);
-        return eWLAN_PAL_STATUS_SUCCESS;
+          if ((usMPDULen+ucMPDUHOffset) <= WDTS_MAX_PAGE_SIZE)
+          {
+              wpalRecoverTail(pFrame);
+              wpalPacketFree(pFrame);
+          } else {
+              //Recovery may cause adjoining buffer corruption
+              WPAL_BUG(0);
+          }
+
+          return eWLAN_PAL_STATUS_SUCCESS;
       }
+
       if(eWLAN_PAL_STATUS_SUCCESS != wpalPacketSetRxLength(pFrame, usMPDULen+ucMPDUHOffset))
       {
           DTI_TRACE( DTI_TRACE_LEVEL_ERROR, "Invalid Frame Length, Frame dropped..");
diff --git a/CORE/WDI/WPAL/inc/wlan_qct_pal_packet.h b/CORE/WDI/WPAL/inc/wlan_qct_pal_packet.h
index 9a2dea1..e57fda9 100644
--- a/CORE/WDI/WPAL/inc/wlan_qct_pal_packet.h
+++ b/CORE/WDI/WPAL/inc/wlan_qct_pal_packet.h
@@ -464,4 +464,34 @@
    void *perPktStat
 );
 
+/*---------------------------------------------------------------------------
+    wpalGetOSPktHead – Get the head of OS spacific socket buffer
+    Param:
+        pPacket – pointer to a wpt_packet
+
+    Return:
+        void* - success
+---------------------------------------------------------------------------*/
+void* wpalGetOSPktHead( wpt_packet *pPacket);
+
+/*---------------------------------------------------------------------------
+     wpalGetOSPktend – Get end pointer of OS spacific socket buffer
+    Param:
+        pPacket – pointer to a wpt_packet
+
+    Return:
+        void*  - success
+---------------------------------------------------------------------------*/
+void* wpalGetOSPktend( wpt_packet *pPacket);
+
+/*---------------------------------------------------------------------------
+    wpalRecoverTail – recover currupted skb tail.
+    Param:
+        pPacket – pointer to a wpt_packet
+
+    Return:
+        void  - success
+---------------------------------------------------------------------------*/
+void wpalRecoverTail( wpt_packet *pPacket);
+
 #endif // __WLAN_QCT_PAL_PACKET_H
diff --git a/CORE/WDI/WPAL/src/wlan_qct_pal_packet.c b/CORE/WDI/WPAL/src/wlan_qct_pal_packet.c
index c8dcf28..da46cba 100644
--- a/CORE/WDI/WPAL/src/wlan_qct_pal_packet.c
+++ b/CORE/WDI/WPAL/src/wlan_qct_pal_packet.c
@@ -441,6 +441,45 @@
    }
 }/*wpalPacketSetRxLength*/
 
+void wpalRecoverTail(wpt_packet *pFrame)
+{
+   // Validate the parameter pointers
+   if (unlikely(NULL == pFrame))
+   {
+      WPAL_TRACE(eWLAN_MODULE_PAL, eWLAN_PAL_TRACE_LEVEL_ERROR,
+                "%s : NULL packet pointer", __func__);
+      return;
+   }
+
+   return vos_recover_tail(WPAL_TO_VOS_PKT(pFrame));
+}
+
+void* wpalGetOSPktHead(wpt_packet *pFrame)
+{
+   // Validate the parameter pointers
+   if (unlikely(NULL == pFrame))
+   {
+      WPAL_TRACE(eWLAN_MODULE_PAL, eWLAN_PAL_TRACE_LEVEL_ERROR,
+                "%s : NULL packet pointer", __func__);
+      return NULL;
+   }
+
+   return vos_get_pkt_head(WPAL_TO_VOS_PKT(pFrame));
+}
+
+void* wpalGetOSPktend(wpt_packet *pFrame)
+{
+   // Validate the parameter pointers
+   if (unlikely(NULL == pFrame))
+   {
+      WPAL_TRACE(eWLAN_MODULE_PAL, eWLAN_PAL_TRACE_LEVEL_ERROR,
+                "%s : NULL packet pointer", __func__);
+      return 0;
+   }
+
+   return vos_get_pkt_end(WPAL_TO_VOS_PKT(pFrame));
+}
+
 /*
   Set of helper functions that will prepare packet for DMA transfer,
   based on the type of transfer : - to and from the device