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