summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2008-09-15 20:58:42 -0700
committerDan Williams <dan.j.williams@intel.com>2008-09-15 20:58:42 -0700
commit1770662bcac724915520fd0784b6f806c8d96752 (patch)
treeeadc61fbd375d5120cbe9eb1a5036967bdf0f716
parentc94709e83f662c4780aa9c6917b03c774747eca5 (diff)
downloadmdadm-1770662bcac724915520fd0784b6f806c8d96752.tar.gz
mdadm-1770662bcac724915520fd0784b6f806c8d96752.tar.xz
mdadm-1770662bcac724915520fd0784b6f806c8d96752.zip
'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>
-rw-r--r--Monitor.c88
-rw-r--r--ReadMe.c1
-rw-r--r--mdadm.86
-rw-r--r--mdadm.c4
-rw-r--r--mdadm.h5
-rw-r--r--monitor.c39
-rw-r--r--sysfs.c55
7 files changed, 166 insertions, 32 deletions
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 <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.