summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2010-06-15 17:55:41 -0700
committerDan Williams <dan.j.williams@intel.com>2010-06-15 17:55:41 -0700
commit33414a0182ae193150f65f7bca97a7e4d818a49e (patch)
treeb468723303ac3fa1e9f452cf2dd43cd94ab4e0e5
parent200871adf9e15d5ad985f28c349fd89c386ef48a (diff)
downloadmdadm-33414a0182ae193150f65f7bca97a7e4d818a49e.zip
mdadm-33414a0182ae193150f65f7bca97a7e4d818a49e.tar.gz
mdadm-33414a0182ae193150f65f7bca97a7e4d818a49e.tar.xz
Kill subarray v2
Support for deleting a subarray out of a container. When all subarrays are deleted the component devices are converted back into spares, a --zero-superblock is still needed to kill the remaining metadata at this point. This operation is blocked when the subarray is active and may also be blocked by the metadata handler when deleting the subarray might change the uuid of other active subarrays. For example, with imsm, deleting subarray 'n' may change the uuid of subarrays with indexes > n. Deleting a subarray needs to be a container wide event to ensure disks that record the modified subarray list perceive other disks that did not receive this change as out of date. Notes: The st->subarray parsing in super-intel.c and super-ddf.c is updated to be more strict now that we are reading user supplied subarray values. Offline container modification shares actions that mdmon typically handles so promote is_container_member() and version_to_superswitch() (formerly find_metadata_methods()) to generic utility functions for the cases where mdadm performs the operation. Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--Kill.c78
-rw-r--r--ReadMe.c1
-rw-r--r--mdadm.810
-rw-r--r--mdadm.c8
-rw-r--r--mdadm.h8
-rw-r--r--mdmon.c25
-rw-r--r--super-ddf.c25
-rw-r--r--super-intel.c179
-rw-r--r--util.c137
9 files changed, 431 insertions, 40 deletions
diff --git a/Kill.c b/Kill.c
index e738978..b3344bd 100644
--- a/Kill.c
+++ b/Kill.c
@@ -79,3 +79,81 @@ int Kill(char *dev, struct supertype *st, int force, int quiet, int noexcl)
close(fd);
return rv;
}
+
+int Kill_subarray(char *dev, char *subarray, int quiet)
+{
+ /* Delete a subarray out of a container, the subarry must be
+ * inactive. The subarray string must be a subarray index
+ * number.
+ *
+ * 0 = successfully deleted subarray from all container members
+ * 1 = failed to sync metadata to one or more devices
+ * 2 = failed to find the container, subarray, or other resource
+ * issue
+ */
+ struct supertype supertype, *st = &supertype;
+ int fd, rv = 2;
+
+ memset(st, 0, sizeof(*st));
+
+ if (snprintf(st->subarray, sizeof(st->subarray), "%s", subarray) >=
+ 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);
+ if (fd < 0)
+ return 2;
+
+ if (!st->ss->kill_subarray) {
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Operation not supported for %s metadata\n",
+ st->ss->name);
+ goto free_super;
+ }
+
+ if (is_subarray_active(subarray, st->devname)) {
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Subarray-%s still active, aborting\n",
+ subarray);
+ goto free_super;
+ }
+
+ if (mdmon_running(st->devnum))
+ st->update_tail = &st->updates;
+
+ /* ok we've found our victim, drop the axe */
+ rv = st->ss->kill_subarray(st);
+ if (rv) {
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Failed to delete subarray-%s from %s\n",
+ subarray, dev);
+ goto free_super;
+ }
+
+ /* FIXME these routines do not report success/failure */
+ if (st->update_tail)
+ flush_metadata_updates(st);
+ else
+ st->ss->sync_metadata(st);
+
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Deleted subarray-%s from %s, UUIDs may have changed\n",
+ subarray, dev);
+
+ rv = 0;
+
+ free_super:
+ st->ss->free_super(st);
+ close(fd);
+
+ return rv;
+}
diff --git a/ReadMe.c b/ReadMe.c
index 9d5a211..387ba6d 100644
--- a/ReadMe.c
+++ b/ReadMe.c
@@ -108,6 +108,7 @@ struct option long_options[] = {
{"examine-bitmap", 0, 0, 'X'},
{"auto-detect", 0, 0, AutoDetect},
{"detail-platform", 0, 0, DetailPlatform},
+ {"kill-subarray", 1, 0, KillSubarray},
/* synonyms */
{"monitor", 0, 0, 'F'},
diff --git a/mdadm.8 b/mdadm.8
index 4edfc41..784ba31 100644
--- a/mdadm.8
+++ b/mdadm.8
@@ -1173,6 +1173,16 @@ the block where the superblock would be is overwritten even if it
doesn't appear to be valid.
.TP
+.B \-\-kill\-subarray=
+If the device is a container and the argument to \-\-kill\-subarray
+specifies an inactive subarray in the container, then the subarray is
+deleted. Deleting all subarrays will leave an 'empty-container' or
+spare superblock on the drives. See \-\-zero\-superblock for completely
+removing a superblock. Note that some formats depend on the subarray
+index for generating a UUID, this command will fail if it would change
+the UUID of an active subarray.
+
+.TP
.BR \-t ", " \-\-test
When used with
.BR \-\-detail ,
diff --git a/mdadm.c b/mdadm.c
index d5e34c0..446fab8 100644
--- a/mdadm.c
+++ b/mdadm.c
@@ -103,6 +103,7 @@ int main(int argc, char *argv[])
int dosyslog = 0;
int rebuild_map = 0;
int auto_update_home = 0;
+ char *subarray = NULL;
int print_help = 0;
FILE *outf;
@@ -216,6 +217,9 @@ int main(int argc, char *argv[])
case 'W':
case Waitclean:
case DetailPlatform:
+ case KillSubarray:
+ if (opt == KillSubarray)
+ subarray = optarg;
case 'K': if (!mode) newmode = MISC; break;
}
if (mode && newmode == mode) {
@@ -807,6 +811,7 @@ int main(int argc, char *argv[])
case O(MISC,'W'):
case O(MISC, Waitclean):
case O(MISC, DetailPlatform):
+ case O(MISC, KillSubarray):
if (devmode && devmode != opt &&
(devmode == 'E' || (opt == 'E' && devmode != 'Q'))) {
fprintf(stderr, Name ": --examine/-E cannot be given with -%c\n",
@@ -1403,6 +1408,9 @@ int main(int argc, char *argv[])
rv |= Wait(dv->devname); continue;
case Waitclean:
rv |= WaitClean(dv->devname, -1, verbose-quiet); continue;
+ case KillSubarray:
+ rv |= Kill_subarray(dv->devname, subarray, quiet);
+ continue;
}
mdfd = open_mddev(dv->devname, 1);
if (mdfd>=0) {
diff --git a/mdadm.h b/mdadm.h
index d9d17b0..f387477 100644
--- a/mdadm.h
+++ b/mdadm.h
@@ -273,6 +273,7 @@ enum special_options {
AutoDetect,
Waitclean,
DetailPlatform,
+ KillSubarray,
};
/* structures read from config file */
@@ -609,6 +610,8 @@ extern struct superswitch {
struct mdinfo *(*container_content)(struct supertype *st);
/* Allow a metadata handler to override mdadm's default layouts */
int (*default_layout)(int level); /* optional */
+ /* Permit subarray's to be deleted from inactive containers */
+ int (*kill_subarray)(struct supertype *st); /* optional */
/* for mdmon */
int (*open_new)(struct supertype *c, struct active_array *a,
@@ -805,6 +808,7 @@ extern int Monitor(mddev_dev_t devlist,
int dosyslog, int test, char *pidfile, int increments);
extern int Kill(char *dev, struct supertype *st, int force, int quiet, int noexcl);
+extern int Kill_subarray(char *dev, char *subarray, int quiet);
extern int Wait(char *dev);
extern int WaitClean(char *dev, int sock, int verbose);
@@ -911,6 +915,10 @@ extern int create_mddev(char *dev, char *name, int autof, int trustworthy,
#define METADATA 3
extern int open_mddev(char *dev, int report_errors);
extern int open_container(int fd);
+extern int is_container_member(struct mdstat_ent *ent, char *devname);
+extern int is_subarray_active(char *subarray, char *devname);
+extern int open_subarray(char *dev, struct supertype *st, int quiet);
+extern struct superswitch *version_to_superswitch(char *vers);
extern char *pid_dir;
extern int mdmon_running(int devnum);
diff --git a/mdmon.c b/mdmon.c
index 69c320e..beb39cf 100644
--- a/mdmon.c
+++ b/mdmon.c
@@ -104,15 +104,6 @@ int __clone2(int (*fn)(void *),
return mon_tid;
}
-static struct superswitch *find_metadata_methods(char *vers)
-{
- if (strcmp(vers, "ddf") == 0)
- return &super_ddf;
- if (strcmp(vers, "imsm") == 0)
- return &super_imsm;
- return NULL;
-}
-
static int make_pidfile(char *devname)
{
char path[100];
@@ -136,18 +127,6 @@ static int make_pidfile(char *devname)
return 0;
}
-int is_container_member(struct mdstat_ent *mdstat, char *container)
-{
- if (mdstat->metadata_version == NULL ||
- strncmp(mdstat->metadata_version, "external:", 9) != 0 ||
- !is_subarray(mdstat->metadata_version+9) ||
- strncmp(mdstat->metadata_version+10, container, strlen(container)) != 0 ||
- mdstat->metadata_version[10+strlen(container)] != '/')
- return 0;
-
- return 1;
-}
-
static void try_kill_monitor(pid_t pid, char *devname, int sock)
{
char buf[100];
@@ -414,9 +393,9 @@ static int mdmon(char *devname, int devnum, int must_fork, int takeover)
exit(3);
}
- container->ss = find_metadata_methods(mdi->text_version);
+ container->ss = version_to_superswitch(mdi->text_version);
if (container->ss == NULL) {
- fprintf(stderr, "mdmon: %s uses unknown metadata: %s\n",
+ fprintf(stderr, "mdmon: %s uses unsupported metadata: %s\n",
devname, mdi->text_version);
exit(3);
}
diff --git a/super-ddf.c b/super-ddf.c
index 0e6f1e5..736e07f 100644
--- a/super-ddf.c
+++ b/super-ddf.c
@@ -845,10 +845,18 @@ static int load_super_ddf(struct supertype *st, int fd,
}
if (st->subarray[0]) {
+ unsigned long val;
struct vcl *v;
+ char *ep;
+
+ val = strtoul(st->subarray, &ep, 10);
+ if (*ep != '\0') {
+ free(super);
+ return 1;
+ }
for (v = super->conflist; v; v = v->next)
- if (v->vcnum == atoi(st->subarray))
+ if (v->vcnum == val)
super->currentconf = v;
if (!super->currentconf) {
free(super);
@@ -2870,14 +2878,25 @@ static int load_super_ddf_all(struct supertype *st, int fd,
return 1;
}
if (st->subarray[0]) {
+ unsigned long val;
struct vcl *v;
+ char *ep;
+
+ val = strtoul(st->subarray, &ep, 10);
+ if (*ep != '\0') {
+ free(super);
+ return 1;
+ }
for (v = super->conflist; v; v = v->next)
- if (v->vcnum == atoi(st->subarray))
+ if (v->vcnum == val)
super->currentconf = v;
- if (!super->currentconf)
+ if (!super->currentconf) {
+ free(super);
return 1;
+ }
}
+
*sbp = super;
if (st->ss == NULL) {
st->ss = &super_ddf;
diff --git a/super-intel.c b/super-intel.c
index bdd7a96..d81d620 100644
--- a/super-intel.c
+++ b/super-intel.c
@@ -282,6 +282,7 @@ struct extent {
enum imsm_update_type {
update_activate_spare,
update_create_array,
+ update_kill_array,
update_add_disk,
};
@@ -303,6 +304,11 @@ struct imsm_update_create_array {
struct imsm_dev dev;
};
+struct imsm_update_kill_array {
+ enum imsm_update_type type;
+ int dev_idx;
+};
+
struct imsm_update_add_disk {
enum imsm_update_type type;
};
@@ -2753,11 +2759,20 @@ static int load_super_imsm_all(struct supertype *st, int fd, void **sbp,
}
if (st->subarray[0]) {
- if (atoi(st->subarray) <= super->anchor->num_raid_devs)
- super->current_vol = atoi(st->subarray);
+ unsigned long val;
+ char *ep;
+
+ err = 1;
+ val = strtoul(st->subarray, &ep, 10);
+ if (*ep != '\0') {
+ free_imsm(super);
+ goto error;
+ }
+
+ if (val < super->anchor->num_raid_devs)
+ super->current_vol = val;
else {
free_imsm(super);
- err = 1;
goto error;
}
}
@@ -2824,8 +2839,17 @@ static int load_super_imsm(struct supertype *st, int fd, char *devname)
}
if (st->subarray[0]) {
- if (atoi(st->subarray) <= super->anchor->num_raid_devs)
- super->current_vol = atoi(st->subarray);
+ unsigned long val;
+ char *ep;
+
+ val = strtoul(st->subarray, &ep, 10);
+ if (*ep != '\0') {
+ free_imsm(super);
+ return 1;
+ }
+
+ if (val < super->anchor->num_raid_devs)
+ super->current_vol = val;
else {
free_imsm(super);
return 1;
@@ -4007,6 +4031,82 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
close(cfd);
return 0;
}
+
+static void handle_missing(struct intel_super *super, struct imsm_dev *dev);
+
+static int kill_subarray_imsm(struct supertype *st)
+{
+ /* remove the subarray currently referenced by ->current_vol */
+ __u8 i;
+ struct intel_dev **dp;
+ struct intel_super *super = st->sb;
+ __u8 current_vol = super->current_vol;
+ struct imsm_super *mpb = super->anchor;
+
+ if (super->current_vol < 0)
+ return 2;
+ super->current_vol = -1; /* invalidate subarray cursor */
+
+ /* block deletions that would change the uuid of active subarrays
+ *
+ * FIXME when immutable ids are available, but note that we'll
+ * also need to fixup the invalidated/active subarray indexes in
+ * mdstat
+ */
+ for (i = 0; i < mpb->num_raid_devs; i++) {
+ char subarray[4];
+
+ if (i < current_vol)
+ continue;
+ sprintf(subarray, "%u", i);
+ if (is_subarray_active(subarray, st->devname)) {
+ fprintf(stderr,
+ Name ": deleting subarray-%d would change the UUID of active subarray-%d, aborting\n",
+ current_vol, i);
+
+ return 2;
+ }
+ }
+
+ if (st->update_tail) {
+ struct imsm_update_kill_array *u = malloc(sizeof(*u));
+
+ if (!u)
+ return 2;
+ u->type = update_kill_array;
+ u->dev_idx = current_vol;
+ append_metadata_update(st, u, sizeof(*u));
+
+ return 0;
+ }
+
+ for (dp = &super->devlist; *dp;)
+ if ((*dp)->index == current_vol) {
+ *dp = (*dp)->next;
+ } else {
+ handle_missing(super, (*dp)->dev);
+ if ((*dp)->index > current_vol)
+ (*dp)->index--;
+ dp = &(*dp)->next;
+ }
+
+ /* no more raid devices, all active components are now spares,
+ * but of course failed are still failed
+ */
+ if (--mpb->num_raid_devs == 0) {
+ struct dl *d;
+
+ for (d = super->disks; d; d = d->next)
+ if (d->index > -2) {
+ d->index = -1;
+ d->disk.status = SPARE_DISK;
+ }
+ }
+
+ super->updates_pending++;
+
+ return 0;
+}
#endif /* MDASSEMBLE */
static int is_rebuilding(struct imsm_dev *dev)
@@ -4347,6 +4447,24 @@ static void mark_missing(struct imsm_dev *dev, struct imsm_disk *disk, int idx)
memmove(&disk->serial[0], &disk->serial[1], MAX_RAID_SERIAL_LEN - 1);
}
+static void handle_missing(struct intel_super *super, struct imsm_dev *dev)
+{
+ __u8 map_state;
+ struct dl *dl;
+ int failed;
+
+ if (!super->missing)
+ return;
+ failed = imsm_count_failed(super, dev);
+ map_state = imsm_check_degraded(super, dev, failed);
+
+ dprintf("imsm: mark missing\n");
+ end_migration(dev, map_state);
+ for (dl = super->missing; dl; dl = dl->next)
+ mark_missing(dev, &dl->disk, dl->index);
+ super->updates_pending++;
+}
+
/* Handle dirty -> clean transititions and resync. Degraded and rebuild
* states are handled in imsm_set_disk() with one exception, when a
* resync is stopped due to a new failure this routine will set the
@@ -4363,15 +4481,8 @@ static int imsm_set_array_state(struct active_array *a, int consistent)
__u32 blocks_per_unit;
/* before we activate this array handle any missing disks */
- if (consistent == 2 && super->missing) {
- struct dl *dl;
-
- dprintf("imsm: mark missing\n");
- end_migration(dev, map_state);
- for (dl = super->missing; dl; dl = dl->next)
- mark_missing(dev, &dl->disk, dl->index);
- super->updates_pending++;
- }
+ if (consistent == 2)
+ handle_missing(super, dev);
if (consistent == 2 &&
(!is_resync_complete(&a->info) ||
@@ -5067,6 +5178,45 @@ static void imsm_process_update(struct supertype *st,
}
break;
}
+ case update_kill_array: {
+ struct imsm_update_kill_array *u = (void *) update->buf;
+ int victim = u->dev_idx;
+ struct active_array *a;
+ struct intel_dev **dp;
+ struct imsm_dev *dev;
+
+ /* sanity check that we are not affecting the uuid of
+ * active arrays, or deleting an active array
+ *
+ * FIXME when immutable ids are available, but note that
+ * we'll also need to fixup the invalidated/active
+ * subarray indexes in mdstat
+ */
+ for (a = st->arrays; a; a = a->next)
+ if (a->info.container_member >= victim)
+ break;
+ /* by definition if mdmon is running at least one array
+ * is active in the container, so checking
+ * mpb->num_raid_devs is just extra paranoia
+ */
+ dev = get_imsm_dev(super, victim);
+ if (a || !dev || mpb->num_raid_devs == 1) {
+ dprintf("failed to delete subarray-%d\n", victim);
+ break;
+ }
+
+ for (dp = &super->devlist; *dp;)
+ if ((*dp)->index == super->current_vol) {
+ *dp = (*dp)->next;
+ } else {
+ if ((*dp)->index > victim)
+ (*dp)->index--;
+ dp = &(*dp)->next;
+ }
+ mpb->num_raid_devs--;
+ super->updates_pending++;
+ break;
+ }
case update_add_disk:
/* we may be able to repair some arrays if disks are
@@ -5242,6 +5392,7 @@ struct superswitch super_imsm = {
.validate_geometry = validate_geometry_imsm,
.add_to_super = add_to_super_imsm,
.detail_platform = detail_platform_imsm,
+ .kill_subarray = kill_subarray_imsm,
#endif
.match_home = match_home_imsm,
.uuid_from_super= uuid_from_super_imsm,
diff --git a/util.c b/util.c
index 25f1e56..66bf2f9 100644
--- a/util.c
+++ b/util.c
@@ -1392,6 +1392,143 @@ int open_container(int fd)
return -1;
}
+struct superswitch *version_to_superswitch(char *vers)
+{
+ int i;
+
+ for (i = 0; superlist[i]; i++) {
+ struct superswitch *ss = superlist[i];
+
+ if (strcmp(vers, ss->name) == 0)
+ return ss;
+ }
+
+ return NULL;
+}
+
+int is_container_member(struct mdstat_ent *mdstat, char *container)
+{
+ if (mdstat->metadata_version == NULL ||
+ strncmp(mdstat->metadata_version, "external:", 9) != 0 ||
+ !is_subarray(mdstat->metadata_version+9) ||
+ strncmp(mdstat->metadata_version+10, container, strlen(container)) != 0 ||
+ mdstat->metadata_version[10+strlen(container)] != '/')
+ return 0;
+
+ return 1;
+}
+
+int is_subarray_active(char *subarray, char *container)
+{
+ struct mdstat_ent *mdstat = mdstat_read(0, 0);
+ struct mdstat_ent *ent;
+
+ for (ent = mdstat; ent; ent = ent->next) {
+ if (is_container_member(ent, container)) {
+ char *inst = &ent->metadata_version[10+strlen(container)+1];
+
+ if (strcmp(inst, subarray) == 0)
+ break;
+ }
+ }
+
+ free_mdstat(mdstat);
+
+ return ent != NULL;
+}
+
+/* open_subarray - opens a subarray in a container
+ * @dev: container device name
+ * @st: supertype with only ->subarray set
+ * @quiet: block reporting errors flag
+ *
+ * On success returns an fd to a container and fills in *st
+ */
+int open_subarray(char *dev, struct supertype *st, int quiet)
+{
+ struct mdinfo *mdi;
+ int fd, err = 1;
+
+ fd = open(dev, O_RDWR|O_EXCL);
+ if (fd < 0) {
+ if (!quiet)
+ fprintf(stderr, Name ": Couldn't open %s, aborting\n",
+ dev);
+ return 2;
+ }
+
+ st->devnum = fd2devnum(fd);
+ if (st->devnum == NoMdDev) {
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Failed to determine device number for %s\n",
+ dev);
+ goto close_fd;
+ }
+
+ mdi = sysfs_read(fd, st->devnum, GET_VERSION|GET_LEVEL);
+ if (!mdi) {
+ if (!quiet)
+ fprintf(stderr, Name ": Failed to read sysfs for %s\n",
+ dev);
+ goto close_fd;
+ }
+
+ if (mdi->array.level != UnSet) {
+ if (!quiet)
+ fprintf(stderr, Name ": %s is not a container\n", dev);
+ goto free_sysfs;
+ }
+
+ st->ss = version_to_superswitch(mdi->text_version);
+ if (!st->ss) {
+ if (!quiet)
+ fprintf(stderr,
+ Name ": Operation not supported for %s metadata\n",
+ mdi->text_version);
+ goto free_sysfs;
+ }
+
+ st->devname = devnum2devname(st->devnum);
+ if (!st->devname) {
+ if (!quiet)
+ fprintf(stderr, Name ": Failed to allocate device name\n");
+ goto free_sysfs;
+ }
+
+ if (st->ss->load_super(st, fd, NULL)) {
+ if (!quiet)
+ fprintf(stderr, Name ": Failed to find subarray-%s in %s\n",
+ st->subarray, dev);
+ goto free_name;
+ }
+
+ if (!st->loaded_container) {
+ if (!quiet)
+ fprintf(stderr, Name ": %s is not a container\n", dev);
+ goto free_super;
+ }
+
+ err = 0;
+
+ free_super:
+ if (err)
+ st->ss->free_super(st);
+ free_name:
+ if (err)
+ free(st->devname);
+ free_sysfs:
+ sysfs_free(mdi);
+ close_fd:
+ if (err)
+ close(fd);
+
+ if (err)
+ return -1;
+ else
+ return fd;
+}
+
int add_disk(int mdfd, struct supertype *st,
struct mdinfo *sra, struct mdinfo *info)
{