xref: /wlan-dirver/qca-wifi-host-cmn/qdf/src/qdf_talloc.c (revision 1397a33f48ea6455be40871470b286e535820eb8)
1 /*
2  * Copyright (c) 2018 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 	QDF_BUG(qdf_spin_is_locked(&__qdf_talloc_meta_lock));
116 
117 	pmeta = __qdf_zalloc_atomic(sizeof(*pmeta), func, line);
118 	if (!pmeta)
119 		return NULL;
120 
121 	pmeta->key = (uintptr_t)parent;
122 	qdf_list_create(&pmeta->children, 0);
123 	qdf_ht_add(__qdf_talloc_meta_ht, &pmeta->entry, pmeta->key);
124 
125 	return pmeta;
126 }
127 
128 static void qdf_talloc_parent_meta_free(struct qdf_talloc_parent_meta *pmeta)
129 {
130 	QDF_BUG(qdf_spin_is_locked(&__qdf_talloc_meta_lock));
131 
132 	qdf_ht_remove(&pmeta->entry);
133 	qdf_list_destroy(&pmeta->children);
134 	__free(pmeta);
135 }
136 
137 static struct qdf_talloc_parent_meta *
138 qdf_talloc_parent_meta_lookup(const void *parent)
139 {
140 	struct qdf_talloc_parent_meta *pmeta;
141 	uintptr_t key = (uintptr_t)parent;
142 
143 	QDF_BUG(qdf_spin_is_locked(&__qdf_talloc_meta_lock));
144 
145 	qdf_ht_get(__qdf_talloc_meta_ht, pmeta, entry, key, key);
146 
147 	return pmeta;
148 }
149 
150 /**
151  * struct qdf_talloc_child_meta - talloc child debug information
152  * @parent: parent pointer used during allocation for leak tracking
153  * @node: list node for membership in @parent's children list
154  * @func: name of the function that requested the allocation
155  * @line: line number of the call site in @func
156  * @size: size of the allocation in bytes
157  */
158 struct qdf_talloc_child_meta {
159 	const void *parent;
160 	qdf_list_node_t node;
161 	char func[QDF_TALLOC_FUNC_NAME_SIZE];
162 	uint16_t line;
163 	uint32_t size;
164 	uint32_t guard;
165 };
166 
167 /**
168  * struct qdf_talloc_header - talloc debug header information
169  * @meta: child allocation metadata
170  * @guard: a known value, used to detect out-of-bounds access
171  */
172 struct qdf_talloc_header {
173 	struct qdf_talloc_child_meta meta;
174 	uint32_t guard;
175 };
176 
177 /**
178  * struct qdf_talloc_trailer - talloc debug trailer information
179  * @guard: a known value, used to detect out-of-bounds access
180  */
181 struct qdf_talloc_trailer {
182 	uint32_t guard;
183 };
184 
185 static uint32_t QDF_TALLOC_GUARD = 0xaabbeeff;
186 
187 #define QDF_TALLOC_DEBUG_SIZE \
188 	(sizeof(struct qdf_talloc_header) + sizeof(struct qdf_talloc_trailer))
189 
190 static struct qdf_talloc_header *qdf_talloc_header(void *ptr)
191 {
192 	return (struct qdf_talloc_header *)ptr - 1;
193 }
194 
195 static void *qdf_talloc_ptr(struct qdf_talloc_header *header)
196 {
197 	return header + 1;
198 }
199 
200 static struct qdf_talloc_trailer *
201 qdf_talloc_trailer(struct qdf_talloc_header *header)
202 {
203 	void *ptr = qdf_talloc_ptr(header);
204 	size_t size = header->meta.size;
205 
206 	return (struct qdf_talloc_trailer *)((uint8_t *)ptr + size);
207 }
208 
209 static void qdf_talloc_meta_init(struct qdf_talloc_header *header,
210 				 const void *parent, const size_t size,
211 				 const char *func, const uint16_t line)
212 {
213 	struct qdf_talloc_trailer *trailer;
214 
215 	/* copy the function name to support multi-*.ko configurations */
216 	qdf_str_lcopy(header->meta.func, func, sizeof(header->meta.func));
217 	header->meta.parent = parent;
218 	header->meta.line = line;
219 	header->meta.size = size;
220 	header->guard = QDF_TALLOC_GUARD;
221 
222 	trailer = qdf_talloc_trailer(header);
223 	trailer->guard = QDF_TALLOC_GUARD;
224 }
225 
226 static bool qdf_talloc_meta_assert_valid(struct qdf_talloc_header *header,
227 					 const char *func, const uint16_t line)
228 {
229 	struct qdf_talloc_trailer *trailer = qdf_talloc_trailer(header);
230 	bool is_valid = true;
231 
232 	if (header->guard != QDF_TALLOC_GUARD) {
233 		qdf_nofl_alert("Corrupted header guard 0x%x (expected 0x%x)",
234 			       header->guard, QDF_TALLOC_GUARD);
235 		is_valid = false;
236 	}
237 
238 	if (header->meta.size > QDF_TALLOC_MAX_BYTES) {
239 		qdf_nofl_alert("Corrupted allocation size %u (expected <= %zu)",
240 			       header->meta.size, QDF_TALLOC_MAX_BYTES);
241 		is_valid = false;
242 	}
243 
244 	if (!qdf_list_node_in_any_list(&header->meta.node)) {
245 		qdf_nofl_alert("Corrupted header node or double free");
246 		is_valid = false;
247 	}
248 
249 	if (trailer->guard != QDF_TALLOC_GUARD) {
250 		qdf_nofl_alert("Corrupted trailer guard 0x%x (expected 0x%x)",
251 			       trailer->guard, QDF_TALLOC_GUARD);
252 		is_valid = false;
253 	}
254 
255 	if (!is_valid)
256 		QDF_DEBUG_PANIC("Fatal memory error detected @ %s():%d",
257 				func, line);
258 
259 	return is_valid;
260 }
261 
262 static void qdf_leaks_print_header(void)
263 {
264 	qdf_nofl_alert("-----------------------------------------------------");
265 	qdf_nofl_alert(" size      function():line");
266 	qdf_nofl_alert("-----------------------------------------------------");
267 }
268 
269 static uint32_t qdf_leaks_print(const struct qdf_talloc_parent_meta *pmeta)
270 {
271 	struct qdf_talloc_child_meta *cmeta;
272 	uint32_t count = 0;
273 
274 	qdf_list_for_each(&pmeta->children, cmeta, node) {
275 		qdf_nofl_alert("%6uB @ %s():%u",
276 			       cmeta->size, cmeta->func, cmeta->line);
277 		count++;
278 	}
279 
280 	return count;
281 }
282 
283 #define qdf_leaks_panic(count, func, line) \
284 	QDF_DEBUG_PANIC("%u fatal memory leaks detected @ %s():%u", \
285 			count, func, line)
286 
287 QDF_STATUS qdf_talloc_feature_init(void)
288 {
289 	qdf_spinlock_create(&__qdf_talloc_meta_lock);
290 	qdf_ht_init(__qdf_talloc_meta_ht);
291 
292 	return QDF_STATUS_SUCCESS;
293 }
294 qdf_export_symbol(qdf_talloc_feature_init);
295 
296 void qdf_talloc_feature_deinit(void)
297 {
298 	qdf_spin_lock_bh(&__qdf_talloc_meta_lock);
299 
300 	if (!qdf_ht_empty(__qdf_talloc_meta_ht)) {
301 		struct qdf_talloc_parent_meta *pmeta;
302 		uint32_t count = 0;
303 		int i;
304 
305 		qdf_leaks_print_header();
306 
307 		qdf_ht_for_each(__qdf_talloc_meta_ht, i, pmeta, entry)
308 			count += qdf_leaks_print(pmeta);
309 
310 		qdf_leaks_panic(count, __func__, __LINE__);
311 	}
312 
313 	qdf_spin_unlock_bh(&__qdf_talloc_meta_lock);
314 
315 	qdf_ht_deinit(__qdf_talloc_meta_ht);
316 	qdf_spinlock_destroy(&__qdf_talloc_meta_lock);
317 }
318 qdf_export_symbol(qdf_talloc_feature_deinit);
319 
320 static QDF_STATUS qdf_talloc_meta_insert(struct qdf_talloc_header *header,
321 					 const char *func, const uint16_t line)
322 {
323 	struct qdf_talloc_child_meta *cmeta = &header->meta;
324 	struct qdf_talloc_parent_meta *pmeta;
325 
326 	QDF_BUG(qdf_spin_is_locked(&__qdf_talloc_meta_lock));
327 
328 	pmeta = qdf_talloc_parent_meta_lookup(cmeta->parent);
329 	if (!pmeta)
330 		pmeta = qdf_talloc_parent_meta_alloc(cmeta->parent, func, line);
331 	if (!pmeta)
332 		return QDF_STATUS_E_NOMEM;
333 
334 	qdf_list_insert_back(&pmeta->children, &cmeta->node);
335 
336 	return QDF_STATUS_SUCCESS;
337 }
338 
339 void *__qdf_talloc_fl(const void *parent, const size_t size,
340 		      const char *func, const uint16_t line)
341 {
342 	QDF_STATUS status;
343 	struct qdf_talloc_header *header;
344 
345 	QDF_BUG(parent);
346 	if (!parent)
347 		return NULL;
348 
349 	QDF_BUG(size <= QDF_TALLOC_MAX_BYTES);
350 	if (size > QDF_TALLOC_MAX_BYTES)
351 		return NULL;
352 
353 	header = __qdf_zalloc_auto(size + QDF_TALLOC_DEBUG_SIZE, func, line);
354 	if (!header)
355 		return NULL;
356 
357 	qdf_talloc_meta_init(header, parent, size, func, line);
358 
359 	qdf_spin_lock_bh(&__qdf_talloc_meta_lock);
360 	status = qdf_talloc_meta_insert(header, func, line);
361 	qdf_spin_unlock_bh(&__qdf_talloc_meta_lock);
362 
363 	if (QDF_IS_STATUS_ERROR(status)) {
364 		__qdf_free(header);
365 		return NULL;
366 	}
367 
368 	return qdf_talloc_ptr(header);
369 }
370 qdf_export_symbol(__qdf_talloc_fl);
371 
372 static void
373 __qdf_talloc_assert_no_children(const void *parent,
374 				const char *func, const uint16_t line)
375 {
376 	struct qdf_talloc_parent_meta *pmeta;
377 	uint32_t count;
378 
379 	QDF_BUG(qdf_spin_is_locked(&__qdf_talloc_meta_lock));
380 
381 	pmeta = qdf_talloc_parent_meta_lookup(parent);
382 	if (!pmeta)
383 		return;
384 
385 	qdf_leaks_print_header();
386 	count = qdf_leaks_print(pmeta);
387 	qdf_leaks_panic(count, func, line);
388 }
389 
390 static void qdf_talloc_meta_remove(struct qdf_talloc_header *header,
391 				   const char *func, const uint16_t line)
392 {
393 	struct qdf_talloc_child_meta *cmeta = &header->meta;
394 	struct qdf_talloc_parent_meta *pmeta;
395 
396 	QDF_BUG(qdf_spin_is_locked(&__qdf_talloc_meta_lock));
397 
398 	__qdf_talloc_assert_no_children(qdf_talloc_ptr(header), func, line);
399 
400 	pmeta = qdf_talloc_parent_meta_lookup(cmeta->parent);
401 	if (!pmeta) {
402 		QDF_DEBUG_PANIC("double-free or free-no-allocate @ %s():%u",
403 				func, line);
404 		return;
405 	}
406 
407 	qdf_list_remove_node(&pmeta->children, &cmeta->node);
408 
409 	if (qdf_list_empty(&pmeta->children))
410 		qdf_talloc_parent_meta_free(pmeta);
411 }
412 
413 void __qdf_tfree_fl(void *ptr, const char *func, const uint16_t line)
414 {
415 	struct qdf_talloc_header *header;
416 
417 	QDF_BUG(ptr);
418 	if (!ptr)
419 		return;
420 
421 	header = qdf_talloc_header(ptr);
422 	qdf_talloc_meta_assert_valid(header, func, line);
423 
424 	qdf_spin_lock_bh(&__qdf_talloc_meta_lock);
425 	qdf_talloc_meta_remove(header, func, line);
426 	qdf_spin_unlock_bh(&__qdf_talloc_meta_lock);
427 
428 	__qdf_free(header);
429 }
430 qdf_export_symbol(__qdf_tfree_fl);
431 
432 void qdf_talloc_assert_no_children_fl(const void *parent,
433 				      const char *func, const uint16_t line)
434 {
435 	qdf_spin_lock_bh(&__qdf_talloc_meta_lock);
436 	__qdf_talloc_assert_no_children(parent, func, line);
437 	qdf_spin_unlock_bh(&__qdf_talloc_meta_lock);
438 }
439 qdf_export_symbol(qdf_talloc_assert_no_children_fl);
440 
441 #endif /* WLAN_TALLOC_DEBUG */
442 
443