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;
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"
}
/* 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;
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;
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))
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;
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;
+ int dnum = fd2devnum(fd);
+ lfd = open_dev_excl(dnum);
+ 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(dnum, 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, 0, 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 *name = devnum2devname(fd2devnum(fd));
+
+ if (!name) {
+ pr_err("unable to get container name\n");
+ return -1;
+ }
+
+ ping_manager(name);
+ free(name);
+ }
+ 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, -1, 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, -1, 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<<MD_DISK_FAULTY)) {
+ pr_err("%s is faulty and cannot be a replacement\n",
+ dv->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)
* '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'
+ *
* For 'f' and 'r', the device can also be a kernel-internal
* name such as 'sdb'.
*/
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;
stb.st_rdev = 0;
for (dv = devlist; dv; dv = dv->next) {
- int err;
int rv;
if (strcmp(dv->devname, "failed") == 0 ||
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();
}
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];
}
} 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){
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 */
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)