]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/lockdep.cc
update sources to v12.2.3
[ceph.git] / ceph / src / common / lockdep.cc
CommitLineData
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.
19namespace 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 36bool g_lockdep;
7c673cae
FG
37struct lockdep_stopper_t {
38 // disable lockdep when this module destructs.
39 ~lockdep_stopper_t() {
40 g_lockdep = 0;
41 }
42};
43static pthread_mutex_t lockdep_mutex = PTHREAD_MUTEX_INITIALIZER;
44static CephContext *g_lockdep_ceph_ctx = NULL;
45static lockdep_stopper_t lockdep_stopper;
46static ceph::unordered_map<std::string, int> lock_ids;
47static map<int, std::string> lock_names;
48static map<int, int> lock_refs;
49static char free_ids[MAX_LOCKS/8]; // bit set = free
50static ceph::unordered_map<pthread_t, map<int,BackTrace*> > held;
51static char follows[MAX_LOCKS][MAX_LOCKS/8]; // follows[a][b] means b taken after a
52static BackTrace *follows_bt[MAX_LOCKS][MAX_LOCKS];
53unsigned current_maxid;
b32b8144
FG
54int last_freed_id = -1;
55static bool free_ids_inited;
7c673cae
FG
56
57static 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 **********/
64void 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
85void 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
110int 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 129out:
7c673cae
FG
130 pthread_mutex_unlock(&lockdep_mutex);
131 return 0;
132}
133
134int 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 165static 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
198int 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
208void 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?
253static 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
283int 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
362int 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 377out:
7c673cae
FG
378 pthread_mutex_unlock(&lockdep_mutex);
379 return id;
380}
381
382int 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 403out:
7c673cae
FG
404 pthread_mutex_unlock(&lockdep_mutex);
405 return id;
406}
407
408