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