]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/lockdep.cc
import ceph quincy 17.2.4
[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"
20effc67 15#include <bitset>
11fdf7f2 16#include "common/ceph_context.h"
7c673cae 17#include "common/dout.h"
7c673cae 18#include "common/valgrind.h"
7c673cae 19
7c673cae
FG
20/******* Constants **********/
21#define lockdep_dout(v) lsubdout(g_lockdep_ceph_ctx, lockdep, v)
7c673cae
FG
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};
f67539c2
TL
32
33
7c673cae
FG
34static pthread_mutex_t lockdep_mutex = PTHREAD_MUTEX_INITIALIZER;
35static CephContext *g_lockdep_ceph_ctx = NULL;
36static lockdep_stopper_t lockdep_stopper;
37static ceph::unordered_map<std::string, int> lock_ids;
f67539c2
TL
38static std::map<int, std::string> lock_names;
39static std::map<int, int> lock_refs;
20effc67
TL
40static constexpr size_t MAX_LOCKS = 128 * 1024; // increase me as needed
41static std::bitset<MAX_LOCKS> free_ids; // bit set = free
f67539c2 42static ceph::unordered_map<pthread_t, std::map<int,ceph::BackTrace*> > held;
20effc67
TL
43static constexpr size_t NR_LOCKS = 4096; // the initial number of locks
44static std::vector<std::bitset<MAX_LOCKS>> follows(NR_LOCKS); // follows[a][b] means b taken after a
45static std::vector<std::map<int,ceph::BackTrace *>> follows_bt(NR_LOCKS);
46// upper bound of lock id
7c673cae 47unsigned current_maxid;
b32b8144
FG
48int last_freed_id = -1;
49static bool free_ids_inited;
7c673cae
FG
50
51static bool lockdep_force_backtrace()
52{
53 return (g_lockdep_ceph_ctx != NULL &&
54 g_lockdep_ceph_ctx->_conf->lockdep_force_backtrace);
55}
56
57/******* Functions **********/
58void lockdep_register_ceph_context(CephContext *cct)
59{
60 static_assert((MAX_LOCKS > 0) && (MAX_LOCKS % 8 == 0),
61 "lockdep's MAX_LOCKS needs to be divisible by 8 to operate correctly.");
62 pthread_mutex_lock(&lockdep_mutex);
63 if (g_lockdep_ceph_ctx == NULL) {
64 ANNOTATE_BENIGN_RACE_SIZED(&g_lockdep_ceph_ctx, sizeof(g_lockdep_ceph_ctx),
65 "lockdep cct");
66 ANNOTATE_BENIGN_RACE_SIZED(&g_lockdep, sizeof(g_lockdep),
67 "lockdep enabled");
68 g_lockdep = true;
69 g_lockdep_ceph_ctx = cct;
70 lockdep_dout(1) << "lockdep start" << dendl;
b32b8144
FG
71 if (!free_ids_inited) {
72 free_ids_inited = true;
92f5a8d4 73 // FIPS zeroization audit 20191115: this memset is not security related.
20effc67 74 free_ids.set();
b32b8144 75 }
7c673cae
FG
76 }
77 pthread_mutex_unlock(&lockdep_mutex);
78}
79
80void lockdep_unregister_ceph_context(CephContext *cct)
81{
82 pthread_mutex_lock(&lockdep_mutex);
83 if (cct == g_lockdep_ceph_ctx) {
84 lockdep_dout(1) << "lockdep stop" << dendl;
85 // this cct is going away; shut it down!
86 g_lockdep = false;
87 g_lockdep_ceph_ctx = NULL;
88
89 // blow away all of our state, too, in case it starts up again.
90 for (unsigned i = 0; i < current_maxid; ++i) {
91 for (unsigned j = 0; j < current_maxid; ++j) {
92 delete follows_bt[i][j];
93 }
94 }
95
96 held.clear();
97 lock_names.clear();
98 lock_ids.clear();
20effc67
TL
99 std::for_each(follows.begin(), std::next(follows.begin(), current_maxid),
100 [](auto& follow) { follow.reset(); });
101 std::for_each(follows_bt.begin(), std::next(follows_bt.begin(), current_maxid),
102 [](auto& follow_bt) { follow_bt = {}; });
7c673cae
FG
103 }
104 pthread_mutex_unlock(&lockdep_mutex);
105}
106
107int lockdep_dump_locks()
108{
109 pthread_mutex_lock(&lockdep_mutex);
b32b8144
FG
110 if (!g_lockdep)
111 goto out;
7c673cae 112
f67539c2 113 for (auto p = held.begin(); p != held.end(); ++p) {
7c673cae 114 lockdep_dout(0) << "--- thread " << p->first << " ---" << dendl;
f67539c2 115 for (auto q = p->second.begin();
7c673cae
FG
116 q != p->second.end();
117 ++q) {
118 lockdep_dout(0) << " * " << lock_names[q->first] << "\n";
119 if (q->second)
120 *_dout << *(q->second);
121 *_dout << dendl;
122 }
123 }
b32b8144 124out:
7c673cae
FG
125 pthread_mutex_unlock(&lockdep_mutex);
126 return 0;
127}
128
129int lockdep_get_free_id(void)
130{
131 // if there's id known to be freed lately, reuse it
20effc67
TL
132 if (last_freed_id >= 0 &&
133 free_ids.test(last_freed_id)) {
7c673cae
FG
134 int tmp = last_freed_id;
135 last_freed_id = -1;
20effc67 136 free_ids.reset(tmp);
7c673cae
FG
137 lockdep_dout(1) << "lockdep reusing last freed id " << tmp << dendl;
138 return tmp;
139 }
140
141 // walk through entire array and locate nonzero char, then find
142 // actual bit.
20effc67
TL
143 for (size_t i = 0; i < free_ids.size(); ++i) {
144 if (free_ids.test(i)) {
145 free_ids.reset(i);
146 return i;
7c673cae
FG
147 }
148 }
149
150 // not found
151 lockdep_dout(0) << "failing miserably..." << dendl;
152 return -1;
153}
154
b32b8144 155static int _lockdep_register(const char *name)
7c673cae 156{
b32b8144 157 int id = -1;
7c673cae 158
b32b8144
FG
159 if (!g_lockdep)
160 return id;
7c673cae
FG
161 ceph::unordered_map<std::string, int>::iterator p = lock_ids.find(name);
162 if (p == lock_ids.end()) {
163 id = lockdep_get_free_id();
164 if (id < 0) {
165 lockdep_dout(0) << "ERROR OUT OF IDS .. have 0"
166 << " max " << MAX_LOCKS << dendl;
167 for (auto& p : lock_names) {
168 lockdep_dout(0) << " lock " << p.first << " " << p.second << dendl;
169 }
11fdf7f2 170 ceph_abort();
7c673cae
FG
171 }
172 if (current_maxid <= (unsigned)id) {
20effc67
TL
173 current_maxid = (unsigned)id + 1;
174 if (current_maxid == follows.size()) {
175 follows.resize(current_maxid + 1);
176 follows_bt.resize(current_maxid + 1);
177 }
7c673cae
FG
178 }
179 lock_ids[name] = id;
180 lock_names[id] = name;
181 lockdep_dout(10) << "registered '" << name << "' as " << id << dendl;
182 } else {
183 id = p->second;
184 lockdep_dout(20) << "had '" << name << "' as " << id << dendl;
185 }
186
187 ++lock_refs[id];
7c673cae
FG
188
189 return id;
190}
191
b32b8144
FG
192int lockdep_register(const char *name)
193{
194 int id;
195
196 pthread_mutex_lock(&lockdep_mutex);
197 id = _lockdep_register(name);
198 pthread_mutex_unlock(&lockdep_mutex);
199 return id;
200}
201
7c673cae
FG
202void lockdep_unregister(int id)
203{
204 if (id < 0) {
205 return;
206 }
207
208 pthread_mutex_lock(&lockdep_mutex);
209
b32b8144 210 std::string name;
f67539c2 211 auto p = lock_names.find(id);
b32b8144 212 if (p == lock_names.end())
11fdf7f2 213 name = "unknown" ;
b32b8144
FG
214 else
215 name = p->second;
7c673cae
FG
216
217 int &refs = lock_refs[id];
218 if (--refs == 0) {
b32b8144
FG
219 if (p != lock_names.end()) {
220 // reset dependency ordering
20effc67 221 follows[id].reset();
b32b8144
FG
222 for (unsigned i=0; i<current_maxid; ++i) {
223 delete follows_bt[id][i];
224 follows_bt[id][i] = NULL;
225
226 delete follows_bt[i][id];
227 follows_bt[i][id] = NULL;
20effc67 228 follows[i].reset(id);
b32b8144 229 }
7c673cae 230
b32b8144
FG
231 lockdep_dout(10) << "unregistered '" << name << "' from " << id << dendl;
232 lock_ids.erase(p->second);
233 lock_names.erase(id);
234 }
7c673cae 235 lock_refs.erase(id);
20effc67 236 free_ids.set(id);
b32b8144
FG
237 last_freed_id = id;
238 } else if (g_lockdep) {
239 lockdep_dout(20) << "have " << refs << " of '" << name << "' " <<
240 "from " << id << dendl;
7c673cae
FG
241 }
242 pthread_mutex_unlock(&lockdep_mutex);
243}
244
245
246// does b follow a?
247static bool does_follow(int a, int b)
248{
20effc67 249 if (follows[a].test(b)) {
7c673cae
FG
250 lockdep_dout(0) << "\n";
251 *_dout << "------------------------------------" << "\n";
252 *_dout << "existing dependency " << lock_names[a] << " (" << a << ") -> "
253 << lock_names[b] << " (" << b << ") at:\n";
254 if (follows_bt[a][b]) {
255 follows_bt[a][b]->print(*_dout);
256 }
257 *_dout << dendl;
258 return true;
259 }
260
261 for (unsigned i=0; i<current_maxid; i++) {
20effc67 262 if (follows[a].test(i) &&
7c673cae
FG
263 does_follow(i, b)) {
264 lockdep_dout(0) << "existing intermediate dependency " << lock_names[a]
265 << " (" << a << ") -> " << lock_names[i] << " (" << i << ") at:\n";
266 if (follows_bt[a][i]) {
267 follows_bt[a][i]->print(*_dout);
268 }
269 *_dout << dendl;
270 return true;
271 }
272 }
273
274 return false;
275}
276
11fdf7f2
TL
277int lockdep_will_lock(const char *name, int id, bool force_backtrace,
278 bool recursive)
7c673cae
FG
279{
280 pthread_t p = pthread_self();
7c673cae
FG
281
282 pthread_mutex_lock(&lockdep_mutex);
b32b8144
FG
283 if (!g_lockdep) {
284 pthread_mutex_unlock(&lockdep_mutex);
285 return id;
286 }
287
288 if (id < 0)
289 id = _lockdep_register(name);
290
7c673cae
FG
291 lockdep_dout(20) << "_will_lock " << name << " (" << id << ")" << dendl;
292
293 // check dependency graph
f67539c2
TL
294 auto& m = held[p];
295 for (auto p = m.begin(); p != m.end(); ++p) {
7c673cae 296 if (p->first == id) {
11fdf7f2
TL
297 if (!recursive) {
298 lockdep_dout(0) << "\n";
299 *_dout << "recursive lock of " << name << " (" << id << ")\n";
20effc67 300 auto bt = new ceph::ClibBackTrace(BACKTRACE_SKIP);
11fdf7f2
TL
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 }
20effc67 310 } else if (!follows[p->first].test(id)) {
7c673cae
FG
311 // new dependency
312
313 // did we just create a cycle?
314 if (does_follow(id, p->first)) {
20effc67 315 auto bt = new ceph::ClibBackTrace(BACKTRACE_SKIP);
7c673cae
FG
316 lockdep_dout(0) << "new dependency " << lock_names[p->first]
317 << " (" << p->first << ") -> " << name << " (" << id << ")"
318 << " creates a cycle at\n";
319 bt->print(*_dout);
320 *_dout << dendl;
321
322 lockdep_dout(0) << "btw, i am holding these locks:" << dendl;
f67539c2 323 for (auto q = m.begin(); q != m.end(); ++q) {
7c673cae
FG
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 {
f67539c2 339 ceph::BackTrace* bt = NULL;
7c673cae 340 if (force_backtrace || lockdep_force_backtrace()) {
20effc67 341 bt = new ceph::ClibBackTrace(BACKTRACE_SKIP);
7c673cae 342 }
20effc67 343 follows[p->first].set(id);
7c673cae
FG
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
354int 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())
20effc67 366 held[p][id] = new ceph::ClibBackTrace(BACKTRACE_SKIP);
7c673cae
FG
367 else
368 held[p][id] = 0;
b32b8144 369out:
7c673cae
FG
370 pthread_mutex_unlock(&lockdep_mutex);
371 return id;
372}
373
374int 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 395out:
7c673cae
FG
396 pthread_mutex_unlock(&lockdep_mutex);
397 return id;
398}
399
400