| /* |
| * 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; |
| } |
| |
| |