DDF: split up ddf_process_update

Function was way too big, make several smaller functions.

Signed-off-by: NeilBrown <neilb@suse.de>
This commit is contained in:
NeilBrown 2014-05-21 12:20:56 +10:00
parent a34c8836f0
commit fea6a6c0bf
1 changed files with 297 additions and 264 deletions

View File

@ -4574,6 +4574,297 @@ static void copy_matching_bvd(struct ddf_super *ddf,
conf->sec_elmnt_seq, guid_str(conf->guid));
}
static void ddf_process_phys_update(struct supertype *st,
struct metadata_update *update)
{
struct ddf_super *ddf = st->sb;
struct phys_disk *pd;
unsigned int ent;
pd = (struct phys_disk*)update->buf;
ent = be16_to_cpu(pd->used_pdes);
if (ent >= be16_to_cpu(ddf->phys->max_pdes))
return;
if (be16_and(pd->entries[0].state, cpu_to_be16(DDF_Missing))) {
struct dl **dlp;
/* removing this disk. */
be16_set(ddf->phys->entries[ent].state,
cpu_to_be16(DDF_Missing));
for (dlp = &ddf->dlist; *dlp; dlp = &(*dlp)->next) {
struct dl *dl = *dlp;
if (dl->pdnum == (signed)ent) {
close(dl->fd);
dl->fd = -1;
/* FIXME this doesn't free
* dl->devname */
update->space = dl;
*dlp = dl->next;
break;
}
}
ddf_set_updates_pending(ddf, NULL);
return;
}
if (!all_ff(ddf->phys->entries[ent].guid))
return;
ddf->phys->entries[ent] = pd->entries[0];
ddf->phys->used_pdes = cpu_to_be16
(1 + be16_to_cpu(ddf->phys->used_pdes));
ddf_set_updates_pending(ddf, NULL);
if (ddf->add_list) {
struct active_array *a;
struct dl *al = ddf->add_list;
ddf->add_list = al->next;
al->next = ddf->dlist;
ddf->dlist = al;
/* As a device has been added, we should check
* for any degraded devices that might make
* use of this spare */
for (a = st->arrays ; a; a=a->next)
a->check_degraded = 1;
}
}
static void ddf_process_virt_update(struct supertype *st,
struct metadata_update *update)
{
struct ddf_super *ddf = st->sb;
struct virtual_disk *vd;
unsigned int ent;
vd = (struct virtual_disk*)update->buf;
if (vd->entries[0].state == DDF_state_deleted) {
if (_kill_subarray_ddf(ddf, vd->entries[0].guid))
return;
} else {
ent = find_vde_by_guid(ddf, vd->entries[0].guid);
if (ent != DDF_NOTFOUND) {
dprintf("%s: VD %s exists already in slot %d\n",
__func__, guid_str(vd->entries[0].guid),
ent);
return;
}
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));
dprintf("%s: added VD %s in slot %d(s=%02x i=%02x)\n",
__func__, guid_str(vd->entries[0].guid), ent,
ddf->virt->entries[ent].state,
ddf->virt->entries[ent].init_state);
}
ddf_set_updates_pending(ddf, NULL);
}
static void ddf_remove_failed(struct ddf_super *ddf)
{
/* Now remove any 'Failed' devices that are not part
* of any VD. They will have the Transition flag set.
* Once done, we need to update all dl->pdnum numbers.
*/
unsigned int pdnum;
unsigned int pd2 = 0;
struct dl *dl;
for (pdnum = 0; pdnum < be16_to_cpu(ddf->phys->max_pdes);
pdnum++) {
if (be32_to_cpu(ddf->phys->entries[pdnum].refnum) ==
0xFFFFFFFF)
continue;
if (be16_and(ddf->phys->entries[pdnum].state,
cpu_to_be16(DDF_Failed))
&& be16_and(ddf->phys->entries[pdnum].state,
cpu_to_be16(DDF_Transition))) {
/* skip this one unless in dlist*/
for (dl = ddf->dlist; dl; dl = dl->next)
if (dl->pdnum == (int)pdnum)
break;
if (!dl)
continue;
}
if (pdnum == pd2)
pd2++;
else {
ddf->phys->entries[pd2] =
ddf->phys->entries[pdnum];
for (dl = ddf->dlist; dl; dl = dl->next)
if (dl->pdnum == (int)pdnum)
dl->pdnum = pd2;
pd2++;
}
}
ddf->phys->used_pdes = cpu_to_be16(pd2);
while (pd2 < pdnum) {
memset(ddf->phys->entries[pd2].guid, 0xff,
DDF_GUID_LEN);
pd2++;
}
}
static void ddf_update_vlist(struct ddf_super *ddf, struct dl *dl)
{
struct vcl *vcl;
unsigned int vn = 0;
int in_degraded = 0;
if (dl->pdnum < 0)
return;
for (vcl = ddf->conflist; vcl ; vcl = vcl->next) {
unsigned int dn, ibvd;
const struct vd_config *conf;
int vstate;
dn = get_pd_index_from_refnum(vcl,
dl->disk.refnum,
ddf->mppe,
&conf, &ibvd);
if (dn == DDF_NOTFOUND)
continue;
dprintf("dev %d/%08x has %s (sec=%u) at %d\n",
dl->pdnum,
be32_to_cpu(dl->disk.refnum),
guid_str(conf->guid),
conf->sec_elmnt_seq, vn);
/* Clear the Transition flag */
if (be16_and
(ddf->phys->entries[dl->pdnum].state,
cpu_to_be16(DDF_Failed)))
be16_clear(ddf->phys
->entries[dl->pdnum].state,
cpu_to_be16(DDF_Transition));
dl->vlist[vn++] = vcl;
vstate = ddf->virt->entries[vcl->vcnum].state
& DDF_state_mask;
if (vstate == DDF_state_degraded ||
vstate == DDF_state_part_optimal)
in_degraded = 1;
}
while (vn < ddf->max_part)
dl->vlist[vn++] = NULL;
if (dl->vlist[0]) {
be16_clear(ddf->phys->entries[dl->pdnum].type,
cpu_to_be16(DDF_Global_Spare));
if (!be16_and(ddf->phys
->entries[dl->pdnum].type,
cpu_to_be16(DDF_Active_in_VD))) {
be16_set(ddf->phys
->entries[dl->pdnum].type,
cpu_to_be16(DDF_Active_in_VD));
if (in_degraded)
be16_set(ddf->phys
->entries[dl->pdnum]
.state,
cpu_to_be16
(DDF_Rebuilding));
}
}
if (dl->spare) {
be16_clear(ddf->phys->entries[dl->pdnum].type,
cpu_to_be16(DDF_Global_Spare));
be16_set(ddf->phys->entries[dl->pdnum].type,
cpu_to_be16(DDF_Spare));
}
if (!dl->vlist[0] && !dl->spare) {
be16_set(ddf->phys->entries[dl->pdnum].type,
cpu_to_be16(DDF_Global_Spare));
be16_clear(ddf->phys->entries[dl->pdnum].type,
cpu_to_be16(DDF_Spare));
be16_clear(ddf->phys->entries[dl->pdnum].type,
cpu_to_be16(DDF_Active_in_VD));
}
}
static void ddf_process_conf_update(struct supertype *st,
struct metadata_update *update)
{
struct ddf_super *ddf = st->sb;
struct vd_config *vc;
struct vcl *vcl;
struct dl *dl;
unsigned int ent;
unsigned int pdnum, len;
vc = (struct vd_config*)update->buf;
len = ddf->conf_rec_len * 512;
if ((unsigned int)update->len != len * vc->sec_elmnt_count) {
pr_err("%s: %s: insufficient data (%d) for %u BVDs\n",
__func__, guid_str(vc->guid), update->len,
vc->sec_elmnt_count);
return;
}
for (vcl = ddf->conflist; vcl ; vcl = vcl->next)
if (memcmp(vcl->conf.guid, vc->guid, DDF_GUID_LEN) == 0)
break;
dprintf("%s: conf update for %s (%s)\n", __func__,
guid_str(vc->guid), (vcl ? "old" : "new"));
if (vcl) {
/* An update, just copy the phys_refnum and lba_offset
* fields
*/
unsigned int i;
unsigned int k;
copy_matching_bvd(ddf, &vcl->conf, update);
for (k = 0; k < be16_to_cpu(vc->prim_elmnt_count); k++)
dprintf("BVD %u has %08x at %llu\n", 0,
be32_to_cpu(vcl->conf.phys_refnum[k]),
be64_to_cpu(LBA_OFFSET(ddf,
&vcl->conf)[k]));
for (i = 1; i < vc->sec_elmnt_count; i++) {
copy_matching_bvd(ddf, vcl->other_bvds[i-1],
update);
for (k = 0; k < be16_to_cpu(
vc->prim_elmnt_count); k++)
dprintf("BVD %u has %08x at %llu\n", i,
be32_to_cpu
(vcl->other_bvds[i-1]->
phys_refnum[k]),
be64_to_cpu
(LBA_OFFSET
(ddf,
vcl->other_bvds[i-1])[k]));
}
} else {
/* A new VD_CONF */
unsigned int i;
if (!update->space)
return;
vcl = update->space;
update->space = NULL;
vcl->next = ddf->conflist;
memcpy(&vcl->conf, vc, len);
ent = find_vde_by_guid(ddf, vc->guid);
if (ent == DDF_NOTFOUND)
return;
vcl->vcnum = ent;
ddf->conflist = vcl;
for (i = 1; i < vc->sec_elmnt_count; i++)
memcpy(vcl->other_bvds[i-1],
update->buf + len * i, len);
}
/* Set DDF_Transition on all Failed devices - to help
* us detect those that are no longer in use
*/
for (pdnum = 0; pdnum < be16_to_cpu(ddf->phys->max_pdes);
pdnum++)
if (be16_and(ddf->phys->entries[pdnum].state,
cpu_to_be16(DDF_Failed)))
be16_set(ddf->phys->entries[pdnum].state,
cpu_to_be16(DDF_Transition));
/* Now make sure vlist is correct for each dl. */
for (dl = ddf->dlist; dl; dl = dl->next)
ddf_update_vlist(ddf, dl);
ddf_remove_failed(ddf);
ddf_set_updates_pending(ddf, vc);
}
static void ddf_process_update(struct supertype *st,
struct metadata_update *update)
{
@ -4604,278 +4895,20 @@ static void ddf_process_update(struct supertype *st,
* and offset. This will also mark the spare as active with
* a spare-assignment record.
*/
struct ddf_super *ddf = st->sb;
be32 *magic = (be32 *)update->buf;
struct phys_disk *pd;
struct virtual_disk *vd;
struct vd_config *vc;
struct vcl *vcl;
struct dl *dl;
unsigned int ent;
unsigned int pdnum, pd2, len;
dprintf("Process update %x\n", be32_to_cpu(*magic));
if (be32_eq(*magic, DDF_PHYS_RECORDS_MAGIC)) {
if (update->len != (sizeof(struct phys_disk) +
if (update->len == (sizeof(struct phys_disk) +
sizeof(struct phys_disk_entry)))
return;
pd = (struct phys_disk*)update->buf;
ent = be16_to_cpu(pd->used_pdes);
if (ent >= be16_to_cpu(ddf->phys->max_pdes))
return;
if (be16_and(pd->entries[0].state, cpu_to_be16(DDF_Missing))) {
struct dl **dlp;
/* removing this disk. */
be16_set(ddf->phys->entries[ent].state,
cpu_to_be16(DDF_Missing));
for (dlp = &ddf->dlist; *dlp; dlp = &(*dlp)->next) {
struct dl *dl = *dlp;
if (dl->pdnum == (signed)ent) {
close(dl->fd);
dl->fd = -1;
/* FIXME this doesn't free
* dl->devname */
update->space = dl;
*dlp = dl->next;
break;
}
}
ddf_set_updates_pending(ddf, NULL);
return;
}
if (!all_ff(ddf->phys->entries[ent].guid))
return;
ddf->phys->entries[ent] = pd->entries[0];
ddf->phys->used_pdes = cpu_to_be16
(1 + be16_to_cpu(ddf->phys->used_pdes));
ddf_set_updates_pending(ddf, NULL);
if (ddf->add_list) {
struct active_array *a;
struct dl *al = ddf->add_list;
ddf->add_list = al->next;
al->next = ddf->dlist;
ddf->dlist = al;
/* As a device has been added, we should check
* for any degraded devices that might make
* use of this spare */
for (a = st->arrays ; a; a=a->next)
a->check_degraded = 1;
}
ddf_process_phys_update(st, update);
} else if (be32_eq(*magic, DDF_VIRT_RECORDS_MAGIC)) {
if (update->len != (sizeof(struct virtual_disk) +
if (update->len == (sizeof(struct virtual_disk) +
sizeof(struct virtual_entry)))
return;
vd = (struct virtual_disk*)update->buf;
if (vd->entries[0].state == DDF_state_deleted) {
if (_kill_subarray_ddf(ddf, vd->entries[0].guid))
return;
} else {
ent = find_vde_by_guid(ddf, vd->entries[0].guid);
if (ent != DDF_NOTFOUND) {
dprintf("%s: VD %s exists already in slot %d\n",
__func__, guid_str(vd->entries[0].guid),
ent);
return;
}
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));
dprintf("%s: added VD %s in slot %d(s=%02x i=%02x)\n",
__func__, guid_str(vd->entries[0].guid), ent,
ddf->virt->entries[ent].state,
ddf->virt->entries[ent].init_state);
}
ddf_set_updates_pending(ddf, NULL);
}
else if (be32_eq(*magic, DDF_VD_CONF_MAGIC)) {
vc = (struct vd_config*)update->buf;
len = ddf->conf_rec_len * 512;
if ((unsigned int)update->len != len * vc->sec_elmnt_count) {
pr_err("%s: %s: insufficient data (%d) for %u BVDs\n",
__func__, guid_str(vc->guid), update->len,
vc->sec_elmnt_count);
return;
}
for (vcl = ddf->conflist; vcl ; vcl = vcl->next)
if (memcmp(vcl->conf.guid, vc->guid, DDF_GUID_LEN) == 0)
break;
dprintf("%s: conf update for %s (%s)\n", __func__,
guid_str(vc->guid), (vcl ? "old" : "new"));
if (vcl) {
/* An update, just copy the phys_refnum and lba_offset
* fields
*/
unsigned int i;
unsigned int k;
copy_matching_bvd(ddf, &vcl->conf, update);
for (k = 0; k < be16_to_cpu(vc->prim_elmnt_count); k++)
dprintf("BVD %u has %08x at %llu\n", 0,
be32_to_cpu(vcl->conf.phys_refnum[k]),
be64_to_cpu(LBA_OFFSET(ddf,
&vcl->conf)[k]));
for (i = 1; i < vc->sec_elmnt_count; i++) {
copy_matching_bvd(ddf, vcl->other_bvds[i-1],
update);
for (k = 0; k < be16_to_cpu(
vc->prim_elmnt_count); k++)
dprintf("BVD %u has %08x at %llu\n", i,
be32_to_cpu
(vcl->other_bvds[i-1]->
phys_refnum[k]),
be64_to_cpu
(LBA_OFFSET
(ddf,
vcl->other_bvds[i-1])[k]));
}
} else {
/* A new VD_CONF */
unsigned int i;
if (!update->space)
return;
vcl = update->space;
update->space = NULL;
vcl->next = ddf->conflist;
memcpy(&vcl->conf, vc, len);
ent = find_vde_by_guid(ddf, vc->guid);
if (ent == DDF_NOTFOUND)
return;
vcl->vcnum = ent;
ddf->conflist = vcl;
for (i = 1; i < vc->sec_elmnt_count; i++)
memcpy(vcl->other_bvds[i-1],
update->buf + len * i, len);
}
/* Set DDF_Transition on all Failed devices - to help
* us detect those that are no longer in use
*/
for (pdnum = 0; pdnum < be16_to_cpu(ddf->phys->max_pdes);
pdnum++)
if (be16_and(ddf->phys->entries[pdnum].state,
cpu_to_be16(DDF_Failed)))
be16_set(ddf->phys->entries[pdnum].state,
cpu_to_be16(DDF_Transition));
/* Now make sure vlist is correct for each dl. */
for (dl = ddf->dlist; dl; dl = dl->next) {
unsigned int vn = 0;
int in_degraded = 0;
if (dl->pdnum < 0)
continue;
for (vcl = ddf->conflist; vcl ; vcl = vcl->next) {
unsigned int dn, ibvd;
const struct vd_config *conf;
int vstate;
dn = get_pd_index_from_refnum(vcl,
dl->disk.refnum,
ddf->mppe,
&conf, &ibvd);
if (dn == DDF_NOTFOUND)
continue;
dprintf("dev %d/%08x has %s (sec=%u) at %d\n",
dl->pdnum,
be32_to_cpu(dl->disk.refnum),
guid_str(conf->guid),
conf->sec_elmnt_seq, vn);
/* Clear the Transition flag */
if (be16_and
(ddf->phys->entries[dl->pdnum].state,
cpu_to_be16(DDF_Failed)))
be16_clear(ddf->phys
->entries[dl->pdnum].state,
cpu_to_be16(DDF_Transition));
dl->vlist[vn++] = vcl;
vstate = ddf->virt->entries[vcl->vcnum].state
& DDF_state_mask;
if (vstate == DDF_state_degraded ||
vstate == DDF_state_part_optimal)
in_degraded = 1;
}
while (vn < ddf->max_part)
dl->vlist[vn++] = NULL;
if (dl->vlist[0]) {
be16_clear(ddf->phys->entries[dl->pdnum].type,
cpu_to_be16(DDF_Global_Spare));
if (!be16_and(ddf->phys
->entries[dl->pdnum].type,
cpu_to_be16(DDF_Active_in_VD))) {
be16_set(ddf->phys
->entries[dl->pdnum].type,
cpu_to_be16(DDF_Active_in_VD));
if (in_degraded)
be16_set(ddf->phys
->entries[dl->pdnum]
.state,
cpu_to_be16
(DDF_Rebuilding));
}
}
if (dl->spare) {
be16_clear(ddf->phys->entries[dl->pdnum].type,
cpu_to_be16(DDF_Global_Spare));
be16_set(ddf->phys->entries[dl->pdnum].type,
cpu_to_be16(DDF_Spare));
}
if (!dl->vlist[0] && !dl->spare) {
be16_set(ddf->phys->entries[dl->pdnum].type,
cpu_to_be16(DDF_Global_Spare));
be16_clear(ddf->phys->entries[dl->pdnum].type,
cpu_to_be16(DDF_Spare));
be16_clear(ddf->phys->entries[dl->pdnum].type,
cpu_to_be16(DDF_Active_in_VD));
}
}
/* Now remove any 'Failed' devices that are not part
* of any VD. They will have the Transition flag set.
* Once done, we need to update all dl->pdnum numbers.
*/
pd2 = 0;
for (pdnum = 0; pdnum < be16_to_cpu(ddf->phys->max_pdes);
pdnum++) {
if (be32_to_cpu(ddf->phys->entries[pdnum].refnum) ==
0xFFFFFFFF)
continue;
if (be16_and(ddf->phys->entries[pdnum].state,
cpu_to_be16(DDF_Failed))
&& be16_and(ddf->phys->entries[pdnum].state,
cpu_to_be16(DDF_Transition))) {
/* skip this one unless in dlist*/
for (dl = ddf->dlist; dl; dl = dl->next)
if (dl->pdnum == (int)pdnum)
break;
if (!dl)
continue;
}
if (pdnum == pd2)
pd2++;
else {
ddf->phys->entries[pd2] =
ddf->phys->entries[pdnum];
for (dl = ddf->dlist; dl; dl = dl->next)
if (dl->pdnum == (int)pdnum)
dl->pdnum = pd2;
pd2++;
}
}
ddf->phys->used_pdes = cpu_to_be16(pd2);
while (pd2 < pdnum) {
memset(ddf->phys->entries[pd2].guid, 0xff,
DDF_GUID_LEN);
pd2++;
}
ddf_set_updates_pending(ddf, vc);
ddf_process_virt_update(st, update);
} else if (be32_eq(*magic, DDF_VD_CONF_MAGIC)) {
ddf_process_conf_update(st, update);
}
/* case DDF_SPARE_ASSIGN_MAGIC */
}