'mdadm --wait-clean' wait for array to be marked clean

For use in distro shutdown scripts with a RAID root file system.
Returns immediately if the array is 'readonly', or not an externally
managed array.  It is up to the distro's scripts to make sure no new
writes hit the device after this returns 'true'.

Signed-off-by: Dan Williams <dan.j.williams@intel.com>
This commit is contained in:
Dan Williams 2008-09-15 20:58:42 -07:00
parent c94709e83f
commit 1770662bca
7 changed files with 166 additions and 32 deletions

View File

@ -628,3 +628,91 @@ int Wait(char *dev)
mdstat_wait(5);
}
}
static char *clean_states[] = {
"clear", "inactive", "readonly", "read-auto", "clean", NULL };
int WaitClean(char *dev)
{
int fd;
struct mdinfo *mdi;
int rv = 1;
int devnum;
fd = open(dev, O_RDONLY);
if (fd < 0) {
fprintf(stderr, Name ": Couldn't open %s: %s\n", dev, strerror(errno));
return 1;
}
devnum = fd2devnum(fd);
mdi = sysfs_read(fd, devnum, GET_VERSION|GET_LEVEL|GET_SAFEMODE);
if (!mdi) {
fprintf(stderr, Name ": Failed to read sysfs attributes for "
"%s\n", dev);
close(fd);
return 0;
}
switch(mdi->array.level) {
case LEVEL_LINEAR:
case LEVEL_MULTIPATH:
case 0:
/* safemode delay is irrelevant for these levels */
rv = 0;
}
/* for internal metadata the kernel handles the final clean
* transition, containers can never be dirty
*/
if (!is_subarray(mdi->text_version))
rv = 0;
/* safemode disabled ? */
if (mdi->safe_mode_delay == 0)
rv = 0;
if (rv) {
int state_fd = sysfs_open(fd2devnum(fd), NULL, "array_state");
unsigned long secs;
char buf[20];
secs = mdi->safe_mode_delay / 1000;
if (mdi->safe_mode_delay - secs * 1000)
secs++;
secs *= 2;
for (; secs; secs--) {
rv = read(state_fd, buf, sizeof(buf));
if (rv < 0)
break;
if (sysfs_match_word(buf, clean_states) <= 4)
break;
sleep(1);
lseek(state_fd, 0, SEEK_SET);
}
if (rv < 0)
rv = 1;
else if (secs) {
/* we need to ping to close the window between array
* state transitioning to clean and the metadata being
* marked clean
*/
if (ping_monitor(mdi->text_version) == 0)
rv = 0;
}
if (rv)
fprintf(stderr, Name ": Error waiting for %s to be clean\n",
dev);
close(state_fd);
}
sysfs_free(mdi);
close(fd);
return rv;
}

View File

@ -161,6 +161,7 @@ struct option long_options[] = {
{"readwrite", 0, 0, 'w'},
{"no-degraded",0,0, NoDegraded },
{"wait", 0, 0, 'W'},
{"wait-clean", 0, 0, Waitclean },
/* For Detail/Examine */
{"brief", 0, 0, 'b'},

View File

@ -1014,6 +1014,12 @@ activity to finish before returning.
will return with success if it actually waited for every device
listed, otherwise it will return failure.
.TP
.BR \-\-wait\-clean
For each md device given, wait for the array to be marked clean before
returning. For native arrays this returns immediately as the kernel
handles dirty-clean transistions at shutdown.
.SH For Incremental Assembly mode:
.TP
.BR \-\-rebuild\-map ", " \-r

View File

@ -214,6 +214,7 @@ int main(int argc, char *argv[])
case 'o':
case 'w':
case 'W':
case Waitclean:
case 'K': if (!mode) newmode = MISC; break;
}
if (mode && newmode == mode) {
@ -770,6 +771,7 @@ int main(int argc, char *argv[])
case O(MISC,'o'):
case O(MISC,'w'):
case O(MISC,'W'):
case O(MISC, Waitclean):
if (devmode && devmode != opt &&
(devmode == 'E' || (opt == 'E' && devmode != 'Q'))) {
fprintf(stderr, Name ": --examine/-E cannot be given with -%c\n",
@ -1280,6 +1282,8 @@ int main(int argc, char *argv[])
rv |= ExamineBitmap(dv->devname, brief, ss); continue;
case 'W':
rv |= Wait(dv->devname); continue;
case Waitclean:
rv |= WaitClean(dv->devname); continue;
}
mdfd = open_mddev(dv->devname, 1);
if (mdfd>=0) {

View File

@ -224,6 +224,7 @@ enum special_options {
AutoHomeHost,
Symlinks,
AutoDetect,
Waitclean,
};
/* structures read from config file */
@ -327,6 +328,7 @@ extern void map_add(struct map_ent **melp,
#define GET_VERSION 64
#define GET_DISKS 128
#define GET_DEGRADED 256
#define GET_SAFEMODE 512
#define GET_DEVS 1024 /* gets role, major, minor */
#define GET_OFFSET 2048
@ -340,6 +342,8 @@ extern void map_add(struct map_ent **melp,
extern int sysfs_open(int devnum, char *devname, char *attr);
extern void sysfs_free(struct mdinfo *sra);
extern struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options);
extern int sysfs_attr_match(const char *attr, const char *str);
extern int sysfs_match_word(const char *word, char **list);
extern int sysfs_set_str(struct mdinfo *sra, struct mdinfo *dev,
char *name, char *val);
extern int sysfs_set_num(struct mdinfo *sra, struct mdinfo *dev,
@ -705,6 +709,7 @@ extern int Monitor(mddev_dev_t devlist,
extern int Kill(char *dev, int force, int quiet, int noexcl);
extern int Wait(char *dev);
extern int WaitClean(char *dev);
extern int Incremental(char *devname, int verbose, int runstop,
struct supertype *st, char *homehost, int autof);

View File

@ -47,7 +47,6 @@ static int read_attr(char *buf, int len, int fd)
return n;
}
int get_resync_start(struct active_array *a)
{
char buf[30];
@ -62,30 +61,6 @@ int get_resync_start(struct active_array *a)
return 1;
}
static int attr_match(const char *attr, const char *str)
{
/* See if attr, read from a sysfs file, matches
* str. They must either be the same, or attr can
* have a trailing newline or comma
*/
while (*attr && *str && *attr == *str) {
attr++;
str++;
}
if (*str || (*attr && *attr != ',' && *attr != '\n'))
return 0;
return 1;
}
static int match_word(const char *word, char **list)
{
int n;
for (n=0; list[n]; n++)
if (attr_match(word, list[n]))
break;
return n;
}
static enum array_state read_state(int fd)
{
@ -94,7 +69,7 @@ static enum array_state read_state(int fd)
if (n <= 0)
return bad_word;
return (enum array_state) match_word(buf, array_states);
return (enum array_state) sysfs_match_word(buf, array_states);
}
static enum sync_action read_action( int fd)
@ -104,7 +79,7 @@ static enum sync_action read_action( int fd)
if (n <= 0)
return bad_action;
return (enum sync_action) match_word(buf, sync_actions);
return (enum sync_action) sysfs_match_word(buf, sync_actions);
}
int read_dev_state(int fd)
@ -119,15 +94,15 @@ int read_dev_state(int fd)
cp = buf;
while (cp) {
if (attr_match(cp, "faulty"))
if (sysfs_attr_match(cp, "faulty"))
rv |= DS_FAULTY;
if (attr_match(cp, "in_sync"))
if (sysfs_attr_match(cp, "in_sync"))
rv |= DS_INSYNC;
if (attr_match(cp, "write_mostly"))
if (sysfs_attr_match(cp, "write_mostly"))
rv |= DS_WRITE_MOSTLY;
if (attr_match(cp, "spare"))
if (sysfs_attr_match(cp, "spare"))
rv |= DS_SPARE;
if (attr_match(cp, "blocked"))
if (sysfs_attr_match(cp, "blocked"))
rv |= DS_BLOCKED;
cp = strchr(cp, ',');
if (cp)

55
sysfs.c
View File

@ -25,6 +25,7 @@
#include "mdadm.h"
#include <dirent.h>
#include <ctype.h>
int load_sys(char *path, char *buf)
{
@ -185,6 +186,35 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
goto abort;
sra->mismatch_cnt = strtoul(buf, NULL, 0);
}
if (options & GET_SAFEMODE) {
int scale = 1;
int dot = 0;
int i;
unsigned long msec;
size_t len;
strcpy(base, "safe_mode_delay");
if (load_sys(fname, buf))
goto abort;
/* remove a period, and count digits after it */
len = strlen(buf);
for (i = 0; i < len; i++) {
if (dot) {
if (isdigit(buf[i])) {
buf[i-1] = buf[i];
scale *= 10;
}
buf[i] = 0;
} else if (buf[i] == '.') {
dot=1;
buf[i] = 0;
}
}
msec = strtoul(buf, NULL, 10);
msec = (msec * 1000) / scale;
sra->safe_mode_delay = msec;
}
if (! (options & GET_DEVS))
return sra;
@ -265,6 +295,31 @@ struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
return NULL;
}
int sysfs_attr_match(const char *attr, const char *str)
{
/* See if attr, read from a sysfs file, matches
* str. They must either be the same, or attr can
* have a trailing newline or comma
*/
while (*attr && *str && *attr == *str) {
attr++;
str++;
}
if (*str || (*attr && *attr != ',' && *attr != '\n'))
return 0;
return 1;
}
int sysfs_match_word(const char *word, char **list)
{
int n;
for (n=0; list[n]; n++)
if (sysfs_attr_match(word, list[n]))
break;
return n;
}
unsigned long long get_component_size(int fd)
{
/* Find out the component size of the array.