]>
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
85 #include <sys/select.h>
88 static void free_member_devnames(struct dev_member
*m
)
91 struct dev_member
*t
= m
;
99 static int add_member_devname(struct dev_member
**m
, char *name
)
101 struct dev_member
*new;
104 if ((t
= strchr(name
, '[')) == NULL
)
108 new = xmalloc(sizeof(*new));
109 new->name
= strndup(name
, t
- name
);
115 /* Detach element from the list, it may modify list_head */
116 static void mdstat_ent_list_detach_element(struct mdstat_ent
**list_head
, struct mdstat_ent
*el
)
118 struct mdstat_ent
*ent
= *list_head
;
121 *list_head
= ent
->next
;
124 if (ent
->next
== el
) {
125 ent
->next
= el
->next
;
134 /* Guard if not found or list is empty - not allowed */
139 void free_mdstat(struct mdstat_ent
*ms
)
142 struct mdstat_ent
*t
;
145 free(ms
->metadata_version
);
146 free_member_devnames(ms
->members
);
153 bool is_mdstat_ent_external(struct mdstat_ent
*ent
)
155 if (!ent
->metadata_version
)
158 if (strncmp(ent
->metadata_version
, "external:", 9) == 0)
163 bool is_mdstat_ent_subarray(struct mdstat_ent
*ent
)
165 if (is_mdstat_ent_external(ent
) && is_subarray(ent
->metadata_version
+ 9))
170 bool is_container_member(struct mdstat_ent
*mdstat
, char *container
)
172 if (is_mdstat_ent_external(mdstat
) &&
173 metadata_container_matches(mdstat
->metadata_version
+ 9, container
))
179 static int mdstat_fd
= -1;
180 struct mdstat_ent
*mdstat_read(int hold
, int start
)
183 struct mdstat_ent
*all
, *rv
, **end
, **insert_here
;
187 if (hold
&& mdstat_fd
!= -1) {
188 off_t offset
= lseek(mdstat_fd
, 0L, 0);
189 if (offset
== (off_t
)-1) {
198 f
= fopen("/proc/mdstat", "r");
202 if (fcntl(fileno(f
), F_SETFD
, FD_CLOEXEC
) < 0) {
209 for (; (line
= conf_line(f
)) ; free_line(line
)) {
210 struct mdstat_ent
*ent
;
215 if (strcmp(line
, "Personalities") == 0)
217 if (strcmp(line
, "read_ahead") == 0)
219 if (strcmp(line
, "unused") == 0)
222 /* Better be an md line.. */
223 if (strncmp(line
, "md", 2)!= 0 || strlen(line
) >= 32 ||
224 (line
[2] != '_' && !isdigit(line
[2])))
228 ent
= xmalloc(sizeof(*ent
));
229 ent
->level
= ent
->pattern
= NULL
;
231 ent
->percent
= RESYNC_NONE
;
234 ent
->metadata_version
= NULL
;
239 strcpy(ent
->devnm
, devnm
);
241 for (w
=dl_next(line
); w
!= line
; w
=dl_next(w
)) {
244 if (strcmp(w
, "active") == 0)
246 else if (strcmp(w
, "inactive") == 0) {
249 } else if (strcmp(w
, "bitmap:") == 0) {
250 /* We need to stop parsing here;
251 * otherwise, ent->raid_disks will be
252 * overwritten by the wrong value.
255 } else if (ent
->active
> 0 &&
256 ent
->level
== NULL
&&
257 w
[0] != '(' /*readonly*/) {
258 ent
->level
= xstrdup(w
);
260 } else if (in_devs
&& strcmp(w
, "blocks") == 0)
263 char *ep
= strchr(w
, '[');
265 add_member_devname(&ent
->members
, w
);
266 if (ep
&& strncmp(w
, "md", 2) == 0) {
267 /* This has an md device as a component.
268 * If that device is already in the
269 * list, make sure we insert before
272 struct mdstat_ent
**ih
;
274 while (ih
!= insert_here
&& *ih
&&
275 ((int)strlen((*ih
)->devnm
) !=
277 strncmp((*ih
)->devnm
, w
,
282 } else if (strcmp(w
, "super") == 0 &&
283 dl_next(w
) != line
) {
285 ent
->metadata_version
= xstrdup(w
);
286 } else if (w
[0] == '[' && isdigit(w
[1])) {
287 ent
->raid_disks
= atoi(w
+1);
288 } else if (!ent
->pattern
&&
290 (w
[1] == 'U' || w
[1] == '_')) {
291 ent
->pattern
= xstrdup(w
+1);
292 if (ent
->pattern
[l
-2] == ']')
293 ent
->pattern
[l
-2] = '\0';
294 } else if (ent
->percent
== RESYNC_NONE
&&
295 strncmp(w
, "re", 2) == 0 &&
297 (eq
= strchr(w
, '=')) != NULL
) {
298 ent
->percent
= atoi(eq
+1);
299 if (strncmp(w
,"resync", 6) == 0)
301 else if (strncmp(w
, "reshape", 7) == 0)
305 } else if (ent
->percent
== RESYNC_NONE
&&
306 (w
[0] == 'r' || w
[0] == 'c')) {
307 if (strncmp(w
, "resync", 6) == 0)
309 if (strncmp(w
, "reshape", 7) == 0)
311 if (strncmp(w
, "recovery", 8) == 0)
313 if (strncmp(w
, "check", 5) == 0)
316 if (l
> 8 && strcmp(w
+l
-8, "=DELAYED") == 0)
317 ent
->percent
= RESYNC_DELAYED
;
318 if (l
> 8 && strcmp(w
+l
-8, "=PENDING") == 0)
319 ent
->percent
= RESYNC_PENDING
;
320 if (l
> 7 && strcmp(w
+l
-7, "=REMOTE") == 0)
321 ent
->percent
= RESYNC_REMOTE
;
322 } else if (ent
->percent
== RESYNC_NONE
&&
326 ent
->percent
= atoi(w
);
329 if (insert_here
&& (*insert_here
)) {
330 ent
->next
= *insert_here
;
337 if (hold
&& mdstat_fd
== -1) {
338 mdstat_fd
= dup(fileno(f
));
339 if (fcntl(mdstat_fd
, F_SETFD
, FD_CLOEXEC
) < 0) {
346 /* If we might want to start array,
347 * reverse the order, so that components comes before composites
352 struct mdstat_ent
*e
= all
;
362 void mdstat_close(void)
370 * function: mdstat_wait
371 * Description: Function waits for event on mdstat.
373 * seconds - timeout for waiting
375 * > 0 - detected event
377 * < 0 - detected error
379 int mdstat_wait(int seconds
)
385 if (mdstat_fd
>= 0) {
386 FD_SET(mdstat_fd
, &fds
);
394 return select(maxfd
+ 1, NULL
, NULL
, &fds
, &tm
);
397 void mdstat_wait_fd(int fd
, const sigset_t
*sigmask
)
405 FD_SET(mdstat_fd
, &fds
);
409 if (fstat(fd
, &stb
) != 0)
411 if ((stb
.st_mode
& S_IFMT
) == S_IFREG
)
412 /* Must be a /proc or /sys fd, so expect
414 * i.e. an 'exceptional' event.
424 if (mdstat_fd
> maxfd
)
427 pselect(maxfd
+ 1, &rfds
, NULL
, &fds
,
431 int mddev_busy(char *devnm
)
433 struct mdstat_ent
*mdstat
= mdstat_read(0, 0);
434 struct mdstat_ent
*me
;
436 for (me
= mdstat
; me
; me
= me
->next
)
437 if (strcmp(me
->devnm
, devnm
) == 0)
444 * mdstat_find_by_member_devnm()- Return first array or external container with member device.
445 * @mdstat: Preloaded mdstat to iterate over.
446 * @member_devnm: devnm of the device to find.
448 * External subarrays are skipped.
450 struct mdstat_ent
*mdstat_find_by_member_name(struct mdstat_ent
*mdstat
, char *member_devnm
)
452 struct mdstat_ent
*ent
;
454 for (ent
= mdstat
; ent
; ent
= ent
->next
) {
455 struct dev_member
*member
;
457 if (is_mdstat_ent_subarray(ent
))
460 for (member
= ent
->members
; member
; member
= member
->next
)
461 if (strcmp(member
->name
, member_devnm
) == 0)
469 struct mdstat_ent
*mdstat_by_component(char *name
)
471 struct mdstat_ent
*mdstat
= mdstat_read(0, 0);
472 struct mdstat_ent
*ent
= mdstat_find_by_member_name(mdstat
, name
);
475 mdstat_ent_list_detach_element(&mdstat
, ent
);
482 struct mdstat_ent
*mdstat_by_subdev(char *subdev
, char *container
)
484 struct mdstat_ent
*mdstat
= mdstat_read(0, 0);
485 struct mdstat_ent
*ent
= NULL
;
487 for (ent
= mdstat
; ent
; ent
= ent
->next
) {
488 /* metadata version must match:
490 * where first %s is 'container' and second %s is 'subdev'
493 if (!is_mdstat_ent_external(ent
))
496 if (!metadata_container_matches(ent
->metadata_version
+ 9, container
))
498 if (!metadata_subdev_matches(ent
->metadata_version
+ 9, subdev
))
505 mdstat_ent_list_detach_element(&mdstat
, ent
);