498 lines
13 KiB
Diff
498 lines
13 KiB
Diff
|
From ca9de185a3b96720adcc5120f6a34c5f9bb98b3f Mon Sep 17 00:00:00 2001
|
||
|
From: "Labun, Marcin" <Marcin.Labun@intel.com>
|
||
|
Date: Mon, 30 Jan 2012 12:00:10 +1100
|
||
|
Subject: [PATCH 3/3] imsm: validate the number of imsm volumes per controller
|
||
|
|
||
|
IMSM OROM limits number of volumes per controller. Volumes
|
||
|
above the limit are blocked in OROM. mdadm should follow OROM limitations
|
||
|
in this area. Therefore we need to count number of volumes on the devices
|
||
|
attached to SATA (ahci driver) or SAS (isci) controller. Adding a new volume
|
||
|
must be blocked if the number of volumes on devices attached to the given
|
||
|
controller is exceeded.
|
||
|
|
||
|
Signed-off-by: Marcin Labun <marcin.labun@intel.com>
|
||
|
Signed-off-by: NeilBrown <neilb@suse.de>
|
||
|
---
|
||
|
super-intel.c | 404 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
|
||
|
1 files changed, 399 insertions(+), 5 deletions(-)
|
||
|
|
||
|
diff --git a/super-intel.c b/super-intel.c
|
||
|
index 1cf8716..7db5177 100644
|
||
|
--- a/super-intel.c
|
||
|
+++ b/super-intel.c
|
||
|
@@ -3285,7 +3285,7 @@ static int parse_raid_devices(struct intel_super *super)
|
||
|
len_migr = sizeof_imsm_dev(dev_iter, 1);
|
||
|
if (len_migr > len)
|
||
|
space_needed += len_migr - len;
|
||
|
-
|
||
|
+
|
||
|
dv = malloc(sizeof(*dv));
|
||
|
if (!dv)
|
||
|
return 1;
|
||
|
@@ -3321,7 +3321,7 @@ static int parse_raid_devices(struct intel_super *super)
|
||
|
super->buf = buf;
|
||
|
super->len = len;
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
@@ -4122,12 +4122,12 @@ get_devlist_super_block(struct md_list *devlist, struct intel_super **super_list
|
||
|
struct md_list *tmpdev;
|
||
|
int err = 0;
|
||
|
int i = 0;
|
||
|
- int lmax = 0;
|
||
|
|
||
|
for (i = 0, tmpdev = devlist; tmpdev; tmpdev = tmpdev->next) {
|
||
|
if (tmpdev->used != 1)
|
||
|
continue;
|
||
|
if (tmpdev->container == 1) {
|
||
|
+ int lmax = 0;
|
||
|
int fd = dev_open(tmpdev->devname, O_RDONLY|O_EXCL);
|
||
|
if (fd < 0) {
|
||
|
fprintf(stderr, Name ": cannot open device %s: %s\n",
|
||
|
@@ -5363,6 +5363,377 @@ static int is_raid_level_supported(const struct imsm_orom *orom, int level, int
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+
|
||
|
+static int
|
||
|
+active_arrays_by_format(char *name, char* hba, struct md_list **devlist,
|
||
|
+ int dpa, int verbose)
|
||
|
+{
|
||
|
+ struct mdstat_ent *mdstat = mdstat_read(0, 0);
|
||
|
+ struct mdstat_ent *memb = NULL;
|
||
|
+ int count = 0;
|
||
|
+ int num = 0;
|
||
|
+ struct md_list *dv = NULL;
|
||
|
+ int found;
|
||
|
+
|
||
|
+ for (memb = mdstat ; memb ; memb = memb->next) {
|
||
|
+ if (memb->metadata_version &&
|
||
|
+ (strncmp(memb->metadata_version, "external:", 9) == 0) &&
|
||
|
+ (strcmp(&memb->metadata_version[9], name) == 0) &&
|
||
|
+ !is_subarray(memb->metadata_version+9) &&
|
||
|
+ memb->members) {
|
||
|
+ struct dev_member *dev = memb->members;
|
||
|
+ int fd = -1;
|
||
|
+ while(dev && (fd < 0)) {
|
||
|
+ char *path = malloc(strlen(dev->name) + strlen("/dev/") + 1);
|
||
|
+ if (path) {
|
||
|
+ num = sprintf(path, "%s%s", "/dev/", dev->name);
|
||
|
+ if (num > 0)
|
||
|
+ fd = open(path, O_RDONLY, 0);
|
||
|
+ if ((num <= 0) || (fd < 0)) {
|
||
|
+ pr_vrb(": Cannot open %s: %s\n",
|
||
|
+ dev->name, strerror(errno));
|
||
|
+ }
|
||
|
+ free(path);
|
||
|
+ }
|
||
|
+ dev = dev->next;
|
||
|
+ }
|
||
|
+ found = 0;
|
||
|
+ if ((fd >= 0) && disk_attached_to_hba(fd, hba)) {
|
||
|
+ struct mdstat_ent *vol;
|
||
|
+ for (vol = mdstat ; vol ; vol = vol->next) {
|
||
|
+ if ((vol->active > 0) &&
|
||
|
+ vol->metadata_version &&
|
||
|
+ is_container_member(vol, memb->dev)) {
|
||
|
+ found++;
|
||
|
+ count++;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (*devlist && (found < dpa)) {
|
||
|
+ dv = calloc(1, sizeof(*dv));
|
||
|
+ if (dv == NULL)
|
||
|
+ fprintf(stderr, Name ": calloc failed\n");
|
||
|
+ else {
|
||
|
+ dv->devname = malloc(strlen(memb->dev) + strlen("/dev/") + 1);
|
||
|
+ if (dv->devname != NULL) {
|
||
|
+ sprintf(dv->devname, "%s%s", "/dev/", memb->dev);
|
||
|
+ dv->found = found;
|
||
|
+ dv->used = 0;
|
||
|
+ dv->next = *devlist;
|
||
|
+ *devlist = dv;
|
||
|
+ } else
|
||
|
+ free(dv);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (fd >= 0)
|
||
|
+ close(fd);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ free_mdstat(mdstat);
|
||
|
+ return count;
|
||
|
+}
|
||
|
+
|
||
|
+#ifdef DEBUG_LOOP
|
||
|
+static struct md_list*
|
||
|
+get_loop_devices(void)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+ struct md_list *devlist = NULL;
|
||
|
+ struct md_list *dv = NULL;
|
||
|
+
|
||
|
+ for(i = 0; i < 12; i++) {
|
||
|
+ dv = calloc(1, sizeof(*dv));
|
||
|
+ if (dv == NULL) {
|
||
|
+ fprintf(stderr, Name ": calloc failed\n");
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ dv->devname = malloc(40);
|
||
|
+ if (dv->devname == NULL) {
|
||
|
+ fprintf(stderr, Name ": malloc failed\n");
|
||
|
+ free(dv);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ sprintf(dv->devname, "/dev/loop%d", i);
|
||
|
+ dv->next = devlist;
|
||
|
+ devlist = dv;
|
||
|
+ }
|
||
|
+ return devlist;
|
||
|
+}
|
||
|
+#endif
|
||
|
+
|
||
|
+static struct md_list*
|
||
|
+get_devices(const char *hba_path)
|
||
|
+{
|
||
|
+ struct md_list *devlist = NULL;
|
||
|
+ struct md_list *dv = NULL;
|
||
|
+ struct dirent *ent;
|
||
|
+ DIR *dir;
|
||
|
+ int err = 0;
|
||
|
+
|
||
|
+#if DEBUG_LOOP
|
||
|
+ devlist = get_loop_devices();
|
||
|
+ return devlist;
|
||
|
+#endif
|
||
|
+ /* scroll through /sys/dev/block looking for devices attached to
|
||
|
+ * this hba
|
||
|
+ */
|
||
|
+ dir = opendir("/sys/dev/block");
|
||
|
+ for (ent = dir ? readdir(dir) : NULL; ent; ent = readdir(dir)) {
|
||
|
+ int fd;
|
||
|
+ char buf[1024];
|
||
|
+ int major, minor;
|
||
|
+ char *path = NULL;
|
||
|
+ if (sscanf(ent->d_name, "%d:%d", &major, &minor) != 2)
|
||
|
+ continue;
|
||
|
+ path = devt_to_devpath(makedev(major, minor));
|
||
|
+ if (!path)
|
||
|
+ continue;
|
||
|
+ if (!path_attached_to_hba(path, hba_path)) {
|
||
|
+ free(path);
|
||
|
+ path = NULL;
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+ free(path);
|
||
|
+ path = NULL;
|
||
|
+ fd = dev_open(ent->d_name, O_RDONLY);
|
||
|
+ if (fd >= 0) {
|
||
|
+ fd2devname(fd, buf);
|
||
|
+ close(fd);
|
||
|
+ } else {
|
||
|
+ fprintf(stderr, Name ": cannot open device: %s\n",
|
||
|
+ ent->d_name);
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+
|
||
|
+
|
||
|
+ dv = calloc(1, sizeof(*dv));
|
||
|
+ if (dv == NULL) {
|
||
|
+ fprintf(stderr, Name ": malloc failed\n");
|
||
|
+ err = 1;
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ dv->devname = strdup(buf);
|
||
|
+ if (dv->devname == NULL) {
|
||
|
+ fprintf(stderr, Name ": malloc failed\n");
|
||
|
+ err = 1;
|
||
|
+ free(dv);
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ dv->next = devlist;
|
||
|
+ devlist = dv;
|
||
|
+ }
|
||
|
+ if (err) {
|
||
|
+ while(devlist) {
|
||
|
+ dv = devlist;
|
||
|
+ devlist = devlist->next;
|
||
|
+ free(dv->devname);
|
||
|
+ free(dv);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ return devlist;
|
||
|
+}
|
||
|
+
|
||
|
+static int
|
||
|
+count_volumes_list(struct md_list *devlist, char *homehost,
|
||
|
+ int verbose, int *found)
|
||
|
+{
|
||
|
+ struct md_list *tmpdev;
|
||
|
+ int count = 0;
|
||
|
+ struct supertype *st = NULL;
|
||
|
+
|
||
|
+ /* first walk the list of devices to find a consistent set
|
||
|
+ * that match the criterea, if that is possible.
|
||
|
+ * We flag the ones we like with 'used'.
|
||
|
+ */
|
||
|
+ *found = 0;
|
||
|
+ st = match_metadata_desc_imsm("imsm");
|
||
|
+ if (st == NULL) {
|
||
|
+ pr_vrb(": cannot allocate memory for imsm supertype\n");
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+
|
||
|
+ for (tmpdev = devlist; tmpdev; tmpdev = tmpdev->next) {
|
||
|
+ char *devname = tmpdev->devname;
|
||
|
+ struct stat stb;
|
||
|
+ struct supertype *tst;
|
||
|
+ int dfd;
|
||
|
+ if (tmpdev->used > 1)
|
||
|
+ continue;
|
||
|
+ tst = dup_super(st);
|
||
|
+ if (tst == NULL) {
|
||
|
+ pr_vrb(": cannot allocate memory for imsm supertype\n");
|
||
|
+ goto err_1;
|
||
|
+ }
|
||
|
+ tmpdev->container = 0;
|
||
|
+ dfd = dev_open(devname, O_RDONLY|O_EXCL);
|
||
|
+ if (dfd < 0) {
|
||
|
+ dprintf(": cannot open device %s: %s\n",
|
||
|
+ devname, strerror(errno));
|
||
|
+ tmpdev->used = 2;
|
||
|
+ } else if (fstat(dfd, &stb)< 0) {
|
||
|
+ /* Impossible! */
|
||
|
+ dprintf(": fstat failed for %s: %s\n",
|
||
|
+ devname, strerror(errno));
|
||
|
+ tmpdev->used = 2;
|
||
|
+ } else if ((stb.st_mode & S_IFMT) != S_IFBLK) {
|
||
|
+ dprintf(": %s is not a block device.\n",
|
||
|
+ devname);
|
||
|
+ tmpdev->used = 2;
|
||
|
+ } else if (must_be_container(dfd)) {
|
||
|
+ struct supertype *cst;
|
||
|
+ cst = super_by_fd(dfd, NULL);
|
||
|
+ if (cst == NULL) {
|
||
|
+ dprintf(": cannot recognize container type %s\n",
|
||
|
+ devname);
|
||
|
+ tmpdev->used = 2;
|
||
|
+ } else if (tst->ss != st->ss) {
|
||
|
+ dprintf(": non-imsm container - ignore it: %s\n",
|
||
|
+ devname);
|
||
|
+ tmpdev->used = 2;
|
||
|
+ } else if (!tst->ss->load_container ||
|
||
|
+ tst->ss->load_container(tst, dfd, NULL))
|
||
|
+ tmpdev->used = 2;
|
||
|
+ else {
|
||
|
+ tmpdev->container = 1;
|
||
|
+ }
|
||
|
+ if (cst)
|
||
|
+ cst->ss->free_super(cst);
|
||
|
+ } else {
|
||
|
+ tmpdev->st_rdev = stb.st_rdev;
|
||
|
+ if (tst->ss->load_super(tst,dfd, NULL)) {
|
||
|
+ dprintf(": no RAID superblock on %s\n",
|
||
|
+ devname);
|
||
|
+ tmpdev->used = 2;
|
||
|
+ } else if (tst->ss->compare_super == NULL) {
|
||
|
+ dprintf(": Cannot assemble %s metadata on %s\n",
|
||
|
+ tst->ss->name, devname);
|
||
|
+ tmpdev->used = 2;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (dfd >= 0)
|
||
|
+ close(dfd);
|
||
|
+ if (tmpdev->used == 2 || tmpdev->used == 4) {
|
||
|
+ /* Ignore unrecognised devices during auto-assembly */
|
||
|
+ goto loop;
|
||
|
+ }
|
||
|
+ else {
|
||
|
+ struct mdinfo info;
|
||
|
+ tst->ss->getinfo_super(tst, &info, NULL);
|
||
|
+
|
||
|
+ if (st->minor_version == -1)
|
||
|
+ st->minor_version = tst->minor_version;
|
||
|
+
|
||
|
+ if (memcmp(info.uuid, uuid_zero,
|
||
|
+ sizeof(int[4])) == 0) {
|
||
|
+ /* this is a floating spare. It cannot define
|
||
|
+ * an array unless there are no more arrays of
|
||
|
+ * this type to be found. It can be included
|
||
|
+ * in an array of this type though.
|
||
|
+ */
|
||
|
+ tmpdev->used = 3;
|
||
|
+ goto loop;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (st->ss != tst->ss ||
|
||
|
+ st->minor_version != tst->minor_version ||
|
||
|
+ st->ss->compare_super(st, tst) != 0) {
|
||
|
+ /* Some mismatch. If exactly one array matches this host,
|
||
|
+ * we can resolve on that one.
|
||
|
+ * Or, if we are auto assembling, we just ignore the second
|
||
|
+ * for now.
|
||
|
+ */
|
||
|
+ dprintf(": superblock on %s doesn't match others - assembly aborted\n",
|
||
|
+ devname);
|
||
|
+ goto loop;
|
||
|
+ }
|
||
|
+ tmpdev->used = 1;
|
||
|
+ *found = 1;
|
||
|
+ dprintf("found: devname: %s\n", devname);
|
||
|
+ }
|
||
|
+ loop:
|
||
|
+ if (tst)
|
||
|
+ tst->ss->free_super(tst);
|
||
|
+ }
|
||
|
+ if (*found != 0) {
|
||
|
+ int err;
|
||
|
+ if ((err = load_super_imsm_all(st, -1, &st->sb, NULL, devlist, 0)) == 0) {
|
||
|
+ struct mdinfo *iter, *head = st->ss->container_content(st, NULL);
|
||
|
+ for (iter = head; iter; iter = iter->next) {
|
||
|
+ dprintf("content->text_version: %s vol\n",
|
||
|
+ iter->text_version);
|
||
|
+ if (iter->array.state & (1<<MD_SB_BLOCK_VOLUME)) {
|
||
|
+ /* do not assemble arrays with unsupported
|
||
|
+ configurations */
|
||
|
+ dprintf(": Cannot activate member %s.\n",
|
||
|
+ iter->text_version);
|
||
|
+ } else
|
||
|
+ count++;
|
||
|
+ }
|
||
|
+ sysfs_free(head);
|
||
|
+
|
||
|
+ } else {
|
||
|
+ dprintf(" no valid super block on device list: err: %d %p\n",
|
||
|
+ err, st->sb);
|
||
|
+ }
|
||
|
+ } else {
|
||
|
+ dprintf(" no more devices to examin\n");
|
||
|
+ }
|
||
|
+
|
||
|
+ for (tmpdev = devlist; tmpdev; tmpdev = tmpdev->next) {
|
||
|
+ if ((tmpdev->used == 1) && (tmpdev->found)) {
|
||
|
+ if (count) {
|
||
|
+ if (count < tmpdev->found)
|
||
|
+ count = 0;
|
||
|
+ else
|
||
|
+ count -= tmpdev->found;
|
||
|
+ }
|
||
|
+ }
|
||
|
+ if (tmpdev->used == 1)
|
||
|
+ tmpdev->used = 4;
|
||
|
+ }
|
||
|
+ err_1:
|
||
|
+ if (st)
|
||
|
+ st->ss->free_super(st);
|
||
|
+ return count;
|
||
|
+}
|
||
|
+
|
||
|
+
|
||
|
+static int
|
||
|
+count_volumes(char *hba, int dpa, int verbose)
|
||
|
+{
|
||
|
+ struct md_list *devlist = NULL;
|
||
|
+ int count = 0;
|
||
|
+ int found = 0;;
|
||
|
+
|
||
|
+ devlist = get_devices(hba);
|
||
|
+ /* if no intel devices return zero volumes */
|
||
|
+ if (devlist == NULL)
|
||
|
+ return 0;
|
||
|
+
|
||
|
+ count = active_arrays_by_format("imsm", hba, &devlist, dpa, verbose);
|
||
|
+ dprintf(" path: %s active arrays: %d\n", hba, count);
|
||
|
+ if (devlist == NULL)
|
||
|
+ return 0;
|
||
|
+ do {
|
||
|
+ found = 0;
|
||
|
+ count += count_volumes_list(devlist,
|
||
|
+ NULL,
|
||
|
+ verbose,
|
||
|
+ &found);
|
||
|
+ dprintf("found %d count: %d\n", found, count);
|
||
|
+ } while (found);
|
||
|
+
|
||
|
+ dprintf("path: %s total number of volumes: %d\n", hba, count);
|
||
|
+
|
||
|
+ while(devlist) {
|
||
|
+ struct md_list *dv = devlist;
|
||
|
+ devlist = devlist->next;
|
||
|
+ free(dv->devname);
|
||
|
+ free(dv);
|
||
|
+ }
|
||
|
+ return count;
|
||
|
+}
|
||
|
+
|
||
|
static int imsm_default_chunk(const struct imsm_orom *orom)
|
||
|
{
|
||
|
/* up to 512 if the plaform supports it, otherwise the platform max.
|
||
|
@@ -5583,6 +5954,15 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level,
|
||
|
|
||
|
*freesize = maxsize;
|
||
|
|
||
|
+ if (super->orom) {
|
||
|
+ int count = count_volumes(super->hba->path,
|
||
|
+ super->orom->dpa, verbose);
|
||
|
+ if (super->orom->vphba <= count) {
|
||
|
+ pr_vrb(": platform does not support more then %d raid volumes.\n",
|
||
|
+ super->orom->vphba);
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ }
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
@@ -5684,6 +6064,7 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
|
||
|
|
||
|
if (!dev) {
|
||
|
if (st->sb) {
|
||
|
+ struct intel_super *super = st->sb;
|
||
|
if (!validate_geometry_imsm_orom(st->sb, level, layout,
|
||
|
raiddisks, chunk,
|
||
|
verbose))
|
||
|
@@ -5696,6 +6077,19 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout,
|
||
|
* created. add_to_super and getinfo_super
|
||
|
* detect when autolayout is in progress.
|
||
|
*/
|
||
|
+ /* assuming that freesize is always given when array is
|
||
|
+ created */
|
||
|
+ if (super->orom && freesize) {
|
||
|
+ int count;
|
||
|
+ count = count_volumes(super->hba->path,
|
||
|
+ super->orom->dpa, verbose);
|
||
|
+ if (super->orom->vphba <= count) {
|
||
|
+ pr_vrb(": platform does not support more"
|
||
|
+ "then %d raid volumes.\n",
|
||
|
+ super->orom->vphba);
|
||
|
+ return 0;
|
||
|
+ }
|
||
|
+ }
|
||
|
if (freesize)
|
||
|
return reserve_space(st, raiddisks, size,
|
||
|
chunk?*chunk:0, freesize);
|
||
|
@@ -7176,7 +7570,7 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a,
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
-
|
||
|
+
|
||
|
mu->space = NULL;
|
||
|
mu->space_list = NULL;
|
||
|
mu->len = sizeof(struct imsm_update_activate_spare) * num_spares;
|
||
|
@@ -7420,7 +7814,7 @@ error_disk_add:
|
||
|
}
|
||
|
|
||
|
static int apply_update_activate_spare(struct imsm_update_activate_spare *u,
|
||
|
- struct intel_super *super,
|
||
|
+ struct intel_super *super,
|
||
|
struct active_array *active_array)
|
||
|
{
|
||
|
struct imsm_super *mpb = super->anchor;
|
||
|
--
|
||
|
1.7.7.6
|
||
|
|