X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=Manage.c;h=b4d73e8902e6f397ce030352ff0d7d181799f47b;hb=a21e848a5578793a5ab6518390433b020785af3b;hp=c1123bd885456bf10c82a07573aed4b974848552;hpb=38aeaf3af6828fbefb19b6da1c12479578915391;p=thirdparty%2Fmdadm.git diff --git a/Manage.c b/Manage.c index c1123bd8..b4d73e89 100644 --- a/Manage.c +++ b/Manage.c @@ -54,7 +54,7 @@ int Manage_ro(char *devname, int fd, int readonly) /* If this is an externally-managed array, we need to modify the * metadata_version so that mdmon doesn't undo our change. */ - mdi = sysfs_read(fd, -1, GET_LEVEL|GET_VERSION); + mdi = sysfs_read(fd, NULL, GET_LEVEL|GET_VERSION); if (mdi && mdi->array.major_version == -1 && is_subarray(mdi->text_version)) { @@ -127,12 +127,12 @@ out: #ifndef MDASSEMBLE -static void remove_devices(int devnum, char *path) +static void remove_devices(char *devnm, char *path) { /* * Remove names at 'path' - possibly with * partition suffixes - which link to the 'standard' - * name for devnum. These were probably created + * name for devnm. These were probably created * by mdadm when the array was assembled. */ char base[40]; @@ -146,10 +146,7 @@ static void remove_devices(int devnum, char *path) if (!path) return; - if (devnum >= 0) - sprintf(base, "/dev/md%d", devnum); - else - sprintf(base, "/dev/md_d%d", -1-devnum); + sprintf(base, "/dev/%s", devnm); be = base + strlen(base); path2 = xmalloc(strlen(path)+20); @@ -212,21 +209,43 @@ int Manage_runstop(char *devname, int fd, int runstop, pr_err("started %s\n", devname); } else if (runstop < 0){ struct map_ent *map = NULL; - struct stat stb; struct mdinfo *mdi; - int devnum; + char devnm[32]; + char container[32]; 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); + strcpy(devnm, fd2devnm(fd)); /* Get EXCL access first. If this fails, then attempting * to stop is probably a bad idea. */ + mdi = sysfs_read(fd, NULL, GET_LEVEL|GET_VERSION); + if (mdi && is_subarray(mdi->text_version)) { + char *sl; + strncpy(container, mdi->text_version+1, sizeof(container)); + container[sizeof(container)-1] = 0; + sl = strchr(container, '/'); + if (sl) + *sl = 0; + } else + container[0] = 0; close(fd); - fd = open(devname, O_RDONLY|O_EXCL); - if (fd < 0 || fd2devnum(fd) != devnum) { + count = 5; + while (((fd = ((devnm[0] == '/') + ?open(devname, O_RDONLY|O_EXCL) + :open_dev_flags(devnm, O_RDONLY|O_EXCL))) < 0 + || strcmp(fd2devnm(fd), devnm) != 0) + && container[0] + && mdmon_running(container) + && count) { + if (fd >= 0) + close(fd); + flush_mdmon(container); + count--; + } + if (fd < 0 || strcmp(fd2devnm(fd), devnm) != 0) { if (fd >= 0) close(fd); if (verbose >= 0) @@ -237,7 +256,6 @@ int Manage_runstop(char *devname, int fd, int runstop, devname); return 1; } - mdi = sysfs_read(fd, -1, GET_LEVEL|GET_VERSION); if (mdi && mdi->array.level > 0 && is_subarray(mdi->text_version)) { @@ -269,7 +287,7 @@ int Manage_runstop(char *devname, int fd, int runstop, /* Give monitor a chance to act */ ping_monitor(mdi->text_version); - fd = open_dev_excl(devnum); + fd = open_dev_excl(devnm); if (fd < 0) { if (verbose >= 0) pr_err("failed to completely stop %s" @@ -296,8 +314,8 @@ int Manage_runstop(char *devname, int fd, int runstop, for (m = mds; m; m = m->next) if (m->metadata_version && strncmp(m->metadata_version, "external:", 9)==0 && - is_subarray(m->metadata_version+9) && - devname2devnum(m->metadata_version+10) == devnum) { + metadata_container_matches(m->metadata_version+9, + devnm)) { if (verbose >= 0) pr_err("Cannot stop container %s: " "member %s still active\n", @@ -340,17 +358,15 @@ int Manage_runstop(char *devname, int fd, int runstop, if (mdi) sysfs_uevent(mdi, "change"); - if (devnum != NoMdDev && - (stat("/dev/.udev", &stb) != 0 || - check_env("MDADM_NO_UDEV"))) { - struct map_ent *mp = map_by_devnum(&map, devnum); - remove_devices(devnum, mp ? mp->path : NULL); + if (devnm[0] && use_udev()) { + struct map_ent *mp = map_by_devnm(&map, devnm); + remove_devices(devnm, mp ? mp->path : NULL); } if (verbose >= 0) pr_err("stopped %s\n", devname); map_lock(&map); - map_remove(&map, devnum); + map_remove(&map, devnm); map_unlock(&map); out: if (mdi) @@ -359,6 +375,18 @@ int Manage_runstop(char *devname, int fd, int runstop, return rv; } +static struct mddev_dev *add_one(struct mddev_dev *dv, char *name, char disp) +{ + struct mddev_dev *new; + new = xmalloc(sizeof(*new)); + memset(new, 0, sizeof(*new)); + new->devname = xstrdup(name); + new->disposition = disp; + new->next = dv->next; + dv->next = new; + return new; +} + static void add_faulty(struct mddev_dev *dv, int fd, char disp) { mdu_array_info_t array; @@ -371,7 +399,6 @@ static void add_faulty(struct mddev_dev *dv, int fd, char disp) remaining_disks = array.nr_disks; for (i = 0; i < MAX_DISKS && remaining_disks > 0; i++) { - struct mddev_dev *new; char buf[40]; disk.number = i; if (ioctl(fd, GET_DISK_INFO, &disk) != 0) @@ -382,12 +409,7 @@ static void add_faulty(struct mddev_dev *dv, int fd, char disp) if ((disk.state & 1) == 0) /* not faulty */ continue; sprintf(buf, "%d:%d", disk.major, disk.minor); - new = xmalloc(sizeof(*new)); - new->devname = xstrdup(buf); - new->disposition = disp; - new->next = dv->next; - dv->next = new; - dv = new; + dv = add_one(dv, buf, disp); } } @@ -403,7 +425,6 @@ static void add_detached(struct mddev_dev *dv, int fd, char disp) remaining_disks = array.nr_disks; for (i = 0; i < MAX_DISKS && remaining_disks > 0; i++) { - struct mddev_dev *new; char buf[40]; int sfd; disk.number = i; @@ -424,12 +445,41 @@ static void add_detached(struct mddev_dev *dv, int fd, char disp) if (errno != ENXIO) /* Probably not detached */ continue; - new = xmalloc(sizeof(*new)); - new->devname = xstrdup(buf); - new->disposition = disp; - new->next = dv->next; - dv->next = new; - dv = new; + dv = add_one(dv, buf, disp); + } +} + +static void add_set(struct mddev_dev *dv, int fd, char set_char) +{ + mdu_array_info_t array; + mdu_disk_info_t disk; + int remaining_disks; + int copies, set; + int i; + + if (ioctl(fd, GET_ARRAY_INFO, &array) != 0) + return; + if (array.level != 10) + return; + copies = ((array.layout & 0xff) * + ((array.layout >> 8) & 0xff)); + if (array.raid_disks % copies) + return; + + remaining_disks = array.nr_disks; + for (i = 0; i < MAX_DISKS && remaining_disks > 0; i++) { + char buf[40]; + disk.number = i; + if (ioctl(fd, GET_DISK_INFO, &disk) != 0) + continue; + if (disk.major == 0 && disk.minor == 0) + continue; + remaining_disks--; + set = disk.raid_disk % copies; + if (set_char != set + 'A') + continue; + sprintf(buf, "%d:%d", disk.major, disk.minor); + dv = add_one(dv, buf, dv->disposition); } } @@ -535,7 +585,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, char *update, unsigned long rdev, unsigned long long array_size) { unsigned long long ldsize; - struct supertype *dev_st; + struct supertype *dev_st = NULL; int j; mdu_disk_info_t disc; @@ -549,7 +599,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, if (tst->ss->validate_geometry( tst, array->level, array->layout, array->raid_disks, NULL, - ldsize >> 9, NULL, NULL, 0) == 0) { + ldsize >> 9, INVALID_SECTORS, NULL, NULL, 0) == 0) { if (!force) { pr_err("%s is larger than %s can " "effectively use.\n" @@ -628,7 +678,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, } /* Make sure device is large enough */ - if (tst->ss->avail_size(tst, ldsize/512) < + if (tst->ss->avail_size(tst, ldsize/512, INVALID_SECTORS) < array_size) { if (dv->disposition == 'M') return 0; @@ -733,7 +783,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, disc.state |= 1 << MD_DISK_WRITEMOSTLY; dfd = dev_open(dv->devname, O_RDWR | O_EXCL|O_DIRECT); if (tst->ss->add_to_super(tst, &disc, dfd, - dv->devname)) + dv->devname, INVALID_SECTORS)) return -1; if (tst->ss->write_init_super(tst)) return -1; @@ -774,10 +824,12 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, struct mdinfo new_mdi; struct mdinfo *sra; int container_fd; - int devnum = fd2devnum(fd); + char devnm[32]; int dfd; - container_fd = open_dev_excl(devnum); + strcpy(devnm, fd2devnm(fd)); + + container_fd = open_dev_excl(devnm); if (container_fd < 0) { pr_err("add failed for %s:" " could not get exclusive access to container\n", @@ -786,11 +838,12 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, return -1; } + Kill(dv->devname, NULL, 0, -1, 0); dfd = dev_open(dv->devname, O_RDWR | O_EXCL|O_DIRECT); - if (mdmon_running(tst->container_dev)) + if (mdmon_running(tst->container_devnm)) tst->update_tail = &tst->updates; if (tst->ss->add_to_super(tst, &disc, dfd, - dv->devname)) { + dv->devname, INVALID_SECTORS)) { close(dfd); close(container_fd); return -1; @@ -800,7 +853,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, else tst->ss->sync_metadata(tst); - sra = sysfs_read(container_fd, -1, 0); + sra = sysfs_read(container_fd, NULL, 0); if (!sra) { pr_err("add failed for %s: sysfs_read failed\n", dv->devname); @@ -824,7 +877,7 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, sysfs_free(sra); return -1; } - ping_monitor_by_id(devnum); + ping_monitor(devnm); sysfs_free(sra); close(container_fd); } else { @@ -840,6 +893,224 @@ int Manage_add(int fd, int tfd, struct mddev_dev *dv, return 1; } +int Manage_remove(struct supertype *tst, int fd, struct mddev_dev *dv, + int sysfd, unsigned long rdev, int verbose, char *devname) +{ + int lfd = -1; + int err; + + if (tst->ss->external) { + /* To remove a device from a container, we must + * check that it isn't in use in an array. + * This involves looking in the 'holders' + * directory - there must be just one entry, + * the container. + * To ensure that it doesn't get used as a + * hot spare while we are checking, we + * get an O_EXCL open on the container + */ + int ret; + char devnm[32]; + strcpy(devnm, fd2devnm(fd)); + lfd = open_dev_excl(devnm); + if (lfd < 0) { + pr_err("Cannot get exclusive access " + " to container - odd\n"); + return -1; + } + /* We may not be able to check on holders in + * sysfs, either because we don't have the dev num + * (rdev == 0) or because the device has been detached + * and the 'holders' directory no longer exists + * (ret == -1). In that case, assume it is OK to + * remove. + */ + if (rdev == 0) + ret = -1; + else + ret = sysfs_unique_holder(devnm, rdev); + if (ret == 0) { + pr_err("%s is not a member, cannot remove.\n", + dv->devname); + close(lfd); + return -1; + } + if (ret >= 2) { + pr_err("%s is still in use, cannot remove.\n", + dv->devname); + close(lfd); + return -1; + } + } + /* FIXME check that it is a current member */ + if (sysfd >= 0) { + /* device has been removed and we don't know + * the major:minor number + */ + int n = write(sysfd, "remove", 6); + if (n != 6) + err = -1; + else + err = 0; + } else { + err = ioctl(fd, HOT_REMOVE_DISK, rdev); + if (err && errno == ENODEV) { + /* Old kernels rejected this if no personality + * is registered */ + struct mdinfo *sra = sysfs_read(fd, NULL, GET_DEVS); + struct mdinfo *dv = NULL; + if (sra) + dv = sra->devs; + for ( ; dv ; dv=dv->next) + if (dv->disk.major == (int)major(rdev) && + dv->disk.minor == (int)minor(rdev)) + break; + if (dv) + err = sysfs_set_str(sra, dv, + "state", "remove"); + else + err = -1; + if (sra) + sysfs_free(sra); + } + } + if (err) { + pr_err("hot remove failed " + "for %s: %s\n", dv->devname, + strerror(errno)); + if (lfd >= 0) + close(lfd); + return -1; + } + if (tst->ss->external) { + /* + * Before dropping our exclusive open we make an + * attempt at preventing mdmon from seeing an + * 'add' event before reconciling this 'remove' + * event. + */ + char *devnm = fd2devnm(fd); + + if (!devnm) { + pr_err("unable to get container name\n"); + return -1; + } + + ping_manager(devnm); + } + if (lfd >= 0) + close(lfd); + if (verbose >= 0) + pr_err("hot removed %s from %s\n", + dv->devname, devname); + return 1; +} + +int Manage_replace(struct supertype *tst, int fd, struct mddev_dev *dv, + unsigned long rdev, int verbose, char *devname) +{ + struct mdinfo *mdi, *di; + if (tst->ss->external) { + pr_err("--replace only supported for native metadata (0.90 or 1.x)\n"); + return -1; + } + /* Need to find the device in sysfs and add 'want_replacement' to the + * status. + */ + mdi = sysfs_read(fd, NULL, GET_DEVS); + if (!mdi || !mdi->devs) { + pr_err("Cannot find status of %s to enable replacement - strange\n", + devname); + return -1; + } + for (di = mdi->devs; di; di = di->next) + if (di->disk.major == (int)major(rdev) && + di->disk.minor == (int)minor(rdev)) + break; + if (di) { + int rv; + if (di->disk.raid_disk < 0) { + pr_err("%s is not active and so cannot be replaced.\n", + dv->devname); + sysfs_free(mdi); + return -1; + } + rv = sysfs_set_str(mdi, di, + "state", "want_replacement"); + if (rv) { + sysfs_free(mdi); + pr_err("Failed to request replacement for %s\n", + dv->devname); + return -1; + } + if (verbose >= 0) + pr_err("Marked %s (device %d in %s) for replacement\n", + dv->devname, di->disk.raid_disk, devname); + /* If there is a matching 'with', we need to tell it which + * raid disk + */ + while (dv && dv->disposition != 'W') + dv = dv->next; + if (dv) { + dv->disposition = 'w'; + dv->used = di->disk.raid_disk; + } + return 1; + } + sysfs_free(mdi); + pr_err("%s not found in %s so cannot --replace it\n", + dv->devname, devname); + return -1; +} + +int Manage_with(struct supertype *tst, int fd, struct mddev_dev *dv, + unsigned long rdev, int verbose, char *devname) +{ + struct mdinfo *mdi, *di; + /* try to set 'slot' for 'rdev' in 'fd' to 'dv->used' */ + mdi = sysfs_read(fd, NULL, GET_DEVS|GET_STATE); + if (!mdi || !mdi->devs) { + pr_err("Cannot find status of %s to enable replacement - strange\n", + devname); + return -1; + } + for (di = mdi->devs; di; di = di->next) + if (di->disk.major == (int)major(rdev) && + di->disk.minor == (int)minor(rdev)) + break; + if (di) { + int rv; + if (di->disk.state & (1<devname); + sysfs_free(mdi); + return -1; + } + if (di->disk.raid_disk >= 0) { + pr_err("%s is active and cannot be a replacement\n", + dv->devname); + sysfs_free(mdi); + return -1; + } + rv = sysfs_set_num(mdi, di, + "slot", dv->used); + if (rv) { + sysfs_free(mdi); + pr_err("Failed to %s as preferred replacement.\n", + dv->devname); + return -1; + } + if (verbose >= 0) + pr_err("Marked %s in %s as replacement for device %d\n", + dv->devname, devname, dv->used); + return 1; + } + sysfs_free(mdi); + pr_err("%s not found in %s so cannot make it preferred replacement\n", + dv->devname, devname); + return -1; +} + int Manage_subdevs(char *devname, int fd, struct mddev_dev *devlist, int verbose, int test, char *update, int force) @@ -856,6 +1127,18 @@ int Manage_subdevs(char *devname, int fd, * 'f' - set the device faulty SET_DISK_FAULTY * device can be 'detached' in which case any device that * is inaccessible will be marked faulty. + * 'R' - mark this device as wanting replacement. + * 'W' - this device is added if necessary and activated as + * a replacement for a previous 'R' device. + * ----- + * 'w' - 'W' will be changed to 'w' when it is paired with + * a 'R' device. If a 'W' is found while walking the list + * it must be unpaired, and is an error. + * 'M' - this is created by a 'missing' target. It is a slight + * variant on 'A' + * 'F' - Another variant of 'A', where the device was faulty + * so must be removed from the array first. + * * For 'f' and 'r', the device can also be a kernel-internal * name such as 'sdb'. */ @@ -866,18 +1149,18 @@ int Manage_subdevs(char *devname, int fd, int tfd = -1; struct supertype *tst; char *subarray = NULL; - int lfd = -1; int sysfd = -1; int count = 0; /* number of actions taken */ struct mdinfo info; int frozen = 0; + int busy = 0; if (ioctl(fd, GET_ARRAY_INFO, &array)) { pr_err("Cannot get array info for %s\n", devname); goto abort; } - sysfs_init(&info, fd, 0); + sysfs_init(&info, fd, NULL); /* array.size is only 32 bits and may be truncated. * So read from sysfs if possible, and record number of sectors @@ -896,18 +1179,19 @@ int Manage_subdevs(char *devname, int fd, stb.st_rdev = 0; for (dv = devlist; dv; dv = dv->next) { - int err; int rv; if (strcmp(dv->devname, "failed") == 0 || strcmp(dv->devname, "faulty") == 0) { - if (dv->disposition != 'r') { + if (dv->disposition != 'A' + && dv->disposition != 'r') { pr_err("%s only meaningful " - "with -r, not -%c\n", + "with -r or --re-add, not -%c\n", dv->devname, dv->disposition); goto abort; } - add_faulty(dv, fd, 'r'); + add_faulty(dv, fd, (dv->disposition == 'A' + ? 'F' : 'r')); continue; } if (strcmp(dv->devname, "detached") == 0) { @@ -926,7 +1210,7 @@ int Manage_subdevs(char *devname, int fd, struct mddev_dev **dp; if (dv->disposition != 'A') { pr_err("'missing' only meaningful " - "with --re-add\n"); + "with --re-add\n"); goto abort; } add_devlist = conf_get_devs(); @@ -942,9 +1226,38 @@ int Manage_subdevs(char *devname, int fd, continue; } + if (strncmp(dv->devname, "set-", 4) == 0 && + strlen(dv->devname) == 5) { + int copies; + + if (dv->disposition != 'r' && + dv->disposition != 'f') { + pr_err("'%s' only meaningful with -r or -f\n", + dv->devname); + goto abort; + } + if (array.level != 10) { + pr_err("'%s' only meaningful with RAID10 arrays\n", + dv->devname); + goto abort; + } + copies = ((array.layout & 0xff) * + ((array.layout >> 8) & 0xff)); + if (array.raid_disks % copies != 0 || + dv->devname[4] < 'A' || + dv->devname[4] >= 'A' + copies || + copies > 26) { + pr_err("'%s' not meaningful with this array\n", + dv->devname); + goto abort; + } + add_set(dv, fd, dv->devname[4]); + continue; + } + if (strchr(dv->devname, '/') == NULL && - strchr(dv->devname, ':') == NULL && - strlen(dv->devname) < 50) { + strchr(dv->devname, ':') == NULL && + strlen(dv->devname) < 50) { /* Assume this is a kernel-internal name like 'sda1' */ int found = 0; char dname[55]; @@ -956,7 +1269,7 @@ int Manage_subdevs(char *devname, int fd, } sprintf(dname, "dev-%s", dv->devname); - sysfd = sysfs_open(fd2devnum(fd), dname, "block/dev"); + sysfd = sysfs_open(fd2devnm(fd), dname, "block/dev"); if (sysfd >= 0) { char dn[20]; int mj,mn; @@ -969,7 +1282,7 @@ int Manage_subdevs(char *devname, int fd, sysfd = -1; } if (!found) { - sysfd = sysfs_open(fd2devnum(fd), dname, "state"); + sysfd = sysfs_open(fd2devnm(fd), dname, "state"); if (sysfd < 0) { pr_err("%s does not appear " "to be a component of %s\n", @@ -979,34 +1292,36 @@ int Manage_subdevs(char *devname, int fd, } } else { tfd = dev_open(dv->devname, O_RDONLY); - if (tfd < 0 && dv->disposition == 'r' && - lstat(dv->devname, &stb) == 0) - /* Be happy, the lstat worked, that is - * enough for --remove - */ - ; + if (tfd >= 0) + fstat(tfd, &stb); else { - if (tfd < 0 || fstat(tfd, &stb) != 0) { - if (tfd >= 0) - close(tfd); + int open_err = errno; + if (stat(dv->devname, &stb) != 0) { + pr_err("Cannot find %s: %s\n", + dv->devname, strerror(errno)); + goto abort; + } + if ((stb.st_mode & S_IFMT) != S_IFBLK) { + if (dv->disposition == 'M') + /* non-fatal. Also improbable */ + continue; + pr_err("%s is not a block device.\n", + dv->devname); + goto abort; + } + if (dv->disposition == 'r') + /* Be happy, the stat worked, that is + * enough for --remove + */ + ; + else { if (dv->disposition == 'M') /* non-fatal */ continue; - pr_err("cannot find %s: %s\n", - dv->devname, strerror(errno)); + pr_err("Cannot open %s: %s\n", + dv->devname, strerror(open_err)); goto abort; } - close(tfd); - tfd = -1; - } - if ((stb.st_mode & S_IFMT) != S_IFBLK) { - if (dv->disposition == 'M') - /* non-fatal. Also improbable */ - continue; - pr_err("%s is not a " - "block device.\n", - dv->devname); - goto abort; } } switch(dv->disposition){ @@ -1016,7 +1331,8 @@ int Manage_subdevs(char *devname, int fd, goto abort; case 'a': case 'A': - case 'M': + case 'M': /* --re-add missing */ + case 'F': /* --re-add faulty */ /* add the device */ if (subarray) { pr_err("Cannot add disks to a" @@ -1024,6 +1340,10 @@ int Manage_subdevs(char *devname, int fd, " operation on the parent container\n"); goto abort; } + if (dv->disposition == 'F') + /* Need to remove first */ + ioctl(fd, HOT_REMOVE_DISK, + (unsigned long)stb.st_rdev); /* Make sure it isn't in use (in 2.6 or later) */ tfd = dev_open(dv->devname, O_RDONLY|O_EXCL); if (tfd >= 0) { @@ -1064,111 +1384,18 @@ int Manage_subdevs(char *devname, int fd, pr_err("Cannot remove disks from a" " \'member\' array, perform this" " operation on the parent container\n"); - if (sysfd >= 0) - close(sysfd); - goto abort; - } - if (tst->ss->external) { - /* To remove a device from a container, we must - * check that it isn't in use in an array. - * This involves looking in the 'holders' - * directory - there must be just one entry, - * the container. - * To ensure that it doesn't get used as a - * hot spare while we are checking, we - * get an O_EXCL open on the container - */ - int dnum = fd2devnum(fd); - lfd = open_dev_excl(dnum); - if (lfd < 0) { - pr_err("Cannot get exclusive access " - " to container - odd\n"); - if (sysfd >= 0) - close(sysfd); - goto abort; - } - /* In the detached case it is not possible to - * check if we are the unique holder, so just - * rely on the 'detached' checks - */ - if (strcmp(dv->devname, "detached") == 0 || - sysfd >= 0 || - sysfs_unique_holder(dnum, stb.st_rdev)) - /* pass */; - else { - pr_err("%s is %s, cannot remove.\n", - dv->devname, - errno == EEXIST ? "still in use": - "not a member"); - close(lfd); - goto abort; - } - } - /* FIXME check that it is a current member */ - if (sysfd >= 0) { - /* device has been removed and we don't know - * the major:minor number - */ - int n = write(sysfd, "remove", 6); - if (n != 6) - err = -1; - else - err = 0; + rv = -1; + } else + rv = Manage_remove(tst, fd, dv, sysfd, + stb.st_rdev, verbose, + devname); + if (sysfd >= 0) close(sysfd); - sysfd = -1; - } else { - err = ioctl(fd, HOT_REMOVE_DISK, (unsigned long)stb.st_rdev); - if (err && errno == ENODEV) { - /* Old kernels rejected this if no personality - * is registered */ - struct mdinfo *sra = sysfs_read(fd, 0, GET_DEVS); - struct mdinfo *dv = NULL; - if (sra) - dv = sra->devs; - for ( ; dv ; dv=dv->next) - if (dv->disk.major == (int)major(stb.st_rdev) && - dv->disk.minor == (int)minor(stb.st_rdev)) - break; - if (dv) - err = sysfs_set_str(sra, dv, - "state", "remove"); - else - err = -1; - if (sra) - sysfs_free(sra); - } - } - if (err) { - pr_err("hot remove failed " - "for %s: %s\n", dv->devname, - strerror(errno)); - if (lfd >= 0) - close(lfd); + sysfd = -1; + if (rv < 0) goto abort; - } - if (tst->ss->external) { - /* - * Before dropping our exclusive open we make an - * attempt at preventing mdmon from seeing an - * 'add' event before reconciling this 'remove' - * event. - */ - char *name = devnum2devname(fd2devnum(fd)); - - if (!name) { - pr_err("unable to get container name\n"); - goto abort; - } - - ping_manager(name); - free(name); - } - if (lfd >= 0) - close(lfd); - count++; - if (verbose >= 0) - pr_err("hot removed %s from %s\n", - dv->devname, devname); + if (rv > 0) + count++; break; case 'f': /* set faulty */ @@ -1176,6 +1403,8 @@ int Manage_subdevs(char *devname, int fd, if ((sysfd >= 0 && write(sysfd, "faulty", 6) != 6) || (sysfd < 0 && ioctl(fd, SET_DISK_FAULTY, (unsigned long) stb.st_rdev))) { + if (errno == EBUSY) + busy = 1; pr_err("set device faulty failed for %s: %s\n", dv->devname, strerror(errno)); if (sysfd >= 0) @@ -1190,6 +1419,38 @@ int Manage_subdevs(char *devname, int fd, pr_err("set %s faulty in %s\n", dv->devname, devname); break; + case 'R': /* Mark as replaceable */ + if (subarray) { + pr_err("Cannot replace disks in a" + " \'member\' array, perform this" + " operation on the parent container\n"); + rv = -1; + } else { + if (!frozen) { + if (sysfs_freeze_array(&info) == 1) + frozen = 1; + else + frozen = -1; + } + rv = Manage_replace(tst, fd, dv, + stb.st_rdev, verbose, + devname); + } + if (rv < 0) + goto abort; + if (rv > 0) + count++; + break; + case 'W': /* --with device that doesn't match */ + pr_err("No matching --replace device for --with %s\n", + dv->devname); + goto abort; + case 'w': /* --with device which was matched */ + rv = Manage_with(tst, fd, dv, + stb.st_rdev, verbose, devname); + if (rv < 0) + goto abort; + break; } } if (frozen > 0) @@ -1201,7 +1462,7 @@ int Manage_subdevs(char *devname, int fd, abort: if (frozen > 0) sysfs_set_str(&info, NULL, "sync_action","idle"); - return 1; + return !test && busy ? 2 : 1; } int autodetect(void) @@ -1235,7 +1496,7 @@ int Update_subarray(char *dev, char *subarray, char *update, struct mddev_ident goto free_super; } - if (mdmon_running(st->devnum)) + if (mdmon_running(st->devnm)) st->update_tail = &st->updates; rv = st->ss->update_subarray(st, subarray, update, ident);