diff options
Diffstat (limited to 'fs/ocfs2/dlm/dlmdebug.c')
-rw-r--r-- | fs/ocfs2/dlm/dlmdebug.c | 911 |
1 files changed, 839 insertions, 72 deletions
diff --git a/fs/ocfs2/dlm/dlmdebug.c b/fs/ocfs2/dlm/dlmdebug.c index 64239b37e5d4..5f6d858770a2 100644 --- a/fs/ocfs2/dlm/dlmdebug.c +++ b/fs/ocfs2/dlm/dlmdebug.c | |||
@@ -5,7 +5,7 @@ | |||
5 | * | 5 | * |
6 | * debug functionality for the dlm | 6 | * debug functionality for the dlm |
7 | * | 7 | * |
8 | * Copyright (C) 2004 Oracle. All rights reserved. | 8 | * Copyright (C) 2004, 2008 Oracle. All rights reserved. |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or | 10 | * This program is free software; you can redistribute it and/or |
11 | * modify it under the terms of the GNU General Public | 11 | * modify it under the terms of the GNU General Public |
@@ -30,6 +30,7 @@ | |||
30 | #include <linux/utsname.h> | 30 | #include <linux/utsname.h> |
31 | #include <linux/sysctl.h> | 31 | #include <linux/sysctl.h> |
32 | #include <linux/spinlock.h> | 32 | #include <linux/spinlock.h> |
33 | #include <linux/debugfs.h> | ||
33 | 34 | ||
34 | #include "cluster/heartbeat.h" | 35 | #include "cluster/heartbeat.h" |
35 | #include "cluster/nodemanager.h" | 36 | #include "cluster/nodemanager.h" |
@@ -37,17 +38,16 @@ | |||
37 | 38 | ||
38 | #include "dlmapi.h" | 39 | #include "dlmapi.h" |
39 | #include "dlmcommon.h" | 40 | #include "dlmcommon.h" |
40 | |||
41 | #include "dlmdomain.h" | 41 | #include "dlmdomain.h" |
42 | #include "dlmdebug.h" | ||
42 | 43 | ||
43 | #define MLOG_MASK_PREFIX ML_DLM | 44 | #define MLOG_MASK_PREFIX ML_DLM |
44 | #include "cluster/masklog.h" | 45 | #include "cluster/masklog.h" |
45 | 46 | ||
47 | int stringify_lockname(const char *lockname, int locklen, char *buf, int len); | ||
48 | |||
46 | void dlm_print_one_lock_resource(struct dlm_lock_resource *res) | 49 | void dlm_print_one_lock_resource(struct dlm_lock_resource *res) |
47 | { | 50 | { |
48 | mlog(ML_NOTICE, "lockres: %.*s, owner=%u, state=%u\n", | ||
49 | res->lockname.len, res->lockname.name, | ||
50 | res->owner, res->state); | ||
51 | spin_lock(&res->spinlock); | 51 | spin_lock(&res->spinlock); |
52 | __dlm_print_one_lock_resource(res); | 52 | __dlm_print_one_lock_resource(res); |
53 | spin_unlock(&res->spinlock); | 53 | spin_unlock(&res->spinlock); |
@@ -58,7 +58,7 @@ static void dlm_print_lockres_refmap(struct dlm_lock_resource *res) | |||
58 | int bit; | 58 | int bit; |
59 | assert_spin_locked(&res->spinlock); | 59 | assert_spin_locked(&res->spinlock); |
60 | 60 | ||
61 | mlog(ML_NOTICE, " refmap nodes: [ "); | 61 | printk(" refmap nodes: [ "); |
62 | bit = 0; | 62 | bit = 0; |
63 | while (1) { | 63 | while (1) { |
64 | bit = find_next_bit(res->refmap, O2NM_MAX_NODES, bit); | 64 | bit = find_next_bit(res->refmap, O2NM_MAX_NODES, bit); |
@@ -70,63 +70,66 @@ static void dlm_print_lockres_refmap(struct dlm_lock_resource *res) | |||
70 | printk("], inflight=%u\n", res->inflight_locks); | 70 | printk("], inflight=%u\n", res->inflight_locks); |
71 | } | 71 | } |
72 | 72 | ||
73 | static void __dlm_print_lock(struct dlm_lock *lock) | ||
74 | { | ||
75 | spin_lock(&lock->spinlock); | ||
76 | |||
77 | printk(" type=%d, conv=%d, node=%u, cookie=%u:%llu, " | ||
78 | "ref=%u, ast=(empty=%c,pend=%c), bast=(empty=%c,pend=%c), " | ||
79 | "pending=(conv=%c,lock=%c,cancel=%c,unlock=%c)\n", | ||
80 | lock->ml.type, lock->ml.convert_type, lock->ml.node, | ||
81 | dlm_get_lock_cookie_node(be64_to_cpu(lock->ml.cookie)), | ||
82 | dlm_get_lock_cookie_seq(be64_to_cpu(lock->ml.cookie)), | ||
83 | atomic_read(&lock->lock_refs.refcount), | ||
84 | (list_empty(&lock->ast_list) ? 'y' : 'n'), | ||
85 | (lock->ast_pending ? 'y' : 'n'), | ||
86 | (list_empty(&lock->bast_list) ? 'y' : 'n'), | ||
87 | (lock->bast_pending ? 'y' : 'n'), | ||
88 | (lock->convert_pending ? 'y' : 'n'), | ||
89 | (lock->lock_pending ? 'y' : 'n'), | ||
90 | (lock->cancel_pending ? 'y' : 'n'), | ||
91 | (lock->unlock_pending ? 'y' : 'n')); | ||
92 | |||
93 | spin_unlock(&lock->spinlock); | ||
94 | } | ||
95 | |||
73 | void __dlm_print_one_lock_resource(struct dlm_lock_resource *res) | 96 | void __dlm_print_one_lock_resource(struct dlm_lock_resource *res) |
74 | { | 97 | { |
75 | struct list_head *iter2; | 98 | struct list_head *iter2; |
76 | struct dlm_lock *lock; | 99 | struct dlm_lock *lock; |
100 | char buf[DLM_LOCKID_NAME_MAX]; | ||
77 | 101 | ||
78 | assert_spin_locked(&res->spinlock); | 102 | assert_spin_locked(&res->spinlock); |
79 | 103 | ||
80 | mlog(ML_NOTICE, "lockres: %.*s, owner=%u, state=%u\n", | 104 | stringify_lockname(res->lockname.name, res->lockname.len, |
81 | res->lockname.len, res->lockname.name, | 105 | buf, sizeof(buf) - 1); |
82 | res->owner, res->state); | 106 | printk("lockres: %s, owner=%u, state=%u\n", |
83 | mlog(ML_NOTICE, " last used: %lu, on purge list: %s\n", | 107 | buf, res->owner, res->state); |
84 | res->last_used, list_empty(&res->purge) ? "no" : "yes"); | 108 | printk(" last used: %lu, refcnt: %u, on purge list: %s\n", |
109 | res->last_used, atomic_read(&res->refs.refcount), | ||
110 | list_empty(&res->purge) ? "no" : "yes"); | ||
111 | printk(" on dirty list: %s, on reco list: %s, " | ||
112 | "migrating pending: %s\n", | ||
113 | list_empty(&res->dirty) ? "no" : "yes", | ||
114 | list_empty(&res->recovering) ? "no" : "yes", | ||
115 | res->migration_pending ? "yes" : "no"); | ||
116 | printk(" inflight locks: %d, asts reserved: %d\n", | ||
117 | res->inflight_locks, atomic_read(&res->asts_reserved)); | ||
85 | dlm_print_lockres_refmap(res); | 118 | dlm_print_lockres_refmap(res); |
86 | mlog(ML_NOTICE, " granted queue: \n"); | 119 | printk(" granted queue:\n"); |
87 | list_for_each(iter2, &res->granted) { | 120 | list_for_each(iter2, &res->granted) { |
88 | lock = list_entry(iter2, struct dlm_lock, list); | 121 | lock = list_entry(iter2, struct dlm_lock, list); |
89 | spin_lock(&lock->spinlock); | 122 | __dlm_print_lock(lock); |
90 | mlog(ML_NOTICE, " type=%d, conv=%d, node=%u, " | ||
91 | "cookie=%u:%llu, ast=(empty=%c,pend=%c), bast=(empty=%c,pend=%c)\n", | ||
92 | lock->ml.type, lock->ml.convert_type, lock->ml.node, | ||
93 | dlm_get_lock_cookie_node(be64_to_cpu(lock->ml.cookie)), | ||
94 | dlm_get_lock_cookie_seq(be64_to_cpu(lock->ml.cookie)), | ||
95 | list_empty(&lock->ast_list) ? 'y' : 'n', | ||
96 | lock->ast_pending ? 'y' : 'n', | ||
97 | list_empty(&lock->bast_list) ? 'y' : 'n', | ||
98 | lock->bast_pending ? 'y' : 'n'); | ||
99 | spin_unlock(&lock->spinlock); | ||
100 | } | 123 | } |
101 | mlog(ML_NOTICE, " converting queue: \n"); | 124 | printk(" converting queue:\n"); |
102 | list_for_each(iter2, &res->converting) { | 125 | list_for_each(iter2, &res->converting) { |
103 | lock = list_entry(iter2, struct dlm_lock, list); | 126 | lock = list_entry(iter2, struct dlm_lock, list); |
104 | spin_lock(&lock->spinlock); | 127 | __dlm_print_lock(lock); |
105 | mlog(ML_NOTICE, " type=%d, conv=%d, node=%u, " | ||
106 | "cookie=%u:%llu, ast=(empty=%c,pend=%c), bast=(empty=%c,pend=%c)\n", | ||
107 | lock->ml.type, lock->ml.convert_type, lock->ml.node, | ||
108 | dlm_get_lock_cookie_node(be64_to_cpu(lock->ml.cookie)), | ||
109 | dlm_get_lock_cookie_seq(be64_to_cpu(lock->ml.cookie)), | ||
110 | list_empty(&lock->ast_list) ? 'y' : 'n', | ||
111 | lock->ast_pending ? 'y' : 'n', | ||
112 | list_empty(&lock->bast_list) ? 'y' : 'n', | ||
113 | lock->bast_pending ? 'y' : 'n'); | ||
114 | spin_unlock(&lock->spinlock); | ||
115 | } | 128 | } |
116 | mlog(ML_NOTICE, " blocked queue: \n"); | 129 | printk(" blocked queue:\n"); |
117 | list_for_each(iter2, &res->blocked) { | 130 | list_for_each(iter2, &res->blocked) { |
118 | lock = list_entry(iter2, struct dlm_lock, list); | 131 | lock = list_entry(iter2, struct dlm_lock, list); |
119 | spin_lock(&lock->spinlock); | 132 | __dlm_print_lock(lock); |
120 | mlog(ML_NOTICE, " type=%d, conv=%d, node=%u, " | ||
121 | "cookie=%u:%llu, ast=(empty=%c,pend=%c), bast=(empty=%c,pend=%c)\n", | ||
122 | lock->ml.type, lock->ml.convert_type, lock->ml.node, | ||
123 | dlm_get_lock_cookie_node(be64_to_cpu(lock->ml.cookie)), | ||
124 | dlm_get_lock_cookie_seq(be64_to_cpu(lock->ml.cookie)), | ||
125 | list_empty(&lock->ast_list) ? 'y' : 'n', | ||
126 | lock->ast_pending ? 'y' : 'n', | ||
127 | list_empty(&lock->bast_list) ? 'y' : 'n', | ||
128 | lock->bast_pending ? 'y' : 'n'); | ||
129 | spin_unlock(&lock->spinlock); | ||
130 | } | 133 | } |
131 | } | 134 | } |
132 | 135 | ||
@@ -136,31 +139,6 @@ void dlm_print_one_lock(struct dlm_lock *lockid) | |||
136 | } | 139 | } |
137 | EXPORT_SYMBOL_GPL(dlm_print_one_lock); | 140 | EXPORT_SYMBOL_GPL(dlm_print_one_lock); |
138 | 141 | ||
139 | #if 0 | ||
140 | void dlm_dump_lock_resources(struct dlm_ctxt *dlm) | ||
141 | { | ||
142 | struct dlm_lock_resource *res; | ||
143 | struct hlist_node *iter; | ||
144 | struct hlist_head *bucket; | ||
145 | int i; | ||
146 | |||
147 | mlog(ML_NOTICE, "struct dlm_ctxt: %s, node=%u, key=%u\n", | ||
148 | dlm->name, dlm->node_num, dlm->key); | ||
149 | if (!dlm || !dlm->name) { | ||
150 | mlog(ML_ERROR, "dlm=%p\n", dlm); | ||
151 | return; | ||
152 | } | ||
153 | |||
154 | spin_lock(&dlm->spinlock); | ||
155 | for (i=0; i<DLM_HASH_BUCKETS; i++) { | ||
156 | bucket = dlm_lockres_hash(dlm, i); | ||
157 | hlist_for_each_entry(res, iter, bucket, hash_node) | ||
158 | dlm_print_one_lock_resource(res); | ||
159 | } | ||
160 | spin_unlock(&dlm->spinlock); | ||
161 | } | ||
162 | #endif /* 0 */ | ||
163 | |||
164 | static const char *dlm_errnames[] = { | 142 | static const char *dlm_errnames[] = { |
165 | [DLM_NORMAL] = "DLM_NORMAL", | 143 | [DLM_NORMAL] = "DLM_NORMAL", |
166 | [DLM_GRANTED] = "DLM_GRANTED", | 144 | [DLM_GRANTED] = "DLM_GRANTED", |
@@ -266,3 +244,792 @@ const char *dlm_errname(enum dlm_status err) | |||
266 | return dlm_errnames[err]; | 244 | return dlm_errnames[err]; |
267 | } | 245 | } |
268 | EXPORT_SYMBOL_GPL(dlm_errname); | 246 | EXPORT_SYMBOL_GPL(dlm_errname); |
247 | |||
248 | /* NOTE: This function converts a lockname into a string. It uses knowledge | ||
249 | * of the format of the lockname that should be outside the purview of the dlm. | ||
250 | * We are adding only to make dlm debugging slightly easier. | ||
251 | * | ||
252 | * For more on lockname formats, please refer to dlmglue.c and ocfs2_lockid.h. | ||
253 | */ | ||
254 | int stringify_lockname(const char *lockname, int locklen, char *buf, int len) | ||
255 | { | ||
256 | int out = 0; | ||
257 | __be64 inode_blkno_be; | ||
258 | |||
259 | #define OCFS2_DENTRY_LOCK_INO_START 18 | ||
260 | if (*lockname == 'N') { | ||
261 | memcpy((__be64 *)&inode_blkno_be, | ||
262 | (char *)&lockname[OCFS2_DENTRY_LOCK_INO_START], | ||
263 | sizeof(__be64)); | ||
264 | out += snprintf(buf + out, len - out, "%.*s%08x", | ||
265 | OCFS2_DENTRY_LOCK_INO_START - 1, lockname, | ||
266 | (unsigned int)be64_to_cpu(inode_blkno_be)); | ||
267 | } else | ||
268 | out += snprintf(buf + out, len - out, "%.*s", | ||
269 | locklen, lockname); | ||
270 | return out; | ||
271 | } | ||
272 | |||
273 | static int stringify_nodemap(unsigned long *nodemap, int maxnodes, | ||
274 | char *buf, int len) | ||
275 | { | ||
276 | int out = 0; | ||
277 | int i = -1; | ||
278 | |||
279 | while ((i = find_next_bit(nodemap, maxnodes, i + 1)) < maxnodes) | ||
280 | out += snprintf(buf + out, len - out, "%d ", i); | ||
281 | |||
282 | return out; | ||
283 | } | ||
284 | |||
285 | static int dump_mle(struct dlm_master_list_entry *mle, char *buf, int len) | ||
286 | { | ||
287 | int out = 0; | ||
288 | unsigned int namelen; | ||
289 | const char *name; | ||
290 | char *mle_type; | ||
291 | |||
292 | if (mle->type != DLM_MLE_MASTER) { | ||
293 | namelen = mle->u.name.len; | ||
294 | name = mle->u.name.name; | ||
295 | } else { | ||
296 | namelen = mle->u.res->lockname.len; | ||
297 | name = mle->u.res->lockname.name; | ||
298 | } | ||
299 | |||
300 | if (mle->type == DLM_MLE_BLOCK) | ||
301 | mle_type = "BLK"; | ||
302 | else if (mle->type == DLM_MLE_MASTER) | ||
303 | mle_type = "MAS"; | ||
304 | else | ||
305 | mle_type = "MIG"; | ||
306 | |||
307 | out += stringify_lockname(name, namelen, buf + out, len - out); | ||
308 | out += snprintf(buf + out, len - out, | ||
309 | "\t%3s\tmas=%3u\tnew=%3u\tevt=%1d\tuse=%1d\tref=%3d\n", | ||
310 | mle_type, mle->master, mle->new_master, | ||
311 | !list_empty(&mle->hb_events), | ||
312 | !!mle->inuse, | ||
313 | atomic_read(&mle->mle_refs.refcount)); | ||
314 | |||
315 | out += snprintf(buf + out, len - out, "Maybe="); | ||
316 | out += stringify_nodemap(mle->maybe_map, O2NM_MAX_NODES, | ||
317 | buf + out, len - out); | ||
318 | out += snprintf(buf + out, len - out, "\n"); | ||
319 | |||
320 | out += snprintf(buf + out, len - out, "Vote="); | ||
321 | out += stringify_nodemap(mle->vote_map, O2NM_MAX_NODES, | ||
322 | buf + out, len - out); | ||
323 | out += snprintf(buf + out, len - out, "\n"); | ||
324 | |||
325 | out += snprintf(buf + out, len - out, "Response="); | ||
326 | out += stringify_nodemap(mle->response_map, O2NM_MAX_NODES, | ||
327 | buf + out, len - out); | ||
328 | out += snprintf(buf + out, len - out, "\n"); | ||
329 | |||
330 | out += snprintf(buf + out, len - out, "Node="); | ||
331 | out += stringify_nodemap(mle->node_map, O2NM_MAX_NODES, | ||
332 | buf + out, len - out); | ||
333 | out += snprintf(buf + out, len - out, "\n"); | ||
334 | |||
335 | out += snprintf(buf + out, len - out, "\n"); | ||
336 | |||
337 | return out; | ||
338 | } | ||
339 | |||
340 | void dlm_print_one_mle(struct dlm_master_list_entry *mle) | ||
341 | { | ||
342 | char *buf; | ||
343 | |||
344 | buf = (char *) get_zeroed_page(GFP_NOFS); | ||
345 | if (buf) { | ||
346 | dump_mle(mle, buf, PAGE_SIZE - 1); | ||
347 | free_page((unsigned long)buf); | ||
348 | } | ||
349 | } | ||
350 | |||
351 | #ifdef CONFIG_DEBUG_FS | ||
352 | |||
353 | static struct dentry *dlm_debugfs_root = NULL; | ||
354 | |||
355 | #define DLM_DEBUGFS_DIR "o2dlm" | ||
356 | #define DLM_DEBUGFS_DLM_STATE "dlm_state" | ||
357 | #define DLM_DEBUGFS_LOCKING_STATE "locking_state" | ||
358 | #define DLM_DEBUGFS_MLE_STATE "mle_state" | ||
359 | #define DLM_DEBUGFS_PURGE_LIST "purge_list" | ||
360 | |||
361 | /* begin - utils funcs */ | ||
362 | static void dlm_debug_free(struct kref *kref) | ||
363 | { | ||
364 | struct dlm_debug_ctxt *dc; | ||
365 | |||
366 | dc = container_of(kref, struct dlm_debug_ctxt, debug_refcnt); | ||
367 | |||
368 | kfree(dc); | ||
369 | } | ||
370 | |||
371 | void dlm_debug_put(struct dlm_debug_ctxt *dc) | ||
372 | { | ||
373 | if (dc) | ||
374 | kref_put(&dc->debug_refcnt, dlm_debug_free); | ||
375 | } | ||
376 | |||
377 | static void dlm_debug_get(struct dlm_debug_ctxt *dc) | ||
378 | { | ||
379 | kref_get(&dc->debug_refcnt); | ||
380 | } | ||
381 | |||
382 | static struct debug_buffer *debug_buffer_allocate(void) | ||
383 | { | ||
384 | struct debug_buffer *db = NULL; | ||
385 | |||
386 | db = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); | ||
387 | if (!db) | ||
388 | goto bail; | ||
389 | |||
390 | db->len = PAGE_SIZE; | ||
391 | db->buf = kmalloc(db->len, GFP_KERNEL); | ||
392 | if (!db->buf) | ||
393 | goto bail; | ||
394 | |||
395 | return db; | ||
396 | bail: | ||
397 | kfree(db); | ||
398 | return NULL; | ||
399 | } | ||
400 | |||
401 | static ssize_t debug_buffer_read(struct file *file, char __user *buf, | ||
402 | size_t nbytes, loff_t *ppos) | ||
403 | { | ||
404 | struct debug_buffer *db = file->private_data; | ||
405 | |||
406 | return simple_read_from_buffer(buf, nbytes, ppos, db->buf, db->len); | ||
407 | } | ||
408 | |||
409 | static loff_t debug_buffer_llseek(struct file *file, loff_t off, int whence) | ||
410 | { | ||
411 | struct debug_buffer *db = file->private_data; | ||
412 | loff_t new = -1; | ||
413 | |||
414 | switch (whence) { | ||
415 | case 0: | ||
416 | new = off; | ||
417 | break; | ||
418 | case 1: | ||
419 | new = file->f_pos + off; | ||
420 | break; | ||
421 | } | ||
422 | |||
423 | if (new < 0 || new > db->len) | ||
424 | return -EINVAL; | ||
425 | |||
426 | return (file->f_pos = new); | ||
427 | } | ||
428 | |||
429 | static int debug_buffer_release(struct inode *inode, struct file *file) | ||
430 | { | ||
431 | struct debug_buffer *db = (struct debug_buffer *)file->private_data; | ||
432 | |||
433 | if (db) | ||
434 | kfree(db->buf); | ||
435 | kfree(db); | ||
436 | |||
437 | return 0; | ||
438 | } | ||
439 | /* end - util funcs */ | ||
440 | |||
441 | /* begin - purge list funcs */ | ||
442 | static int debug_purgelist_print(struct dlm_ctxt *dlm, struct debug_buffer *db) | ||
443 | { | ||
444 | struct dlm_lock_resource *res; | ||
445 | int out = 0; | ||
446 | unsigned long total = 0; | ||
447 | |||
448 | out += snprintf(db->buf + out, db->len - out, | ||
449 | "Dumping Purgelist for Domain: %s\n", dlm->name); | ||
450 | |||
451 | spin_lock(&dlm->spinlock); | ||
452 | list_for_each_entry(res, &dlm->purge_list, purge) { | ||
453 | ++total; | ||
454 | if (db->len - out < 100) | ||
455 | continue; | ||
456 | spin_lock(&res->spinlock); | ||
457 | out += stringify_lockname(res->lockname.name, | ||
458 | res->lockname.len, | ||
459 | db->buf + out, db->len - out); | ||
460 | out += snprintf(db->buf + out, db->len - out, "\t%ld\n", | ||
461 | (jiffies - res->last_used)/HZ); | ||
462 | spin_unlock(&res->spinlock); | ||
463 | } | ||
464 | spin_unlock(&dlm->spinlock); | ||
465 | |||
466 | out += snprintf(db->buf + out, db->len - out, | ||
467 | "Total on list: %ld\n", total); | ||
468 | |||
469 | return out; | ||
470 | } | ||
471 | |||
472 | static int debug_purgelist_open(struct inode *inode, struct file *file) | ||
473 | { | ||
474 | struct dlm_ctxt *dlm = inode->i_private; | ||
475 | struct debug_buffer *db; | ||
476 | |||
477 | db = debug_buffer_allocate(); | ||
478 | if (!db) | ||
479 | goto bail; | ||
480 | |||
481 | db->len = debug_purgelist_print(dlm, db); | ||
482 | |||
483 | file->private_data = db; | ||
484 | |||
485 | return 0; | ||
486 | bail: | ||
487 | return -ENOMEM; | ||
488 | } | ||
489 | |||
490 | static struct file_operations debug_purgelist_fops = { | ||
491 | .open = debug_purgelist_open, | ||
492 | .release = debug_buffer_release, | ||
493 | .read = debug_buffer_read, | ||
494 | .llseek = debug_buffer_llseek, | ||
495 | }; | ||
496 | /* end - purge list funcs */ | ||
497 | |||
498 | /* begin - debug mle funcs */ | ||
499 | static int debug_mle_print(struct dlm_ctxt *dlm, struct debug_buffer *db) | ||
500 | { | ||
501 | struct dlm_master_list_entry *mle; | ||
502 | int out = 0; | ||
503 | unsigned long total = 0; | ||
504 | |||
505 | out += snprintf(db->buf + out, db->len - out, | ||
506 | "Dumping MLEs for Domain: %s\n", dlm->name); | ||
507 | |||
508 | spin_lock(&dlm->master_lock); | ||
509 | list_for_each_entry(mle, &dlm->master_list, list) { | ||
510 | ++total; | ||
511 | if (db->len - out < 200) | ||
512 | continue; | ||
513 | out += dump_mle(mle, db->buf + out, db->len - out); | ||
514 | } | ||
515 | spin_unlock(&dlm->master_lock); | ||
516 | |||
517 | out += snprintf(db->buf + out, db->len - out, | ||
518 | "Total on list: %ld\n", total); | ||
519 | return out; | ||
520 | } | ||
521 | |||
522 | static int debug_mle_open(struct inode *inode, struct file *file) | ||
523 | { | ||
524 | struct dlm_ctxt *dlm = inode->i_private; | ||
525 | struct debug_buffer *db; | ||
526 | |||
527 | db = debug_buffer_allocate(); | ||
528 | if (!db) | ||
529 | goto bail; | ||
530 | |||
531 | db->len = debug_mle_print(dlm, db); | ||
532 | |||
533 | file->private_data = db; | ||
534 | |||
535 | return 0; | ||
536 | bail: | ||
537 | return -ENOMEM; | ||
538 | } | ||
539 | |||
540 | static struct file_operations debug_mle_fops = { | ||
541 | .open = debug_mle_open, | ||
542 | .release = debug_buffer_release, | ||
543 | .read = debug_buffer_read, | ||
544 | .llseek = debug_buffer_llseek, | ||
545 | }; | ||
546 | |||
547 | /* end - debug mle funcs */ | ||
548 | |||
549 | /* begin - debug lockres funcs */ | ||
550 | static int dump_lock(struct dlm_lock *lock, int list_type, char *buf, int len) | ||
551 | { | ||
552 | int out; | ||
553 | |||
554 | #define DEBUG_LOCK_VERSION 1 | ||
555 | spin_lock(&lock->spinlock); | ||
556 | out = snprintf(buf, len, "LOCK:%d,%d,%d,%d,%d,%d:%lld,%d,%d,%d,%d,%d," | ||
557 | "%d,%d,%d,%d\n", | ||
558 | DEBUG_LOCK_VERSION, | ||
559 | list_type, lock->ml.type, lock->ml.convert_type, | ||
560 | lock->ml.node, | ||
561 | dlm_get_lock_cookie_node(be64_to_cpu(lock->ml.cookie)), | ||
562 | dlm_get_lock_cookie_seq(be64_to_cpu(lock->ml.cookie)), | ||
563 | !list_empty(&lock->ast_list), | ||
564 | !list_empty(&lock->bast_list), | ||
565 | lock->ast_pending, lock->bast_pending, | ||
566 | lock->convert_pending, lock->lock_pending, | ||
567 | lock->cancel_pending, lock->unlock_pending, | ||
568 | atomic_read(&lock->lock_refs.refcount)); | ||
569 | spin_unlock(&lock->spinlock); | ||
570 | |||
571 | return out; | ||
572 | } | ||
573 | |||
574 | static int dump_lockres(struct dlm_lock_resource *res, char *buf, int len) | ||
575 | { | ||
576 | struct dlm_lock *lock; | ||
577 | int i; | ||
578 | int out = 0; | ||
579 | |||
580 | out += snprintf(buf + out, len - out, "NAME:"); | ||
581 | out += stringify_lockname(res->lockname.name, res->lockname.len, | ||
582 | buf + out, len - out); | ||
583 | out += snprintf(buf + out, len - out, "\n"); | ||
584 | |||
585 | #define DEBUG_LRES_VERSION 1 | ||
586 | out += snprintf(buf + out, len - out, | ||
587 | "LRES:%d,%d,%d,%ld,%d,%d,%d,%d,%d,%d,%d\n", | ||
588 | DEBUG_LRES_VERSION, | ||
589 | res->owner, res->state, res->last_used, | ||
590 | !list_empty(&res->purge), | ||
591 | !list_empty(&res->dirty), | ||
592 | !list_empty(&res->recovering), | ||
593 | res->inflight_locks, res->migration_pending, | ||
594 | atomic_read(&res->asts_reserved), | ||
595 | atomic_read(&res->refs.refcount)); | ||
596 | |||
597 | /* refmap */ | ||
598 | out += snprintf(buf + out, len - out, "RMAP:"); | ||
599 | out += stringify_nodemap(res->refmap, O2NM_MAX_NODES, | ||
600 | buf + out, len - out); | ||
601 | out += snprintf(buf + out, len - out, "\n"); | ||
602 | |||
603 | /* lvb */ | ||
604 | out += snprintf(buf + out, len - out, "LVBX:"); | ||
605 | for (i = 0; i < DLM_LVB_LEN; i++) | ||
606 | out += snprintf(buf + out, len - out, | ||
607 | "%02x", (unsigned char)res->lvb[i]); | ||
608 | out += snprintf(buf + out, len - out, "\n"); | ||
609 | |||
610 | /* granted */ | ||
611 | list_for_each_entry(lock, &res->granted, list) | ||
612 | out += dump_lock(lock, 0, buf + out, len - out); | ||
613 | |||
614 | /* converting */ | ||
615 | list_for_each_entry(lock, &res->converting, list) | ||
616 | out += dump_lock(lock, 1, buf + out, len - out); | ||
617 | |||
618 | /* blocked */ | ||
619 | list_for_each_entry(lock, &res->blocked, list) | ||
620 | out += dump_lock(lock, 2, buf + out, len - out); | ||
621 | |||
622 | out += snprintf(buf + out, len - out, "\n"); | ||
623 | |||
624 | return out; | ||
625 | } | ||
626 | |||
627 | static void *lockres_seq_start(struct seq_file *m, loff_t *pos) | ||
628 | { | ||
629 | struct debug_lockres *dl = m->private; | ||
630 | struct dlm_ctxt *dlm = dl->dl_ctxt; | ||
631 | struct dlm_lock_resource *res = NULL; | ||
632 | |||
633 | spin_lock(&dlm->spinlock); | ||
634 | |||
635 | if (dl->dl_res) { | ||
636 | list_for_each_entry(res, &dl->dl_res->tracking, tracking) { | ||
637 | if (dl->dl_res) { | ||
638 | dlm_lockres_put(dl->dl_res); | ||
639 | dl->dl_res = NULL; | ||
640 | } | ||
641 | if (&res->tracking == &dlm->tracking_list) { | ||
642 | mlog(0, "End of list found, %p\n", res); | ||
643 | dl = NULL; | ||
644 | break; | ||
645 | } | ||
646 | dlm_lockres_get(res); | ||
647 | dl->dl_res = res; | ||
648 | break; | ||
649 | } | ||
650 | } else { | ||
651 | if (!list_empty(&dlm->tracking_list)) { | ||
652 | list_for_each_entry(res, &dlm->tracking_list, tracking) | ||
653 | break; | ||
654 | dlm_lockres_get(res); | ||
655 | dl->dl_res = res; | ||
656 | } else | ||
657 | dl = NULL; | ||
658 | } | ||
659 | |||
660 | if (dl) { | ||
661 | spin_lock(&dl->dl_res->spinlock); | ||
662 | dump_lockres(dl->dl_res, dl->dl_buf, dl->dl_len - 1); | ||
663 | spin_unlock(&dl->dl_res->spinlock); | ||
664 | } | ||
665 | |||
666 | spin_unlock(&dlm->spinlock); | ||
667 | |||
668 | return dl; | ||
669 | } | ||
670 | |||
671 | static void lockres_seq_stop(struct seq_file *m, void *v) | ||
672 | { | ||
673 | } | ||
674 | |||
675 | static void *lockres_seq_next(struct seq_file *m, void *v, loff_t *pos) | ||
676 | { | ||
677 | return NULL; | ||
678 | } | ||
679 | |||
680 | static int lockres_seq_show(struct seq_file *s, void *v) | ||
681 | { | ||
682 | struct debug_lockres *dl = (struct debug_lockres *)v; | ||
683 | |||
684 | seq_printf(s, "%s", dl->dl_buf); | ||
685 | |||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | static struct seq_operations debug_lockres_ops = { | ||
690 | .start = lockres_seq_start, | ||
691 | .stop = lockres_seq_stop, | ||
692 | .next = lockres_seq_next, | ||
693 | .show = lockres_seq_show, | ||
694 | }; | ||
695 | |||
696 | static int debug_lockres_open(struct inode *inode, struct file *file) | ||
697 | { | ||
698 | struct dlm_ctxt *dlm = inode->i_private; | ||
699 | int ret = -ENOMEM; | ||
700 | struct seq_file *seq; | ||
701 | struct debug_lockres *dl = NULL; | ||
702 | |||
703 | dl = kzalloc(sizeof(struct debug_lockres), GFP_KERNEL); | ||
704 | if (!dl) { | ||
705 | mlog_errno(ret); | ||
706 | goto bail; | ||
707 | } | ||
708 | |||
709 | dl->dl_len = PAGE_SIZE; | ||
710 | dl->dl_buf = kmalloc(dl->dl_len, GFP_KERNEL); | ||
711 | if (!dl->dl_buf) { | ||
712 | mlog_errno(ret); | ||
713 | goto bail; | ||
714 | } | ||
715 | |||
716 | ret = seq_open(file, &debug_lockres_ops); | ||
717 | if (ret) { | ||
718 | mlog_errno(ret); | ||
719 | goto bail; | ||
720 | } | ||
721 | |||
722 | seq = (struct seq_file *) file->private_data; | ||
723 | seq->private = dl; | ||
724 | |||
725 | dlm_grab(dlm); | ||
726 | dl->dl_ctxt = dlm; | ||
727 | |||
728 | return 0; | ||
729 | bail: | ||
730 | if (dl) | ||
731 | kfree(dl->dl_buf); | ||
732 | kfree(dl); | ||
733 | return ret; | ||
734 | } | ||
735 | |||
736 | static int debug_lockres_release(struct inode *inode, struct file *file) | ||
737 | { | ||
738 | struct seq_file *seq = (struct seq_file *)file->private_data; | ||
739 | struct debug_lockres *dl = (struct debug_lockres *)seq->private; | ||
740 | |||
741 | if (dl->dl_res) | ||
742 | dlm_lockres_put(dl->dl_res); | ||
743 | dlm_put(dl->dl_ctxt); | ||
744 | kfree(dl->dl_buf); | ||
745 | return seq_release_private(inode, file); | ||
746 | } | ||
747 | |||
748 | static struct file_operations debug_lockres_fops = { | ||
749 | .open = debug_lockres_open, | ||
750 | .release = debug_lockres_release, | ||
751 | .read = seq_read, | ||
752 | .llseek = seq_lseek, | ||
753 | }; | ||
754 | /* end - debug lockres funcs */ | ||
755 | |||
756 | /* begin - debug state funcs */ | ||
757 | static int debug_state_print(struct dlm_ctxt *dlm, struct debug_buffer *db) | ||
758 | { | ||
759 | int out = 0; | ||
760 | struct dlm_reco_node_data *node; | ||
761 | char *state; | ||
762 | int lres, rres, ures, tres; | ||
763 | |||
764 | lres = atomic_read(&dlm->local_resources); | ||
765 | rres = atomic_read(&dlm->remote_resources); | ||
766 | ures = atomic_read(&dlm->unknown_resources); | ||
767 | tres = lres + rres + ures; | ||
768 | |||
769 | spin_lock(&dlm->spinlock); | ||
770 | |||
771 | switch (dlm->dlm_state) { | ||
772 | case DLM_CTXT_NEW: | ||
773 | state = "NEW"; break; | ||
774 | case DLM_CTXT_JOINED: | ||
775 | state = "JOINED"; break; | ||
776 | case DLM_CTXT_IN_SHUTDOWN: | ||
777 | state = "SHUTDOWN"; break; | ||
778 | case DLM_CTXT_LEAVING: | ||
779 | state = "LEAVING"; break; | ||
780 | default: | ||
781 | state = "UNKNOWN"; break; | ||
782 | } | ||
783 | |||
784 | /* Domain: xxxxxxxxxx Key: 0xdfbac769 */ | ||
785 | out += snprintf(db->buf + out, db->len - out, | ||
786 | "Domain: %s Key: 0x%08x\n", dlm->name, dlm->key); | ||
787 | |||
788 | /* Thread Pid: xxx Node: xxx State: xxxxx */ | ||
789 | out += snprintf(db->buf + out, db->len - out, | ||
790 | "Thread Pid: %d Node: %d State: %s\n", | ||
791 | dlm->dlm_thread_task->pid, dlm->node_num, state); | ||
792 | |||
793 | /* Number of Joins: xxx Joining Node: xxx */ | ||
794 | out += snprintf(db->buf + out, db->len - out, | ||
795 | "Number of Joins: %d Joining Node: %d\n", | ||
796 | dlm->num_joins, dlm->joining_node); | ||
797 | |||
798 | /* Domain Map: xx xx xx */ | ||
799 | out += snprintf(db->buf + out, db->len - out, "Domain Map: "); | ||
800 | out += stringify_nodemap(dlm->domain_map, O2NM_MAX_NODES, | ||
801 | db->buf + out, db->len - out); | ||
802 | out += snprintf(db->buf + out, db->len - out, "\n"); | ||
803 | |||
804 | /* Live Map: xx xx xx */ | ||
805 | out += snprintf(db->buf + out, db->len - out, "Live Map: "); | ||
806 | out += stringify_nodemap(dlm->live_nodes_map, O2NM_MAX_NODES, | ||
807 | db->buf + out, db->len - out); | ||
808 | out += snprintf(db->buf + out, db->len - out, "\n"); | ||
809 | |||
810 | /* Mastered Resources Total: xxx Locally: xxx Remotely: ... */ | ||
811 | out += snprintf(db->buf + out, db->len - out, | ||
812 | "Mastered Resources Total: %d Locally: %d " | ||
813 | "Remotely: %d Unknown: %d\n", | ||
814 | tres, lres, rres, ures); | ||
815 | |||
816 | /* Lists: Dirty=Empty Purge=InUse PendingASTs=Empty ... */ | ||
817 | out += snprintf(db->buf + out, db->len - out, | ||
818 | "Lists: Dirty=%s Purge=%s PendingASTs=%s " | ||
819 | "PendingBASTs=%s Master=%s\n", | ||
820 | (list_empty(&dlm->dirty_list) ? "Empty" : "InUse"), | ||
821 | (list_empty(&dlm->purge_list) ? "Empty" : "InUse"), | ||
822 | (list_empty(&dlm->pending_asts) ? "Empty" : "InUse"), | ||
823 | (list_empty(&dlm->pending_basts) ? "Empty" : "InUse"), | ||
824 | (list_empty(&dlm->master_list) ? "Empty" : "InUse")); | ||
825 | |||
826 | /* Purge Count: xxx Refs: xxx */ | ||
827 | out += snprintf(db->buf + out, db->len - out, | ||
828 | "Purge Count: %d Refs: %d\n", dlm->purge_count, | ||
829 | atomic_read(&dlm->dlm_refs.refcount)); | ||
830 | |||
831 | /* Dead Node: xxx */ | ||
832 | out += snprintf(db->buf + out, db->len - out, | ||
833 | "Dead Node: %d\n", dlm->reco.dead_node); | ||
834 | |||
835 | /* What about DLM_RECO_STATE_FINALIZE? */ | ||
836 | if (dlm->reco.state == DLM_RECO_STATE_ACTIVE) | ||
837 | state = "ACTIVE"; | ||
838 | else | ||
839 | state = "INACTIVE"; | ||
840 | |||
841 | /* Recovery Pid: xxxx Master: xxx State: xxxx */ | ||
842 | out += snprintf(db->buf + out, db->len - out, | ||
843 | "Recovery Pid: %d Master: %d State: %s\n", | ||
844 | dlm->dlm_reco_thread_task->pid, | ||
845 | dlm->reco.new_master, state); | ||
846 | |||
847 | /* Recovery Map: xx xx */ | ||
848 | out += snprintf(db->buf + out, db->len - out, "Recovery Map: "); | ||
849 | out += stringify_nodemap(dlm->recovery_map, O2NM_MAX_NODES, | ||
850 | db->buf + out, db->len - out); | ||
851 | out += snprintf(db->buf + out, db->len - out, "\n"); | ||
852 | |||
853 | /* Recovery Node State: */ | ||
854 | out += snprintf(db->buf + out, db->len - out, "Recovery Node State:\n"); | ||
855 | list_for_each_entry(node, &dlm->reco.node_data, list) { | ||
856 | switch (node->state) { | ||
857 | case DLM_RECO_NODE_DATA_INIT: | ||
858 | state = "INIT"; | ||
859 | break; | ||
860 | case DLM_RECO_NODE_DATA_REQUESTING: | ||
861 | state = "REQUESTING"; | ||
862 | break; | ||
863 | case DLM_RECO_NODE_DATA_DEAD: | ||
864 | state = "DEAD"; | ||
865 | break; | ||
866 | case DLM_RECO_NODE_DATA_RECEIVING: | ||
867 | state = "RECEIVING"; | ||
868 | break; | ||
869 | case DLM_RECO_NODE_DATA_REQUESTED: | ||
870 | state = "REQUESTED"; | ||
871 | break; | ||
872 | case DLM_RECO_NODE_DATA_DONE: | ||
873 | state = "DONE"; | ||
874 | break; | ||
875 | case DLM_RECO_NODE_DATA_FINALIZE_SENT: | ||
876 | state = "FINALIZE-SENT"; | ||
877 | break; | ||
878 | default: | ||
879 | state = "BAD"; | ||
880 | break; | ||
881 | } | ||
882 | out += snprintf(db->buf + out, db->len - out, "\t%u - %s\n", | ||
883 | node->node_num, state); | ||
884 | } | ||
885 | |||
886 | spin_unlock(&dlm->spinlock); | ||
887 | |||
888 | return out; | ||
889 | } | ||
890 | |||
891 | static int debug_state_open(struct inode *inode, struct file *file) | ||
892 | { | ||
893 | struct dlm_ctxt *dlm = inode->i_private; | ||
894 | struct debug_buffer *db = NULL; | ||
895 | |||
896 | db = debug_buffer_allocate(); | ||
897 | if (!db) | ||
898 | goto bail; | ||
899 | |||
900 | db->len = debug_state_print(dlm, db); | ||
901 | |||
902 | file->private_data = db; | ||
903 | |||
904 | return 0; | ||
905 | bail: | ||
906 | return -ENOMEM; | ||
907 | } | ||
908 | |||
909 | static struct file_operations debug_state_fops = { | ||
910 | .open = debug_state_open, | ||
911 | .release = debug_buffer_release, | ||
912 | .read = debug_buffer_read, | ||
913 | .llseek = debug_buffer_llseek, | ||
914 | }; | ||
915 | /* end - debug state funcs */ | ||
916 | |||
917 | /* files in subroot */ | ||
918 | int dlm_debug_init(struct dlm_ctxt *dlm) | ||
919 | { | ||
920 | struct dlm_debug_ctxt *dc = dlm->dlm_debug_ctxt; | ||
921 | |||
922 | /* for dumping dlm_ctxt */ | ||
923 | dc->debug_state_dentry = debugfs_create_file(DLM_DEBUGFS_DLM_STATE, | ||
924 | S_IFREG|S_IRUSR, | ||
925 | dlm->dlm_debugfs_subroot, | ||
926 | dlm, &debug_state_fops); | ||
927 | if (!dc->debug_state_dentry) { | ||
928 | mlog_errno(-ENOMEM); | ||
929 | goto bail; | ||
930 | } | ||
931 | |||
932 | /* for dumping lockres */ | ||
933 | dc->debug_lockres_dentry = | ||
934 | debugfs_create_file(DLM_DEBUGFS_LOCKING_STATE, | ||
935 | S_IFREG|S_IRUSR, | ||
936 | dlm->dlm_debugfs_subroot, | ||
937 | dlm, &debug_lockres_fops); | ||
938 | if (!dc->debug_lockres_dentry) { | ||
939 | mlog_errno(-ENOMEM); | ||
940 | goto bail; | ||
941 | } | ||
942 | |||
943 | /* for dumping mles */ | ||
944 | dc->debug_mle_dentry = debugfs_create_file(DLM_DEBUGFS_MLE_STATE, | ||
945 | S_IFREG|S_IRUSR, | ||
946 | dlm->dlm_debugfs_subroot, | ||
947 | dlm, &debug_mle_fops); | ||
948 | if (!dc->debug_mle_dentry) { | ||
949 | mlog_errno(-ENOMEM); | ||
950 | goto bail; | ||
951 | } | ||
952 | |||
953 | /* for dumping lockres on the purge list */ | ||
954 | dc->debug_purgelist_dentry = | ||
955 | debugfs_create_file(DLM_DEBUGFS_PURGE_LIST, | ||
956 | S_IFREG|S_IRUSR, | ||
957 | dlm->dlm_debugfs_subroot, | ||
958 | dlm, &debug_purgelist_fops); | ||
959 | if (!dc->debug_purgelist_dentry) { | ||
960 | mlog_errno(-ENOMEM); | ||
961 | goto bail; | ||
962 | } | ||
963 | |||
964 | dlm_debug_get(dc); | ||
965 | return 0; | ||
966 | |||
967 | bail: | ||
968 | dlm_debug_shutdown(dlm); | ||
969 | return -ENOMEM; | ||
970 | } | ||
971 | |||
972 | void dlm_debug_shutdown(struct dlm_ctxt *dlm) | ||
973 | { | ||
974 | struct dlm_debug_ctxt *dc = dlm->dlm_debug_ctxt; | ||
975 | |||
976 | if (dc) { | ||
977 | if (dc->debug_purgelist_dentry) | ||
978 | debugfs_remove(dc->debug_purgelist_dentry); | ||
979 | if (dc->debug_mle_dentry) | ||
980 | debugfs_remove(dc->debug_mle_dentry); | ||
981 | if (dc->debug_lockres_dentry) | ||
982 | debugfs_remove(dc->debug_lockres_dentry); | ||
983 | if (dc->debug_state_dentry) | ||
984 | debugfs_remove(dc->debug_state_dentry); | ||
985 | dlm_debug_put(dc); | ||
986 | } | ||
987 | } | ||
988 | |||
989 | /* subroot - domain dir */ | ||
990 | int dlm_create_debugfs_subroot(struct dlm_ctxt *dlm) | ||
991 | { | ||
992 | dlm->dlm_debugfs_subroot = debugfs_create_dir(dlm->name, | ||
993 | dlm_debugfs_root); | ||
994 | if (!dlm->dlm_debugfs_subroot) { | ||
995 | mlog_errno(-ENOMEM); | ||
996 | goto bail; | ||
997 | } | ||
998 | |||
999 | dlm->dlm_debug_ctxt = kzalloc(sizeof(struct dlm_debug_ctxt), | ||
1000 | GFP_KERNEL); | ||
1001 | if (!dlm->dlm_debug_ctxt) { | ||
1002 | mlog_errno(-ENOMEM); | ||
1003 | goto bail; | ||
1004 | } | ||
1005 | kref_init(&dlm->dlm_debug_ctxt->debug_refcnt); | ||
1006 | |||
1007 | return 0; | ||
1008 | bail: | ||
1009 | dlm_destroy_debugfs_subroot(dlm); | ||
1010 | return -ENOMEM; | ||
1011 | } | ||
1012 | |||
1013 | void dlm_destroy_debugfs_subroot(struct dlm_ctxt *dlm) | ||
1014 | { | ||
1015 | if (dlm->dlm_debugfs_subroot) | ||
1016 | debugfs_remove(dlm->dlm_debugfs_subroot); | ||
1017 | } | ||
1018 | |||
1019 | /* debugfs root */ | ||
1020 | int dlm_create_debugfs_root(void) | ||
1021 | { | ||
1022 | dlm_debugfs_root = debugfs_create_dir(DLM_DEBUGFS_DIR, NULL); | ||
1023 | if (!dlm_debugfs_root) { | ||
1024 | mlog_errno(-ENOMEM); | ||
1025 | return -ENOMEM; | ||
1026 | } | ||
1027 | return 0; | ||
1028 | } | ||
1029 | |||
1030 | void dlm_destroy_debugfs_root(void) | ||
1031 | { | ||
1032 | if (dlm_debugfs_root) | ||
1033 | debugfs_remove(dlm_debugfs_root); | ||
1034 | } | ||
1035 | #endif /* CONFIG_DEBUG_FS */ | ||