Merge branch 'for-linus' of git://git.kernel.dk/linux-2.6-block
[pandora-kernel.git] / drivers / block / drbd / drbd_proc.c
1 /*
2    drbd_proc.c
3
4    This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
5
6    Copyright (C) 2001-2008, LINBIT Information Technologies GmbH.
7    Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>.
8    Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
9
10    drbd is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2, or (at your option)
13    any later version.
14
15    drbd is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with drbd; see the file COPYING.  If not, write to
22    the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
24  */
25
26 #include <linux/module.h>
27
28 #include <asm/uaccess.h>
29 #include <linux/fs.h>
30 #include <linux/file.h>
31 #include <linux/proc_fs.h>
32 #include <linux/seq_file.h>
33 #include <linux/drbd.h>
34 #include "drbd_int.h"
35
36 static int drbd_proc_open(struct inode *inode, struct file *file);
37
38
39 struct proc_dir_entry *drbd_proc;
40 const struct file_operations drbd_proc_fops = {
41         .owner          = THIS_MODULE,
42         .open           = drbd_proc_open,
43         .read           = seq_read,
44         .llseek         = seq_lseek,
45         .release        = single_release,
46 };
47
48
49 /*lge
50  * progress bars shamelessly adapted from driver/md/md.c
51  * output looks like
52  *      [=====>..............] 33.5% (23456/123456)
53  *      finish: 2:20:20 speed: 6,345 (6,456) K/sec
54  */
55 static void drbd_syncer_progress(struct drbd_conf *mdev, struct seq_file *seq)
56 {
57         unsigned long db, dt, dbdt, rt, rs_left;
58         unsigned int res;
59         int i, x, y;
60         int stalled = 0;
61
62         drbd_get_syncer_progress(mdev, &rs_left, &res);
63
64         x = res/50;
65         y = 20-x;
66         seq_printf(seq, "\t[");
67         for (i = 1; i < x; i++)
68                 seq_printf(seq, "=");
69         seq_printf(seq, ">");
70         for (i = 0; i < y; i++)
71                 seq_printf(seq, ".");
72         seq_printf(seq, "] ");
73
74         seq_printf(seq, "sync'ed:%3u.%u%% ", res / 10, res % 10);
75         /* if more than 1 GB display in MB */
76         if (mdev->rs_total > 0x100000L)
77                 seq_printf(seq, "(%lu/%lu)M\n\t",
78                             (unsigned long) Bit2KB(rs_left >> 10),
79                             (unsigned long) Bit2KB(mdev->rs_total >> 10));
80         else
81                 seq_printf(seq, "(%lu/%lu)K\n\t",
82                             (unsigned long) Bit2KB(rs_left),
83                             (unsigned long) Bit2KB(mdev->rs_total));
84
85         /* see drivers/md/md.c
86          * We do not want to overflow, so the order of operands and
87          * the * 100 / 100 trick are important. We do a +1 to be
88          * safe against division by zero. We only estimate anyway.
89          *
90          * dt: time from mark until now
91          * db: blocks written from mark until now
92          * rt: remaining time
93          */
94         /* Rolling marks. last_mark+1 may just now be modified.  last_mark+2 is
95          * at least (DRBD_SYNC_MARKS-2)*DRBD_SYNC_MARK_STEP old, and has at
96          * least DRBD_SYNC_MARK_STEP time before it will be modified. */
97         i = (mdev->rs_last_mark + 2) % DRBD_SYNC_MARKS;
98         dt = (jiffies - mdev->rs_mark_time[i]) / HZ;
99         if (dt > (DRBD_SYNC_MARK_STEP * DRBD_SYNC_MARKS))
100                 stalled = 1;
101
102         if (!dt)
103                 dt++;
104         db = mdev->rs_mark_left[i] - rs_left;
105         rt = (dt * (rs_left / (db/100+1)))/100; /* seconds */
106
107         seq_printf(seq, "finish: %lu:%02lu:%02lu",
108                 rt / 3600, (rt % 3600) / 60, rt % 60);
109
110         /* current speed average over (SYNC_MARKS * SYNC_MARK_STEP) jiffies */
111         dbdt = Bit2KB(db/dt);
112         if (dbdt > 1000)
113                 seq_printf(seq, " speed: %ld,%03ld",
114                         dbdt/1000, dbdt % 1000);
115         else
116                 seq_printf(seq, " speed: %ld", dbdt);
117
118         /* mean speed since syncer started
119          * we do account for PausedSync periods */
120         dt = (jiffies - mdev->rs_start - mdev->rs_paused) / HZ;
121         if (dt == 0)
122                 dt = 1;
123         db = mdev->rs_total - rs_left;
124         dbdt = Bit2KB(db/dt);
125         if (dbdt > 1000)
126                 seq_printf(seq, " (%ld,%03ld)",
127                         dbdt/1000, dbdt % 1000);
128         else
129                 seq_printf(seq, " (%ld)", dbdt);
130
131         if (mdev->state.conn == C_SYNC_TARGET) {
132                 if (mdev->c_sync_rate > 1000)
133                         seq_printf(seq, " want: %d,%03d",
134                                    mdev->c_sync_rate / 1000, mdev->c_sync_rate % 1000);
135                 else
136                         seq_printf(seq, " want: %d", mdev->c_sync_rate);
137         }
138         seq_printf(seq, " K/sec%s\n", stalled ? " (stalled)" : "");
139 }
140
141 static void resync_dump_detail(struct seq_file *seq, struct lc_element *e)
142 {
143         struct bm_extent *bme = lc_entry(e, struct bm_extent, lce);
144
145         seq_printf(seq, "%5d %s %s\n", bme->rs_left,
146                    bme->flags & BME_NO_WRITES ? "NO_WRITES" : "---------",
147                    bme->flags & BME_LOCKED ? "LOCKED" : "------"
148                    );
149 }
150
151 static int drbd_seq_show(struct seq_file *seq, void *v)
152 {
153         int i, hole = 0;
154         const char *sn;
155         struct drbd_conf *mdev;
156
157         static char write_ordering_chars[] = {
158                 [WO_none] = 'n',
159                 [WO_drain_io] = 'd',
160                 [WO_bdev_flush] = 'f',
161         };
162
163         seq_printf(seq, "version: " REL_VERSION " (api:%d/proto:%d-%d)\n%s\n",
164                    API_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX, drbd_buildtag());
165
166         /*
167           cs .. connection state
168           ro .. node role (local/remote)
169           ds .. disk state (local/remote)
170              protocol
171              various flags
172           ns .. network send
173           nr .. network receive
174           dw .. disk write
175           dr .. disk read
176           al .. activity log write count
177           bm .. bitmap update write count
178           pe .. pending (waiting for ack or data reply)
179           ua .. unack'd (still need to send ack or data reply)
180           ap .. application requests accepted, but not yet completed
181           ep .. number of epochs currently "on the fly", P_BARRIER_ACK pending
182           wo .. write ordering mode currently in use
183          oos .. known out-of-sync kB
184         */
185
186         for (i = 0; i < minor_count; i++) {
187                 mdev = minor_to_mdev(i);
188                 if (!mdev) {
189                         hole = 1;
190                         continue;
191                 }
192                 if (hole) {
193                         hole = 0;
194                         seq_printf(seq, "\n");
195                 }
196
197                 sn = drbd_conn_str(mdev->state.conn);
198
199                 if (mdev->state.conn == C_STANDALONE &&
200                     mdev->state.disk == D_DISKLESS &&
201                     mdev->state.role == R_SECONDARY) {
202                         seq_printf(seq, "%2d: cs:Unconfigured\n", i);
203                 } else {
204                         seq_printf(seq,
205                            "%2d: cs:%s ro:%s/%s ds:%s/%s %c %c%c%c%c%c%c\n"
206                            "    ns:%u nr:%u dw:%u dr:%u al:%u bm:%u "
207                            "lo:%d pe:%d ua:%d ap:%d ep:%d wo:%c",
208                            i, sn,
209                            drbd_role_str(mdev->state.role),
210                            drbd_role_str(mdev->state.peer),
211                            drbd_disk_str(mdev->state.disk),
212                            drbd_disk_str(mdev->state.pdsk),
213                            (mdev->net_conf == NULL ? ' ' :
214                             (mdev->net_conf->wire_protocol - DRBD_PROT_A+'A')),
215                            is_susp(mdev->state) ? 's' : 'r',
216                            mdev->state.aftr_isp ? 'a' : '-',
217                            mdev->state.peer_isp ? 'p' : '-',
218                            mdev->state.user_isp ? 'u' : '-',
219                            mdev->congestion_reason ?: '-',
220                            test_bit(AL_SUSPENDED, &mdev->flags) ? 's' : '-',
221                            mdev->send_cnt/2,
222                            mdev->recv_cnt/2,
223                            mdev->writ_cnt/2,
224                            mdev->read_cnt/2,
225                            mdev->al_writ_cnt,
226                            mdev->bm_writ_cnt,
227                            atomic_read(&mdev->local_cnt),
228                            atomic_read(&mdev->ap_pending_cnt) +
229                            atomic_read(&mdev->rs_pending_cnt),
230                            atomic_read(&mdev->unacked_cnt),
231                            atomic_read(&mdev->ap_bio_cnt),
232                            mdev->epochs,
233                            write_ordering_chars[mdev->write_ordering]
234                         );
235                         seq_printf(seq, " oos:%lu\n",
236                                    Bit2KB(drbd_bm_total_weight(mdev)));
237                 }
238                 if (mdev->state.conn == C_SYNC_SOURCE ||
239                     mdev->state.conn == C_SYNC_TARGET)
240                         drbd_syncer_progress(mdev, seq);
241
242                 if (mdev->state.conn == C_VERIFY_S || mdev->state.conn == C_VERIFY_T)
243                         seq_printf(seq, "\t%3d%%      %lu/%lu\n",
244                                    (int)((mdev->rs_total-mdev->ov_left) /
245                                          (mdev->rs_total/100+1)),
246                                    mdev->rs_total - mdev->ov_left,
247                                    mdev->rs_total);
248
249                 if (proc_details >= 1 && get_ldev_if_state(mdev, D_FAILED)) {
250                         lc_seq_printf_stats(seq, mdev->resync);
251                         lc_seq_printf_stats(seq, mdev->act_log);
252                         put_ldev(mdev);
253                 }
254
255                 if (proc_details >= 2) {
256                         if (mdev->resync) {
257                                 lc_seq_dump_details(seq, mdev->resync, "rs_left",
258                                         resync_dump_detail);
259                         }
260                 }
261         }
262
263         return 0;
264 }
265
266 static int drbd_proc_open(struct inode *inode, struct file *file)
267 {
268         return single_open(file, drbd_seq_show, PDE(inode)->data);
269 }
270
271 /* PROC FS stuff end */