1 /* 2 * Copyright (c) 2019, 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 #include "qdf_debug_domain.h" 20 #include "qdf_lock.h" 21 #include "qdf_mem.h" 22 #include "qdf_module.h" 23 #include "qdf_ptr_hash.h" 24 #include "qdf_status.h" 25 #include "qdf_str.h" 26 #include "qdf_tracker.h" 27 #include "qdf_types.h" 28 29 struct qdf_tracker_node { 30 struct qdf_ptr_hash_entry entry; 31 enum qdf_debug_domain domain; 32 char func[QDF_TRACKER_FUNC_SIZE]; 33 uint32_t line; 34 }; 35 36 void qdf_tracker_init(struct qdf_tracker *tracker) 37 { 38 qdf_spinlock_create(&tracker->lock); 39 qdf_ptr_hash_init(tracker->ht); 40 } 41 qdf_export_symbol(qdf_tracker_init); 42 43 void qdf_tracker_deinit(struct qdf_tracker *tracker) 44 { 45 qdf_tracker_check_for_leaks(tracker); 46 47 qdf_spin_lock_bh(&tracker->lock); 48 QDF_BUG(qdf_ptr_hash_empty(tracker->ht)); 49 qdf_spin_unlock_bh(&tracker->lock); 50 51 qdf_ptr_hash_deinit(tracker->ht); 52 qdf_spinlock_destroy(&tracker->lock); 53 } 54 qdf_export_symbol(qdf_tracker_deinit); 55 56 static inline void qdf_tracker_print_break(void) 57 { 58 qdf_nofl_alert("-----------------------------------------------------"); 59 } 60 61 static uint32_t qdf_tracker_leaks_print(struct qdf_tracker *tracker, 62 enum qdf_debug_domain domain) 63 { 64 struct qdf_ptr_hash_bucket *bucket; 65 struct qdf_tracker_node *node; 66 bool print_header = true; 67 uint32_t count = 0; 68 69 qdf_ptr_hash_for_each(tracker->ht, bucket, node, entry) { 70 if (node->domain != domain) 71 continue; 72 73 if (print_header) { 74 print_header = false; 75 qdf_nofl_alert("%s detected in %s domain!", 76 tracker->leak_title, 77 qdf_debug_domain_name(domain)); 78 qdf_tracker_print_break(); 79 } 80 81 count++; 82 qdf_nofl_alert("0x%lx @ %s:%u", node->entry.key, 83 node->func, node->line); 84 } 85 86 if (count) 87 qdf_tracker_print_break(); 88 89 return count; 90 } 91 92 void qdf_tracker_check_for_leaks(struct qdf_tracker *tracker) 93 { 94 enum qdf_debug_domain domain = qdf_debug_domain_get(); 95 uint32_t leaks; 96 97 qdf_spin_lock_bh(&tracker->lock); 98 leaks = qdf_tracker_leaks_print(tracker, domain); 99 if (leaks) 100 QDF_DEBUG_PANIC("%u fatal %s detected in %s domain!", 101 leaks, tracker->leak_title, 102 qdf_debug_domain_name(domain)); 103 qdf_spin_unlock_bh(&tracker->lock); 104 } 105 qdf_export_symbol(qdf_tracker_check_for_leaks); 106 107 QDF_STATUS qdf_tracker_track(struct qdf_tracker *tracker, void *ptr, 108 const char *func, uint32_t line) 109 { 110 struct qdf_tracker_node *node; 111 112 QDF_BUG(ptr); 113 if (!ptr) 114 return QDF_STATUS_E_INVAL; 115 116 qdf_spin_lock_bh(&tracker->lock); 117 node = qdf_ptr_hash_get(tracker->ht, ptr, node, entry); 118 if (node) 119 QDF_DEBUG_PANIC("Double %s (via %s:%u); last %s from %s:%u", 120 tracker->track_title, func, line, 121 tracker->track_title, node->func, node->line); 122 qdf_spin_unlock_bh(&tracker->lock); 123 124 if (node) 125 return QDF_STATUS_E_ALREADY; 126 127 node = qdf_mem_malloc(sizeof(*node)); 128 if (!node) 129 return QDF_STATUS_E_NOMEM; 130 131 node->domain = qdf_debug_domain_get(); 132 qdf_str_lcopy(node->func, func, QDF_TRACKER_FUNC_SIZE); 133 node->line = line; 134 135 qdf_spin_lock_bh(&tracker->lock); 136 qdf_ptr_hash_add(tracker->ht, ptr, node, entry); 137 qdf_spin_unlock_bh(&tracker->lock); 138 139 return QDF_STATUS_SUCCESS; 140 } 141 qdf_export_symbol(qdf_tracker_track); 142 143 void qdf_tracker_untrack(struct qdf_tracker *tracker, void *ptr, 144 const char *func, uint32_t line) 145 { 146 enum qdf_debug_domain domain = qdf_debug_domain_get(); 147 struct qdf_tracker_node *node; 148 149 QDF_BUG(ptr); 150 if (!ptr) 151 return; 152 153 qdf_spin_lock_bh(&tracker->lock); 154 node = qdf_ptr_hash_remove(tracker->ht, ptr, node, entry); 155 if (!node) 156 QDF_DEBUG_PANIC("Double %s (via %s:%u)", 157 tracker->untrack_title, func, line); 158 else if (node->domain != domain) 159 QDF_DEBUG_PANIC("%s domain mismatch; tracked:%s, %s:%u; untracked:%s , %s:%u", 160 tracker->untrack_title, 161 qdf_debug_domain_name(node->domain), 162 node->func, node->line, 163 qdf_debug_domain_name(domain), 164 func, line); 165 qdf_spin_unlock_bh(&tracker->lock); 166 167 if (node) 168 qdf_mem_free(node); 169 } 170 qdf_export_symbol(qdf_tracker_untrack); 171 172 bool qdf_tracker_lookup(struct qdf_tracker *tracker, void *ptr, 173 char (*out_func)[QDF_TRACKER_FUNC_SIZE], 174 uint32_t *out_line) 175 { 176 struct qdf_tracker_node *node; 177 178 qdf_spin_lock_bh(&tracker->lock); 179 node = qdf_ptr_hash_get(tracker->ht, ptr, node, entry); 180 if (node) { 181 qdf_str_lcopy((char *)out_func, node->func, 182 QDF_TRACKER_FUNC_SIZE); 183 *out_line = node->line; 184 } 185 qdf_spin_unlock_bh(&tracker->lock); 186 187 return !!node; 188 } 189 190