xref: /linux/drivers/block/drbd/drbd_proc.c (revision 4896e8c1b8fb7e46a65a6676e271fc047a260a3e)
1b411b363SPhilipp Reisner /*
2b411b363SPhilipp Reisner    drbd_proc.c
3b411b363SPhilipp Reisner 
4b411b363SPhilipp Reisner    This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
5b411b363SPhilipp Reisner 
6b411b363SPhilipp Reisner    Copyright (C) 2001-2008, LINBIT Information Technologies GmbH.
7b411b363SPhilipp Reisner    Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>.
8b411b363SPhilipp Reisner    Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
9b411b363SPhilipp Reisner 
10b411b363SPhilipp Reisner    drbd is free software; you can redistribute it and/or modify
11b411b363SPhilipp Reisner    it under the terms of the GNU General Public License as published by
12b411b363SPhilipp Reisner    the Free Software Foundation; either version 2, or (at your option)
13b411b363SPhilipp Reisner    any later version.
14b411b363SPhilipp Reisner 
15b411b363SPhilipp Reisner    drbd is distributed in the hope that it will be useful,
16b411b363SPhilipp Reisner    but WITHOUT ANY WARRANTY; without even the implied warranty of
17b411b363SPhilipp Reisner    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18b411b363SPhilipp Reisner    GNU General Public License for more details.
19b411b363SPhilipp Reisner 
20b411b363SPhilipp Reisner    You should have received a copy of the GNU General Public License
21b411b363SPhilipp Reisner    along with drbd; see the file COPYING.  If not, write to
22b411b363SPhilipp Reisner    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23b411b363SPhilipp Reisner 
24b411b363SPhilipp Reisner  */
25b411b363SPhilipp Reisner 
26b411b363SPhilipp Reisner #include <linux/module.h>
27b411b363SPhilipp Reisner 
28b411b363SPhilipp Reisner #include <asm/uaccess.h>
29b411b363SPhilipp Reisner #include <linux/fs.h>
30b411b363SPhilipp Reisner #include <linux/file.h>
31b411b363SPhilipp Reisner #include <linux/proc_fs.h>
32b411b363SPhilipp Reisner #include <linux/seq_file.h>
33b411b363SPhilipp Reisner #include <linux/drbd.h>
34b411b363SPhilipp Reisner #include "drbd_int.h"
35b411b363SPhilipp Reisner 
36b411b363SPhilipp Reisner static int drbd_proc_open(struct inode *inode, struct file *file);
37b411b363SPhilipp Reisner 
38b411b363SPhilipp Reisner 
39b411b363SPhilipp Reisner struct proc_dir_entry *drbd_proc;
407d4e9d09SEmese Revfy const struct file_operations drbd_proc_fops = {
41b411b363SPhilipp Reisner 	.owner		= THIS_MODULE,
42b411b363SPhilipp Reisner 	.open		= drbd_proc_open,
43b411b363SPhilipp Reisner 	.read		= seq_read,
44b411b363SPhilipp Reisner 	.llseek		= seq_lseek,
45b411b363SPhilipp Reisner 	.release	= single_release,
46b411b363SPhilipp Reisner };
47b411b363SPhilipp Reisner 
48439d5953SLars Ellenberg void seq_printf_with_thousands_grouping(struct seq_file *seq, long v)
49439d5953SLars Ellenberg {
50439d5953SLars Ellenberg 	/* v is in kB/sec. We don't expect TiByte/sec yet. */
51439d5953SLars Ellenberg 	if (unlikely(v >= 1000000)) {
52439d5953SLars Ellenberg 		/* cool: > GiByte/s */
53439d5953SLars Ellenberg 		seq_printf(seq, "%ld,", v / 1000000);
54439d5953SLars Ellenberg 		v /= 1000000;
55439d5953SLars Ellenberg 		seq_printf(seq, "%03ld,%03ld", v/1000, v % 1000);
56439d5953SLars Ellenberg 	} else if (likely(v >= 1000))
57439d5953SLars Ellenberg 		seq_printf(seq, "%ld,%03ld", v/1000, v % 1000);
58439d5953SLars Ellenberg 	else
59439d5953SLars Ellenberg 		seq_printf(seq, "%ld", v);
60439d5953SLars Ellenberg }
61b411b363SPhilipp Reisner 
62b411b363SPhilipp Reisner /*lge
63b411b363SPhilipp Reisner  * progress bars shamelessly adapted from driver/md/md.c
64b411b363SPhilipp Reisner  * output looks like
65b411b363SPhilipp Reisner  *	[=====>..............] 33.5% (23456/123456)
66b411b363SPhilipp Reisner  *	finish: 2:20:20 speed: 6,345 (6,456) K/sec
67b411b363SPhilipp Reisner  */
68b411b363SPhilipp Reisner static void drbd_syncer_progress(struct drbd_conf *mdev, struct seq_file *seq)
69b411b363SPhilipp Reisner {
70b411b363SPhilipp Reisner 	unsigned long db, dt, dbdt, rt, rs_left;
71b411b363SPhilipp Reisner 	unsigned int res;
72b411b363SPhilipp Reisner 	int i, x, y;
731d7734a0SLars Ellenberg 	int stalled = 0;
74b411b363SPhilipp Reisner 
75b411b363SPhilipp Reisner 	drbd_get_syncer_progress(mdev, &rs_left, &res);
76b411b363SPhilipp Reisner 
77b411b363SPhilipp Reisner 	x = res/50;
78b411b363SPhilipp Reisner 	y = 20-x;
79b411b363SPhilipp Reisner 	seq_printf(seq, "\t[");
80b411b363SPhilipp Reisner 	for (i = 1; i < x; i++)
81b411b363SPhilipp Reisner 		seq_printf(seq, "=");
82b411b363SPhilipp Reisner 	seq_printf(seq, ">");
83b411b363SPhilipp Reisner 	for (i = 0; i < y; i++)
84b411b363SPhilipp Reisner 		seq_printf(seq, ".");
85b411b363SPhilipp Reisner 	seq_printf(seq, "] ");
86b411b363SPhilipp Reisner 
875f9915bbSLars Ellenberg 	if (mdev->state.conn == C_VERIFY_S || mdev->state.conn == C_VERIFY_T)
885f9915bbSLars Ellenberg 		seq_printf(seq, "verified:");
895f9915bbSLars Ellenberg 	else
905f9915bbSLars Ellenberg 		seq_printf(seq, "sync'ed:");
915f9915bbSLars Ellenberg 	seq_printf(seq, "%3u.%u%% ", res / 10, res % 10);
925f9915bbSLars Ellenberg 
93b411b363SPhilipp Reisner 	/* if more than 1 GB display in MB */
94b411b363SPhilipp Reisner 	if (mdev->rs_total > 0x100000L)
95e7f52dfbSLars Ellenberg 		seq_printf(seq, "(%lu/%lu)M\n\t",
96b411b363SPhilipp Reisner 			    (unsigned long) Bit2KB(rs_left >> 10),
97b411b363SPhilipp Reisner 			    (unsigned long) Bit2KB(mdev->rs_total >> 10));
98b411b363SPhilipp Reisner 	else
99e7f52dfbSLars Ellenberg 		seq_printf(seq, "(%lu/%lu)K\n\t",
100b411b363SPhilipp Reisner 			    (unsigned long) Bit2KB(rs_left),
101b411b363SPhilipp Reisner 			    (unsigned long) Bit2KB(mdev->rs_total));
102b411b363SPhilipp Reisner 
103b411b363SPhilipp Reisner 	/* see drivers/md/md.c
104b411b363SPhilipp Reisner 	 * We do not want to overflow, so the order of operands and
105b411b363SPhilipp Reisner 	 * the * 100 / 100 trick are important. We do a +1 to be
106b411b363SPhilipp Reisner 	 * safe against division by zero. We only estimate anyway.
107b411b363SPhilipp Reisner 	 *
108b411b363SPhilipp Reisner 	 * dt: time from mark until now
109b411b363SPhilipp Reisner 	 * db: blocks written from mark until now
110b411b363SPhilipp Reisner 	 * rt: remaining time
111b411b363SPhilipp Reisner 	 */
1121d7734a0SLars Ellenberg 	/* Rolling marks. last_mark+1 may just now be modified.  last_mark+2 is
1131d7734a0SLars Ellenberg 	 * at least (DRBD_SYNC_MARKS-2)*DRBD_SYNC_MARK_STEP old, and has at
1141d7734a0SLars Ellenberg 	 * least DRBD_SYNC_MARK_STEP time before it will be modified. */
115439d5953SLars Ellenberg 	/* ------------------------ ~18s average ------------------------ */
1161d7734a0SLars Ellenberg 	i = (mdev->rs_last_mark + 2) % DRBD_SYNC_MARKS;
1171d7734a0SLars Ellenberg 	dt = (jiffies - mdev->rs_mark_time[i]) / HZ;
1181d7734a0SLars Ellenberg 	if (dt > (DRBD_SYNC_MARK_STEP * DRBD_SYNC_MARKS))
1191d7734a0SLars Ellenberg 		stalled = 1;
120b411b363SPhilipp Reisner 
121b411b363SPhilipp Reisner 	if (!dt)
122b411b363SPhilipp Reisner 		dt++;
1231d7734a0SLars Ellenberg 	db = mdev->rs_mark_left[i] - rs_left;
124b411b363SPhilipp Reisner 	rt = (dt * (rs_left / (db/100+1)))/100; /* seconds */
125b411b363SPhilipp Reisner 
126b411b363SPhilipp Reisner 	seq_printf(seq, "finish: %lu:%02lu:%02lu",
127b411b363SPhilipp Reisner 		rt / 3600, (rt % 3600) / 60, rt % 60);
128b411b363SPhilipp Reisner 
129b411b363SPhilipp Reisner 	dbdt = Bit2KB(db/dt);
130439d5953SLars Ellenberg 	seq_printf(seq, " speed: ");
131439d5953SLars Ellenberg 	seq_printf_with_thousands_grouping(seq, dbdt);
132439d5953SLars Ellenberg 	seq_printf(seq, " (");
133439d5953SLars Ellenberg 	/* ------------------------- ~3s average ------------------------ */
134439d5953SLars Ellenberg 	if (proc_details >= 1) {
135439d5953SLars Ellenberg 		/* this is what drbd_rs_should_slow_down() uses */
136439d5953SLars Ellenberg 		i = (mdev->rs_last_mark + DRBD_SYNC_MARKS-1) % DRBD_SYNC_MARKS;
137439d5953SLars Ellenberg 		dt = (jiffies - mdev->rs_mark_time[i]) / HZ;
138439d5953SLars Ellenberg 		if (!dt)
139439d5953SLars Ellenberg 			dt++;
140439d5953SLars Ellenberg 		db = mdev->rs_mark_left[i] - rs_left;
141439d5953SLars Ellenberg 		dbdt = Bit2KB(db/dt);
142439d5953SLars Ellenberg 		seq_printf_with_thousands_grouping(seq, dbdt);
143439d5953SLars Ellenberg 		seq_printf(seq, " -- ");
144439d5953SLars Ellenberg 	}
145439d5953SLars Ellenberg 
146439d5953SLars Ellenberg 	/* --------------------- long term average ---------------------- */
147b411b363SPhilipp Reisner 	/* mean speed since syncer started
148b411b363SPhilipp Reisner 	 * we do account for PausedSync periods */
149b411b363SPhilipp Reisner 	dt = (jiffies - mdev->rs_start - mdev->rs_paused) / HZ;
15022657695SDan Carpenter 	if (dt == 0)
151b411b363SPhilipp Reisner 		dt = 1;
152b411b363SPhilipp Reisner 	db = mdev->rs_total - rs_left;
153b411b363SPhilipp Reisner 	dbdt = Bit2KB(db/dt);
154439d5953SLars Ellenberg 	seq_printf_with_thousands_grouping(seq, dbdt);
155439d5953SLars Ellenberg 	seq_printf(seq, ")");
156b411b363SPhilipp Reisner 
1572649f080SLars Ellenberg 	if (mdev->state.conn == C_SYNC_TARGET ||
1582649f080SLars Ellenberg 	    mdev->state.conn == C_VERIFY_S) {
1595f9915bbSLars Ellenberg 		seq_printf(seq, " want: ");
1605f9915bbSLars Ellenberg 		seq_printf_with_thousands_grouping(seq, mdev->c_sync_rate);
1611d7734a0SLars Ellenberg 	}
1621d7734a0SLars Ellenberg 	seq_printf(seq, " K/sec%s\n", stalled ? " (stalled)" : "");
1635f9915bbSLars Ellenberg 
1645f9915bbSLars Ellenberg 	if (proc_details >= 1) {
1655f9915bbSLars Ellenberg 		/* 64 bit:
1665f9915bbSLars Ellenberg 		 * we convert to sectors in the display below. */
167*4896e8c1SLars Ellenberg 		unsigned long bm_bits = drbd_bm_bits(mdev);
168*4896e8c1SLars Ellenberg 		unsigned long bit_pos;
1695f9915bbSLars Ellenberg 		if (mdev->state.conn == C_VERIFY_S ||
1705f9915bbSLars Ellenberg 		    mdev->state.conn == C_VERIFY_T)
1715f9915bbSLars Ellenberg 			bit_pos = bm_bits - mdev->ov_left;
1725f9915bbSLars Ellenberg 		else
1735f9915bbSLars Ellenberg 			bit_pos = mdev->bm_resync_fo;
1745f9915bbSLars Ellenberg 		/* Total sectors may be slightly off for oddly
1755f9915bbSLars Ellenberg 		 * sized devices. So what. */
1765f9915bbSLars Ellenberg 		seq_printf(seq,
1775f9915bbSLars Ellenberg 			"\t%3d%% sector pos: %llu/%llu\n",
1785f9915bbSLars Ellenberg 			(int)(bit_pos / (bm_bits/100+1)),
179*4896e8c1SLars Ellenberg 			(unsigned long long)bit_pos * BM_SECT_PER_BIT,
180*4896e8c1SLars Ellenberg 			(unsigned long long)bm_bits * BM_SECT_PER_BIT);
1815f9915bbSLars Ellenberg 	}
182b411b363SPhilipp Reisner }
183b411b363SPhilipp Reisner 
184b411b363SPhilipp Reisner static void resync_dump_detail(struct seq_file *seq, struct lc_element *e)
185b411b363SPhilipp Reisner {
186b411b363SPhilipp Reisner 	struct bm_extent *bme = lc_entry(e, struct bm_extent, lce);
187b411b363SPhilipp Reisner 
188b411b363SPhilipp Reisner 	seq_printf(seq, "%5d %s %s\n", bme->rs_left,
189b411b363SPhilipp Reisner 		   bme->flags & BME_NO_WRITES ? "NO_WRITES" : "---------",
190b411b363SPhilipp Reisner 		   bme->flags & BME_LOCKED ? "LOCKED" : "------"
191b411b363SPhilipp Reisner 		   );
192b411b363SPhilipp Reisner }
193b411b363SPhilipp Reisner 
194b411b363SPhilipp Reisner static int drbd_seq_show(struct seq_file *seq, void *v)
195b411b363SPhilipp Reisner {
196b411b363SPhilipp Reisner 	int i, hole = 0;
197b411b363SPhilipp Reisner 	const char *sn;
198b411b363SPhilipp Reisner 	struct drbd_conf *mdev;
199b411b363SPhilipp Reisner 
200b411b363SPhilipp Reisner 	static char write_ordering_chars[] = {
201b411b363SPhilipp Reisner 		[WO_none] = 'n',
202b411b363SPhilipp Reisner 		[WO_drain_io] = 'd',
203b411b363SPhilipp Reisner 		[WO_bdev_flush] = 'f',
204b411b363SPhilipp Reisner 	};
205b411b363SPhilipp Reisner 
206b411b363SPhilipp Reisner 	seq_printf(seq, "version: " REL_VERSION " (api:%d/proto:%d-%d)\n%s\n",
207b411b363SPhilipp Reisner 		   API_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX, drbd_buildtag());
208b411b363SPhilipp Reisner 
209b411b363SPhilipp Reisner 	/*
210b411b363SPhilipp Reisner 	  cs .. connection state
211b411b363SPhilipp Reisner 	  ro .. node role (local/remote)
212b411b363SPhilipp Reisner 	  ds .. disk state (local/remote)
213b411b363SPhilipp Reisner 	     protocol
214b411b363SPhilipp Reisner 	     various flags
215b411b363SPhilipp Reisner 	  ns .. network send
216b411b363SPhilipp Reisner 	  nr .. network receive
217b411b363SPhilipp Reisner 	  dw .. disk write
218b411b363SPhilipp Reisner 	  dr .. disk read
219b411b363SPhilipp Reisner 	  al .. activity log write count
220b411b363SPhilipp Reisner 	  bm .. bitmap update write count
221b411b363SPhilipp Reisner 	  pe .. pending (waiting for ack or data reply)
222b411b363SPhilipp Reisner 	  ua .. unack'd (still need to send ack or data reply)
223b411b363SPhilipp Reisner 	  ap .. application requests accepted, but not yet completed
224b411b363SPhilipp Reisner 	  ep .. number of epochs currently "on the fly", P_BARRIER_ACK pending
225b411b363SPhilipp Reisner 	  wo .. write ordering mode currently in use
226b411b363SPhilipp Reisner 	 oos .. known out-of-sync kB
227b411b363SPhilipp Reisner 	*/
228b411b363SPhilipp Reisner 
229b411b363SPhilipp Reisner 	for (i = 0; i < minor_count; i++) {
230b411b363SPhilipp Reisner 		mdev = minor_to_mdev(i);
231b411b363SPhilipp Reisner 		if (!mdev) {
232b411b363SPhilipp Reisner 			hole = 1;
233b411b363SPhilipp Reisner 			continue;
234b411b363SPhilipp Reisner 		}
235b411b363SPhilipp Reisner 		if (hole) {
236b411b363SPhilipp Reisner 			hole = 0;
237b411b363SPhilipp Reisner 			seq_printf(seq, "\n");
238b411b363SPhilipp Reisner 		}
239b411b363SPhilipp Reisner 
240b411b363SPhilipp Reisner 		sn = drbd_conn_str(mdev->state.conn);
241b411b363SPhilipp Reisner 
242b411b363SPhilipp Reisner 		if (mdev->state.conn == C_STANDALONE &&
243b411b363SPhilipp Reisner 		    mdev->state.disk == D_DISKLESS &&
244b411b363SPhilipp Reisner 		    mdev->state.role == R_SECONDARY) {
245b411b363SPhilipp Reisner 			seq_printf(seq, "%2d: cs:Unconfigured\n", i);
246b411b363SPhilipp Reisner 		} else {
247b411b363SPhilipp Reisner 			seq_printf(seq,
2480778286aSPhilipp Reisner 			   "%2d: cs:%s ro:%s/%s ds:%s/%s %c %c%c%c%c%c%c\n"
249b411b363SPhilipp Reisner 			   "    ns:%u nr:%u dw:%u dr:%u al:%u bm:%u "
250b411b363SPhilipp Reisner 			   "lo:%d pe:%d ua:%d ap:%d ep:%d wo:%c",
251b411b363SPhilipp Reisner 			   i, sn,
252b411b363SPhilipp Reisner 			   drbd_role_str(mdev->state.role),
253b411b363SPhilipp Reisner 			   drbd_role_str(mdev->state.peer),
254b411b363SPhilipp Reisner 			   drbd_disk_str(mdev->state.disk),
255b411b363SPhilipp Reisner 			   drbd_disk_str(mdev->state.pdsk),
256b411b363SPhilipp Reisner 			   (mdev->net_conf == NULL ? ' ' :
257b411b363SPhilipp Reisner 			    (mdev->net_conf->wire_protocol - DRBD_PROT_A+'A')),
258fb22c402SPhilipp Reisner 			   is_susp(mdev->state) ? 's' : 'r',
259b411b363SPhilipp Reisner 			   mdev->state.aftr_isp ? 'a' : '-',
260b411b363SPhilipp Reisner 			   mdev->state.peer_isp ? 'p' : '-',
261b411b363SPhilipp Reisner 			   mdev->state.user_isp ? 'u' : '-',
262b411b363SPhilipp Reisner 			   mdev->congestion_reason ?: '-',
2630778286aSPhilipp Reisner 			   test_bit(AL_SUSPENDED, &mdev->flags) ? 's' : '-',
264b411b363SPhilipp Reisner 			   mdev->send_cnt/2,
265b411b363SPhilipp Reisner 			   mdev->recv_cnt/2,
266b411b363SPhilipp Reisner 			   mdev->writ_cnt/2,
267b411b363SPhilipp Reisner 			   mdev->read_cnt/2,
268b411b363SPhilipp Reisner 			   mdev->al_writ_cnt,
269b411b363SPhilipp Reisner 			   mdev->bm_writ_cnt,
270b411b363SPhilipp Reisner 			   atomic_read(&mdev->local_cnt),
271b411b363SPhilipp Reisner 			   atomic_read(&mdev->ap_pending_cnt) +
272b411b363SPhilipp Reisner 			   atomic_read(&mdev->rs_pending_cnt),
273b411b363SPhilipp Reisner 			   atomic_read(&mdev->unacked_cnt),
274b411b363SPhilipp Reisner 			   atomic_read(&mdev->ap_bio_cnt),
275b411b363SPhilipp Reisner 			   mdev->epochs,
276b411b363SPhilipp Reisner 			   write_ordering_chars[mdev->write_ordering]
277b411b363SPhilipp Reisner 			);
27818edc0b9SLars Ellenberg 			seq_printf(seq, " oos:%llu\n",
27918edc0b9SLars Ellenberg 				   Bit2KB((unsigned long long)
28018edc0b9SLars Ellenberg 					   drbd_bm_total_weight(mdev)));
281b411b363SPhilipp Reisner 		}
282b411b363SPhilipp Reisner 		if (mdev->state.conn == C_SYNC_SOURCE ||
283439d5953SLars Ellenberg 		    mdev->state.conn == C_SYNC_TARGET ||
284439d5953SLars Ellenberg 		    mdev->state.conn == C_VERIFY_S ||
285439d5953SLars Ellenberg 		    mdev->state.conn == C_VERIFY_T)
286b411b363SPhilipp Reisner 			drbd_syncer_progress(mdev, seq);
287b411b363SPhilipp Reisner 
288b411b363SPhilipp Reisner 		if (proc_details >= 1 && get_ldev_if_state(mdev, D_FAILED)) {
289b411b363SPhilipp Reisner 			lc_seq_printf_stats(seq, mdev->resync);
290b411b363SPhilipp Reisner 			lc_seq_printf_stats(seq, mdev->act_log);
291b411b363SPhilipp Reisner 			put_ldev(mdev);
292b411b363SPhilipp Reisner 		}
293b411b363SPhilipp Reisner 
294b411b363SPhilipp Reisner 		if (proc_details >= 2) {
295b411b363SPhilipp Reisner 			if (mdev->resync) {
296b411b363SPhilipp Reisner 				lc_seq_dump_details(seq, mdev->resync, "rs_left",
297b411b363SPhilipp Reisner 					resync_dump_detail);
298b411b363SPhilipp Reisner 			}
299b411b363SPhilipp Reisner 		}
300b411b363SPhilipp Reisner 	}
301b411b363SPhilipp Reisner 
302b411b363SPhilipp Reisner 	return 0;
303b411b363SPhilipp Reisner }
304b411b363SPhilipp Reisner 
305b411b363SPhilipp Reisner static int drbd_proc_open(struct inode *inode, struct file *file)
306b411b363SPhilipp Reisner {
307b411b363SPhilipp Reisner 	return single_open(file, drbd_seq_show, PDE(inode)->data);
308b411b363SPhilipp Reisner }
309b411b363SPhilipp Reisner 
310b411b363SPhilipp Reisner /* PROC FS stuff end */
311