X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fmdadm.git;a=blobdiff_plain;f=super-ddf.c;h=14f83304bde10c8f4ddff2bc17d5f4eb1d08dce5;hp=eb9ccbbba28afa4edf257cb92954dd3d8ffab419;hb=d23534e4646313a67296b295666d165a87bb2c92;hpb=3d2c4fc7b6bed924f8d5d1eae9a164b191e1b6da diff --git a/super-ddf.c b/super-ddf.c index eb9ccbbb..14f83304 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 @@ -423,10 +423,14 @@ struct ddf_super { unsigned long long size; /* sectors */ int pdnum; /* index in ->phys */ struct spare_assign *spare; + void *mdupdate; /* hold metadata update */ + + /* These fields used by auto-layout */ + int raiddisk; /* slot to fill in autolayout */ + __u64 esize; }; }; struct disk_data disk; - void *mdupdate; /* hold metadata update */ struct vcl *vlist[0]; /* max_part in size */ } *dlist, *add_list; }; @@ -446,7 +450,10 @@ static int calc_crc(void *buf, int len) newcrc = crc32(0, buf, len); ddf->crc = oldcrc; - return newcrc; + /* The crc is store (like everything) bigendian, so convert + * here for simplicity + */ + return __cpu_to_be32(newcrc); } static int load_ddf_header(int fd, unsigned long long lba, @@ -636,6 +643,7 @@ static int load_ddf_local(int fd, struct ddf_super *super, struct stat stb; char *conf; int i; + int confsec; int vnum; int max_virt_disks = __be16_to_cpu(super->active->max_vd_entries); unsigned long long dsize; @@ -686,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) { @@ -754,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) { @@ -774,24 +785,24 @@ static int load_super_ddf(struct supertype *st, int fd, /* 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)); @@ -829,6 +840,18 @@ static int load_super_ddf(struct supertype *st, int fd, return rv; } + if (st->subarray[0]) { + struct vcl *v; + + for (v = super->conflist; v; v = v->next) + if (v->vcnum == atoi(st->subarray)) + super->currentconf = v; + if (!super->currentconf) { + free(super); + return 1; + } + } + /* Should possibly check the sections .... */ st->sb = super; @@ -967,6 +990,15 @@ static int map_num1(struct num_mapping *map, int num) return map[i].num2; } +static int all_ff(char *guid) +{ + int i; + for (i = 0; i < DDF_GUID_LEN; i++) + if (guid[i] != (char)0xff) + return 0; + return 1; +} + #ifndef MDASSEMBLE static void print_guid(char *guid, int tstamp) { @@ -984,7 +1016,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) { + int i; struct vd_config *vc = &vcl->conf; if (calc_crc(vc, crl*512) != vc->crc) @@ -1018,8 +1051,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, @@ -1031,9 +1078,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); } } @@ -1045,6 +1092,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)); @@ -1068,40 +1116,44 @@ 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, + 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"); } } @@ -1125,8 +1177,9 @@ static void examine_super_ddf(struct supertype *st, char *homehost) 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 */ @@ -1134,9 +1187,48 @@ static void brief_examine_super_ddf(struct supertype *st) char nbuf[64]; getinfo_super_ddf(st, &info); fname_from_uuid(st, &info, nbuf, ':'); - printf("ARRAY /dev/ddf metadata=ddf UUID=%s\n", nbuf + 5); + + printf("ARRAY metadata=ddf UUID=%s\n", nbuf + 5); } +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; + 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]; + if (all_ff(ve->guid)) + continue; + memcpy(vcl.conf.guid, ve->guid, DDF_GUID_LEN); + ddf->currentconf =&vcl; + uuid_from_super_ddf(st, info.uuid); + fname_from_uuid(st, &info, nbuf1, ':'); + printf("ARRAY container=%s member=%d UUID=%s\n", + nbuf+5, i, nbuf1+5); + } +} + +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 @@ -1234,8 +1326,6 @@ static void uuid_from_super_ddf(struct supertype *st, int uuid[4]) sha1_init_ctx(&ctx); sha1_process_bytes(guid, DDF_GUID_LEN, &ctx); - if (vcl && vcl->conf.sec_elmnt_count > 1) - sha1_process_bytes(&vcl->conf.sec_elmnt_seq, 1, &ctx); sha1_finish_ctx(&ctx, buf); memcpy(uuid, buf, 4*4); } @@ -1273,9 +1363,10 @@ 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->reshape_active = 0; @@ -1297,6 +1388,8 @@ static void getinfo_super_ddf_bvd(struct supertype *st, struct mdinfo *info) struct ddf_super *ddf = st->sb; struct vcl *vc = ddf->currentconf; int cd = ddf->currentdev; + int j; + struct dl *dl; /* FIXME this returns BVD info - what if we want SVD ?? */ @@ -1309,6 +1402,7 @@ 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) { info->data_offset = __be64_to_cpu(vc->lba_offset[cd]); @@ -1318,8 +1412,15 @@ static void getinfo_super_ddf_bvd(struct supertype *st, struct mdinfo *info) info->component_size = __be64_to_cpu(vc->conf.blocks); } + for (dl = ddf->dlist; dl ; dl = dl->next) + if (dl->raiddisk == info->disk.raid_disk) + break; info->disk.major = 0; info->disk.minor = 0; + if (dl) { + info->disk.major = dl->major; + info->disk.minor = dl->minor; + } // info->disk.number = __be32_to_cpu(ddf->disk.refnum); // info->disk.raid_disk = find refnum in the table and use index; // info->disk.state = ???; @@ -1332,7 +1433,7 @@ static void getinfo_super_ddf_bvd(struct supertype *st, struct mdinfo *info) (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); @@ -1344,7 +1445,11 @@ static void getinfo_super_ddf_bvd(struct supertype *st, struct mdinfo *info) st->subarray); info->safe_mode_delay = 200; - info->name[0] = 0; + 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; } @@ -1421,7 +1526,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. @@ -1436,11 +1540,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, @@ -1488,13 +1589,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__); @@ -1503,6 +1599,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. @@ -1625,6 +1727,8 @@ static int init_super_ddf(struct supertype *st, memcpy(ddf->controller.product_id, "What Is My PID??", 16); memset(ddf->controller.pad, 0xff, 8); memset(ddf->controller.vendor_data, 0xff, 448); + if (homehost && strlen(homehost) < 440) + strcpy((char*)ddf->controller.vendor_data, homehost); if (posix_memalign((void**)&pd, 512, pdsize) != 0) { fprintf(stderr, Name ": %s could not allocate pd\n", __func__); @@ -1660,14 +1764,6 @@ static int init_super_ddf(struct supertype *st, return 1; } -static int all_ff(char *guid) -{ - int i; - for (i = 0; i < DDF_GUID_LEN; i++) - if (guid[i] != (char)0xff) - return 0; - return 1; -} static int chunk_to_shift(int chunksize) { return ffs(chunksize/512)-1; @@ -1702,20 +1798,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; } @@ -1750,11 +1851,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; } @@ -1920,7 +2021,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; @@ -1939,6 +2040,9 @@ static void add_to_super_ddf_bvd(struct supertype *st, * the phys_refnum and lba_offset for the newly created vd_config. * We might also want to update the type in the phys_disk * section. + * + * Alternately: fd == -1 and we have already chosen which device to + * use and recorded in dlist->raid_disk; */ struct dl *dl; struct ddf_super *ddf = st->sb; @@ -1949,10 +2053,16 @@ static void add_to_super_ddf_bvd(struct supertype *st, unsigned long long blocks, pos, esize; struct extent *ex; - for (dl = ddf->dlist; dl ; dl = dl->next) - if (dl->major == dk->major && - dl->minor == dk->minor) - break; + if (fd == -1) { + for (dl = ddf->dlist; dl ; dl = dl->next) + if (dl->raiddisk == dk->raid_disk) + break; + } else { + for (dl = ddf->dlist; dl ; dl = dl->next) + if (dl->major == dk->major && + dl->minor == dk->minor) + break; + } if (!dl || ! (dk->state & (1<vlist[i] = ddf->currentconf; - dl->fd = fd; - dl->devname = devname; + if (fd >= 0) + dl->fd = fd; + if (devname) + dl->devname = devname; /* Check how many working raid_disks, and if we can mark * array as optimal yet @@ -2024,7 +2136,7 @@ static void add_to_super_ddf_bvd(struct supertype *st, /* add a device to a container, either while creating it or while * expanding a pre-existing container */ -static void add_to_super_ddf(struct supertype *st, +static int add_to_super_ddf(struct supertype *st, mdu_disk_info_t *dk, int fd, char *devname) { struct ddf_super *ddf = st->sb; @@ -2038,7 +2150,7 @@ static void add_to_super_ddf(struct supertype *st, if (ddf->currentconf) { add_to_super_ddf_bvd(st, dk, fd, devname); - return; + return 0; } /* This is device numbered dk->number. We need to create @@ -2050,7 +2162,7 @@ static void add_to_super_ddf(struct supertype *st, fprintf(stderr, Name ": %s could allocate buffer for new disk, aborting\n", __func__); - abort(); + return 1; } dd->major = major(stb.st_rdev); dd->minor = minor(stb.st_rdev); @@ -2063,12 +2175,12 @@ static void 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(); + dd->disk.refnum = random32(); for (i = __be16_to_cpu(ddf->active->max_pd_entries) - 1; i >= 0; i--) if (ddf->phys->entries[i].refnum == dd->disk.refnum) @@ -2121,6 +2233,8 @@ static void add_to_super_ddf(struct supertype *st, ddf->dlist = dd; ddf->updates_pending = 1; } + + return 0; } /* @@ -2139,15 +2253,20 @@ static int __write_init_super_ddf(struct supertype *st, int do_close) struct dl *d; int n_config; int conf_size; - + int attempts = 0; + int successes = 0; unsigned long long size, sector; + /* try to write updated metadata, + * if we catch a failure move on to the next disk + */ for (d = ddf->dlist; d; d=d->next) { int fd = d->fd; if (fd < 0) continue; + attempts++; /* We need to fill in the primary, (secondary) and workspace * lba's in the headers, set their checksums, * Also checksum phys, virt.... @@ -2177,17 +2296,21 @@ static int __write_init_super_ddf(struct supertype *st, int do_close) sector = size - 16*1024*2; lseek64(fd, sector<<9, 0); - write(fd, &ddf->primary, 512); + if (write(fd, &ddf->primary, 512) < 0) + continue; ddf->controller.crc = calc_crc(&ddf->controller, 512); - write(fd, &ddf->controller, 512); + if (write(fd, &ddf->controller, 512) < 0) + continue; ddf->phys->crc = calc_crc(ddf->phys, ddf->pdsize); - write(fd, ddf->phys, ddf->pdsize); + if (write(fd, ddf->phys, ddf->pdsize) < 0) + continue; ddf->virt->crc = calc_crc(ddf->virt, ddf->vdsize); - write(fd, ddf->virt, ddf->vdsize); + if (write(fd, ddf->virt, ddf->vdsize) < 0) + continue; /* Now write lots of config records. */ n_config = ddf->max_part; @@ -2199,45 +2322,60 @@ static int __write_init_super_ddf(struct supertype *st, int do_close) if (c) { c->conf.crc = calc_crc(&c->conf, conf_size); - write(fd, &c->conf, conf_size); + if (write(fd, &c->conf, conf_size) < 0) + break; } else { 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; while (togo > sizeof(null_conf)-512) { - write(fd, null_aligned, sizeof(null_conf)-512); + if (write(fd, null_aligned, sizeof(null_conf)-512) < 0) + break; togo -= sizeof(null_conf)-512; } - write(fd, null_aligned, togo); + if (write(fd, null_aligned, togo) < 0) + break; } } + if (i <= n_config) + continue; d->disk.crc = calc_crc(&d->disk, 512); - write(fd, &d->disk, 512); + if (write(fd, &d->disk, 512) < 0) + continue; /* Maybe do the same for secondary */ lseek64(fd, (size-1)*512, SEEK_SET); - write(fd, &ddf->anchor, 512); - if (do_close) { - close(fd); + if (write(fd, &ddf->anchor, 512) < 0) + continue; + successes++; + } + + if (do_close) + for (d = ddf->dlist; d; d=d->next) { + close(d->fd); d->fd = -1; } - } - return 1; + + return attempts != successes; } 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)); @@ -2256,14 +2394,14 @@ 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! */ @@ -2283,6 +2421,96 @@ static __u64 avail_size_ddf(struct supertype *st, __u64 devsize) } #ifndef MDASSEMBLE + +static int reserve_space(struct supertype *st, int raiddisks, + unsigned long long size, int chunk, + unsigned long long *freesize) +{ + /* Find 'raiddisks' spare extents at least 'size' big (but + * only caring about multiples of 'chunk') and remember + * them. + * If the cannot be found, fail. + */ + struct dl *dl; + struct ddf_super *ddf = st->sb; + int cnt = 0; + + for (dl = ddf->dlist; dl ; dl=dl->next) { + dl->raiddisk = -1; + dl->esize = 0; + } + /* Now find largest extent on each device */ + for (dl = ddf->dlist ; dl ; dl=dl->next) { + struct extent *e = get_extents(ddf, dl); + unsigned long long pos = 0; + int i = 0; + int found = 0; + unsigned long long minsize = size; + + if (size == 0) + minsize = chunk; + + if (!e) + continue; + do { + unsigned long long esize; + esize = e[i].start - pos; + if (esize >= minsize) { + found = 1; + minsize = esize; + } + pos = e[i].start + e[i].size; + i++; + } while (e[i-1].size); + if (found) { + cnt++; + dl->esize = minsize; + } + free(e); + } + if (cnt < raiddisks) { + fprintf(stderr, Name ": not enough devices with space to create array.\n"); + return 0; /* No enough free spaces large enough */ + } + if (size == 0) { + /* choose the largest size of which there are at least 'raiddisk' */ + for (dl = ddf->dlist ; dl ; dl=dl->next) { + struct dl *dl2; + if (dl->esize <= size) + continue; + /* This is bigger than 'size', see if there are enough */ + cnt = 0; + for (dl2 = dl; dl2 ; dl2=dl2->next) + if (dl2->esize >= dl->esize) + cnt++; + if (cnt >= raiddisks) + size = dl->esize; + } + if (chunk) { + size = size / chunk; + size *= chunk; + } + *freesize = size; + if (size < 32) { + fprintf(stderr, Name ": not enough spare devices to create array.\n"); + return 0; + } + } + /* We have a 'size' of which there are enough spaces. + * We simply do a first-fit */ + cnt = 0; + for (dl = ddf->dlist ; dl && cnt < raiddisks ; dl=dl->next) { + if (dl->esize < size) + continue; + + dl->raiddisk = cnt; + cnt++; + } + return 1; +} + + + static int validate_geometry_ddf_container(struct supertype *st, int level, int layout, int raiddisks, @@ -2321,27 +2549,42 @@ static int validate_geometry_ddf(struct supertype *st, verbose); } - if (st->sb) { - /* A container has already been opened, so we are - * creating in there. Maybe a BVD, maybe an SVD. - * Should make a distinction one day. - */ - return validate_geometry_ddf_bvd(st, level, layout, raiddisks, - chunk, size, dev, freesize, - verbose); - } if (!dev) { /* Initial sanity check. Exclude illegal levels. */ int i; 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) { + /* --create was given a container to create in. + * So we need to check that there are enough + * free spaces and return the amount of space. + * We may as well remember which drives were + * chosen so that add_to_super/getinfo_super + * can return them. + */ + return reserve_space(st, raiddisks, size, chunk, freesize); + } return 1; } + if (st->sb) { + /* A container has already been opened, so we are + * creating in there. Maybe a BVD, maybe an SVD. + * Should make a distinction one day. + */ + return validate_geometry_ddf_bvd(st, level, layout, raiddisks, + chunk, size, dev, freesize, + verbose); + } /* This is the first device for the array. * If it is a container, we read it in and do automagic allocations, * no other devices should be given. @@ -2365,7 +2608,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; } @@ -2437,6 +2680,8 @@ validate_geometry_ddf_container(struct supertype *st, close(fd); *freesize = avail_size_ddf(st, ldsize >> 9); + if (*freesize == 0) + return 0; return 1; } @@ -2455,8 +2700,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; @@ -2543,8 +2791,14 @@ static int load_super_ddf_all(struct supertype *st, int fd, int seq; char nm[20]; int dfd; + int devnum = fd2devnum(fd); + enum sysfs_read_flags flags; - sra = sysfs_read(fd, 0, GET_LEVEL|GET_VERSION|GET_DEVS|GET_STATE); + flags = GET_LEVEL|GET_VERSION|GET_DEVS|GET_STATE; + if (mdmon_running(devnum)) + flags |= SKIP_GONE_DEVS; + + sra = sysfs_read(fd, 0, flags); if (!sra) return 1; if (sra->array.major_version != -1 || @@ -2638,6 +2892,7 @@ static struct mdinfo *container_content_ddf(struct supertype *st) for (vc = ddf->conflist ; vc ; vc=vc->next) { int i; + int j; struct mdinfo *this; this = malloc(sizeof(*this)); memset(this, 0, sizeof(*this)); @@ -2666,10 +2921,13 @@ 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, 32); - this->name[32]=0; + 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; memset(this->uuid, 0, sizeof(this->uuid)); this->component_size = __be64_to_cpu(vc->conf.blocks); @@ -2697,7 +2955,8 @@ static struct mdinfo *container_content_ddf(struct supertype *st) 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; dev = malloc(sizeof(*dev)); memset(dev, 0, sizeof(*dev)); @@ -2709,6 +2968,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]); @@ -2720,12 +2980,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; @@ -2797,7 +3067,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) @@ -2809,9 +3079,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; @@ -2819,7 +3089,7 @@ 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; } @@ -3056,6 +3326,8 @@ static void ddf_process_update(struct supertype *st, mppe * (sizeof(__u32) + sizeof(__u64))); } else { /* A new VD_CONF */ + if (!update->space) + return; vcl = update->space; update->space = NULL; vcl->next = ddf->conflist; @@ -3116,9 +3388,10 @@ static void ddf_prepare_update(struct supertype *st, struct ddf_super *ddf = st->sb; __u32 *magic = (__u32*)update->buf; if (*magic == DDF_VD_CONF_MAGIC) - posix_memalign(&update->space, 512, + if (posix_memalign(&update->space, 512, offsetof(struct vcl, conf) - + ddf->conf_rec_len * 512); + + ddf->conf_rec_len * 512) != 0) + update->space = NULL; } /* @@ -3267,12 +3540,15 @@ static struct mdinfo *ddf_activate_spare(struct active_array *a, /* Cool, we have a device with some space at pos */ di = malloc(sizeof(*di)); + if (!di) + continue; memset(di, 0, sizeof(*di)); di->disk.number = i; di->disk.raid_disk = i; 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; @@ -3299,8 +3575,21 @@ static struct mdinfo *ddf_activate_spare(struct active_array *a, * phys_refnum and lba_offset values */ mu = malloc(sizeof(*mu)); + if (mu && posix_memalign(&mu->space, 512, sizeof(struct vcl)) != 0) { + free(mu); + mu = NULL; + } + if (!mu) { + while (rv) { + struct mdinfo *n = rv->next; + + free(rv); + rv = n; + } + return NULL; + } + mu->buf = malloc(ddf->conf_rec_len * 512); - posix_memalign(&mu->space, 512, sizeof(struct vcl)); mu->len = ddf->conf_rec_len; mu->next = *updates; vc = find_vdcr(ddf, a->info.container_member); @@ -3318,10 +3607,29 @@ 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, @@ -3339,10 +3647,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, @@ -3356,4 +3665,5 @@ struct superswitch super_ddf = { .prepare_update = ddf_prepare_update, .activate_spare = ddf_activate_spare, #endif + .name = "ddf", };