blob: b1fe8f078d52e59dd09e584130a7fa817688d916 [file] [log] [blame]
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08001/*
Jeff Johnson103cc5b2016-01-08 15:42:27 -08002 * Copyright (c) 2015-2016 The Linux Foundation. All rights reserved.
Prakash Dhavali7090c5f2015-11-02 17:55:19 -08003 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for
7 * any purpose with or without fee is hereby granted, provided that the
8 * above copyright notice and this permission notice appear in all
9 * copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
12 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
13 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
15 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
16 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
17 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
18 * PERFORMANCE OF THIS SOFTWARE.
19 */
20
21/*
22 * This file was originally distributed by Qualcomm Atheros, Inc.
23 * under proprietary terms before Copyright ownership was assigned
24 * to the Linux Foundation.
25 */
26
27/**
28 * DOC: wlan_hdd_lro.c
29 *
30 * WLAN HDD LRO interface implementation
31 */
32
Jeff Johnson103cc5b2016-01-08 15:42:27 -080033/* denote that this file does not allow legacy hddLog */
34#define HDD_DISALLOW_LEGACY_HDDLOG 1
35
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080036#include <wlan_hdd_includes.h>
Anurag Chouhan6d760662016-02-20 16:05:43 +053037#include <qdf_types.h>
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080038#include <wlan_hdd_lro.h>
39#include <wlan_hdd_napi.h>
40#include <wma_api.h>
41#include <ol_txrx_types.h>
42#include <ol_cfg.h>
Dhanashri Atreb08959a2016-03-01 17:28:03 -080043#include <cdp_txrx_lro.h>
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080044
45#include <linux/inet_lro.h>
46#include <linux/list.h>
47#include <linux/random.h>
48#include <net/tcp.h>
49
50#define LRO_MAX_AGGR_SIZE 100
51
52#define LRO_VALID_FIELDS \
53 (LRO_DESC | LRO_ELIGIBILITY_CHECKED | LRO_TCP_ACK_NUM | \
54 LRO_TCP_DATA_CSUM | LRO_TCP_SEQ_NUM | LRO_TCP_WIN)
55
56/**
57 * hdd_lro_get_skb_header() - LRO callback function
58 * @skb: network buffer
59 * @ip_hdr: contains a pointer to the IP header
60 * @tcpudp_hdr: contains a pointer to the TCP header
61 * @hdr_flags: indicates if this is a TCP, IPV4 frame
62 * @priv: private driver specific opaque pointer
63 *
64 * Get the IP and TCP headers from the skb
65 *
66 * Return: 0 - success, < 0 - failure
67 */
68static int hdd_lro_get_skb_header(struct sk_buff *skb, void **ip_hdr,
69 void **tcpudp_hdr, u64 *hdr_flags, void *priv)
70{
Anurag Chouhanf04e84f2016-03-03 10:12:12 +053071 if (QDF_NBUF_CB_RX_IPV6_PROTO(skb)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080072 hdr_flags = 0;
73 return -EINVAL;
74 }
75
76 *hdr_flags |= (LRO_IPV4 | LRO_TCP);
77 (*ip_hdr) = skb->data;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +053078 (*tcpudp_hdr) = skb->data + QDF_NBUF_CB_RX_TCP_OFFSET(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080079 return 0;
80}
81
82/**
83 * hdd_lro_desc_pool_init() - Initialize the free pool of LRO
84 * descriptors
85 * @lro_desc_pool: free pool of the LRO descriptors
86 * @lro_mgr: LRO manager
87 *
88 * Initialize a list that holds the free LRO descriptors
89 *
90 * Return: none
91 */
92static void hdd_lro_desc_pool_init(struct hdd_lro_desc_pool *lro_desc_pool,
93 struct net_lro_mgr *lro_mgr)
94{
95 int i;
96
97 INIT_LIST_HEAD(&lro_desc_pool->lro_free_list_head);
98
99 for (i = 0; i < LRO_DESC_POOL_SZ; i++) {
100 lro_desc_pool->lro_desc_array[i].lro_desc =
101 &lro_mgr->lro_arr[i];
102 list_add_tail(&lro_desc_pool->lro_desc_array[i].lro_node,
103 &lro_desc_pool->lro_free_list_head);
104 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530105 qdf_spinlock_create(&lro_desc_pool->lro_pool_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800106}
107
108/**
109 * hdd_lro_desc_info_init() - Initialize the LRO descriptors
110 * @hdd_info: HDD LRO data structure
111 *
112 * Initialize the free pool of LRO descriptors and the entries
113 * of the hash table
114 *
115 * Return: none
116 */
117static void hdd_lro_desc_info_init(struct hdd_lro_s *hdd_info)
118{
119 int i;
120
121 /* Initialize pool of free LRO desc.*/
122 hdd_lro_desc_pool_init(&hdd_info->lro_desc_info.lro_desc_pool,
123 hdd_info->lro_mgr);
124
125 /* Initialize the hash table of LRO desc.*/
126 for (i = 0; i < LRO_DESC_TABLE_SZ; i++) {
127 /* initialize the flows in the hash table */
128 INIT_LIST_HEAD(&hdd_info->lro_desc_info.
129 lro_hash_table[i].lro_desc_list);
130 }
131
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530132 qdf_spinlock_create(&hdd_info->lro_desc_info.lro_hash_lock);
Manjunathappa Prakashcf26ae62016-08-12 14:31:58 -0700133 qdf_spinlock_create(&hdd_info->lro_mgr_arr_access_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800134}
135
136/**
137 * hdd_lro_desc_pool_deinit() - Free the LRO descriptor list
138 * @hdd_info: HDD LRO data structure
139 *
140 * Free the pool of LRO descriptors
141 *
142 * Return: none
143 */
144static void hdd_lro_desc_pool_deinit(struct hdd_lro_desc_pool *lro_desc_pool)
145{
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800146 INIT_LIST_HEAD(&lro_desc_pool->lro_free_list_head);
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530147 qdf_spinlock_destroy(&lro_desc_pool->lro_pool_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800148}
149
150/**
151 * hdd_lro_desc_info_deinit() - Deinitialize the LRO descriptors
152 *
153 * @hdd_info: HDD LRO data structure
154 *
155 * Deinitialize the free pool of LRO descriptors and the entries
156 * of the hash table
157 *
158 * Return: none
159 */
160static void hdd_lro_desc_info_deinit(struct hdd_lro_s *hdd_info)
161{
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800162 struct hdd_lro_desc_info *desc_info = &hdd_info->lro_desc_info;
163
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800164 hdd_lro_desc_pool_deinit(&desc_info->lro_desc_pool);
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530165 qdf_spinlock_destroy(&desc_info->lro_hash_lock);
Manjunathappa Prakashcf26ae62016-08-12 14:31:58 -0700166 qdf_spinlock_destroy(&hdd_info->lro_mgr_arr_access_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800167}
168
169/**
170 * hdd_lro_tcp_flow_match() - function to check for a flow match
171 * @iph: IP header
172 * @tcph: TCP header
173 * @lro_desc: LRO decriptor
174 *
175 * Checks if the descriptor belongs to the same flow as the one
176 * indicated by the TCP and IP header.
177 *
178 * Return: true - flow match, false - flow does not match
179 */
180static inline bool hdd_lro_tcp_flow_match(struct net_lro_desc *lro_desc,
181 struct iphdr *iph,
182 struct tcphdr *tcph)
183{
184 if ((lro_desc->tcph->source != tcph->source) ||
185 (lro_desc->tcph->dest != tcph->dest) ||
186 (lro_desc->iph->saddr != iph->saddr) ||
187 (lro_desc->iph->daddr != iph->daddr))
188 return false;
189
190 return true;
191
192}
193
194/**
195 * hdd_lro_desc_find() - LRO descriptor look-up function
196 *
197 * @adapter: HDD adaptor
198 * @skb: network buffer
199 * @iph: IP header
200 * @tcph: TCP header
201 * @lro_desc: contains a pointer to the LRO decriptor
202 *
203 * Look-up the LRO descriptor in the hash table based on the
204 * flow ID toeplitz. If the flow is not found, allocates a new
205 * LRO descriptor and places it in the hash table
206 *
207 * Return: 0 - success, < 0 - failure
208 */
209static int hdd_lro_desc_find(hdd_adapter_t *adapter,
210 struct sk_buff *skb, struct iphdr *iph, struct tcphdr *tcph,
211 struct net_lro_desc **lro_desc)
212{
213 uint32_t i;
214 struct hdd_lro_desc_table *lro_hash_table;
215 struct list_head *ptr;
216 struct hdd_lro_desc_entry *entry;
217 struct hdd_lro_desc_pool free_pool;
218 struct hdd_lro_desc_info *desc_info = &adapter->lro_info.lro_desc_info;
219
220 *lro_desc = NULL;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530221 i = QDF_NBUF_CB_RX_FLOW_ID_TOEPLITZ(skb) & LRO_DESC_TABLE_SZ_MASK;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800222
223 lro_hash_table = &desc_info->lro_hash_table[i];
224
225 if (!lro_hash_table) {
226 hdd_err("Invalid hash entry");
Anurag Chouhanb2dc16f2016-02-25 11:47:37 +0530227 QDF_ASSERT(0);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800228 return -EINVAL;
229 }
230
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530231 qdf_spin_lock_bh(&desc_info->lro_hash_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800232 /* Check if this flow exists in the descriptor list */
233 list_for_each(ptr, &lro_hash_table->lro_desc_list) {
234 struct net_lro_desc *tmp_lro_desc = NULL;
235 entry = list_entry(ptr, struct hdd_lro_desc_entry, lro_node);
236 tmp_lro_desc = entry->lro_desc;
237 if (tmp_lro_desc->active) {
238 if (hdd_lro_tcp_flow_match(tmp_lro_desc, iph, tcph)) {
239 *lro_desc = entry->lro_desc;
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530240 qdf_spin_unlock_bh(&desc_info->lro_hash_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800241 return 0;
242 }
243 }
244 }
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530245 qdf_spin_unlock_bh(&desc_info->lro_hash_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800246
247 /* no existing flow found, a new LRO desc needs to be allocated */
248 free_pool = adapter->lro_info.lro_desc_info.lro_desc_pool;
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530249 qdf_spin_lock_bh(&free_pool.lro_pool_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800250 entry = list_first_entry_or_null(
251 &free_pool.lro_free_list_head,
252 struct hdd_lro_desc_entry, lro_node);
253 if (NULL == entry) {
254 hdd_err("Could not allocate LRO desc!");
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530255 qdf_spin_unlock_bh(&free_pool.lro_pool_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800256 return -ENOMEM;
257 }
258
Govind Singh8c54d6c2016-09-08 19:28:13 +0530259 if (list_empty(&entry->lro_node)) {
260 hdd_err("Reached max supported lro_desc range\n");
261 return -EINVAL;
262 }
263
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800264 list_del_init(&entry->lro_node);
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530265 qdf_spin_unlock_bh(&free_pool.lro_pool_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800266
267 if (NULL == entry->lro_desc) {
268 hdd_err("entry->lro_desc is NULL!\n");
269 return -EINVAL;
270 }
271
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530272 qdf_mem_zero((void *)entry->lro_desc, sizeof(struct net_lro_desc));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800273
274 /*
275 * lro_desc->active should be 0 and lro_desc->tcp_rcv_tsval
276 * should be 0 for newly allocated lro descriptors
277 */
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530278 qdf_spin_lock_bh(&desc_info->lro_hash_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800279 list_add_tail(&entry->lro_node,
280 &lro_hash_table->lro_desc_list);
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530281 qdf_spin_unlock_bh(&desc_info->lro_hash_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800282 *lro_desc = entry->lro_desc;
283
284 return 0;
285}
286
287/**
288 * hdd_lro_get_desc() - LRO descriptor look-up function
289 * @iph: IP header
290 * @tcph: TCP header
291 * @lro_arr: Array of LRO decriptors
292 * @lro_mgr: LRO manager
293 *
294 * Looks-up the LRO descriptor for a given flow
295 *
296 * Return: LRO descriptor
297 */
298static struct net_lro_desc *hdd_lro_get_desc(struct net_lro_mgr *lro_mgr,
299 struct net_lro_desc *lro_arr,
300 struct iphdr *iph,
301 struct tcphdr *tcph)
302{
303 int i;
304
305 for (i = 0; i < lro_mgr->max_desc; i++) {
306 if (lro_arr[i].active)
307 if (hdd_lro_tcp_flow_match(&lro_arr[i], iph, tcph))
308 return &lro_arr[i];
309 }
310
311 return NULL;
312}
313
314/**
315 * hdd_lro_eligible() - LRO eligibilty check
316 * @iph: IP header
317 * @tcph: TCP header
318 * @adapter: HDD adaptor
319 * @desc: LRO descriptor
320 * @skb: network buffer
321 *
322 * Determines if the frame is LRO eligible
323 *
324 * Return: true - LRO eligible frame, false - frame is not LRO
325 * eligible
326 */
327static bool hdd_lro_eligible(hdd_adapter_t *adapter, struct sk_buff *skb,
328 struct iphdr *iph, struct tcphdr *tcph, struct net_lro_desc **desc)
329{
330 struct net_lro_desc *lro_desc = NULL;
331 int hw_lro_eligible =
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530332 QDF_NBUF_CB_RX_LRO_ELIGIBLE(skb) &&
333 (!QDF_NBUF_CB_RX_TCP_PURE_ACK(skb));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800334
335 if (!hw_lro_eligible)
336 return false;
337
338 if (0 != hdd_lro_desc_find(adapter, skb, iph, tcph, desc)) {
339 hdd_err("finding the LRO desc failed");
340 return false;
341 }
342
343 lro_desc = *desc;
344 if (!lro_desc)
345 return false;
346
347 /* if this is not the first skb, check the timestamp option */
348 if (lro_desc->tcp_rcv_tsval) {
349 if (tcph->doff == 8) {
350 __be32 *topt = (__be32 *)(tcph + 1);
351
352 if (*topt != htonl((TCPOPT_NOP << 24)
353 |(TCPOPT_NOP << 16)
354 | (TCPOPT_TIMESTAMP << 8)
355 | TCPOLEN_TIMESTAMP))
356 return true;
357
358 /* timestamp should be in right order */
359 topt++;
360 if (after(ntohl(lro_desc->tcp_rcv_tsval),
361 ntohl(*topt)))
362 return false;
363
364 /* timestamp reply should not be zero */
365 topt++;
366 if (*topt == 0)
367 return false;
368 }
369 }
370
371 return true;
372}
373
374/**
375 * hdd_lro_desc_free() - Free the LRO descriptor
376 * @adapter: HDD adaptor
377 * @desc: LRO descriptor
378 *
379 * Return the LRO descriptor to the free pool
380 *
381 * Return: none
382 */
383static void hdd_lro_desc_free(struct net_lro_desc *desc,
384 hdd_adapter_t *adapter)
385{
386 struct hdd_lro_desc_entry *entry;
387 struct net_lro_desc *arr_base = adapter->lro_info.lro_mgr->lro_arr;
388 struct hdd_lro_desc_info *desc_info = &adapter->lro_info.lro_desc_info;
389 int i = desc - arr_base;
390
391 if (i >= LRO_DESC_POOL_SZ) {
392 hdd_err("invalid index %d", i);
393 return;
394 }
395
396 entry = &desc_info->lro_desc_pool.lro_desc_array[i];
397
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530398 qdf_spin_lock_bh(&desc_info->lro_hash_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800399 list_del_init(&entry->lro_node);
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530400 qdf_spin_unlock_bh(&desc_info->lro_hash_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800401
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530402 qdf_spin_lock_bh(&desc_info->lro_desc_pool.lro_pool_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800403 list_add_tail(&entry->lro_node, &desc_info->
404 lro_desc_pool.lro_free_list_head);
Anurag Chouhana37b5b72016-02-21 14:53:42 +0530405 qdf_spin_unlock_bh(&desc_info->lro_desc_pool.lro_pool_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800406}
407
408/**
409 * hdd_lro_flush_pkt() - function to flush the LRO flow
410 * @iph: IP header
411 * @tcph: TCP header
412 * @adapter: HDD adaptor
413 * @lro_mgr: LRO manager
414 *
415 * Flush all the packets aggregated in the LRO manager for the
416 * flow indicated by the TCP and IP header
417 *
418 * Return: none
419 */
420void hdd_lro_flush_pkt(struct net_lro_mgr *lro_mgr,
421 struct iphdr *iph, struct tcphdr *tcph, hdd_adapter_t *adapter)
422{
423 struct net_lro_desc *lro_desc;
424
425 lro_desc = hdd_lro_get_desc(lro_mgr, lro_mgr->lro_arr, iph, tcph);
426
Manjunathappa Prakashb4ae4ab2016-08-19 12:36:05 -0700427 if (lro_desc) {
428 hdd_lro_desc_free(lro_desc, adapter);
429 lro_flush_desc(lro_mgr, lro_desc);
430 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800431}
432
433/**
434 * hdd_lro_flush() - LRO flush callback
435 * @data: opaque pointer containing HDD specific information
436 *
437 * Callback registered to flush all the packets aggregated in
438 * the LRO manager for all the flows
439 *
440 * Return: none
441 */
442void hdd_lro_flush(void *data)
443{
444 hdd_adapter_t *adapter = (hdd_adapter_t *)data;
Manjunathappa Prakashb4ae4ab2016-08-19 12:36:05 -0700445 struct hdd_lro_s *hdd_lro = &adapter->lro_info;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800446 int i;
447
Manjunathappa Prakashb4ae4ab2016-08-19 12:36:05 -0700448 qdf_spin_lock_bh(&hdd_lro->lro_mgr_arr_access_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800449 for (i = 0; i < adapter->lro_info.lro_mgr->max_desc; i++) {
450 if (adapter->lro_info.lro_mgr->lro_arr[i].active) {
451 hdd_lro_desc_free(
452 &adapter->lro_info.lro_mgr->lro_arr[i],
453 (void *)adapter);
454 lro_flush_desc(adapter->lro_info.lro_mgr,
455 &adapter->lro_info.lro_mgr->lro_arr[i]);
456 }
457 }
Manjunathappa Prakashb4ae4ab2016-08-19 12:36:05 -0700458 qdf_spin_unlock_bh(&hdd_lro->lro_mgr_arr_access_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800459}
460
461/**
462 * hdd_lro_init() - initialization for LRO
463 * @hdd_ctx: HDD context
464 *
465 * This function sends the LRO configuration to the firmware
466 * via WMA
Orhan K AKYILDIZb71d0612016-08-06 15:16:59 -0700467 * Make sure that this function gets called after NAPI
468 * instances have been created.
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800469 *
470 * Return: 0 - success, < 0 - failure
471 */
472int hdd_lro_init(hdd_context_t *hdd_ctx)
473{
474 struct wma_lro_config_cmd_t lro_config;
475
Orhan K AKYILDIZb71d0612016-08-06 15:16:59 -0700476 if ((!hdd_ctx->config->lro_enable) &&
477 (hdd_napi_enabled(HDD_NAPI_ANY) == 0))
478 {
479 hdd_err("LRO and NAPI are both disabled.");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800480 return 0;
481 }
482
483 lro_config.lro_enable = 1;
484 lro_config.tcp_flag = TCPHDR_ACK;
485 lro_config.tcp_flag_mask = TCPHDR_FIN | TCPHDR_SYN | TCPHDR_RST |
486 TCPHDR_ACK | TCPHDR_URG | TCPHDR_ECE | TCPHDR_CWR;
487
488 get_random_bytes(lro_config.toeplitz_hash_ipv4,
489 (sizeof(lro_config.toeplitz_hash_ipv4[0]) *
490 LRO_IPV4_SEED_ARR_SZ));
491
492 get_random_bytes(lro_config.toeplitz_hash_ipv6,
493 (sizeof(lro_config.toeplitz_hash_ipv6[0]) *
494 LRO_IPV6_SEED_ARR_SZ));
495
496 hdd_debug("sending the LRO configuration to the fw");
497 if (0 != wma_lro_init(&lro_config)) {
498 hdd_err("Failed to send LRO configuration!");
499 hdd_ctx->config->lro_enable = 0;
500 return -EAGAIN;
501 }
502
503 return 0;
504}
505
506/**
507 * hdd_lro_enable() - enable LRO
508 * @hdd_ctx: HDD context
509 * @adapter: HDD adapter
510 *
511 * This function enables LRO in the network device attached to
512 * the HDD adapter. It also allocates the HDD LRO instance for
513 * that network device
514 *
515 * Return: 0 - success, < 0 - failure
516 */
517int hdd_lro_enable(hdd_context_t *hdd_ctx,
518 hdd_adapter_t *adapter)
519{
520 struct hdd_lro_s *hdd_lro;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800521 size_t lro_mgr_sz, desc_arr_sz, desc_pool_sz, hash_table_sz;
522 uint8_t *lro_mem_ptr;
523
524 if (!hdd_ctx->config->lro_enable ||
Govind Singhd9b6ca82016-09-07 20:45:32 +0530525 QDF_STA_MODE != adapter->device_mode) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800526 hdd_info("LRO Disabled");
527 return 0;
528 }
529
530 hdd_info("LRO Enabled");
531
532 hdd_lro = &adapter->lro_info;
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530533 qdf_mem_zero((void *)hdd_lro, sizeof(struct hdd_lro_s));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800534 /*
535 * Allocate all the LRO data structures at once and then carve
536 * them up as needed
537 */
538 lro_mgr_sz = sizeof(struct net_lro_mgr);
539 desc_arr_sz = (LRO_DESC_POOL_SZ * sizeof(struct net_lro_desc));
540 desc_pool_sz = (LRO_DESC_POOL_SZ * sizeof(struct hdd_lro_desc_entry));
541 hash_table_sz = (sizeof(struct hdd_lro_desc_table) * LRO_DESC_TABLE_SZ);
542
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530543 lro_mem_ptr = qdf_mem_malloc(lro_mgr_sz + desc_arr_sz + desc_pool_sz +
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800544 hash_table_sz);
545
546 if (NULL == lro_mem_ptr) {
547 hdd_err("Unable to allocate memory for LRO");
548 hdd_ctx->config->lro_enable = 0;
549 return -ENOMEM;
550 }
551
552 /* LRO manager */
553 hdd_lro->lro_mgr = (struct net_lro_mgr *)lro_mem_ptr;
554 lro_mem_ptr += lro_mgr_sz;
555
556 /* LRO decriptor array */
557 hdd_lro->lro_mgr->lro_arr = (struct net_lro_desc *)lro_mem_ptr;
558 lro_mem_ptr += desc_arr_sz;
559
560 /* LRO descriptor pool */
561 hdd_lro->lro_desc_info.lro_desc_pool.lro_desc_array =
562 (struct hdd_lro_desc_entry *)lro_mem_ptr;
563 lro_mem_ptr += desc_pool_sz;
564
565 /* hash table to store the LRO descriptors */
566 hdd_lro->lro_desc_info.lro_hash_table =
567 (struct hdd_lro_desc_table *)lro_mem_ptr;
568
569 /* Initialize the LRO descriptors */
570 hdd_lro_desc_info_init(hdd_lro);
571
572 hdd_lro->lro_mgr->dev = adapter->dev;
Nirav Shahbd36b062016-07-18 11:12:59 +0530573 if (hdd_ctx->enableRxThread)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800574 hdd_lro->lro_mgr->features = LRO_F_NI;
575
576 if (hdd_napi_enabled(HDD_NAPI_ANY))
577 hdd_lro->lro_mgr->features |= LRO_F_NAPI;
578
579 hdd_lro->lro_mgr->ip_summed_aggr = CHECKSUM_UNNECESSARY;
580 hdd_lro->lro_mgr->max_aggr = LRO_MAX_AGGR_SIZE;
581 hdd_lro->lro_mgr->get_skb_header = hdd_lro_get_skb_header;
582 hdd_lro->lro_mgr->ip_summed = CHECKSUM_UNNECESSARY;
583 hdd_lro->lro_mgr->max_desc = LRO_DESC_POOL_SZ;
584
585 adapter->dev->features |= NETIF_F_LRO;
586
587 /* Register the flush callback */
588 ol_register_lro_flush_cb(hdd_lro_flush, adapter);
589
590 return 0;
591}
592
593/**
594 * hdd_lro_disable() - disable LRO
595 * @hdd_ctx: HDD context
596 * @adapter: HDD adapter
597 *
598 * This function frees the HDD LRO instance for the network
599 * device attached to the HDD adapter
600 *
601 * Return: none
602 */
603void hdd_lro_disable(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter)
604{
605 if (!hdd_ctx->config->lro_enable ||
Govind Singhd9b6ca82016-09-07 20:45:32 +0530606 QDF_STA_MODE != adapter->device_mode)
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800607 return;
608
Dhanashri Atre8d978172015-10-30 15:12:03 -0700609 /* Deregister the flush callback */
610 ol_deregister_lro_flush_cb();
611
612 if (adapter->lro_info.lro_mgr) {
613 hdd_lro_desc_info_deinit(&adapter->lro_info);
Anurag Chouhan600c3a02016-03-01 10:33:54 +0530614 qdf_mem_free(adapter->lro_info.lro_mgr);
Dhanashri Atre8d978172015-10-30 15:12:03 -0700615 adapter->lro_info.lro_mgr = NULL;
616 adapter->lro_info.lro_desc_info.
617 lro_desc_pool.lro_desc_array = NULL;
618 adapter->lro_info.lro_desc_info.
619 lro_hash_table = NULL;
620 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800621 return;
622}
623
624/**
625 * hdd_lro_rx() - LRO receive function
626 * @hdd_ctx: HDD context
627 * @adapter: HDD adapter
628 * @skb: network buffer
629 *
630 * Delivers LRO eligible frames to the LRO manager
631 *
632 * Return: HDD_LRO_RX - frame delivered to LRO manager
633 * HDD_LRO_NO_RX - frame not delivered
634 */
635enum hdd_lro_rx_status hdd_lro_rx(hdd_context_t *hdd_ctx,
636 hdd_adapter_t *adapter, struct sk_buff *skb)
637{
638 enum hdd_lro_rx_status status = HDD_LRO_NO_RX;
639
640 if ((adapter->dev->features & NETIF_F_LRO) &&
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530641 QDF_NBUF_CB_RX_TCP_PROTO(skb)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800642 struct iphdr *iph;
643 struct tcphdr *tcph;
644 struct net_lro_desc *lro_desc = NULL;
Manjunathappa Prakashb4ae4ab2016-08-19 12:36:05 -0700645 struct hdd_lro_s *hdd_lro = &adapter->lro_info;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800646 iph = (struct iphdr *)skb->data;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530647 tcph = (struct tcphdr *)(skb->data + QDF_NBUF_CB_RX_TCP_OFFSET(skb));
Manjunathappa Prakashb4ae4ab2016-08-19 12:36:05 -0700648 qdf_spin_lock_bh(
649 &hdd_lro->lro_mgr_arr_access_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800650 if (hdd_lro_eligible(adapter, skb, iph, tcph, &lro_desc)) {
651 struct net_lro_info hdd_lro_info;
652
653 hdd_lro_info.valid_fields = LRO_VALID_FIELDS;
654
655 hdd_lro_info.lro_desc = lro_desc;
656 hdd_lro_info.lro_eligible = 1;
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530657 hdd_lro_info.tcp_ack_num = QDF_NBUF_CB_RX_TCP_ACK_NUM(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800658 hdd_lro_info.tcp_data_csum =
Anurag Chouhanf04e84f2016-03-03 10:12:12 +0530659 csum_unfold(htons(QDF_NBUF_CB_RX_TCP_CHKSUM(skb)));
660 hdd_lro_info.tcp_seq_num = QDF_NBUF_CB_RX_TCP_SEQ_NUM(skb);
661 hdd_lro_info.tcp_win = QDF_NBUF_CB_RX_TCP_WIN(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800662
663 lro_receive_skb_ext(adapter->lro_info.lro_mgr, skb,
664 (void *)adapter, &hdd_lro_info);
665
Manjunathappa Prakashb4ae4ab2016-08-19 12:36:05 -0700666 if (!hdd_lro_info.lro_desc->active) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800667 hdd_lro_desc_free(lro_desc, adapter);
Manjunathappa Prakashb4ae4ab2016-08-19 12:36:05 -0700668 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800669
670 status = HDD_LRO_RX;
671 } else {
672 hdd_lro_flush_pkt(adapter->lro_info.lro_mgr,
673 iph, tcph, adapter);
674 }
Manjunathappa Prakashb4ae4ab2016-08-19 12:36:05 -0700675 qdf_spin_unlock_bh(
676 &hdd_lro->lro_mgr_arr_access_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800677 }
678 return status;
679}