1#include "ceph_debug.h"
2
3#include <linux/device.h>
4#include <linux/slab.h>
5#include <linux/module.h>
6#include <linux/ctype.h>
7#include <linux/debugfs.h>
8#include <linux/seq_file.h>
9
10#include "super.h"
11#include "mds_client.h"
12#include "mon_client.h"
13#include "auth.h"
14
15#ifdef CONFIG_DEBUG_FS
16
17/*
18 * Implement /sys/kernel/debug/ceph fun
19 *
20 * /sys/kernel/debug/ceph/client*  - an instance of the ceph client
21 *      .../osdmap      - current osdmap
22 *      .../mdsmap      - current mdsmap
23 *      .../monmap      - current monmap
24 *      .../osdc        - active osd requests
25 *      .../mdsc        - active mds requests
26 *      .../monc        - mon client state
27 *      .../dentry_lru  - dump contents of dentry lru
28 *      .../caps        - expose cap (reservation) stats
29 *      .../bdi         - symlink to ../../bdi/something
30 */
31
32static struct dentry *ceph_debugfs_dir;
33
34static int monmap_show(struct seq_file *s, void *p)
35{
36	int i;
37	struct ceph_client *client = s->private;
38
39	if (client->monc.monmap == NULL)
40		return 0;
41
42	seq_printf(s, "epoch %d\n", client->monc.monmap->epoch);
43	for (i = 0; i < client->monc.monmap->num_mon; i++) {
44		struct ceph_entity_inst *inst =
45			&client->monc.monmap->mon_inst[i];
46
47		seq_printf(s, "\t%s%lld\t%s\n",
48			   ENTITY_NAME(inst->name),
49			   pr_addr(&inst->addr.in_addr));
50	}
51	return 0;
52}
53
54static int mdsmap_show(struct seq_file *s, void *p)
55{
56	int i;
57	struct ceph_client *client = s->private;
58
59	if (client->mdsc.mdsmap == NULL)
60		return 0;
61	seq_printf(s, "epoch %d\n", client->mdsc.mdsmap->m_epoch);
62	seq_printf(s, "root %d\n", client->mdsc.mdsmap->m_root);
63	seq_printf(s, "session_timeout %d\n",
64		       client->mdsc.mdsmap->m_session_timeout);
65	seq_printf(s, "session_autoclose %d\n",
66		       client->mdsc.mdsmap->m_session_autoclose);
67	for (i = 0; i < client->mdsc.mdsmap->m_max_mds; i++) {
68		struct ceph_entity_addr *addr =
69			&client->mdsc.mdsmap->m_info[i].addr;
70		int state = client->mdsc.mdsmap->m_info[i].state;
71
72		seq_printf(s, "\tmds%d\t%s\t(%s)\n", i, pr_addr(&addr->in_addr),
73			       ceph_mds_state_name(state));
74	}
75	return 0;
76}
77
78static int osdmap_show(struct seq_file *s, void *p)
79{
80	int i;
81	struct ceph_client *client = s->private;
82	struct rb_node *n;
83
84	if (client->osdc.osdmap == NULL)
85		return 0;
86	seq_printf(s, "epoch %d\n", client->osdc.osdmap->epoch);
87	seq_printf(s, "flags%s%s\n",
88		   (client->osdc.osdmap->flags & CEPH_OSDMAP_NEARFULL) ?
89		   " NEARFULL" : "",
90		   (client->osdc.osdmap->flags & CEPH_OSDMAP_FULL) ?
91		   " FULL" : "");
92	for (n = rb_first(&client->osdc.osdmap->pg_pools); n; n = rb_next(n)) {
93		struct ceph_pg_pool_info *pool =
94			rb_entry(n, struct ceph_pg_pool_info, node);
95		seq_printf(s, "pg_pool %d pg_num %d / %d, lpg_num %d / %d\n",
96			   pool->id, pool->v.pg_num, pool->pg_num_mask,
97			   pool->v.lpg_num, pool->lpg_num_mask);
98	}
99	for (i = 0; i < client->osdc.osdmap->max_osd; i++) {
100		struct ceph_entity_addr *addr =
101			&client->osdc.osdmap->osd_addr[i];
102		int state = client->osdc.osdmap->osd_state[i];
103		char sb[64];
104
105		seq_printf(s, "\tosd%d\t%s\t%3d%%\t(%s)\n",
106			   i, pr_addr(&addr->in_addr),
107			   ((client->osdc.osdmap->osd_weight[i]*100) >> 16),
108			   ceph_osdmap_state_str(sb, sizeof(sb), state));
109	}
110	return 0;
111}
112
113static int monc_show(struct seq_file *s, void *p)
114{
115	struct ceph_client *client = s->private;
116	struct ceph_mon_generic_request *req;
117	struct ceph_mon_client *monc = &client->monc;
118	struct rb_node *rp;
119
120	mutex_lock(&monc->mutex);
121
122	if (monc->have_mdsmap)
123		seq_printf(s, "have mdsmap %u\n", (unsigned)monc->have_mdsmap);
124	if (monc->have_osdmap)
125		seq_printf(s, "have osdmap %u\n", (unsigned)monc->have_osdmap);
126	if (monc->want_next_osdmap)
127		seq_printf(s, "want next osdmap\n");
128
129	for (rp = rb_first(&monc->generic_request_tree); rp; rp = rb_next(rp)) {
130		__u16 op;
131		req = rb_entry(rp, struct ceph_mon_generic_request, node);
132		op = le16_to_cpu(req->request->hdr.type);
133		if (op == CEPH_MSG_STATFS)
134			seq_printf(s, "%lld statfs\n", req->tid);
135		else
136			seq_printf(s, "%lld unknown\n", req->tid);
137	}
138
139	mutex_unlock(&monc->mutex);
140	return 0;
141}
142
143static int mdsc_show(struct seq_file *s, void *p)
144{
145	struct ceph_client *client = s->private;
146	struct ceph_mds_client *mdsc = &client->mdsc;
147	struct ceph_mds_request *req;
148	struct rb_node *rp;
149	int pathlen;
150	u64 pathbase;
151	char *path;
152
153	mutex_lock(&mdsc->mutex);
154	for (rp = rb_first(&mdsc->request_tree); rp; rp = rb_next(rp)) {
155		req = rb_entry(rp, struct ceph_mds_request, r_node);
156
157		if (req->r_request)
158			seq_printf(s, "%lld\tmds%d\t", req->r_tid, req->r_mds);
159		else
160			seq_printf(s, "%lld\t(no request)\t", req->r_tid);
161
162		seq_printf(s, "%s", ceph_mds_op_name(req->r_op));
163
164		if (req->r_got_unsafe)
165			seq_printf(s, "\t(unsafe)");
166		else
167			seq_printf(s, "\t");
168
169		if (req->r_inode) {
170			seq_printf(s, " #%llx", ceph_ino(req->r_inode));
171		} else if (req->r_dentry) {
172			path = ceph_mdsc_build_path(req->r_dentry, &pathlen,
173						    &pathbase, 0);
174			if (IS_ERR(path))
175				path = NULL;
176			spin_lock(&req->r_dentry->d_lock);
177			seq_printf(s, " #%llx/%.*s (%s)",
178				   ceph_ino(req->r_dentry->d_parent->d_inode),
179				   req->r_dentry->d_name.len,
180				   req->r_dentry->d_name.name,
181				   path ? path : "");
182			spin_unlock(&req->r_dentry->d_lock);
183			kfree(path);
184		} else if (req->r_path1) {
185			seq_printf(s, " #%llx/%s", req->r_ino1.ino,
186				   req->r_path1);
187		}
188
189		if (req->r_old_dentry) {
190			path = ceph_mdsc_build_path(req->r_old_dentry, &pathlen,
191						    &pathbase, 0);
192			if (IS_ERR(path))
193				path = NULL;
194			spin_lock(&req->r_old_dentry->d_lock);
195			seq_printf(s, " #%llx/%.*s (%s)",
196			   ceph_ino(req->r_old_dentry->d_parent->d_inode),
197				   req->r_old_dentry->d_name.len,
198				   req->r_old_dentry->d_name.name,
199				   path ? path : "");
200			spin_unlock(&req->r_old_dentry->d_lock);
201			kfree(path);
202		} else if (req->r_path2) {
203			if (req->r_ino2.ino)
204				seq_printf(s, " #%llx/%s", req->r_ino2.ino,
205					   req->r_path2);
206			else
207				seq_printf(s, " %s", req->r_path2);
208		}
209
210		seq_printf(s, "\n");
211	}
212	mutex_unlock(&mdsc->mutex);
213
214	return 0;
215}
216
217static int osdc_show(struct seq_file *s, void *pp)
218{
219	struct ceph_client *client = s->private;
220	struct ceph_osd_client *osdc = &client->osdc;
221	struct rb_node *p;
222
223	mutex_lock(&osdc->request_mutex);
224	for (p = rb_first(&osdc->requests); p; p = rb_next(p)) {
225		struct ceph_osd_request *req;
226		struct ceph_osd_request_head *head;
227		struct ceph_osd_op *op;
228		int num_ops;
229		int opcode, olen;
230		int i;
231
232		req = rb_entry(p, struct ceph_osd_request, r_node);
233
234		seq_printf(s, "%lld\tosd%d\t%d.%x\t", req->r_tid,
235			   req->r_osd ? req->r_osd->o_osd : -1,
236			   le32_to_cpu(req->r_pgid.pool),
237			   le16_to_cpu(req->r_pgid.ps));
238
239		head = req->r_request->front.iov_base;
240		op = (void *)(head + 1);
241
242		num_ops = le16_to_cpu(head->num_ops);
243		olen = le32_to_cpu(head->object_len);
244		seq_printf(s, "%.*s", olen,
245			   (const char *)(head->ops + num_ops));
246
247		if (req->r_reassert_version.epoch)
248			seq_printf(s, "\t%u'%llu",
249			   (unsigned)le32_to_cpu(req->r_reassert_version.epoch),
250			   le64_to_cpu(req->r_reassert_version.version));
251		else
252			seq_printf(s, "\t");
253
254		for (i = 0; i < num_ops; i++) {
255			opcode = le16_to_cpu(op->op);
256			seq_printf(s, "\t%s", ceph_osd_op_name(opcode));
257			op++;
258		}
259
260		seq_printf(s, "\n");
261	}
262	mutex_unlock(&osdc->request_mutex);
263	return 0;
264}
265
266static int caps_show(struct seq_file *s, void *p)
267{
268	struct ceph_client *client = s->private;
269	int total, avail, used, reserved, min;
270
271	ceph_reservation_status(client, &total, &avail, &used, &reserved, &min);
272	seq_printf(s, "total\t\t%d\n"
273		   "avail\t\t%d\n"
274		   "used\t\t%d\n"
275		   "reserved\t%d\n"
276		   "min\t%d\n",
277		   total, avail, used, reserved, min);
278	return 0;
279}
280
281static int dentry_lru_show(struct seq_file *s, void *ptr)
282{
283	struct ceph_client *client = s->private;
284	struct ceph_mds_client *mdsc = &client->mdsc;
285	struct ceph_dentry_info *di;
286
287	spin_lock(&mdsc->dentry_lru_lock);
288	list_for_each_entry(di, &mdsc->dentry_lru, lru) {
289		struct dentry *dentry = di->dentry;
290		seq_printf(s, "%p %p\t%.*s\n",
291			   di, dentry, dentry->d_name.len, dentry->d_name.name);
292	}
293	spin_unlock(&mdsc->dentry_lru_lock);
294
295	return 0;
296}
297
298#define DEFINE_SHOW_FUNC(name)						\
299static int name##_open(struct inode *inode, struct file *file)		\
300{									\
301	struct seq_file *sf;						\
302	int ret;							\
303									\
304	ret = single_open(file, name, NULL);				\
305	sf = file->private_data;					\
306	sf->private = inode->i_private;					\
307	return ret;							\
308}									\
309									\
310static const struct file_operations name##_fops = {			\
311	.open		= name##_open,					\
312	.read		= seq_read,					\
313	.llseek		= seq_lseek,					\
314	.release	= single_release,				\
315};
316
317DEFINE_SHOW_FUNC(monmap_show)
318DEFINE_SHOW_FUNC(mdsmap_show)
319DEFINE_SHOW_FUNC(osdmap_show)
320DEFINE_SHOW_FUNC(monc_show)
321DEFINE_SHOW_FUNC(mdsc_show)
322DEFINE_SHOW_FUNC(osdc_show)
323DEFINE_SHOW_FUNC(dentry_lru_show)
324DEFINE_SHOW_FUNC(caps_show)
325
326static int congestion_kb_set(void *data, u64 val)
327{
328	struct ceph_client *client = (struct ceph_client *)data;
329
330	if (client)
331		client->mount_args->congestion_kb = (int)val;
332
333	return 0;
334}
335
336static int congestion_kb_get(void *data, u64 *val)
337{
338	struct ceph_client *client = (struct ceph_client *)data;
339
340	if (client)
341		*val = (u64)client->mount_args->congestion_kb;
342
343	return 0;
344}
345
346
347DEFINE_SIMPLE_ATTRIBUTE(congestion_kb_fops, congestion_kb_get,
348			congestion_kb_set, "%llu\n");
349
350int __init ceph_debugfs_init(void)
351{
352	ceph_debugfs_dir = debugfs_create_dir("ceph", NULL);
353	if (!ceph_debugfs_dir)
354		return -ENOMEM;
355	return 0;
356}
357
358void ceph_debugfs_cleanup(void)
359{
360	debugfs_remove(ceph_debugfs_dir);
361}
362
363int ceph_debugfs_client_init(struct ceph_client *client)
364{
365	int ret = 0;
366	char name[80];
367
368	snprintf(name, sizeof(name), "%pU.client%lld", &client->fsid,
369		 client->monc.auth->global_id);
370
371	client->debugfs_dir = debugfs_create_dir(name, ceph_debugfs_dir);
372	if (!client->debugfs_dir)
373		goto out;
374
375	client->monc.debugfs_file = debugfs_create_file("monc",
376						      0600,
377						      client->debugfs_dir,
378						      client,
379						      &monc_show_fops);
380	if (!client->monc.debugfs_file)
381		goto out;
382
383	client->mdsc.debugfs_file = debugfs_create_file("mdsc",
384						      0600,
385						      client->debugfs_dir,
386						      client,
387						      &mdsc_show_fops);
388	if (!client->mdsc.debugfs_file)
389		goto out;
390
391	client->osdc.debugfs_file = debugfs_create_file("osdc",
392						      0600,
393						      client->debugfs_dir,
394						      client,
395						      &osdc_show_fops);
396	if (!client->osdc.debugfs_file)
397		goto out;
398
399	client->debugfs_monmap = debugfs_create_file("monmap",
400					0600,
401					client->debugfs_dir,
402					client,
403					&monmap_show_fops);
404	if (!client->debugfs_monmap)
405		goto out;
406
407	client->debugfs_mdsmap = debugfs_create_file("mdsmap",
408					0600,
409					client->debugfs_dir,
410					client,
411					&mdsmap_show_fops);
412	if (!client->debugfs_mdsmap)
413		goto out;
414
415	client->debugfs_osdmap = debugfs_create_file("osdmap",
416					0600,
417					client->debugfs_dir,
418					client,
419					&osdmap_show_fops);
420	if (!client->debugfs_osdmap)
421		goto out;
422
423	client->debugfs_dentry_lru = debugfs_create_file("dentry_lru",
424					0600,
425					client->debugfs_dir,
426					client,
427					&dentry_lru_show_fops);
428	if (!client->debugfs_dentry_lru)
429		goto out;
430
431	client->debugfs_caps = debugfs_create_file("caps",
432						   0400,
433						   client->debugfs_dir,
434						   client,
435						   &caps_show_fops);
436	if (!client->debugfs_caps)
437		goto out;
438
439	client->debugfs_congestion_kb =
440		debugfs_create_file("writeback_congestion_kb",
441				    0600,
442				    client->debugfs_dir,
443				    client,
444				    &congestion_kb_fops);
445	if (!client->debugfs_congestion_kb)
446		goto out;
447
448	sprintf(name, "../../bdi/%s", dev_name(client->sb->s_bdi->dev));
449	client->debugfs_bdi = debugfs_create_symlink("bdi", client->debugfs_dir,
450						     name);
451
452	return 0;
453
454out:
455	ceph_debugfs_client_cleanup(client);
456	return ret;
457}
458
459void ceph_debugfs_client_cleanup(struct ceph_client *client)
460{
461	debugfs_remove(client->debugfs_bdi);
462	debugfs_remove(client->debugfs_caps);
463	debugfs_remove(client->debugfs_dentry_lru);
464	debugfs_remove(client->debugfs_osdmap);
465	debugfs_remove(client->debugfs_mdsmap);
466	debugfs_remove(client->debugfs_monmap);
467	debugfs_remove(client->osdc.debugfs_file);
468	debugfs_remove(client->mdsc.debugfs_file);
469	debugfs_remove(client->monc.debugfs_file);
470	debugfs_remove(client->debugfs_congestion_kb);
471	debugfs_remove(client->debugfs_dir);
472}
473
474#else  /* CONFIG_DEBUG_FS */
475
476int __init ceph_debugfs_init(void)
477{
478	return 0;
479}
480
481void ceph_debugfs_cleanup(void)
482{
483}
484
485int ceph_debugfs_client_init(struct ceph_client *client)
486{
487	return 0;
488}
489
490void ceph_debugfs_client_cleanup(struct ceph_client *client)
491{
492}
493
494#endif  /* CONFIG_DEBUG_FS */
495