blob: 189f5456fe7357dd136b8f02d3240da1d04922d0 [file] [log] [blame]
/*
* Copyright (c) 2015-2018 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: wlan_hdd_lro.c
*
* WLAN HDD LRO interface implementation
*/
#include <wlan_hdd_includes.h>
#include <qdf_types.h>
#include <qdf_lro.h>
#include <wlan_hdd_lro.h>
#include <wlan_hdd_napi.h>
#include <wma_api.h>
#include <linux/inet_lro.h>
#include <linux/list.h>
#include <linux/random.h>
#include <net/tcp.h>
#define LRO_VALID_FIELDS \
(LRO_DESC | LRO_ELIGIBILITY_CHECKED | LRO_TCP_ACK_NUM | \
LRO_TCP_DATA_CSUM | LRO_TCP_SEQ_NUM | LRO_TCP_WIN)
#if defined(QCA_WIFI_QCA6290)
static qdf_lro_ctx_t wlan_hdd_get_lro_ctx(struct sk_buff *skb)
{
return (qdf_lro_ctx_t)QDF_NBUF_CB_RX_LRO_CTX(skb);
}
#else
static qdf_lro_ctx_t wlan_hdd_get_lro_ctx(struct sk_buff *skb)
{
struct hif_opaque_softc *hif_hdl =
(struct hif_opaque_softc *)cds_get_context(QDF_MODULE_ID_HIF);
if (hif_hdl == NULL) {
hdd_err("hif_hdl is NULL");
return NULL;
}
return hif_get_lro_info(QDF_NBUF_CB_RX_CTX_ID(skb), hif_hdl);
}
#endif
/**
* hdd_lro_rx() - LRO receive function
* @adapter: HDD adapter
* @skb: network buffer
*
* Delivers LRO eligible frames to the LRO manager
*
* Return: QDF_STATUS_SUCCESS - frame delivered to LRO manager
* QDF_STATUS_E_FAILURE - frame not delivered
*/
QDF_STATUS hdd_lro_rx(struct hdd_adapter *adapter, struct sk_buff *skb)
{
qdf_lro_ctx_t ctx;
QDF_STATUS status = QDF_STATUS_E_FAILURE;
struct qdf_lro_info info;
struct net_lro_desc *lro_desc = NULL;
if ((adapter->dev->features & NETIF_F_LRO) != NETIF_F_LRO)
return QDF_STATUS_E_NOSUPPORT;
ctx = wlan_hdd_get_lro_ctx(skb);
if (!ctx) {
hdd_err("LRO mgr is NULL");
return status;
}
info.iph = skb->data;
info.tcph = skb->data + QDF_NBUF_CB_RX_TCP_OFFSET(skb);
ctx->lro_mgr->dev = adapter->dev;
if (qdf_lro_get_info(ctx, skb, &info, (void **)&lro_desc)) {
struct net_lro_info hdd_lro_info;
hdd_lro_info.valid_fields = LRO_VALID_FIELDS;
hdd_lro_info.lro_desc = lro_desc;
hdd_lro_info.lro_eligible = 1;
hdd_lro_info.tcp_ack_num = QDF_NBUF_CB_RX_TCP_ACK_NUM(skb);
hdd_lro_info.tcp_data_csum =
csum_unfold(htons(QDF_NBUF_CB_RX_TCP_CHKSUM(skb)));
hdd_lro_info.tcp_seq_num = QDF_NBUF_CB_RX_TCP_SEQ_NUM(skb);
hdd_lro_info.tcp_win = QDF_NBUF_CB_RX_TCP_WIN(skb);
lro_receive_skb_ext(ctx->lro_mgr, skb, (void *)adapter,
&hdd_lro_info);
if (!hdd_lro_info.lro_desc->active)
qdf_lro_desc_free(ctx, lro_desc);
status = QDF_STATUS_SUCCESS;
} else {
qdf_lro_flush_pkt(ctx, &info);
}
return status;
}
/**
* hdd_lro_display_stats() - display LRO statistics
* @hdd_ctx: hdd context
*
* Return: none
*/
void hdd_lro_display_stats(struct hdd_context *hdd_ctx)
{
hdd_debug("LRO stats is broken, will fix it");
}
QDF_STATUS
hdd_lro_set_reset(struct hdd_context *hdd_ctx, struct hdd_adapter *adapter,
uint8_t enable_flag)
{
if ((hdd_ctx->ol_enable != CFG_LRO_ENABLED) ||
(adapter->device_mode != QDF_STA_MODE)) {
hdd_debug("LRO is already Disabled");
return 0;
}
if (enable_flag) {
qdf_atomic_set(&hdd_ctx->vendor_disable_lro_flag, 0);
adapter->dev->features |= NETIF_F_LRO;
} else {
/* Disable LRO, Enable tcpdelack*/
qdf_atomic_set(&hdd_ctx->vendor_disable_lro_flag, 1);
adapter->dev->features &= ~NETIF_F_LRO;
hdd_debug("LRO Disabled");
if (hdd_ctx->en_tcp_delack_no_lro) {
struct wlan_rx_tp_data rx_tp_data;
hdd_debug("Enable TCP delack as LRO is disabled");
rx_tp_data.rx_tp_flags = TCP_DEL_ACK_IND;
rx_tp_data.level = GET_CUR_RX_LVL(hdd_ctx);
wlan_hdd_send_svc_nlink_msg(hdd_ctx->radio_index,
WLAN_SVC_WLAN_TP_IND,
&rx_tp_data,
sizeof(rx_tp_data));
hdd_ctx->en_tcp_delack_no_lro = 1;
}
}
return 0;
}