wlan: Dynamically change tcp delay ack using hdd stats
We start a periodic timer in which we monitor number of rx
packets received. Using this count we decide bus width/rx_level.
We send this throughput level to cnss-daemon so that daemon can
dynamically set tcp delay ack value based on current receive
traffic situation to improve tcp performace. Added ini item to
enable this feature.
CRs-Fixed: 763246
Change-Id: I61f8a55ec9215a8fe86433d9f34a8ee7082a04bb
diff --git a/CORE/HDD/src/wlan_hdd_main.c b/CORE/HDD/src/wlan_hdd_main.c
index 6795ca1..cfd03d2 100755
--- a/CORE/HDD/src/wlan_hdd_main.c
+++ b/CORE/HDD/src/wlan_hdd_main.c
@@ -7072,6 +7072,7 @@
INIT_DELAYED_WORK(&pAdapter->roc_work,
hdd_p2p_roc_work_queue);
}
+
break;
}
case WLAN_HDD_MONITOR:
@@ -8761,6 +8762,17 @@
"%s: Cannot deallocate Traffic monitor timer", __func__);
}
+ if (VOS_TIMER_STATE_RUNNING ==
+ vos_timer_getCurrentState(&pHddCtx->delack_timer)) {
+ vos_timer_stop(&pHddCtx->delack_timer);
+ }
+
+ if (!VOS_IS_STATUS_SUCCESS(vos_timer_destroy(
+ &pHddCtx->delack_timer))) {
+ hddLog(VOS_TRACE_LEVEL_ERROR,
+ "%s: Cannot deallocate Bus bandwidth timer", __func__);
+ }
+
//Disable IMPS/BMPS as we do not want the device to enter any power
//save mode during shutdown
sme_DisablePowerSave(pHddCtx->hHal, ePMC_IDLE_MODE_POWER_SAVE);
@@ -9261,6 +9273,14 @@
nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr)));
skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr)));
break;
+ case WLAN_SVC_WLAN_TP_IND:
+ ani_hdr->length = len;
+ nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr)
+ + len));
+ nl_data = (char *)ani_hdr + sizeof(tAniMsgHdr);
+ memcpy(nl_data, data, len);
+ skb_put(skb, NLMSG_SPACE(sizeof(tAniMsgHdr) + len));
+ break;
case WLAN_MSG_RPS_ENABLE_IND:
ani_hdr->length = len;
nlh->nlmsg_len = NLMSG_LENGTH((sizeof(tAniMsgHdr) + len));
@@ -9280,7 +9300,68 @@
return;
}
+/**
+ * hdd_request_tcp_delack() - Find the Delack value based on RX packet
+ * @pHddCtx: Valid Global HDD context pointer
+ * @rx_packets: Number of RX packet in perticular time
+ *
+ * Based on the RX packet this function calculate next value of tcp delack.
+ * This function compare rx packet value to high and low threshold limit.
+ *
+ * Return: void
+ */
+void hdd_request_tcp_delack(hdd_context_t *pHddCtx, uint64_t rx_packets)
+{
+ /* average of rx_packets and prev_rx is taken so that
+ bus width doesnot fluctuate much */
+ uint64_t temp_rx = (rx_packets + pHddCtx->prev_rx)/2;
+ TP_IND_TYPE next_rx_level = pHddCtx->cur_rx_level;
+ pHddCtx->prev_rx = rx_packets;
+ if (temp_rx > pHddCtx->cfg_ini->tcpDelAckThresholdHigh)
+ next_rx_level = TP_IND_HIGH;
+ else if (temp_rx <= pHddCtx->cfg_ini->tcpDelAckThresholdLow)
+ next_rx_level = TP_IND_LOW;
+
+ hdd_set_delack_value(pHddCtx, next_rx_level);
+}
+
+#define HDD_BW_GET_DIFF(x, y) ((x) >= (y) ? (x) - (y) : (ULONG_MAX - (y) + (x)))
+
+/**
+ * hdd_tcp_delack_compute_function() - get link status
+ * @priv: Valid Global HDD context pointer
+ *
+ * This function find number of RX packet during timer life span.
+ * It request tcp delack with number of RX packet and re-configure delack timer
+ * for tcpDelAckComputeInterval timer interval.
+ *
+ * Return: void
+ */
+void hdd_tcp_delack_compute_function(void *priv)
+{
+ hdd_context_t *pHddCtx = (hdd_context_t *)priv;
+ hdd_adapter_t *pAdapter = NULL;
+ v_U32_t rx_packets = 0;
+ hdd_adapter_list_node_t *pAdapterNode = NULL;
+ VOS_STATUS status = 0;
+
+ for (status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
+ NULL != pAdapterNode && VOS_STATUS_SUCCESS == status;
+ status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pAdapterNode)) {
+ if ((pAdapter = pAdapterNode->pAdapter) == NULL)
+ continue;
+
+ rx_packets += HDD_BW_GET_DIFF(pAdapter->stats.rx_packets,
+ pAdapter->prev_rx_packets);
+ pAdapter->prev_rx_packets = pAdapter->stats.rx_packets;
+ }
+
+ hdd_request_tcp_delack(pHddCtx, rx_packets);
+
+ vos_timer_start(&pHddCtx->delack_timer,
+ pHddCtx->cfg_ini->tcpDelAckComputeInterval);
+}
/**---------------------------------------------------------------------------
@@ -10497,6 +10578,10 @@
}
wlan_hdd_cfg80211_nan_init(pHddCtx);
+ mutex_init(&pHddCtx->cur_rx_level_lock);
+ vos_timer_init(&pHddCtx->delack_timer, VOS_TIMER_TYPE_SW,
+ hdd_tcp_delack_compute_function,(void *)pHddCtx);
+
#ifdef WLAN_FEATURE_EXTSCAN
sme_EXTScanRegisterCallback(pHddCtx->hHal,
wlan_hdd_cfg80211_extscan_callback,
@@ -11403,6 +11488,7 @@
* --------------------------------------------------------------------------*/
void wlan_hdd_decr_active_session(hdd_context_t *pHddCtx, tVOS_CON_MODE mode)
{
+
switch (mode) {
case VOS_STA_MODE:
case VOS_P2P_CLIENT_MODE:
@@ -11615,6 +11701,178 @@
return status;
}
+/**
+ * hdd_get_total_sessions() - provide total number of active sessions
+ * @pHddCtx: Valid Global HDD context pointer
+ *
+ * This function iterates through pAdaptors and find the number of all active
+ * sessions. This active sessions includes connected sta, p2p client and number
+ * of client connected to sap/p2p go.
+ *
+ * Return: Total number of active sessions.
+ */
+v_U8_t hdd_get_total_sessions(hdd_context_t *pHddCtx)
+{
+ v_U8_t active_session = 0;
+ hdd_station_ctx_t *pHddStaCtx;
+ hdd_adapter_list_node_t *pAdapterNode, *pNext;
+ hdd_adapter_t *pAdapter;
+ VOS_STATUS status;
+
+ status = hdd_get_front_adapter(pHddCtx, &pAdapterNode);
+ while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) {
+ pAdapter = pAdapterNode->pAdapter;
+ switch (pAdapter->device_mode) {
+ case VOS_STA_MODE:
+ case VOS_P2P_CLIENT_MODE:
+ pHddStaCtx = WLAN_HDD_GET_STATION_CTX_PTR(pAdapter);
+ if(eConnectionState_Associated == pHddStaCtx->conn_info.connState)
+ active_session += 1;
+ break;
+ case VOS_STA_SAP_MODE:
+ case VOS_P2P_GO_MODE:
+ active_session += hdd_softap_get_connected_sta(pAdapter);
+ break;
+ default:
+ break;
+ }
+
+ status = hdd_get_next_adapter(pHddCtx, pAdapterNode, &pNext);
+ pAdapterNode = pNext;
+ }
+
+ return active_session;
+}
+
+/**
+ * hdd_set_delack_value() - Set delack value
+ * @pHddCtx: Valid Global HDD context pointer
+ * @next_rx_level: Value to set for delack
+ *
+ * This function compare present value and next value of delack. If the both
+ * are diffrent then it sets next value .
+ *
+ * Return: void.
+ */
+void hdd_set_delack_value(hdd_context_t *pHddCtx, v_U32_t next_rx_level)
+{
+ if (pHddCtx->cur_rx_level != next_rx_level) {
+ VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
+ "%s: TCP DELACK trigger level %d",
+ __func__, next_rx_level);
+ mutex_lock(&pHddCtx->cur_rx_level_lock);
+ pHddCtx->cur_rx_level = next_rx_level;
+ mutex_unlock(&pHddCtx->cur_rx_level_lock);
+ wlan_hdd_send_svc_nlink_msg(WLAN_SVC_WLAN_TP_IND, &next_rx_level,
+ sizeof(next_rx_level));
+ }
+}
+
+/**
+ * hdd_set_default_stop_delack_timer() - Start delack timer
+ * @pHddCtx: Valid Global HDD context pointer
+ *
+ * This function stop delack timer and set delack value to default..
+ *
+ * Return: void.
+ */
+
+void hdd_set_default_stop_delack_timer(hdd_context_t *pHddCtx)
+{
+ if (VOS_TIMER_STATE_RUNNING !=
+ vos_timer_getCurrentState(&pHddCtx->delack_timer)) {
+ VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
+ "%s: Can not stop timer", __func__);
+ return;
+ }
+
+ vos_timer_stop(&pHddCtx->delack_timer);
+ hdd_set_delack_value(pHddCtx, TP_IND_LOW);
+}
+
+/**
+ * hdd_start_delack_timer() - Start delack timer
+ * @pHddCtx: Valid Global HDD context pointer
+ *
+ * This function starts the delack timer for tcpDelAckComputeInterval time
+ * interval.The default timer value is 2 second.
+ *
+ * Return: void.
+ */
+void hdd_start_delack_timer(hdd_context_t *pHddCtx)
+{
+ if (VOS_TIMER_STATE_RUNNING ==
+ vos_timer_getCurrentState(&pHddCtx->delack_timer)) {
+ VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
+ "%s: Timer is already running", __func__);
+ return;
+ }
+
+ vos_timer_start(&pHddCtx->delack_timer,
+ pHddCtx->cfg_ini->tcpDelAckComputeInterval);
+}
+
+/**
+ * hdd_update_prev_rx_packet_count() - Update previous rx packet count
+ * @pHddCtx: Valid Global HDD context pointer
+ *
+ * This function updates the prev_rx_packets count from the corresponding
+ * pAdapter states. This prev_rx_packets will diffed with the packet count
+ * at the end of delack timer. That can give number of RX packet is spacific
+ * time.
+ *
+ * Return: void.
+ */
+void hdd_update_prev_rx_packet_count(hdd_context_t *pHddCtx)
+{
+ hdd_adapter_list_node_t *pAdapterNode, *pNext;
+ hdd_adapter_t *pAdapter;
+ VOS_STATUS status;
+
+ status = hdd_get_front_adapter ( pHddCtx, &pAdapterNode );
+ while (NULL != pAdapterNode && VOS_STATUS_SUCCESS == status) {
+ pAdapter = pAdapterNode->pAdapter;
+ pAdapter->prev_rx_packets = pAdapter->stats.rx_packets;
+ status = hdd_get_next_adapter ( pHddCtx, pAdapterNode, &pNext );
+ pAdapterNode = pNext;
+ }
+}
+
+/**
+ * hdd_manage_delack_timer() - start\stop delack timer
+ * @pHddCtx: Valid Global HDD context pointer
+ *
+ * This function check the number of concerent session present, it starts the
+ * delack timer if only one session is present.
+ * In the case of BT_COEX and TDLS mode it blindly stop delack functionality.
+ *
+ * Return: void.
+ */
+void hdd_manage_delack_timer(hdd_context_t *pHddCtx)
+{
+ uint8_t sessions;
+
+ if (!pHddCtx->cfg_ini->enable_delack) {
+ VOS_TRACE(VOS_MODULE_ID_HDD, VOS_TRACE_LEVEL_DEBUG,
+ "%s: TCP DELACK is not enabled", __func__);
+ return;
+ }
+
+ /* Blindly stop timer of BTCOEX and TDLS Session is up */
+ if (pHddCtx->mode != 0) {
+ hdd_set_default_stop_delack_timer(pHddCtx);
+ return;
+ }
+
+ sessions = hdd_get_total_sessions(pHddCtx);
+ if (sessions == 1) {
+ hdd_update_prev_rx_packet_count(pHddCtx);
+ hdd_start_delack_timer(pHddCtx);
+ } else {
+ hdd_set_default_stop_delack_timer(pHddCtx);
+ }
+}
+
/**---------------------------------------------------------------------------
*
* \brief wlan_hdd_init_channels