1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | drbd_proc.c |
4 | |
5 | This file is part of DRBD by Philipp Reisner and Lars Ellenberg. |
6 | |
7 | Copyright (C) 2001-2008, LINBIT Information Technologies GmbH. |
8 | Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>. |
9 | Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>. |
10 | |
11 | |
12 | */ |
13 | |
14 | #include <linux/module.h> |
15 | |
16 | #include <linux/uaccess.h> |
17 | #include <linux/fs.h> |
18 | #include <linux/file.h> |
19 | #include <linux/proc_fs.h> |
20 | #include <linux/seq_file.h> |
21 | #include <linux/drbd.h> |
22 | #include "drbd_int.h" |
23 | |
24 | struct proc_dir_entry *drbd_proc; |
25 | |
26 | static void seq_printf_with_thousands_grouping(struct seq_file *seq, long v) |
27 | { |
28 | /* v is in kB/sec. We don't expect TiByte/sec yet. */ |
29 | if (unlikely(v >= 1000000)) { |
30 | /* cool: > GiByte/s */ |
31 | seq_printf(m: seq, fmt: "%ld," , v / 1000000); |
32 | v %= 1000000; |
33 | seq_printf(m: seq, fmt: "%03ld,%03ld" , v/1000, v % 1000); |
34 | } else if (likely(v >= 1000)) |
35 | seq_printf(m: seq, fmt: "%ld,%03ld" , v/1000, v % 1000); |
36 | else |
37 | seq_printf(m: seq, fmt: "%ld" , v); |
38 | } |
39 | |
40 | static void drbd_get_syncer_progress(struct drbd_device *device, |
41 | union drbd_dev_state state, unsigned long *rs_total, |
42 | unsigned long *bits_left, unsigned int *per_mil_done) |
43 | { |
44 | /* this is to break it at compile time when we change that, in case we |
45 | * want to support more than (1<<32) bits on a 32bit arch. */ |
46 | typecheck(unsigned long, device->rs_total); |
47 | *rs_total = device->rs_total; |
48 | |
49 | /* note: both rs_total and rs_left are in bits, i.e. in |
50 | * units of BM_BLOCK_SIZE. |
51 | * for the percentage, we don't care. */ |
52 | |
53 | if (state.conn == C_VERIFY_S || state.conn == C_VERIFY_T) |
54 | *bits_left = device->ov_left; |
55 | else |
56 | *bits_left = drbd_bm_total_weight(device) - device->rs_failed; |
57 | /* >> 10 to prevent overflow, |
58 | * +1 to prevent division by zero */ |
59 | if (*bits_left > *rs_total) { |
60 | /* D'oh. Maybe a logic bug somewhere. More likely just a race |
61 | * between state change and reset of rs_total. |
62 | */ |
63 | *bits_left = *rs_total; |
64 | *per_mil_done = *rs_total ? 0 : 1000; |
65 | } else { |
66 | /* Make sure the division happens in long context. |
67 | * We allow up to one petabyte storage right now, |
68 | * at a granularity of 4k per bit that is 2**38 bits. |
69 | * After shift right and multiplication by 1000, |
70 | * this should still fit easily into a 32bit long, |
71 | * so we don't need a 64bit division on 32bit arch. |
72 | * Note: currently we don't support such large bitmaps on 32bit |
73 | * arch anyways, but no harm done to be prepared for it here. |
74 | */ |
75 | unsigned int shift = *rs_total > UINT_MAX ? 16 : 10; |
76 | unsigned long left = *bits_left >> shift; |
77 | unsigned long total = 1UL + (*rs_total >> shift); |
78 | unsigned long tmp = 1000UL - left * 1000UL/total; |
79 | *per_mil_done = tmp; |
80 | } |
81 | } |
82 | |
83 | |
84 | /*lge |
85 | * progress bars shamelessly adapted from driver/md/md.c |
86 | * output looks like |
87 | * [=====>..............] 33.5% (23456/123456) |
88 | * finish: 2:20:20 speed: 6,345 (6,456) K/sec |
89 | */ |
90 | static void drbd_syncer_progress(struct drbd_device *device, struct seq_file *seq, |
91 | union drbd_dev_state state) |
92 | { |
93 | unsigned long db, dt, dbdt, rt, rs_total, rs_left; |
94 | unsigned int res; |
95 | int i, x, y; |
96 | int stalled = 0; |
97 | |
98 | drbd_get_syncer_progress(device, state, rs_total: &rs_total, bits_left: &rs_left, per_mil_done: &res); |
99 | |
100 | x = res/50; |
101 | y = 20-x; |
102 | seq_puts(m: seq, s: "\t[" ); |
103 | for (i = 1; i < x; i++) |
104 | seq_putc(m: seq, c: '='); |
105 | seq_putc(m: seq, c: '>'); |
106 | for (i = 0; i < y; i++) |
107 | seq_putc(m: seq, c: '.'); |
108 | seq_puts(m: seq, s: "] " ); |
109 | |
110 | if (state.conn == C_VERIFY_S || state.conn == C_VERIFY_T) |
111 | seq_puts(m: seq, s: "verified:" ); |
112 | else |
113 | seq_puts(m: seq, s: "sync'ed:" ); |
114 | seq_printf(m: seq, fmt: "%3u.%u%% " , res / 10, res % 10); |
115 | |
116 | /* if more than a few GB, display in MB */ |
117 | if (rs_total > (4UL << (30 - BM_BLOCK_SHIFT))) |
118 | seq_printf(m: seq, fmt: "(%lu/%lu)M" , |
119 | (unsigned long) Bit2KB(rs_left >> 10), |
120 | (unsigned long) Bit2KB(rs_total >> 10)); |
121 | else |
122 | seq_printf(m: seq, fmt: "(%lu/%lu)K" , |
123 | (unsigned long) Bit2KB(rs_left), |
124 | (unsigned long) Bit2KB(rs_total)); |
125 | |
126 | seq_puts(m: seq, s: "\n\t" ); |
127 | |
128 | /* see drivers/md/md.c |
129 | * We do not want to overflow, so the order of operands and |
130 | * the * 100 / 100 trick are important. We do a +1 to be |
131 | * safe against division by zero. We only estimate anyway. |
132 | * |
133 | * dt: time from mark until now |
134 | * db: blocks written from mark until now |
135 | * rt: remaining time |
136 | */ |
137 | /* Rolling marks. last_mark+1 may just now be modified. last_mark+2 is |
138 | * at least (DRBD_SYNC_MARKS-2)*DRBD_SYNC_MARK_STEP old, and has at |
139 | * least DRBD_SYNC_MARK_STEP time before it will be modified. */ |
140 | /* ------------------------ ~18s average ------------------------ */ |
141 | i = (device->rs_last_mark + 2) % DRBD_SYNC_MARKS; |
142 | dt = (jiffies - device->rs_mark_time[i]) / HZ; |
143 | if (dt > 180) |
144 | stalled = 1; |
145 | |
146 | if (!dt) |
147 | dt++; |
148 | db = device->rs_mark_left[i] - rs_left; |
149 | rt = (dt * (rs_left / (db/100+1)))/100; /* seconds */ |
150 | |
151 | seq_printf(m: seq, fmt: "finish: %lu:%02lu:%02lu" , |
152 | rt / 3600, (rt % 3600) / 60, rt % 60); |
153 | |
154 | dbdt = Bit2KB(db/dt); |
155 | seq_puts(m: seq, s: " speed: " ); |
156 | seq_printf_with_thousands_grouping(seq, v: dbdt); |
157 | seq_puts(m: seq, s: " (" ); |
158 | /* ------------------------- ~3s average ------------------------ */ |
159 | if (drbd_proc_details >= 1) { |
160 | /* this is what drbd_rs_should_slow_down() uses */ |
161 | i = (device->rs_last_mark + DRBD_SYNC_MARKS-1) % DRBD_SYNC_MARKS; |
162 | dt = (jiffies - device->rs_mark_time[i]) / HZ; |
163 | if (!dt) |
164 | dt++; |
165 | db = device->rs_mark_left[i] - rs_left; |
166 | dbdt = Bit2KB(db/dt); |
167 | seq_printf_with_thousands_grouping(seq, v: dbdt); |
168 | seq_puts(m: seq, s: " -- " ); |
169 | } |
170 | |
171 | /* --------------------- long term average ---------------------- */ |
172 | /* mean speed since syncer started |
173 | * we do account for PausedSync periods */ |
174 | dt = (jiffies - device->rs_start - device->rs_paused) / HZ; |
175 | if (dt == 0) |
176 | dt = 1; |
177 | db = rs_total - rs_left; |
178 | dbdt = Bit2KB(db/dt); |
179 | seq_printf_with_thousands_grouping(seq, v: dbdt); |
180 | seq_putc(m: seq, c: ')'); |
181 | |
182 | if (state.conn == C_SYNC_TARGET || |
183 | state.conn == C_VERIFY_S) { |
184 | seq_puts(m: seq, s: " want: " ); |
185 | seq_printf_with_thousands_grouping(seq, v: device->c_sync_rate); |
186 | } |
187 | seq_printf(m: seq, fmt: " K/sec%s\n" , stalled ? " (stalled)" : "" ); |
188 | |
189 | if (drbd_proc_details >= 1) { |
190 | /* 64 bit: |
191 | * we convert to sectors in the display below. */ |
192 | unsigned long bm_bits = drbd_bm_bits(device); |
193 | unsigned long bit_pos; |
194 | unsigned long long stop_sector = 0; |
195 | if (state.conn == C_VERIFY_S || |
196 | state.conn == C_VERIFY_T) { |
197 | bit_pos = bm_bits - device->ov_left; |
198 | if (verify_can_do_stop_sector(device)) |
199 | stop_sector = device->ov_stop_sector; |
200 | } else |
201 | bit_pos = device->bm_resync_fo; |
202 | /* Total sectors may be slightly off for oddly |
203 | * sized devices. So what. */ |
204 | seq_printf(m: seq, |
205 | fmt: "\t%3d%% sector pos: %llu/%llu" , |
206 | (int)(bit_pos / (bm_bits/100+1)), |
207 | (unsigned long long)bit_pos * BM_SECT_PER_BIT, |
208 | (unsigned long long)bm_bits * BM_SECT_PER_BIT); |
209 | if (stop_sector != 0 && stop_sector != ULLONG_MAX) |
210 | seq_printf(m: seq, fmt: " stop sector: %llu" , stop_sector); |
211 | seq_putc(m: seq, c: '\n'); |
212 | } |
213 | } |
214 | |
215 | int drbd_seq_show(struct seq_file *seq, void *v) |
216 | { |
217 | int i, prev_i = -1; |
218 | const char *sn; |
219 | struct drbd_device *device; |
220 | struct net_conf *nc; |
221 | union drbd_dev_state state; |
222 | char wp; |
223 | |
224 | static char write_ordering_chars[] = { |
225 | [WO_NONE] = 'n', |
226 | [WO_DRAIN_IO] = 'd', |
227 | [WO_BDEV_FLUSH] = 'f', |
228 | }; |
229 | |
230 | seq_printf(m: seq, fmt: "version: " REL_VERSION " (api:%d/proto:%d-%d)\n%s\n" , |
231 | GENL_MAGIC_VERSION, PRO_VERSION_MIN, PRO_VERSION_MAX, drbd_buildtag()); |
232 | |
233 | /* |
234 | cs .. connection state |
235 | ro .. node role (local/remote) |
236 | ds .. disk state (local/remote) |
237 | protocol |
238 | various flags |
239 | ns .. network send |
240 | nr .. network receive |
241 | dw .. disk write |
242 | dr .. disk read |
243 | al .. activity log write count |
244 | bm .. bitmap update write count |
245 | pe .. pending (waiting for ack or data reply) |
246 | ua .. unack'd (still need to send ack or data reply) |
247 | ap .. application requests accepted, but not yet completed |
248 | ep .. number of epochs currently "on the fly", P_BARRIER_ACK pending |
249 | wo .. write ordering mode currently in use |
250 | oos .. known out-of-sync kB |
251 | */ |
252 | |
253 | rcu_read_lock(); |
254 | idr_for_each_entry(&drbd_devices, device, i) { |
255 | if (prev_i != i - 1) |
256 | seq_putc(m: seq, c: '\n'); |
257 | prev_i = i; |
258 | |
259 | state = device->state; |
260 | sn = drbd_conn_str(state.conn); |
261 | |
262 | if (state.conn == C_STANDALONE && |
263 | state.disk == D_DISKLESS && |
264 | state.role == R_SECONDARY) { |
265 | seq_printf(m: seq, fmt: "%2d: cs:Unconfigured\n" , i); |
266 | } else { |
267 | /* reset device->congestion_reason */ |
268 | |
269 | nc = rcu_dereference(first_peer_device(device)->connection->net_conf); |
270 | wp = nc ? nc->wire_protocol - DRBD_PROT_A + 'A' : ' '; |
271 | seq_printf(m: seq, |
272 | fmt: "%2d: cs:%s ro:%s/%s ds:%s/%s %c %c%c%c%c%c%c\n" |
273 | " ns:%u nr:%u dw:%u dr:%u al:%u bm:%u " |
274 | "lo:%d pe:%d ua:%d ap:%d ep:%d wo:%c" , |
275 | i, sn, |
276 | drbd_role_str(state.role), |
277 | drbd_role_str(state.peer), |
278 | drbd_disk_str(state.disk), |
279 | drbd_disk_str(state.pdsk), |
280 | wp, |
281 | drbd_suspended(device) ? 's' : 'r', |
282 | state.aftr_isp ? 'a' : '-', |
283 | state.peer_isp ? 'p' : '-', |
284 | state.user_isp ? 'u' : '-', |
285 | device->congestion_reason ?: '-', |
286 | test_bit(AL_SUSPENDED, &device->flags) ? 's' : '-', |
287 | device->send_cnt/2, |
288 | device->recv_cnt/2, |
289 | device->writ_cnt/2, |
290 | device->read_cnt/2, |
291 | device->al_writ_cnt, |
292 | device->bm_writ_cnt, |
293 | atomic_read(v: &device->local_cnt), |
294 | atomic_read(v: &device->ap_pending_cnt) + |
295 | atomic_read(v: &device->rs_pending_cnt), |
296 | atomic_read(v: &device->unacked_cnt), |
297 | atomic_read(v: &device->ap_bio_cnt), |
298 | first_peer_device(device)->connection->epochs, |
299 | write_ordering_chars[device->resource->write_ordering] |
300 | ); |
301 | seq_printf(m: seq, fmt: " oos:%llu\n" , |
302 | Bit2KB((unsigned long long) |
303 | drbd_bm_total_weight(device))); |
304 | } |
305 | if (state.conn == C_SYNC_SOURCE || |
306 | state.conn == C_SYNC_TARGET || |
307 | state.conn == C_VERIFY_S || |
308 | state.conn == C_VERIFY_T) |
309 | drbd_syncer_progress(device, seq, state); |
310 | |
311 | if (drbd_proc_details >= 1 && get_ldev_if_state(device, D_FAILED)) { |
312 | lc_seq_printf_stats(seq, lc: device->resync); |
313 | lc_seq_printf_stats(seq, lc: device->act_log); |
314 | put_ldev(device); |
315 | } |
316 | |
317 | if (drbd_proc_details >= 2) |
318 | seq_printf(m: seq, fmt: "\tblocked on activity log: %d\n" , atomic_read(v: &device->ap_actlog_cnt)); |
319 | } |
320 | rcu_read_unlock(); |
321 | |
322 | return 0; |
323 | } |
324 | |