2 * mdstat - parse /proc/mdstat file. Part of:
3 * mdadm - manage Linux "md" devices aka RAID arrays.
5 * Copyright (C) 2002-2009 Neil Brown <neilb@suse.de>
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.
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.
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
23 * Email: <neilb@suse.de>
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}}
34 * Normally only 4 md lines, but all are listed.
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>}
43 * STATUS is personality dependant:
44 * linear: %dk rounding
46 * raid1: [%d/%d] [U_U] ( raid/working. operational or not)
47 * raid5: level 4/5, %dk chunk, algorithm %d [%d/%d] [U_U]
50 * {resync|recovery}=%u%% finish=%u.%umin
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)} ...
60 * unused devices: {dev dev .. | <none>}
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
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
76 * As continuation is indicated by leading space, we use
77 * conf_line from config.c to read logical lines
83 #include <sys/select.h>
86 static void free_member_devnames(struct dev_member *m)
89 struct dev_member *t = m;
97 static int add_member_devname(struct dev_member **m, char *name)
99 struct dev_member *new;
102 if ((t = strchr(name, '[')) == NULL)
106 new = xmalloc(sizeof(*new));
107 new->name = strndup(name, t - name);
113 void free_mdstat(struct mdstat_ent *ms)
116 struct mdstat_ent *t;
119 free(ms->metadata_version);
120 free_member_devnames(ms->members);
127 static int mdstat_fd = -1;
128 struct mdstat_ent *mdstat_read(int hold, int start)
131 struct mdstat_ent *all, *rv, **end, **insert_here;
135 if (hold && mdstat_fd != -1) {
136 off_t offset = lseek(mdstat_fd, 0L, 0);
137 if (offset == (off_t)-1) {
147 f = fopen("/proc/mdstat", "r");
151 fcntl(fileno(f), F_SETFD, FD_CLOEXEC);
155 for (; (line = conf_line(f)) ; free_line(line)) {
156 struct mdstat_ent *ent;
161 if (strcmp(line, "Personalities") == 0)
163 if (strcmp(line, "read_ahead") == 0)
165 if (strcmp(line, "unused") == 0)
168 /* Better be an md line.. */
169 if (strncmp(line, "md", 2)!= 0 || strlen(line) >= 32 ||
170 (line[2] != '_' && !isdigit(line[2])))
174 ent = xmalloc(sizeof(*ent));
175 ent->level = ent->pattern= NULL;
177 ent->percent = RESYNC_NONE;
180 ent->metadata_version = NULL;
185 strcpy(ent->devnm, devnm);
187 for (w=dl_next(line); w!= line ; w=dl_next(w)) {
190 if (strcmp(w, "active") == 0)
192 else if (strcmp(w, "inactive") == 0) {
195 } else if (ent->active > 0 &&
196 ent->level == NULL &&
197 w[0] != '(' /*readonly*/) {
198 ent->level = xstrdup(w);
200 } else if (in_devs && strcmp(w, "blocks") == 0)
203 char *ep = strchr(w, '[');
205 add_member_devname(&ent->members, w);
206 if (ep && strncmp(w, "md", 2) == 0) {
207 /* This has an md device as a component.
208 * If that device is already in the
209 * list, make sure we insert before
212 struct mdstat_ent **ih;
214 while (ih != insert_here && *ih &&
215 ((int)strlen((*ih)->devnm) !=
217 strncmp((*ih)->devnm, w,
222 } else if (strcmp(w, "super") == 0 &&
223 dl_next(w) != line) {
225 ent->metadata_version = xstrdup(w);
226 } else if (w[0] == '[' && isdigit(w[1])) {
227 ent->raid_disks = atoi(w+1);
228 } else if (!ent->pattern &&
230 (w[1] == 'U' || w[1] == '_')) {
231 ent->pattern = xstrdup(w+1);
232 if (ent->pattern[l-2] == ']')
233 ent->pattern[l-2] = '\0';
234 } else if (ent->percent == RESYNC_NONE &&
235 strncmp(w, "re", 2) == 0 &&
237 (eq = strchr(w, '=')) != NULL ) {
238 ent->percent = atoi(eq+1);
239 if (strncmp(w,"resync", 6) == 0)
241 else if (strncmp(w, "reshape", 7) == 0)
245 } else if (ent->percent == RESYNC_NONE &&
246 (w[0] == 'r' || w[0] == 'c')) {
247 if (strncmp(w, "resync", 6) == 0)
249 if (strncmp(w, "reshape", 7) == 0)
251 if (strncmp(w, "recovery", 8) == 0)
253 if (strncmp(w, "check", 5) == 0)
256 if (l > 8 && strcmp(w+l-8, "=DELAYED") == 0)
257 ent->percent = RESYNC_DELAYED;
258 if (l > 8 && strcmp(w+l-8, "=PENDING") == 0)
259 ent->percent = RESYNC_PENDING;
260 } else if (ent->percent == RESYNC_NONE &&
264 ent->percent = atoi(w);
267 if (insert_here && (*insert_here)) {
268 ent->next = *insert_here;
275 if (hold && mdstat_fd == -1) {
276 mdstat_fd = dup(fileno(f));
277 fcntl(mdstat_fd, F_SETFD, FD_CLOEXEC);
281 /* If we might want to start array,
282 * reverse the order, so that components comes before composites
287 struct mdstat_ent *e = all;
297 void mdstat_close(void)
304 void mdstat_wait(int seconds)
310 if (mdstat_fd >= 0) {
311 FD_SET(mdstat_fd, &fds);
316 select(maxfd + 1, NULL, NULL, &fds, &tm);
319 void mdstat_wait_fd(int fd, const sigset_t *sigmask)
327 FD_SET(mdstat_fd, &fds);
332 if ((stb.st_mode & S_IFMT) == S_IFREG)
333 /* Must be a /proc or /sys fd, so expect
335 * i.e. an 'exceptional' event.
345 if (mdstat_fd > maxfd)
348 pselect(maxfd + 1, &rfds, NULL, &fds,
352 int mddev_busy(char *devnm)
354 struct mdstat_ent *mdstat = mdstat_read(0, 0);
355 struct mdstat_ent *me;
357 for (me = mdstat ; me ; me = me->next)
358 if (strcmp(me->devnm, devnm) == 0)
364 struct mdstat_ent *mdstat_by_component(char *name)
366 struct mdstat_ent *mdstat = mdstat_read(0, 0);
369 struct dev_member *m;
370 struct mdstat_ent *ent;
371 if (mdstat->metadata_version &&
372 strncmp(mdstat->metadata_version, "external:", 9) == 0 &&
373 is_subarray(mdstat->metadata_version+9))
374 /* don't return subarrays, only containers */
376 else for (m = mdstat->members; m; m = m->next) {
377 if (strcmp(m->name, name) == 0) {
378 free_mdstat(mdstat->next);
384 mdstat = mdstat->next;
391 struct mdstat_ent *mdstat_by_subdev(char *subdev, char *container)
393 struct mdstat_ent *mdstat = mdstat_read(0, 0);
394 struct mdstat_ent *ent = NULL;
397 /* metadata version must match:
399 * where first %s is 'container' and second %s is 'subdev'
404 mdstat = mdstat->next;
407 if (ent->metadata_version == NULL ||
408 strncmp(ent->metadata_version, "external:", 9) != 0)
411 if (!metadata_container_matches(ent->metadata_version+9,
413 !metadata_subdev_matches(ent->metadata_version+9,