xref: /wlan-dirver/qca-wifi-host-cmn/hal/wifi3.0/hal_rx_flow.c (revision 8cfe6b10058a04cafb17eed051f2ddf11bee8931)
1 /*
2  * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
3  * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include "qdf_module.h"
19 #include "dp_types.h"
20 #include "hal_rx_flow.h"
21 
22 /**
23  * hal_rx_flow_get_cmem_fse() - Get FSE from CMEM
24  * @hal_soc_hdl: HAL SOC handle
25  * @fse_offset: CMEM FSE offset
26  * @fse: reference where FSE will be copied
27  * @len: length of FSE
28  *
29  * Return: If read is successful or not
30  */
31 static void
32 hal_rx_flow_get_cmem_fse(hal_soc_handle_t hal_soc_hdl, uint32_t fse_offset,
33 			 uint32_t *fse, qdf_size_t len)
34 {
35 	struct hal_soc *hal_soc = (struct hal_soc *)hal_soc_hdl;
36 
37 	if (hal_soc->ops->hal_rx_flow_get_cmem_fse) {
38 		return hal_soc->ops->hal_rx_flow_get_cmem_fse(
39 						hal_soc, fse_offset, fse, len);
40 	}
41 }
42 
43 #if defined(WLAN_SUPPORT_RX_FISA)
44 static inline void hal_rx_dump_fse(struct rx_flow_search_entry *fse, int index)
45 {
46 		dp_info("index %d:"
47 		" src_ip_127_96 0x%x"
48 		" src_ip_95_640 0x%x"
49 		" src_ip_63_32 0x%x"
50 		" src_ip_31_0 0x%x"
51 		" dest_ip_127_96 0x%x"
52 		" dest_ip_95_64 0x%x"
53 		" dest_ip_63_32 0x%x"
54 		" dest_ip_31_0 0x%x"
55 		" src_port 0x%x"
56 		" dest_port 0x%x"
57 		" l4_protocol 0x%x"
58 		" valid 0x%x"
59 		" reo_destination_indication 0x%x"
60 		" msdu_drop 0x%x"
61 		" reo_destination_handler 0x%x"
62 		" metadata 0x%x"
63 		" aggregation_count0x%x"
64 		" lro_eligible 0x%x"
65 		" msdu_count 0x%x"
66 		" msdu_byte_count 0x%x"
67 		" timestamp 0x%x"
68 		" cumulative_l4_checksum 0x%x"
69 		" cumulative_ip_length 0x%x"
70 		" tcp_sequence_number 0x%x",
71 		index,
72 		fse->src_ip_127_96,
73 		fse->src_ip_95_64,
74 		fse->src_ip_63_32,
75 		fse->src_ip_31_0,
76 		fse->dest_ip_127_96,
77 		fse->dest_ip_95_64,
78 		fse->dest_ip_63_32,
79 		fse->dest_ip_31_0,
80 		fse->src_port,
81 		fse->dest_port,
82 		fse->l4_protocol,
83 		fse->valid,
84 		fse->reo_destination_indication,
85 		fse->msdu_drop,
86 		fse->reo_destination_handler,
87 		fse->metadata,
88 		fse->aggregation_count,
89 		fse->lro_eligible,
90 		fse->msdu_count,
91 		fse->msdu_byte_count,
92 		fse->timestamp,
93 #ifdef QCA_WIFI_KIWI_V2
94 		fse->cumulative_ip_length_pmac1,
95 #else
96 		fse->cumulative_l4_checksum,
97 #endif
98 		fse->cumulative_ip_length,
99 		fse->tcp_sequence_number);
100 }
101 
102 void hal_rx_dump_fse_table(struct hal_rx_fst *fst)
103 {
104 	int i = 0;
105 	struct rx_flow_search_entry *fse =
106 		(struct rx_flow_search_entry *)fst->base_vaddr;
107 
108 	dp_info("Number flow table entries %d", fst->add_flow_count);
109 	for (i = 0; i < fst->max_entries; i++) {
110 		if (fse[i].valid)
111 			hal_rx_dump_fse(&fse[i], i);
112 	}
113 }
114 
115 void hal_rx_dump_cmem_fse(hal_soc_handle_t hal_soc_hdl, uint32_t fse_offset,
116 			  int index)
117 {
118 	struct rx_flow_search_entry fse = {0};
119 
120 	if (!fse_offset)
121 		return;
122 
123 	hal_rx_flow_get_cmem_fse(hal_soc_hdl, fse_offset, (uint32_t *)&fse,
124 				 sizeof(struct rx_flow_search_entry));
125 	if (fse.valid)
126 		hal_rx_dump_fse(&fse, index);
127 }
128 #else
129 void hal_rx_dump_fse_table(struct hal_rx_fst *fst)
130 {
131 }
132 
133 void hal_rx_dump_cmem_fse(hal_soc_handle_t hal_soc_hdl, uint32_t fse_offset,
134 			  int index)
135 {
136 }
137 #endif
138 
139 /**
140  * hal_rx_flow_setup_fse() - Setup a flow search entry in HW FST
141  * @fst: Pointer to the Rx Flow Search Table
142  * @table_offset: offset into the table where the flow is to be setup
143  * @flow: Flow Parameters
144  *
145  * Return: Success/Failure
146  */
147 void *
148 hal_rx_flow_setup_fse(hal_soc_handle_t hal_soc_hdl,
149 		      struct hal_rx_fst *fst, uint32_t table_offset,
150 		      struct hal_rx_flow *flow)
151 {
152 	struct hal_soc *hal_soc = (struct hal_soc *)hal_soc_hdl;
153 
154 	if (hal_soc->ops->hal_rx_flow_setup_fse) {
155 		return hal_soc->ops->hal_rx_flow_setup_fse((uint8_t *)fst,
156 							   table_offset,
157 							   (uint8_t *)flow);
158 	}
159 
160 	return NULL;
161 }
162 qdf_export_symbol(hal_rx_flow_setup_fse);
163 
164 /**
165  * hal_rx_flow_setup_cmem_fse() - Setup a flow search entry in HW CMEM FST
166  * @hal_soc_hdl: HAL SOC handle
167  * @cmem_ba: CMEM base address
168  * @table_offset: offset into the table where the flow is to be setup
169  * @flow: Flow Parameters
170  *
171  * Return: Success/Failure
172  */
173 uint32_t
174 hal_rx_flow_setup_cmem_fse(hal_soc_handle_t hal_soc_hdl, uint32_t cmem_ba,
175 			   uint32_t table_offset, struct hal_rx_flow *flow)
176 {
177 	struct hal_soc *hal_soc = (struct hal_soc *)hal_soc_hdl;
178 
179 	if (hal_soc->ops->hal_rx_flow_setup_cmem_fse) {
180 		return hal_soc->ops->hal_rx_flow_setup_cmem_fse(
181 						hal_soc, cmem_ba,
182 						table_offset, (uint8_t *)flow);
183 	}
184 
185 	return 0;
186 }
187 qdf_export_symbol(hal_rx_flow_setup_cmem_fse);
188 
189 /**
190  * hal_rx_flow_get_cmem_fse_timestamp() - Get timestamp field from CMEM FSE
191  * @hal_soc_hdl: HAL SOC handle
192  * @fse_offset: CMEM FSE offset
193  *
194  * Return: Timestamp
195  */
196 uint32_t hal_rx_flow_get_cmem_fse_timestamp(hal_soc_handle_t hal_soc_hdl,
197 					    uint32_t fse_offset)
198 {
199 	struct hal_soc *hal_soc = (struct hal_soc *)hal_soc_hdl;
200 
201 	if (hal_soc->ops->hal_rx_flow_get_cmem_fse_ts) {
202 		return hal_soc->ops->hal_rx_flow_get_cmem_fse_ts(hal_soc,
203 								 fse_offset);
204 	}
205 
206 	return 0;
207 }
208 qdf_export_symbol(hal_rx_flow_get_cmem_fse_timestamp);
209 
210 /**
211  * hal_rx_flow_delete_entry() - Delete a flow from the Rx Flow Search Table
212  * @hal_soc_hdl: HAL SOC handle
213  * @fst: Pointer to the Rx Flow Search Table
214  * @hal_rx_fse: Pointer to the Rx Flow that is to be deleted from the FST
215  *
216  * Return: Success/Failure
217  */
218 QDF_STATUS
219 hal_rx_flow_delete_entry(hal_soc_handle_t hal_soc_hdl,
220 			 struct hal_rx_fst *fst, void *hal_rx_fse)
221 {
222 	struct hal_soc *hal_soc = (struct hal_soc *)hal_soc_hdl;
223 
224 	if (hal_soc->ops->hal_rx_flow_delete_entry) {
225 		return hal_soc->ops->hal_rx_flow_delete_entry((uint8_t *)fst,
226 							   hal_rx_fse);
227 	}
228 
229 	return QDF_STATUS_E_NOSUPPORT;
230 }
231 
232 qdf_export_symbol(hal_rx_flow_delete_entry);
233 
234 #ifndef WLAN_SUPPORT_RX_FISA
235 /**
236  * hal_rx_fst_key_configure() - Configure the Toeplitz key in the FST
237  * @fst: Pointer to the Rx Flow Search Table
238  *
239  * Return: Success/Failure
240  */
241 static void hal_rx_fst_key_configure(struct hal_rx_fst *fst)
242 {
243 	uint8_t key_bytes[HAL_FST_HASH_KEY_SIZE_BYTES];
244 
245 	qdf_mem_copy(key_bytes, fst->key, HAL_FST_HASH_KEY_SIZE_BYTES);
246 
247 	/**
248 	 * The Toeplitz algorithm as per the Microsoft spec works in a
249 	 * “big-endian” manner, using the MSBs of the key to hash the
250 	 * initial bytes of the input going on to use up the lower order bits
251 	 * of the key to hash further bytes of the input until the LSBs of the
252 	 * key are used finally.
253 	 *
254 	 * So first, rightshift 320-bit input key 5 times to get 315 MS bits
255 	 */
256 	key_bitwise_shift_left(key_bytes, HAL_FST_HASH_KEY_SIZE_BYTES, 5);
257 	key_reverse(fst->shifted_key, key_bytes, HAL_FST_HASH_KEY_SIZE_BYTES);
258 }
259 #else
260 static void hal_rx_fst_key_configure(struct hal_rx_fst *fst)
261 {
262 }
263 #endif
264 
265 /**
266  * hal_rx_fst_get_base() - Retrieve the virtual base address of the Rx FST
267  * @fst: Pointer to the Rx Flow Search Table
268  *
269  * Return: Success/Failure
270  */
271 static inline void *hal_rx_fst_get_base(struct hal_rx_fst *fst)
272 {
273 	return fst->base_vaddr;
274 }
275 
276 /**
277  * hal_rx_fst_get_fse_size() - Retrieve the size of each entry(flow) in Rx FST
278  *
279  * Return: size of each entry/flow in Rx FST
280  */
281 static inline uint32_t
282 hal_rx_fst_get_fse_size(hal_soc_handle_t hal_soc_hdl)
283 {
284 	struct hal_soc *hal_soc = (struct hal_soc *)hal_soc_hdl;
285 
286 	if (hal_soc->ops->hal_rx_fst_get_fse_size)
287 		return hal_soc->ops->hal_rx_fst_get_fse_size();
288 
289 	return 0;
290 }
291 
292 /**
293  * hal_rx_flow_get_tuple_info() - Get a flow search entry in HW FST
294  * @hal_soc_hdl: HAL SOC handle
295  * @fst: Pointer to the Rx Flow Search Table
296  * @hal_hash: HAL 5 tuple hash
297  * @tuple_info: 5-tuple info of the flow returned to the caller
298  *
299  * Return: Success/Failure
300  */
301 void *
302 hal_rx_flow_get_tuple_info(hal_soc_handle_t hal_soc_hdl,
303 			   struct hal_rx_fst *fst,
304 			   uint32_t hal_hash,
305 			   struct hal_flow_tuple_info *tuple_info)
306 {
307 	struct hal_soc *hal_soc = (struct hal_soc *)hal_soc_hdl;
308 
309 	if (hal_soc->ops->hal_rx_flow_get_tuple_info)
310 		return hal_soc->ops->hal_rx_flow_get_tuple_info(
311 						(uint8_t *)fst,
312 						hal_hash,
313 						(uint8_t *)tuple_info);
314 
315 	return NULL;
316 }
317 
318 #ifndef WLAN_SUPPORT_RX_FISA
319 /**
320  * hal_flow_toeplitz_create_cache() - Calculate hashes for each possible
321  * byte value with the key taken as is
322  *
323  * @fst: FST Handle
324  * @key: Hash Key
325  *
326  * Return: Success/Failure
327  */
328 static void hal_flow_toeplitz_create_cache(struct hal_rx_fst *fst)
329 {
330 	int bit;
331 	int val;
332 	int i;
333 	uint8_t *key = fst->shifted_key;
334 
335 	/*
336 	 * Initialise to first 32 bits of the key; shift in further key material
337 	 * through the loop
338 	 */
339 	uint32_t cur_key = (key[0] << 24) | (key[1] << 16) | (key[2] << 8) |
340 		key[3];
341 
342 	for (i = 0; i < HAL_FST_HASH_KEY_SIZE_BYTES; i++) {
343 		uint8_t new_key_byte;
344 		uint32_t shifted_key[8];
345 
346 		if (i + 4 < HAL_FST_HASH_KEY_SIZE_BYTES)
347 			new_key_byte = key[i + 4];
348 		else
349 			new_key_byte = 0;
350 
351 		shifted_key[0] = cur_key;
352 
353 		for (bit = 1; bit < 8; bit++) {
354 			/*
355 			 * For each iteration, shift out one more bit of the
356 			 * current key and shift in one more bit of the new key
357 			 * material
358 			 */
359 			shifted_key[bit] = cur_key << bit |
360 				new_key_byte >> (8 - bit);
361 		}
362 
363 		for (val = 0; val < (1 << 8); val++) {
364 			uint32_t hash = 0;
365 			int mask;
366 
367 			/*
368 			 * For each bit set in the input, XOR in
369 			 * the appropriately shifted key
370 			 */
371 			for (bit = 0, mask = 1 << 7; bit < 8; bit++, mask >>= 1)
372 				if ((val & mask))
373 					hash ^= shifted_key[bit];
374 
375 			fst->key_cache[i][val] = hash;
376 		}
377 
378 		cur_key = cur_key << 8 | new_key_byte;
379 	}
380 }
381 #else
382 static void hal_flow_toeplitz_create_cache(struct hal_rx_fst *fst)
383 {
384 }
385 #endif
386 
387 /**
388  * hal_rx_fst_attach() - Initialize Rx flow search table in HW FST
389  *
390  * @qdf_dev: QDF device handle
391  * @hal_fst_base_paddr: Pointer to the physical base address of the Rx FST
392  * @max_entries: Max number of flows allowed in the FST
393  * @max_search: Number of collisions allowed in the hash-based FST
394  * @hash_key: Toeplitz key used for the hash FST
395  *
396  * Return:
397  */
398 struct hal_rx_fst *
399 hal_rx_fst_attach(hal_soc_handle_t hal_soc_hdl,
400 		  qdf_device_t qdf_dev,
401 		  uint64_t *hal_fst_base_paddr, uint16_t max_entries,
402 		  uint16_t max_search, uint8_t *hash_key,
403 		  uint64_t fst_cmem_base)
404 {
405 	struct hal_rx_fst *fst = qdf_mem_malloc(sizeof(struct hal_rx_fst));
406 	uint32_t fst_entry_size;
407 
408 	if (!fst) {
409 		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
410 			  FL("hal fst allocation failed,"));
411 		return NULL;
412 	}
413 
414 	qdf_mem_set(fst, sizeof(struct hal_rx_fst), 0);
415 
416 	fst->key = hash_key;
417 	fst->max_skid_length = max_search;
418 	fst->max_entries = max_entries;
419 	fst->hash_mask = max_entries - 1;
420 
421 	fst_entry_size = hal_rx_fst_get_fse_size(hal_soc_hdl);
422 	fst->fst_entry_size = fst_entry_size;
423 
424 	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
425 		  "HAL FST allocation %pK %d * %d\n", fst,
426 		  fst->max_entries, fst_entry_size);
427 
428 	if (fst_cmem_base == 0) {
429 		/* FST is in DDR */
430 		fst->base_vaddr = (uint8_t *)qdf_mem_alloc_consistent(qdf_dev,
431 				    qdf_dev->dev,
432 				    (fst->max_entries * fst_entry_size),
433 				    &fst->base_paddr);
434 
435 		if (!fst->base_vaddr) {
436 			QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
437 				  FL("hal fst->base_vaddr allocation failed"));
438 			qdf_mem_free(fst);
439 			return NULL;
440 		}
441 
442 		*hal_fst_base_paddr = (uint64_t)fst->base_paddr;
443 	} else {
444 		*hal_fst_base_paddr = fst_cmem_base;
445 		goto out;
446 	}
447 
448 	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO,
449 		  "hal_rx_fst base address 0x%pK", (void *)fst->base_paddr);
450 
451 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_DEBUG,
452 			   (void *)fst->key, HAL_FST_HASH_KEY_SIZE_BYTES);
453 
454 	qdf_mem_set((uint8_t *)fst->base_vaddr,
455 		    (fst->max_entries * fst_entry_size), 0);
456 
457 out:
458 	hal_rx_fst_key_configure(fst);
459 	hal_flow_toeplitz_create_cache(fst);
460 
461 	return fst;
462 }
463 qdf_export_symbol(hal_rx_fst_attach);
464 
465 /**
466  * hal_rx_fst_detach() - De-init the Rx flow search table from HW
467  *
468  * @hal_soc_hdl: HAL SOC handler
469  * @rx_fst: Pointer to the Rx FST
470  * @qdf_dev: QDF device handle
471  *
472  * Return:
473  */
474 void hal_rx_fst_detach(hal_soc_handle_t hal_soc_hdl, struct hal_rx_fst *rx_fst,
475 		       qdf_device_t qdf_dev, uint64_t fst_cmem_base)
476 {
477 
478 	if (!rx_fst || !qdf_dev)
479 		return;
480 
481 	if (fst_cmem_base == 0 && rx_fst->base_vaddr) {
482 		qdf_mem_free_consistent(qdf_dev, qdf_dev->dev,
483 					rx_fst->max_entries *
484 					rx_fst->fst_entry_size,
485 					rx_fst->base_vaddr, rx_fst->base_paddr,
486 					0);
487 	}
488 
489 	qdf_mem_free(rx_fst);
490 }
491 qdf_export_symbol(hal_rx_fst_detach);
492 
493 #ifndef WLAN_SUPPORT_RX_FISA
494 /**
495  * hal_flow_toeplitz_hash() - Calculate Toeplitz hash by using the cached key
496  *
497  * @hal_fst: FST Handle
498  * @flow: Flow Parameters
499  *
500  * Return: Success/Failure
501  */
502 uint32_t
503 hal_flow_toeplitz_hash(void *hal_fst, struct hal_rx_flow *flow)
504 {
505 	int i, j;
506 	uint32_t hash = 0;
507 	struct hal_rx_fst *fst = (struct hal_rx_fst *)hal_fst;
508 	uint32_t input[HAL_FST_HASH_KEY_SIZE_WORDS];
509 	uint8_t *tuple;
510 
511 	qdf_mem_zero(input, HAL_FST_HASH_KEY_SIZE_BYTES);
512 	*(uint32_t *)&input[0] = qdf_htonl(flow->tuple_info.src_ip_127_96);
513 	*(uint32_t *)&input[1] = qdf_htonl(flow->tuple_info.src_ip_95_64);
514 	*(uint32_t *)&input[2] = qdf_htonl(flow->tuple_info.src_ip_63_32);
515 	*(uint32_t *)&input[3] = qdf_htonl(flow->tuple_info.src_ip_31_0);
516 	*(uint32_t *)&input[4] = qdf_htonl(flow->tuple_info.dest_ip_127_96);
517 	*(uint32_t *)&input[5] = qdf_htonl(flow->tuple_info.dest_ip_95_64);
518 	*(uint32_t *)&input[6] = qdf_htonl(flow->tuple_info.dest_ip_63_32);
519 	*(uint32_t *)&input[7] = qdf_htonl(flow->tuple_info.dest_ip_31_0);
520 	*(uint32_t *)&input[8] = (flow->tuple_info.dest_port << 16) |
521 				 (flow->tuple_info.src_port);
522 	*(uint32_t *)&input[9] = flow->tuple_info.l4_protocol;
523 
524 	tuple = (uint8_t *)input;
525 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG,
526 			   tuple, sizeof(input));
527 	for (i = 0, j = HAL_FST_HASH_DATA_SIZE - 1;
528 	     i < HAL_FST_HASH_KEY_SIZE_BYTES && j >= 0; i++, j--) {
529 		hash ^= fst->key_cache[i][tuple[j]];
530 	}
531 
532 	QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_LOW,
533 		  "Hash value %u %u truncated hash %u\n", hash,
534 		  (hash >> 12), (hash >> 12) % (fst->max_entries));
535 
536 	hash >>= 12;
537 	hash &= (fst->max_entries - 1);
538 
539 	return hash;
540 }
541 #else
542 uint32_t
543 hal_flow_toeplitz_hash(void *hal_fst, struct hal_rx_flow *flow)
544 {
545 	return 0;
546 }
547 #endif
548 qdf_export_symbol(hal_flow_toeplitz_hash);
549 
550 /**
551  * hal_rx_get_hal_hash() - Retrieve hash index of a flow in the FST table
552  *
553  * @hal_fst: HAL Rx FST Handle
554  * @flow_hash: Flow hash computed from flow tuple
555  *
556  * Return: hash index truncated to the size of the hash table
557  */
558 uint32_t hal_rx_get_hal_hash(struct hal_rx_fst *hal_fst, uint32_t flow_hash)
559 {
560 	uint32_t trunc_hash = flow_hash;
561 
562 	/* Take care of hash wrap around scenario */
563 	if (flow_hash >= hal_fst->max_entries)
564 		trunc_hash &= hal_fst->hash_mask;
565 	return trunc_hash;
566 }
567 qdf_export_symbol(hal_rx_get_hal_hash);
568 
569 /**
570  * hal_rx_insert_flow_entry() - Add a flow into the FST table
571  * @hal_soc: HAL SOC handle
572  * @hal_fst: HAL Rx FST Handle
573  * @flow_hash: Flow hash computed from flow tuple
574  * @flow_tuple_info: Flow tuple used to compute the hash
575  * @flow_index: Hash index of the flow in the table when inserted successfully
576  *
577  * Return: Success if flow is inserted into the table, error otherwise
578  */
579 QDF_STATUS
580 hal_rx_insert_flow_entry(hal_soc_handle_t hal_soc,
581 			 struct hal_rx_fst *fst, uint32_t flow_hash,
582 			 void *flow_tuple_info, uint32_t *flow_idx)
583 {
584 	int i;
585 	void *hal_fse = NULL;
586 	uint32_t hal_hash = 0;
587 	struct hal_flow_tuple_info hal_tuple_info = { 0 };
588 
589 	for (i = 0; i < fst->max_skid_length; i++) {
590 		hal_hash = hal_rx_get_hal_hash(fst, (flow_hash + i));
591 
592 		hal_fse = hal_rx_flow_get_tuple_info(hal_soc, fst, hal_hash,
593 						     &hal_tuple_info);
594 		if (!hal_fse)
595 			break;
596 
597 		/* Find the matching flow entry in HW FST */
598 		if (!qdf_mem_cmp(&hal_tuple_info,
599 				 flow_tuple_info,
600 				 sizeof(struct hal_flow_tuple_info))) {
601 			dp_err("Duplicate flow entry in FST %u at skid %u ",
602 			       hal_hash, i);
603 			return QDF_STATUS_E_EXISTS;
604 		}
605 	}
606 	if (i == fst->max_skid_length) {
607 		dp_err("Max skid length reached for hash %u", flow_hash);
608 		return QDF_STATUS_E_RANGE;
609 	}
610 	*flow_idx = hal_hash;
611 	dp_info("flow_hash = %u, skid_entry = %d, flow_addr = %pK flow_idx = %d",
612 		flow_hash, i, hal_fse, *flow_idx);
613 
614 	return QDF_STATUS_SUCCESS;
615 }
616 qdf_export_symbol(hal_rx_insert_flow_entry);
617 
618 /**
619  * hal_rx_find_flow_from_tuple() - Find a flow in the FST table
620  *
621  * @fst: HAL Rx FST Handle
622  * @flow_hash: Flow hash computed from flow tuple
623  * @flow_tuple_info: Flow tuple used to compute the hash
624  * @flow_index: Hash index of the flow in the table when found
625  *
626  * Return: Success if matching flow is found in the table, error otherwise
627  */
628 QDF_STATUS
629 hal_rx_find_flow_from_tuple(hal_soc_handle_t hal_soc_hdl,
630 			    struct hal_rx_fst *fst, uint32_t flow_hash,
631 			    void *flow_tuple_info, uint32_t *flow_idx)
632 {
633 	int i;
634 	void *hal_fse = NULL;
635 	uint32_t hal_hash = 0;
636 	struct hal_flow_tuple_info hal_tuple_info = { 0 };
637 
638 	for (i = 0; i < fst->max_skid_length; i++) {
639 		hal_hash = hal_rx_get_hal_hash(fst, (flow_hash + i));
640 
641 		hal_fse = hal_rx_flow_get_tuple_info(hal_soc_hdl, fst, hal_hash,
642 						     &hal_tuple_info);
643 		if (!hal_fse)
644 			continue;
645 
646 		/* Find the matching flow entry in HW FST */
647 		if (!qdf_mem_cmp(&hal_tuple_info,
648 				 flow_tuple_info,
649 				 sizeof(struct hal_flow_tuple_info))) {
650 			break;
651 		}
652 	}
653 
654 	if (i == fst->max_skid_length) {
655 		dp_err("Max skid length reached for hash %u", flow_hash);
656 		return QDF_STATUS_E_RANGE;
657 	}
658 
659 	*flow_idx = hal_hash;
660 	dp_info("flow_hash = %u, skid_entry = %d, flow_addr = %pK flow_idx = %d",
661 		flow_hash, i, hal_fse, *flow_idx);
662 
663 	return QDF_STATUS_SUCCESS;
664 }
665 qdf_export_symbol(hal_rx_find_flow_from_tuple);
666