summaryrefslogtreecommitdiffstats
path: root/Manage.c
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2011-03-23 15:42:24 +1100
committerNeilBrown <neilb@suse.de>2011-03-23 15:42:24 +1100
commitfb0d4b9ca2fca333d82fabd1a7aa55e138e83910 (patch)
tree864ee17ddd112ab16543c90fec422ff33412221e /Manage.c
parentce7a187b9a2c58783276c9da65088494f4f85124 (diff)
downloadmdadm-fb0d4b9ca2fca333d82fabd1a7aa55e138e83910.tar.gz
--stop: separate 'is busy' test for 'did it stop properly'.
Stopping an md array requires that there is no other user of it. However with udev and udisks and such there can be transient other users of md devices which can interfere with stopping the array. If there is a transient users, we really want "mdadm --stop" to wait a little while and retry. However if the array is genuinely in-use (e.g. mounted), then we don't want to wait at all - we want to fail immediately. So before trying to stop, re-open device with O_EXCL. If this fails then the device is probably in use, so give up. If it succeeds, but a subsequent STOP_ARRAY fails, then it is possibly a transient failure, so try again for a few seconds. Signed-off-by: NeilBrown <neilb@suse.de>
Diffstat (limited to 'Manage.c')
-rw-r--r--Manage.c38
1 files changed, 36 insertions, 2 deletions
diff --git a/Manage.c b/Manage.c
index 5932c903..3502175d 100644
--- a/Manage.c
+++ b/Manage.c
@@ -207,10 +207,26 @@ int Manage_runstop(char *devname, int fd, int runstop, int quiet)
struct stat stb;
struct mdinfo *mdi;
int devnum;
+ int err;
+ int count;
/* If this is an mdmon managed array, just write 'inactive'
* to the array state and let mdmon clear up.
*/
devnum = fd2devnum(fd);
+ /* Get EXCL access first. If this fails, then attempting
+ * to stop is probably a bad idea.
+ */
+ close(fd);
+ fd = open(devname, O_RDONLY|O_EXCL);
+ if (fd < 0 || fd2devnum(fd) != devnum) {
+ if (fd >= 0)
+ close(fd);
+ fprintf(stderr,
+ Name ": Cannot get exclusive access to %s:"
+ " possibly it is still in use.\n",
+ devname);
+ return 1;
+ }
mdi = sysfs_read(fd, -1, GET_LEVEL|GET_VERSION);
if (mdi &&
mdi->array.level > 0 &&
@@ -229,7 +245,14 @@ int Manage_runstop(char *devname, int fd, int runstop, int quiet)
/* Give monitor a chance to act */
ping_monitor(mdi->text_version);
- fd = open(devname, O_RDONLY);
+ fd = open_dev_excl(devnum);
+ if (fd < 0) {
+ fprintf(stderr, Name
+ ": failed to completely stop %s"
+ ": Device is busy\n",
+ devname);
+ return 1;
+ }
} else if (mdi &&
mdi->array.major_version == -1 &&
mdi->array.minor_version == -2 &&
@@ -262,7 +285,18 @@ int Manage_runstop(char *devname, int fd, int runstop, int quiet)
}
}
- if (fd >= 0 && ioctl(fd, STOP_ARRAY, NULL)) {
+ /* As we have an O_EXCL open, any use of the device
+ * which blocks STOP_ARRAY is probably a transient use,
+ * so it is reasonable to retry for a while - 5 seconds.
+ */
+ count = 25; err = 0;
+ while (count && fd >= 0
+ && (err = ioctl(fd, STOP_ARRAY, NULL)) < 0
+ && errno == EBUSY) {
+ usleep(200000);
+ count --;
+ }
+ if (fd >= 0 && err) {
if (quiet == 0) {
fprintf(stderr, Name
": failed to stop array %s: %s\n",