diff --git a/Monitor.c b/Monitor.c index b02ab3c..3197b42 100644 --- a/Monitor.c +++ b/Monitor.c @@ -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; +} + + diff --git a/ReadMe.c b/ReadMe.c index 12ed17f..1fcad30 100644 --- a/ReadMe.c +++ b/ReadMe.c @@ -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'}, diff --git a/mdadm.8 b/mdadm.8 index 3c283ca..ab659fc 100644 --- a/mdadm.8 +++ b/mdadm.8 @@ -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 diff --git a/mdadm.c b/mdadm.c index b7865ef..6732352 100644 --- a/mdadm.c +++ b/mdadm.c @@ -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) { diff --git a/mdadm.h b/mdadm.h index 2eaaffd..5ef69f3 100644 --- a/mdadm.h +++ b/mdadm.h @@ -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); diff --git a/monitor.c b/monitor.c index a1d87e1..35f80a7 100644 --- a/monitor.c +++ b/monitor.c @@ -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) diff --git a/sysfs.c b/sysfs.c index dced322..182028c 100644 --- a/sysfs.c +++ b/sysfs.c @@ -25,6 +25,7 @@ #include "mdadm.h" #include +#include 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.