]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2008-2011 New Dream Network | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
31f18b77 | 14 | #include "lockdep.h" |
11fdf7f2 | 15 | #include "common/ceph_context.h" |
7c673cae | 16 | #include "common/dout.h" |
7c673cae | 17 | #include "common/valgrind.h" |
7c673cae | 18 | |
7c673cae FG |
19 | /******* Constants **********/ |
20 | #define lockdep_dout(v) lsubdout(g_lockdep_ceph_ctx, lockdep, v) | |
21 | #define MAX_LOCKS 4096 // increase me as needed | |
22 | #define BACKTRACE_SKIP 2 | |
23 | ||
24 | /******* Globals **********/ | |
b32b8144 | 25 | bool g_lockdep; |
7c673cae FG |
26 | struct lockdep_stopper_t { |
27 | // disable lockdep when this module destructs. | |
28 | ~lockdep_stopper_t() { | |
29 | g_lockdep = 0; | |
30 | } | |
31 | }; | |
f67539c2 TL |
32 | |
33 | ||
7c673cae FG |
34 | static pthread_mutex_t lockdep_mutex = PTHREAD_MUTEX_INITIALIZER; |
35 | static CephContext *g_lockdep_ceph_ctx = NULL; | |
36 | static lockdep_stopper_t lockdep_stopper; | |
37 | static ceph::unordered_map<std::string, int> lock_ids; | |
f67539c2 TL |
38 | static std::map<int, std::string> lock_names; |
39 | static std::map<int, int> lock_refs; | |
7c673cae | 40 | static char free_ids[MAX_LOCKS/8]; // bit set = free |
f67539c2 | 41 | static ceph::unordered_map<pthread_t, std::map<int,ceph::BackTrace*> > held; |
7c673cae | 42 | static char follows[MAX_LOCKS][MAX_LOCKS/8]; // follows[a][b] means b taken after a |
f67539c2 | 43 | static ceph::BackTrace *follows_bt[MAX_LOCKS][MAX_LOCKS]; |
7c673cae | 44 | unsigned current_maxid; |
b32b8144 FG |
45 | int last_freed_id = -1; |
46 | static bool free_ids_inited; | |
7c673cae FG |
47 | |
48 | static bool lockdep_force_backtrace() | |
49 | { | |
50 | return (g_lockdep_ceph_ctx != NULL && | |
51 | g_lockdep_ceph_ctx->_conf->lockdep_force_backtrace); | |
52 | } | |
53 | ||
54 | /******* Functions **********/ | |
55 | void lockdep_register_ceph_context(CephContext *cct) | |
56 | { | |
57 | static_assert((MAX_LOCKS > 0) && (MAX_LOCKS % 8 == 0), | |
58 | "lockdep's MAX_LOCKS needs to be divisible by 8 to operate correctly."); | |
59 | pthread_mutex_lock(&lockdep_mutex); | |
60 | if (g_lockdep_ceph_ctx == NULL) { | |
61 | ANNOTATE_BENIGN_RACE_SIZED(&g_lockdep_ceph_ctx, sizeof(g_lockdep_ceph_ctx), | |
62 | "lockdep cct"); | |
63 | ANNOTATE_BENIGN_RACE_SIZED(&g_lockdep, sizeof(g_lockdep), | |
64 | "lockdep enabled"); | |
65 | g_lockdep = true; | |
66 | g_lockdep_ceph_ctx = cct; | |
67 | lockdep_dout(1) << "lockdep start" << dendl; | |
b32b8144 FG |
68 | if (!free_ids_inited) { |
69 | free_ids_inited = true; | |
92f5a8d4 | 70 | // FIPS zeroization audit 20191115: this memset is not security related. |
b32b8144 FG |
71 | memset((void*) &free_ids[0], 255, sizeof(free_ids)); |
72 | } | |
7c673cae FG |
73 | } |
74 | pthread_mutex_unlock(&lockdep_mutex); | |
75 | } | |
76 | ||
77 | void lockdep_unregister_ceph_context(CephContext *cct) | |
78 | { | |
79 | pthread_mutex_lock(&lockdep_mutex); | |
80 | if (cct == g_lockdep_ceph_ctx) { | |
81 | lockdep_dout(1) << "lockdep stop" << dendl; | |
82 | // this cct is going away; shut it down! | |
83 | g_lockdep = false; | |
84 | g_lockdep_ceph_ctx = NULL; | |
85 | ||
86 | // blow away all of our state, too, in case it starts up again. | |
87 | for (unsigned i = 0; i < current_maxid; ++i) { | |
88 | for (unsigned j = 0; j < current_maxid; ++j) { | |
89 | delete follows_bt[i][j]; | |
90 | } | |
91 | } | |
92 | ||
93 | held.clear(); | |
94 | lock_names.clear(); | |
95 | lock_ids.clear(); | |
92f5a8d4 | 96 | // FIPS zeroization audit 20191115: these memsets are not security related. |
7c673cae | 97 | memset((void*)&follows[0][0], 0, current_maxid * MAX_LOCKS/8); |
f67539c2 | 98 | memset((void*)&follows_bt[0][0], 0, sizeof(ceph::BackTrace*) * current_maxid * MAX_LOCKS); |
7c673cae FG |
99 | } |
100 | pthread_mutex_unlock(&lockdep_mutex); | |
101 | } | |
102 | ||
103 | int lockdep_dump_locks() | |
104 | { | |
105 | pthread_mutex_lock(&lockdep_mutex); | |
b32b8144 FG |
106 | if (!g_lockdep) |
107 | goto out; | |
7c673cae | 108 | |
f67539c2 | 109 | for (auto p = held.begin(); p != held.end(); ++p) { |
7c673cae | 110 | lockdep_dout(0) << "--- thread " << p->first << " ---" << dendl; |
f67539c2 | 111 | for (auto q = p->second.begin(); |
7c673cae FG |
112 | q != p->second.end(); |
113 | ++q) { | |
114 | lockdep_dout(0) << " * " << lock_names[q->first] << "\n"; | |
115 | if (q->second) | |
116 | *_dout << *(q->second); | |
117 | *_dout << dendl; | |
118 | } | |
119 | } | |
b32b8144 | 120 | out: |
7c673cae FG |
121 | pthread_mutex_unlock(&lockdep_mutex); |
122 | return 0; | |
123 | } | |
124 | ||
125 | int lockdep_get_free_id(void) | |
126 | { | |
127 | // if there's id known to be freed lately, reuse it | |
128 | if ((last_freed_id >= 0) && | |
129 | (free_ids[last_freed_id/8] & (1 << (last_freed_id % 8)))) { | |
130 | int tmp = last_freed_id; | |
131 | last_freed_id = -1; | |
132 | free_ids[tmp/8] &= 255 - (1 << (tmp % 8)); | |
133 | lockdep_dout(1) << "lockdep reusing last freed id " << tmp << dendl; | |
134 | return tmp; | |
135 | } | |
136 | ||
137 | // walk through entire array and locate nonzero char, then find | |
138 | // actual bit. | |
139 | for (int i = 0; i < MAX_LOCKS / 8; ++i) { | |
140 | if (free_ids[i] != 0) { | |
141 | for (int j = 0; j < 8; ++j) { | |
142 | if (free_ids[i] & (1 << j)) { | |
143 | free_ids[i] &= 255 - (1 << j); | |
144 | lockdep_dout(1) << "lockdep using id " << i * 8 + j << dendl; | |
145 | return i * 8 + j; | |
146 | } | |
147 | } | |
148 | } | |
149 | } | |
150 | ||
151 | // not found | |
152 | lockdep_dout(0) << "failing miserably..." << dendl; | |
153 | return -1; | |
154 | } | |
155 | ||
b32b8144 | 156 | static int _lockdep_register(const char *name) |
7c673cae | 157 | { |
b32b8144 | 158 | int id = -1; |
7c673cae | 159 | |
b32b8144 FG |
160 | if (!g_lockdep) |
161 | return id; | |
7c673cae FG |
162 | ceph::unordered_map<std::string, int>::iterator p = lock_ids.find(name); |
163 | if (p == lock_ids.end()) { | |
164 | id = lockdep_get_free_id(); | |
165 | if (id < 0) { | |
166 | lockdep_dout(0) << "ERROR OUT OF IDS .. have 0" | |
167 | << " max " << MAX_LOCKS << dendl; | |
168 | for (auto& p : lock_names) { | |
169 | lockdep_dout(0) << " lock " << p.first << " " << p.second << dendl; | |
170 | } | |
11fdf7f2 | 171 | ceph_abort(); |
7c673cae FG |
172 | } |
173 | if (current_maxid <= (unsigned)id) { | |
174 | current_maxid = (unsigned)id + 1; | |
175 | } | |
176 | lock_ids[name] = id; | |
177 | lock_names[id] = name; | |
178 | lockdep_dout(10) << "registered '" << name << "' as " << id << dendl; | |
179 | } else { | |
180 | id = p->second; | |
181 | lockdep_dout(20) << "had '" << name << "' as " << id << dendl; | |
182 | } | |
183 | ||
184 | ++lock_refs[id]; | |
7c673cae FG |
185 | |
186 | return id; | |
187 | } | |
188 | ||
b32b8144 FG |
189 | int lockdep_register(const char *name) |
190 | { | |
191 | int id; | |
192 | ||
193 | pthread_mutex_lock(&lockdep_mutex); | |
194 | id = _lockdep_register(name); | |
195 | pthread_mutex_unlock(&lockdep_mutex); | |
196 | return id; | |
197 | } | |
198 | ||
7c673cae FG |
199 | void lockdep_unregister(int id) |
200 | { | |
201 | if (id < 0) { | |
202 | return; | |
203 | } | |
204 | ||
205 | pthread_mutex_lock(&lockdep_mutex); | |
206 | ||
b32b8144 | 207 | std::string name; |
f67539c2 | 208 | auto p = lock_names.find(id); |
b32b8144 | 209 | if (p == lock_names.end()) |
11fdf7f2 | 210 | name = "unknown" ; |
b32b8144 FG |
211 | else |
212 | name = p->second; | |
7c673cae FG |
213 | |
214 | int &refs = lock_refs[id]; | |
215 | if (--refs == 0) { | |
b32b8144 FG |
216 | if (p != lock_names.end()) { |
217 | // reset dependency ordering | |
92f5a8d4 | 218 | // FIPS zeroization audit 20191115: this memset is not security related. |
b32b8144 FG |
219 | memset((void*)&follows[id][0], 0, MAX_LOCKS/8); |
220 | for (unsigned i=0; i<current_maxid; ++i) { | |
221 | delete follows_bt[id][i]; | |
222 | follows_bt[id][i] = NULL; | |
223 | ||
224 | delete follows_bt[i][id]; | |
225 | follows_bt[i][id] = NULL; | |
226 | follows[i][id / 8] &= 255 - (1 << (id % 8)); | |
227 | } | |
7c673cae | 228 | |
b32b8144 FG |
229 | lockdep_dout(10) << "unregistered '" << name << "' from " << id << dendl; |
230 | lock_ids.erase(p->second); | |
231 | lock_names.erase(id); | |
232 | } | |
7c673cae FG |
233 | lock_refs.erase(id); |
234 | free_ids[id/8] |= (1 << (id % 8)); | |
b32b8144 FG |
235 | last_freed_id = id; |
236 | } else if (g_lockdep) { | |
237 | lockdep_dout(20) << "have " << refs << " of '" << name << "' " << | |
238 | "from " << id << dendl; | |
7c673cae FG |
239 | } |
240 | pthread_mutex_unlock(&lockdep_mutex); | |
241 | } | |
242 | ||
243 | ||
244 | // does b follow a? | |
245 | static bool does_follow(int a, int b) | |
246 | { | |
247 | if (follows[a][b/8] & (1 << (b % 8))) { | |
248 | lockdep_dout(0) << "\n"; | |
249 | *_dout << "------------------------------------" << "\n"; | |
250 | *_dout << "existing dependency " << lock_names[a] << " (" << a << ") -> " | |
251 | << lock_names[b] << " (" << b << ") at:\n"; | |
252 | if (follows_bt[a][b]) { | |
253 | follows_bt[a][b]->print(*_dout); | |
254 | } | |
255 | *_dout << dendl; | |
256 | return true; | |
257 | } | |
258 | ||
259 | for (unsigned i=0; i<current_maxid; i++) { | |
260 | if ((follows[a][i/8] & (1 << (i % 8))) && | |
261 | does_follow(i, b)) { | |
262 | lockdep_dout(0) << "existing intermediate dependency " << lock_names[a] | |
263 | << " (" << a << ") -> " << lock_names[i] << " (" << i << ") at:\n"; | |
264 | if (follows_bt[a][i]) { | |
265 | follows_bt[a][i]->print(*_dout); | |
266 | } | |
267 | *_dout << dendl; | |
268 | return true; | |
269 | } | |
270 | } | |
271 | ||
272 | return false; | |
273 | } | |
274 | ||
11fdf7f2 TL |
275 | int lockdep_will_lock(const char *name, int id, bool force_backtrace, |
276 | bool recursive) | |
7c673cae FG |
277 | { |
278 | pthread_t p = pthread_self(); | |
7c673cae FG |
279 | |
280 | pthread_mutex_lock(&lockdep_mutex); | |
b32b8144 FG |
281 | if (!g_lockdep) { |
282 | pthread_mutex_unlock(&lockdep_mutex); | |
283 | return id; | |
284 | } | |
285 | ||
286 | if (id < 0) | |
287 | id = _lockdep_register(name); | |
288 | ||
7c673cae FG |
289 | lockdep_dout(20) << "_will_lock " << name << " (" << id << ")" << dendl; |
290 | ||
291 | // check dependency graph | |
f67539c2 TL |
292 | auto& m = held[p]; |
293 | for (auto p = m.begin(); p != m.end(); ++p) { | |
7c673cae | 294 | if (p->first == id) { |
11fdf7f2 TL |
295 | if (!recursive) { |
296 | lockdep_dout(0) << "\n"; | |
297 | *_dout << "recursive lock of " << name << " (" << id << ")\n"; | |
f67539c2 | 298 | auto bt = new ceph::BackTrace(BACKTRACE_SKIP); |
11fdf7f2 TL |
299 | bt->print(*_dout); |
300 | if (p->second) { | |
301 | *_dout << "\npreviously locked at\n"; | |
302 | p->second->print(*_dout); | |
303 | } | |
304 | delete bt; | |
305 | *_dout << dendl; | |
306 | ceph_abort(); | |
7c673cae | 307 | } |
7c673cae FG |
308 | } |
309 | else if (!(follows[p->first][id/8] & (1 << (id % 8)))) { | |
310 | // new dependency | |
311 | ||
312 | // did we just create a cycle? | |
313 | if (does_follow(id, p->first)) { | |
f67539c2 | 314 | auto bt = new ceph::BackTrace(BACKTRACE_SKIP); |
7c673cae FG |
315 | lockdep_dout(0) << "new dependency " << lock_names[p->first] |
316 | << " (" << p->first << ") -> " << name << " (" << id << ")" | |
317 | << " creates a cycle at\n"; | |
318 | bt->print(*_dout); | |
319 | *_dout << dendl; | |
320 | ||
321 | lockdep_dout(0) << "btw, i am holding these locks:" << dendl; | |
f67539c2 | 322 | for (auto q = m.begin(); q != m.end(); ++q) { |
7c673cae FG |
323 | lockdep_dout(0) << " " << lock_names[q->first] << " (" << q->first << ")" << dendl; |
324 | if (q->second) { | |
325 | lockdep_dout(0) << " "; | |
326 | q->second->print(*_dout); | |
327 | *_dout << dendl; | |
328 | } | |
329 | } | |
330 | ||
331 | lockdep_dout(0) << "\n" << dendl; | |
332 | ||
333 | // don't add this dependency, or we'll get aMutex. cycle in the graph, and | |
334 | // does_follow() won't terminate. | |
335 | ||
336 | ceph_abort(); // actually, we should just die here. | |
337 | } else { | |
f67539c2 | 338 | ceph::BackTrace* bt = NULL; |
7c673cae | 339 | if (force_backtrace || lockdep_force_backtrace()) { |
f67539c2 | 340 | bt = new ceph::BackTrace(BACKTRACE_SKIP); |
7c673cae FG |
341 | } |
342 | follows[p->first][id/8] |= 1 << (id % 8); | |
343 | follows_bt[p->first][id] = bt; | |
344 | lockdep_dout(10) << lock_names[p->first] << " -> " << name << " at" << dendl; | |
345 | //bt->print(*_dout); | |
346 | } | |
347 | } | |
348 | } | |
7c673cae FG |
349 | pthread_mutex_unlock(&lockdep_mutex); |
350 | return id; | |
351 | } | |
352 | ||
353 | int lockdep_locked(const char *name, int id, bool force_backtrace) | |
354 | { | |
355 | pthread_t p = pthread_self(); | |
356 | ||
7c673cae | 357 | pthread_mutex_lock(&lockdep_mutex); |
b32b8144 FG |
358 | if (!g_lockdep) |
359 | goto out; | |
360 | if (id < 0) | |
361 | id = _lockdep_register(name); | |
362 | ||
7c673cae FG |
363 | lockdep_dout(20) << "_locked " << name << dendl; |
364 | if (force_backtrace || lockdep_force_backtrace()) | |
f67539c2 | 365 | held[p][id] = new ceph::BackTrace(BACKTRACE_SKIP); |
7c673cae FG |
366 | else |
367 | held[p][id] = 0; | |
b32b8144 | 368 | out: |
7c673cae FG |
369 | pthread_mutex_unlock(&lockdep_mutex); |
370 | return id; | |
371 | } | |
372 | ||
373 | int lockdep_will_unlock(const char *name, int id) | |
374 | { | |
375 | pthread_t p = pthread_self(); | |
376 | ||
377 | if (id < 0) { | |
378 | //id = lockdep_register(name); | |
11fdf7f2 | 379 | ceph_assert(id == -1); |
7c673cae FG |
380 | return id; |
381 | } | |
382 | ||
383 | pthread_mutex_lock(&lockdep_mutex); | |
b32b8144 FG |
384 | if (!g_lockdep) |
385 | goto out; | |
7c673cae FG |
386 | lockdep_dout(20) << "_will_unlock " << name << dendl; |
387 | ||
388 | // don't assert.. lockdep may be enabled at any point in time | |
389 | //assert(held.count(p)); | |
390 | //assert(held[p].count(id)); | |
391 | ||
392 | delete held[p][id]; | |
393 | held[p].erase(id); | |
b32b8144 | 394 | out: |
7c673cae FG |
395 | pthread_mutex_unlock(&lockdep_mutex); |
396 | return id; | |
397 | } | |
398 | ||
399 |