]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/lockdep.cc
import new upstream nautilus stable release 14.2.8
[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"
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 25bool g_lockdep;
7c673cae
FG
26struct lockdep_stopper_t {
27 // disable lockdep when this module destructs.
28 ~lockdep_stopper_t() {
29 g_lockdep = 0;
30 }
31};
32static pthread_mutex_t lockdep_mutex = PTHREAD_MUTEX_INITIALIZER;
33static CephContext *g_lockdep_ceph_ctx = NULL;
34static lockdep_stopper_t lockdep_stopper;
35static ceph::unordered_map<std::string, int> lock_ids;
36static map<int, std::string> lock_names;
37static map<int, int> lock_refs;
38static char free_ids[MAX_LOCKS/8]; // bit set = free
39static ceph::unordered_map<pthread_t, map<int,BackTrace*> > held;
40static char follows[MAX_LOCKS][MAX_LOCKS/8]; // follows[a][b] means b taken after a
41static BackTrace *follows_bt[MAX_LOCKS][MAX_LOCKS];
42unsigned current_maxid;
b32b8144
FG
43int last_freed_id = -1;
44static bool free_ids_inited;
7c673cae
FG
45
46static 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 **********/
53void 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
75void 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
101int 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 120out:
7c673cae
FG
121 pthread_mutex_unlock(&lockdep_mutex);
122 return 0;
123}
124
125int 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 156static 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
189int 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
199void 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?
245static 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
275int 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
357int 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 372out:
7c673cae
FG
373 pthread_mutex_unlock(&lockdep_mutex);
374 return id;
375}
376
377int 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 398out:
7c673cae
FG
399 pthread_mutex_unlock(&lockdep_mutex);
400 return id;
401}
402
403