#define DDF_NOTFOUND (~0U)
#define DDF_CONTAINER (DDF_NOTFOUND-1)
+/* Default for safe_mode_delay. Same value as for IMSM.
+ */
+static const int DDF_SAFE_MODE_DELAY = 4000;
+
/* The DDF metadata handling.
* DDF metadata lives at the end of the device.
* The last 512 byte block provides an 'anchor' which is used to locate
struct ddf_header *active;
struct phys_disk *phys;
struct virtual_disk *virt;
+ char *conf;
int pdsize, vdsize;
unsigned int max_part, mppe, conf_rec_len;
int currentdev;
if (len > 1024)
return NULL;
- if (buf) {
- /* All pre-allocated sections are a single block */
- if (len != 1)
- return NULL;
- } else if (posix_memalign(&buf, 512, len<<9) != 0)
+ if (!buf && posix_memalign(&buf, 512, len<<9) != 0)
buf = NULL;
if (!buf)
super->primary.openflag && !super->secondary.openflag)
)
super->active = &super->secondary;
- } else if (devname)
+ } else if (devname &&
+ be64_to_cpu(super->anchor.secondary_lba) != ~(__u64)0)
pr_err("Failed to load secondary DDF header on %s\n",
devname);
if (super->active == NULL)
* the conflist
*/
- conf = load_section(fd, super, NULL,
+ conf = load_section(fd, super, super->conf,
super->active->config_section_offset,
super->active->config_section_length,
0);
-
+ super->conf = conf;
vnum = 0;
for (confsec = 0;
confsec < be32_to_cpu(super->active->config_section_length);
if (i < max_virt_disks)
vcl->vcnum = i;
}
- free(conf);
return 0;
}
return;
free(ddf->phys);
free(ddf->virt);
+ free(ddf->conf);
while (ddf->conflist) {
struct vcl *v = ddf->conflist;
ddf->conflist = v->next;
static void uuid_from_ddf_guid(const char *guid, int uuid[4]);
static void uuid_from_super_ddf(struct supertype *st, int uuid[4]);
+static void _ddf_array_name(char *name, const struct ddf_super *ddf, int i);
static unsigned int get_vd_num_of_subarray(struct supertype *st)
{
struct virtual_entry *ve = &ddf->virt->entries[i];
struct vcl vcl;
char nbuf1[64];
+ char namebuf[17];
if (all_ff(ve->guid))
continue;
memcpy(vcl.conf.guid, ve->guid, DDF_GUID_LEN);
ddf->currentconf =&vcl;
+ vcl.vcnum = i;
uuid_from_super_ddf(st, info.uuid);
fname_from_uuid(st, &info, nbuf1, ':');
- printf("ARRAY container=%s member=%d UUID=%s\n",
+ _ddf_array_name(namebuf, ddf, i);
+ printf("ARRAY%s%s container=%s member=%d UUID=%s\n",
+ namebuf[0] == '\0' ? "" : " /dev/md/", namebuf,
nbuf+5, i, nbuf1+5);
}
}
printf("MD_METADATA=ddf\n");
printf("MD_LEVEL=container\n");
printf("MD_UUID=%s\n", nbuf+5);
+ printf("MD_DEVICES=%u\n",
+ be16_to_cpu(((struct ddf_super *)st->sb)->phys->used_pdes));
}
static int copy_metadata_ddf(struct supertype *st, int from, int to)
*/
}
+static const char *vendors_with_variable_volume_UUID[] = {
+ "LSI ",
+};
+
+static int volume_id_is_reliable(const struct ddf_super *ddf)
+{
+ int n = ARRAY_SIZE(vendors_with_variable_volume_UUID);
+ int i;
+ for (i = 0; i < n; i++)
+ if (!memcmp(ddf->controller.guid,
+ vendors_with_variable_volume_UUID[i], 8))
+ return 0;
+ return 1;
+}
+
+static void uuid_of_ddf_subarray(const struct ddf_super *ddf,
+ unsigned int vcnum, int uuid[4])
+{
+ char buf[DDF_GUID_LEN+18], sha[20], *p;
+ struct sha1_ctx ctx;
+ if (volume_id_is_reliable(ddf)) {
+ uuid_from_ddf_guid(ddf->virt->entries[vcnum].guid, uuid);
+ return;
+ }
+ /*
+ * Some fake RAID BIOSes (in particular, LSI ones) change the
+ * VD GUID at every boot. These GUIDs are not suitable for
+ * identifying an array. Luckily the header GUID appears to
+ * remain constant.
+ * We construct a pseudo-UUID from the header GUID and those
+ * properties of the subarray that we expect to remain constant.
+ */
+ memset(buf, 0, sizeof(buf));
+ p = buf;
+ memcpy(p, ddf->anchor.guid, DDF_GUID_LEN);
+ p += DDF_GUID_LEN;
+ memcpy(p, ddf->virt->entries[vcnum].name, 16);
+ p += 16;
+ *((__u16 *) p) = vcnum;
+ sha1_init_ctx(&ctx);
+ sha1_process_bytes(buf, sizeof(buf), &ctx);
+ sha1_finish_ctx(&ctx, sha);
+ memcpy(uuid, sha, 4*4);
+}
+
static void brief_detail_super_ddf(struct supertype *st)
{
struct mdinfo info;
else if (vcnum == DDF_NOTFOUND)
return;
else
- uuid_from_ddf_guid(ddf->virt->entries[vcnum].guid, info.uuid);
+ uuid_of_ddf_subarray(ddf, vcnum, info.uuid);
fname_from_uuid(st, &info, nbuf,':');
printf(" UUID=%s", nbuf + 5);
}
*/
struct ddf_super *ddf = st->sb;
struct vcl *vcl = ddf->currentconf;
- char *guid;
if (vcl)
- guid = vcl->conf.guid;
+ uuid_of_ddf_subarray(ddf, vcl->vcnum, uuid);
else
- guid = ddf->anchor.guid;
- uuid_from_ddf_guid(guid, uuid);
+ uuid_from_ddf_guid(ddf->anchor.guid, uuid);
}
static void getinfo_super_ddf_bvd(struct supertype *st, struct mdinfo *info, char *map);
}
}
+/* size of name must be at least 17 bytes! */
+static void _ddf_array_name(char *name, const struct ddf_super *ddf, int i)
+{
+ int j;
+ memcpy(name, ddf->virt->entries[i].name, 16);
+ name[16] = 0;
+ for(j = 0; j < 16; j++)
+ if (name[j] == ' ')
+ name[j] = 0;
+}
+
static void getinfo_super_ddf_bvd(struct supertype *st, struct mdinfo *info, char *map)
{
struct ddf_super *ddf = st->sb;
sprintf(info->text_version, "/%s/%d",
st->container_devnm,
info->container_member);
- info->safe_mode_delay = 200;
+ info->safe_mode_delay = DDF_SAFE_MODE_DELAY;
- memcpy(info->name, ddf->virt->entries[info->container_member].name, 16);
- info->name[16]=0;
- for(j=0; j<16; j++)
- if (info->name[j] == ' ')
- info->name[j] = 0;
+ _ddf_array_name(info->name, ddf, info->container_member);
if (map)
for (j = 0; j < map_disks; j++) {
return DDF_NOTFOUND;
}
+static void _set_config_size(struct phys_disk_entry *pde, const struct dl *dl)
+{
+ __u64 cfs, t;
+ cfs = min(dl->size - 32*1024*2ULL, be64_to_cpu(dl->primary_lba));
+ t = be64_to_cpu(dl->secondary_lba);
+ if (t != ~(__u64)0)
+ cfs = min(cfs, t);
+ /*
+ * Some vendor DDF structures interpret workspace_lba
+ * very differently then us. Make a sanity check on the value.
+ */
+ t = be64_to_cpu(dl->workspace_lba);
+ if (t < cfs) {
+ __u64 wsp = cfs - t;
+ if (wsp > 1024*1024*2ULL && wsp > dl->size / 16) {
+ pr_err("%s: %x:%x: workspace size 0x%llx too big, ignoring\n",
+ __func__, dl->major, dl->minor, wsp);
+ } else
+ cfs = t;
+ }
+ pde->config_size = cpu_to_be64(cfs);
+ dprintf("%s: %x:%x config_size %llx, DDF structure is %llx blocks\n",
+ __func__, dl->major, dl->minor, cfs, dl->size-cfs);
+}
+
/* add a device to a container, either while creating it or while
* expanding a pre-existing container
*/
} while (0)
__calc_lba(dd, ddf->dlist, workspace_lba, 32);
__calc_lba(dd, ddf->dlist, primary_lba, 16);
- __calc_lba(dd, ddf->dlist, secondary_lba, 32);
- pde->config_size = dd->workspace_lba;
+ if (ddf->dlist == NULL ||
+ be64_to_cpu(ddf->dlist->secondary_lba) != ~(__u64)0)
+ __calc_lba(dd, ddf->dlist, secondary_lba, 32);
+ _set_config_size(pde, dd);
sprintf(pde->path, "%17.17s","Information: nil") ;
memset(pde->pad, 0xff, 6);
*/
#define NULL_CONF_SZ 4096
-static char *null_aligned;
-static int __write_ddf_structure(struct dl *d, struct ddf_super *ddf, __u8 type,
- int update)
+static int __write_ddf_structure(struct dl *d, struct ddf_super *ddf, __u8 type)
{
unsigned long long sector;
struct ddf_header *header;
- int fd, i, n_config, conf_size;
+ int fd, i, n_config, conf_size, buf_size;
int ret = 0;
-
- if (null_aligned == NULL) {
- if (posix_memalign((void **)&null_aligned, 4096, NULL_CONF_SZ)
- != 0)
- return 0;
- memset(null_aligned, 0xff, NULL_CONF_SZ);
- }
+ char *conf;
fd = d->fd;
default:
return 0;
}
+ if (sector == ~(__u64)0)
+ return 0;
header->type = type;
header->openflag = 1;
/* Now write lots of config records. */
n_config = ddf->max_part;
conf_size = ddf->conf_rec_len * 512;
+ conf = ddf->conf;
+ buf_size = conf_size * (n_config + 1);
+ if (!conf) {
+ if (posix_memalign((void**)&conf, 512, buf_size) != 0)
+ goto out;
+ ddf->conf = conf;
+ }
for (i = 0 ; i <= n_config ; i++) {
struct vcl *c;
struct vd_config *vdc = NULL;
vdc->sec_elmnt_seq);
vdc->seqnum = header->seq;
vdc->crc = calc_crc(vdc, conf_size);
- if (write(fd, vdc, conf_size) < 0)
- break;
- } else if (!update) {
- unsigned int togo = conf_size;
- while (togo > NULL_CONF_SZ) {
- if (write(fd, null_aligned, NULL_CONF_SZ) < 0)
- break;
- togo -= NULL_CONF_SZ;
- }
- if (write(fd, null_aligned, togo) < 0)
- break;
+ memcpy(conf + i*conf_size, vdc, conf_size);
} else
- lseek(fd, conf_size, SEEK_CUR);
+ memset(conf + i*conf_size, 0xff, conf_size);
}
- if (i <= n_config)
+ if (write(fd, conf, buf_size) != buf_size)
goto out;
d->disk.crc = calc_crc(&d->disk, 512);
return ret;
}
-static int _write_super_to_disk(struct ddf_super *ddf, struct dl *d,
- int update)
+static int _write_super_to_disk(struct ddf_super *ddf, struct dl *d)
{
unsigned long long size;
int fd = d->fd;
ddf->anchor.seq = cpu_to_be32(0xFFFFFFFF); /* no sequencing in anchor */
ddf->anchor.crc = calc_crc(&ddf->anchor, 512);
- if (!__write_ddf_structure(d, ddf, DDF_HEADER_PRIMARY, update))
+ if (!__write_ddf_structure(d, ddf, DDF_HEADER_PRIMARY))
return 0;
- if (!__write_ddf_structure(d, ddf, DDF_HEADER_SECONDARY, update))
+ if (!__write_ddf_structure(d, ddf, DDF_HEADER_SECONDARY))
return 0;
lseek64(fd, (size-1)*512, SEEK_SET);
}
#ifndef MDASSEMBLE
-static int __write_init_super_ddf(struct supertype *st, int update)
+static int __write_init_super_ddf(struct supertype *st)
{
struct ddf_super *ddf = st->sb;
struct dl *d;
*/
for (d = ddf->dlist; d; d=d->next) {
attempts++;
- successes += _write_super_to_disk(ddf, d, update);
+ successes += _write_super_to_disk(ddf, d);
}
return attempts != successes;
if (!currentconf)
for (d = ddf->dlist; d; d=d->next)
while (Kill(d->devname, NULL, 0, -1, 1) == 0);
- return __write_init_super_ddf(st, 0);
+ return __write_init_super_ddf(st);
}
}
for (vc = ddf->conflist ; vc ; vc=vc->next)
{
unsigned int i;
- unsigned int j;
struct mdinfo *this;
char *ep;
__u32 *cptr;
this->array.md_minor = -1;
this->array.major_version = -1;
this->array.minor_version = -2;
+ this->safe_mode_delay = DDF_SAFE_MODE_DELAY;
cptr = (__u32 *)(vc->conf.guid + 16);
this->array.ctime = DECADE + __be32_to_cpu(*cptr);
this->array.utime = DECADE +
this->array.state = 1;
this->resync_start = MaxSector;
}
- memcpy(this->name, ddf->virt->entries[i].name, 16);
- this->name[16]=0;
- for(j=0; j<16; j++)
- if (this->name[j] == ' ')
- this->name[j] = 0;
-
+ _ddf_array_name(this->name, ddf, i);
memset(this->uuid, 0, sizeof(this->uuid));
this->component_size = be64_to_cpu(vc->conf.blocks);
this->array.size = this->component_size / 2;
}
ofd = dl->fd;
dl->fd = fd;
- ret = (_write_super_to_disk(ddf, dl, 0) != 1);
+ ret = (_write_super_to_disk(ddf, dl) != 1);
dl->fd = ofd;
return ret;
}
if (memcmp(first->anchor.guid, second->anchor.guid, DDF_GUID_LEN) != 0)
return 2;
- if (!be32_eq(first->anchor.seq, second->anchor.seq)) {
- dprintf("%s: sequence number mismatch %u/%u\n", __func__,
- be32_to_cpu(first->anchor.seq),
- be32_to_cpu(second->anchor.seq));
+ if (!be32_eq(first->active->seq, second->active->seq)) {
+ dprintf("%s: sequence number mismatch %u<->%u\n", __func__,
+ be32_to_cpu(first->active->seq),
+ be32_to_cpu(second->active->seq));
return 3;
}
if (first->max_part != second->max_part ||
{
struct ddf_super *ddf = c->sb;
int n = atoi(inst);
+ struct mdinfo *dev;
+ struct dl *dl;
+ static const char faulty[] = "faulty";
+
if (all_ff(ddf->virt->entries[n].guid)) {
pr_err("%s: subarray %d doesn't exist\n", __func__, n);
return -ENODEV;
}
- dprintf("ddf: open_new %d\n", n);
+ dprintf("%s: new subarray %d, GUID: %s\n", __func__, n,
+ guid_str(ddf->virt->entries[n].guid));
+ for (dev = a->info.devs; dev; dev = dev->next) {
+ for (dl = ddf->dlist; dl; dl = dl->next)
+ if (dl->major == dev->disk.major &&
+ dl->minor == dev->disk.minor)
+ break;
+ if (!dl) {
+ pr_err("%s: device %d/%d of subarray %d not found in meta data\n",
+ __func__, dev->disk.major, dev->disk.minor, n);
+ return -1;
+ }
+ if ((be16_to_cpu(ddf->phys->entries[dl->pdnum].state) &
+ (DDF_Online|DDF_Missing|DDF_Failed)) != DDF_Online) {
+ pr_err("%s: new subarray %d contains broken device %d/%d (%02x)\n",
+ __func__, n, dl->major, dl->minor,
+ be16_to_cpu(
+ ddf->phys->entries[dl->pdnum].state));
+ if (write(dev->state_fd, faulty, sizeof(faulty)-1) !=
+ sizeof(faulty) - 1)
+ pr_err("Write to state_fd failed\n");
+ dev->curr_state = DS_FAULTY;
+ }
+ }
a->info.container_member = n;
return 0;
}
if (!ddf->updates_pending)
return;
ddf->updates_pending = 0;
- __write_init_super_ddf(st, 1);
+ __write_init_super_ddf(st);
dprintf("ddf: sync_metadata\n");
}
* Create a metadata_update record to update the
* phys_refnum and lba_offset values
*/
- vc = find_vdcr(ddf, a->info.container_member, di->disk.raid_disk,
+ vc = find_vdcr(ddf, a->info.container_member, rv->disk.raid_disk,
&n_bvd, &vcl);
if (vc == NULL)
return NULL;