]>
git.ipfire.org Git - thirdparty/mdadm.git/blob - mdstat.c
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 = malloc(sizeof(*new));
107 new->name
= strndup(name
, t
- name
);
113 void free_mdstat(struct mdstat_ent
*ms
)
116 struct mdstat_ent
*t
;
120 free(ms
->metadata_version
);
121 free_member_devnames(ms
->members
);
128 static int mdstat_fd
= -1;
129 struct mdstat_ent
*mdstat_read(int hold
, int start
)
132 struct mdstat_ent
*all
, *rv
, **end
, **insert_here
;
135 if (hold
&& mdstat_fd
!= -1) {
136 lseek(mdstat_fd
, 0L, 0);
137 f
= fdopen(dup(mdstat_fd
), "r");
139 f
= fopen("/proc/mdstat", "r");
143 fcntl(fileno(f
), F_SETFD
, FD_CLOEXEC
);
147 for (; (line
= conf_line(f
)) ; free_line(line
)) {
148 struct mdstat_ent
*ent
;
154 if (strcmp(line
, "Personalities")==0)
156 if (strcmp(line
, "read_ahead")==0)
158 if (strcmp(line
, "unused")==0)
161 /* Better be an md line.. */
162 if (strncmp(line
, "md", 2)!= 0)
164 if (strncmp(line
, "md_d", 4) == 0)
165 devnum
= -1-strtoul(line
+4, &ep
, 10);
166 else if (strncmp(line
, "md", 2) == 0)
167 devnum
= strtoul(line
+2, &ep
, 10);
170 if (ep
== NULL
|| *ep
) {
171 /* fprintf(stderr, Name ": bad /proc/mdstat line starts: %s\n", line); */
175 ent
= malloc(sizeof(*ent
));
177 fprintf(stderr
, Name
": malloc failed reading /proc/mdstat.\n");
181 ent
->dev
= ent
->level
= ent
->pattern
= NULL
;
186 ent
->metadata_version
= NULL
;
191 ent
->dev
= strdup(line
);
192 ent
->devnum
= devnum
;
194 for (w
=dl_next(line
); w
!= line
; w
=dl_next(w
)) {
197 if (strcmp(w
, "active")==0)
199 else if (strcmp(w
, "inactive")==0) {
202 } else if (ent
->active
> 0 &&
203 ent
->level
== NULL
&&
204 w
[0] != '(' /*readonly*/) {
205 ent
->level
= strdup(w
);
207 } else if (in_devs
&& strcmp(w
, "blocks")==0)
211 add_member_devname(&ent
->members
, w
);
212 if (strncmp(w
, "md", 2)==0) {
213 /* This has an md device as a component.
214 * If that device is already in the
215 * list, make sure we insert before
218 struct mdstat_ent
**ih
;
219 int dn2
= devname2devnum(w
);
221 while (ih
!= insert_here
&& *ih
&&
222 (*ih
)->devnum
!= dn2
)
226 } else if (strcmp(w
, "super") == 0 &&
227 dl_next(w
) != line
) {
229 ent
->metadata_version
= strdup(w
);
230 } else if (w
[0] == '[' && isdigit(w
[1])) {
231 ent
->raid_disks
= atoi(w
+1);
232 } else if (!ent
->pattern
&&
234 (w
[1] == 'U' || w
[1] == '_')) {
235 ent
->pattern
= strdup(w
+1);
236 if (ent
->pattern
[l
-2]==']')
237 ent
->pattern
[l
-2] = '\0';
238 } else if (ent
->percent
== -1 &&
239 strncmp(w
, "re", 2)== 0 &&
241 (eq
=strchr(w
, '=')) != NULL
) {
242 ent
->percent
= atoi(eq
+1);
243 if (strncmp(w
,"resync", 6)==0)
245 else if (strncmp(w
, "reshape", 7)==0)
249 } else if (ent
->percent
== -1 &&
250 (w
[0] == 'r' || w
[0] == 'c')) {
251 if (strncmp(w
, "resync", 4)==0)
253 if (strncmp(w
, "reshape", 7)==0)
255 if (strncmp(w
, "recovery", 8)==0)
257 if (strncmp(w
, "check", 5)==0)
260 if (l
> 8 && strcmp(w
+l
-8, "=DELAYED") == 0)
261 ent
->percent
= PROCESS_DELAYED
;
262 if (l
> 8 && strcmp(w
+l
-8, "=PENDING") == 0)
263 ent
->percent
= PROCESS_PENDING
;
264 } else if (ent
->percent
== -1 &&
268 ent
->percent
= atoi(w
);
271 if (insert_here
&& (*insert_here
)) {
272 ent
->next
= *insert_here
;
279 if (hold
&& mdstat_fd
== -1) {
280 mdstat_fd
= dup(fileno(f
));
281 fcntl(mdstat_fd
, F_SETFD
, FD_CLOEXEC
);
285 /* If we might want to start array,
286 * reverse the order, so that components comes before composites
291 struct mdstat_ent
*e
= all
;
300 void mdstat_wait(int seconds
)
306 if (mdstat_fd
>= 0) {
307 FD_SET(mdstat_fd
, &fds
);
312 select(maxfd
+ 1, NULL
, NULL
, &fds
, &tm
);
315 void mdstat_wait_fd(int fd
, const sigset_t
*sigmask
)
323 FD_SET(mdstat_fd
, &fds
);
328 if ((stb
.st_mode
& S_IFMT
) == S_IFREG
)
329 /* Must be a /proc or /sys fd, so expect
331 * i.e. an 'exceptional' event.
341 if (mdstat_fd
> maxfd
)
344 pselect(maxfd
+ 1, &rfds
, NULL
, &fds
,
348 int mddev_busy(int devnum
)
350 struct mdstat_ent
*mdstat
= mdstat_read(0, 0);
351 struct mdstat_ent
*me
;
353 for (me
= mdstat
; me
; me
= me
->next
)
354 if (me
->devnum
== devnum
)
360 struct mdstat_ent
*mdstat_by_component(char *name
)
362 struct mdstat_ent
*mdstat
= mdstat_read(0, 0);
365 struct dev_member
*m
;
366 struct mdstat_ent
*ent
;
367 if (mdstat
->metadata_version
&&
368 strncmp(mdstat
->metadata_version
, "external:", 9) == 0 &&
369 is_subarray(mdstat
->metadata_version
+9))
370 /* don't return subarrays, only containers */
372 else for (m
= mdstat
->members
; m
; m
= m
->next
) {
373 if (strcmp(m
->name
, name
) == 0) {
374 free_mdstat(mdstat
->next
);
380 mdstat
= mdstat
->next
;
387 struct mdstat_ent
*mdstat_by_subdev(char *subdev
, int container
)
389 struct mdstat_ent
*mdstat
= mdstat_read(0, 0);
392 struct mdstat_ent
*ent
;
394 /* metadata version must match:
395 * external:[/-]md%d/%s
396 * where %d is 'container' and %s is 'subdev'
398 if (mdstat
->metadata_version
&&
399 strncmp(mdstat
->metadata_version
, "external:", 9) == 0 &&
400 strchr("/-", mdstat
->metadata_version
[9]) != NULL
&&
401 strncmp(mdstat
->metadata_version
+10, "md", 2) == 0 &&
402 strtoul(mdstat
->metadata_version
+12, &pos
, 10)
403 == (unsigned)container
&&
404 pos
> mdstat
->metadata_version
+12 &&
406 strcmp(pos
+1, subdev
) == 0
408 free_mdstat(mdstat
->next
);
413 mdstat
= mdstat
->next
;