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