xref: /wlan-dirver/qca-wifi-host-cmn/qdf/src/qdf_tracker.c (revision 2f4b444fb7e689b83a4ab0e7b3b38f0bf4def8e0)
1 /*
2  * Copyright (c) 2019, 2021 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 #include "qdf_debug_domain.h"
20 #include "qdf_lock.h"
21 #include "qdf_mem.h"
22 #include "qdf_module.h"
23 #include "qdf_ptr_hash.h"
24 #include "qdf_status.h"
25 #include "qdf_str.h"
26 #include "qdf_tracker.h"
27 #include "qdf_types.h"
28 
29 struct qdf_tracker_node {
30 	struct qdf_ptr_hash_entry entry;
31 	enum qdf_debug_domain domain;
32 	char func[QDF_TRACKER_FUNC_SIZE];
33 	uint32_t line;
34 };
35 
36 void qdf_tracker_init(struct qdf_tracker *tracker)
37 {
38 	qdf_spinlock_create(&tracker->lock);
39 	qdf_ptr_hash_init(tracker->ht);
40 }
41 qdf_export_symbol(qdf_tracker_init);
42 
43 void qdf_tracker_deinit(struct qdf_tracker *tracker)
44 {
45 	qdf_tracker_check_for_leaks(tracker);
46 
47 	qdf_spin_lock_bh(&tracker->lock);
48 	QDF_BUG(qdf_ptr_hash_empty(tracker->ht));
49 	qdf_spin_unlock_bh(&tracker->lock);
50 
51 	qdf_ptr_hash_deinit(tracker->ht);
52 	qdf_spinlock_destroy(&tracker->lock);
53 }
54 qdf_export_symbol(qdf_tracker_deinit);
55 
56 static inline void qdf_tracker_print_break(void)
57 {
58 	qdf_nofl_alert("-----------------------------------------------------");
59 }
60 
61 static uint32_t qdf_tracker_leaks_print(struct qdf_tracker *tracker,
62 					enum qdf_debug_domain domain)
63 {
64 	struct qdf_ptr_hash_bucket *bucket;
65 	struct qdf_tracker_node *node;
66 	bool print_header = true;
67 	uint32_t count = 0;
68 
69 	qdf_ptr_hash_for_each(tracker->ht, bucket, node, entry) {
70 		if (node->domain != domain)
71 			continue;
72 
73 		if (print_header) {
74 			print_header = false;
75 			qdf_nofl_alert("%s detected in %s domain!",
76 				       tracker->leak_title,
77 				       qdf_debug_domain_name(domain));
78 			qdf_tracker_print_break();
79 		}
80 
81 		count++;
82 		qdf_nofl_alert("0x%lx @ %s:%u", node->entry.key,
83 			       node->func, node->line);
84 	}
85 
86 	if (count)
87 		qdf_tracker_print_break();
88 
89 	return count;
90 }
91 
92 void qdf_tracker_check_for_leaks(struct qdf_tracker *tracker)
93 {
94 	enum qdf_debug_domain domain = qdf_debug_domain_get();
95 	uint32_t leaks;
96 
97 	qdf_spin_lock_bh(&tracker->lock);
98 	leaks = qdf_tracker_leaks_print(tracker, domain);
99 	if (leaks)
100 		QDF_DEBUG_PANIC("%u fatal %s detected in %s domain!",
101 				leaks, tracker->leak_title,
102 				qdf_debug_domain_name(domain));
103 	qdf_spin_unlock_bh(&tracker->lock);
104 }
105 qdf_export_symbol(qdf_tracker_check_for_leaks);
106 
107 QDF_STATUS qdf_tracker_track(struct qdf_tracker *tracker, void *ptr,
108 			     const char *func, uint32_t line)
109 {
110 	struct qdf_tracker_node *node;
111 
112 	QDF_BUG(ptr);
113 	if (!ptr)
114 		return QDF_STATUS_E_INVAL;
115 
116 	qdf_spin_lock_bh(&tracker->lock);
117 	node = qdf_ptr_hash_get(tracker->ht, ptr, node, entry);
118 	if (node)
119 		QDF_DEBUG_PANIC("Double %s (via %s:%u); last %s from %s:%u",
120 				tracker->track_title, func, line,
121 				tracker->track_title, node->func, node->line);
122 	qdf_spin_unlock_bh(&tracker->lock);
123 
124 	if (node)
125 		return QDF_STATUS_E_ALREADY;
126 
127 	node = qdf_mem_malloc(sizeof(*node));
128 	if (!node)
129 		return QDF_STATUS_E_NOMEM;
130 
131 	node->domain = qdf_debug_domain_get();
132 	qdf_str_lcopy(node->func, func, QDF_TRACKER_FUNC_SIZE);
133 	node->line = line;
134 
135 	qdf_spin_lock_bh(&tracker->lock);
136 	qdf_ptr_hash_add(tracker->ht, ptr, node, entry);
137 	qdf_spin_unlock_bh(&tracker->lock);
138 
139 	return QDF_STATUS_SUCCESS;
140 }
141 qdf_export_symbol(qdf_tracker_track);
142 
143 void qdf_tracker_untrack(struct qdf_tracker *tracker, void *ptr,
144 			 const char *func, uint32_t line)
145 {
146 	enum qdf_debug_domain domain = qdf_debug_domain_get();
147 	struct qdf_tracker_node *node;
148 
149 	QDF_BUG(ptr);
150 	if (!ptr)
151 		return;
152 
153 	qdf_spin_lock_bh(&tracker->lock);
154 	node = qdf_ptr_hash_remove(tracker->ht, ptr, node, entry);
155 	if (!node)
156 		QDF_DEBUG_PANIC("Double %s (via %s:%u)",
157 				tracker->untrack_title, func, line);
158 	else if (node->domain != domain)
159 		QDF_DEBUG_PANIC("%s domain mismatch; tracked:%s, %s:%u; untracked:%s , %s:%u",
160 				tracker->untrack_title,
161 				qdf_debug_domain_name(node->domain),
162 				node->func, node->line,
163 				qdf_debug_domain_name(domain),
164 				func, line);
165 	qdf_spin_unlock_bh(&tracker->lock);
166 
167 	if (node)
168 		qdf_mem_free(node);
169 }
170 qdf_export_symbol(qdf_tracker_untrack);
171 
172 bool qdf_tracker_lookup(struct qdf_tracker *tracker, void *ptr,
173 			char (*out_func)[QDF_TRACKER_FUNC_SIZE],
174 			uint32_t *out_line)
175 {
176 	struct qdf_tracker_node *node;
177 
178 	qdf_spin_lock_bh(&tracker->lock);
179 	node = qdf_ptr_hash_get(tracker->ht, ptr, node, entry);
180 	if (node) {
181 		qdf_str_lcopy((char *)out_func, node->func,
182 			      QDF_TRACKER_FUNC_SIZE);
183 		*out_line = node->line;
184 	}
185 	qdf_spin_unlock_bh(&tracker->lock);
186 
187 	return !!node;
188 }
189 
190