xref: /wlan-driver/qca-wifi-host-cmn/qdf/linux/src/qdf_lro.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1*5113495bSYour Name /*
2*5113495bSYour Name  * Copyright (c) 2015-2017, 2019 The Linux Foundation. All rights reserved.
3*5113495bSYour Name  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4*5113495bSYour Name  *
5*5113495bSYour Name  * Permission to use, copy, modify, and/or distribute this software for
6*5113495bSYour Name  * any purpose with or without fee is hereby granted, provided that the
7*5113495bSYour Name  * above copyright notice and this permission notice appear in all
8*5113495bSYour Name  * copies.
9*5113495bSYour Name  *
10*5113495bSYour Name  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11*5113495bSYour Name  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12*5113495bSYour Name  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13*5113495bSYour Name  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14*5113495bSYour Name  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15*5113495bSYour Name  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16*5113495bSYour Name  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17*5113495bSYour Name  * PERFORMANCE OF THIS SOFTWARE.
18*5113495bSYour Name  */
19*5113495bSYour Name 
20*5113495bSYour Name /**
21*5113495bSYour Name  * DOC: qdf_lro.c
22*5113495bSYour Name  * QCA driver framework(QDF) Large Receive Offload
23*5113495bSYour Name  */
24*5113495bSYour Name 
25*5113495bSYour Name #include <qdf_lro.h>
26*5113495bSYour Name #include <qdf_trace.h>
27*5113495bSYour Name #include <qdf_types.h>
28*5113495bSYour Name 
29*5113495bSYour Name #include <linux/list.h>
30*5113495bSYour Name #include <net/tcp.h>
31*5113495bSYour Name 
32*5113495bSYour Name /**
33*5113495bSYour Name  * qdf_lro_desc_pool_init() - Initialize the free pool of LRO
34*5113495bSYour Name  *                            descriptors
35*5113495bSYour Name  * @lro_desc_pool: free pool of the LRO descriptors
36*5113495bSYour Name  * @lro_mgr: LRO manager
37*5113495bSYour Name  *
38*5113495bSYour Name  * Initialize a list that holds the free LRO descriptors
39*5113495bSYour Name  *
40*5113495bSYour Name  * Return: none
41*5113495bSYour Name  */
qdf_lro_desc_pool_init(struct qdf_lro_desc_pool * lro_desc_pool,struct net_lro_mgr * lro_mgr)42*5113495bSYour Name static void qdf_lro_desc_pool_init(struct qdf_lro_desc_pool *lro_desc_pool,
43*5113495bSYour Name 				   struct net_lro_mgr *lro_mgr)
44*5113495bSYour Name {
45*5113495bSYour Name 	int i;
46*5113495bSYour Name 
47*5113495bSYour Name 	INIT_LIST_HEAD(&lro_desc_pool->lro_free_list_head);
48*5113495bSYour Name 
49*5113495bSYour Name 	for (i = 0; i < QDF_LRO_DESC_POOL_SZ; i++) {
50*5113495bSYour Name 		lro_desc_pool->lro_desc_array[i].lro_desc =
51*5113495bSYour Name 			 &lro_mgr->lro_arr[i];
52*5113495bSYour Name 		list_add_tail(&lro_desc_pool->lro_desc_array[i].lro_node,
53*5113495bSYour Name 			 &lro_desc_pool->lro_free_list_head);
54*5113495bSYour Name 	}
55*5113495bSYour Name }
56*5113495bSYour Name 
57*5113495bSYour Name /**
58*5113495bSYour Name  * qdf_lro_desc_info_init() - Initialize the LRO descriptors
59*5113495bSYour Name  * @qdf_info: QDF LRO data structure
60*5113495bSYour Name  *
61*5113495bSYour Name  * Initialize the free pool of LRO descriptors and the entries
62*5113495bSYour Name  * of the hash table
63*5113495bSYour Name  *
64*5113495bSYour Name  * Return: none
65*5113495bSYour Name  */
qdf_lro_desc_info_init(struct qdf_lro_s * qdf_info)66*5113495bSYour Name static void qdf_lro_desc_info_init(struct qdf_lro_s *qdf_info)
67*5113495bSYour Name {
68*5113495bSYour Name 	int i;
69*5113495bSYour Name 
70*5113495bSYour Name 	/* Initialize pool of free LRO desc.*/
71*5113495bSYour Name 	qdf_lro_desc_pool_init(&qdf_info->lro_desc_info.lro_desc_pool,
72*5113495bSYour Name 		 qdf_info->lro_mgr);
73*5113495bSYour Name 
74*5113495bSYour Name 	/* Initialize the hash table of LRO desc.*/
75*5113495bSYour Name 	for (i = 0; i < QDF_LRO_DESC_TABLE_SZ; i++) {
76*5113495bSYour Name 		/* initialize the flows in the hash table */
77*5113495bSYour Name 		INIT_LIST_HEAD(&qdf_info->lro_desc_info.
78*5113495bSYour Name 			 lro_hash_table[i].lro_desc_list);
79*5113495bSYour Name 	}
80*5113495bSYour Name 
81*5113495bSYour Name }
82*5113495bSYour Name 
83*5113495bSYour Name /**
84*5113495bSYour Name  * qdf_lro_get_skb_header() - LRO callback function
85*5113495bSYour Name  * @skb: network buffer
86*5113495bSYour Name  * @ip_hdr: contains a pointer to the IP header
87*5113495bSYour Name  * @tcpudp_hdr: contains a pointer to the TCP header
88*5113495bSYour Name  * @hdr_flags: indicates if this is a TCP, IPV4 frame
89*5113495bSYour Name  * @priv: private driver specific opaque pointer
90*5113495bSYour Name  *
91*5113495bSYour Name  * Get the IP and TCP headers from the skb
92*5113495bSYour Name  *
93*5113495bSYour Name  * Return: 0 - success, < 0 - failure
94*5113495bSYour Name  */
qdf_lro_get_skb_header(struct sk_buff * skb,void ** ip_hdr,void ** tcpudp_hdr,u64 * hdr_flags,void * priv)95*5113495bSYour Name static int qdf_lro_get_skb_header(struct sk_buff *skb, void **ip_hdr,
96*5113495bSYour Name 	void **tcpudp_hdr, u64 *hdr_flags, void *priv)
97*5113495bSYour Name {
98*5113495bSYour Name 	if (QDF_NBUF_CB_RX_IPV6_PROTO(skb)) {
99*5113495bSYour Name 		hdr_flags = 0;
100*5113495bSYour Name 		return -EINVAL;
101*5113495bSYour Name 	}
102*5113495bSYour Name 
103*5113495bSYour Name 	*hdr_flags |= (LRO_IPV4 | LRO_TCP);
104*5113495bSYour Name 	(*ip_hdr) = skb->data;
105*5113495bSYour Name 	(*tcpudp_hdr) = skb->data + QDF_NBUF_CB_RX_TCP_OFFSET(skb);
106*5113495bSYour Name 	return 0;
107*5113495bSYour Name }
108*5113495bSYour Name 
qdf_lro_init(void)109*5113495bSYour Name qdf_lro_ctx_t qdf_lro_init(void)
110*5113495bSYour Name {
111*5113495bSYour Name 	struct qdf_lro_s *lro_ctx;
112*5113495bSYour Name 	size_t lro_info_sz, lro_mgr_sz, desc_arr_sz, desc_pool_sz;
113*5113495bSYour Name 	size_t hash_table_sz;
114*5113495bSYour Name 	uint8_t *lro_mem_ptr;
115*5113495bSYour Name 
116*5113495bSYour Name 	/*
117*5113495bSYour Name 	 * Allocate all the LRO data structures at once and then carve
118*5113495bSYour Name 	 * them up as needed
119*5113495bSYour Name 	 */
120*5113495bSYour Name 	lro_info_sz = sizeof(struct qdf_lro_s);
121*5113495bSYour Name 	lro_mgr_sz = sizeof(struct net_lro_mgr);
122*5113495bSYour Name 	desc_arr_sz =
123*5113495bSYour Name 		 (QDF_LRO_DESC_POOL_SZ * sizeof(struct net_lro_desc));
124*5113495bSYour Name 	desc_pool_sz =
125*5113495bSYour Name 		 (QDF_LRO_DESC_POOL_SZ * sizeof(struct qdf_lro_desc_entry));
126*5113495bSYour Name 	hash_table_sz =
127*5113495bSYour Name 		 (sizeof(struct qdf_lro_desc_table) * QDF_LRO_DESC_TABLE_SZ);
128*5113495bSYour Name 
129*5113495bSYour Name 	lro_mem_ptr = qdf_mem_malloc(lro_info_sz + lro_mgr_sz + desc_arr_sz +
130*5113495bSYour Name 					desc_pool_sz + hash_table_sz);
131*5113495bSYour Name 
132*5113495bSYour Name 	if (unlikely(!lro_mem_ptr))
133*5113495bSYour Name 		return NULL;
134*5113495bSYour Name 
135*5113495bSYour Name 	lro_ctx = (struct qdf_lro_s *)lro_mem_ptr;
136*5113495bSYour Name 	lro_mem_ptr += lro_info_sz;
137*5113495bSYour Name 	/* LRO manager */
138*5113495bSYour Name 	lro_ctx->lro_mgr = (struct net_lro_mgr *)lro_mem_ptr;
139*5113495bSYour Name 	lro_mem_ptr += lro_mgr_sz;
140*5113495bSYour Name 
141*5113495bSYour Name 	/* LRO descriptor array */
142*5113495bSYour Name 	lro_ctx->lro_mgr->lro_arr = (struct net_lro_desc *)lro_mem_ptr;
143*5113495bSYour Name 	lro_mem_ptr += desc_arr_sz;
144*5113495bSYour Name 
145*5113495bSYour Name 	/* LRO descriptor pool */
146*5113495bSYour Name 	lro_ctx->lro_desc_info.lro_desc_pool.lro_desc_array =
147*5113495bSYour Name 		 (struct qdf_lro_desc_entry *)lro_mem_ptr;
148*5113495bSYour Name 	lro_mem_ptr += desc_pool_sz;
149*5113495bSYour Name 
150*5113495bSYour Name 	/* hash table to store the LRO descriptors */
151*5113495bSYour Name 	lro_ctx->lro_desc_info.lro_hash_table =
152*5113495bSYour Name 		 (struct qdf_lro_desc_table *)lro_mem_ptr;
153*5113495bSYour Name 
154*5113495bSYour Name 	/* Initialize the LRO descriptors */
155*5113495bSYour Name 	qdf_lro_desc_info_init(lro_ctx);
156*5113495bSYour Name 
157*5113495bSYour Name 	/* LRO TODO - NAPI or RX thread */
158*5113495bSYour Name 	lro_ctx->lro_mgr->features |= LRO_F_NAPI;
159*5113495bSYour Name 
160*5113495bSYour Name 	lro_ctx->lro_mgr->ip_summed_aggr = CHECKSUM_UNNECESSARY;
161*5113495bSYour Name 	lro_ctx->lro_mgr->max_aggr = QDF_LRO_MAX_AGGR_SIZE;
162*5113495bSYour Name 	lro_ctx->lro_mgr->get_skb_header = qdf_lro_get_skb_header;
163*5113495bSYour Name 	lro_ctx->lro_mgr->ip_summed = CHECKSUM_UNNECESSARY;
164*5113495bSYour Name 	lro_ctx->lro_mgr->max_desc = QDF_LRO_DESC_POOL_SZ;
165*5113495bSYour Name 
166*5113495bSYour Name 	return lro_ctx;
167*5113495bSYour Name }
168*5113495bSYour Name 
qdf_lro_deinit(qdf_lro_ctx_t lro_ctx)169*5113495bSYour Name void qdf_lro_deinit(qdf_lro_ctx_t lro_ctx)
170*5113495bSYour Name {
171*5113495bSYour Name 	if (likely(lro_ctx)) {
172*5113495bSYour Name 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
173*5113495bSYour Name 			 "LRO instance %pK is being freed", lro_ctx);
174*5113495bSYour Name 		qdf_mem_free(lro_ctx);
175*5113495bSYour Name 	}
176*5113495bSYour Name }
177*5113495bSYour Name 
178*5113495bSYour Name /**
179*5113495bSYour Name  * qdf_lro_tcp_flow_match() - function to check for a flow match
180*5113495bSYour Name  * @lro_desc: LRO descriptor
181*5113495bSYour Name  * @iph: IP header
182*5113495bSYour Name  * @tcph: TCP header
183*5113495bSYour Name  *
184*5113495bSYour Name  * Checks if the descriptor belongs to the same flow as the one
185*5113495bSYour Name  * indicated by the TCP and IP header.
186*5113495bSYour Name  *
187*5113495bSYour Name  * Return: true - flow match, false - flow does not match
188*5113495bSYour Name  */
qdf_lro_tcp_flow_match(struct net_lro_desc * lro_desc,struct iphdr * iph,struct tcphdr * tcph)189*5113495bSYour Name static inline bool qdf_lro_tcp_flow_match(struct net_lro_desc *lro_desc,
190*5113495bSYour Name 					  struct iphdr *iph,
191*5113495bSYour Name 					  struct tcphdr *tcph)
192*5113495bSYour Name {
193*5113495bSYour Name 	if ((lro_desc->tcph->source != tcph->source) ||
194*5113495bSYour Name 		 (lro_desc->tcph->dest != tcph->dest) ||
195*5113495bSYour Name 		 (lro_desc->iph->saddr != iph->saddr) ||
196*5113495bSYour Name 		 (lro_desc->iph->daddr != iph->daddr))
197*5113495bSYour Name 		return false;
198*5113495bSYour Name 
199*5113495bSYour Name 	return true;
200*5113495bSYour Name 
201*5113495bSYour Name }
202*5113495bSYour Name 
203*5113495bSYour Name /**
204*5113495bSYour Name  * qdf_lro_desc_find() - LRO descriptor look-up function
205*5113495bSYour Name  *
206*5113495bSYour Name  * @lro_ctx: LRO context
207*5113495bSYour Name  * @skb: network buffer
208*5113495bSYour Name  * @iph: IP header
209*5113495bSYour Name  * @tcph: TCP header
210*5113495bSYour Name  * @flow_hash: toeplitz hash
211*5113495bSYour Name  * @lro_desc: LRO descriptor to be returned
212*5113495bSYour Name  *
213*5113495bSYour Name  * Look-up the LRO descriptor in the hash table based on the
214*5113495bSYour Name  * flow ID toeplitz. If the flow is not found, allocates a new
215*5113495bSYour Name  * LRO descriptor and places it in the hash table
216*5113495bSYour Name  *
217*5113495bSYour Name  * Return: 0 - success, < 0 - failure
218*5113495bSYour Name  */
qdf_lro_desc_find(struct qdf_lro_s * lro_ctx,struct sk_buff * skb,struct iphdr * iph,struct tcphdr * tcph,uint32_t flow_hash,struct net_lro_desc ** lro_desc)219*5113495bSYour Name static int qdf_lro_desc_find(struct qdf_lro_s *lro_ctx,
220*5113495bSYour Name 	 struct sk_buff *skb, struct iphdr *iph, struct tcphdr *tcph,
221*5113495bSYour Name 	 uint32_t flow_hash, struct net_lro_desc **lro_desc)
222*5113495bSYour Name {
223*5113495bSYour Name 	uint32_t i;
224*5113495bSYour Name 	struct qdf_lro_desc_table *lro_hash_table;
225*5113495bSYour Name 	struct list_head *ptr;
226*5113495bSYour Name 	struct qdf_lro_desc_entry *entry;
227*5113495bSYour Name 	struct qdf_lro_desc_pool *free_pool;
228*5113495bSYour Name 	struct qdf_lro_desc_info *desc_info = &lro_ctx->lro_desc_info;
229*5113495bSYour Name 
230*5113495bSYour Name 	*lro_desc = NULL;
231*5113495bSYour Name 	i = flow_hash & QDF_LRO_DESC_TABLE_SZ_MASK;
232*5113495bSYour Name 
233*5113495bSYour Name 	lro_hash_table = &desc_info->lro_hash_table[i];
234*5113495bSYour Name 
235*5113495bSYour Name 	if (unlikely(!lro_hash_table)) {
236*5113495bSYour Name 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
237*5113495bSYour Name 			 "Invalid hash entry");
238*5113495bSYour Name 		QDF_ASSERT(0);
239*5113495bSYour Name 		return -EINVAL;
240*5113495bSYour Name 	}
241*5113495bSYour Name 
242*5113495bSYour Name 	/* Check if this flow exists in the descriptor list */
243*5113495bSYour Name 	list_for_each(ptr, &lro_hash_table->lro_desc_list) {
244*5113495bSYour Name 		struct net_lro_desc *tmp_lro_desc = NULL;
245*5113495bSYour Name 
246*5113495bSYour Name 		entry = list_entry(ptr, struct qdf_lro_desc_entry, lro_node);
247*5113495bSYour Name 		tmp_lro_desc = entry->lro_desc;
248*5113495bSYour Name 			if (qdf_lro_tcp_flow_match(entry->lro_desc, iph, tcph)) {
249*5113495bSYour Name 				*lro_desc = entry->lro_desc;
250*5113495bSYour Name 				return 0;
251*5113495bSYour Name 			}
252*5113495bSYour Name 	}
253*5113495bSYour Name 
254*5113495bSYour Name 	/* no existing flow found, a new LRO desc needs to be allocated */
255*5113495bSYour Name 	free_pool = &lro_ctx->lro_desc_info.lro_desc_pool;
256*5113495bSYour Name 	entry = list_first_entry_or_null(
257*5113495bSYour Name 		 &free_pool->lro_free_list_head,
258*5113495bSYour Name 		 struct qdf_lro_desc_entry, lro_node);
259*5113495bSYour Name 	if (unlikely(!entry)) {
260*5113495bSYour Name 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
261*5113495bSYour Name 			 "Could not allocate LRO desc!");
262*5113495bSYour Name 		return -ENOMEM;
263*5113495bSYour Name 	}
264*5113495bSYour Name 
265*5113495bSYour Name 	list_del_init(&entry->lro_node);
266*5113495bSYour Name 
267*5113495bSYour Name 	if (unlikely(!entry->lro_desc)) {
268*5113495bSYour Name 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
269*5113495bSYour Name 			 "entry->lro_desc is NULL!");
270*5113495bSYour Name 		return -EINVAL;
271*5113495bSYour Name 	}
272*5113495bSYour Name 
273*5113495bSYour Name 	memset(entry->lro_desc, 0, sizeof(struct net_lro_desc));
274*5113495bSYour Name 
275*5113495bSYour Name 	/*
276*5113495bSYour Name 	 * lro_desc->active should be 0 and lro_desc->tcp_rcv_tsval
277*5113495bSYour Name 	 * should be 0 for newly allocated lro descriptors
278*5113495bSYour Name 	 */
279*5113495bSYour Name 	list_add_tail(&entry->lro_node,
280*5113495bSYour Name 		 &lro_hash_table->lro_desc_list);
281*5113495bSYour Name 
282*5113495bSYour Name 	*lro_desc = entry->lro_desc;
283*5113495bSYour Name 	return 0;
284*5113495bSYour Name }
285*5113495bSYour Name 
286*5113495bSYour Name /**
287*5113495bSYour Name  * qdf_lro_get_info() - Update the LRO information
288*5113495bSYour Name  *
289*5113495bSYour Name  * @lro_ctx: LRO context
290*5113495bSYour Name  * @nbuf: network buffer
291*5113495bSYour Name  * @info: LRO related information passed in by the caller
292*5113495bSYour Name  * @plro_desc: lro information returned as output
293*5113495bSYour Name  *
294*5113495bSYour Name  * Look-up the LRO descriptor based on the LRO information and
295*5113495bSYour Name  * the network buffer provided. Update the skb cb with the
296*5113495bSYour Name  * descriptor found
297*5113495bSYour Name  *
298*5113495bSYour Name  * Return: true: LRO eligible false: LRO ineligible
299*5113495bSYour Name  */
qdf_lro_get_info(qdf_lro_ctx_t lro_ctx,qdf_nbuf_t nbuf,struct qdf_lro_info * info,void ** plro_desc)300*5113495bSYour Name bool qdf_lro_get_info(qdf_lro_ctx_t lro_ctx, qdf_nbuf_t nbuf,
301*5113495bSYour Name 						 struct qdf_lro_info *info,
302*5113495bSYour Name 						 void **plro_desc)
303*5113495bSYour Name {
304*5113495bSYour Name 	struct net_lro_desc *lro_desc;
305*5113495bSYour Name 	struct iphdr *iph;
306*5113495bSYour Name 	struct tcphdr *tcph;
307*5113495bSYour Name 	int hw_lro_eligible =
308*5113495bSYour Name 		 QDF_NBUF_CB_RX_LRO_ELIGIBLE(nbuf) &&
309*5113495bSYour Name 		 (!QDF_NBUF_CB_RX_TCP_PURE_ACK(nbuf));
310*5113495bSYour Name 
311*5113495bSYour Name 	if (unlikely(!lro_ctx)) {
312*5113495bSYour Name 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
313*5113495bSYour Name 			 "Invalid LRO context");
314*5113495bSYour Name 		return false;
315*5113495bSYour Name 	}
316*5113495bSYour Name 
317*5113495bSYour Name 	if (!hw_lro_eligible)
318*5113495bSYour Name 		return false;
319*5113495bSYour Name 
320*5113495bSYour Name 	iph = (struct iphdr *)info->iph;
321*5113495bSYour Name 	tcph = (struct tcphdr *)info->tcph;
322*5113495bSYour Name 	if (0 != qdf_lro_desc_find(lro_ctx, nbuf, iph, tcph,
323*5113495bSYour Name 		 QDF_NBUF_CB_RX_FLOW_ID(nbuf),
324*5113495bSYour Name 		 (struct net_lro_desc **)plro_desc)) {
325*5113495bSYour Name 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
326*5113495bSYour Name 			 "finding the LRO desc failed");
327*5113495bSYour Name 		return false;
328*5113495bSYour Name 	}
329*5113495bSYour Name 
330*5113495bSYour Name 	lro_desc = (struct net_lro_desc *)(*plro_desc);
331*5113495bSYour Name 	if (unlikely(!lro_desc)) {
332*5113495bSYour Name 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
333*5113495bSYour Name 			 "finding the LRO desc failed");
334*5113495bSYour Name 		return false;
335*5113495bSYour Name 	}
336*5113495bSYour Name 
337*5113495bSYour Name 	/* if this is not the first skb, check the timestamp option */
338*5113495bSYour Name 	if (lro_desc->tcp_rcv_tsval) {
339*5113495bSYour Name 		if (tcph->doff == 8) {
340*5113495bSYour Name 			__be32 *topt = (__be32 *)(tcph + 1);
341*5113495bSYour Name 
342*5113495bSYour Name 			if (*topt != htonl((TCPOPT_NOP << 24)
343*5113495bSYour Name 				 |(TCPOPT_NOP << 16)
344*5113495bSYour Name 				 | (TCPOPT_TIMESTAMP << 8)
345*5113495bSYour Name 				 | TCPOLEN_TIMESTAMP))
346*5113495bSYour Name 				return true;
347*5113495bSYour Name 
348*5113495bSYour Name 			/* timestamp should be in right order */
349*5113495bSYour Name 			topt++;
350*5113495bSYour Name 			if (after(ntohl(lro_desc->tcp_rcv_tsval),
351*5113495bSYour Name 					 ntohl(*topt)))
352*5113495bSYour Name 				return false;
353*5113495bSYour Name 
354*5113495bSYour Name 			/* timestamp reply should not be zero */
355*5113495bSYour Name 			topt++;
356*5113495bSYour Name 			if (*topt == 0)
357*5113495bSYour Name 				return false;
358*5113495bSYour Name 		}
359*5113495bSYour Name 	}
360*5113495bSYour Name 
361*5113495bSYour Name 	return true;
362*5113495bSYour Name }
363*5113495bSYour Name 
qdf_lro_desc_free(qdf_lro_ctx_t lro_ctx,void * data)364*5113495bSYour Name void qdf_lro_desc_free(qdf_lro_ctx_t lro_ctx, void *data)
365*5113495bSYour Name {
366*5113495bSYour Name 	struct qdf_lro_desc_entry *entry;
367*5113495bSYour Name 	struct net_lro_mgr *lro_mgr;
368*5113495bSYour Name 	struct net_lro_desc *arr_base;
369*5113495bSYour Name 	struct qdf_lro_desc_info *desc_info;
370*5113495bSYour Name 	int i;
371*5113495bSYour Name 	struct net_lro_desc *desc = (struct net_lro_desc *)data;
372*5113495bSYour Name 
373*5113495bSYour Name 	qdf_assert(desc);
374*5113495bSYour Name 	qdf_assert(lro_ctx);
375*5113495bSYour Name 
376*5113495bSYour Name 	if (unlikely(!desc || !lro_ctx)) {
377*5113495bSYour Name 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
378*5113495bSYour Name 			 "invalid input");
379*5113495bSYour Name 		return;
380*5113495bSYour Name 	}
381*5113495bSYour Name 
382*5113495bSYour Name 	lro_mgr = lro_ctx->lro_mgr;
383*5113495bSYour Name 	arr_base = lro_mgr->lro_arr;
384*5113495bSYour Name 	i = desc - arr_base;
385*5113495bSYour Name 
386*5113495bSYour Name 	if (unlikely(i >= QDF_LRO_DESC_POOL_SZ)) {
387*5113495bSYour Name 		QDF_TRACE(QDF_MODULE_ID_QDF, QDF_TRACE_LEVEL_ERROR,
388*5113495bSYour Name 			 "invalid index %d", i);
389*5113495bSYour Name 		return;
390*5113495bSYour Name 	}
391*5113495bSYour Name 
392*5113495bSYour Name 	desc_info =  &lro_ctx->lro_desc_info;
393*5113495bSYour Name 	entry = &desc_info->lro_desc_pool.lro_desc_array[i];
394*5113495bSYour Name 
395*5113495bSYour Name 	list_del_init(&entry->lro_node);
396*5113495bSYour Name 
397*5113495bSYour Name 	list_add_tail(&entry->lro_node, &desc_info->
398*5113495bSYour Name 		 lro_desc_pool.lro_free_list_head);
399*5113495bSYour Name }
400*5113495bSYour Name 
qdf_lro_flush(qdf_lro_ctx_t lro_ctx)401*5113495bSYour Name void qdf_lro_flush(qdf_lro_ctx_t lro_ctx)
402*5113495bSYour Name {
403*5113495bSYour Name 	struct net_lro_mgr *lro_mgr = lro_ctx->lro_mgr;
404*5113495bSYour Name 	int i;
405*5113495bSYour Name 
406*5113495bSYour Name 	for (i = 0; i < lro_mgr->max_desc; i++) {
407*5113495bSYour Name 		if (lro_mgr->lro_arr[i].active) {
408*5113495bSYour Name 			qdf_lro_desc_free(lro_ctx, &lro_mgr->lro_arr[i]);
409*5113495bSYour Name 			lro_flush_desc(lro_mgr, &lro_mgr->lro_arr[i]);
410*5113495bSYour Name 		}
411*5113495bSYour Name 	}
412*5113495bSYour Name }
413*5113495bSYour Name 
414*5113495bSYour Name /**
415*5113495bSYour Name  * qdf_lro_get_desc() - LRO descriptor look-up function
416*5113495bSYour Name  * @iph: IP header
417*5113495bSYour Name  * @tcph: TCP header
418*5113495bSYour Name  * @lro_arr: Array of LRO descriptors
419*5113495bSYour Name  * @lro_mgr: LRO manager
420*5113495bSYour Name  *
421*5113495bSYour Name  * Looks-up the LRO descriptor for a given flow
422*5113495bSYour Name  *
423*5113495bSYour Name  * Return: LRO descriptor
424*5113495bSYour Name  */
qdf_lro_get_desc(struct net_lro_mgr * lro_mgr,struct net_lro_desc * lro_arr,struct iphdr * iph,struct tcphdr * tcph)425*5113495bSYour Name static struct net_lro_desc *qdf_lro_get_desc(struct net_lro_mgr *lro_mgr,
426*5113495bSYour Name 	 struct net_lro_desc *lro_arr,
427*5113495bSYour Name 	 struct iphdr *iph,
428*5113495bSYour Name 	 struct tcphdr *tcph)
429*5113495bSYour Name {
430*5113495bSYour Name 	int i;
431*5113495bSYour Name 
432*5113495bSYour Name 	for (i = 0; i < lro_mgr->max_desc; i++) {
433*5113495bSYour Name 		if (lro_arr[i].active)
434*5113495bSYour Name 			if (qdf_lro_tcp_flow_match(&lro_arr[i], iph, tcph))
435*5113495bSYour Name 				return &lro_arr[i];
436*5113495bSYour Name 	}
437*5113495bSYour Name 
438*5113495bSYour Name 	return NULL;
439*5113495bSYour Name }
440*5113495bSYour Name 
qdf_lro_flush_pkt(qdf_lro_ctx_t lro_ctx,struct qdf_lro_info * info)441*5113495bSYour Name void qdf_lro_flush_pkt(qdf_lro_ctx_t lro_ctx,
442*5113495bSYour Name 		       struct qdf_lro_info *info)
443*5113495bSYour Name {
444*5113495bSYour Name 	struct net_lro_desc *lro_desc;
445*5113495bSYour Name 	struct net_lro_mgr *lro_mgr = lro_ctx->lro_mgr;
446*5113495bSYour Name 	struct iphdr *iph = (struct iphdr *) info->iph;
447*5113495bSYour Name 	struct tcphdr *tcph = (struct tcphdr *) info->tcph;
448*5113495bSYour Name 
449*5113495bSYour Name 	lro_desc = qdf_lro_get_desc(lro_mgr, lro_mgr->lro_arr, iph, tcph);
450*5113495bSYour Name 
451*5113495bSYour Name 	if (lro_desc) {
452*5113495bSYour Name 		/* statistics */
453*5113495bSYour Name 		qdf_lro_desc_free(lro_ctx, lro_desc);
454*5113495bSYour Name 		lro_flush_desc(lro_mgr, lro_desc);
455*5113495bSYour Name 	}
456*5113495bSYour Name }
457