]>
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; | |
92f5a8d4 | 68 | // FIPS zeroization audit 20191115: this memset is not security related. |
b32b8144 FG |
69 | memset((void*) &free_ids[0], 255, sizeof(free_ids)); |
70 | } | |
7c673cae FG |
71 | } |
72 | pthread_mutex_unlock(&lockdep_mutex); | |
73 | } | |
74 | ||
75 | void lockdep_unregister_ceph_context(CephContext *cct) | |
76 | { | |
77 | pthread_mutex_lock(&lockdep_mutex); | |
78 | if (cct == g_lockdep_ceph_ctx) { | |
79 | lockdep_dout(1) << "lockdep stop" << dendl; | |
80 | // this cct is going away; shut it down! | |
81 | g_lockdep = false; | |
82 | g_lockdep_ceph_ctx = NULL; | |
83 | ||
84 | // blow away all of our state, too, in case it starts up again. | |
85 | for (unsigned i = 0; i < current_maxid; ++i) { | |
86 | for (unsigned j = 0; j < current_maxid; ++j) { | |
87 | delete follows_bt[i][j]; | |
88 | } | |
89 | } | |
90 | ||
91 | held.clear(); | |
92 | lock_names.clear(); | |
93 | lock_ids.clear(); | |
92f5a8d4 | 94 | // FIPS zeroization audit 20191115: these memsets are not security related. |
7c673cae FG |
95 | memset((void*)&follows[0][0], 0, current_maxid * MAX_LOCKS/8); |
96 | memset((void*)&follows_bt[0][0], 0, sizeof(BackTrace*) * current_maxid * MAX_LOCKS); | |
7c673cae FG |
97 | } |
98 | pthread_mutex_unlock(&lockdep_mutex); | |
99 | } | |
100 | ||
101 | int lockdep_dump_locks() | |
102 | { | |
103 | pthread_mutex_lock(&lockdep_mutex); | |
b32b8144 FG |
104 | if (!g_lockdep) |
105 | goto out; | |
7c673cae FG |
106 | |
107 | for (ceph::unordered_map<pthread_t, map<int,BackTrace*> >::iterator p = held.begin(); | |
108 | p != held.end(); | |
109 | ++p) { | |
110 | lockdep_dout(0) << "--- thread " << p->first << " ---" << dendl; | |
111 | for (map<int,BackTrace*>::iterator q = p->second.begin(); | |
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; |
7c673cae | 208 | map<int, std::string>::iterator 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 | |
292 | map<int, BackTrace *> &m = held[p]; | |
293 | for (map<int, BackTrace *>::iterator p = m.begin(); | |
294 | p != m.end(); | |
295 | ++p) { | |
296 | if (p->first == id) { | |
11fdf7f2 TL |
297 | if (!recursive) { |
298 | lockdep_dout(0) << "\n"; | |
299 | *_dout << "recursive lock of " << name << " (" << id << ")\n"; | |
300 | BackTrace *bt = new BackTrace(BACKTRACE_SKIP); | |
301 | bt->print(*_dout); | |
302 | if (p->second) { | |
303 | *_dout << "\npreviously locked at\n"; | |
304 | p->second->print(*_dout); | |
305 | } | |
306 | delete bt; | |
307 | *_dout << dendl; | |
308 | ceph_abort(); | |
7c673cae | 309 | } |
7c673cae FG |
310 | } |
311 | else if (!(follows[p->first][id/8] & (1 << (id % 8)))) { | |
312 | // new dependency | |
313 | ||
314 | // did we just create a cycle? | |
315 | if (does_follow(id, p->first)) { | |
316 | BackTrace *bt = new BackTrace(BACKTRACE_SKIP); | |
317 | lockdep_dout(0) << "new dependency " << lock_names[p->first] | |
318 | << " (" << p->first << ") -> " << name << " (" << id << ")" | |
319 | << " creates a cycle at\n"; | |
320 | bt->print(*_dout); | |
321 | *_dout << dendl; | |
322 | ||
323 | lockdep_dout(0) << "btw, i am holding these locks:" << dendl; | |
324 | for (map<int, BackTrace *>::iterator q = m.begin(); | |
325 | q != m.end(); | |
326 | ++q) { | |
327 | lockdep_dout(0) << " " << lock_names[q->first] << " (" << q->first << ")" << dendl; | |
328 | if (q->second) { | |
329 | lockdep_dout(0) << " "; | |
330 | q->second->print(*_dout); | |
331 | *_dout << dendl; | |
332 | } | |
333 | } | |
334 | ||
335 | lockdep_dout(0) << "\n" << dendl; | |
336 | ||
337 | // don't add this dependency, or we'll get aMutex. cycle in the graph, and | |
338 | // does_follow() won't terminate. | |
339 | ||
340 | ceph_abort(); // actually, we should just die here. | |
341 | } else { | |
342 | BackTrace *bt = NULL; | |
343 | if (force_backtrace || lockdep_force_backtrace()) { | |
344 | bt = new BackTrace(BACKTRACE_SKIP); | |
345 | } | |
346 | follows[p->first][id/8] |= 1 << (id % 8); | |
347 | follows_bt[p->first][id] = bt; | |
348 | lockdep_dout(10) << lock_names[p->first] << " -> " << name << " at" << dendl; | |
349 | //bt->print(*_dout); | |
350 | } | |
351 | } | |
352 | } | |
7c673cae FG |
353 | pthread_mutex_unlock(&lockdep_mutex); |
354 | return id; | |
355 | } | |
356 | ||
357 | int lockdep_locked(const char *name, int id, bool force_backtrace) | |
358 | { | |
359 | pthread_t p = pthread_self(); | |
360 | ||
7c673cae | 361 | pthread_mutex_lock(&lockdep_mutex); |
b32b8144 FG |
362 | if (!g_lockdep) |
363 | goto out; | |
364 | if (id < 0) | |
365 | id = _lockdep_register(name); | |
366 | ||
7c673cae FG |
367 | lockdep_dout(20) << "_locked " << name << dendl; |
368 | if (force_backtrace || lockdep_force_backtrace()) | |
369 | held[p][id] = new BackTrace(BACKTRACE_SKIP); | |
370 | else | |
371 | held[p][id] = 0; | |
b32b8144 | 372 | out: |
7c673cae FG |
373 | pthread_mutex_unlock(&lockdep_mutex); |
374 | return id; | |
375 | } | |
376 | ||
377 | int lockdep_will_unlock(const char *name, int id) | |
378 | { | |
379 | pthread_t p = pthread_self(); | |
380 | ||
381 | if (id < 0) { | |
382 | //id = lockdep_register(name); | |
11fdf7f2 | 383 | ceph_assert(id == -1); |
7c673cae FG |
384 | return id; |
385 | } | |
386 | ||
387 | pthread_mutex_lock(&lockdep_mutex); | |
b32b8144 FG |
388 | if (!g_lockdep) |
389 | goto out; | |
7c673cae FG |
390 | lockdep_dout(20) << "_will_unlock " << name << dendl; |
391 | ||
392 | // don't assert.. lockdep may be enabled at any point in time | |
393 | //assert(held.count(p)); | |
394 | //assert(held[p].count(id)); | |
395 | ||
396 | delete held[p][id]; | |
397 | held[p].erase(id); | |
b32b8144 | 398 | out: |
7c673cae FG |
399 | pthread_mutex_unlock(&lockdep_mutex); |
400 | return id; | |
401 | } | |
402 | ||
403 |