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