}
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 */
* 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);
goto out;
}
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 ||
/* 7/ Is there enough devices to possibly start the array? */
/* 7a/ if not, finish with success. */
if (info.array.level == LEVEL_CONTAINER) {
+ int devnum = devnum; /* defined and used iff ->external */
/* Try to assemble within the container */
map_unlock(&map);
sysfs_uevent(&info, "change");
": container %s now has %d devices\n",
chosen_name, info.array.working_disks);
wait_for(chosen_name, mdfd);
+ if (st->ss->external)
+ devnum = fd2devnum(mdfd);
close(mdfd);
sysfs_free(sra);
rv = Incremental(chosen_name, verbose, runstop,
* 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_by_id(devnum);
return rv;
}
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)
+ 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);
+ -1, 0, NULL);
close(mdfd);
}
if (verbose > 0) {
}
sysfs_free(chosen);
}
- return rv ? 0 : 1;
+ 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))
int trustworthy;
struct mddev_ident *match;
int rv = 0;
+ struct domainlist *domains;
+ struct map_ent *smp;
+ int suuid[4];
+ int sfd;
- memset(&info, 0, sizeof(info));
st->ss->getinfo_super(st, &info, NULL);
if ((runstop > 0 && info.container_enough >= 0) ||
if (map_lock(&map))
fprintf(stderr, Name ": failed to get exclusive lock on "
"mapfile\n");
+ /* do not assemble arrays that might have bad blocks */
+ if (list->array.state & (1<<MD_SB_BBM_ERRORS)) {
+ fprintf(stderr, Name ": BBM log found in metadata. "
+ "Cannot activate array(s).\n");
+ /* free container data and exit */
+ sysfs_free(list);
+ return 2;
+ }
for (ra = list ; ra ; ra = ra->next) {
int mdfd;
}
assemble_container_content(st, mdfd, ra, runstop,
- chosen_name, verbose);
+ chosen_name, verbose, NULL);
+ close(mdfd);
+ }
+
+ /* 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);
map_unlock(&map);
return 0;
}
int subfd = open_dev(memb->devnum);
if (subfd >= 0) {
Manage_subdevs(memb->dev, subfd,
- &devlist, verbose, 0);
+ &devlist, verbose, 0,
+ NULL);
close(subfd);
}
}
free_mdstat(mdstat);
} else
- Manage_subdevs(ent->dev, mdfd, &devlist, verbose, 0);
+ Manage_subdevs(ent->dev, mdfd, &devlist, verbose, 0, NULL);
devlist.disposition = 'r';
- rv = Manage_subdevs(ent->dev, mdfd, &devlist, verbose, 0);
+ rv = Manage_subdevs(ent->dev, mdfd, &devlist, verbose, 0, NULL);
close(mdfd);
free_mdstat(ent);
return rv;