2 * mdstat - parse /proc/mdstat file. Part of:
3 * mdadm - manage Linux "md" devices aka RAID arrays.
5 * Copyright (C) 2002-2006 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@cse.unsw.edu.au>
25 * School of Computer Science and Engineering
26 * The University of New South Wales
32 * The /proc/mdstat file comes in at least 3 flavours:
33 * In an unpatched 2.2 kernel (md 0.36.6):
34 * Personalities : [n raidx] ...
35 * read_ahead {not set|%d sectors}
36 * md0 : {in}active{ raidX /dev/hda... %d blocks{ maxfault=%d}}
39 * Normally only 4 md lines, but all are listed.
41 * In a patched 2.2 kernel (md 0.90.0)
42 * Personalities : [raidx] ...
43 * read_ahead {not set|%d sectors}
44 * mdN : {in}active {(readonly)} raidX dev[%d]{(F)} ... %d blocks STATUS RESYNC
45 * ... Only initialised arrays listed
46 * unused devices: {dev dev ... | <none>}
48 * STATUS is personality dependant:
49 * linear: %dk rounding
51 * raid1: [%d/%d] [U_U] ( raid/working. operational or not)
52 * raid5: level 4/5, %dk chunk, algorithm %d [%d/%d] [U_U]
55 * {resync|recovery}=%u%% finish=%u.%umin
59 * In a 2.4 kernel (md 0.90.0/2.4)
60 * Personalities : [raidX] ...
61 * read_ahead {not set|%d sectors}
62 * mdN : {in}active {(read-only)} raidX dev[%d]{(F)} ...
65 * unused devices: {dev dev .. | <none>}
67 * STATUS matches 0.90.0/2.2
68 * RESYNC includes [===>....],
69 * adds a space after {resync|recovery} and before and after '='
70 * adds a decimal to the recovery percent.
71 * adds (%d/%d) resync amount and max_blocks, before finish.
72 * adds speed=%dK/sec after finish
76 * Out of this we want to extract:
77 * list of devices, active or not
78 * pattern of failed drives (so need number of drives)
79 * percent resync complete
81 * As continuation is indicated by leading space, we use
82 * conf_line from config.c to read logical lines
88 #include <sys/select.h>
90 void free_mdstat(struct mdstat_ent
*ms
)
94 if (ms
->dev
) free(ms
->dev
);
95 if (ms
->level
) free(ms
->level
);
96 if (ms
->pattern
) free(ms
->pattern
);
103 static int mdstat_fd
= -1;
104 struct mdstat_ent
*mdstat_read(int hold
, int start
)
107 struct mdstat_ent
*all
, *rv
, **end
, **insert_here
;
110 if (hold
&& mdstat_fd
!= -1) {
111 lseek(mdstat_fd
, 0L, 0);
112 f
= fdopen(dup(mdstat_fd
), "r");
114 f
= fopen("/proc/mdstat", "r");
120 for (; (line
= conf_line(f
)) ; free_line(line
)) {
121 struct mdstat_ent
*ent
;
127 if (strcmp(line
, "Personalities")==0)
129 if (strcmp(line
, "read_ahead")==0)
131 if (strcmp(line
, "unused")==0)
134 /* Better be an md line.. */
135 if (strncmp(line
, "md", 2)!= 0)
137 if (strncmp(line
, "md_d", 4) == 0)
138 devnum
= -1-strtoul(line
+4, &ep
, 10);
139 else if (strncmp(line
, "md", 2) == 0)
140 devnum
= strtoul(line
+2, &ep
, 10);
143 if (ep
== NULL
|| *ep
) {
144 /* fprintf(stderr, Name ": bad /proc/mdstat line starts: %s\n", line); */
148 ent
= malloc(sizeof(*ent
));
150 fprintf(stderr
, Name
": malloc failed reading /proc/mdstat.\n");
154 ent
->dev
= ent
->level
= ent
->pattern
= NULL
;
160 ent
->dev
= strdup(line
);
161 ent
->devnum
= devnum
;
163 for (w
=dl_next(line
); w
!= line
; w
=dl_next(w
)) {
166 if (strcmp(w
, "active")==0)
168 else if (strcmp(w
, "inactive")==0)
170 else if (ent
->active
>=0 &&
171 ent
->level
== NULL
&&
172 w
[0] != '(' /*readonly*/) {
173 ent
->level
= strdup(w
);
175 } else if (in_devs
&& strcmp(w
, "blocks")==0)
177 else if (in_devs
&& strncmp(w
, "md", 2)==0) {
178 /* This has an md device as a component.
179 * If that device is already in the list,
180 * make sure we insert before there.
182 struct mdstat_ent
**ih
;
184 if (strncmp(w
, "md_d", 4)==0)
185 dn2
= -1-strtoul(w
+4, &ep
, 10);
187 dn2
= strtoul(w
+2, &ep
, 10);
189 while (ih
!= insert_here
&& *ih
&&
190 (*ih
)->devnum
!= dn2
)
193 } else if (!ent
->pattern
&&
195 (w
[1] == 'U' || w
[1] == '_')) {
196 ent
->pattern
= strdup(w
+1);
197 if (ent
->pattern
[l
-2]==']')
198 ent
->pattern
[l
-2] = '\0';
199 } else if (ent
->percent
== -1 &&
200 strncmp(w
, "re", 2)== 0 &&
202 (eq
=strchr(w
, '=')) != NULL
) {
203 ent
->percent
= atoi(eq
+1);
204 if (strncmp(w
,"resync", 4)==0)
206 } else if (ent
->percent
== -1 &&
207 strncmp(w
, "resync", 4)==0) {
209 } else if (ent
->percent
== -1 &&
213 ent
->percent
= atoi(w
);
216 if (insert_here
&& (*insert_here
)) {
217 ent
->next
= *insert_here
;
224 if (hold
&& mdstat_fd
== -1)
225 mdstat_fd
= dup(fileno(f
));
228 /* If we might want to start array,
229 * reverse the order, so that components comes before composites
234 struct mdstat_ent
*e
= all
;
243 void mdstat_wait(int seconds
)
249 FD_SET(mdstat_fd
, &fds
);
252 select(mdstat_fd
>2 ? mdstat_fd
+1:3, NULL
, NULL
, &fds
, &tm
);
255 int mddev_busy(int devnum
)
257 struct mdstat_ent
*mdstat
= mdstat_read(0, 0);
258 struct mdstat_ent
*me
;
260 for (me
= mdstat
; me
; me
= me
->next
)
261 if (me
->devnum
== devnum
)