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