diff options
author | NeilBrown <neilb@suse.de> | 2010-11-25 08:58:27 +0100 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2010-11-25 08:58:27 +0100 |
commit | 5739e0d007a3eea80f5108d73d444751dbbde1ef (patch) | |
tree | b436fa7134053e0edcc0999f5f86f89a9fed1fb8 /Monitor.c | |
parent | Monitor: separate 'choose_spare' out from 'move_spare' (diff) | |
download | mdadm-5739e0d007a3eea80f5108d73d444751dbbde1ef.tar.xz mdadm-5739e0d007a3eea80f5108d73d444751dbbde1ef.zip |
Monitor: choose spare correctly for external metadata.
When metadata is managed externally - probably as a container - we
need to examine that metadata to see which devices are spares.
So use the getinfo_super_disk message and use the info returned.
Signed-off-by: NeilBrown <neilb@suse.de>
Diffstat (limited to '')
-rw-r--r-- | Monitor.c | 63 |
1 files changed, 62 insertions, 1 deletions
@@ -798,6 +798,63 @@ static int choose_spare(struct state *from, struct state *to, return dev; } +static int container_choose_spare(struct state *from, struct state *to, + struct domainlist *domlist) +{ + /* This is similar to choose_spare, but we cannot trust devstate, + * so we need to read the metadata instead + */ + + struct supertype *st = from->metadata; + int fd = open(st->devname, O_RDONLY); + int err; + struct mdinfo *disks, *d; + unsigned long long min_size + = min_spare_size_required(to); + int dev; + + if (fd < 0) + return 0; + if (!st->ss->getinfo_super_disks) + return 0; + + err = st->ss->load_container(st, fd, NULL); + close(fd); + if (err) + return 0; + + disks = st->ss->getinfo_super_disks(st); + st->ss->free_super(st); + + if (!disks) + return 0; + + for (d = disks->devs ; d && !dev ; d = d->next) { + if (d->disk.state == 0) { + struct dev_policy *pol; + unsigned long long dev_size; + dev = makedev(d->disk.major,d->disk.minor); + + if (min_size && + dev_size_from_id(dev, &dev_size) && + dev_size < min_size) + continue; + + pol = devnum_policy(dev); + if (from->spare_group) + pol_add(&pol, pol_domain, + from->spare_group, NULL); + if (!domain_test(domlist, pol, to->metadata->ss->name)) + dev = 0; + + dev_policy_free(pol); + } + } + sysfs_free(disks); + return dev; +} + + static void try_spare_migration(struct state *statelist, struct alert_info *info) { struct state *from; @@ -827,7 +884,11 @@ static void try_spare_migration(struct state *statelist, struct alert_info *info int devid; if (!check_donor(from, to, domlist)) continue; - devid = choose_spare(from, to, domlist); + if (from->metadata->ss->external) + devid = container_choose_spare( + from, to, domlist); + else + devid = choose_spare(from, to, domlist); if (devid > 0 && move_spare(from, to, devid, info)) break; |