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