From 2.6.30, /proc/mounts and various /sys files will probably always returns 'readable' to select, so we will need to wait on POLLPRI to get the 'new data is available' signal. When using select, this corresponds to an 'exception', so adjust calls to select accordingly. In one case we sometimes wait on a socket and sometime on /proc/mounts, so we need to test which. Signed-off-by: NeilBrown <neilb@suse.de>
313 lines
7.8 KiB
C
313 lines
7.8 KiB
C
/*
|
|
* mdstat - parse /proc/mdstat file. Part of:
|
|
* mdadm - manage Linux "md" devices aka RAID arrays.
|
|
*
|
|
* Copyright (C) 2002-2006 Neil Brown <neilb@suse.de>
|
|
*
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* Author: Neil Brown
|
|
* Email: <neilb@cse.unsw.edu.au>
|
|
* Paper: Neil Brown
|
|
* School of Computer Science and Engineering
|
|
* The University of New South Wales
|
|
* Sydney, 2052
|
|
* Australia
|
|
*/
|
|
|
|
/*
|
|
* The /proc/mdstat file comes in at least 3 flavours:
|
|
* In an unpatched 2.2 kernel (md 0.36.6):
|
|
* Personalities : [n raidx] ...
|
|
* read_ahead {not set|%d sectors}
|
|
* md0 : {in}active{ raidX /dev/hda... %d blocks{ maxfault=%d}}
|
|
* md1 : .....
|
|
*
|
|
* Normally only 4 md lines, but all are listed.
|
|
*
|
|
* In a patched 2.2 kernel (md 0.90.0)
|
|
* Personalities : [raidx] ...
|
|
* read_ahead {not set|%d sectors}
|
|
* mdN : {in}active {(readonly)} raidX dev[%d]{(F)} ... %d blocks STATUS RESYNC
|
|
* ... Only initialised arrays listed
|
|
* unused devices: {dev dev ... | <none>}
|
|
*
|
|
* STATUS is personality dependant:
|
|
* linear: %dk rounding
|
|
* raid0: %dk chunks
|
|
* raid1: [%d/%d] [U_U] ( raid/working. operational or not)
|
|
* raid5: level 4/5, %dk chunk, algorithm %d [%d/%d] [U_U]
|
|
*
|
|
* RESYNC is empty or:
|
|
* {resync|recovery}=%u%% finish=%u.%umin
|
|
* or
|
|
* resync=DELAYED
|
|
*
|
|
* In a 2.4 kernel (md 0.90.0/2.4)
|
|
* Personalities : [raidX] ...
|
|
* read_ahead {not set|%d sectors}
|
|
* mdN : {in}active {(read-only)} raidX dev[%d]{(F)} ...
|
|
* %d blocks STATUS
|
|
* RESYNC
|
|
* unused devices: {dev dev .. | <none>}
|
|
*
|
|
* STATUS matches 0.90.0/2.2
|
|
* RESYNC includes [===>....],
|
|
* adds a space after {resync|recovery} and before and after '='
|
|
* adds a decimal to the recovery percent.
|
|
* adds (%d/%d) resync amount and max_blocks, before finish.
|
|
* adds speed=%dK/sec after finish
|
|
*
|
|
*
|
|
*
|
|
* Out of this we want to extract:
|
|
* list of devices, active or not
|
|
* pattern of failed drives (so need number of drives)
|
|
* percent resync complete
|
|
*
|
|
* As continuation is indicated by leading space, we use
|
|
* conf_line from config.c to read logical lines
|
|
*
|
|
*/
|
|
|
|
#include "mdadm.h"
|
|
#include "dlink.h"
|
|
#include <sys/select.h>
|
|
#include <ctype.h>
|
|
|
|
void free_mdstat(struct mdstat_ent *ms)
|
|
{
|
|
while (ms) {
|
|
struct mdstat_ent *t;
|
|
if (ms->dev) free(ms->dev);
|
|
if (ms->level) free(ms->level);
|
|
if (ms->pattern) free(ms->pattern);
|
|
if (ms->metadata_version) free(ms->metadata_version);
|
|
t = ms;
|
|
ms = ms->next;
|
|
free(t);
|
|
}
|
|
}
|
|
|
|
static int mdstat_fd = -1;
|
|
struct mdstat_ent *mdstat_read(int hold, int start)
|
|
{
|
|
FILE *f;
|
|
struct mdstat_ent *all, *rv, **end, **insert_here;
|
|
char *line;
|
|
|
|
if (hold && mdstat_fd != -1) {
|
|
lseek(mdstat_fd, 0L, 0);
|
|
f = fdopen(dup(mdstat_fd), "r");
|
|
} else
|
|
f = fopen("/proc/mdstat", "r");
|
|
if (f == NULL)
|
|
return NULL;
|
|
else
|
|
fcntl(fileno(f), F_SETFD, FD_CLOEXEC);
|
|
|
|
all = NULL;
|
|
end = &all;
|
|
for (; (line = conf_line(f)) ; free_line(line)) {
|
|
struct mdstat_ent *ent;
|
|
char *w;
|
|
int devnum;
|
|
int in_devs = 0;
|
|
char *ep;
|
|
|
|
if (strcmp(line, "Personalities")==0)
|
|
continue;
|
|
if (strcmp(line, "read_ahead")==0)
|
|
continue;
|
|
if (strcmp(line, "unused")==0)
|
|
continue;
|
|
insert_here = NULL;
|
|
/* Better be an md line.. */
|
|
if (strncmp(line, "md", 2)!= 0)
|
|
continue;
|
|
if (strncmp(line, "md_d", 4) == 0)
|
|
devnum = -1-strtoul(line+4, &ep, 10);
|
|
else if (strncmp(line, "md", 2) == 0)
|
|
devnum = strtoul(line+2, &ep, 10);
|
|
else
|
|
continue;
|
|
if (ep == NULL || *ep ) {
|
|
/* fprintf(stderr, Name ": bad /proc/mdstat line starts: %s\n", line); */
|
|
continue;
|
|
}
|
|
|
|
ent = malloc(sizeof(*ent));
|
|
if (!ent) {
|
|
fprintf(stderr, Name ": malloc failed reading /proc/mdstat.\n");
|
|
free_line(line);
|
|
break;
|
|
}
|
|
ent->dev = ent->level = ent->pattern= NULL;
|
|
ent->next = NULL;
|
|
ent->percent = -1;
|
|
ent->active = -1;
|
|
ent->resync = 0;
|
|
ent->metadata_version = NULL;
|
|
ent->raid_disks = 0;
|
|
ent->chunk_size = 0;
|
|
ent->devcnt = 0;
|
|
|
|
ent->dev = strdup(line);
|
|
ent->devnum = devnum;
|
|
|
|
for (w=dl_next(line); w!= line ; w=dl_next(w)) {
|
|
int l = strlen(w);
|
|
char *eq;
|
|
if (strcmp(w, "active")==0)
|
|
ent->active = 1;
|
|
else if (strcmp(w, "inactive")==0)
|
|
ent->active = 0;
|
|
else if (ent->active >=0 &&
|
|
ent->level == NULL &&
|
|
w[0] != '(' /*readonly*/) {
|
|
ent->level = strdup(w);
|
|
in_devs = 1;
|
|
} else if (in_devs && strcmp(w, "blocks")==0)
|
|
in_devs = 0;
|
|
else if (in_devs) {
|
|
ent->devcnt++;
|
|
if (strncmp(w, "md", 2)==0) {
|
|
/* This has an md device as a component.
|
|
* If that device is already in the
|
|
* list, make sure we insert before
|
|
* there.
|
|
*/
|
|
struct mdstat_ent **ih;
|
|
int dn2 = devname2devnum(w);
|
|
ih = &all;
|
|
while (ih != insert_here && *ih &&
|
|
(*ih)->devnum != dn2)
|
|
ih = & (*ih)->next;
|
|
insert_here = ih;
|
|
}
|
|
} else if (strcmp(w, "super") == 0 &&
|
|
dl_next(w) != line) {
|
|
w = dl_next(w);
|
|
ent->metadata_version = strdup(w);
|
|
} else if (w[0] == '[' && isdigit(w[1])) {
|
|
ent->raid_disks = atoi(w+1);
|
|
} else if (!ent->pattern &&
|
|
w[0] == '[' &&
|
|
(w[1] == 'U' || w[1] == '_')) {
|
|
ent->pattern = strdup(w+1);
|
|
if (ent->pattern[l-2]==']')
|
|
ent->pattern[l-2] = '\0';
|
|
} else if (ent->percent == -1 &&
|
|
strncmp(w, "re", 2)== 0 &&
|
|
w[l-1] == '%' &&
|
|
(eq=strchr(w, '=')) != NULL ) {
|
|
ent->percent = atoi(eq+1);
|
|
if (strncmp(w,"resync", 4)==0)
|
|
ent->resync = 1;
|
|
} else if (ent->percent == -1 &&
|
|
strncmp(w, "resync", 4)==0) {
|
|
ent->resync = 1;
|
|
} else if (ent->percent == -1 &&
|
|
w[0] >= '0' &&
|
|
w[0] <= '9' &&
|
|
w[l-1] == '%') {
|
|
ent->percent = atoi(w);
|
|
}
|
|
}
|
|
if (insert_here && (*insert_here)) {
|
|
ent->next = *insert_here;
|
|
*insert_here = ent;
|
|
} else {
|
|
*end = ent;
|
|
end = &ent->next;
|
|
}
|
|
}
|
|
if (hold && mdstat_fd == -1) {
|
|
mdstat_fd = dup(fileno(f));
|
|
fcntl(mdstat_fd, F_SETFD, FD_CLOEXEC);
|
|
}
|
|
fclose(f);
|
|
|
|
/* If we might want to start array,
|
|
* reverse the order, so that components comes before composites
|
|
*/
|
|
if (start) {
|
|
rv = NULL;
|
|
while (all) {
|
|
struct mdstat_ent *e = all;
|
|
all = all->next;
|
|
e->next = rv;
|
|
rv = e;
|
|
}
|
|
} else rv = all;
|
|
return rv;
|
|
}
|
|
|
|
void mdstat_wait(int seconds)
|
|
{
|
|
fd_set fds;
|
|
struct timeval tm;
|
|
int maxfd = 0;
|
|
FD_ZERO(&fds);
|
|
if (mdstat_fd >= 0) {
|
|
FD_SET(mdstat_fd, &fds);
|
|
maxfd = mdstat_fd;
|
|
}
|
|
tm.tv_sec = seconds;
|
|
tm.tv_usec = 0;
|
|
select(maxfd + 1, NULL, NULL, &fds, &tm);
|
|
}
|
|
|
|
void mdstat_wait_fd(int fd, const sigset_t *sigmask)
|
|
{
|
|
fd_set fds, rfds;
|
|
int maxfd = fd;
|
|
|
|
FD_ZERO(&fds);
|
|
FD_ZERO(&rfds);
|
|
if (mdstat_fd >= 0)
|
|
FD_SET(mdstat_fd, &fds);
|
|
if (fd >= 0) {
|
|
struct stat stb;
|
|
fstat(fd, &stb);
|
|
if ((stb.st_mode & S_IFMT) == S_IFREG)
|
|
/* Must be a /proc or /sys fd, so expect
|
|
* POLLPRI
|
|
* i.e. an 'exceptional' event.
|
|
*/
|
|
FD_SET(fd, &fds);
|
|
else
|
|
FD_SET(fd, &rfds);
|
|
}
|
|
if (mdstat_fd > maxfd)
|
|
maxfd = mdstat_fd;
|
|
|
|
pselect(maxfd + 1, &rfds, NULL, &fds,
|
|
NULL, sigmask);
|
|
}
|
|
|
|
int mddev_busy(int devnum)
|
|
{
|
|
struct mdstat_ent *mdstat = mdstat_read(0, 0);
|
|
struct mdstat_ent *me;
|
|
|
|
for (me = mdstat ; me ; me = me->next)
|
|
if (me->devnum == devnum)
|
|
break;
|
|
free_mdstat(mdstat);
|
|
return me != NULL;
|
|
}
|