xref: /wlan-dirver/qca-wifi-host-cmn/qdf/src/qdf_talloc.c (revision cc9cfde56b108942f9ccfc4fa51aaa0d3a4f00cd)
1 /*
2  * Copyright (c) 2018, 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 /**
20  * DOC: qdf_talloc.c
21  *
22  * OS-independent talloc implementation
23  */
24 
25 #ifdef WLAN_TALLOC_DEBUG
26 
27 #include "i_qdf_talloc.h"
28 #include "qdf_hashtable.h"
29 #include "qdf_list.h"
30 #include "qdf_mc_timer.h"
31 #include "qdf_mem.h"
32 #include "qdf_module.h"
33 #include "qdf_status.h"
34 #include "qdf_str.h"
35 #include "qdf_talloc.h"
36 
37 #define QDF_TALLOC_MAX_BYTES __page_size
38 #define QDF_TALLOC_SLEEP_TIMEOUT_MS 300
39 #define QDF_TALLOC_FUNC_NAME_SIZE 48
40 #define QDF_TALLOC_HT_BITS 8 /* 256 buckets */
41 
42 static void
43 __qdf_talloc_log_nomem(const size_t size, const char *func, const uint16_t line)
44 {
45 	qdf_nofl_info("Failed to alloc %zuB; via %s():%d", size, func, line);
46 }
47 
48 static void *
49 __qdf_zalloc_auto(const size_t size, const char *func, const uint16_t line)
50 {
51 	unsigned long start, duration;
52 	void *ptr;
53 
54 	start = qdf_mc_timer_get_system_time();
55 	ptr = __zalloc_auto(size);
56 	duration = qdf_mc_timer_get_system_time() - start;
57 
58 	if (duration > QDF_TALLOC_SLEEP_TIMEOUT_MS)
59 		qdf_nofl_info("Alloc slept; %lums, %zuB; via %s():%d",
60 			      duration, size, func, line);
61 
62 	if (!ptr) {
63 		__qdf_talloc_log_nomem(size, func, line);
64 		return NULL;
65 	}
66 
67 	qdf_mem_kmalloc_inc(__alloc_size(ptr));
68 
69 	return ptr;
70 }
71 
72 static void *
73 __qdf_zalloc_atomic(const size_t size, const char *func, const uint16_t line)
74 {
75 	void *ptr;
76 
77 	ptr = __zalloc_atomic(size);
78 	if (!ptr) {
79 		__qdf_talloc_log_nomem(size, func, line);
80 		return NULL;
81 	}
82 
83 	qdf_mem_kmalloc_inc(__alloc_size(ptr));
84 
85 	return ptr;
86 }
87 
88 static void __qdf_free(const void *ptr)
89 {
90 	qdf_mem_kmalloc_dec(__alloc_size(ptr));
91 
92 	__free(ptr);
93 }
94 
95 static qdf_ht_declare(__qdf_talloc_meta_ht, QDF_TALLOC_HT_BITS);
96 static qdf_spinlock_t __qdf_talloc_meta_lock;
97 
98 /**
99  * struct qdf_talloc_parent_meta - parent/children metadata for memory tracking
100  * @entry: entry for membership in the parent hashtable
101  * @children: list of associated children
102  */
103 struct qdf_talloc_parent_meta {
104 	struct qdf_ht_entry entry;
105 	uintptr_t key;
106 	qdf_list_t children;
107 };
108 
109 static struct qdf_talloc_parent_meta *
110 qdf_talloc_parent_meta_alloc(const void *parent,
111 			     const char *func, const uint16_t line)
112 {
113 	struct qdf_talloc_parent_meta *pmeta;
114 
115 	pmeta = __qdf_zalloc_atomic(sizeof(*pmeta), func, line);
116 	if (!pmeta)
117 		return NULL;
118 
119 	pmeta->key = (uintptr_t)parent;
120 	qdf_list_create(&pmeta->children, 0);
121 	qdf_ht_add(__qdf_talloc_meta_ht, &pmeta->entry, pmeta->key);
122 
123 	return pmeta;
124 }
125 
126 static void qdf_talloc_parent_meta_free(struct qdf_talloc_parent_meta *pmeta)
127 {
128 	qdf_ht_remove(&pmeta->entry);
129 	qdf_list_destroy(&pmeta->children);
130 	__free(pmeta);
131 }
132 
133 static struct qdf_talloc_parent_meta *
134 qdf_talloc_parent_meta_lookup(const void *parent)
135 {
136 	struct qdf_talloc_parent_meta *pmeta;
137 	uintptr_t key = (uintptr_t)parent;
138 
139 	qdf_ht_get(__qdf_talloc_meta_ht, pmeta, entry, key, key);
140 
141 	return pmeta;
142 }
143 
144 /**
145  * struct qdf_talloc_child_meta - talloc child debug information
146  * @parent: parent pointer used during allocation for leak tracking
147  * @node: list node for membership in @parent's children list
148  * @func: name of the function that requested the allocation
149  * @line: line number of the call site in @func
150  * @size: size of the allocation in bytes
151  */
152 struct qdf_talloc_child_meta {
153 	const void *parent;
154 	qdf_list_node_t node;
155 	char func[QDF_TALLOC_FUNC_NAME_SIZE];
156 	uint16_t line;
157 	uint32_t size;
158 	uint32_t guard;
159 };
160 
161 /**
162  * struct qdf_talloc_header - talloc debug header information
163  * @meta: child allocation metadata
164  * @guard: a known value, used to detect out-of-bounds access
165  */
166 struct qdf_talloc_header {
167 	struct qdf_talloc_child_meta meta;
168 	uint32_t guard;
169 };
170 
171 /**
172  * struct qdf_talloc_trailer - talloc debug trailer information
173  * @guard: a known value, used to detect out-of-bounds access
174  */
175 struct qdf_talloc_trailer {
176 	uint32_t guard;
177 };
178 
179 static uint32_t QDF_TALLOC_GUARD = 0xaabbeeff;
180 
181 #define QDF_TALLOC_DEBUG_SIZE \
182 	(sizeof(struct qdf_talloc_header) + sizeof(struct qdf_talloc_trailer))
183 
184 static struct qdf_talloc_header *qdf_talloc_header(void *ptr)
185 {
186 	return (struct qdf_talloc_header *)ptr - 1;
187 }
188 
189 static void *qdf_talloc_ptr(struct qdf_talloc_header *header)
190 {
191 	return header + 1;
192 }
193 
194 static struct qdf_talloc_trailer *
195 qdf_talloc_trailer(struct qdf_talloc_header *header)
196 {
197 	void *ptr = qdf_talloc_ptr(header);
198 	size_t size = header->meta.size;
199 
200 	return (struct qdf_talloc_trailer *)((uint8_t *)ptr + size);
201 }
202 
203 static void qdf_talloc_meta_init(struct qdf_talloc_header *header,
204 				 const void *parent, const size_t size,
205 				 const char *func, const uint16_t line)
206 {
207 	struct qdf_talloc_trailer *trailer;
208 
209 	/* copy the function name to support multi-*.ko configurations */
210 	qdf_str_lcopy(header->meta.func, func, sizeof(header->meta.func));
211 	header->meta.parent = parent;
212 	header->meta.line = line;
213 	header->meta.size = size;
214 	header->guard = QDF_TALLOC_GUARD;
215 
216 	trailer = qdf_talloc_trailer(header);
217 	trailer->guard = QDF_TALLOC_GUARD;
218 }
219 
220 static bool qdf_talloc_meta_assert_valid(struct qdf_talloc_header *header,
221 					 const char *func, const uint16_t line)
222 {
223 	struct qdf_talloc_trailer *trailer = qdf_talloc_trailer(header);
224 	bool is_valid = true;
225 
226 	if (header->guard != QDF_TALLOC_GUARD) {
227 		qdf_nofl_alert("Corrupted header guard 0x%x (expected 0x%x)",
228 			       header->guard, QDF_TALLOC_GUARD);
229 		is_valid = false;
230 	}
231 
232 	if (header->meta.size > QDF_TALLOC_MAX_BYTES) {
233 		qdf_nofl_alert("Corrupted allocation size %u (expected <= %zu)",
234 			       header->meta.size, QDF_TALLOC_MAX_BYTES);
235 		is_valid = false;
236 	}
237 
238 	if (!qdf_list_node_in_any_list(&header->meta.node)) {
239 		qdf_nofl_alert("Corrupted header node or double free");
240 		is_valid = false;
241 	}
242 
243 	if (trailer->guard != QDF_TALLOC_GUARD) {
244 		qdf_nofl_alert("Corrupted trailer guard 0x%x (expected 0x%x)",
245 			       trailer->guard, QDF_TALLOC_GUARD);
246 		is_valid = false;
247 	}
248 
249 	if (!is_valid)
250 		QDF_DEBUG_PANIC("Fatal memory error detected @ %s():%d",
251 				func, line);
252 
253 	return is_valid;
254 }
255 
256 static void qdf_leaks_print_header(void)
257 {
258 	qdf_nofl_alert("-----------------------------------------------------");
259 	qdf_nofl_alert(" size      function():line");
260 	qdf_nofl_alert("-----------------------------------------------------");
261 }
262 
263 static uint32_t qdf_leaks_print(const struct qdf_talloc_parent_meta *pmeta)
264 {
265 	struct qdf_talloc_child_meta *cmeta;
266 	uint32_t count = 0;
267 
268 	qdf_list_for_each(&pmeta->children, cmeta, node) {
269 		qdf_nofl_alert("%6uB @ %s():%u",
270 			       cmeta->size, cmeta->func, cmeta->line);
271 		count++;
272 	}
273 
274 	return count;
275 }
276 
277 #define qdf_leaks_panic(count, func, line) \
278 	QDF_DEBUG_PANIC("%u fatal memory leaks detected @ %s():%u", \
279 			count, func, line)
280 
281 QDF_STATUS qdf_talloc_feature_init(void)
282 {
283 	qdf_spinlock_create(&__qdf_talloc_meta_lock);
284 	qdf_ht_init(__qdf_talloc_meta_ht);
285 
286 	return QDF_STATUS_SUCCESS;
287 }
288 qdf_export_symbol(qdf_talloc_feature_init);
289 
290 void qdf_talloc_feature_deinit(void)
291 {
292 	qdf_spin_lock_bh(&__qdf_talloc_meta_lock);
293 
294 	if (!qdf_ht_empty(__qdf_talloc_meta_ht)) {
295 		struct qdf_talloc_parent_meta *pmeta;
296 		uint32_t count = 0;
297 		int i;
298 
299 		qdf_leaks_print_header();
300 
301 		qdf_ht_for_each(__qdf_talloc_meta_ht, i, pmeta, entry)
302 			count += qdf_leaks_print(pmeta);
303 
304 		qdf_leaks_panic(count, __func__, __LINE__);
305 	}
306 
307 	qdf_spin_unlock_bh(&__qdf_talloc_meta_lock);
308 
309 	qdf_ht_deinit(__qdf_talloc_meta_ht);
310 	qdf_spinlock_destroy(&__qdf_talloc_meta_lock);
311 }
312 qdf_export_symbol(qdf_talloc_feature_deinit);
313 
314 static QDF_STATUS qdf_talloc_meta_insert(struct qdf_talloc_header *header,
315 					 const char *func, const uint16_t line)
316 {
317 	struct qdf_talloc_child_meta *cmeta = &header->meta;
318 	struct qdf_talloc_parent_meta *pmeta;
319 
320 	pmeta = qdf_talloc_parent_meta_lookup(cmeta->parent);
321 	if (!pmeta)
322 		pmeta = qdf_talloc_parent_meta_alloc(cmeta->parent, func, line);
323 	if (!pmeta)
324 		return QDF_STATUS_E_NOMEM;
325 
326 	qdf_list_insert_back(&pmeta->children, &cmeta->node);
327 
328 	return QDF_STATUS_SUCCESS;
329 }
330 
331 void *__qdf_talloc_fl(const void *parent, const size_t size,
332 		      const char *func, const uint16_t line)
333 {
334 	QDF_STATUS status;
335 	struct qdf_talloc_header *header;
336 
337 	QDF_BUG(parent);
338 	if (!parent)
339 		return NULL;
340 
341 	QDF_BUG(size <= QDF_TALLOC_MAX_BYTES);
342 	if (size > QDF_TALLOC_MAX_BYTES)
343 		return NULL;
344 
345 	header = __qdf_zalloc_auto(size + QDF_TALLOC_DEBUG_SIZE, func, line);
346 	if (!header)
347 		return NULL;
348 
349 	qdf_talloc_meta_init(header, parent, size, func, line);
350 
351 	qdf_spin_lock_bh(&__qdf_talloc_meta_lock);
352 	status = qdf_talloc_meta_insert(header, func, line);
353 	qdf_spin_unlock_bh(&__qdf_talloc_meta_lock);
354 
355 	if (QDF_IS_STATUS_ERROR(status)) {
356 		__qdf_free(header);
357 		return NULL;
358 	}
359 
360 	return qdf_talloc_ptr(header);
361 }
362 qdf_export_symbol(__qdf_talloc_fl);
363 
364 static void
365 __qdf_talloc_assert_no_children(const void *parent,
366 				const char *func, const uint16_t line)
367 {
368 	struct qdf_talloc_parent_meta *pmeta;
369 	uint32_t count;
370 
371 	pmeta = qdf_talloc_parent_meta_lookup(parent);
372 	if (!pmeta)
373 		return;
374 
375 	qdf_leaks_print_header();
376 	count = qdf_leaks_print(pmeta);
377 	qdf_leaks_panic(count, func, line);
378 }
379 
380 static void qdf_talloc_meta_remove(struct qdf_talloc_header *header,
381 				   const char *func, const uint16_t line)
382 {
383 	struct qdf_talloc_child_meta *cmeta = &header->meta;
384 	struct qdf_talloc_parent_meta *pmeta;
385 
386 	__qdf_talloc_assert_no_children(qdf_talloc_ptr(header), func, line);
387 
388 	pmeta = qdf_talloc_parent_meta_lookup(cmeta->parent);
389 	if (!pmeta) {
390 		QDF_DEBUG_PANIC("double-free or free-no-allocate @ %s():%u",
391 				func, line);
392 		return;
393 	}
394 
395 	qdf_list_remove_node(&pmeta->children, &cmeta->node);
396 
397 	if (qdf_list_empty(&pmeta->children))
398 		qdf_talloc_parent_meta_free(pmeta);
399 }
400 
401 void __qdf_tfree_fl(void *ptr, const char *func, const uint16_t line)
402 {
403 	struct qdf_talloc_header *header;
404 
405 	QDF_BUG(ptr);
406 	if (!ptr)
407 		return;
408 
409 	header = qdf_talloc_header(ptr);
410 	qdf_talloc_meta_assert_valid(header, func, line);
411 
412 	qdf_spin_lock_bh(&__qdf_talloc_meta_lock);
413 	qdf_talloc_meta_remove(header, func, line);
414 	qdf_spin_unlock_bh(&__qdf_talloc_meta_lock);
415 
416 	__qdf_free(header);
417 }
418 qdf_export_symbol(__qdf_tfree_fl);
419 
420 void qdf_talloc_assert_no_children_fl(const void *parent,
421 				      const char *func, const uint16_t line)
422 {
423 	qdf_spin_lock_bh(&__qdf_talloc_meta_lock);
424 	__qdf_talloc_assert_no_children(parent, func, line);
425 	qdf_spin_unlock_bh(&__qdf_talloc_meta_lock);
426 }
427 qdf_export_symbol(qdf_talloc_assert_no_children_fl);
428 
429 #endif /* WLAN_TALLOC_DEBUG */
430 
431