X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=super-ddf.c;h=93de75022d7626cad1d11b5785dba63a13a8b965;hb=666bba9b5011150ff01de421dd44b06c0a6610a5;hp=3b3cac0551b56c713dc3b241c1ff051adaa7d994;hpb=8592f29d642655a2b337b010dfa435a10b362769;p=thirdparty%2Fmdadm.git diff --git a/super-ddf.c b/super-ddf.c index 3b3cac05..93de7502 100644 --- a/super-ddf.c +++ b/super-ddf.c @@ -1,7 +1,7 @@ /* * mdadm - manage Linux "md" devices aka RAID arrays. * - * Copyright (C) 2006-2007 Neil Brown + * Copyright (C) 2006-2009 Neil Brown * * * This program is free software; you can redistribute it and/or modify @@ -396,7 +396,7 @@ struct ddf_super { struct phys_disk *phys; struct virtual_disk *virt; int pdsize, vdsize; - int max_part, mppe, conf_rec_len; + unsigned int max_part, mppe, conf_rec_len; int currentdev; int updates_pending; struct vcl { @@ -406,7 +406,7 @@ struct ddf_super { struct vcl *next; __u64 *lba_offset; /* location in 'conf' of * the lba table */ - int vcnum; /* index into ->virt */ + unsigned int vcnum; /* index into ->virt */ __u64 *block_sizes; /* NULL if all the same */ }; }; @@ -440,7 +440,7 @@ struct ddf_super { #endif -static int calc_crc(void *buf, int len) +static unsigned int calc_crc(void *buf, int len) { /* crcs are always at the same place as in the ddf_header */ struct ddf_header *ddf = buf; @@ -522,12 +522,12 @@ static void *load_section(int fd, struct ddf_super *super, void *buf, else offset += __be64_to_cpu(super->active->secondary_lba); - if (lseek64(fd, offset<<9, 0) != (offset<<9)) { + if ((unsigned long long)lseek64(fd, offset<<9, 0) != (offset<<9)) { if (dofree) free(buf); return NULL; } - if (read(fd, buf, len<<9) != (len<<9)) { + if ((unsigned long long)read(fd, buf, len<<9) != (len<<9)) { if (dofree) free(buf); return NULL; @@ -642,9 +642,10 @@ static int load_ddf_local(int fd, struct ddf_super *super, struct dl *dl; struct stat stb; char *conf; - int i; + unsigned int i; + unsigned int confsec; int vnum; - int max_virt_disks = __be16_to_cpu(super->active->max_vd_entries); + unsigned int max_virt_disks = __be16_to_cpu(super->active->max_vd_entries); unsigned long long dsize; /* First the local disk info */ @@ -672,11 +673,11 @@ static int load_ddf_local(int fd, struct ddf_super *super, if (get_dev_size(fd, devname, &dsize)) dl->size = dsize >> 9; dl->spare = NULL; - for (i=0 ; i < super->max_part ; i++) + for (i = 0 ; i < super->max_part ; i++) dl->vlist[i] = NULL; super->dlist = dl; dl->pdnum = -1; - for (i=0; i < __be16_to_cpu(super->active->max_pd_entries); i++) + for (i = 0; i < __be16_to_cpu(super->active->max_pd_entries); i++) if (memcmp(super->phys->entries[i].guid, dl->disk.guid, DDF_GUID_LEN) == 0) dl->pdnum = i; @@ -693,11 +694,11 @@ static int load_ddf_local(int fd, struct ddf_super *super, 0); vnum = 0; - for (i = 0; - i < __be32_to_cpu(super->active->config_section_length); - i += super->conf_rec_len) { + for (confsec = 0; + confsec < __be32_to_cpu(super->active->config_section_length); + confsec += super->conf_rec_len) { struct vd_config *vd = - (struct vd_config *)((char*)conf + i*512); + (struct vd_config *)((char*)conf + confsec*512); struct vcl *vcl; if (vd->magic == DDF_SPARE_ASSIGN_MAGIC) { @@ -761,6 +762,9 @@ static int load_ddf_local(int fd, struct ddf_super *super, static int load_super_ddf_all(struct supertype *st, int fd, void **sbp, char *devname, int keep_fd); #endif + +static void free_super_ddf(struct supertype *st); + static int load_super_ddf(struct supertype *st, int fd, char *devname) { @@ -779,26 +783,30 @@ static int load_super_ddf(struct supertype *st, int fd, if (get_dev_size(fd, devname, &dsize) == 0) return 1; + if (test_partition(fd)) + /* DDF is not allowed on partitions */ + return 1; + /* 32M is a lower bound */ if (dsize <= 32*1024*1024) { - if (devname) { + if (devname) fprintf(stderr, Name ": %s is too small for ddf: " "size is %llu sectors.\n", devname, dsize>>9); - return 1; - } + return 1; } if (dsize & 511) { - if (devname) { + if (devname) fprintf(stderr, Name ": %s is an odd size for ddf: " "size is %llu bytes.\n", devname, dsize); - return 1; - } + return 1; } + free_super_ddf(st); + if (posix_memalign((void**)&super, 512, sizeof(*super))!= 0) { fprintf(stderr, Name ": malloc of %zu failed.\n", sizeof(*super)); @@ -836,6 +844,26 @@ static int load_super_ddf(struct supertype *st, int fd, return rv; } + if (st->subarray[0]) { + unsigned long val; + struct vcl *v; + char *ep; + + val = strtoul(st->subarray, &ep, 10); + if (*ep != '\0') { + free(super); + return 1; + } + + for (v = super->conflist; v; v = v->next) + if (v->vcnum == val) + super->currentconf = v; + if (!super->currentconf) { + free(super); + return 1; + } + } + /* Should possibly check the sections .... */ st->sb = super; @@ -872,6 +900,15 @@ static void free_super_ddf(struct supertype *st) free(d->spare); free(d); } + while (ddf->add_list) { + struct dl *d = ddf->add_list; + ddf->add_list = d->next; + if (d->fd >= 0) + close(d->fd); + if (d->spare) + free(d->spare); + free(d); + } free(ddf); st->sb = NULL; } @@ -1000,7 +1037,7 @@ static void print_guid(char *guid, int tstamp) printf("%02X", guid[i]&255); } - printf(" ("); + printf("\n ("); while (l && guid[l-1] == ' ') l--; for (i=0 ; iconflist ; vcl ; vcl = vcl->next) { + unsigned int i; struct vd_config *vc = &vcl->conf; if (calc_crc(vc, crl*512) != vc->crc) @@ -1034,8 +1072,22 @@ static void examine_vd(int n, struct ddf_super *sb, char *guid) continue; /* Ok, we know about this VD, let's give more details */ - printf(" Raid Devices[%d] : %d\n", n, + printf(" Raid Devices[%d] : %d (", n, __be16_to_cpu(vc->prim_elmnt_count)); + for (i = 0; i < __be16_to_cpu(vc->prim_elmnt_count); i++) { + int j; + int cnt = __be16_to_cpu(sb->phys->used_pdes); + for (j=0; jphys_refnum[i] == sb->phys->entries[j].refnum) + break; + if (i) printf(" "); + if (j < cnt) + printf("%d", j); + else + printf("--"); + } + printf(")\n"); + if (vc->chunk_shift != 255) printf(" Chunk Size[%d] : %d sectors\n", n, 1 << vc->chunk_shift); printf(" Raid Level[%d] : %s\n", n, @@ -1047,9 +1099,9 @@ static void examine_vd(int n, struct ddf_super *sb, char *guid) map_num(ddf_sec_level, vc->srl) ?: "-unknown-"); } printf(" Device Size[%d] : %llu\n", n, - __be64_to_cpu(vc->blocks)/2); + (unsigned long long)__be64_to_cpu(vc->blocks)/2); printf(" Array Size[%d] : %llu\n", n, - __be64_to_cpu(vc->array_blocks)/2); + (unsigned long long)__be64_to_cpu(vc->array_blocks)/2); } } @@ -1061,6 +1113,7 @@ static void examine_vds(struct ddf_super *sb) for (i=0; ivirt->entries[i]; + printf("\n"); printf(" VD GUID[%d] : ", i); print_guid(ve->guid, 1); printf("\n"); printf(" unit[%d] : %d\n", i, __be16_to_cpu(ve->unit)); @@ -1084,40 +1137,47 @@ static void examine_pds(struct ddf_super *sb) int i; struct dl *dl; printf(" Physical Disks : %d\n", cnt); + printf(" Number RefNo Size Device Type/State\n"); for (i=0 ; iphys->entries[i]; int type = __be16_to_cpu(pd->type); int state = __be16_to_cpu(pd->state); - printf(" PD GUID[%d] : ", i); print_guid(pd->guid, 0); - printf("\n"); - printf(" ref[%d] : %08x\n", i, + //printf(" PD GUID[%d] : ", i); print_guid(pd->guid, 0); + //printf("\n"); + printf(" %3d %08x ", i, __be32_to_cpu(pd->refnum)); - printf(" mode[%d] : %s%s%s%s%s\n", i, + printf("%8lluK ", + (unsigned long long)__be64_to_cpu(pd->config_size)>>1); + for (dl = sb->dlist; dl ; dl = dl->next) { + if (dl->disk.refnum == pd->refnum) { + char *dv = map_dev(dl->major, dl->minor, 0); + if (dv) { + printf("%-15s", dv); + break; + } + } + } + if (!dl) + printf("%15s",""); + printf(" %s%s%s%s%s", (type&2) ? "active":"", - (type&4) ? "Global Spare":"", + (type&4) ? "Global-Spare":"", (type&8) ? "spare" : "", (type&16)? ", foreign" : "", (type&32)? "pass-through" : ""); - printf(" state[%d] : %s%s%s%s%s%s%s\n", i, + if (state & DDF_Failed) + /* This over-rides these three */ + state &= ~(DDF_Online|DDF_Rebuilding|DDF_Transition); + printf("/%s%s%s%s%s%s%s", (state&1)? "Online": "Offline", (state&2)? ", Failed": "", (state&4)? ", Rebuilding": "", (state&8)? ", in-transition": "", - (state&16)? ", SMART errors": "", - (state&32)? ", Unrecovered Read Errors": "", + (state&16)? ", SMART-errors": "", + (state&32)? ", Unrecovered-Read-Errors": "", (state&64)? ", Missing" : ""); - printf(" Avail Size[%d] : %llu K\n", i, - __be64_to_cpu(pd->config_size)>>1); - for (dl = sb->dlist; dl ; dl = dl->next) { - if (dl->disk.refnum == pd->refnum) { - char *dv = map_dev(dl->major, dl->minor, 0); - if (dv) - printf(" Device[%d] : %s\n", - i, dv); - } - } printf("\n"); } } @@ -1143,19 +1203,30 @@ static void getinfo_super_ddf(struct supertype *st, struct mdinfo *info); static void uuid_from_super_ddf(struct supertype *st, int uuid[4]); -static void brief_examine_super_ddf(struct supertype *st) +static void brief_examine_super_ddf(struct supertype *st, int verbose) { /* We just write a generic DDF ARRAY entry */ - struct ddf_super *ddf = st->sb; struct mdinfo info; - int i; char nbuf[64]; getinfo_super_ddf(st, &info); fname_from_uuid(st, &info, nbuf, ':'); + printf("ARRAY metadata=ddf UUID=%s\n", nbuf + 5); +} - for (i=0; i<__be16_to_cpu(ddf->virt->max_vdes); i++) { +static void brief_examine_subarrays_ddf(struct supertype *st, int verbose) +{ + /* We just write a generic DDF ARRAY entry + */ + struct ddf_super *ddf = st->sb; + struct mdinfo info; + unsigned int i; + char nbuf[64]; + getinfo_super_ddf(st, &info); + fname_from_uuid(st, &info, nbuf, ':'); + + for (i = 0; i < __be16_to_cpu(ddf->virt->max_vdes); i++) { struct virtual_entry *ve = &ddf->virt->entries[i]; struct vcl vcl; char nbuf1[64]; @@ -1170,6 +1241,18 @@ static void brief_examine_super_ddf(struct supertype *st) } } +static void export_examine_super_ddf(struct supertype *st) +{ + struct mdinfo info; + char nbuf[64]; + getinfo_super_ddf(st, &info); + fname_from_uuid(st, &info, nbuf, ':'); + printf("MD_METADATA=ddf\n"); + printf("MD_LEVEL=container\n"); + printf("MD_UUID=%s\n", nbuf+5); +} + + static void detail_super_ddf(struct supertype *st, char *homehost) { /* FIXME later @@ -1201,7 +1284,11 @@ static int match_home_ddf(struct supertype *st, char *homehost) * the hostname */ struct ddf_super *ddf = st->sb; - int len = strlen(homehost); + unsigned int len; + + if (!homehost) + return 0; + len = strlen(homehost); return (memcmp(ddf->controller.guid, T10, 8) == 0 && len < sizeof(ddf->controller.vendor_data) && @@ -1210,7 +1297,7 @@ static int match_home_ddf(struct supertype *st, char *homehost) } #ifndef MDASSEMBLE -static struct vd_config *find_vdcr(struct ddf_super *ddf, int inst) +static struct vd_config *find_vdcr(struct ddf_super *ddf, unsigned int inst) { struct vcl *v; @@ -1226,8 +1313,8 @@ static int find_phys(struct ddf_super *ddf, __u32 phys_refnum) /* Find the entry in phys_disk which has the given refnum * and return it's index */ - int i; - for (i=0; i < __be16_to_cpu(ddf->phys->max_pdes); i++) + unsigned int i; + for (i = 0; i < __be16_to_cpu(ddf->phys->max_pdes); i++) if (ddf->phys->entries[i].refnum == phys_refnum) return i; return -1; @@ -1290,6 +1377,7 @@ static void getinfo_super_ddf(struct supertype *st, struct mdinfo *info) (ddf->anchor.guid+16)); info->array.utime = 0; info->array.chunk_size = 0; + info->container_enough = 1; info->disk.major = 0; @@ -1304,11 +1392,13 @@ static void getinfo_super_ddf(struct supertype *st, struct mdinfo *info) info->component_size = ddf->dlist->size - info->data_offset; } else { info->disk.number = -1; + info->disk.raid_disk = -1; // info->disk.raid_disk = find refnum in the table and use index; } - info->disk.state = (1 << MD_DISK_SYNC); + info->disk.state = (1 << MD_DISK_SYNC) | (1 << MD_DISK_ACTIVE); + info->recovery_start = MaxSector; info->reshape_active = 0; info->name[0] = 0; @@ -1342,8 +1432,9 @@ static void getinfo_super_ddf_bvd(struct supertype *st, struct mdinfo *info) __be32_to_cpu(*(__u32*)(vc->conf.guid+16)); info->array.utime = DECADE + __be32_to_cpu(vc->conf.timestamp); info->array.chunk_size = 512 << vc->conf.chunk_shift; + info->custom_array_size = 0; - if (cd >= 0 && cd < ddf->mppe) { + if (cd >= 0 && (unsigned)cd < ddf->mppe) { info->data_offset = __be64_to_cpu(vc->lba_offset[cd]); if (vc->block_sizes) info->component_size = vc->block_sizes[cd]; @@ -1366,17 +1457,18 @@ static void getinfo_super_ddf_bvd(struct supertype *st, struct mdinfo *info) info->container_member = ddf->currentconf->vcnum; + info->recovery_start = MaxSector; info->resync_start = 0; + info->reshape_active = 0; if (!(ddf->virt->entries[info->container_member].state & DDF_state_inconsistent) && (ddf->virt->entries[info->container_member].init_state & DDF_initstate_mask) == DDF_init_full) - info->resync_start = ~0ULL; + info->resync_start = MaxSector; uuid_from_super_ddf(st, info->uuid); - info->container_member = atoi(st->subarray); info->array.major_version = -1; info->array.minor_version = -2; sprintf(info->text_version, "/%s/%s", @@ -1465,7 +1557,6 @@ static int update_super_ddf(struct supertype *st, struct mdinfo *info, static void make_header_guid(char *guid) { __u32 stamp; - int rfd; /* Create a DDF Header of Virtual Disk GUID */ /* 24 bytes of fiction required. @@ -1480,11 +1571,8 @@ static void make_header_guid(char *guid) memcpy(guid+12, &stamp, 4); stamp = __cpu_to_be32(time(0) - DECADE); memcpy(guid+16, &stamp, 4); - rfd = open("/dev/urandom", O_RDONLY); - if (rfd < 0 || read(rfd, &stamp, 4) != 4) - stamp = random(); + stamp = random32(); memcpy(guid+20, &stamp, 4); - if (rfd >= 0) close(rfd); } static int init_super_ddf_bvd(struct supertype *st, @@ -1532,13 +1620,8 @@ static int init_super_ddf(struct supertype *st, struct phys_disk *pd; struct virtual_disk *vd; - if (!info) { - st->sb = NULL; - return 0; - } if (st->sb) - return init_super_ddf_bvd(st, info, size, name, homehost, - uuid); + return init_super_ddf_bvd(st, info, size, name, homehost, uuid); if (posix_memalign((void**)&ddf, 512, sizeof(*ddf)) != 0) { fprintf(stderr, Name ": %s could not allocate superblock\n", __func__); @@ -1547,6 +1630,12 @@ static int init_super_ddf(struct supertype *st, memset(ddf, 0, sizeof(*ddf)); ddf->dlist = NULL; /* no physical disks yet */ ddf->conflist = NULL; /* No virtual disks yet */ + st->sb = ddf; + + if (info == NULL) { + /* zeroing superblock */ + return 0; + } /* At least 32MB *must* be reserved for the ddf. So let's just * start 32MB from the end, and put the primary header there. @@ -1681,7 +1770,7 @@ static int init_super_ddf(struct supertype *st, memset(pd, 0xff, pdsize); memset(pd, 0, sizeof(*pd)); - pd->magic = DDF_PHYS_DATA_MAGIC; + pd->magic = DDF_PHYS_RECORDS_MAGIC; pd->used_pdes = __cpu_to_be16(0); pd->max_pdes = __cpu_to_be16(max_phys_disks); memset(pd->pad, 0xff, 52); @@ -1740,20 +1829,25 @@ static int layout_to_rlq(int level, int layout, int raiddisks) } break; case 5: - case 6: switch(layout) { case ALGORITHM_LEFT_ASYMMETRIC: return DDF_RAID5_N_RESTART; case ALGORITHM_RIGHT_ASYMMETRIC: - if (level == 5) - return DDF_RAID5_0_RESTART; - else - return DDF_RAID6_0_RESTART; + return DDF_RAID5_0_RESTART; case ALGORITHM_LEFT_SYMMETRIC: return DDF_RAID5_N_CONTINUE; case ALGORITHM_RIGHT_SYMMETRIC: return -1; /* not mentioned in standard */ } + case 6: + switch(layout) { + case ALGORITHM_ROTATING_N_RESTART: + return DDF_RAID5_N_RESTART; + case ALGORITHM_ROTATING_ZERO_RESTART: + return DDF_RAID6_0_RESTART; + case ALGORITHM_ROTATING_N_CONTINUE: + return DDF_RAID5_N_CONTINUE; + } } return -1; } @@ -1788,11 +1882,11 @@ static int rlq_to_layout(int rlq, int prl, int raiddisks) case DDF_RAID6: switch(rlq) { case DDF_RAID5_N_RESTART: - return ALGORITHM_LEFT_ASYMMETRIC; + return ALGORITHM_ROTATING_N_RESTART; case DDF_RAID6_0_RESTART: - return ALGORITHM_RIGHT_ASYMMETRIC; + return ALGORITHM_ROTATING_ZERO_RESTART; case DDF_RAID5_N_CONTINUE: - return ALGORITHM_LEFT_SYMMETRIC; + return ALGORITHM_ROTATING_N_CONTINUE; default: return -1; } @@ -1826,7 +1920,7 @@ FIXME ignore DDF_Legacy devices? */ struct extent *rv; int n = 0; - int i, j; + unsigned int i, j; rv = malloc(sizeof(struct extent) * (ddf->max_part + 2)); if (!rv) @@ -1836,7 +1930,7 @@ FIXME ignore DDF_Legacy devices? struct vcl *v = dl->vlist[i]; if (v == NULL) continue; - for (j=0; j < v->conf.prim_elmnt_count; j++) + for (j = 0; j < v->conf.prim_elmnt_count; j++) if (v->conf.phys_refnum[j] == dl->disk.refnum) { /* This device plays role 'j' in 'v'. */ rv[n].start = __be64_to_cpu(v->lba_offset[j]); @@ -1864,7 +1958,7 @@ static int init_super_ddf_bvd(struct supertype *st, * We need to create a new vd_config and a new virtual_entry */ struct ddf_super *ddf = st->sb; - int venum; + unsigned int venum; struct virtual_entry *ve; struct vcl *vcl; struct vd_config *vc; @@ -1877,6 +1971,19 @@ static int init_super_ddf_bvd(struct supertype *st, return 0; } + if (name) + for (venum = 0; venum < __be16_to_cpu(ddf->virt->max_vdes); venum++) + if (!all_ff(ddf->virt->entries[venum].guid)) { + char *n = ddf->virt->entries[venum].name; + + if (strncmp(name, n, 16) == 0) { + fprintf(stderr, Name ": This ddf already" + " has an array called %s\n", + name); + return 0; + } + } + for (venum = 0; venum < __be16_to_cpu(ddf->virt->max_vdes); venum++) if (all_ff(ddf->virt->entries[venum].guid)) break; @@ -1958,7 +2065,7 @@ static int init_super_ddf_bvd(struct supertype *st, memset(vc->vendor, 0xff, 32); memset(vc->phys_refnum, 0xff, 4*ddf->mppe); - memset(vc->phys_refnum+(ddf->mppe * 4), 0x00, 8*ddf->mppe); + memset(vc->phys_refnum+ddf->mppe, 0x00, 8*ddf->mppe); vcl->next = ddf->conflist; ddf->conflist = vcl; @@ -1985,8 +2092,8 @@ static void add_to_super_ddf_bvd(struct supertype *st, struct ddf_super *ddf = st->sb; struct vd_config *vc; __u64 *lba_offset; - int working; - int i; + unsigned int working; + unsigned int i; unsigned long long blocks, pos, esize; struct extent *ex; @@ -2031,7 +2138,7 @@ static void add_to_super_ddf_bvd(struct supertype *st, vc->phys_refnum[dk->raid_disk] = dl->disk.refnum; lba_offset[dk->raid_disk] = __cpu_to_be64(pos); - for (i=0; i < ddf->max_part ; i++) + for (i = 0; i < ddf->max_part ; i++) if (dl->vlist[i] == NULL) break; if (i == ddf->max_part) @@ -2048,7 +2155,7 @@ static void add_to_super_ddf_bvd(struct supertype *st, */ working = 0; - for (i=0; i < __be16_to_cpu(vc->prim_elmnt_count); i++) + for (i = 0; i < __be16_to_cpu(vc->prim_elmnt_count); i++) if (vc->phys_refnum[i] != 0xffffffff) working++; @@ -2082,7 +2189,7 @@ static int add_to_super_ddf(struct supertype *st, struct tm *tm; unsigned long long size; struct phys_disk_entry *pde; - int n, i; + unsigned int n, i; struct stat stb; if (ddf->currentconf) { @@ -2112,17 +2219,17 @@ static int add_to_super_ddf(struct supertype *st, tm = localtime(&now); sprintf(dd->disk.guid, "%8s%04d%02d%02d", T10, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday); - *(__u32*)(dd->disk.guid + 16) = random(); - *(__u32*)(dd->disk.guid + 20) = random(); + *(__u32*)(dd->disk.guid + 16) = random32(); + *(__u32*)(dd->disk.guid + 20) = random32(); do { /* Cannot be bothered finding a CRC of some irrelevant details*/ - dd->disk.refnum = random(); - for (i = __be16_to_cpu(ddf->active->max_pd_entries) - 1; - i >= 0; i--) - if (ddf->phys->entries[i].refnum == dd->disk.refnum) + dd->disk.refnum = random32(); + for (i = __be16_to_cpu(ddf->active->max_pd_entries); + i > 0; i--) + if (ddf->phys->entries[i-1].refnum == dd->disk.refnum) break; - } while (i >= 0); + } while (i > 0); dd->disk.forced_ref = 1; dd->disk.forced_guid = 1; @@ -2174,6 +2281,40 @@ static int add_to_super_ddf(struct supertype *st, return 0; } +static int remove_from_super_ddf(struct supertype *st, mdu_disk_info_t *dk) +{ + struct ddf_super *ddf = st->sb; + struct dl *dl; + + /* mdmon has noticed that this disk (dk->major/dk->minor) has + * disappeared from the container. + * We need to arrange that it disappears from the metadata and + * internal data structures too. + * Most of the work is done by ddf_process_update which edits + * the metadata and closes the file handle and attaches the memory + * where free_updates will free it. + */ + for (dl = ddf->dlist; dl ; dl = dl->next) + if (dl->major == dk->major && + dl->minor == dk->minor) + break; + if (!dl) + return -1; + + if (st->update_tail) { + int len = (sizeof(struct phys_disk) + + sizeof(struct phys_disk_entry)); + struct phys_disk *pd; + + pd = malloc(len); + pd->magic = DDF_PHYS_RECORDS_MAGIC; + pd->used_pdes = __cpu_to_be16(dl->pdnum); + pd->entries[0].state = __cpu_to_be16(DDF_Missing); + append_metadata_update(st, pd, len); + } + return 0; +} + /* * This is the write_init_super method for a ddf container. It is * called when creating a container or adding another device to a @@ -2265,7 +2406,7 @@ static int __write_init_super_ddf(struct supertype *st, int do_close) char *null_aligned = (char*)((((unsigned long)null_conf)+511)&~511UL); if (null_conf[0] != 0xff) memset(null_conf, 0xff, sizeof(null_conf)); - int togo = conf_size; + unsigned int togo = conf_size; while (togo > sizeof(null_conf)-512) { if (write(fd, null_aligned, sizeof(null_conf)-512) < 0) break; @@ -2300,15 +2441,19 @@ static int __write_init_super_ddf(struct supertype *st, int do_close) static int write_init_super_ddf(struct supertype *st) { + struct ddf_super *ddf = st->sb; + struct vcl *currentconf = ddf->currentconf; + + /* we are done with currentconf reset it to point st at the container */ + ddf->currentconf = NULL; if (st->update_tail) { /* queue the virtual_disk and vd_config as metadata updates */ struct virtual_disk *vd; struct vd_config *vc; - struct ddf_super *ddf = st->sb; int len; - if (!ddf->currentconf) { + if (!currentconf) { int len = (sizeof(struct phys_disk) + sizeof(struct phys_disk_entry)); @@ -2327,20 +2472,24 @@ static int write_init_super_ddf(struct supertype *st) len = sizeof(struct virtual_disk) + sizeof(struct virtual_entry); vd = malloc(len); *vd = *ddf->virt; - vd->entries[0] = ddf->virt->entries[ddf->currentconf->vcnum]; - vd->populated_vdes = __cpu_to_be16(ddf->currentconf->vcnum); + vd->entries[0] = ddf->virt->entries[currentconf->vcnum]; + vd->populated_vdes = __cpu_to_be16(currentconf->vcnum); append_metadata_update(st, vd, len); /* Then the vd_config */ len = ddf->conf_rec_len * 512; vc = malloc(len); - memcpy(vc, &ddf->currentconf->conf, len); + memcpy(vc, ¤tconf->conf, len); append_metadata_update(st, vc, len); /* FIXME I need to close the fds! */ return 0; - } else + } else { + struct dl *d; + for (d = ddf->dlist; d; d=d->next) + while (Kill(d->devname, NULL, 0, 1, 1) == 0); return __write_init_super_ddf(st, 1); + } } #endif @@ -2488,8 +2637,12 @@ static int validate_geometry_ddf(struct supertype *st, for (i=0; ddf_level_num[i].num1 != MAXINT; i++) if (ddf_level_num[i].num2 == level) break; - if (ddf_level_num[i].num1 == MAXINT) + if (ddf_level_num[i].num1 == MAXINT) { + if (verbose) + fprintf(stderr, Name ": DDF does not support level %d arrays\n", + level); return 0; + } /* Should check layout? etc */ if (st->sb && freesize) { @@ -2537,7 +2690,7 @@ static int validate_geometry_ddf(struct supertype *st, if (verbose) fprintf(stderr, Name ": ddf: Cannot create this array " - "on device %s\n", + "on device %s - a container is required.\n", dev); return 0; } @@ -2609,6 +2762,8 @@ validate_geometry_ddf_container(struct supertype *st, close(fd); *freesize = avail_size_ddf(st, ldsize >> 9); + if (*freesize == 0) + return 0; return 1; } @@ -2627,8 +2782,11 @@ static int validate_geometry_ddf_bvd(struct supertype *st, struct extent *e; int i; /* ddf/bvd supports lots of things, but not containers */ - if (level == LEVEL_CONTAINER) + if (level == LEVEL_CONTAINER) { + if (verbose) + fprintf(stderr, Name ": DDF cannot create a container within an container\n"); return 0; + } /* We must have the container info already read in. */ if (!ddf) return 0; @@ -2677,8 +2835,8 @@ static int validate_geometry_ddf_bvd(struct supertype *st, if ((S_IFMT & stb.st_mode) != S_IFBLK) return 0; for (dl = ddf->dlist ; dl ; dl = dl->next) { - if (dl->major == major(stb.st_rdev) && - dl->minor == minor(stb.st_rdev)) + if (dl->major == (int)major(stb.st_rdev) && + dl->minor == (int)minor(stb.st_rdev)) break; } if (!dl) { @@ -2773,14 +2931,25 @@ static int load_super_ddf_all(struct supertype *st, int fd, return 1; } if (st->subarray[0]) { + unsigned long val; struct vcl *v; + char *ep; + + val = strtoul(st->subarray, &ep, 10); + if (*ep != '\0') { + free(super); + return 1; + } for (v = super->conflist; v; v = v->next) - if (v->vcnum == atoi(st->subarray)) + if (v->vcnum == val) super->currentconf = v; - if (!super->currentconf) + if (!super->currentconf) { + free(super); return 1; + } } + *sbp = super; if (st->ss == NULL) { st->ss = &super_ddf; @@ -2809,8 +2978,8 @@ static struct mdinfo *container_content_ddf(struct supertype *st) for (vc = ddf->conflist ; vc ; vc=vc->next) { - int i; - int j; + unsigned int i; + unsigned int j; struct mdinfo *this; this = malloc(sizeof(*this)); memset(this, 0, sizeof(*this)); @@ -2839,7 +3008,7 @@ static struct mdinfo *container_content_ddf(struct supertype *st) this->resync_start = 0; } else { this->array.state = 1; - this->resync_start = ~0ULL; + this->resync_start = MaxSector; } memcpy(this->name, ddf->virt->entries[i].name, 16); this->name[16]=0; @@ -2860,20 +3029,26 @@ static struct mdinfo *container_content_ddf(struct supertype *st) devnum2devname(st->container_dev), this->container_member); - for (i=0 ; i < ddf->mppe ; i++) { + for (i = 0 ; i < ddf->mppe ; i++) { struct mdinfo *dev; struct dl *d; + int stt; if (vc->conf.phys_refnum[i] == 0xFFFFFFFF) continue; - this->array.working_disks++; - for (d = ddf->dlist; d ; d=d->next) if (d->disk.refnum == vc->conf.phys_refnum[i]) break; if (d == NULL) - break; + /* Haven't found that one yet, maybe there are others */ + continue; + stt = __be16_to_cpu(ddf->phys->entries[d->pdnum].state); + if ((stt & (DDF_Online|DDF_Failed|DDF_Rebuilding)) + != DDF_Online) + continue; + + this->array.working_disks++; dev = malloc(sizeof(*dev)); memset(dev, 0, sizeof(*dev)); @@ -2885,6 +3060,7 @@ static struct mdinfo *container_content_ddf(struct supertype *st) dev->disk.minor = d->minor; dev->disk.raid_disk = i; dev->disk.state = (1<recovery_start = MaxSector; dev->events = __be32_to_cpu(ddf->primary.seq); dev->data_offset = __be64_to_cpu(vc->lba_offset[i]); @@ -2896,12 +3072,22 @@ static struct mdinfo *container_content_ddf(struct supertype *st) return rest; } -static int store_zero_ddf(struct supertype *st, int fd) +static int store_super_ddf(struct supertype *st, int fd) { + struct ddf_super *ddf = st->sb; unsigned long long dsize; void *buf; int rc; + if (!ddf) + return 1; + + /* ->dlist and ->conflist will be set for updates, currently not + * supported + */ + if (ddf->dlist || ddf->conflist) + return 1; + if (!get_dev_size(fd, NULL, &dsize)) return 1; @@ -2973,7 +3159,7 @@ static int ddf_set_array_state(struct active_array *a, int consistent) if (consistent == 2) { /* Should check if a recovery should be started FIXME */ consistent = 1; - if (!is_resync_complete(a)) + if (!is_resync_complete(&a->info)) consistent = 0; } if (consistent) @@ -2985,9 +3171,9 @@ static int ddf_set_array_state(struct active_array *a, int consistent) old = ddf->virt->entries[inst].init_state; ddf->virt->entries[inst].init_state &= ~DDF_initstate_mask; - if (is_resync_complete(a)) + if (is_resync_complete(&a->info)) ddf->virt->entries[inst].init_state |= DDF_init_full; - else if (a->resync_start == 0) + else if (a->info.resync_start == 0) ddf->virt->entries[inst].init_state |= DDF_init_not; else ddf->virt->entries[inst].init_state |= DDF_init_quick; @@ -2995,10 +3181,13 @@ static int ddf_set_array_state(struct active_array *a, int consistent) ddf->updates_pending = 1; dprintf("ddf mark %d %s %llu\n", inst, consistent?"clean":"dirty", - a->resync_start); + a->info.resync_start); return consistent; } +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) /* * The state of each disk is stored in the global phys_disk structure * in phys_disk.entries[n].state. @@ -3016,24 +3205,47 @@ static int ddf_set_array_state(struct active_array *a, int consistent) static void ddf_set_disk(struct active_array *a, int n, int state) { struct ddf_super *ddf = a->container->sb; - int inst = a->info.container_member; + unsigned int inst = a->info.container_member; struct vd_config *vc = find_vdcr(ddf, inst); int pd = find_phys(ddf, vc->phys_refnum[n]); int i, st, working; + struct mdinfo *mdi; + struct dl *dl; if (vc == NULL) { dprintf("ddf: cannot find instance %d!!\n", inst); return; } - if (pd < 0) { - /* disk doesn't currently exist. If it is now in_sync, - * insert it. */ + /* Find the matching slot in 'info'. */ + for (mdi = a->info.devs; mdi; mdi = mdi->next) + if (mdi->disk.raid_disk == n) + break; + if (!mdi) + return; + + /* and find the 'dl' entry corresponding to that. */ + for (dl = ddf->dlist; dl; dl = dl->next) + if (mdi->state_fd >= 0 && + mdi->disk.major == dl->major && + mdi->disk.minor == dl->minor) + break; + if (!dl) + return; + + if (pd < 0 || pd != dl->pdnum) { + /* disk doesn't currently exist or has changed. + * If it is now in_sync, insert it. */ if ((state & DS_INSYNC) && ! (state & DS_FAULTY)) { - /* Find dev 'n' in a->info->devs, determine the - * ddf refnum, and set vc->phys_refnum and update - * phys->entries[] - */ - /* FIXME */ + struct vcl *vcl; + pd = dl->pdnum; + vc->phys_refnum[n] = dl->disk.refnum; + vcl = container_of(vc, struct vcl, conf); + vcl->lba_offset[n] = mdi->data_offset; + ddf->phys->entries[pd].type &= + ~__cpu_to_be16(DDF_Global_Spare); + ddf->phys->entries[pd].type |= + __cpu_to_be16(DDF_Active_in_VD); + ddf->updates_pending = 1; } } else { int old = ddf->phys->entries[pd].state; @@ -3076,6 +3288,8 @@ static void ddf_set_disk(struct active_array *a, int n, int state) case DDF_RAID1: if (working == 0) state = DDF_state_failed; + else if (working == 2 && state == DDF_state_degraded) + state = DDF_state_part_optimal; break; case DDF_RAID4: case DDF_RAID5: @@ -3128,8 +3342,8 @@ static void ddf_process_update(struct supertype *st, * our actions. * Possible update are: * DDF_PHYS_RECORDS_MAGIC - * Add a new physical device. Changes to this record - * only happen implicitly. + * Add a new physical device or remove an old one. + * Changes to this record only happen implicitly. * used_pdes is the device number. * DDF_VIRT_RECORDS_MAGIC * Add a new VD. Possibly also change the 'access' bits. @@ -3157,8 +3371,9 @@ static void ddf_process_update(struct supertype *st, struct vd_config *vc; struct vcl *vcl; struct dl *dl; - int mppe; - int ent; + unsigned int mppe; + unsigned int ent; + unsigned int pdnum, pd2; dprintf("Process update %x\n", *magic); @@ -3173,6 +3388,25 @@ static void ddf_process_update(struct supertype *st, ent = __be16_to_cpu(pd->used_pdes); if (ent >= __be16_to_cpu(ddf->phys->max_pdes)) return; + if (pd->entries[0].state & __cpu_to_be16(DDF_Missing)) { + struct dl **dlp; + /* removing this disk. */ + ddf->phys->entries[ent].state |= __cpu_to_be16(DDF_Missing); + for (dlp = &ddf->dlist; *dlp; dlp = &(*dlp)->next) { + struct dl *dl = *dlp; + if (dl->pdnum == (signed)ent) { + close(dl->fd); + dl->fd = -1; + /* FIXME this doesn't free + * dl->devname */ + update->space = dl; + *dlp = dl->next; + break; + } + } + ddf->updates_pending = 1; + return; + } if (!all_ff(ddf->phys->entries[ent].guid)) return; ddf->phys->entries[ent] = pd->entries[0]; @@ -3217,7 +3451,7 @@ static void ddf_process_update(struct supertype *st, dprintf("len %d %d\n", update->len, ddf->conf_rec_len); mppe = __be16_to_cpu(ddf->anchor.max_primary_element_entries); - if (update->len != ddf->conf_rec_len * 512) + if ((unsigned)update->len != ddf->conf_rec_len * 512) return; vc = (struct vd_config*)update->buf; for (vcl = ddf->conflist; vcl ; vcl = vcl->next) @@ -3240,19 +3474,48 @@ static void ddf_process_update(struct supertype *st, memcpy(&vcl->conf, vc, update->len); vcl->lba_offset = (__u64*) &vcl->conf.phys_refnum[mppe]; + for (ent = 0; + ent < __be16_to_cpu(ddf->virt->populated_vdes); + ent++) + if (memcmp(vc->guid, ddf->virt->entries[ent].guid, + DDF_GUID_LEN) == 0) { + vcl->vcnum = ent; + break; + } ddf->conflist = vcl; } + /* Set DDF_Transition on all Failed devices - to help + * us detect those that are no longer in use + */ + for (pdnum = 0; pdnum < __be16_to_cpu(ddf->phys->used_pdes); pdnum++) + if (ddf->phys->entries[pdnum].state + & __be16_to_cpu(DDF_Failed)) + ddf->phys->entries[pdnum].state + |= __be16_to_cpu(DDF_Transition); /* Now make sure vlist is correct for each dl. */ for (dl = ddf->dlist; dl; dl = dl->next) { - int dn; - int vn = 0; + unsigned int dn; + unsigned int vn = 0; + int in_degraded = 0; for (vcl = ddf->conflist; vcl ; vcl = vcl->next) for (dn=0; dn < ddf->mppe ; dn++) if (vcl->conf.phys_refnum[dn] == dl->disk.refnum) { + int vstate; dprintf("dev %d has %p at %d\n", dl->pdnum, vcl, vn); + /* Clear the Transition flag */ + if (ddf->phys->entries[dl->pdnum].state + & __be16_to_cpu(DDF_Failed)) + ddf->phys->entries[dl->pdnum].state &= + ~__be16_to_cpu(DDF_Transition); + dl->vlist[vn++] = vcl; + vstate = ddf->virt->entries[vcl->vcnum].state + & DDF_state_mask; + if (vstate == DDF_state_degraded || + vstate == DDF_state_part_optimal) + in_degraded = 1; break; } while (vn < ddf->max_part) @@ -3260,8 +3523,14 @@ static void ddf_process_update(struct supertype *st, if (dl->vlist[0]) { ddf->phys->entries[dl->pdnum].type &= ~__cpu_to_be16(DDF_Global_Spare); - ddf->phys->entries[dl->pdnum].type |= - __cpu_to_be16(DDF_Active_in_VD); + if (!(ddf->phys->entries[dl->pdnum].type & + __cpu_to_be16(DDF_Active_in_VD))) { + ddf->phys->entries[dl->pdnum].type |= + __cpu_to_be16(DDF_Active_in_VD); + if (in_degraded) + ddf->phys->entries[dl->pdnum].state |= + __cpu_to_be16(DDF_Rebuilding); + } } if (dl->spare) { ddf->phys->entries[dl->pdnum].type &= @@ -3277,6 +3546,33 @@ static void ddf_process_update(struct supertype *st, DDF_Active_in_VD); } } + + /* Now remove any 'Failed' devices that are not part + * of any VD. They will have the Transition flag set. + * Once done, we need to update all dl->pdnum numbers. + */ + pd2 = 0; + for (pdnum = 0; pdnum < __be16_to_cpu(ddf->phys->used_pdes); pdnum++) + if ((ddf->phys->entries[pdnum].state + & __be16_to_cpu(DDF_Failed)) + && (ddf->phys->entries[pdnum].state + & __be16_to_cpu(DDF_Transition))) + /* skip this one */; + else if (pdnum == pd2) + pd2++; + else { + ddf->phys->entries[pd2] = ddf->phys->entries[pdnum]; + for (dl = ddf->dlist; dl; dl = dl->next) + if (dl->pdnum == (int)pdnum) + dl->pdnum = pd2; + pd2++; + } + ddf->phys->used_pdes = __cpu_to_be16(pd2); + while (pd2 < pdnum) { + memset(ddf->phys->entries[pd2].guid, 0xff, DDF_GUID_LEN); + pd2++; + } + ddf->updates_pending = 1; break; case DDF_SPARE_ASSIGN_MAGIC: @@ -3380,10 +3676,11 @@ static struct mdinfo *ddf_activate_spare(struct active_array *a, int is_global = 0; int is_dedicated = 0; struct extent *ex; - int j; + unsigned int j; /* If in this array, skip */ for (d2 = a->info.devs ; d2 ; d2 = d2->next) - if (d2->disk.major == dl->major && + if (d2->state_fd >= 0 && + d2->disk.major == dl->major && d2->disk.minor == dl->minor) { dprintf("%x:%x already in array\n", dl->major, dl->minor); break; @@ -3432,13 +3729,14 @@ static struct mdinfo *ddf_activate_spare(struct active_array *a, esize = ex[j].start - pos; if (esize >= a->info.component_size) break; - pos = ex[i].start + ex[i].size; - i++; - } while (ex[i-1].size); + pos = ex[j].start + ex[j].size; + j++; + } while (ex[j-1].size); free(ex); if (esize < a->info.component_size) { - dprintf("%x:%x has no room: %llu %llu\n", dl->major, dl->minor, + dprintf("%x:%x has no room: %llu %llu\n", + dl->major, dl->minor, esize, a->info.component_size); /* No room */ continue; @@ -3454,6 +3752,7 @@ static struct mdinfo *ddf_activate_spare(struct active_array *a, di->disk.major = dl->major; di->disk.minor = dl->minor; di->disk.state = 0; + di->recovery_start = 0; di->data_offset = pos; di->component_size = a->info.component_size; di->container_member = dl->pdnum; @@ -3495,7 +3794,8 @@ static struct mdinfo *ddf_activate_spare(struct active_array *a, } mu->buf = malloc(ddf->conf_rec_len * 512); - mu->len = ddf->conf_rec_len; + mu->len = ddf->conf_rec_len * 512; + mu->space = NULL; mu->next = *updates; vc = find_vdcr(ddf, a->info.container_member); memcpy(mu->buf, vc, ddf->conf_rec_len * 512); @@ -3512,15 +3812,35 @@ static struct mdinfo *ddf_activate_spare(struct active_array *a, } #endif /* MDASSEMBLE */ +static int ddf_level_to_layout(int level) +{ + switch(level) { + case 0: + case 1: + return 0; + case 5: + return ALGORITHM_LEFT_SYMMETRIC; + case 6: + return ALGORITHM_ROTATING_N_CONTINUE; + case 10: + return 0x102; + default: + return UnSet; + } +} + struct superswitch super_ddf = { #ifndef MDASSEMBLE .examine_super = examine_super_ddf, .brief_examine_super = brief_examine_super_ddf, + .brief_examine_subarrays = brief_examine_subarrays_ddf, + .export_examine_super = export_examine_super_ddf, .detail_super = detail_super_ddf, .brief_detail_super = brief_detail_super_ddf, .validate_geometry = validate_geometry_ddf, .write_init_super = write_init_super_ddf, .add_to_super = add_to_super_ddf, + .remove_from_super = remove_from_super_ddf, #endif .match_home = match_home_ddf, .uuid_from_super= uuid_from_super_ddf, @@ -3533,10 +3853,11 @@ struct superswitch super_ddf = { .load_super = load_super_ddf, .init_super = init_super_ddf, - .store_super = store_zero_ddf, + .store_super = store_super_ddf, .free_super = free_super_ddf, .match_metadata_desc = match_metadata_desc_ddf, .container_content = container_content_ddf, + .default_layout = ddf_level_to_layout, .external = 1, @@ -3550,4 +3871,5 @@ struct superswitch super_ddf = { .prepare_update = ddf_prepare_update, .activate_spare = ddf_activate_spare, #endif + .name = "ddf", };