]> git.ipfire.org Git - thirdparty/mdadm.git/commitdiff
'mdadm --wait-clean' wait for array to be marked clean
authorDan Williams <dan.j.williams@intel.com>
Tue, 16 Sep 2008 03:58:42 +0000 (20:58 -0700)
committerDan Williams <dan.j.williams@intel.com>
Tue, 16 Sep 2008 03:58:42 +0000 (20:58 -0700)
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>
Monitor.c
ReadMe.c
mdadm.8
mdadm.c
mdadm.h
monitor.c
sysfs.c

index b02ab3cedb71b135524300a585227c5dcec09725..3197b42a37b86daeb6856747ea253b38a3fec058 100644 (file)
--- 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;
+}
+
+
index 12ed17f93e7a869de87623b2a4487baf27feef8c..1fcad3073d3da6c2b38216823762a84b54f4cd65 100644 (file)
--- 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 3c283ca9e7b4bda5beb92b41f6b35acc499bdd59..ab659fcb02c40d0ed8d2867310b454a52d38c509 100644 (file)
--- 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 b7865ef73d63f08bc22e0e090ad424ae2f817f84..6732352b77e3eb8f63bcc3206886a3f976e278ce 100644 (file)
--- 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 2eaaffd6dff2daad481745d588763a93b0f03410..5ef69f33bc3f5d9a908d74127a5414f259d626c0 100644 (file)
--- 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);
index a1d87e1d1e65ccf716a00c3a1a235a1f55dc226c..35f80a70993028abfd5a7fff046e3e4abf940ccf 100644 (file)
--- 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 dced322f7a3d36669b065832065ccad101c52a0b..182028cea6efef992bce97e8c12f67857daa2e76 100644 (file)
--- a/sysfs.c
+++ b/sysfs.c
@@ -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.