diff options
Diffstat (limited to 'fs/dlm/debug_fs.c')
-rw-r--r-- | fs/dlm/debug_fs.c | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/fs/dlm/debug_fs.c b/fs/dlm/debug_fs.c new file mode 100644 index 000000000000..98b49a1ece47 --- /dev/null +++ b/fs/dlm/debug_fs.c | |||
@@ -0,0 +1,310 @@ | |||
1 | /****************************************************************************** | ||
2 | ******************************************************************************* | ||
3 | ** | ||
4 | ** Copyright (C) 2005 Red Hat, Inc. All rights reserved. | ||
5 | ** | ||
6 | ** This copyrighted material is made available to anyone wishing to use, | ||
7 | ** modify, copy, or redistribute it subject to the terms and conditions | ||
8 | ** of the GNU General Public License v.2. | ||
9 | ** | ||
10 | ******************************************************************************* | ||
11 | ******************************************************************************/ | ||
12 | |||
13 | #include <linux/pagemap.h> | ||
14 | #include <linux/seq_file.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/ctype.h> | ||
17 | #include <linux/debugfs.h> | ||
18 | |||
19 | #include "dlm_internal.h" | ||
20 | |||
21 | |||
22 | static struct dentry *dlm_root; | ||
23 | |||
24 | struct rsb_iter { | ||
25 | int entry; | ||
26 | struct dlm_ls *ls; | ||
27 | struct list_head *next; | ||
28 | struct dlm_rsb *rsb; | ||
29 | }; | ||
30 | |||
31 | static char *print_lockmode(int mode) | ||
32 | { | ||
33 | switch (mode) { | ||
34 | case DLM_LOCK_IV: | ||
35 | return "--"; | ||
36 | case DLM_LOCK_NL: | ||
37 | return "NL"; | ||
38 | case DLM_LOCK_CR: | ||
39 | return "CR"; | ||
40 | case DLM_LOCK_CW: | ||
41 | return "CW"; | ||
42 | case DLM_LOCK_PR: | ||
43 | return "PR"; | ||
44 | case DLM_LOCK_PW: | ||
45 | return "PW"; | ||
46 | case DLM_LOCK_EX: | ||
47 | return "EX"; | ||
48 | default: | ||
49 | return "??"; | ||
50 | } | ||
51 | } | ||
52 | |||
53 | static void print_lock(struct seq_file *s, struct dlm_lkb *lkb, | ||
54 | struct dlm_rsb *res) | ||
55 | { | ||
56 | seq_printf(s, "%08x %s", lkb->lkb_id, print_lockmode(lkb->lkb_grmode)); | ||
57 | |||
58 | if (lkb->lkb_status == DLM_LKSTS_CONVERT | ||
59 | || lkb->lkb_status == DLM_LKSTS_WAITING) | ||
60 | seq_printf(s, " (%s)", print_lockmode(lkb->lkb_rqmode)); | ||
61 | |||
62 | if (lkb->lkb_range) { | ||
63 | /* FIXME: this warns on Alpha */ | ||
64 | if (lkb->lkb_status == DLM_LKSTS_CONVERT | ||
65 | || lkb->lkb_status == DLM_LKSTS_GRANTED) | ||
66 | seq_printf(s, " %" PRIx64 "-%" PRIx64, | ||
67 | lkb->lkb_range[GR_RANGE_START], | ||
68 | lkb->lkb_range[GR_RANGE_END]); | ||
69 | if (lkb->lkb_status == DLM_LKSTS_CONVERT | ||
70 | || lkb->lkb_status == DLM_LKSTS_WAITING) | ||
71 | seq_printf(s, " (%" PRIx64 "-%" PRIx64 ")", | ||
72 | lkb->lkb_range[RQ_RANGE_START], | ||
73 | lkb->lkb_range[RQ_RANGE_END]); | ||
74 | } | ||
75 | |||
76 | if (lkb->lkb_nodeid) { | ||
77 | if (lkb->lkb_nodeid != res->res_nodeid) | ||
78 | seq_printf(s, " Remote: %3d %08x", lkb->lkb_nodeid, | ||
79 | lkb->lkb_remid); | ||
80 | else | ||
81 | seq_printf(s, " Master: %08x", lkb->lkb_remid); | ||
82 | } | ||
83 | |||
84 | if (lkb->lkb_wait_type) | ||
85 | seq_printf(s, " wait_type: %d", lkb->lkb_wait_type); | ||
86 | |||
87 | seq_printf(s, "\n"); | ||
88 | } | ||
89 | |||
90 | static int print_resource(struct dlm_rsb *res, struct seq_file *s) | ||
91 | { | ||
92 | struct dlm_lkb *lkb; | ||
93 | int i, lvblen = res->res_ls->ls_lvblen; | ||
94 | |||
95 | seq_printf(s, "\nResource %p Name (len=%d) \"", res, res->res_length); | ||
96 | for (i = 0; i < res->res_length; i++) { | ||
97 | if (isprint(res->res_name[i])) | ||
98 | seq_printf(s, "%c", res->res_name[i]); | ||
99 | else | ||
100 | seq_printf(s, "%c", '.'); | ||
101 | } | ||
102 | if (res->res_nodeid > 0) | ||
103 | seq_printf(s, "\" \nLocal Copy, Master is node %d\n", | ||
104 | res->res_nodeid); | ||
105 | else if (res->res_nodeid == 0) | ||
106 | seq_printf(s, "\" \nMaster Copy\n"); | ||
107 | else if (res->res_nodeid == -1) | ||
108 | seq_printf(s, "\" \nLooking up master (lkid %x)\n", | ||
109 | res->res_first_lkid); | ||
110 | else | ||
111 | seq_printf(s, "\" \nInvalid master %d\n", res->res_nodeid); | ||
112 | |||
113 | /* Print the LVB: */ | ||
114 | if (res->res_lvbptr) { | ||
115 | seq_printf(s, "LVB: "); | ||
116 | for (i = 0; i < lvblen; i++) { | ||
117 | if (i == lvblen / 2) | ||
118 | seq_printf(s, "\n "); | ||
119 | seq_printf(s, "%02x ", | ||
120 | (unsigned char) res->res_lvbptr[i]); | ||
121 | } | ||
122 | if (rsb_flag(res, RSB_VALNOTVALID)) | ||
123 | seq_printf(s, " (INVALID)"); | ||
124 | seq_printf(s, "\n"); | ||
125 | } | ||
126 | |||
127 | /* Print the locks attached to this resource */ | ||
128 | seq_printf(s, "Granted Queue\n"); | ||
129 | list_for_each_entry(lkb, &res->res_grantqueue, lkb_statequeue) | ||
130 | print_lock(s, lkb, res); | ||
131 | |||
132 | seq_printf(s, "Conversion Queue\n"); | ||
133 | list_for_each_entry(lkb, &res->res_convertqueue, lkb_statequeue) | ||
134 | print_lock(s, lkb, res); | ||
135 | |||
136 | seq_printf(s, "Waiting Queue\n"); | ||
137 | list_for_each_entry(lkb, &res->res_waitqueue, lkb_statequeue) | ||
138 | print_lock(s, lkb, res); | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | static int rsb_iter_next(struct rsb_iter *ri) | ||
144 | { | ||
145 | struct dlm_ls *ls = ri->ls; | ||
146 | int i; | ||
147 | |||
148 | if (!ri->next) { | ||
149 | top: | ||
150 | /* Find the next non-empty hash bucket */ | ||
151 | for (i = ri->entry; i < ls->ls_rsbtbl_size; i++) { | ||
152 | read_lock(&ls->ls_rsbtbl[i].lock); | ||
153 | if (!list_empty(&ls->ls_rsbtbl[i].list)) { | ||
154 | ri->next = ls->ls_rsbtbl[i].list.next; | ||
155 | read_unlock(&ls->ls_rsbtbl[i].lock); | ||
156 | break; | ||
157 | } | ||
158 | read_unlock(&ls->ls_rsbtbl[i].lock); | ||
159 | } | ||
160 | ri->entry = i; | ||
161 | |||
162 | if (ri->entry >= ls->ls_rsbtbl_size) | ||
163 | return 1; | ||
164 | } else { | ||
165 | i = ri->entry; | ||
166 | read_lock(&ls->ls_rsbtbl[i].lock); | ||
167 | ri->next = ri->next->next; | ||
168 | if (ri->next->next == ls->ls_rsbtbl[i].list.next) { | ||
169 | /* End of list - move to next bucket */ | ||
170 | ri->next = NULL; | ||
171 | ri->entry++; | ||
172 | read_unlock(&ls->ls_rsbtbl[i].lock); | ||
173 | goto top; | ||
174 | } | ||
175 | read_unlock(&ls->ls_rsbtbl[i].lock); | ||
176 | } | ||
177 | ri->rsb = list_entry(ri->next, struct dlm_rsb, res_hashchain); | ||
178 | |||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | static void rsb_iter_free(struct rsb_iter *ri) | ||
183 | { | ||
184 | kfree(ri); | ||
185 | } | ||
186 | |||
187 | static struct rsb_iter *rsb_iter_init(struct dlm_ls *ls) | ||
188 | { | ||
189 | struct rsb_iter *ri; | ||
190 | |||
191 | ri = kmalloc(sizeof *ri, GFP_KERNEL); | ||
192 | if (!ri) | ||
193 | return NULL; | ||
194 | |||
195 | ri->ls = ls; | ||
196 | ri->entry = 0; | ||
197 | ri->next = NULL; | ||
198 | |||
199 | if (rsb_iter_next(ri)) { | ||
200 | rsb_iter_free(ri); | ||
201 | return NULL; | ||
202 | } | ||
203 | |||
204 | return ri; | ||
205 | } | ||
206 | |||
207 | static void *seq_start(struct seq_file *file, loff_t *pos) | ||
208 | { | ||
209 | struct rsb_iter *ri; | ||
210 | loff_t n = *pos; | ||
211 | |||
212 | ri = rsb_iter_init(file->private); | ||
213 | if (!ri) | ||
214 | return NULL; | ||
215 | |||
216 | while (n--) { | ||
217 | if (rsb_iter_next(ri)) { | ||
218 | rsb_iter_free(ri); | ||
219 | return NULL; | ||
220 | } | ||
221 | } | ||
222 | |||
223 | return ri; | ||
224 | } | ||
225 | |||
226 | static void *seq_next(struct seq_file *file, void *iter_ptr, loff_t *pos) | ||
227 | { | ||
228 | struct rsb_iter *ri = iter_ptr; | ||
229 | |||
230 | (*pos)++; | ||
231 | |||
232 | if (rsb_iter_next(ri)) { | ||
233 | rsb_iter_free(ri); | ||
234 | return NULL; | ||
235 | } | ||
236 | |||
237 | return ri; | ||
238 | } | ||
239 | |||
240 | static void seq_stop(struct seq_file *file, void *iter_ptr) | ||
241 | { | ||
242 | /* nothing for now */ | ||
243 | } | ||
244 | |||
245 | static int seq_show(struct seq_file *file, void *iter_ptr) | ||
246 | { | ||
247 | struct rsb_iter *ri = iter_ptr; | ||
248 | |||
249 | print_resource(ri->rsb, file); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static struct seq_operations dlm_seq_ops = { | ||
255 | .start = seq_start, | ||
256 | .next = seq_next, | ||
257 | .stop = seq_stop, | ||
258 | .show = seq_show, | ||
259 | }; | ||
260 | |||
261 | static int do_open(struct inode *inode, struct file *file) | ||
262 | { | ||
263 | struct seq_file *seq; | ||
264 | int ret; | ||
265 | |||
266 | ret = seq_open(file, &dlm_seq_ops); | ||
267 | if (ret) | ||
268 | return ret; | ||
269 | |||
270 | seq = file->private_data; | ||
271 | seq->private = inode->u.generic_ip; | ||
272 | |||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | static struct file_operations dlm_fops = { | ||
277 | .owner = THIS_MODULE, | ||
278 | .open = do_open, | ||
279 | .read = seq_read, | ||
280 | .llseek = seq_lseek, | ||
281 | .release = seq_release | ||
282 | }; | ||
283 | |||
284 | int dlm_create_debug_file(struct dlm_ls *ls) | ||
285 | { | ||
286 | ls->ls_debug_dentry = debugfs_create_file(ls->ls_name, | ||
287 | S_IFREG | S_IRUGO, | ||
288 | dlm_root, | ||
289 | ls, | ||
290 | &dlm_fops); | ||
291 | return ls->ls_debug_dentry ? 0 : -ENOMEM; | ||
292 | } | ||
293 | |||
294 | void dlm_delete_debug_file(struct dlm_ls *ls) | ||
295 | { | ||
296 | if (ls->ls_debug_dentry) | ||
297 | debugfs_remove(ls->ls_debug_dentry); | ||
298 | } | ||
299 | |||
300 | int dlm_register_debugfs(void) | ||
301 | { | ||
302 | dlm_root = debugfs_create_dir("dlm", NULL); | ||
303 | return dlm_root ? 0 : -ENOMEM; | ||
304 | } | ||
305 | |||
306 | void dlm_unregister_debugfs(void) | ||
307 | { | ||
308 | debugfs_remove(dlm_root); | ||
309 | } | ||
310 | |||