diff options
author | mwilck@arcor.de <mwilck@arcor.de> | 2013-07-08 23:50:40 +0200 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2013-07-10 02:40:43 +0200 |
commit | f646805e808971a8dd874b8f7ae74f17c1b37f0b (patch) | |
tree | 3d00d39c542d6af0487ef6269f4fa4f1d37e89f7 | |
parent | DDF: write_init_super_ddf: don't zero superblocks for subarrays (diff) | |
download | mdadm-f646805e808971a8dd874b8f7ae74f17c1b37f0b.tar.xz mdadm-f646805e808971a8dd874b8f7ae74f17c1b37f0b.zip |
DDF: implement kill_subarray
Implement kill_subarray, for mdmon running and not running.
The way Kill_subarray() is implemented, this requires that the
DDF layer uses "currentconf" to remember the last subarray
queried with container_content(), and use it as the one to kill.
I don't like this much but IMSM does it the same way.
Signed-off-by: NeilBrown <neilb@suse.de>
-rw-r--r-- | super-ddf.c | 106 |
1 files changed, 99 insertions, 7 deletions
diff --git a/super-ddf.c b/super-ddf.c index 87584fa5..ee4652ad 100644 --- a/super-ddf.c +++ b/super-ddf.c @@ -3616,7 +3616,8 @@ static struct mdinfo *container_content_ddf(struct supertype *st, char *subarray ddf->currentconf = vc; uuid_from_super_ddf(st, this->uuid); - ddf->currentconf = NULL; + if (!subarray) + ddf->currentconf = NULL; sprintf(this->text_version, "/%s/%d", st->container_devnm, this->container_member); @@ -4160,6 +4161,88 @@ static void ddf_sync_metadata(struct supertype *st) dprintf("ddf: sync_metadata\n"); } +static int del_from_conflist(struct vcl **list, const char *guid) +{ + struct vcl **p; + int found = 0; + for (p = list; p && *p; p = &((*p)->next)) + if (!memcmp((*p)->conf.guid, guid, DDF_GUID_LEN)) { + found = 1; + *p = (*p)->next; + } + return found; +} + +static int _kill_subarray_ddf(struct ddf_super *ddf, const char *guid) +{ + struct dl *dl; + unsigned int vdnum, i; + vdnum = find_vde_by_guid(ddf, guid); + if (vdnum == DDF_NOTFOUND) { + pr_err("%s: could not find VD %s\n", __func__, + guid_str(guid)); + return -1; + } + if (del_from_conflist(&ddf->conflist, guid) == 0) { + pr_err("%s: could not find conf %s\n", __func__, + guid_str(guid)); + return -1; + } + for (dl = ddf->dlist; dl; dl = dl->next) + for (i = 0; i < ddf->max_part; i++) + if (dl->vlist[i] != NULL && + !memcmp(dl->vlist[i]->conf.guid, guid, + DDF_GUID_LEN)) + dl->vlist[i] = NULL; + memset(ddf->virt->entries[vdnum].guid, 0xff, DDF_GUID_LEN); + dprintf("%s: deleted %s\n", __func__, guid_str(guid)); + return 0; +} + +static int kill_subarray_ddf(struct supertype *st) +{ + struct ddf_super *ddf = st->sb; + /* + * currentconf is set in container_content_ddf, + * called with subarray arg + */ + struct vcl *victim = ddf->currentconf; + struct vd_config *conf; + ddf->currentconf = NULL; + unsigned int vdnum; + if (!victim) { + pr_err("%s: nothing to kill\n", __func__); + return -1; + } + conf = &victim->conf; + vdnum = find_vde_by_guid(ddf, conf->guid); + if (vdnum == DDF_NOTFOUND) { + pr_err("%s: could not find VD %s\n", __func__, + guid_str(conf->guid)); + return -1; + } + if (st->update_tail) { + struct virtual_disk *vd; + int len = sizeof(struct virtual_disk) + + sizeof(struct virtual_entry); + vd = xmalloc(len); + if (vd == NULL) { + pr_err("%s: failed to allocate %d bytes\n", __func__, + len); + return -1; + } + memset(vd, 0 , len); + vd->magic = DDF_VIRT_RECORDS_MAGIC; + vd->populated_vdes = 0; + memcpy(vd->entries[0].guid, conf->guid, DDF_GUID_LEN); + /* we use DDF_state_deleted as marker */ + vd->entries[0].state = DDF_state_deleted; + append_metadata_update(st, vd, len); + } else + _kill_subarray_ddf(ddf, conf->guid); + return 0; +} + static void ddf_process_update(struct supertype *st, struct metadata_update *update) { @@ -4262,12 +4345,20 @@ static void ddf_process_update(struct supertype *st, return; vd = (struct virtual_disk*)update->buf; - ent = find_unused_vde(ddf); - if (ent == DDF_NOTFOUND) - return; - ddf->virt->entries[ent] = vd->entries[0]; - ddf->virt->populated_vdes = __cpu_to_be16(1 + - __be16_to_cpu(ddf->virt->populated_vdes)); + if (vd->entries[0].state == DDF_state_deleted) { + if (_kill_subarray_ddf(ddf, vd->entries[0].guid)) + return; + } else { + + ent = find_unused_vde(ddf); + if (ent == DDF_NOTFOUND) + return; + ddf->virt->entries[ent] = vd->entries[0]; + ddf->virt->populated_vdes = + __cpu_to_be16( + 1 + __be16_to_cpu( + ddf->virt->populated_vdes)); + } ddf_set_updates_pending(ddf); break; @@ -4700,6 +4791,7 @@ struct superswitch super_ddf = { .match_metadata_desc = match_metadata_desc_ddf, .container_content = container_content_ddf, .default_geometry = default_geometry_ddf, + .kill_subarray = kill_subarray_ddf, .external = 1, |