1 /* 2 * Copyright (c) 2019 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_BUG(qdf_spin_is_locked(&tracker->lock)); 70 71 qdf_ptr_hash_for_each(tracker->ht, bucket, node, entry) { 72 if (node->domain != domain) 73 continue; 74 75 if (print_header) { 76 print_header = false; 77 qdf_nofl_alert("%s detected in %s domain!", 78 tracker->leak_title, 79 qdf_debug_domain_name(domain)); 80 qdf_tracker_print_break(); 81 } 82 83 count++; 84 qdf_nofl_alert("0x%lx @ %s:%u", node->entry.key, 85 node->func, node->line); 86 } 87 88 if (count) 89 qdf_tracker_print_break(); 90 91 return count; 92 } 93 94 void qdf_tracker_check_for_leaks(struct qdf_tracker *tracker) 95 { 96 enum qdf_debug_domain domain = qdf_debug_domain_get(); 97 uint32_t leaks; 98 99 qdf_spin_lock_bh(&tracker->lock); 100 leaks = qdf_tracker_leaks_print(tracker, domain); 101 if (leaks) 102 QDF_DEBUG_PANIC("%u fatal %s detected in %s domain!", 103 leaks, tracker->leak_title, 104 qdf_debug_domain_name(domain)); 105 qdf_spin_unlock_bh(&tracker->lock); 106 } 107 qdf_export_symbol(qdf_tracker_check_for_leaks); 108 109 QDF_STATUS qdf_tracker_track(struct qdf_tracker *tracker, void *ptr, 110 const char *func, uint32_t line) 111 { 112 struct qdf_tracker_node *node; 113 114 QDF_BUG(ptr); 115 if (!ptr) 116 return QDF_STATUS_E_INVAL; 117 118 qdf_spin_lock_bh(&tracker->lock); 119 node = qdf_ptr_hash_get(tracker->ht, ptr, node, entry); 120 if (node) 121 QDF_DEBUG_PANIC("Double %s (via %s:%u); last %s from %s:%u", 122 tracker->track_title, func, line, 123 tracker->track_title, node->func, node->line); 124 qdf_spin_unlock_bh(&tracker->lock); 125 126 if (node) 127 return QDF_STATUS_E_ALREADY; 128 129 node = qdf_mem_malloc(sizeof(*node)); 130 if (!node) 131 return QDF_STATUS_E_NOMEM; 132 133 node->domain = qdf_debug_domain_get(); 134 qdf_str_lcopy(node->func, func, QDF_TRACKER_FUNC_SIZE); 135 node->line = line; 136 137 qdf_spin_lock_bh(&tracker->lock); 138 qdf_ptr_hash_add(tracker->ht, ptr, node, entry); 139 qdf_spin_unlock_bh(&tracker->lock); 140 141 return QDF_STATUS_SUCCESS; 142 } 143 qdf_export_symbol(qdf_tracker_track); 144 145 void qdf_tracker_untrack(struct qdf_tracker *tracker, void *ptr, 146 const char *func, uint32_t line) 147 { 148 enum qdf_debug_domain domain = qdf_debug_domain_get(); 149 struct qdf_tracker_node *node; 150 151 QDF_BUG(ptr); 152 if (!ptr) 153 return; 154 155 qdf_spin_lock_bh(&tracker->lock); 156 node = qdf_ptr_hash_remove(tracker->ht, ptr, node, entry); 157 if (!node) 158 QDF_DEBUG_PANIC("Double %s (via %s:%u)", 159 tracker->untrack_title, func, line); 160 else if (node->domain != domain) 161 QDF_DEBUG_PANIC("%s domain mismatch; tracked:%s, %s:%u; untracked:%s , %s:%u", 162 tracker->untrack_title, 163 qdf_debug_domain_name(node->domain), 164 node->func, node->line, 165 qdf_debug_domain_name(domain), 166 func, line); 167 qdf_spin_unlock_bh(&tracker->lock); 168 169 if (node) 170 qdf_mem_free(node); 171 } 172 qdf_export_symbol(qdf_tracker_untrack); 173 174 bool qdf_tracker_lookup(struct qdf_tracker *tracker, void *ptr, 175 char (*out_func)[QDF_TRACKER_FUNC_SIZE], 176 uint32_t *out_line) 177 { 178 struct qdf_tracker_node *node; 179 180 qdf_spin_lock_bh(&tracker->lock); 181 node = qdf_ptr_hash_get(tracker->ht, ptr, node, entry); 182 if (node) { 183 qdf_str_lcopy((char *)out_func, node->func, 184 QDF_TRACKER_FUNC_SIZE); 185 *out_line = node->line; 186 } 187 qdf_spin_unlock_bh(&tracker->lock); 188 189 return !!node; 190 } 191 192