static int Incremental_container(struct supertype *st, char *devname,
char *homehost,
- int verbose, int runstop, int autof);
-
-static struct mddev_ident *search_mdstat(struct supertype *st,
- struct mdinfo *info,
- char *devname,
- int verbose, int *rvp);
+ int verbose, int runstop, int autof,
+ int freeze_reshape);
int Incremental(char *devname, int verbose, int runstop,
struct supertype *st, char *homehost, int require_homehost,
- int autof)
+ int autof, int freeze_reshape)
{
/* Add this device to an array, creating the array if necessary
* and starting the array if sensible or - if runstop>0 - if possible.
rv = st->ss->load_container(st, dfd, NULL);
close(dfd);
- if (!rv && st->ss->container_content)
- return Incremental_container(st, devname, homehost,
- verbose, runstop, autof);
+ if (!rv && st->ss->container_content) {
+ if (map_lock(&map))
+ fprintf(stderr, Name ": failed to get "
+ "exclusive lock on mapfile\n");
+ rv = Incremental_container(st, devname, homehost,
+ verbose, runstop, autof,
+ freeze_reshape);
+ map_unlock(&map);
+ return rv;
+ }
fprintf(stderr, Name ": %s is not part of an md array.\n",
devname);
}
close (dfd); dfd = -1;
- memset(&info, 0, sizeof(info));
st->ss->getinfo_super(st, &info, NULL);
/* 3/ Check if there is a match in mdadm.conf */
- match = search_mdstat(st, &info, devname, verbose, &rv);
+ match = conf_match(st, &info, devname, verbose, &rv);
if (!match && rv == 2)
goto out;
name_to_use, autof, trustworthy, chosen_name);
if (mdfd < 0)
- goto out;
+ goto out_unlock;
sysfs_init(&info, mdfd, 0);
fprintf(stderr, Name ": failed to set array info for %s: %s\n",
chosen_name, strerror(errno));
rv = 2;
- goto out;
+ goto out_unlock;
}
dinfo = info;
devname, chosen_name, strerror(errno));
ioctl(mdfd, STOP_ARRAY, 0);
rv = 2;
- goto out;
+ goto out_unlock;
}
sra = sysfs_read(mdfd, -1, (GET_DEVS | GET_STATE |
GET_OFFSET | GET_SIZE));
" --incremental reliably. Aborting.\n");
sysfs_free(sra);
rv = 2;
- goto out;
+ goto out_unlock;
}
info.array.working_disks = 1;
/* 6/ Make sure /var/run/mdadm.map contains this array. */
* array was possibly started early and our best bet is
* to add this anyway.
* Also if action policy is re-add or better we allow
- * re-add
+ * re-add.
+ * This doesn't apply to containers as the 'non-spare'
+ * flag has a different meaning. The test has to happen
+ * at the device level there
*/
- if ((info.disk.state & (1<<MD_DISK_SYNC)) != 0
+ if (!st->ss->external
+ && (info.disk.state & (1<<MD_DISK_SYNC)) != 0
&& ! policy_action_allows(policy, st->ss->name,
act_re_add)
&& runstop < 1) {
- int active = 0;
-
- if (st->ss->external) {
- char *devname = devnum2devname(fd2devnum(mdfd));
-
- active = devname && is_container_active(devname);
- free(devname);
- } else if (ioctl(mdfd, GET_ARRAY_INFO, &ainf) == 0)
- active = 1;
- if (active) {
+ if (ioctl(mdfd, GET_ARRAY_INFO, &ainf) == 0) {
fprintf(stderr, Name
": not adding %s to active array (without --run) %s\n",
devname, chosen_name);
rv = 2;
- goto out;
+ goto out_unlock;
}
}
if (!sra) {
rv = 2;
- goto out;
+ goto out_unlock;
}
if (sra->devs) {
sprintf(dn, "%d:%d", sra->devs->disk.major,
sra->devs->disk.minor);
dfd2 = dev_open(dn, O_RDONLY);
+ if (dfd2 < 0) {
+ fprintf(stderr, Name
+ ": unable to open %s\n", devname);
+ rv = 2;
+ goto out_unlock;
+ }
st2 = dup_super(st);
if (st2->ss->load_super(st2, dfd2, NULL) ||
st->ss->compare_super(st, st2) != 0) {
devname, chosen_name);
close(dfd2);
rv = 2;
- goto out;
+ goto out_unlock;
}
close(dfd2);
- memset(&info2, 0, sizeof(info2));
st2->ss->getinfo_super(st2, &info2, NULL);
st2->ss->free_super(st2);
if (info.array.level != info2.array.level ||
": unexpected difference between %s and %s.\n",
chosen_name, devname);
rv = 2;
- goto out;
+ goto out_unlock;
}
}
info2.disk.major = major(stb.st_rdev);
fprintf(stderr, Name ": failed to add %s to %s: %s.\n",
devname, chosen_name, strerror(errno));
rv = 2;
- goto out;
+ goto out_unlock;
}
info.array.working_disks = 0;
for (d = sra->devs; d; d=d->next)
/* 7/ Is there enough devices to possibly start the array? */
/* 7a/ if not, finish with success. */
if (info.array.level == LEVEL_CONTAINER) {
- char *devname = NULL;
+ int devnum = devnum; /* defined and used iff ->external */
/* Try to assemble within the container */
- map_unlock(&map);
- sysfs_uevent(&info, "change");
if (verbose >= 0)
fprintf(stderr, Name
": container %s now has %d devices\n",
chosen_name, info.array.working_disks);
wait_for(chosen_name, mdfd);
if (st->ss->external)
- devname = devnum2devname(fd2devnum(mdfd));
+ devnum = fd2devnum(mdfd);
+ if (st->ss->load_container)
+ rv = st->ss->load_container(st, mdfd, NULL);
close(mdfd);
sysfs_free(sra);
- rv = Incremental(chosen_name, verbose, runstop,
- NULL, homehost, require_homehost, autof);
+ if (!rv)
+ rv = Incremental_container(st, chosen_name, homehost,
+ verbose, runstop, autof,
+ freeze_reshape);
+ map_unlock(&map);
if (rv == 1)
/* Don't fail the whole -I if a subarray didn't
* have enough devices to start yet
rv = 0;
/* after spare is added, ping monitor for external metadata
* so that it can eg. try to rebuild degraded array */
- if (st->ss->external) {
- ping_monitor(devname);
- free(devname);
- }
+ if (st->ss->external)
+ ping_monitor_by_id(devnum);
return rv;
}
fprintf(stderr, Name
": %s attached to %s, not enough to start (%d).\n",
devname, chosen_name, active_disks);
- map_unlock(&map);
rv = 0;
- goto out;
+ goto out_unlock;
}
/* 7b/ if yes, */
fprintf(stderr, Name
": %s attached to %s which is already active.\n",
devname, chosen_name);
- map_unlock(&map);
rv = 0;
- goto out;
+ goto out_unlock;
}
map_unlock(&map);
if (sra)
sysfs_free(sra);
return rv;
-}
-
-static struct mddev_ident *search_mdstat(struct supertype *st,
- struct mdinfo *info,
- char *devname,
- int verbose, int *rvp)
-{
- struct mddev_ident *array_list, *match;
- array_list = conf_get_ident(NULL);
- match = NULL;
- for (; array_list; array_list = array_list->next) {
- if (array_list->uuid_set &&
- same_uuid(array_list->uuid, info->uuid, st->ss->swapuuid)
- == 0) {
- if (verbose >= 2 && array_list->devname)
- fprintf(stderr, Name
- ": UUID differs from %s.\n",
- array_list->devname);
- continue;
- }
- if (array_list->name[0] &&
- strcasecmp(array_list->name, info->name) != 0) {
- if (verbose >= 2 && array_list->devname)
- fprintf(stderr, Name
- ": Name differs from %s.\n",
- array_list->devname);
- continue;
- }
- if (array_list->devices &&
- !match_oneof(array_list->devices, devname)) {
- if (verbose >= 2 && array_list->devname)
- fprintf(stderr, Name
- ": Not a listed device for %s.\n",
- array_list->devname);
- continue;
- }
- if (array_list->super_minor != UnSet &&
- array_list->super_minor != info->array.md_minor) {
- if (verbose >= 2 && array_list->devname)
- fprintf(stderr, Name
- ": Different super-minor to %s.\n",
- array_list->devname);
- continue;
- }
- if (!array_list->uuid_set &&
- !array_list->name[0] &&
- !array_list->devices &&
- array_list->super_minor == UnSet) {
- if (verbose >= 2 && array_list->devname)
- fprintf(stderr, Name
- ": %s doesn't have any identifying information.\n",
- array_list->devname);
- continue;
- }
- /* FIXME, should I check raid_disks and level too?? */
-
- if (match) {
- if (verbose >= 0) {
- if (match->devname && array_list->devname)
- fprintf(stderr, Name
- ": we match both %s and %s - cannot decide which to use.\n",
- match->devname, array_list->devname);
- else
- fprintf(stderr, Name
- ": multiple lines in mdadm.conf match\n");
- }
- *rvp = 2;
- match = NULL;
- break;
- }
- match = array_list;
- }
- return match;
+out_unlock:
+ map_unlock(&map);
+ goto out;
}
static void find_reject(int mdfd, struct supertype *st, struct mdinfo *sra,
int cnt = 0;
__u64 max_events = 0;
char *avail = NULL;
- int *best;
+ int *best = NULL;
char *devmap = NULL;
int numdevs = 0;
int devnum;
best[info.disk.raid_disk] = devnum;
st->ss->getinfo_super(st, bestinfo, NULL);
} else { /* info.events much bigger */
- memset(avail, 0, info.disk.raid_disk);
+ memset(avail, 0, raid_disks);
max_events = info.events;
avail[info.disk.raid_disk] = 2;
+ best[info.disk.raid_disk] = devnum;
st->ss->getinfo_super(st, bestinfo, NULL);
}
}
return cnt;
}
+/* test if container has degraded member(s) */
+static int container_members_max_degradation(struct map_ent *map, struct map_ent *me)
+{
+ mdu_array_info_t array;
+ int afd;
+ int max_degraded = 0;
+
+ for(; map; map = map->next) {
+ if (!is_subarray(map->metadata) ||
+ devname2devnum(map->metadata+1) != me->devnum)
+ continue;
+ afd = open_dev(map->devnum);
+ if (afd < 0)
+ continue;
+ /* most accurate information regarding array degradation */
+ if (ioctl(afd, GET_ARRAY_INFO, &array) >= 0) {
+ int degraded = array.raid_disks - array.active_disks -
+ array.spare_disks;
+ if (degraded > max_degraded)
+ max_degraded = degraded;
+ }
+ close(afd);
+ }
+ return (max_degraded);
+}
+
static int array_try_spare(char *devname, int *dfdp, struct dev_policy *pol,
struct map_ent *target, int bare,
struct supertype *st, int verbose)
* If st is set, then only arrays of that type are considered
* Return 0 on success, or some exit code on failure, probably 1.
*/
- int rv = -1;
+ int rv = 1;
struct stat stb;
struct map_ent *mp, *map = NULL;
struct mdinfo *chosen = NULL;
struct domainlist *dl = NULL;
struct mdinfo *sra;
unsigned long long devsize;
+ unsigned long long component_size = 0;
if (is_subarray(mp->metadata))
continue;
GET_DEVS|GET_OFFSET|GET_SIZE|GET_STATE|
GET_COMPONENT|GET_VERSION);
if (sra)
- sra->array.failed_disks = 0;
+ sra->array.failed_disks = -1;
}
if (!sra)
continue;
goto next;
} else
st2 = st;
+ /* update number of failed disks for mostly degraded
+ * container member */
+ if (sra->array.failed_disks == -1)
+ sra->array.failed_disks = container_members_max_degradation(map, mp);
+
get_dev_size(dfd, NULL, &devsize);
- if (st2->ss->avail_size(st2, devsize) < sra->component_size) {
- if (verbose > 1)
- fprintf(stderr, Name ": not adding %s to %s as it is too small\n",
- devname, mp->path);
- goto next;
+ if (sra->component_size == 0) {
+ /* true for containers, here we must read superblock
+ * to obtain minimum spare size */
+ struct supertype *st3 = dup_super(st2);
+ int mdfd = open_dev(mp->devnum);
+ if (mdfd < 0) {
+ free(st3);
+ goto next;
+ }
+ if (st3->ss->load_container &&
+ !st3->ss->load_container(st3, mdfd, mp->path)) {
+ component_size = st3->ss->min_acceptable_spare_size(st3);
+ st3->ss->free_super(st3);
+ }
+ free(st3);
+ close(mdfd);
}
- dl = domain_from_array(sra, st2->ss->name);
- if (!domain_test(dl, pol, st2->ss->name)) {
- /* domain test fails */
+ if ((sra->component_size > 0 &&
+ st2->ss->avail_size(st2, devsize) < sra->component_size)
+ ||
+ (sra->component_size == 0 && devsize < component_size)) {
if (verbose > 1)
- fprintf(stderr, Name ": not adding %s to %s as it is not in a compatible domain\n",
+ fprintf(stderr, Name ": not adding %s to %s as it is too small\n",
devname, mp->path);
-
goto next;
}
/* test against target.
* arrays/containers that match 'target'.
* If 'target' is set and 'bare' is true, we prefer the
* array which matches 'target'.
+ * target is considered only if we deal with degraded array
*/
- if (target) {
+ if (target && policy_action_allows(pol, st2->ss->name,
+ act_spare_same_slot)) {
if (strcmp(target->metadata, mp->metadata) == 0 &&
memcmp(target->uuid, mp->uuid,
- sizeof(target->uuid)) == 0) {
+ sizeof(target->uuid)) == 0 &&
+ sra->array.failed_disks > 0) {
/* This is our target!! */
if (chosen)
sysfs_free(chosen);
goto next;
}
+ dl = domain_from_array(sra, st2->ss->name);
+ if (domain_test(dl, pol, st2->ss->name) != 1) {
+ /* domain test fails */
+ if (verbose > 1)
+ fprintf(stderr, Name ": not adding %s to %s as"
+ " it is not in a compatible domain\n",
+ devname, mp->path);
+
+ goto next;
+ }
/* all tests passed, OK to add to this array */
if (!chosen) {
chosen = sra;
close(dfd);
*dfdp = -1;
rv = Manage_subdevs(chosen->sys_name, mdfd, &devlist,
- -1, 0, NULL);
+ -1, 0, NULL, 0);
close(mdfd);
}
if (verbose > 0) {
}
sysfs_free(chosen);
}
- return rv ? 0 : 1;
+ map_unlock(&map);
+ return rv;
}
static int partition_try_spare(char *devname, int *dfdp, struct dev_policy *pol,
DIR *dir;
struct dirent *de;
char *chosen = NULL;
- unsigned long long chosen_size;
+ unsigned long long chosen_size = 0;
struct supertype *chosen_st = NULL;
int fd;
pol2 = path_policy(de->d_name, type_disk);
domain_merge(&domlist, pol2, st ? st->ss->name : NULL);
- if (domain_test(domlist, pol, st ? st->ss->name : NULL) == 0)
+ if (domain_test(domlist, pol, st ? st->ss->name : NULL) != 1)
/* new device is incompatible with this device. */
goto next;
domain_free(domlist);
domlist = NULL;
- asprintf(&devname, "/dev/disk/by-path/%s", de->d_name);
+ if (asprintf(&devname, "/dev/disk/by-path/%s", de->d_name) != 1) {
+ devname = NULL;
+ goto next;
+ }
fd = open(devname, O_RDONLY);
if (fd < 0)
goto next;
if (!st) {
/* Check domain policy again, this time referring to metadata */
domain_merge(&domlist, pol2, st2->ss->name);
- if (domain_test(domlist, pol, st2->ss->name) == 0)
+ if (domain_test(domlist, pol, st2->ss->name) != 1)
/* Incompatible devices for this metadata type */
goto next;
if (!policy_action_allows(pol, st2->ss->name, act_spare))
close(fd);
}
+ closedir(dir);
+
if (!chosen)
return 1;
strerror(errno));
rv = 1;
}
+ sysfs_free(sra);
}
}
return rv;
static int Incremental_container(struct supertype *st, char *devname,
char *homehost, int verbose,
- int runstop, int autof)
+ int runstop, int autof, int freeze_reshape)
{
/* Collect the contents of this container and for each
* array, choose a device name and assemble the array.
int trustworthy;
struct mddev_ident *match;
int rv = 0;
+ struct domainlist *domains;
+ struct map_ent *smp;
+ int suuid[4];
+ int sfd;
+ int ra_blocked = 0;
+ int ra_all = 0;
- memset(&info, 0, sizeof(info));
st->ss->getinfo_super(st, &info, NULL);
if ((runstop > 0 && info.container_enough >= 0) ||
return 0;
}
- match = search_mdstat(st, &info, devname, verbose, &rv);
+ match = conf_match(st, &info, devname, verbose, &rv);
if (match == NULL && rv == 2)
return rv;
trustworthy = FOREIGN;
list = st->ss->container_content(st, NULL);
- if (map_lock(&map))
- fprintf(stderr, Name ": failed to get exclusive lock on "
- "mapfile\n");
-
+ /* when nothing to activate - quit */
+ if (list == NULL)
+ return 0;
for (ra = list ; ra ; ra = ra->next) {
int mdfd;
char chosen_name[1024];
struct map_ent *mp;
struct mddev_ident *match = NULL;
+ ra_all++;
+ /* do not activate arrays blocked by metadata handler */
+ if (ra->array.state & (1 << MD_SB_BLOCK_VOLUME)) {
+ fprintf(stderr, Name ": Cannot activate array %s in %s.\n",
+ ra->text_version, devname);
+ ra_blocked++;
+ continue;
+ }
mp = map_by_uuid(&map, ra->uuid);
if (mp) {
}
assemble_container_content(st, mdfd, ra, runstop,
- chosen_name, verbose);
+ chosen_name, verbose, NULL,
+ freeze_reshape);
+ close(mdfd);
}
- map_unlock(&map);
+
+ /* don't move spares to container with volume being activated
+ when all volumes are blocked */
+ if (ra_all == ra_blocked)
+ return 0;
+
+ /* Now move all suitable spares from spare container */
+ domains = domain_from_array(list, st->ss->name);
+ memcpy(suuid, uuid_zero, sizeof(int[4]));
+ if (domains &&
+ (smp = map_by_uuid(&map, suuid)) != NULL &&
+ (sfd = open(smp->path, O_RDONLY)) >= 0) {
+ /* spare container found */
+ struct supertype *sst =
+ super_imsm.match_metadata_desc("imsm");
+ struct mdinfo *sinfo;
+ unsigned long long min_size = 0;
+ if (st->ss->min_acceptable_spare_size)
+ min_size = st->ss->min_acceptable_spare_size(st);
+ if (!sst->ss->load_container(sst, sfd, NULL)) {
+ close(sfd);
+ sinfo = container_choose_spares(sst, min_size,
+ domains, NULL,
+ st->ss->name, 0);
+ sst->ss->free_super(sst);
+ if (sinfo){
+ int count = 0;
+ struct mdinfo *disks = sinfo->devs;
+ while (disks) {
+ /* move spare from spare
+ * container to currently
+ * assembled one
+ */
+ if (move_spare(
+ smp->path,
+ devname,
+ makedev(disks->disk.major,
+ disks->disk.minor)))
+ count++;
+ disks = disks->next;
+ }
+ if (count)
+ fprintf(stderr, Name
+ ": Added %d spare%s to %s\n",
+ count, count>1?"s":"", devname);
+ }
+ sysfs_free(sinfo);
+ } else
+ close(sfd);
+ }
+ domain_free(domains);
return 0;
}
if (subfd >= 0) {
Manage_subdevs(memb->dev, subfd,
&devlist, verbose, 0,
- NULL);
+ NULL, 0);
close(subfd);
}
}
free_mdstat(mdstat);
} else
- Manage_subdevs(ent->dev, mdfd, &devlist, verbose, 0, NULL);
+ Manage_subdevs(ent->dev, mdfd, &devlist, verbose, 0, NULL, 0);
devlist.disposition = 'r';
- rv = Manage_subdevs(ent->dev, mdfd, &devlist, verbose, 0, NULL);
+ rv = Manage_subdevs(ent->dev, mdfd, &devlist, verbose, 0, NULL, 0);
close(mdfd);
free_mdstat(ent);
return rv;