xref: /wlan-dirver/qca-wifi-host-cmn/qdf/linux/src/qdf_nbuf_frag.c (revision 97f44cd39e4ff816eaa1710279d28cf6b9e65ad9)
1 /*
2  * Copyright (c) 2020 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_nbuf_frag.c
21  * QCA driver framework(QDF) network nbuf frag management APIs
22  */
23 
24 #include <qdf_atomic.h>
25 #include <qdf_list.h>
26 #include <qdf_debugfs.h>
27 #include <qdf_module.h>
28 #include <qdf_nbuf_frag.h>
29 #include <qdf_trace.h>
30 #include "qdf_str.h"
31 
32 #ifdef QDF_NBUF_FRAG_GLOBAL_COUNT
33 #define FRAG_DEBUGFS_NAME    "frag_counters"
34 static qdf_atomic_t frag_count;
35 #endif
36 
37 #if defined(NBUF_FRAG_MEMORY_DEBUG) || defined(QDF_NBUF_FRAG_GLOBAL_COUNT)
38 static bool is_initial_mem_debug_disabled;
39 #endif
40 
41 #ifdef QDF_NBUF_FRAG_GLOBAL_COUNT
42 
43 uint32_t __qdf_frag_count_get(void)
44 {
45 	return qdf_atomic_read(&frag_count);
46 }
47 
48 qdf_export_symbol(__qdf_frag_count_get);
49 
50 void __qdf_frag_count_inc(uint32_t value)
51 {
52 	if (qdf_likely(is_initial_mem_debug_disabled))
53 		return;
54 
55 	qdf_atomic_add(value, &frag_count);
56 }
57 
58 qdf_export_symbol(__qdf_frag_count_inc);
59 
60 void __qdf_frag_count_dec(uint32_t value)
61 {
62 	if (qdf_likely(is_initial_mem_debug_disabled))
63 		return;
64 
65 	qdf_atomic_sub(value, &frag_count);
66 }
67 
68 qdf_export_symbol(__qdf_frag_count_dec);
69 
70 void __qdf_frag_mod_init(void)
71 {
72 	is_initial_mem_debug_disabled = qdf_mem_debug_config_get();
73 	qdf_atomic_init(&frag_count);
74 	qdf_debugfs_create_atomic(FRAG_DEBUGFS_NAME, S_IRUSR, NULL,
75 				  &frag_count);
76 }
77 
78 void __qdf_frag_mod_exit(void)
79 {
80 }
81 #endif /* QDF_NBUF_FRAG_GLOBAL_COUNT */
82 
83 #ifdef NBUF_FRAG_MEMORY_DEBUG
84 
85 #define QDF_FRAG_TRACK_MAX_SIZE    1024
86 
87 /**
88  * struct qdf_frag_track_node_t - Network frag tracking node structure
89  * @hnode: list_head for next and prev pointers
90  * @p_frag: Pointer to frag
91  * @alloc_func_name: Function where frag is allocated
92  * @alloc_func_line: Allocation function line no.
93  * @refcount: No. of refereces to the frag
94  * @last_func_name: Function where frag recently accessed
95  * @last_func_line_num: Line number of last function
96  *
97  **/
98 struct qdf_frag_track_node_t {
99 	qdf_list_node_t hnode;
100 	qdf_frag_t p_frag;
101 	char alloc_func_name[QDF_MEM_FUNC_NAME_SIZE];
102 	uint32_t alloc_func_line;
103 	uint8_t refcount;
104 	char last_func_name[QDF_MEM_FUNC_NAME_SIZE];
105 	uint32_t last_func_line;
106 };
107 
108 /**
109  * struct qdf_frag_tracking_list_t - Frag node tracking list
110  * @track_list: qdf_list_t for maintaining the list
111  * @list_lock: Lock over the list
112  *
113  */
114 typedef struct qdf_frag_tracking_list_t {
115 	qdf_list_t track_list;
116 	qdf_spinlock_t list_lock;
117 } qdf_frag_tracking_list;
118 
119 typedef struct qdf_frag_track_node_t QDF_FRAG_TRACK;
120 
121 /**
122  * Array of tracking list for maintaining
123  * allocated debug frag nodes as per the calculated
124  * hash value.
125  */
126 static qdf_frag_tracking_list gp_qdf_frag_track_tbl[QDF_FRAG_TRACK_MAX_SIZE];
127 
128 static struct kmem_cache *frag_tracking_cache;
129 
130 /* Tracking list for maintaining the free debug frag nodes */
131 static qdf_frag_tracking_list qdf_frag_track_free_list;
132 
133 /**
134  * Parameters for statistics
135  * qdf_frag_track_free_list_count: No. of free nodes
136  * qdf_frag_track_used_list_count : No. of nodes used
137  * qdf_frag_track_max_used : Max no. of nodes used during execution
138  * qdf_frag_track_max_free : Max free nodes observed during execution
139  * qdf_frag_track_max_allocated: Max no. of allocated nodes
140  */
141 static uint32_t qdf_frag_track_free_list_count;
142 static uint32_t qdf_frag_track_used_list_count;
143 static uint32_t qdf_frag_track_max_used;
144 static uint32_t qdf_frag_track_max_free;
145 static uint32_t qdf_frag_track_max_allocated;
146 
147 /**
148  * qdf_frag_update_max_used() - Update qdf_frag_track_max_used tracking variable
149  *
150  * Tracks the max number of frags that the wlan driver was tracking at any one
151  * time
152  *
153  * Return: none
154  **/
155 static inline void qdf_frag_update_max_used(void)
156 {
157 	int sum;
158 
159 	/* Update max_used if it is less than used list count */
160 	if (qdf_frag_track_max_used < qdf_frag_track_used_list_count)
161 		qdf_frag_track_max_used = qdf_frag_track_used_list_count;
162 
163 	/* Calculate no. of allocated nodes */
164 	sum = qdf_frag_track_used_list_count + qdf_frag_track_free_list_count;
165 
166 	/* Update max allocated if less then no. of allocated nodes */
167 	if (qdf_frag_track_max_allocated < sum)
168 		qdf_frag_track_max_allocated = sum;
169 }
170 
171 /**
172  * qdf_frag_update_max_free() - Update qdf_frag_track_max_free
173  *
174  * Tracks the max number tracking buffers kept in the freelist.
175  *
176  * Return: none
177  */
178 static inline void qdf_frag_update_max_free(void)
179 {
180 	if (qdf_frag_track_max_free < qdf_frag_track_free_list_count)
181 		qdf_frag_track_max_free = qdf_frag_track_free_list_count;
182 }
183 
184 /**
185  * qdf_frag_track_alloc() - Allocate a cookie to track frags allocated by wlan
186  *
187  * This function pulls from freelist if possible,otherwise uses kmem_cache_alloc
188  * This function also adds fexibility to adjust the allocation and freelist
189  * schemes.
190  *
191  * Return: Pointer to an unused QDF_FRAG_TRACK structure which may not be zeroed
192  */
193 static QDF_FRAG_TRACK *qdf_frag_track_alloc(void)
194 {
195 	int flags = GFP_KERNEL;
196 	QDF_FRAG_TRACK *frag_track_node = NULL;
197 	qdf_list_node_t *temp_list_node;
198 
199 	qdf_spin_lock_irqsave(&qdf_frag_track_free_list.list_lock);
200 	qdf_frag_track_used_list_count++;
201 
202 	if (!qdf_list_empty(&qdf_frag_track_free_list.track_list)) {
203 		qdf_list_remove_front(&qdf_frag_track_free_list.track_list,
204 				      &temp_list_node);
205 		frag_track_node = qdf_container_of(temp_list_node,
206 						   struct qdf_frag_track_node_t,
207 						   hnode);
208 		qdf_frag_track_free_list_count--;
209 	}
210 
211 	qdf_frag_update_max_used();
212 	qdf_spin_unlock_irqrestore(&qdf_frag_track_free_list.list_lock);
213 
214 	if (frag_track_node)
215 		return frag_track_node;
216 
217 	if (in_interrupt() || irqs_disabled() || in_atomic())
218 		flags = GFP_ATOMIC;
219 
220 	frag_track_node = kmem_cache_alloc(frag_tracking_cache, flags);
221 	if (frag_track_node)
222 		qdf_init_list_head(&frag_track_node->hnode);
223 
224 	return frag_track_node;
225 }
226 
227 /* FREEQ_POOLSIZE initial and minimum desired freelist poolsize */
228 #define FREEQ_POOLSIZE    2048
229 
230 /**
231  * qdf_frag_track_free() - Free the frag tracking cookie.
232  * @frag_track_node : Debug frag node address
233  *
234  * Matches calls to qdf_frag_track_alloc.
235  * Either frees the tracking cookie to kernel or an internal
236  * freelist based on the size of the freelist.
237  *
238  * Return: none
239  */
240 static void qdf_frag_track_free(QDF_FRAG_TRACK *frag_track_node)
241 {
242 	if (!frag_track_node)
243 		return;
244 
245 	/*
246 	 * Try to shrink the freelist if free_list_count > than FREEQ_POOLSIZE
247 	 * only shrink the freelist if it is bigger than twice the number of
248 	 * frags in use. Otherwise add the frag debug track node to the front
249 	 * of qdf_frag_track_free_list.
250 	 */
251 
252 	qdf_spin_lock_irqsave(&qdf_frag_track_free_list.list_lock);
253 
254 	qdf_frag_track_used_list_count--;
255 	if (qdf_frag_track_free_list_count > FREEQ_POOLSIZE &&
256 	    (qdf_frag_track_free_list_count >
257 	    qdf_frag_track_used_list_count << 1)) {
258 		kmem_cache_free(frag_tracking_cache, frag_track_node);
259 	} else {
260 		qdf_list_insert_front(&qdf_frag_track_free_list.track_list,
261 				      &frag_track_node->hnode);
262 		qdf_frag_track_free_list_count++;
263 	}
264 	qdf_frag_update_max_free();
265 	qdf_spin_unlock_irqrestore(&qdf_frag_track_free_list.list_lock);
266 }
267 
268 /**
269  * qdf_frag_track_prefill() - Prefill the frag tracking cookie freelist
270  *
271  * Return: none
272  */
273 static void qdf_frag_track_prefill(void)
274 {
275 	int index;
276 	QDF_FRAG_TRACK *curr_node, *next_node;
277 	qdf_list_t temp_list;
278 
279 	qdf_list_create(&temp_list, 0);
280 
281 	/* Prepopulate the freelist */
282 	for (index = 0; index < FREEQ_POOLSIZE; index++) {
283 		curr_node = qdf_frag_track_alloc();
284 		if (!curr_node)
285 			continue;
286 		qdf_list_insert_front(&temp_list, &curr_node->hnode);
287 	}
288 
289 	curr_node = NULL;
290 	next_node = NULL;
291 
292 	qdf_list_for_each_del(&temp_list, curr_node, next_node, hnode) {
293 		qdf_list_remove_node(&temp_list, &curr_node->hnode);
294 		qdf_frag_track_free(curr_node);
295 	}
296 
297 	/* prefilled buffers should not count as used */
298 	qdf_frag_track_max_used = 0;
299 
300 	qdf_list_destroy(&temp_list);
301 }
302 
303 /**
304  * qdf_frag_track_memory_manager_create() - Manager for frag tracking cookies
305  *
306  * This initializes the memory manager for the frag tracking cookies. Because
307  * these cookies are all the same size and only used in this feature, we can
308  * use a kmem_cache to provide tracking as well as to speed up allocations.
309  * To avoid the overhead of allocating and freeing the buffers (including SLUB
310  * features) a freelist is prepopulated here.
311  *
312  * Return: none
313  */
314 static void qdf_frag_track_memory_manager_create(void)
315 {
316 	qdf_spinlock_create(&qdf_frag_track_free_list.list_lock);
317 	qdf_list_create(&qdf_frag_track_free_list.track_list, 0);
318 	frag_tracking_cache = kmem_cache_create("qdf_frag_tracking_cache",
319 						sizeof(QDF_FRAG_TRACK),
320 						0, 0, NULL);
321 
322 	qdf_frag_track_prefill();
323 }
324 
325 /**
326  * qdf_frag_track_memory_manager_destroy() - Manager for frag tracking cookies
327  *
328  * Empty the freelist and print out usage statistics when it is no longer
329  * needed. Also the kmem_cache should be destroyed here so that it can warn if
330  * any frag tracking cookies were leaked.
331  *
332  * Return: none
333  */
334 static void qdf_frag_track_memory_manager_destroy(void)
335 {
336 	QDF_FRAG_TRACK *curr_node, *next_node;
337 
338 	curr_node = next_node = NULL;
339 
340 	qdf_spin_lock_irqsave(&qdf_frag_track_free_list.list_lock);
341 
342 	if (qdf_frag_track_max_used > FREEQ_POOLSIZE * 4)
343 		qdf_info("Unexpectedly large max_used count %d",
344 			  qdf_frag_track_max_used);
345 
346 	if (qdf_frag_track_max_used < qdf_frag_track_max_allocated)
347 		qdf_info("%d Unused trackers were allocated",
348 			  qdf_frag_track_max_allocated -
349 			  qdf_frag_track_max_used);
350 
351 	if (qdf_frag_track_free_list_count > FREEQ_POOLSIZE &&
352 	    qdf_frag_track_free_list_count > 3 * qdf_frag_track_max_used / 4)
353 		qdf_info("Check freelist shrinking functionality");
354 
355 	qdf_info("%d Residual freelist size", qdf_frag_track_free_list_count);
356 
357 	qdf_info("%d Max freelist size observed", qdf_frag_track_max_free);
358 
359 	qdf_info("%d Max buffers used observed", qdf_frag_track_max_used);
360 
361 	qdf_info("%d Max buffers allocated observed",
362 		  qdf_frag_track_max_allocated);
363 
364 	qdf_list_for_each_del(&qdf_frag_track_free_list.track_list,
365 			      curr_node, next_node, hnode) {
366 		qdf_list_remove_node(&qdf_frag_track_free_list.track_list,
367 				     &curr_node->hnode);
368 		kmem_cache_free(frag_tracking_cache, curr_node);
369 		qdf_frag_track_free_list_count--;
370 	}
371 
372 	if (qdf_frag_track_free_list_count != 0)
373 		qdf_info("%d Unfreed tracking memory lost in freelist",
374 			 qdf_frag_track_free_list_count);
375 
376 	if (qdf_frag_track_used_list_count != 0)
377 		qdf_info("%d Unfreed tracking memory still in use",
378 			 qdf_frag_track_used_list_count);
379 
380 	qdf_spin_unlock_irqrestore(&qdf_frag_track_free_list.list_lock);
381 	kmem_cache_destroy(frag_tracking_cache);
382 
383 	qdf_list_destroy(&qdf_frag_track_free_list.track_list);
384 	qdf_spinlock_destroy(&qdf_frag_track_free_list.list_lock);
385 }
386 
387 /**
388  * qdf_frag_debug_init() - Initialize network frag debug functionality
389  *
390  * QDF frag buffer debug feature tracks all frags allocated by WLAN driver
391  * in a hash table and when driver is unloaded it reports about leaked frags.
392  *
393  * Return: none
394  */
395 void qdf_frag_debug_init(void)
396 {
397 	uint32_t index;
398 
399 	is_initial_mem_debug_disabled = qdf_mem_debug_config_get();
400 
401 	if (is_initial_mem_debug_disabled)
402 		return;
403 
404 	qdf_frag_track_memory_manager_create();
405 
406 	for (index = 0; index < QDF_FRAG_TRACK_MAX_SIZE; index++) {
407 		qdf_list_create(&gp_qdf_frag_track_tbl[index].track_list, 0);
408 		qdf_spinlock_create(&gp_qdf_frag_track_tbl[index].list_lock);
409 	}
410 }
411 
412 qdf_export_symbol(qdf_frag_debug_init);
413 
414 /**
415  * qdf_frag_buf_debug_exit() - Exit network frag debug functionality
416  *
417  * Exit network frag tracking debug functionality and log frag memory leaks
418  *
419  * Return: none
420  */
421 void qdf_frag_debug_exit(void)
422 {
423 	uint32_t index;
424 	QDF_FRAG_TRACK *p_node;
425 	QDF_FRAG_TRACK *p_prev;
426 
427 	if (is_initial_mem_debug_disabled)
428 		return;
429 
430 	for (index = 0; index < QDF_FRAG_TRACK_MAX_SIZE; index++) {
431 		qdf_spin_lock_irqsave(&gp_qdf_frag_track_tbl[index].list_lock);
432 		qdf_list_for_each_del(&gp_qdf_frag_track_tbl[index].track_list,
433 				      p_prev, p_node, hnode) {
434 			qdf_list_remove_node(
435 				&gp_qdf_frag_track_tbl[index].track_list,
436 				&p_prev->hnode);
437 			qdf_info("******Frag Memory Leak******");
438 			qdf_info("@Frag Address: %pK", p_prev->p_frag);
439 			qdf_info("@Refcount: %u", p_prev->refcount);
440 			qdf_info("@Alloc Func Name: %s, @Alloc Func Line: %d",
441 				 p_prev->alloc_func_name,
442 				 p_prev->alloc_func_line);
443 			qdf_info("@Last Func Name: %s, @Last Func Line: %d",
444 				 p_prev->last_func_name,
445 				 p_prev->last_func_line);
446 			qdf_info("****************************");
447 
448 			qdf_frag_track_free(p_prev);
449 		}
450 		qdf_list_destroy(&gp_qdf_frag_track_tbl[index].track_list);
451 		qdf_spin_unlock_irqrestore(
452 				&gp_qdf_frag_track_tbl[index].list_lock);
453 		qdf_spinlock_destroy(&gp_qdf_frag_track_tbl[index].list_lock);
454 	}
455 
456 	qdf_frag_track_memory_manager_destroy();
457 }
458 
459 qdf_export_symbol(qdf_frag_debug_exit);
460 
461 /**
462  * qdf_frag_debug_hash() - Hash network frag pointer
463  * @p_frag: Frag address
464  *
465  * Return: hash value
466  */
467 static uint32_t qdf_frag_debug_hash(qdf_frag_t p_frag)
468 {
469 	uint32_t index;
470 
471 	index = (uint32_t)(((uintptr_t)p_frag) >> 4);
472 	index += (uint32_t)(((uintptr_t)p_frag) >> 14);
473 	index &= (QDF_FRAG_TRACK_MAX_SIZE - 1);
474 
475 	return index;
476 }
477 
478 /**
479  * qdf_frag_debug_look_up() - Look up network frag in debug hash table
480  * @p_frag: Frag address
481  *
482  * Return: If frag is found in hash table then return pointer to network frag
483  *	else return NULL
484  */
485 static QDF_FRAG_TRACK *qdf_frag_debug_look_up(qdf_frag_t p_frag)
486 {
487 	uint32_t index;
488 	QDF_FRAG_TRACK *p_node;
489 
490 	index = qdf_frag_debug_hash(p_frag);
491 
492 	qdf_list_for_each(&gp_qdf_frag_track_tbl[index].track_list, p_node,
493 			  hnode) {
494 		if (p_node->p_frag == p_frag)
495 			return p_node;
496 	}
497 
498 	return NULL;
499 }
500 
501 /**
502  * __qdf_frag_debug_add_node()- Add frag node to debug tracker
503  * @fragp: Frag Pointer
504  * @idx: Index
505  * @func_name: Caller function name
506  * @line_num: Caller function line no.
507  *
508  * Return: Allocated frag tracker node address
509  */
510 static QDF_FRAG_TRACK *__qdf_frag_debug_add_node(qdf_frag_t fragp,
511 						 uint32_t idx,
512 						 const char *func_name,
513 						 uint32_t line_num)
514 {
515 	QDF_FRAG_TRACK *p_node;
516 
517 	p_node = qdf_frag_track_alloc();
518 
519 	if (p_node) {
520 		p_node->p_frag = fragp;
521 		qdf_str_lcopy(p_node->alloc_func_name, func_name,
522 			      QDF_MEM_FUNC_NAME_SIZE);
523 		p_node->alloc_func_line = line_num;
524 		p_node->refcount = QDF_NBUF_FRAG_DEBUG_COUNT_ZERO;
525 
526 		qdf_str_lcopy(p_node->last_func_name, func_name,
527 			      QDF_MEM_FUNC_NAME_SIZE);
528 		p_node->last_func_line = line_num;
529 
530 		qdf_list_insert_front(&gp_qdf_frag_track_tbl[idx].track_list,
531 				      &p_node->hnode);
532 	}
533 	return p_node;
534 }
535 
536 /**
537  * __qdf_frag_debug_delete_node()- Remove frag node from debug tracker
538  * @p_node: Frag node address in debug tracker
539  * @idx: Index
540  *
541  * Return: none
542  */
543 static void __qdf_frag_debug_delete_node(QDF_FRAG_TRACK *p_node, uint32_t idx)
544 {
545 	if (idx < QDF_FRAG_TRACK_MAX_SIZE) {
546 		qdf_list_remove_node(&gp_qdf_frag_track_tbl[idx].track_list,
547 				     &p_node->hnode);
548 		qdf_frag_track_free(p_node);
549 	} else {
550 		qdf_info("Index value exceeds %d for delete node operation",
551 			  QDF_FRAG_TRACK_MAX_SIZE);
552 	}
553 }
554 
555 void qdf_frag_debug_add_node(qdf_frag_t fragp, const char *func_name,
556 			     uint32_t line_num)
557 {
558 	uint32_t index;
559 	QDF_FRAG_TRACK *p_node;
560 
561 	if (is_initial_mem_debug_disabled)
562 		return;
563 
564 	index = qdf_frag_debug_hash(fragp);
565 
566 	qdf_spin_lock_irqsave(&gp_qdf_frag_track_tbl[index].list_lock);
567 
568 	p_node = qdf_frag_debug_look_up(fragp);
569 
570 	if (p_node) {
571 		qdf_info("Double addition of frag %pK to debug tracker!!",
572 			 fragp);
573 		qdf_info("Already added from %s %d Current addition from %s %d",
574 			  p_node->alloc_func_name,
575 			  p_node->alloc_func_line, func_name, line_num);
576 	} else {
577 		p_node = __qdf_frag_debug_add_node(fragp, index, func_name,
578 						   line_num);
579 		if (!p_node)
580 			qdf_info("Memory allocation failed !! "
581 				 "Add node oprt failed for frag %pK from %s %d",
582 				 fragp, func_name, line_num);
583 	}
584 	qdf_spin_unlock_irqrestore(&gp_qdf_frag_track_tbl[index].list_lock);
585 }
586 
587 void qdf_frag_debug_refcount_inc(qdf_frag_t fragp, const char *func_name,
588 				 uint32_t line_num)
589 {
590 	uint32_t index;
591 	QDF_FRAG_TRACK *p_node;
592 
593 	if (is_initial_mem_debug_disabled)
594 		return;
595 
596 	index = qdf_frag_debug_hash(fragp);
597 
598 	qdf_spin_lock_irqsave(&gp_qdf_frag_track_tbl[index].list_lock);
599 
600 	p_node = qdf_frag_debug_look_up(fragp);
601 
602 	if (p_node) {
603 		(p_node->refcount)++;
604 
605 		qdf_str_lcopy(p_node->last_func_name, func_name,
606 			      QDF_MEM_FUNC_NAME_SIZE);
607 		p_node->last_func_line = line_num;
608 	} else {
609 		p_node = __qdf_frag_debug_add_node(fragp, index, func_name,
610 						   line_num);
611 		if (p_node)
612 			p_node->refcount = QDF_NBUF_FRAG_DEBUG_COUNT_ONE;
613 		else
614 			qdf_info("Memory allocation failed !! "
615 				 "Refcount inc failed for frag %pK from %s %d",
616 				 fragp, func_name, line_num);
617 	}
618 	qdf_spin_unlock_irqrestore(&gp_qdf_frag_track_tbl[index].list_lock);
619 }
620 
621 void qdf_frag_debug_refcount_dec(qdf_frag_t fragp, const char *func_name,
622 				 uint32_t line_num)
623 {
624 	uint32_t index;
625 	QDF_FRAG_TRACK *p_node;
626 
627 	if (is_initial_mem_debug_disabled)
628 		return;
629 
630 	index = qdf_frag_debug_hash(fragp);
631 
632 	qdf_spin_lock_irqsave(&gp_qdf_frag_track_tbl[index].list_lock);
633 
634 	p_node = qdf_frag_debug_look_up(fragp);
635 
636 	if (p_node) {
637 		if (!(p_node->refcount)) {
638 			qdf_info("Refcount dec oprt for frag %pK not permitted "
639 				 "as refcount=0", fragp);
640 			goto done;
641 		}
642 		(p_node->refcount)--;
643 
644 		if (!(p_node->refcount)) {
645 			/* Remove frag debug node when refcount reaches 0 */
646 			__qdf_frag_debug_delete_node(p_node, index);
647 		} else {
648 			qdf_str_lcopy(p_node->last_func_name, func_name,
649 				      QDF_MEM_FUNC_NAME_SIZE);
650 			p_node->last_func_line = line_num;
651 		}
652 	} else {
653 		qdf_info("Unallocated frag !! Could not track frag %pK", fragp);
654 		qdf_info("Refcount dec oprt failed for frag %pK from %s %d",
655 			 fragp, func_name, line_num);
656 	}
657 done:
658 	qdf_spin_unlock_irqrestore(&gp_qdf_frag_track_tbl[index].list_lock);
659 }
660 
661 void qdf_frag_debug_delete_node(qdf_frag_t fragp, const char *func_name,
662 				uint32_t line_num)
663 {
664 	uint32_t index;
665 	QDF_FRAG_TRACK *p_node;
666 
667 	if (is_initial_mem_debug_disabled)
668 		return;
669 
670 	index = qdf_frag_debug_hash(fragp);
671 
672 	qdf_spin_lock_irqsave(&gp_qdf_frag_track_tbl[index].list_lock);
673 
674 	p_node = qdf_frag_debug_look_up(fragp);
675 
676 	if (p_node) {
677 		if (p_node->refcount) {
678 			qdf_info("Frag %pK has refcount %d", fragp,
679 				 p_node->refcount);
680 			qdf_info("Delete oprt failed for frag %pK from %s %d",
681 				 fragp, func_name, line_num);
682 		} else {
683 			/* Remove node from tracker as refcount=0 */
684 			__qdf_frag_debug_delete_node(p_node, index);
685 		}
686 	} else {
687 		qdf_info("Unallocated frag !! Double free of frag %pK", fragp);
688 		qdf_info("Could not track frag %pK for delete oprt from %s %d",
689 			 fragp, func_name, line_num);
690 	}
691 
692 	qdf_spin_unlock_irqrestore(&gp_qdf_frag_track_tbl[index].list_lock);
693 }
694 
695 void qdf_frag_debug_update_addr(qdf_frag_t p_fragp, qdf_frag_t n_fragp,
696 				const char *func_name, uint32_t line_num)
697 {
698 	uint32_t prev_index, new_index;
699 	QDF_FRAG_TRACK *p_node;
700 
701 	if (is_initial_mem_debug_disabled)
702 		return;
703 
704 	prev_index = qdf_frag_debug_hash(p_fragp);
705 
706 	new_index = qdf_frag_debug_hash(n_fragp);
707 
708 	qdf_spin_lock_irqsave(&gp_qdf_frag_track_tbl[prev_index].list_lock);
709 
710 	p_node = qdf_frag_debug_look_up(p_fragp);
711 
712 	if (!p_node) {
713 		qdf_info("Unallocated frag !! Could not track frag %pK",
714 			 p_fragp);
715 		qdf_info("Update address oprt failed for frag %pK from %s %d",
716 			 p_fragp, func_name, line_num);
717 		qdf_spin_unlock_irqrestore(
718 				&gp_qdf_frag_track_tbl[prev_index].list_lock);
719 	} else {
720 		/* Update frag address */
721 		p_node->p_frag = n_fragp;
722 
723 		qdf_str_lcopy(p_node->last_func_name, func_name,
724 			      QDF_MEM_FUNC_NAME_SIZE);
725 		p_node->last_func_line = line_num;
726 
727 		if (prev_index != new_index) {
728 			qdf_list_remove_node(
729 				&gp_qdf_frag_track_tbl[prev_index].track_list,
730 				&p_node->hnode);
731 
732 			qdf_spin_unlock_irqrestore(
733 				&gp_qdf_frag_track_tbl[prev_index].list_lock);
734 
735 			qdf_spin_lock_irqsave(
736 				&gp_qdf_frag_track_tbl[new_index].list_lock);
737 
738 			qdf_list_insert_front(
739 				&gp_qdf_frag_track_tbl[new_index].track_list,
740 				&p_node->hnode);
741 
742 			qdf_spin_unlock_irqrestore(
743 				&gp_qdf_frag_track_tbl[new_index].list_lock);
744 		} else {
745 			qdf_spin_unlock_irqrestore(
746 				&gp_qdf_frag_track_tbl[prev_index].list_lock);
747 		}
748 	}
749 }
750 
751 qdf_frag_t qdf_frag_alloc_debug(unsigned int frag_size, const char *func_name,
752 				uint32_t line_num)
753 {
754 	qdf_frag_t p_frag;
755 
756 	if (is_initial_mem_debug_disabled)
757 		return __qdf_frag_alloc(frag_size);
758 
759 	p_frag =  __qdf_frag_alloc(frag_size);
760 
761 	/* Store frag in QDF Frag Tracking Table */
762 	if (qdf_likely(p_frag))
763 		qdf_frag_debug_add_node(p_frag, func_name, line_num);
764 
765 	return p_frag;
766 }
767 
768 qdf_export_symbol(qdf_frag_alloc_debug);
769 
770 void qdf_frag_free_debug(qdf_frag_t vaddr, const char *func_name,
771 			 uint32_t line_num)
772 {
773 	if (qdf_unlikely(!vaddr))
774 		return;
775 
776 	if (is_initial_mem_debug_disabled)
777 		goto free_frag;
778 
779 	qdf_frag_debug_delete_node(vaddr, func_name, line_num);
780 free_frag:
781 	__qdf_frag_free(vaddr);
782 }
783 
784 qdf_export_symbol(qdf_frag_free_debug);
785 
786 #endif /* NBUF_FRAG_MEMORY_DEBUG */
787 
788 #if defined(HIF_PCI)
789 QDF_STATUS __qdf_mem_map_page(qdf_device_t osdev, __qdf_frag_t buf,
790 			      qdf_dma_dir_t dir, size_t nbytes,
791 			      qdf_dma_addr_t *phy_addr)
792 {
793 	struct page *page;
794 	unsigned long offset;
795 
796 	page = virt_to_head_page(buf);
797 	offset = buf - page_address(page);
798 	*phy_addr = dma_map_page(osdev->dev, page, offset, nbytes,
799 				 __qdf_dma_dir_to_os(dir));
800 
801 	return dma_mapping_error(osdev->dev, *phy_addr) ?
802 		QDF_STATUS_E_FAILURE : QDF_STATUS_SUCCESS;
803 }
804 #else
805 QDF_STATUS __qdf_mem_map_page(qdf_device_t osdev, __qdf_frag_t buf,
806 			      qdf_dma_dir_t dir, size_t nbytes,
807 			      qdf_dma_addr_t *phy_addr)
808 {
809 	return QDF_STATUS_SUCCESS;
810 }
811 #endif
812 
813 qdf_export_symbol(__qdf_mem_map_page);
814 
815 #if defined(HIF_PCI)
816 void __qdf_mem_unmap_page(qdf_device_t osdev, qdf_dma_addr_t paddr,
817 			  size_t nbytes, qdf_dma_dir_t dir)
818 {
819 	dma_unmap_page(osdev->dev, paddr, nbytes,
820 		       __qdf_dma_dir_to_os(dir));
821 }
822 #else
823 void __qdf_mem_unmap_page(qdf_device_t osdev, qdf_dma_addr_t paddr,
824 			  size_t nbytes, qdf_dma_dir_t dir)
825 {
826 }
827 #endif
828 
829 qdf_export_symbol(__qdf_mem_unmap_page);
830