Adding upstream version 4.2.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
16732c81e5
commit
4fd4995b67
279 changed files with 77998 additions and 0 deletions
441
mdstat.c
Normal file
441
mdstat.c
Normal file
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
* mdstat - parse /proc/mdstat file. Part of:
|
||||
* mdadm - manage Linux "md" devices aka RAID arrays.
|
||||
*
|
||||
* Copyright (C) 2002-2009 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@suse.de>
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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>
|
||||
|
||||
static void free_member_devnames(struct dev_member *m)
|
||||
{
|
||||
while(m) {
|
||||
struct dev_member *t = m;
|
||||
|
||||
m = m->next;
|
||||
free(t->name);
|
||||
free(t);
|
||||
}
|
||||
}
|
||||
|
||||
static int add_member_devname(struct dev_member **m, char *name)
|
||||
{
|
||||
struct dev_member *new;
|
||||
char *t;
|
||||
|
||||
if ((t = strchr(name, '[')) == NULL)
|
||||
/* not a device */
|
||||
return 0;
|
||||
|
||||
new = xmalloc(sizeof(*new));
|
||||
new->name = strndup(name, t - name);
|
||||
new->next = *m;
|
||||
*m = new;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void free_mdstat(struct mdstat_ent *ms)
|
||||
{
|
||||
while (ms) {
|
||||
struct mdstat_ent *t;
|
||||
free(ms->level);
|
||||
free(ms->pattern);
|
||||
free(ms->metadata_version);
|
||||
free_member_devnames(ms->members);
|
||||
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;
|
||||
int fd;
|
||||
|
||||
if (hold && mdstat_fd != -1) {
|
||||
off_t offset = lseek(mdstat_fd, 0L, 0);
|
||||
if (offset == (off_t)-1) {
|
||||
return NULL;
|
||||
}
|
||||
fd = dup(mdstat_fd);
|
||||
if (fd >= 0)
|
||||
f = fdopen(fd, "r");
|
||||
else
|
||||
return NULL;
|
||||
} 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;
|
||||
char devnm[32];
|
||||
int in_devs = 0;
|
||||
|
||||
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 || strlen(line) >= 32 ||
|
||||
(line[2] != '_' && !isdigit(line[2])))
|
||||
continue;
|
||||
strcpy(devnm, line);
|
||||
|
||||
ent = xmalloc(sizeof(*ent));
|
||||
ent->level = ent->pattern= NULL;
|
||||
ent->next = NULL;
|
||||
ent->percent = RESYNC_NONE;
|
||||
ent->active = -1;
|
||||
ent->resync = 0;
|
||||
ent->metadata_version = NULL;
|
||||
ent->raid_disks = 0;
|
||||
ent->devcnt = 0;
|
||||
ent->members = NULL;
|
||||
|
||||
strcpy(ent->devnm, devnm);
|
||||
|
||||
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;
|
||||
in_devs = 1;
|
||||
} else if (strcmp(w, "bitmap:") == 0) {
|
||||
/* We need to stop parsing here;
|
||||
* otherwise, ent->raid_disks will be
|
||||
* overwritten by the wrong value.
|
||||
*/
|
||||
break;
|
||||
} else if (ent->active > 0 &&
|
||||
ent->level == NULL &&
|
||||
w[0] != '(' /*readonly*/) {
|
||||
ent->level = xstrdup(w);
|
||||
in_devs = 1;
|
||||
} else if (in_devs && strcmp(w, "blocks") == 0)
|
||||
in_devs = 0;
|
||||
else if (in_devs) {
|
||||
char *ep = strchr(w, '[');
|
||||
ent->devcnt +=
|
||||
add_member_devname(&ent->members, w);
|
||||
if (ep && 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;
|
||||
ih = &all;
|
||||
while (ih != insert_here && *ih &&
|
||||
((int)strlen((*ih)->devnm) !=
|
||||
ep-w ||
|
||||
strncmp((*ih)->devnm, w,
|
||||
ep-w) != 0))
|
||||
ih = & (*ih)->next;
|
||||
insert_here = ih;
|
||||
}
|
||||
} else if (strcmp(w, "super") == 0 &&
|
||||
dl_next(w) != line) {
|
||||
w = dl_next(w);
|
||||
ent->metadata_version = xstrdup(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 = xstrdup(w+1);
|
||||
if (ent->pattern[l-2] == ']')
|
||||
ent->pattern[l-2] = '\0';
|
||||
} else if (ent->percent == RESYNC_NONE &&
|
||||
strncmp(w, "re", 2) == 0 &&
|
||||
w[l-1] == '%' &&
|
||||
(eq = strchr(w, '=')) != NULL ) {
|
||||
ent->percent = atoi(eq+1);
|
||||
if (strncmp(w,"resync", 6) == 0)
|
||||
ent->resync = 1;
|
||||
else if (strncmp(w, "reshape", 7) == 0)
|
||||
ent->resync = 2;
|
||||
else
|
||||
ent->resync = 0;
|
||||
} else if (ent->percent == RESYNC_NONE &&
|
||||
(w[0] == 'r' || w[0] == 'c')) {
|
||||
if (strncmp(w, "resync", 6) == 0)
|
||||
ent->resync = 1;
|
||||
if (strncmp(w, "reshape", 7) == 0)
|
||||
ent->resync = 2;
|
||||
if (strncmp(w, "recovery", 8) == 0)
|
||||
ent->resync = 0;
|
||||
if (strncmp(w, "check", 5) == 0)
|
||||
ent->resync = 3;
|
||||
|
||||
if (l > 8 && strcmp(w+l-8, "=DELAYED") == 0)
|
||||
ent->percent = RESYNC_DELAYED;
|
||||
if (l > 8 && strcmp(w+l-8, "=PENDING") == 0)
|
||||
ent->percent = RESYNC_PENDING;
|
||||
if (l > 7 && strcmp(w+l-7, "=REMOTE") == 0)
|
||||
ent->percent = RESYNC_REMOTE;
|
||||
} else if (ent->percent == RESYNC_NONE &&
|
||||
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_close(void)
|
||||
{
|
||||
if (mdstat_fd >= 0)
|
||||
close(mdstat_fd);
|
||||
mdstat_fd = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* function: mdstat_wait
|
||||
* Description: Function waits for event on mdstat.
|
||||
* Parameters:
|
||||
* seconds - timeout for waiting
|
||||
* Returns:
|
||||
* > 0 - detected event
|
||||
* 0 - timeout
|
||||
* < 0 - detected error
|
||||
*/
|
||||
int 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;
|
||||
} else
|
||||
return -1;
|
||||
|
||||
tm.tv_sec = seconds;
|
||||
tm.tv_usec = 0;
|
||||
|
||||
return select(maxfd + 1, NULL, NULL, &fds, &tm);
|
||||
}
|
||||
|
||||
void mdstat_wait_fd(int fd, const sigset_t *sigmask)
|
||||
{
|
||||
fd_set fds, rfds;
|
||||
int maxfd = 0;
|
||||
|
||||
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 (fd > maxfd)
|
||||
maxfd = fd;
|
||||
|
||||
}
|
||||
if (mdstat_fd > maxfd)
|
||||
maxfd = mdstat_fd;
|
||||
|
||||
pselect(maxfd + 1, &rfds, NULL, &fds,
|
||||
NULL, sigmask);
|
||||
}
|
||||
|
||||
int mddev_busy(char *devnm)
|
||||
{
|
||||
struct mdstat_ent *mdstat = mdstat_read(0, 0);
|
||||
struct mdstat_ent *me;
|
||||
|
||||
for (me = mdstat ; me ; me = me->next)
|
||||
if (strcmp(me->devnm, devnm) == 0)
|
||||
break;
|
||||
free_mdstat(mdstat);
|
||||
return me != NULL;
|
||||
}
|
||||
|
||||
struct mdstat_ent *mdstat_by_component(char *name)
|
||||
{
|
||||
struct mdstat_ent *mdstat = mdstat_read(0, 0);
|
||||
|
||||
while (mdstat) {
|
||||
struct dev_member *m;
|
||||
struct mdstat_ent *ent;
|
||||
if (mdstat->metadata_version &&
|
||||
strncmp(mdstat->metadata_version, "external:", 9) == 0 &&
|
||||
is_subarray(mdstat->metadata_version+9))
|
||||
/* don't return subarrays, only containers */
|
||||
;
|
||||
else for (m = mdstat->members; m; m = m->next) {
|
||||
if (strcmp(m->name, name) == 0) {
|
||||
free_mdstat(mdstat->next);
|
||||
mdstat->next = NULL;
|
||||
return mdstat;
|
||||
}
|
||||
}
|
||||
ent = mdstat;
|
||||
mdstat = mdstat->next;
|
||||
ent->next = NULL;
|
||||
free_mdstat(ent);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct mdstat_ent *mdstat_by_subdev(char *subdev, char *container)
|
||||
{
|
||||
struct mdstat_ent *mdstat = mdstat_read(0, 0);
|
||||
struct mdstat_ent *ent = NULL;
|
||||
|
||||
while (mdstat) {
|
||||
/* metadata version must match:
|
||||
* external:[/-]%s/%s
|
||||
* where first %s is 'container' and second %s is 'subdev'
|
||||
*/
|
||||
if (ent)
|
||||
free_mdstat(ent);
|
||||
ent = mdstat;
|
||||
mdstat = mdstat->next;
|
||||
ent->next = NULL;
|
||||
|
||||
if (ent->metadata_version == NULL ||
|
||||
strncmp(ent->metadata_version, "external:", 9) != 0)
|
||||
continue;
|
||||
|
||||
if (!metadata_container_matches(ent->metadata_version+9,
|
||||
container) ||
|
||||
!metadata_subdev_matches(ent->metadata_version+9,
|
||||
subdev))
|
||||
continue;
|
||||
|
||||
free_mdstat(mdstat);
|
||||
return ent;
|
||||
}
|
||||
return NULL;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue