From 1770662bcac724915520fd0784b6f806c8d96752 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 15 Sep 2008 20:58:42 -0700 Subject: [PATCH] '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 --- Monitor.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ ReadMe.c | 1 + mdadm.8 | 6 ++++ mdadm.c | 4 +++ mdadm.h | 5 ++++ monitor.c | 39 +++++------------------- sysfs.c | 55 ++++++++++++++++++++++++++++++++++ 7 files changed, 166 insertions(+), 32 deletions(-) diff --git a/Monitor.c b/Monitor.c index b02ab3ce..3197b42a 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 12ed17f9..1fcad307 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 3c283ca9..ab659fcb 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 b7865ef7..6732352b 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 2eaaffd6..5ef69f33 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 a1d87e1d..35f80a70 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 dced322f..182028ce 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. -- 2.39.2