blob: 1ea7dd4bf8d2cdd11da450557a47e1f42e4c4320 [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>
43
44#include <linux/inet_lro.h>
45#include <linux/list.h>
46#include <linux/random.h>
47#include <net/tcp.h>
48
49#define LRO_MAX_AGGR_SIZE 100
50
51#define LRO_VALID_FIELDS \
52 (LRO_DESC | LRO_ELIGIBILITY_CHECKED | LRO_TCP_ACK_NUM | \
53 LRO_TCP_DATA_CSUM | LRO_TCP_SEQ_NUM | LRO_TCP_WIN)
54
55/**
56 * hdd_lro_get_skb_header() - LRO callback function
57 * @skb: network buffer
58 * @ip_hdr: contains a pointer to the IP header
59 * @tcpudp_hdr: contains a pointer to the TCP header
60 * @hdr_flags: indicates if this is a TCP, IPV4 frame
61 * @priv: private driver specific opaque pointer
62 *
63 * Get the IP and TCP headers from the skb
64 *
65 * Return: 0 - success, < 0 - failure
66 */
67static int hdd_lro_get_skb_header(struct sk_buff *skb, void **ip_hdr,
68 void **tcpudp_hdr, u64 *hdr_flags, void *priv)
69{
Houston Hoffman43d47fa2016-02-24 16:34:30 -080070 if (NBUF_CB_RX_IPV6_PROTO(skb)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080071 hdr_flags = 0;
72 return -EINVAL;
73 }
74
75 *hdr_flags |= (LRO_IPV4 | LRO_TCP);
76 (*ip_hdr) = skb->data;
Houston Hoffman43d47fa2016-02-24 16:34:30 -080077 (*tcpudp_hdr) = skb->data + NBUF_CB_RX_TCP_OFFSET(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -080078 return 0;
79}
80
81/**
82 * hdd_lro_desc_pool_init() - Initialize the free pool of LRO
83 * descriptors
84 * @lro_desc_pool: free pool of the LRO descriptors
85 * @lro_mgr: LRO manager
86 *
87 * Initialize a list that holds the free LRO descriptors
88 *
89 * Return: none
90 */
91static void hdd_lro_desc_pool_init(struct hdd_lro_desc_pool *lro_desc_pool,
92 struct net_lro_mgr *lro_mgr)
93{
94 int i;
95
96 INIT_LIST_HEAD(&lro_desc_pool->lro_free_list_head);
97
98 for (i = 0; i < LRO_DESC_POOL_SZ; i++) {
99 lro_desc_pool->lro_desc_array[i].lro_desc =
100 &lro_mgr->lro_arr[i];
101 list_add_tail(&lro_desc_pool->lro_desc_array[i].lro_node,
102 &lro_desc_pool->lro_free_list_head);
103 }
104 cdf_spinlock_init(&lro_desc_pool->lro_pool_lock);
105}
106
107/**
108 * hdd_lro_desc_info_init() - Initialize the LRO descriptors
109 * @hdd_info: HDD LRO data structure
110 *
111 * Initialize the free pool of LRO descriptors and the entries
112 * of the hash table
113 *
114 * Return: none
115 */
116static void hdd_lro_desc_info_init(struct hdd_lro_s *hdd_info)
117{
118 int i;
119
120 /* Initialize pool of free LRO desc.*/
121 hdd_lro_desc_pool_init(&hdd_info->lro_desc_info.lro_desc_pool,
122 hdd_info->lro_mgr);
123
124 /* Initialize the hash table of LRO desc.*/
125 for (i = 0; i < LRO_DESC_TABLE_SZ; i++) {
126 /* initialize the flows in the hash table */
127 INIT_LIST_HEAD(&hdd_info->lro_desc_info.
128 lro_hash_table[i].lro_desc_list);
129 }
130
131 cdf_spinlock_init(&hdd_info->lro_desc_info.lro_hash_lock);
132}
133
134/**
135 * hdd_lro_desc_pool_deinit() - Free the LRO descriptor list
136 * @hdd_info: HDD LRO data structure
137 *
138 * Free the pool of LRO descriptors
139 *
140 * Return: none
141 */
142static void hdd_lro_desc_pool_deinit(struct hdd_lro_desc_pool *lro_desc_pool)
143{
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800144 INIT_LIST_HEAD(&lro_desc_pool->lro_free_list_head);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800145 cdf_spinlock_destroy(&lro_desc_pool->lro_pool_lock);
146}
147
148/**
149 * hdd_lro_desc_info_deinit() - Deinitialize the LRO descriptors
150 *
151 * @hdd_info: HDD LRO data structure
152 *
153 * Deinitialize the free pool of LRO descriptors and the entries
154 * of the hash table
155 *
156 * Return: none
157 */
158static void hdd_lro_desc_info_deinit(struct hdd_lro_s *hdd_info)
159{
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800160 struct hdd_lro_desc_info *desc_info = &hdd_info->lro_desc_info;
161
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800162 hdd_lro_desc_pool_deinit(&desc_info->lro_desc_pool);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800163 cdf_spinlock_destroy(&desc_info->lro_hash_lock);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800164}
165
166/**
167 * hdd_lro_tcp_flow_match() - function to check for a flow match
168 * @iph: IP header
169 * @tcph: TCP header
170 * @lro_desc: LRO decriptor
171 *
172 * Checks if the descriptor belongs to the same flow as the one
173 * indicated by the TCP and IP header.
174 *
175 * Return: true - flow match, false - flow does not match
176 */
177static inline bool hdd_lro_tcp_flow_match(struct net_lro_desc *lro_desc,
178 struct iphdr *iph,
179 struct tcphdr *tcph)
180{
181 if ((lro_desc->tcph->source != tcph->source) ||
182 (lro_desc->tcph->dest != tcph->dest) ||
183 (lro_desc->iph->saddr != iph->saddr) ||
184 (lro_desc->iph->daddr != iph->daddr))
185 return false;
186
187 return true;
188
189}
190
191/**
192 * hdd_lro_desc_find() - LRO descriptor look-up function
193 *
194 * @adapter: HDD adaptor
195 * @skb: network buffer
196 * @iph: IP header
197 * @tcph: TCP header
198 * @lro_desc: contains a pointer to the LRO decriptor
199 *
200 * Look-up the LRO descriptor in the hash table based on the
201 * flow ID toeplitz. If the flow is not found, allocates a new
202 * LRO descriptor and places it in the hash table
203 *
204 * Return: 0 - success, < 0 - failure
205 */
206static int hdd_lro_desc_find(hdd_adapter_t *adapter,
207 struct sk_buff *skb, struct iphdr *iph, struct tcphdr *tcph,
208 struct net_lro_desc **lro_desc)
209{
210 uint32_t i;
211 struct hdd_lro_desc_table *lro_hash_table;
212 struct list_head *ptr;
213 struct hdd_lro_desc_entry *entry;
214 struct hdd_lro_desc_pool free_pool;
215 struct hdd_lro_desc_info *desc_info = &adapter->lro_info.lro_desc_info;
216
217 *lro_desc = NULL;
Houston Hoffman43d47fa2016-02-24 16:34:30 -0800218 i = NBUF_CB_RX_FLOW_ID_TOEPLITZ(skb) & LRO_DESC_TABLE_SZ_MASK;
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800219
220 lro_hash_table = &desc_info->lro_hash_table[i];
221
222 if (!lro_hash_table) {
223 hdd_err("Invalid hash entry");
224 CDF_ASSERT(0);
225 return -EINVAL;
226 }
227
228 cdf_spin_lock_bh(&desc_info->lro_hash_lock);
229 /* Check if this flow exists in the descriptor list */
230 list_for_each(ptr, &lro_hash_table->lro_desc_list) {
231 struct net_lro_desc *tmp_lro_desc = NULL;
232 entry = list_entry(ptr, struct hdd_lro_desc_entry, lro_node);
233 tmp_lro_desc = entry->lro_desc;
234 if (tmp_lro_desc->active) {
235 if (hdd_lro_tcp_flow_match(tmp_lro_desc, iph, tcph)) {
236 *lro_desc = entry->lro_desc;
237 cdf_spin_unlock_bh(&desc_info->lro_hash_lock);
238 return 0;
239 }
240 }
241 }
242 cdf_spin_unlock_bh(&desc_info->lro_hash_lock);
243
244 /* no existing flow found, a new LRO desc needs to be allocated */
245 free_pool = adapter->lro_info.lro_desc_info.lro_desc_pool;
246 cdf_spin_lock_bh(&free_pool.lro_pool_lock);
247 entry = list_first_entry_or_null(
248 &free_pool.lro_free_list_head,
249 struct hdd_lro_desc_entry, lro_node);
250 if (NULL == entry) {
251 hdd_err("Could not allocate LRO desc!");
252 cdf_spin_unlock_bh(&free_pool.lro_pool_lock);
253 return -ENOMEM;
254 }
255
256 list_del_init(&entry->lro_node);
257 cdf_spin_unlock_bh(&free_pool.lro_pool_lock);
258
259 if (NULL == entry->lro_desc) {
260 hdd_err("entry->lro_desc is NULL!\n");
261 return -EINVAL;
262 }
263
264 cdf_mem_zero((void *)entry->lro_desc, sizeof(struct net_lro_desc));
265
266 /*
267 * lro_desc->active should be 0 and lro_desc->tcp_rcv_tsval
268 * should be 0 for newly allocated lro descriptors
269 */
270 cdf_spin_lock_bh(&desc_info->lro_hash_lock);
271 list_add_tail(&entry->lro_node,
272 &lro_hash_table->lro_desc_list);
273 cdf_spin_unlock_bh(&desc_info->lro_hash_lock);
274 *lro_desc = entry->lro_desc;
275
276 return 0;
277}
278
279/**
280 * hdd_lro_get_desc() - LRO descriptor look-up function
281 * @iph: IP header
282 * @tcph: TCP header
283 * @lro_arr: Array of LRO decriptors
284 * @lro_mgr: LRO manager
285 *
286 * Looks-up the LRO descriptor for a given flow
287 *
288 * Return: LRO descriptor
289 */
290static struct net_lro_desc *hdd_lro_get_desc(struct net_lro_mgr *lro_mgr,
291 struct net_lro_desc *lro_arr,
292 struct iphdr *iph,
293 struct tcphdr *tcph)
294{
295 int i;
296
297 for (i = 0; i < lro_mgr->max_desc; i++) {
298 if (lro_arr[i].active)
299 if (hdd_lro_tcp_flow_match(&lro_arr[i], iph, tcph))
300 return &lro_arr[i];
301 }
302
303 return NULL;
304}
305
306/**
307 * hdd_lro_eligible() - LRO eligibilty check
308 * @iph: IP header
309 * @tcph: TCP header
310 * @adapter: HDD adaptor
311 * @desc: LRO descriptor
312 * @skb: network buffer
313 *
314 * Determines if the frame is LRO eligible
315 *
316 * Return: true - LRO eligible frame, false - frame is not LRO
317 * eligible
318 */
319static bool hdd_lro_eligible(hdd_adapter_t *adapter, struct sk_buff *skb,
320 struct iphdr *iph, struct tcphdr *tcph, struct net_lro_desc **desc)
321{
322 struct net_lro_desc *lro_desc = NULL;
323 int hw_lro_eligible =
Houston Hoffman43d47fa2016-02-24 16:34:30 -0800324 NBUF_CB_RX_LRO_ELIGIBLE(skb) && (!NBUF_CB_RX_TCP_PURE_ACK(skb));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800325
326 if (!hw_lro_eligible)
327 return false;
328
329 if (0 != hdd_lro_desc_find(adapter, skb, iph, tcph, desc)) {
330 hdd_err("finding the LRO desc failed");
331 return false;
332 }
333
334 lro_desc = *desc;
335 if (!lro_desc)
336 return false;
337
338 /* if this is not the first skb, check the timestamp option */
339 if (lro_desc->tcp_rcv_tsval) {
340 if (tcph->doff == 8) {
341 __be32 *topt = (__be32 *)(tcph + 1);
342
343 if (*topt != htonl((TCPOPT_NOP << 24)
344 |(TCPOPT_NOP << 16)
345 | (TCPOPT_TIMESTAMP << 8)
346 | TCPOLEN_TIMESTAMP))
347 return true;
348
349 /* timestamp should be in right order */
350 topt++;
351 if (after(ntohl(lro_desc->tcp_rcv_tsval),
352 ntohl(*topt)))
353 return false;
354
355 /* timestamp reply should not be zero */
356 topt++;
357 if (*topt == 0)
358 return false;
359 }
360 }
361
362 return true;
363}
364
365/**
366 * hdd_lro_desc_free() - Free the LRO descriptor
367 * @adapter: HDD adaptor
368 * @desc: LRO descriptor
369 *
370 * Return the LRO descriptor to the free pool
371 *
372 * Return: none
373 */
374static void hdd_lro_desc_free(struct net_lro_desc *desc,
375 hdd_adapter_t *adapter)
376{
377 struct hdd_lro_desc_entry *entry;
378 struct net_lro_desc *arr_base = adapter->lro_info.lro_mgr->lro_arr;
379 struct hdd_lro_desc_info *desc_info = &adapter->lro_info.lro_desc_info;
380 int i = desc - arr_base;
381
382 if (i >= LRO_DESC_POOL_SZ) {
383 hdd_err("invalid index %d", i);
384 return;
385 }
386
387 entry = &desc_info->lro_desc_pool.lro_desc_array[i];
388
389 cdf_spin_lock_bh(&desc_info->lro_hash_lock);
390 list_del_init(&entry->lro_node);
391 cdf_spin_unlock_bh(&desc_info->lro_hash_lock);
392
393 cdf_spin_lock_bh(&desc_info->lro_desc_pool.lro_pool_lock);
394 list_add_tail(&entry->lro_node, &desc_info->
395 lro_desc_pool.lro_free_list_head);
396 cdf_spin_unlock_bh(&desc_info->lro_desc_pool.lro_pool_lock);
397}
398
399/**
400 * hdd_lro_flush_pkt() - function to flush the LRO flow
401 * @iph: IP header
402 * @tcph: TCP header
403 * @adapter: HDD adaptor
404 * @lro_mgr: LRO manager
405 *
406 * Flush all the packets aggregated in the LRO manager for the
407 * flow indicated by the TCP and IP header
408 *
409 * Return: none
410 */
411void hdd_lro_flush_pkt(struct net_lro_mgr *lro_mgr,
412 struct iphdr *iph, struct tcphdr *tcph, hdd_adapter_t *adapter)
413{
414 struct net_lro_desc *lro_desc;
415
416 lro_desc = hdd_lro_get_desc(lro_mgr, lro_mgr->lro_arr, iph, tcph);
417
418 if (!lro_desc)
419 return;
420
421 hdd_lro_desc_free(lro_desc, adapter);
422 lro_flush_desc(lro_mgr, lro_desc);
423}
424
425/**
426 * hdd_lro_flush() - LRO flush callback
427 * @data: opaque pointer containing HDD specific information
428 *
429 * Callback registered to flush all the packets aggregated in
430 * the LRO manager for all the flows
431 *
432 * Return: none
433 */
434void hdd_lro_flush(void *data)
435{
436 hdd_adapter_t *adapter = (hdd_adapter_t *)data;
437 int i;
438
439 for (i = 0; i < adapter->lro_info.lro_mgr->max_desc; i++) {
440 if (adapter->lro_info.lro_mgr->lro_arr[i].active) {
441 hdd_lro_desc_free(
442 &adapter->lro_info.lro_mgr->lro_arr[i],
443 (void *)adapter);
444 lro_flush_desc(adapter->lro_info.lro_mgr,
445 &adapter->lro_info.lro_mgr->lro_arr[i]);
446 }
447 }
448}
449
450/**
451 * hdd_lro_init() - initialization for LRO
452 * @hdd_ctx: HDD context
453 *
454 * This function sends the LRO configuration to the firmware
455 * via WMA
456 *
457 * Return: 0 - success, < 0 - failure
458 */
459int hdd_lro_init(hdd_context_t *hdd_ctx)
460{
461 struct wma_lro_config_cmd_t lro_config;
462
463 if (!hdd_ctx->config->lro_enable) {
Jeff Johnson103cc5b2016-01-08 15:42:27 -0800464 hdd_err("LRO Disabled");
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800465 return 0;
466 }
467
468 lro_config.lro_enable = 1;
469 lro_config.tcp_flag = TCPHDR_ACK;
470 lro_config.tcp_flag_mask = TCPHDR_FIN | TCPHDR_SYN | TCPHDR_RST |
471 TCPHDR_ACK | TCPHDR_URG | TCPHDR_ECE | TCPHDR_CWR;
472
473 get_random_bytes(lro_config.toeplitz_hash_ipv4,
474 (sizeof(lro_config.toeplitz_hash_ipv4[0]) *
475 LRO_IPV4_SEED_ARR_SZ));
476
477 get_random_bytes(lro_config.toeplitz_hash_ipv6,
478 (sizeof(lro_config.toeplitz_hash_ipv6[0]) *
479 LRO_IPV6_SEED_ARR_SZ));
480
481 hdd_debug("sending the LRO configuration to the fw");
482 if (0 != wma_lro_init(&lro_config)) {
483 hdd_err("Failed to send LRO configuration!");
484 hdd_ctx->config->lro_enable = 0;
485 return -EAGAIN;
486 }
487
488 return 0;
489}
490
491/**
492 * hdd_lro_enable() - enable LRO
493 * @hdd_ctx: HDD context
494 * @adapter: HDD adapter
495 *
496 * This function enables LRO in the network device attached to
497 * the HDD adapter. It also allocates the HDD LRO instance for
498 * that network device
499 *
500 * Return: 0 - success, < 0 - failure
501 */
502int hdd_lro_enable(hdd_context_t *hdd_ctx,
503 hdd_adapter_t *adapter)
504{
505 struct hdd_lro_s *hdd_lro;
Anurag Chouhan6d760662016-02-20 16:05:43 +0530506 struct ol_txrx_pdev_t *pdev = cds_get_context(QDF_MODULE_ID_TXRX);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800507 size_t lro_mgr_sz, desc_arr_sz, desc_pool_sz, hash_table_sz;
508 uint8_t *lro_mem_ptr;
509
510 if (!hdd_ctx->config->lro_enable ||
511 NL80211_IFTYPE_STATION != adapter->wdev.iftype) {
512 hdd_info("LRO Disabled");
513 return 0;
514 }
515
516 hdd_info("LRO Enabled");
517
518 hdd_lro = &adapter->lro_info;
519 cdf_mem_zero((void *)hdd_lro, sizeof(struct hdd_lro_s));
520 /*
521 * Allocate all the LRO data structures at once and then carve
522 * them up as needed
523 */
524 lro_mgr_sz = sizeof(struct net_lro_mgr);
525 desc_arr_sz = (LRO_DESC_POOL_SZ * sizeof(struct net_lro_desc));
526 desc_pool_sz = (LRO_DESC_POOL_SZ * sizeof(struct hdd_lro_desc_entry));
527 hash_table_sz = (sizeof(struct hdd_lro_desc_table) * LRO_DESC_TABLE_SZ);
528
529 lro_mem_ptr = cdf_mem_malloc(lro_mgr_sz + desc_arr_sz + desc_pool_sz +
530 hash_table_sz);
531
532 if (NULL == lro_mem_ptr) {
533 hdd_err("Unable to allocate memory for LRO");
534 hdd_ctx->config->lro_enable = 0;
535 return -ENOMEM;
536 }
537
538 /* LRO manager */
539 hdd_lro->lro_mgr = (struct net_lro_mgr *)lro_mem_ptr;
540 lro_mem_ptr += lro_mgr_sz;
541
542 /* LRO decriptor array */
543 hdd_lro->lro_mgr->lro_arr = (struct net_lro_desc *)lro_mem_ptr;
544 lro_mem_ptr += desc_arr_sz;
545
546 /* LRO descriptor pool */
547 hdd_lro->lro_desc_info.lro_desc_pool.lro_desc_array =
548 (struct hdd_lro_desc_entry *)lro_mem_ptr;
549 lro_mem_ptr += desc_pool_sz;
550
551 /* hash table to store the LRO descriptors */
552 hdd_lro->lro_desc_info.lro_hash_table =
553 (struct hdd_lro_desc_table *)lro_mem_ptr;
554
555 /* Initialize the LRO descriptors */
556 hdd_lro_desc_info_init(hdd_lro);
557
558 hdd_lro->lro_mgr->dev = adapter->dev;
559 if (ol_cfg_is_rx_thread_enabled(pdev->ctrl_pdev))
560 hdd_lro->lro_mgr->features = LRO_F_NI;
561
562 if (hdd_napi_enabled(HDD_NAPI_ANY))
563 hdd_lro->lro_mgr->features |= LRO_F_NAPI;
564
565 hdd_lro->lro_mgr->ip_summed_aggr = CHECKSUM_UNNECESSARY;
566 hdd_lro->lro_mgr->max_aggr = LRO_MAX_AGGR_SIZE;
567 hdd_lro->lro_mgr->get_skb_header = hdd_lro_get_skb_header;
568 hdd_lro->lro_mgr->ip_summed = CHECKSUM_UNNECESSARY;
569 hdd_lro->lro_mgr->max_desc = LRO_DESC_POOL_SZ;
570
571 adapter->dev->features |= NETIF_F_LRO;
572
573 /* Register the flush callback */
574 ol_register_lro_flush_cb(hdd_lro_flush, adapter);
575
576 return 0;
577}
578
579/**
580 * hdd_lro_disable() - disable LRO
581 * @hdd_ctx: HDD context
582 * @adapter: HDD adapter
583 *
584 * This function frees the HDD LRO instance for the network
585 * device attached to the HDD adapter
586 *
587 * Return: none
588 */
589void hdd_lro_disable(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter)
590{
591 if (!hdd_ctx->config->lro_enable ||
592 NL80211_IFTYPE_STATION != adapter->wdev.iftype)
593 return;
594
Dhanashri Atre8d978172015-10-30 15:12:03 -0700595 /* Deregister the flush callback */
596 ol_deregister_lro_flush_cb();
597
598 if (adapter->lro_info.lro_mgr) {
599 hdd_lro_desc_info_deinit(&adapter->lro_info);
600 cdf_mem_free(adapter->lro_info.lro_mgr);
601 adapter->lro_info.lro_mgr = NULL;
602 adapter->lro_info.lro_desc_info.
603 lro_desc_pool.lro_desc_array = NULL;
604 adapter->lro_info.lro_desc_info.
605 lro_hash_table = NULL;
606 }
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800607 return;
608}
609
610/**
611 * hdd_lro_rx() - LRO receive function
612 * @hdd_ctx: HDD context
613 * @adapter: HDD adapter
614 * @skb: network buffer
615 *
616 * Delivers LRO eligible frames to the LRO manager
617 *
618 * Return: HDD_LRO_RX - frame delivered to LRO manager
619 * HDD_LRO_NO_RX - frame not delivered
620 */
621enum hdd_lro_rx_status hdd_lro_rx(hdd_context_t *hdd_ctx,
622 hdd_adapter_t *adapter, struct sk_buff *skb)
623{
624 enum hdd_lro_rx_status status = HDD_LRO_NO_RX;
625
626 if ((adapter->dev->features & NETIF_F_LRO) &&
Houston Hoffman43d47fa2016-02-24 16:34:30 -0800627 NBUF_CB_RX_TCP_PROTO(skb)) {
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800628 struct iphdr *iph;
629 struct tcphdr *tcph;
630 struct net_lro_desc *lro_desc = NULL;
631 iph = (struct iphdr *)skb->data;
Houston Hoffman43d47fa2016-02-24 16:34:30 -0800632 tcph = (struct tcphdr *)(skb->data + NBUF_CB_RX_TCP_OFFSET(skb));
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800633 if (hdd_lro_eligible(adapter, skb, iph, tcph, &lro_desc)) {
634 struct net_lro_info hdd_lro_info;
635
636 hdd_lro_info.valid_fields = LRO_VALID_FIELDS;
637
638 hdd_lro_info.lro_desc = lro_desc;
639 hdd_lro_info.lro_eligible = 1;
Houston Hoffman43d47fa2016-02-24 16:34:30 -0800640 hdd_lro_info.tcp_ack_num = NBUF_CB_RX_TCP_ACK_NUM(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800641 hdd_lro_info.tcp_data_csum =
Houston Hoffman43d47fa2016-02-24 16:34:30 -0800642 csum_unfold(htons(NBUF_CB_RX_TCP_CHKSUM(skb)));
643 hdd_lro_info.tcp_seq_num = NBUF_CB_RX_TCP_SEQ_NUM(skb);
644 hdd_lro_info.tcp_win = NBUF_CB_RX_TCP_WIN(skb);
Prakash Dhavali7090c5f2015-11-02 17:55:19 -0800645
646 lro_receive_skb_ext(adapter->lro_info.lro_mgr, skb,
647 (void *)adapter, &hdd_lro_info);
648
649 if (!hdd_lro_info.lro_desc->active)
650 hdd_lro_desc_free(lro_desc, adapter);
651
652 status = HDD_LRO_RX;
653 } else {
654 hdd_lro_flush_pkt(adapter->lro_info.lro_mgr,
655 iph, tcph, adapter);
656 }
657 }
658 return status;
659}