1 /*
2 * Copyright (c) 2019, 2021 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2021 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 #include "qdf_debug_domain.h"
21 #include "qdf_lock.h"
22 #include "qdf_mem.h"
23 #include "qdf_module.h"
24 #include "qdf_ptr_hash.h"
25 #include "qdf_status.h"
26 #include "qdf_str.h"
27 #include "qdf_tracker.h"
28 #include "qdf_types.h"
29
30 #ifdef CONFIG_LEAK_DETECTION
31 struct qdf_tracker_node {
32 struct qdf_ptr_hash_entry entry;
33 enum qdf_debug_domain domain;
34 char func[QDF_TRACKER_FUNC_SIZE];
35 uint32_t line;
36 };
37
qdf_tracker_init(struct qdf_tracker * tracker)38 void qdf_tracker_init(struct qdf_tracker *tracker)
39 {
40 qdf_spinlock_create(&tracker->lock);
41 qdf_ptr_hash_init(tracker->ht);
42 }
43 qdf_export_symbol(qdf_tracker_init);
44
qdf_tracker_deinit(struct qdf_tracker * tracker)45 void qdf_tracker_deinit(struct qdf_tracker *tracker)
46 {
47 qdf_tracker_check_for_leaks(tracker);
48
49 qdf_spin_lock_bh(&tracker->lock);
50 QDF_BUG(qdf_ptr_hash_empty(tracker->ht));
51 qdf_spin_unlock_bh(&tracker->lock);
52
53 qdf_ptr_hash_deinit(tracker->ht);
54 qdf_spinlock_destroy(&tracker->lock);
55 }
56 qdf_export_symbol(qdf_tracker_deinit);
57
qdf_tracker_print_break(void)58 static inline void qdf_tracker_print_break(void)
59 {
60 qdf_nofl_alert("-----------------------------------------------------");
61 }
62
qdf_tracker_leaks_print(struct qdf_tracker * tracker,enum qdf_debug_domain domain)63 static uint32_t qdf_tracker_leaks_print(struct qdf_tracker *tracker,
64 enum qdf_debug_domain domain)
65 {
66 struct qdf_ptr_hash_bucket *bucket;
67 struct qdf_tracker_node *node;
68 bool print_header = true;
69 uint32_t count = 0;
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
qdf_tracker_check_for_leaks(struct qdf_tracker * tracker)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
qdf_tracker_track(struct qdf_tracker * tracker,void * ptr,const char * func,uint32_t line)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
qdf_tracker_untrack(struct qdf_tracker * tracker,void * ptr,const char * func,uint32_t line)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
qdf_tracker_lookup(struct qdf_tracker * tracker,void * ptr,char (* out_func)[QDF_TRACKER_FUNC_SIZE],uint32_t * out_line)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 #endif
192