X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fmdadm.git;a=blobdiff_plain;f=Manage.c;h=66d697803e2866923897e7a3af27b70749631432;hp=d9530a489e34c8f613b1e4ee9533c3ec5cf040a5;hb=9e6d9291275267d3fd4b6d85d7232081f89cd8e2;hpb=a5d85af748aafe3e3830b9d16faa5c92e783b171 diff --git a/Manage.c b/Manage.c index d9530a48..66d69780 100644 --- a/Manage.c +++ b/Manage.c @@ -56,7 +56,6 @@ int Manage_ro(char *devname, int fd, int readonly) mdi = sysfs_read(fd, -1, GET_LEVEL|GET_VERSION); if (mdi && mdi->array.major_version == -1 && - mdi->array.level > 0 && is_subarray(mdi->text_version)) { char vers[64]; strcpy(vers, "external:"); @@ -88,6 +87,8 @@ int Manage_ro(char *devname, int fd, int readonly) if (*cp) *cp = 0; ping_monitor(vers+10); + if (mdi->array.level <= 0) + sysfs_set_str(mdi, NULL, "array_state", "active"); } return 0; } @@ -207,29 +208,63 @@ 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:" + "Perhaps a running " + "process, mounted filesystem " + "or active volume group?\n", + devname); + return 1; + } mdi = sysfs_read(fd, -1, GET_LEVEL|GET_VERSION); if (mdi && mdi->array.level > 0 && is_subarray(mdi->text_version)) { + int err; /* This is mdmon managed. */ close(fd); - if (sysfs_set_str(mdi, NULL, - "array_state", "inactive") < 0) { - if (quiet == 0) - fprintf(stderr, Name - ": failed to stop array %s: %s\n", - devname, strerror(errno)); + + count = 25; + while (count && + (err = sysfs_set_str(mdi, NULL, + "array_state", + "inactive")) < 0 + && errno == EBUSY) { + usleep(200000); + count--; + } + if (err && !quiet) { + fprintf(stderr, Name + ": failed to stop array %s: %s\n", + devname, strerror(errno)); return 1; } /* 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 +297,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", @@ -324,7 +370,8 @@ int Manage_resize(char *devname, int fd, long long size, int raid_disks) } int Manage_subdevs(char *devname, int fd, - mddev_dev_t devlist, int verbose, int test) + struct mddev_dev *devlist, int verbose, int test, + char *update) { /* do something to each dev. * devmode can be @@ -340,15 +387,16 @@ int Manage_subdevs(char *devname, int fd, * For 'f' and 'r', the device can also be a kernel-internal * name such as 'sdb'. */ - mddev_dev_t add_devlist = NULL; + struct mddev_dev *add_devlist = NULL; mdu_array_info_t array; mdu_disk_info_t disc; unsigned long long array_size; - mddev_dev_t dv, next = NULL; + struct mddev_dev *dv, *next = NULL; struct stat stb; int j, jnext = 0; int tfd = -1; struct supertype *st, *tst; + char *subarray = NULL; int duuid[4]; int ouuid[4]; int lfd = -1; @@ -369,7 +417,7 @@ int Manage_subdevs(char *devname, int fd, if (array_size <= 0) array_size = array.size * 2; - tst = super_by_fd(fd); + tst = super_by_fd(fd, &subarray); if (!tst) { fprintf(stderr, Name ": unsupport array - version %d.%d\n", array.major_version, array.minor_version); @@ -390,19 +438,21 @@ int Manage_subdevs(char *devname, int fd, if (strcmp(dv->devname, "failed")==0 || strcmp(dv->devname, "faulty")==0) { + int remaining_disks = array.nr_disks; if (dv->disposition != 'r') { fprintf(stderr, Name ": %s only meaningful " "with -r, not -%c\n", dv->devname, dv->disposition); return 1; } - for (; j < array.raid_disks + array.nr_disks ; j++) { + for (; j < 1024 && remaining_disks > 0; j++) { unsigned dev; disc.number = j; if (ioctl(fd, GET_DISK_INFO, &disc)) continue; if (disc.major == 0 && disc.minor == 0) continue; + remaining_disks --; if ((disc.state & 1) == 0) /* faulty */ continue; dev = makedev(disc.major, disc.minor); @@ -418,16 +468,17 @@ int Manage_subdevs(char *devname, int fd, dnprintable = dvname; break; } - if (jnext == 0) + if (next != dv) continue; } else if (strcmp(dv->devname, "detached") == 0) { + int remaining_disks = array.nr_disks; if (dv->disposition != 'r' && dv->disposition != 'f') { fprintf(stderr, Name ": %s only meaningful " "with -r of -f, not -%c\n", dv->devname, dv->disposition); return 1; } - for (; j < array.raid_disks + array.nr_disks; j++) { + for (; j < 1024 && remaining_disks > 0; j++) { int sfd; unsigned dev; disc.number = j; @@ -435,6 +486,7 @@ int Manage_subdevs(char *devname, int fd, continue; if (disc.major == 0 && disc.minor == 0) continue; + remaining_disks --; sprintf(dvname,"%d:%d", disc.major, disc.minor); sfd = dev_open(dvname, O_RDONLY); if (sfd >= 0) { @@ -458,7 +510,7 @@ int Manage_subdevs(char *devname, int fd, dnprintable = dvname; break; } - if (jnext == 0) + if (next != dv) continue; } else if (strcmp(dv->devname, "missing") == 0) { if (dv->disposition != 'a' || dv->re_add == 0) { @@ -548,7 +600,7 @@ int Manage_subdevs(char *devname, int fd, return 1; case 'a': /* add the device */ - if (tst->subarray[0]) { + if (subarray) { fprintf(stderr, Name ": Cannot add disks to a" " \'member\' array, perform this" " operation on the parent container\n"); @@ -608,7 +660,7 @@ int Manage_subdevs(char *devname, int fd, if (tst->sb) /* already loaded */; else if (tst->ss->external) { - tst->ss->load_super(tst, fd, NULL); + tst->ss->load_container(tst, fd, NULL); } else for (j = 0; j < tst->max_devs; j++) { char *dev; int dfd; @@ -634,7 +686,7 @@ int Manage_subdevs(char *devname, int fd, /* FIXME this is a bad test to be using */ if (!tst->sb) { close(tfd); - fprintf(stderr, Name ": cannot find valid superblock in this array - HELP\n"); + fprintf(stderr, Name ": cannot load array metadata from %s\n", devname); return 1; } @@ -656,13 +708,7 @@ int Manage_subdevs(char *devname, int fd, */ tst->ss->uuid_from_super(tst, duuid); - /* re-add doesn't work for version-1 superblocks - * before 2.6.18 :-( - */ - if (array.major_version == 1 && - get_linux_version() <= 2006018) - ; - else if (st->sb) { + if (st->sb) { struct mdinfo mdi; st->ss->getinfo_super(st, &mdi, NULL); st->ss->uuid_from_super(st, ouuid); @@ -672,6 +718,12 @@ int Manage_subdevs(char *devname, int fd, /* look like it is worth a try. Need to * make sure kernel will accept it though. */ + /* re-add doesn't work for version-1 superblocks + * before 2.6.18 :-( + */ + if (array.major_version == 1 && + get_linux_version() <= 2006018) + goto skip_re_add; disc.number = mdi.disk.number; if (ioctl(fd, GET_DISK_INFO, &disc) != 0 || disc.major != 0 || disc.minor != 0 @@ -689,6 +741,24 @@ int Manage_subdevs(char *devname, int fd, remove_partitions(tfd); close(tfd); tfd = -1; + if (update) { + int rv = -1; + tfd = dev_open(dv->devname, O_RDWR); + + if (tfd >= 0) + rv = st->ss->update_super( + st, NULL, update, + devname, verbose, 0, NULL); + if (rv == 0) + rv = st->ss->store_super(st, tfd); + close(tfd); + tfd = -1; + if (rv != 0) { + fprintf(stderr, Name ": failed to update" + " superblock during re-add\n"); + return 1; + } + } /* don't even try if disk is marked as faulty */ errno = 0; if (ioctl(fd, ADD_NEW_DISK, &disc) == 0) { @@ -707,6 +777,7 @@ int Manage_subdevs(char *devname, int fd, skip_re_add: re_add_failed = 1; } + st->ss->free_super(st); } if (add_dev != dv->devname) { if (verbose > 0) @@ -774,7 +845,7 @@ int Manage_subdevs(char *devname, int fd, disc.minor = minor(stb.st_rdev); disc.number =j; disc.state = 0; - if (array.not_persistent==0 || tst->ss->external) { + if (array.not_persistent==0) { int dfd; if (dv->writemostly == 1) disc.state |= 1 << MD_DISK_WRITEMOSTLY; @@ -784,12 +855,10 @@ int Manage_subdevs(char *devname, int fd, close(dfd); return 1; } - /* write_init_super will close 'dfd' */ - if (tst->ss->external) - /* mdmon will write the metadata */ + if (tst->ss->write_init_super(tst)) { close(dfd); - else if (tst->ss->write_init_super(tst)) return 1; + } } else if (dv->re_add) { /* this had better be raid1. * As we are "--re-add"ing we must find a spare slot @@ -823,34 +892,43 @@ int Manage_subdevs(char *devname, int fd, if (dv->writemostly == 1) disc.state |= (1 << MD_DISK_WRITEMOSTLY); if (tst->ss->external) { - /* add a disk to an external metadata container - * only if mdmon is around to see it - */ + /* add a disk + * to an external metadata container */ struct mdinfo new_mdi; struct mdinfo *sra; int container_fd; int devnum = fd2devnum(fd); + int dfd; container_fd = open_dev_excl(devnum); if (container_fd < 0) { fprintf(stderr, Name ": add failed for %s:" " could not get exclusive access to container\n", dv->devname); + tst->ss->free_super(tst); return 1; } - if (!mdmon_running(devnum)) { - fprintf(stderr, Name ": add failed for %s: mdmon not running\n", - dv->devname); + dfd = dev_open(dv->devname, O_RDWR | O_EXCL|O_DIRECT); + if (mdmon_running(tst->container_dev)) + tst->update_tail = &tst->updates; + if (tst->ss->add_to_super(tst, &disc, dfd, + dv->devname)) { + close(dfd); close(container_fd); return 1; } + if (tst->update_tail) + flush_metadata_updates(tst); + else + tst->ss->sync_metadata(tst); sra = sysfs_read(container_fd, -1, 0); if (!sra) { fprintf(stderr, Name ": add failed for %s: sysfs_read failed\n", dv->devname); close(container_fd); + tst->ss->free_super(tst); return 1; } sra->array.level = LEVEL_CONTAINER; @@ -859,19 +937,26 @@ int Manage_subdevs(char *devname, int fd, new_mdi.disk.major = disc.major; new_mdi.disk.minor = disc.minor; new_mdi.recovery_start = 0; + /* Make sure fds are closed as they are O_EXCL which + * would block add_disk */ + tst->ss->free_super(tst); if (sysfs_add_disk(sra, &new_mdi, 0) != 0) { fprintf(stderr, Name ": add new device to external metadata" " failed for %s\n", dv->devname); close(container_fd); + sysfs_free(sra); return 1; } - ping_monitor(devnum2devname(devnum)); + ping_monitor_by_id(devnum); sysfs_free(sra); close(container_fd); - } else if (ioctl(fd, ADD_NEW_DISK, &disc)) { - fprintf(stderr, Name ": add new device failed for %s as %d: %s\n", - dv->devname, j, strerror(errno)); - return 1; + } else { + tst->ss->free_super(tst); + if (ioctl(fd, ADD_NEW_DISK, &disc)) { + fprintf(stderr, Name ": add new device failed for %s as %d: %s\n", + dv->devname, j, strerror(errno)); + return 1; + } } if (verbose >= 0) fprintf(stderr, Name ": added %s\n", dv->devname); @@ -879,7 +964,7 @@ int Manage_subdevs(char *devname, int fd, case 'r': /* hot remove */ - if (tst->subarray[0]) { + if (subarray) { fprintf(stderr, Name ": Cannot remove disks from a" " \'member\' array, perform this" " operation on the parent container\n"); @@ -1031,22 +1116,14 @@ int autodetect(void) return rv; } -int Update_subarray(char *dev, char *subarray, char *update, mddev_ident_t ident, int quiet) +int Update_subarray(char *dev, char *subarray, char *update, struct mddev_ident *ident, int quiet) { struct supertype supertype, *st = &supertype; int fd, rv = 2; memset(st, 0, sizeof(*st)); - if (snprintf(st->subarray, sizeof(st->subarray), "%s", subarray) >= - (signed)sizeof(st->subarray)) { - if (!quiet) - fprintf(stderr, - Name ": Input overflow for subarray '%s' > %zu bytes\n", - subarray, sizeof(st->subarray) - 1); - return 2; - } - fd = open_subarray(dev, st, quiet); + fd = open_subarray(dev, subarray, st, quiet); if (fd < 0) return 2; @@ -1061,7 +1138,7 @@ int Update_subarray(char *dev, char *subarray, char *update, mddev_ident_t ident if (mdmon_running(st->devnum)) st->update_tail = &st->updates; - rv = st->ss->update_subarray(st, update, ident); + rv = st->ss->update_subarray(st, subarray, update, ident); if (rv) { if (!quiet) @@ -1083,4 +1160,48 @@ int Update_subarray(char *dev, char *subarray, char *update, mddev_ident_t ident return rv; } + +/* Move spare from one array to another + * If adding to destination array fails + * add back to original array + * Returns 1 on success, 0 on failure */ +int move_spare(char *from_devname, char *to_devname, dev_t devid) +{ + struct mddev_dev devlist; + char devname[20]; + + /* try to remove and add */ + int fd1 = open(to_devname, O_RDONLY); + int fd2 = open(from_devname, O_RDONLY); + + if (fd1 < 0 || fd2 < 0) { + if (fd1>=0) close(fd1); + if (fd2>=0) close(fd2); + return 0; + } + + devlist.next = NULL; + devlist.used = 0; + devlist.re_add = 0; + devlist.writemostly = 0; + devlist.devname = devname; + sprintf(devname, "%d:%d", major(devid), minor(devid)); + + devlist.disposition = 'r'; + if (Manage_subdevs(from_devname, fd2, &devlist, -1, 0, NULL) == 0) { + devlist.disposition = 'a'; + if (Manage_subdevs(to_devname, fd1, &devlist, -1, 0, NULL) == 0) { + /* make sure manager is aware of changes */ + ping_manager(to_devname); + ping_manager(from_devname); + close(fd1); + close(fd2); + return 1; + } + else Manage_subdevs(from_devname, fd2, &devlist, -1, 0, NULL); + } + close(fd1); + close(fd2); + return 0; +} #endif