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