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