]> git.ipfire.org Git - thirdparty/mdadm.git/blob - mdstat.c
Introduce pr_err for printing error messages.
[thirdparty/mdadm.git] / mdstat.c
1 /*
2 * mdstat - parse /proc/mdstat file. Part of:
3 * mdadm - manage Linux "md" devices aka RAID arrays.
4 *
5 * Copyright (C) 2002-2009 Neil Brown <neilb@suse.de>
6 *
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * Author: Neil Brown
23 * Email: <neilb@suse.de>
24 */
25
26 /*
27 * The /proc/mdstat file comes in at least 3 flavours:
28 * In an unpatched 2.2 kernel (md 0.36.6):
29 * Personalities : [n raidx] ...
30 * read_ahead {not set|%d sectors}
31 * md0 : {in}active{ raidX /dev/hda... %d blocks{ maxfault=%d}}
32 * md1 : .....
33 *
34 * Normally only 4 md lines, but all are listed.
35 *
36 * In a patched 2.2 kernel (md 0.90.0)
37 * Personalities : [raidx] ...
38 * read_ahead {not set|%d sectors}
39 * mdN : {in}active {(readonly)} raidX dev[%d]{(F)} ... %d blocks STATUS RESYNC
40 * ... Only initialised arrays listed
41 * unused devices: {dev dev ... | <none>}
42 *
43 * STATUS is personality dependant:
44 * linear: %dk rounding
45 * raid0: %dk chunks
46 * raid1: [%d/%d] [U_U] ( raid/working. operational or not)
47 * raid5: level 4/5, %dk chunk, algorithm %d [%d/%d] [U_U]
48 *
49 * RESYNC is empty or:
50 * {resync|recovery}=%u%% finish=%u.%umin
51 * or
52 * resync=DELAYED
53 *
54 * In a 2.4 kernel (md 0.90.0/2.4)
55 * Personalities : [raidX] ...
56 * read_ahead {not set|%d sectors}
57 * mdN : {in}active {(read-only)} raidX dev[%d]{(F)} ...
58 * %d blocks STATUS
59 * RESYNC
60 * unused devices: {dev dev .. | <none>}
61 *
62 * STATUS matches 0.90.0/2.2
63 * RESYNC includes [===>....],
64 * adds a space after {resync|recovery} and before and after '='
65 * adds a decimal to the recovery percent.
66 * adds (%d/%d) resync amount and max_blocks, before finish.
67 * adds speed=%dK/sec after finish
68 *
69 *
70 *
71 * Out of this we want to extract:
72 * list of devices, active or not
73 * pattern of failed drives (so need number of drives)
74 * percent resync complete
75 *
76 * As continuation is indicated by leading space, we use
77 * conf_line from config.c to read logical lines
78 *
79 */
80
81 #include "mdadm.h"
82 #include "dlink.h"
83 #include <sys/select.h>
84 #include <ctype.h>
85
86 static void free_member_devnames(struct dev_member *m)
87 {
88 while(m) {
89 struct dev_member *t = m;
90
91 m = m->next;
92 free(t->name);
93 free(t);
94 }
95 }
96
97 static int add_member_devname(struct dev_member **m, char *name)
98 {
99 struct dev_member *new;
100 char *t;
101
102 if ((t = strchr(name, '[')) == NULL)
103 /* not a device */
104 return 0;
105
106 new = malloc(sizeof(*new));
107 new->name = strndup(name, t - name);
108 new->next = *m;
109 *m = new;
110 return 1;
111 }
112
113 void free_mdstat(struct mdstat_ent *ms)
114 {
115 while (ms) {
116 struct mdstat_ent *t;
117 free(ms->dev);
118 free(ms->level);
119 free(ms->pattern);
120 free(ms->metadata_version);
121 free_member_devnames(ms->members);
122 t = ms;
123 ms = ms->next;
124 free(t);
125 }
126 }
127
128 static int mdstat_fd = -1;
129 struct mdstat_ent *mdstat_read(int hold, int start)
130 {
131 FILE *f;
132 struct mdstat_ent *all, *rv, **end, **insert_here;
133 char *line;
134 int fd;
135
136 if (hold && mdstat_fd != -1) {
137 lseek(mdstat_fd, 0L, 0);
138 fd = dup(mdstat_fd);
139 if (fd >= 0)
140 f = fdopen(fd, "r");
141 else
142 return NULL;
143 } else
144 f = fopen("/proc/mdstat", "r");
145 if (f == NULL)
146 return NULL;
147 else
148 fcntl(fileno(f), F_SETFD, FD_CLOEXEC);
149
150 all = NULL;
151 end = &all;
152 for (; (line = conf_line(f)) ; free_line(line)) {
153 struct mdstat_ent *ent;
154 char *w;
155 int devnum;
156 int in_devs = 0;
157 char *ep;
158
159 if (strcmp(line, "Personalities")==0)
160 continue;
161 if (strcmp(line, "read_ahead")==0)
162 continue;
163 if (strcmp(line, "unused")==0)
164 continue;
165 insert_here = NULL;
166 /* Better be an md line.. */
167 if (strncmp(line, "md", 2)!= 0)
168 continue;
169 if (strncmp(line, "md_d", 4) == 0)
170 devnum = -1-strtoul(line+4, &ep, 10);
171 else if (strncmp(line, "md", 2) == 0)
172 devnum = strtoul(line+2, &ep, 10);
173 else
174 continue;
175 if (ep == NULL || *ep ) {
176 /* pr_err("bad /proc/mdstat line starts: %s\n", line); */
177 continue;
178 }
179
180 ent = malloc(sizeof(*ent));
181 if (!ent) {
182 pr_err("malloc failed reading /proc/mdstat.\n");
183 free_line(line);
184 break;
185 }
186 ent->dev = ent->level = ent->pattern= NULL;
187 ent->next = NULL;
188 ent->percent = RESYNC_NONE;
189 ent->active = -1;
190 ent->resync = 0;
191 ent->metadata_version = NULL;
192 ent->raid_disks = 0;
193 ent->devcnt = 0;
194 ent->members = NULL;
195
196 ent->dev = strdup(line);
197 ent->devnum = devnum;
198
199 for (w=dl_next(line); w!= line ; w=dl_next(w)) {
200 int l = strlen(w);
201 char *eq;
202 if (strcmp(w, "active")==0)
203 ent->active = 1;
204 else if (strcmp(w, "inactive")==0) {
205 ent->active = 0;
206 in_devs = 1;
207 } else if (ent->active > 0 &&
208 ent->level == NULL &&
209 w[0] != '(' /*readonly*/) {
210 ent->level = strdup(w);
211 in_devs = 1;
212 } else if (in_devs && strcmp(w, "blocks")==0)
213 in_devs = 0;
214 else if (in_devs) {
215 ent->devcnt +=
216 add_member_devname(&ent->members, w);
217 if (strncmp(w, "md", 2)==0) {
218 /* This has an md device as a component.
219 * If that device is already in the
220 * list, make sure we insert before
221 * there.
222 */
223 struct mdstat_ent **ih;
224 int dn2 = devname2devnum(w);
225 ih = &all;
226 while (ih != insert_here && *ih &&
227 (*ih)->devnum != dn2)
228 ih = & (*ih)->next;
229 insert_here = ih;
230 }
231 } else if (strcmp(w, "super") == 0 &&
232 dl_next(w) != line) {
233 w = dl_next(w);
234 ent->metadata_version = strdup(w);
235 } else if (w[0] == '[' && isdigit(w[1])) {
236 ent->raid_disks = atoi(w+1);
237 } else if (!ent->pattern &&
238 w[0] == '[' &&
239 (w[1] == 'U' || w[1] == '_')) {
240 ent->pattern = strdup(w+1);
241 if (ent->pattern[l-2]==']')
242 ent->pattern[l-2] = '\0';
243 } else if (ent->percent == RESYNC_NONE &&
244 strncmp(w, "re", 2)== 0 &&
245 w[l-1] == '%' &&
246 (eq=strchr(w, '=')) != NULL ) {
247 ent->percent = atoi(eq+1);
248 if (strncmp(w,"resync", 6)==0)
249 ent->resync = 1;
250 else if (strncmp(w, "reshape", 7)==0)
251 ent->resync = 2;
252 else
253 ent->resync = 0;
254 } else if (ent->percent == RESYNC_NONE &&
255 (w[0] == 'r' || w[0] == 'c')) {
256 if (strncmp(w, "resync", 4)==0)
257 ent->resync = 1;
258 if (strncmp(w, "reshape", 7)==0)
259 ent->resync = 2;
260 if (strncmp(w, "recovery", 8)==0)
261 ent->resync = 0;
262 if (strncmp(w, "check", 5)==0)
263 ent->resync = 3;
264
265 if (l > 8 && strcmp(w+l-8, "=DELAYED") == 0)
266 ent->percent = RESYNC_DELAYED;
267 if (l > 8 && strcmp(w+l-8, "=PENDING") == 0)
268 ent->percent = RESYNC_PENDING;
269 } else if (ent->percent == RESYNC_NONE &&
270 w[0] >= '0' &&
271 w[0] <= '9' &&
272 w[l-1] == '%') {
273 ent->percent = atoi(w);
274 }
275 }
276 if (insert_here && (*insert_here)) {
277 ent->next = *insert_here;
278 *insert_here = ent;
279 } else {
280 *end = ent;
281 end = &ent->next;
282 }
283 }
284 if (hold && mdstat_fd == -1) {
285 mdstat_fd = dup(fileno(f));
286 fcntl(mdstat_fd, F_SETFD, FD_CLOEXEC);
287 }
288 fclose(f);
289
290 /* If we might want to start array,
291 * reverse the order, so that components comes before composites
292 */
293 if (start) {
294 rv = NULL;
295 while (all) {
296 struct mdstat_ent *e = all;
297 all = all->next;
298 e->next = rv;
299 rv = e;
300 }
301 } else rv = all;
302 return rv;
303 }
304
305 void mdstat_wait(int seconds)
306 {
307 fd_set fds;
308 struct timeval tm;
309 int maxfd = 0;
310 FD_ZERO(&fds);
311 if (mdstat_fd >= 0) {
312 FD_SET(mdstat_fd, &fds);
313 maxfd = mdstat_fd;
314 }
315 tm.tv_sec = seconds;
316 tm.tv_usec = 0;
317 select(maxfd + 1, NULL, NULL, &fds, &tm);
318 }
319
320 void mdstat_wait_fd(int fd, const sigset_t *sigmask)
321 {
322 fd_set fds, rfds;
323 int maxfd = 0;
324
325 FD_ZERO(&fds);
326 FD_ZERO(&rfds);
327 if (mdstat_fd >= 0)
328 FD_SET(mdstat_fd, &fds);
329
330 if (fd >= 0) {
331 struct stat stb;
332 fstat(fd, &stb);
333 if ((stb.st_mode & S_IFMT) == S_IFREG)
334 /* Must be a /proc or /sys fd, so expect
335 * POLLPRI
336 * i.e. an 'exceptional' event.
337 */
338 FD_SET(fd, &fds);
339 else
340 FD_SET(fd, &rfds);
341
342 if (fd > maxfd)
343 maxfd = fd;
344
345 }
346 if (mdstat_fd > maxfd)
347 maxfd = mdstat_fd;
348
349 pselect(maxfd + 1, &rfds, NULL, &fds,
350 NULL, sigmask);
351 }
352
353 int mddev_busy(int devnum)
354 {
355 struct mdstat_ent *mdstat = mdstat_read(0, 0);
356 struct mdstat_ent *me;
357
358 for (me = mdstat ; me ; me = me->next)
359 if (me->devnum == devnum)
360 break;
361 free_mdstat(mdstat);
362 return me != NULL;
363 }
364
365 struct mdstat_ent *mdstat_by_component(char *name)
366 {
367 struct mdstat_ent *mdstat = mdstat_read(0, 0);
368
369 while (mdstat) {
370 struct dev_member *m;
371 struct mdstat_ent *ent;
372 if (mdstat->metadata_version &&
373 strncmp(mdstat->metadata_version, "external:", 9) == 0 &&
374 is_subarray(mdstat->metadata_version+9))
375 /* don't return subarrays, only containers */
376 ;
377 else for (m = mdstat->members; m; m = m->next) {
378 if (strcmp(m->name, name) == 0) {
379 free_mdstat(mdstat->next);
380 mdstat->next = NULL;
381 return mdstat;
382 }
383 }
384 ent = mdstat;
385 mdstat = mdstat->next;
386 ent->next = NULL;
387 free_mdstat(ent);
388 }
389 return NULL;
390 }
391
392 struct mdstat_ent *mdstat_by_subdev(char *subdev, int container)
393 {
394 struct mdstat_ent *mdstat = mdstat_read(0, 0);
395
396 while (mdstat) {
397 struct mdstat_ent *ent;
398 char *pos;
399 /* metadata version must match:
400 * external:[/-]md%d/%s
401 * where %d is 'container' and %s is 'subdev'
402 */
403 if (mdstat->metadata_version &&
404 strncmp(mdstat->metadata_version, "external:", 9) == 0 &&
405 strchr("/-", mdstat->metadata_version[9]) != NULL &&
406 strncmp(mdstat->metadata_version+10, "md", 2) == 0 &&
407 strtoul(mdstat->metadata_version+12, &pos, 10)
408 == (unsigned)container &&
409 pos > mdstat->metadata_version+12 &&
410 *pos == '/' &&
411 strcmp(pos+1, subdev) == 0
412 ) {
413 free_mdstat(mdstat->next);
414 mdstat->next = NULL;
415 return mdstat;
416 }
417 ent = mdstat;
418 mdstat = mdstat->next;
419 ent->next = NULL;
420 free_mdstat(ent);
421 }
422 return NULL;
423 }