X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fmdadm.git;a=blobdiff_plain;f=super-intel.c;h=642ca268b627c4656c567888ebb42de8fab851a6;hp=2c1bf05865f15d1b861b62f8acbe403dae8d1b7a;hb=e03640bda562df11b60ceaaa40a56425f358090e;hpb=7d0c5e24a5b2425abcfb778f9c31db39f53703c8 diff --git a/super-intel.c b/super-intel.c index 2c1bf058..642ca268 100644 --- a/super-intel.c +++ b/super-intel.c @@ -95,22 +95,29 @@ #define IMSM_MAX_DEVICES 255 struct imsm_disk { __u8 serial[MAX_RAID_SERIAL_LEN];/* 0xD8 - 0xE7 ascii serial number */ - __u32 total_blocks; /* 0xE8 - 0xEB total blocks */ + __u32 total_blocks_lo; /* 0xE8 - 0xEB total blocks lo */ __u32 scsi_id; /* 0xEC - 0xEF scsi ID */ #define SPARE_DISK __cpu_to_le32(0x01) /* Spare */ #define CONFIGURED_DISK __cpu_to_le32(0x02) /* Member of some RaidDev */ #define FAILED_DISK __cpu_to_le32(0x04) /* Permanent failure */ __u32 status; /* 0xF0 - 0xF3 */ __u32 owner_cfg_num; /* which config 0,1,2... owns this disk */ -#define IMSM_DISK_FILLERS 4 - __u32 filler[IMSM_DISK_FILLERS]; /* 0xF4 - 0x107 MPB_DISK_FILLERS for future expansion */ + __u32 total_blocks_hi; /* 0xF4 - 0xF5 total blocks hi */ +#define IMSM_DISK_FILLERS 3 + __u32 filler[IMSM_DISK_FILLERS]; /* 0xF5 - 0x107 MPB_DISK_FILLERS for future expansion */ }; +/* map selector for map managment + */ +#define MAP_0 0 +#define MAP_1 1 +#define MAP_X -1 + /* RAID map configuration infos. */ struct imsm_map { - __u32 pba_of_lba0; /* start address of partition */ - __u32 blocks_per_member;/* blocks per member */ - __u32 num_data_stripes; /* number of data stripes */ + __u32 pba_of_lba0_lo; /* start address of partition */ + __u32 blocks_per_member_lo;/* blocks per member */ + __u32 num_data_stripes_lo; /* number of data stripes */ __u16 blocks_per_strip; __u8 map_state; /* Normal, Uninitialized, Degraded, Failed */ #define IMSM_T_STATE_NORMAL 0 @@ -125,7 +132,10 @@ struct imsm_map { __u8 num_domains; /* number of parity domains */ __u8 failed_disk_num; /* valid only when state is degraded */ __u8 ddf; - __u32 filler[7]; /* expansion area */ + __u32 pba_of_lba0_hi; + __u32 blocks_per_member_hi; + __u32 num_data_stripes_hi; + __u32 filler[4]; /* expansion area */ #define IMSM_ORD_REBUILD (1 << 24) __u32 disk_ord_tbl[1]; /* disk_ord_tbl[num_members], * top byte contains some flags @@ -234,6 +244,12 @@ static char *map_state_str[] = { "normal", "uninitialized", "degraded", "failed" #define GEN_MIGR_AREA_SIZE 2048 /* General Migration Copy Area size in blocks */ +#define MIGR_REC_BUF_SIZE 512 /* size of migr_record i/o buffer */ +#define MIGR_REC_POSITION 512 /* migr_record position offset on disk, + * MIGR_REC_BUF_SIZE <= MIGR_REC_POSITION + */ + + #define UNIT_SRC_NORMAL 0 /* Source data for curr_migr_unit must * be recovered using srcMap */ #define UNIT_SRC_IN_CP_AREA 1 /* Source data for curr_migr_unit has @@ -265,6 +281,22 @@ struct migr_record { * (for recovered migrations) */ } __attribute__ ((__packed__)); +struct md_list { + /* usage marker: + * 1: load metadata + * 2: metadata does not match + * 4: already checked + */ + int used; + char *devname; + int found; + int container; + dev_t st_rdev; + struct md_list *next; +}; + +#define pr_vrb(fmt, arg...) (void) (verbose && fprintf(stderr, Name fmt, ##arg)) + static __u8 migr_type(struct imsm_dev *dev) { if (dev->vol.migr_type == MIGR_VERIFY && @@ -290,7 +322,7 @@ static void set_migr_type(struct imsm_dev *dev, __u8 migr_type) static unsigned int sector_count(__u32 bytes) { - return ((bytes + (512-1)) & (~(512-1))) / 512; + return ROUND_UP(bytes, 512) / 512; } static unsigned int mpb_sectors(struct imsm_super *mpb) @@ -325,12 +357,15 @@ struct intel_super { void *migr_rec_buf; /* buffer for I/O operations */ struct migr_record *migr_rec; /* migration record */ }; + int clean_migration_record_by_mdmon; /* when reshape is switched to next + array, it indicates that mdmon is allowed to clean migration + record */ size_t len; /* size of the 'buf' allocation */ void *next_buf; /* for realloc'ing buf from the manager */ size_t next_len; int updates_pending; /* count of pending updates for mdmon */ int current_vol; /* index of raid device undergoing creation */ - __u32 create_offset; /* common start for 'current_vol' */ + unsigned long long create_offset; /* common start for 'current_vol' */ __u32 random; /* random data for seeding new family numbers */ struct intel_dev *devlist; struct dl { @@ -662,21 +697,30 @@ struct imsm_map *get_imsm_map(struct imsm_dev *dev, int second_map) { /* A device can have 2 maps if it is in the middle of a migration. * If second_map is: - * 0 - we return the first map - * 1 - we return the second map if it exists, else NULL - * -1 - we return the second map if it exists, else the first + * MAP_0 - we return the first map + * MAP_1 - we return the second map if it exists, else NULL + * MAP_X - we return the second map if it exists, else the first */ struct imsm_map *map = &dev->vol.map[0]; + struct imsm_map *map2 = NULL; - if (second_map == 1 && !dev->vol.migr_state) - return NULL; - else if (second_map == 1 || - (second_map < 0 && dev->vol.migr_state)) { - void *ptr = map; + if (dev->vol.migr_state) + map2 = (void *)map + sizeof_imsm_map(map); - return ptr + sizeof_imsm_map(map); - } else - return map; + switch (second_map) { + case MAP_0: + break; + case MAP_1: + map = map2; + break; + case MAP_X: + if (map2) + map = map2; + break; + default: + map = NULL; + } + return map; } @@ -686,13 +730,13 @@ struct imsm_map *get_imsm_map(struct imsm_dev *dev, int second_map) static size_t sizeof_imsm_dev(struct imsm_dev *dev, int migr_state) { size_t size = sizeof(*dev) - sizeof(struct imsm_map) + - sizeof_imsm_map(get_imsm_map(dev, 0)); + sizeof_imsm_map(get_imsm_map(dev, MAP_0)); /* migrating means an additional map */ if (dev->vol.migr_state) - size += sizeof_imsm_map(get_imsm_map(dev, 1)); + size += sizeof_imsm_map(get_imsm_map(dev, MAP_1)); else if (migr_state) - size += sizeof_imsm_map(get_imsm_map(dev, 0)); + size += sizeof_imsm_map(get_imsm_map(dev, MAP_0)); return size; } @@ -746,9 +790,9 @@ static struct imsm_dev *get_imsm_dev(struct intel_super *super, __u8 index) /* * for second_map: - * == 0 get first map - * == 1 get second map - * == -1 than get map according to the current migr_state + * == MAP_0 get first map + * == MAP_1 get second map + * == MAP_X than get map according to the current migr_state */ static __u32 get_imsm_ord_tbl_ent(struct imsm_dev *dev, int slot, @@ -819,7 +863,7 @@ static int count_memberships(struct dl *dl, struct intel_super *super) for (i = 0; i < super->anchor->num_raid_devs; i++) { struct imsm_dev *dev = get_imsm_dev(super, i); - struct imsm_map *map = get_imsm_map(dev, 0); + struct imsm_map *map = get_imsm_map(dev, MAP_0); if (get_imsm_disk_slot(map, dl->index) >= 0) memberships++; @@ -830,6 +874,69 @@ static int count_memberships(struct dl *dl, struct intel_super *super) static __u32 imsm_min_reserved_sectors(struct intel_super *super); +static int split_ull(unsigned long long n, __u32 *lo, __u32 *hi) +{ + if (lo == 0 || hi == 0) + return 1; + *lo = __le32_to_cpu((unsigned)n); + *hi = __le32_to_cpu((unsigned)(n >> 32)); + return 0; +} + +static unsigned long long join_u32(__u32 lo, __u32 hi) +{ + return (unsigned long long)__le32_to_cpu(lo) | + (((unsigned long long)__le32_to_cpu(hi)) << 32); +} + +static unsigned long long total_blocks(struct imsm_disk *disk) +{ + if (disk == NULL) + return 0; + return join_u32(disk->total_blocks_lo, disk->total_blocks_hi); +} + +static unsigned long long pba_of_lba0(struct imsm_map *map) +{ + if (map == NULL) + return 0; + return join_u32(map->pba_of_lba0_lo, map->pba_of_lba0_hi); +} + +static unsigned long long blocks_per_member(struct imsm_map *map) +{ + if (map == NULL) + return 0; + return join_u32(map->blocks_per_member_lo, map->blocks_per_member_hi); +} + +static unsigned long long num_data_stripes(struct imsm_map *map) +{ + if (map == NULL) + return 0; + return join_u32(map->num_data_stripes_lo, map->num_data_stripes_hi); +} + +static void set_total_blocks(struct imsm_disk *disk, unsigned long long n) +{ + split_ull(n, &disk->total_blocks_lo, &disk->total_blocks_hi); +} + +static void set_pba_of_lba0(struct imsm_map *map, unsigned long long n) +{ + split_ull(n, &map->pba_of_lba0_lo, &map->pba_of_lba0_hi); +} + +static void set_blocks_per_member(struct imsm_map *map, unsigned long long n) +{ + split_ull(n, &map->blocks_per_member_lo, &map->blocks_per_member_hi); +} + +static void set_num_data_stripes(struct imsm_map *map, unsigned long long n) +{ + split_ull(n, &map->num_data_stripes_lo, &map->num_data_stripes_hi); +} + static struct extent *get_extents(struct intel_super *super, struct dl *dl) { /* find a list of used extents on the given physical device */ @@ -854,11 +961,11 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl) for (i = 0; i < super->anchor->num_raid_devs; i++) { struct imsm_dev *dev = get_imsm_dev(super, i); - struct imsm_map *map = get_imsm_map(dev, 0); + struct imsm_map *map = get_imsm_map(dev, MAP_0); if (get_imsm_disk_slot(map, dl->index) >= 0) { - e->start = __le32_to_cpu(map->pba_of_lba0); - e->size = __le32_to_cpu(map->blocks_per_member); + e->start = pba_of_lba0(map); + e->size = blocks_per_member(map); e++; } } @@ -871,10 +978,9 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl) */ if (memberships) { struct extent *last = &rv[memberships - 1]; - __u32 remainder; + unsigned long long remainder; - remainder = __le32_to_cpu(dl->disk.total_blocks) - - (last->start + last->size); + remainder = total_blocks(&dl->disk) - (last->start + last->size); /* round down to 1k block to satisfy precision of the kernel * 'size' interface */ @@ -885,7 +991,7 @@ static struct extent *get_extents(struct intel_super *super, struct dl *dl) if (reservation > remainder) reservation = remainder; } - e->start = __le32_to_cpu(dl->disk.total_blocks) - reservation; + e->start = total_blocks(&dl->disk) - reservation; e->size = 0; return rv; } @@ -914,7 +1020,7 @@ static __u32 imsm_reserved_sectors(struct intel_super *super, struct dl *dl) for (i = 0; e[i].size; i++) continue; - rv = __le32_to_cpu(dl->disk.total_blocks) - e[i].start; + rv = total_blocks(&dl->disk) - e[i].start; free(e); @@ -944,7 +1050,8 @@ static __u32 imsm_min_reserved_sectors(struct intel_super *super) { struct extent *e; int i; - __u32 min_active, remainder; + unsigned long long min_active; + __u32 remainder; __u32 rv = MPB_SECTOR_CNT + IMSM_RESERVED_SECTORS; struct dl *dl, *dl_min = NULL; @@ -955,9 +1062,10 @@ static __u32 imsm_min_reserved_sectors(struct intel_super *super) for (dl = super->disks; dl; dl = dl->next) { if (dl->index < 0) continue; - if (dl->disk.total_blocks < min_active || min_active == 0) { + unsigned long long blocks = total_blocks(&dl->disk); + if (blocks < min_active || min_active == 0) { dl_min = dl; - min_active = dl->disk.total_blocks; + min_active = blocks; } } if (!dl_min) @@ -989,7 +1097,6 @@ static unsigned long long min_acceptable_spare_size_imsm(struct supertype *st) struct extent *e; int i; unsigned long long rv = 0; - __u32 reservation; if (!super) return rv; @@ -1007,7 +1114,6 @@ static unsigned long long min_acceptable_spare_size_imsm(struct supertype *st) continue; if (i > 0) rv = e[i-1].start + e[i-1].size; - reservation = __le32_to_cpu(dl->disk.total_blocks) - e[i].start; free(e); /* add the amount of space needed for metadata */ @@ -1016,6 +1122,8 @@ static unsigned long long min_acceptable_spare_size_imsm(struct supertype *st) return rv * 512; } +static int is_gen_migration(struct imsm_dev *dev); + #ifndef MDASSEMBLE static __u64 blocks_per_migr_unit(struct intel_super *super, struct imsm_dev *dev); @@ -1027,8 +1135,8 @@ static void print_imsm_dev(struct intel_super *super, { __u64 sz; int slot, i; - struct imsm_map *map = get_imsm_map(dev, 0); - struct imsm_map *map2 = get_imsm_map(dev, 1); + struct imsm_map *map = get_imsm_map(dev, MAP_0); + struct imsm_map *map2 = get_imsm_map(dev, MAP_1); __u32 ord; printf("\n"); @@ -1044,14 +1152,14 @@ static void print_imsm_dev(struct intel_super *super, printf("\n"); printf(" Slots : ["); for (i = 0; i < map->num_members; i++) { - ord = get_imsm_ord_tbl_ent(dev, i, 0); + ord = get_imsm_ord_tbl_ent(dev, i, MAP_0); printf("%s", ord & IMSM_ORD_REBUILD ? "_" : "U"); } printf("]"); if (map2) { printf(" <-- ["); for (i = 0; i < map2->num_members; i++) { - ord = get_imsm_ord_tbl_ent(dev, i, 1); + ord = get_imsm_ord_tbl_ent(dev, i, MAP_1); printf("%s", ord & IMSM_ORD_REBUILD ? "_" : "U"); } printf("]"); @@ -1065,7 +1173,7 @@ static void print_imsm_dev(struct intel_super *super, printf("\n"); slot = get_imsm_disk_slot(map, disk_idx); if (slot >= 0) { - ord = get_imsm_ord_tbl_ent(dev, slot, -1); + ord = get_imsm_ord_tbl_ent(dev, slot, MAP_X); printf(" This Slot : %d%s\n", slot, ord & IMSM_ORD_REBUILD ? " (out-of-sync)" : ""); } else @@ -1075,13 +1183,13 @@ static void print_imsm_dev(struct intel_super *super, sz += __le32_to_cpu(dev->size_low); printf(" Array Size : %llu%s\n", (unsigned long long)sz, human_size(sz * 512)); - sz = __le32_to_cpu(map->blocks_per_member); + sz = blocks_per_member(map); printf(" Per Dev Size : %llu%s\n", (unsigned long long)sz, human_size(sz * 512)); - printf(" Sector Offset : %u\n", - __le32_to_cpu(map->pba_of_lba0)); - printf(" Num Stripes : %u\n", - __le32_to_cpu(map->num_data_stripes)); + printf(" Sector Offset : %llu\n", + pba_of_lba0(map)); + printf(" Num Stripes : %llu\n", + num_data_stripes(map)); printf(" Chunk Size : %u KiB", __le16_to_cpu(map->blocks_per_strip) / 2); if (map2) @@ -1109,12 +1217,16 @@ static void print_imsm_dev(struct intel_super *super, printf("idle\n"); printf(" Map State : %s", map_state_str[map->map_state]); if (dev->vol.migr_state) { - struct imsm_map *map = get_imsm_map(dev, 1); + struct imsm_map *map = get_imsm_map(dev, MAP_1); printf(" <-- %s", map_state_str[map->map_state]); - printf("\n Checkpoint : %u (%llu)", - __le32_to_cpu(dev->vol.curr_migr_unit), - (unsigned long long)blocks_per_migr_unit(super, dev)); + printf("\n Checkpoint : %u ", + __le32_to_cpu(dev->vol.curr_migr_unit)); + if ((is_gen_migration(dev)) && ((slot > 1) || (slot < 0))) + printf("(N/A)"); + else + printf("(%llu)", (unsigned long long) + blocks_per_migr_unit(super, dev)); } printf("\n"); printf(" Dirty State : %s\n", dev->vol.dirty ? "dirty" : "clean"); @@ -1138,13 +1250,11 @@ static void print_imsm_disk(struct imsm_disk *disk, int index, __u32 reserved) is_configured(disk) ? " active" : "", is_failed(disk) ? " failed" : ""); printf(" Id : %08x\n", __le32_to_cpu(disk->scsi_id)); - sz = __le32_to_cpu(disk->total_blocks) - reserved; + sz = total_blocks(disk) - reserved; printf(" Usable Size : %llu%s\n", (unsigned long long)sz, human_size(sz * 512)); } -static int is_gen_migration(struct imsm_dev *dev); - void examine_migr_rec_imsm(struct intel_super *super) { struct migr_record *migr_rec = super->migr_rec; @@ -1153,11 +1263,19 @@ void examine_migr_rec_imsm(struct intel_super *super) for (i = 0; i < mpb->num_raid_devs; i++) { struct imsm_dev *dev = __get_imsm_dev(mpb, i); + struct imsm_map *map; + int slot = -1; + if (is_gen_migration(dev) == 0) continue; printf("\nMigration Record Information:"); - if (super->disks->index > 1) { + + /* first map under migration */ + map = get_imsm_map(dev, MAP_0); + if (map) + slot = get_imsm_disk_slot(map, super->disks->index); + if ((map == NULL) || (slot > 1) || (slot < 0)) { printf(" Empty\n "); printf("Examine one of first two disks in array\n"); break; @@ -1574,8 +1692,8 @@ static int ahci_enumerate_ports(const char *hba_path, int port_count, int host_b printf(" (%.*s)\n", MAX_RAID_SERIAL_LEN, buf); else printf(" ()\n"); + close(fd); } - close(fd); free(path); path = NULL; } @@ -1842,7 +1960,7 @@ get_imsm_numerical_version(struct imsm_super *mpb, int *m, int *p) static __u32 migr_strip_blocks_resync(struct imsm_dev *dev) { /* migr_strip_size when repairing or initializing parity */ - struct imsm_map *map = get_imsm_map(dev, 0); + struct imsm_map *map = get_imsm_map(dev, MAP_0); __u32 chunk = __le32_to_cpu(map->blocks_per_strip); switch (get_imsm_raid_level(map)) { @@ -1860,7 +1978,7 @@ static __u32 migr_strip_blocks_rebuild(struct imsm_dev *dev) * this is different than migr_strip_size_resync(), but it's good * to be compatible */ - struct imsm_map *map = get_imsm_map(dev, 1); + struct imsm_map *map = get_imsm_map(dev, MAP_1); __u32 chunk = __le32_to_cpu(map->blocks_per_strip); switch (get_imsm_raid_level(map)) { @@ -1879,8 +1997,8 @@ static __u32 migr_strip_blocks_rebuild(struct imsm_dev *dev) static __u32 num_stripes_per_unit_resync(struct imsm_dev *dev) { - struct imsm_map *lo = get_imsm_map(dev, 0); - struct imsm_map *hi = get_imsm_map(dev, 1); + struct imsm_map *lo = get_imsm_map(dev, MAP_0); + struct imsm_map *hi = get_imsm_map(dev, MAP_1); __u32 lo_chunk = __le32_to_cpu(lo->blocks_per_strip); __u32 hi_chunk = __le32_to_cpu(hi->blocks_per_strip); @@ -1889,11 +2007,11 @@ static __u32 num_stripes_per_unit_resync(struct imsm_dev *dev) static __u32 num_stripes_per_unit_rebuild(struct imsm_dev *dev) { - struct imsm_map *lo = get_imsm_map(dev, 0); + struct imsm_map *lo = get_imsm_map(dev, MAP_0); int level = get_imsm_raid_level(lo); if (level == 1 || level == 10) { - struct imsm_map *hi = get_imsm_map(dev, 1); + struct imsm_map *hi = get_imsm_map(dev, MAP_1); return hi->num_domains; } else @@ -1922,7 +2040,7 @@ static __u8 imsm_num_data_members(struct imsm_dev *dev, int second_map) static __u32 parity_segment_depth(struct imsm_dev *dev) { - struct imsm_map *map = get_imsm_map(dev, 0); + struct imsm_map *map = get_imsm_map(dev, MAP_0); __u32 chunk = __le32_to_cpu(map->blocks_per_strip); switch(get_imsm_raid_level(map)) { @@ -1938,7 +2056,7 @@ static __u32 parity_segment_depth(struct imsm_dev *dev) static __u32 map_migr_block(struct imsm_dev *dev, __u32 block) { - struct imsm_map *map = get_imsm_map(dev, 1); + struct imsm_map *map = get_imsm_map(dev, MAP_1); __u32 chunk = __le32_to_cpu(map->blocks_per_strip); __u32 strip = block / chunk; @@ -1977,7 +2095,7 @@ static __u64 blocks_per_migr_unit(struct intel_super *super, case MIGR_VERIFY: case MIGR_REPAIR: case MIGR_INIT: { - struct imsm_map *map = get_imsm_map(dev, 0); + struct imsm_map *map = get_imsm_map(dev, MAP_0); __u32 stripes_per_unit; __u32 blocks_per_unit; __u32 parity_depth; @@ -1993,7 +2111,7 @@ static __u64 blocks_per_migr_unit(struct intel_super *super, */ stripes_per_unit = num_stripes_per_unit_resync(dev); migr_chunk = migr_strip_blocks_resync(dev); - disks = imsm_num_data_members(dev, 0); + disks = imsm_num_data_members(dev, MAP_0); blocks_per_unit = stripes_per_unit * migr_chunk * disks; stripe = __le16_to_cpu(map->blocks_per_strip) * disks; segment = blocks_per_unit / stripe; @@ -2047,13 +2165,14 @@ static int read_imsm_migr_rec(int fd, struct intel_super *super) unsigned long long dsize; get_dev_size(fd, NULL, &dsize); - if (lseek64(fd, dsize - 512, SEEK_SET) < 0) { + if (lseek64(fd, dsize - MIGR_REC_POSITION, SEEK_SET) < 0) { fprintf(stderr, Name ": Cannot seek to anchor block: %s\n", strerror(errno)); goto out; } - if (read(fd, super->migr_rec_buf, 512) != 512) { + if (read(fd, super->migr_rec_buf, MIGR_REC_BUF_SIZE) != + MIGR_REC_BUF_SIZE) { fprintf(stderr, Name ": Cannot read migr record block: %s\n", strerror(errno)); @@ -2065,6 +2184,19 @@ out: return ret_val; } +static struct imsm_dev *imsm_get_device_during_migration( + struct intel_super *super) +{ + + struct intel_dev *dv; + + for (dv = super->devlist; dv; dv = dv->next) { + if (is_gen_migration(dv->dev)) + return dv->dev; + } + return NULL; +} + /******************************************************************************* * Function: load_imsm_migr_rec * Description: Function reads imsm migration record (it is stored at the last @@ -2075,6 +2207,7 @@ out: * Returns: * 0 : success * -1 : fail + * -2 : no migration in progress ******************************************************************************/ static int load_imsm_migr_rec(struct intel_super *super, struct mdinfo *info) { @@ -2083,13 +2216,31 @@ static int load_imsm_migr_rec(struct intel_super *super, struct mdinfo *info) char nm[30]; int retval = -1; int fd = -1; + struct imsm_dev *dev; + struct imsm_map *map = NULL; + int slot = -1; + + /* find map under migration */ + dev = imsm_get_device_during_migration(super); + /* nothing to load,no migration in progress? + */ + if (dev == NULL) + return -2; + map = get_imsm_map(dev, MAP_0); if (info) { for (sd = info->devs ; sd ; sd = sd->next) { + /* skip spare and failed disks + */ + if (sd->disk.raid_disk < 0) + continue; /* read only from one of the first two slots */ - if ((sd->disk.raid_disk > 1) || - (sd->disk.raid_disk < 0)) + if (map) + slot = get_imsm_disk_slot(map, + sd->disk.raid_disk); + if ((map == NULL) || (slot > 1) || (slot < 0)) continue; + sprintf(nm, "%d:%d", sd->disk.major, sd->disk.minor); fd = dev_open(nm, O_RDONLY); if (fd >= 0) @@ -2098,8 +2249,14 @@ static int load_imsm_migr_rec(struct intel_super *super, struct mdinfo *info) } if (fd < 0) { for (dl = super->disks; dl; dl = dl->next) { + /* skip spare and failed disks + */ + if (dl->index < 0) + continue; /* read only from one of the first two slots */ - if (dl->index > 1) + if (map) + slot = get_imsm_disk_slot(map, dl->index); + if ((map == NULL) || (slot > 1) || (slot < 0)) continue; sprintf(nm, "%d:%d", dl->major, dl->minor); fd = dev_open(nm, O_RDONLY); @@ -2183,23 +2340,45 @@ static int write_imsm_migr_rec(struct supertype *st) struct dl *sd; int len; struct imsm_update_general_migration_checkpoint *u; + struct imsm_dev *dev; + struct imsm_map *map = NULL; + + /* find map under migration */ + dev = imsm_get_device_during_migration(super); + /* if no migration, write buffer anyway to clear migr_record + * on disk based on first available device + */ + if (dev == NULL) + dev = get_imsm_dev(super, super->current_vol < 0 ? 0 : + super->current_vol); + + map = get_imsm_map(dev, MAP_0); for (sd = super->disks ; sd ; sd = sd->next) { + int slot = -1; + + /* skip failed and spare devices */ + if (sd->index < 0) + continue; /* write to 2 first slots only */ - if ((sd->index < 0) || (sd->index > 1)) + if (map) + slot = get_imsm_disk_slot(map, sd->index); + if ((map == NULL) || (slot > 1) || (slot < 0)) continue; + sprintf(nm, "%d:%d", sd->major, sd->minor); fd = dev_open(nm, O_RDWR); if (fd < 0) continue; get_dev_size(fd, NULL, &dsize); - if (lseek64(fd, dsize - 512, SEEK_SET) < 0) { + if (lseek64(fd, dsize - MIGR_REC_POSITION, SEEK_SET) < 0) { fprintf(stderr, Name ": Cannot seek to anchor block: %s\n", strerror(errno)); goto out; } - if (write(fd, super->migr_rec_buf, 512) != 512) { + if (write(fd, super->migr_rec_buf, MIGR_REC_BUF_SIZE) != + MIGR_REC_BUF_SIZE) { fprintf(stderr, Name ": Cannot write migr record block: %s\n", strerror(errno)); @@ -2266,8 +2445,8 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, struct intel_super *super = st->sb; struct migr_record *migr_rec = super->migr_rec; struct imsm_dev *dev = get_imsm_dev(super, super->current_vol); - struct imsm_map *map = get_imsm_map(dev, 0); - struct imsm_map *prev_map = get_imsm_map(dev, 1); + struct imsm_map *map = get_imsm_map(dev, MAP_0); + struct imsm_map *prev_map = get_imsm_map(dev, MAP_1); struct imsm_map *map_to_analyse = map; struct dl *dl; char *devname; @@ -2295,8 +2474,7 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, info->custom_array_size |= __le32_to_cpu(dev->size_low); info->recovery_blocked = imsm_reshape_blocks_arrays_changes(st->sb); - if (prev_map && map->map_state == prev_map->map_state && - (migr_type(dev) == MIGR_GEN_MIGR)) { + if (is_gen_migration(dev)) { info->reshape_active = 1; info->new_level = get_imsm_raid_level(map); info->new_layout = imsm_level_to_layout(info->new_level); @@ -2306,7 +2484,7 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, /* this needs to be applied to every array * in the container. */ - info->reshape_active = 2; + info->reshape_active = CONTAINER_RESHAPE; } /* We shape information that we give to md might have to be * modify to cope with md's requirement for reshaping arrays. @@ -2352,9 +2530,8 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, dl->index); } - info->data_offset = __le32_to_cpu(map_to_analyse->pba_of_lba0); - info->component_size = - __le32_to_cpu(map_to_analyse->blocks_per_member); + info->data_offset = pba_of_lba0(map_to_analyse); + info->component_size = blocks_per_member(map_to_analyse); /* check component size aligment */ @@ -2413,9 +2590,9 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, (unsigned long long)blocks_per_unit, info->reshape_progress); - used_disks = imsm_num_data_members(dev, 1); + used_disks = imsm_num_data_members(dev, MAP_1); if (used_disks > 0) { - array_blocks = map->blocks_per_member * + array_blocks = blocks_per_member(map) * used_disks; /* round array size down to closest MB */ @@ -2458,7 +2635,7 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, dmap[i] = 0; if (i < info->array.raid_disks) { struct imsm_disk *dsk; - j = get_imsm_disk_idx(dev, i, -1); + j = get_imsm_disk_idx(dev, i, MAP_X); dsk = get_imsm_disk(super, j); if (dsk && (dsk->status & CONFIGURED_DISK)) dmap[i] = 1; @@ -2467,8 +2644,30 @@ static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, } } -static __u8 imsm_check_degraded(struct intel_super *super, struct imsm_dev *dev, int failed); -static int imsm_count_failed(struct intel_super *super, struct imsm_dev *dev); +static __u8 imsm_check_degraded(struct intel_super *super, struct imsm_dev *dev, + int failed, int look_in_map); + +static int imsm_count_failed(struct intel_super *super, struct imsm_dev *dev, + int look_in_map); + + +#ifndef MDASSEMBLE +static void manage_second_map(struct intel_super *super, struct imsm_dev *dev) +{ + if (is_gen_migration(dev)) { + int failed; + __u8 map_state; + struct imsm_map *map2 = get_imsm_map(dev, MAP_1); + + failed = imsm_count_failed(super, dev, MAP_1); + map_state = imsm_check_degraded(super, dev, failed, MAP_1); + if (map2->map_state != map_state) { + map2->map_state = map_state; + super->updates_pending++; + } + } +} +#endif static struct imsm_disk *get_imsm_missing(struct intel_super *super, __u8 index) { @@ -2529,15 +2728,15 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char * struct imsm_map *map; __u8 state; - failed = imsm_count_failed(super, dev); - state = imsm_check_degraded(super, dev, failed); - map = get_imsm_map(dev, dev->vol.migr_state); + failed = imsm_count_failed(super, dev, MAP_0); + state = imsm_check_degraded(super, dev, failed, MAP_0); + map = get_imsm_map(dev, MAP_0); /* any newly missing disks? * (catches single-degraded vs double-degraded) */ for (j = 0; j < map->num_members; j++) { - __u32 ord = get_imsm_ord_tbl_ent(dev, i, -1); + __u32 ord = get_imsm_ord_tbl_ent(dev, j, MAP_0); __u32 idx = ord_to_idx(ord); if (!(ord & IMSM_ORD_REBUILD) && @@ -2554,7 +2753,17 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char * enough = 0; else /* we're normal, or already degraded */ enough = 1; - + if (is_gen_migration(dev) && missing) { + /* during general migration we need all disks + * that process is running on. + * No new missing disk is allowed. + */ + max_enough = -1; + enough = -1; + /* no more checks necessary + */ + break; + } /* in the missing/failed disk case check to see * if at least one array is runnable */ @@ -2567,7 +2776,7 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info, char * __u32 reserved = imsm_reserved_sectors(super, super->disks); disk = &super->disks->disk; - info->data_offset = __le32_to_cpu(disk->total_blocks) - reserved; + info->data_offset = total_blocks(&super->disks->disk) - reserved; info->component_size = reserved; info->disk.state = is_configured(disk) ? (1 << MD_DISK_ACTIVE) : 0; /* we don't change info->disk.raid_disk here because @@ -2678,25 +2887,30 @@ static int update_super_imsm(struct supertype *st, struct mdinfo *info, mpb = super->anchor; - if (strcmp(update, "uuid") == 0 && uuid_set && !info->update_private) - rv = -1; - else if (strcmp(update, "uuid") == 0 && uuid_set && info->update_private) { - mpb->orig_family_num = *((__u32 *) info->update_private); - rv = 0; - } else if (strcmp(update, "uuid") == 0) { - __u32 *new_family = malloc(sizeof(*new_family)); - - /* update orig_family_number with the incoming random - * data, report the new effective uuid, and store the - * new orig_family_num for future updates. + if (strcmp(update, "uuid") == 0) { + /* We take this to mean that the family_num should be updated. + * However that is much smaller than the uuid so we cannot really + * allow an explicit uuid to be given. And it is hard to reliably + * know if one was. + * So if !uuid_set we know the current uuid is random and just used + * the first 'int' and copy it to the other 3 positions. + * Otherwise we require the 4 'int's to be the same as would be the + * case if we are using a random uuid. So an explicit uuid will be + * accepted as long as all for ints are the same... which shouldn't hurt */ - if (new_family) { - memcpy(&mpb->orig_family_num, info->uuid, sizeof(__u32)); - uuid_from_super_imsm(st, info->uuid); - *new_family = mpb->orig_family_num; - info->update_private = new_family; + if (!uuid_set) { + info->uuid[1] = info->uuid[2] = info->uuid[3] = info->uuid[0]; rv = 0; + } else { + if (info->uuid[0] != info->uuid[1] || + info->uuid[1] != info->uuid[2] || + info->uuid[2] != info->uuid[3]) + rv = -1; + else + rv = 0; } + if (rv == 0) + mpb->orig_family_num = info->uuid[0]; } else if (strcmp(update, "assemble") == 0) rv = 0; else @@ -2867,11 +3081,13 @@ static void fd2devname(int fd, char *name) rv = readlink(path, dname, sizeof(dname)-1); if (rv <= 0) return; - + dname[rv] = '\0'; nm = strrchr(dname, '/'); - nm++; - snprintf(name, MAX_RAID_SERIAL_LEN, "/dev/%s", nm); + if (nm) { + nm++; + snprintf(name, MAX_RAID_SERIAL_LEN, "/dev/%s", nm); + } } extern int scsi_get_serial(int fd, void *buf, size_t buf_len); @@ -3062,12 +3278,12 @@ static void migrate(struct imsm_dev *dev, struct intel_super *super, __u8 to_state, int migr_type) { struct imsm_map *dest; - struct imsm_map *src = get_imsm_map(dev, 0); + struct imsm_map *src = get_imsm_map(dev, MAP_0); dev->vol.migr_state = 1; set_migr_type(dev, migr_type); dev->vol.curr_migr_unit = 0; - dest = get_imsm_map(dev, 1); + dest = get_imsm_map(dev, MAP_1); /* duplicate and then set the target end state in map[0] */ memcpy(dest, src, sizeof_imsm_map(src)); @@ -3089,10 +3305,12 @@ static void migrate(struct imsm_dev *dev, struct intel_super *super, src->map_state = to_state; } -static void end_migration(struct imsm_dev *dev, __u8 map_state) +static void end_migration(struct imsm_dev *dev, struct intel_super *super, + __u8 map_state) { - struct imsm_map *map = get_imsm_map(dev, 0); - struct imsm_map *prev = get_imsm_map(dev, dev->vol.migr_state); + struct imsm_map *map = get_imsm_map(dev, MAP_0); + struct imsm_map *prev = get_imsm_map(dev, dev->vol.migr_state == 0 ? + MAP_0 : MAP_1); int i, j; /* merge any IMSM_ORD_REBUILD bits that were not successfully @@ -3100,16 +3318,28 @@ static void end_migration(struct imsm_dev *dev, __u8 map_state) * * FIXME add support for raid-level-migration */ - for (i = 0; i < prev->num_members; i++) - for (j = 0; j < map->num_members; j++) - /* during online capacity expansion - * disks position can be changed if takeover is used - */ - if (ord_to_idx(map->disk_ord_tbl[j]) == - ord_to_idx(prev->disk_ord_tbl[i])) { - map->disk_ord_tbl[j] |= prev->disk_ord_tbl[i]; - break; - } + if ((map_state != map->map_state) && (is_gen_migration(dev) == 0) && + (prev->map_state != IMSM_T_STATE_UNINITIALIZED)) { + /* when final map state is other than expected + * merge maps (not for migration) + */ + int failed; + + for (i = 0; i < prev->num_members; i++) + for (j = 0; j < map->num_members; j++) + /* during online capacity expansion + * disks position can be changed + * if takeover is used + */ + if (ord_to_idx(map->disk_ord_tbl[j]) == + ord_to_idx(prev->disk_ord_tbl[i])) { + map->disk_ord_tbl[j] |= + prev->disk_ord_tbl[i]; + break; + } + failed = imsm_count_failed(super, dev, MAP_0); + map_state = imsm_check_degraded(super, dev, failed, MAP_0); + } dev->vol.migr_state = 0; set_migr_type(dev, 0); @@ -3135,7 +3365,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; @@ -3171,7 +3401,7 @@ static int parse_raid_devices(struct intel_super *super) super->buf = buf; super->len = len; } - + return 0; } @@ -3212,9 +3442,9 @@ int check_mpb_migr_compatibility(struct intel_super *super) dev_iter->vol.migr_state == 1 && dev_iter->vol.migr_type == MIGR_GEN_MIGR) { /* This device is migrating */ - map0 = get_imsm_map(dev_iter, 0); - map1 = get_imsm_map(dev_iter, 1); - if (map0->pba_of_lba0 != map1->pba_of_lba0) + map0 = get_imsm_map(dev_iter, MAP_0); + map1 = get_imsm_map(dev_iter, MAP_1); + if (pba_of_lba0(map0) != pba_of_lba0(map1)) /* migration optimization area was used */ return -1; if (migr_rec->ascending_migr == 0 @@ -3299,12 +3529,13 @@ static int load_imsm_mpb(int fd, struct intel_super *super, char *devname) sectors = mpb_sectors(anchor) - 1; free(anchor); - if (posix_memalign(&super->migr_rec_buf, 512, 512) != 0) { + if (posix_memalign(&super->migr_rec_buf, 512, MIGR_REC_BUF_SIZE) != 0) { fprintf(stderr, Name ": %s could not allocate migr_rec buffer\n", __func__); free(super->buf); return 2; } + super->clean_migration_record_by_mdmon = 0; if (!sectors) { check_sum = __gen_imsm_checksum(super->anchor); @@ -3359,6 +3590,32 @@ static int load_imsm_mpb(int fd, struct intel_super *super, char *devname) static int read_imsm_migr_rec(int fd, struct intel_super *super); +/* clears hi bits in metadata if MPB_ATTRIB_2TB_DISK not set */ +static void clear_hi(struct intel_super *super) +{ + struct imsm_super *mpb = super->anchor; + int i, n; + if (mpb->attributes & MPB_ATTRIB_2TB_DISK) + return; + for (i = 0; i < mpb->num_disks; ++i) { + struct imsm_disk *disk = &mpb->disk[i]; + disk->total_blocks_hi = 0; + } + for (i = 0; i < mpb->num_raid_devs; ++i) { + struct imsm_dev *dev = get_imsm_dev(super, i); + if (!dev) + return; + for (n = 0; n < 2; ++n) { + struct imsm_map *map = get_imsm_map(dev, n); + if (!map) + continue; + map->pba_of_lba0_hi = 0; + map->blocks_per_member_hi = 0; + map->num_data_stripes_hi = 0; + } + } +} + static int load_and_parse_mpb(int fd, struct intel_super *super, char *devname, int keep_fd) { @@ -3371,7 +3628,7 @@ load_and_parse_mpb(int fd, struct intel_super *super, char *devname, int keep_fd if (err) return err; err = parse_raid_devices(super); - + clear_hi(super); return err; } @@ -3462,7 +3719,7 @@ static struct intel_super *alloc_super(void) if (super) { memset(super, 0, sizeof(*super)); super->current_vol = -1; - super->create_offset = ~((__u32 ) 0); + super->create_offset = ~((unsigned long long) 0); } return super; } @@ -3875,67 +4132,33 @@ imsm_thunderdome(struct intel_super **super_list, int len) return champion; } + +static int +get_sra_super_block(int fd, struct intel_super **super_list, char *devname, int *max, int keep_fd); +static int get_super_block(struct intel_super **super_list, int devnum, char *devname, + int major, int minor, int keep_fd); +static int +get_devlist_super_block(struct md_list *devlist, struct intel_super **super_list, + int *max, int keep_fd); + + static int load_super_imsm_all(struct supertype *st, int fd, void **sbp, - char *devname) + char *devname, struct md_list *devlist, + int keep_fd) { - struct mdinfo *sra; struct intel_super *super_list = NULL; struct intel_super *super = NULL; - int devnum = fd2devnum(fd); - struct mdinfo *sd; - int retry; int err = 0; - int i; - - /* check if 'fd' an opened container */ - sra = sysfs_read(fd, 0, GET_LEVEL|GET_VERSION|GET_DEVS|GET_STATE); - if (!sra) - return 1; + int i = 0; - if (sra->array.major_version != -1 || - sra->array.minor_version != -2 || - strcmp(sra->text_version, "imsm") != 0) { - err = 1; + if (fd >= 0) + /* 'fd' is an opened container */ + err = get_sra_super_block(fd, &super_list, devname, &i, keep_fd); + else + /* get super block from devlist devices */ + err = get_devlist_super_block(devlist, &super_list, &i, keep_fd); + if (err) goto error; - } - /* load all mpbs */ - for (sd = sra->devs, i = 0; sd; sd = sd->next, i++) { - struct intel_super *s = alloc_super(); - char nm[32]; - int dfd; - int rv; - - err = 1; - if (!s) - goto error; - s->next = super_list; - super_list = s; - - err = 2; - sprintf(nm, "%d:%d", sd->disk.major, sd->disk.minor); - dfd = dev_open(nm, O_RDWR); - if (dfd < 0) - goto error; - - rv = find_intel_hba_capability(dfd, s, devname); - /* no orom/efi or non-intel hba of the disk */ - if (rv != 0) - goto error; - - err = load_and_parse_mpb(dfd, s, NULL, 1); - - /* retry the load if we might have raced against mdmon */ - if (err == 3 && mdmon_running(devnum)) - for (retry = 0; retry < 3; retry++) { - usleep(3000); - err = load_and_parse_mpb(dfd, s, NULL, 1); - if (err != 3) - break; - } - if (err) - goto error; - } - /* all mpbs enter, maybe one leaves */ super = imsm_thunderdome(&super_list, i); if (!super) { @@ -3951,13 +4174,16 @@ static int load_super_imsm_all(struct supertype *st, int fd, void **sbp, /* load migration record */ err = load_imsm_migr_rec(super, NULL); - if (err) { + if (err == -1) { + /* migration is in progress, + * but migr_rec cannot be loaded, + */ err = 4; goto error; } /* Check migration compatibility */ - if (check_mpb_migr_compatibility(super) != 0) { + if ((err == 0) && (check_mpb_migr_compatibility(super) != 0)) { fprintf(stderr, Name ": Unsupported migration detected"); if (devname) fprintf(stderr, " on %s\n", devname); @@ -3977,13 +4203,16 @@ static int load_super_imsm_all(struct supertype *st, int fd, void **sbp, super_list = super_list->next; free_imsm(s); } - sysfs_free(sra); + if (err) return err; *sbp = super; - st->container_dev = devnum; + if (fd >= 0) + st->container_dev = fd2devnum(fd); + else + st->container_dev = NoMdDev; if (err == 0 && st->ss == NULL) { st->ss = &super_imsm; st->minor_version = 0; @@ -3992,52 +4221,191 @@ static int load_super_imsm_all(struct supertype *st, int fd, void **sbp, return 0; } -static int load_container_imsm(struct supertype *st, int fd, char *devname) + +static int +get_devlist_super_block(struct md_list *devlist, struct intel_super **super_list, + int *max, int keep_fd) { - return load_super_imsm_all(st, fd, &st->sb, devname); + struct md_list *tmpdev; + int err = 0; + int i = 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", + tmpdev->devname, strerror(errno)); + err = 8; + goto error; + } + err = get_sra_super_block(fd, super_list, + tmpdev->devname, &lmax, + keep_fd); + i += lmax; + close(fd); + if (err) { + err = 7; + goto error; + } + } else { + int major = major(tmpdev->st_rdev); + int minor = minor(tmpdev->st_rdev); + err = get_super_block(super_list, + -1, + tmpdev->devname, + major, minor, + keep_fd); + i++; + if (err) { + err = 6; + goto error; + } + } + } + error: + *max = i; + return err; } -#endif -static int load_super_imsm(struct supertype *st, int fd, char *devname) +static int get_super_block(struct intel_super **super_list, int devnum, char *devname, + int major, int minor, int keep_fd) { - struct intel_super *super; + struct intel_super*s = NULL; + char nm[32]; + int dfd = -1; int rv; + int err = 0; + int retry; - if (test_partition(fd)) - /* IMSM not allowed on partitions */ - return 1; - - free_super_imsm(st); + s = alloc_super(); + if (!s) { + err = 1; + goto error; + } - super = alloc_super(); - if (!super) { - fprintf(stderr, - Name ": malloc of %zu failed.\n", - sizeof(*super)); - return 1; + sprintf(nm, "%d:%d", major, minor); + dfd = dev_open(nm, O_RDWR); + if (dfd < 0) { + err = 2; + goto error; } - /* Load hba and capabilities if they exist. - * But do not preclude loading metadata in case capabilities or hba are - * non-compliant and ignore_hw_compat is set. - */ - rv = find_intel_hba_capability(fd, super, devname); + + rv = find_intel_hba_capability(dfd, s, devname); /* no orom/efi or non-intel hba of the disk */ - if ((rv != 0) && (st->ignore_hw_compat == 0)) { - if (devname) - fprintf(stderr, - Name ": No OROM/EFI properties for %s\n", devname); - free_imsm(super); - return 2; + if (rv != 0) { + err = 4; + goto error; } - rv = load_and_parse_mpb(fd, super, devname, 0); - if (rv) { - if (devname) - fprintf(stderr, - Name ": Failed to load all information " - "sections on %s\n", devname); - free_imsm(super); - return rv; + err = load_and_parse_mpb(dfd, s, NULL, keep_fd); + + /* retry the load if we might have raced against mdmon */ + if (err == 3 && (devnum != -1) && mdmon_running(devnum)) + for (retry = 0; retry < 3; retry++) { + usleep(3000); + err = load_and_parse_mpb(dfd, s, NULL, keep_fd); + if (err != 3) + break; + } + error: + if (!err) { + s->next = *super_list; + *super_list = s; + } else { + if (s) + free(s); + if (dfd) + close(dfd); + } + if ((dfd >= 0) && (!keep_fd)) + close(dfd); + return err; + +} + +static int +get_sra_super_block(int fd, struct intel_super **super_list, char *devname, int *max, int keep_fd) +{ + struct mdinfo *sra; + int devnum; + struct mdinfo *sd; + int err = 0; + int i = 0; + sra = sysfs_read(fd, 0, GET_LEVEL|GET_VERSION|GET_DEVS|GET_STATE); + if (!sra) + return 1; + + if (sra->array.major_version != -1 || + sra->array.minor_version != -2 || + strcmp(sra->text_version, "imsm") != 0) { + err = 1; + goto error; + } + /* load all mpbs */ + devnum = fd2devnum(fd); + for (sd = sra->devs, i = 0; sd; sd = sd->next, i++) { + if (get_super_block(super_list, devnum, devname, + sd->disk.major, sd->disk.minor, keep_fd) != 0) { + err = 7; + goto error; + } + } + error: + sysfs_free(sra); + *max = i; + return err; +} + +static int load_container_imsm(struct supertype *st, int fd, char *devname) +{ + return load_super_imsm_all(st, fd, &st->sb, devname, NULL, 1); +} +#endif + +static int load_super_imsm(struct supertype *st, int fd, char *devname) +{ + struct intel_super *super; + int rv; + + if (test_partition(fd)) + /* IMSM not allowed on partitions */ + return 1; + + free_super_imsm(st); + + super = alloc_super(); + if (!super) { + fprintf(stderr, + Name ": malloc of %zu failed.\n", + sizeof(*super)); + return 1; + } + /* Load hba and capabilities if they exist. + * But do not preclude loading metadata in case capabilities or hba are + * non-compliant and ignore_hw_compat is set. + */ + rv = find_intel_hba_capability(fd, super, devname); + /* no orom/efi or non-intel hba of the disk */ + if ((rv != 0) && (st->ignore_hw_compat == 0)) { + if (devname) + fprintf(stderr, + Name ": No OROM/EFI properties for %s\n", devname); + free_imsm(super); + return 2; + } + rv = load_and_parse_mpb(fd, super, devname, 0); + + if (rv) { + if (devname) + fprintf(stderr, + Name ": Failed to load all information " + "sections on %s\n", devname); + free_imsm(super); + return rv; } st->sb = super; @@ -4071,22 +4439,13 @@ static __u16 info_to_blocks_per_strip(mdu_array_info_t *info) return info->chunk_size >> 9; } -static __u32 info_to_num_data_stripes(mdu_array_info_t *info, int num_domains) -{ - __u32 num_stripes; - - num_stripes = (info->size * 2) / info_to_blocks_per_strip(info); - num_stripes /= num_domains; - - return num_stripes; -} - -static __u32 info_to_blocks_per_member(mdu_array_info_t *info) +static unsigned long long info_to_blocks_per_member(mdu_array_info_t *info, + unsigned long long size) { if (info->level == 1) - return info->size * 2; + return size * 2; else - return (info->size * 2) & ~(info_to_blocks_per_strip(info) - 1); + return (size * 2) & ~(info_to_blocks_per_strip(info) - 1); } static void imsm_update_version_info(struct intel_super *super) @@ -4100,7 +4459,7 @@ static void imsm_update_version_info(struct intel_super *super) for (i = 0; i < mpb->num_raid_devs; i++) { dev = get_imsm_dev(super, i); - map = get_imsm_map(dev, 0); + map = get_imsm_map(dev, MAP_0); if (__le32_to_cpu(dev->size_high) > 0) mpb->attributes |= MPB_ATTRIB_2TB; @@ -4177,7 +4536,7 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info, int i; unsigned long long array_blocks; size_t size_old, size_new; - __u32 num_data_stripes; + unsigned long long num_data_stripes; if (super->orom && mpb->num_raid_devs >= super->orom->vpa) { fprintf(stderr, Name": This imsm-container already has the " @@ -4196,12 +4555,14 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info, fprintf(stderr, Name": could not allocate new mpb\n"); return 0; } - if (posix_memalign(&super->migr_rec_buf, 512, 512) != 0) { + if (posix_memalign(&super->migr_rec_buf, 512, + MIGR_REC_BUF_SIZE) != 0) { fprintf(stderr, Name ": %s could not allocate migr_rec buffer\n", __func__); free(super->buf); free(super); + free(mpb_new); return 0; } memcpy(mpb_new, mpb, size_old); @@ -4262,12 +4623,9 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info, } strncpy((char *) dev->volume, name, MAX_RAID_SERIAL_LEN); - if (info->level == 1) - array_blocks = info_to_blocks_per_member(info); - else - array_blocks = calc_array_size(info->level, info->raid_disks, + array_blocks = calc_array_size(info->level, info->raid_disks, info->layout, info->chunk_size, - info->size*2); + size * 2); /* round array size down to closest MB */ array_blocks = (array_blocks >> SECT_PER_MB_SHIFT) << SECT_PER_MB_SHIFT; @@ -4279,12 +4637,16 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info, set_migr_type(dev, MIGR_INIT); vol->dirty = !info->state; vol->curr_migr_unit = 0; - map = get_imsm_map(dev, 0); - map->pba_of_lba0 = __cpu_to_le32(super->create_offset); - map->blocks_per_member = __cpu_to_le32(info_to_blocks_per_member(info)); + map = get_imsm_map(dev, MAP_0); + set_pba_of_lba0(map, super->create_offset); + set_blocks_per_member(map, info_to_blocks_per_member(info, size)); map->blocks_per_strip = __cpu_to_le16(info_to_blocks_per_strip(info)); map->failed_disk_num = ~0; - map->map_state = info->failed_disks ? IMSM_T_STATE_DEGRADED : IMSM_T_STATE_NORMAL; + if (info->level > 0) + map->map_state = IMSM_T_STATE_UNINITIALIZED; + else + map->map_state = info->failed_disks ? IMSM_T_STATE_FAILED : + IMSM_T_STATE_NORMAL; map->ddf = 1; if (info->level == 1 && info->raid_disks > 2) { @@ -4304,8 +4666,10 @@ static int init_super_imsm_volume(struct supertype *st, mdu_array_info_t *info, else map->num_domains = 1; - num_data_stripes = info_to_num_data_stripes(info, map->num_domains); - map->num_data_stripes = __cpu_to_le32(num_data_stripes); + /* info->size is only int so use the 'size' parameter instead */ + num_data_stripes = (size * 2) / info_to_blocks_per_strip(info); + num_data_stripes /= map->num_domains; + set_num_data_stripes(map, num_data_stripes); map->num_members = info->raid_disks; for (i = 0; i < map->num_members; i++) { @@ -4359,7 +4723,7 @@ static int init_super_imsm(struct supertype *st, mdu_array_info_t *info, ": %s could not allocate superblock\n", __func__); return 0; } - if (posix_memalign(&super->migr_rec_buf, 512, 512) != 0) { + if (posix_memalign(&super->migr_rec_buf, 512, MIGR_REC_BUF_SIZE) != 0) { fprintf(stderr, Name ": %s could not allocate migr_rec buffer\n", __func__); free(super->buf); @@ -4399,7 +4763,7 @@ static int add_to_super_imsm_volume(struct supertype *st, mdu_disk_info_t *dk, int slot; dev = get_imsm_dev(super, super->current_vol); - map = get_imsm_map(dev, 0); + map = get_imsm_map(dev, MAP_0); if (! (dk->state & (1<index); if (slot >= 0 && - (get_imsm_ord_tbl_ent(dev, slot, -1) & IMSM_ORD_REBUILD) == 0) { + (get_imsm_ord_tbl_ent(dev, slot, MAP_X) & IMSM_ORD_REBUILD) == 0) { fprintf(stderr, Name ": %s has been included in this array twice\n", devname); return 1; @@ -4448,8 +4812,8 @@ static int add_to_super_imsm_volume(struct supertype *st, mdu_disk_info_t *dk, */ if (super->current_vol == 0) { for (df = super->missing; df; df = df->next) { - if (dl->disk.total_blocks > df->disk.total_blocks) - df->disk.total_blocks = dl->disk.total_blocks; + if (total_blocks(&dl->disk) > total_blocks(&df->disk)) + set_total_blocks(&df->disk, total_blocks(&dl->disk)); _disk = __get_imsm_disk(mpb, df->index); *_disk = df->disk; } @@ -4458,11 +4822,28 @@ static int add_to_super_imsm_volume(struct supertype *st, mdu_disk_info_t *dk, /* refresh unset/failed slots to point to valid 'missing' entries */ for (df = super->missing; df; df = df->next) for (slot = 0; slot < mpb->num_disks; slot++) { - __u32 ord = get_imsm_ord_tbl_ent(dev, slot, -1); + __u32 ord = get_imsm_ord_tbl_ent(dev, slot, MAP_X); if ((ord & IMSM_ORD_REBUILD) == 0) continue; set_imsm_ord_tbl_ent(map, slot, df->index | IMSM_ORD_REBUILD); + if (is_gen_migration(dev)) { + struct imsm_map *map2 = get_imsm_map(dev, + MAP_1); + int slot2 = get_imsm_disk_slot(map2, df->index); + if ((slot2 < map2->num_members) && + (slot2 >= 0)) { + __u32 ord2 = get_imsm_ord_tbl_ent(dev, + slot2, + MAP_1); + if ((unsigned)df->index == + ord_to_idx(ord2)) + set_imsm_ord_tbl_ent(map2, + slot2, + df->index | + IMSM_ORD_REBUILD); + } + } dprintf("set slot:%d to missing disk:%d\n", slot, df->index); break; } @@ -4571,7 +4952,11 @@ static int add_to_super_imsm(struct supertype *st, mdu_disk_info_t *dk, get_dev_size(fd, NULL, &size); size /= 512; serialcpy(dd->disk.serial, dd->serial); - dd->disk.total_blocks = __cpu_to_le32(size); + set_total_blocks(&dd->disk, size); + if (__le32_to_cpu(dd->disk.total_blocks_hi) > 0) { + struct imsm_super *mpb = super->anchor; + mpb->attributes |= MPB_ATTRIB_2TB_DISK; + } mark_spare(dd); if (sysfs_disk_to_scsi_id(fd, &id) == 0) dd->disk.scsi_id = __cpu_to_le32(id); @@ -4735,25 +5120,35 @@ static int write_super_imsm(struct supertype *st, int doclose) sum = __gen_imsm_checksum(mpb); mpb->check_sum = __cpu_to_le32(sum); + if (super->clean_migration_record_by_mdmon) { + clear_migration_record = 1; + super->clean_migration_record_by_mdmon = 0; + } if (clear_migration_record) - memset(super->migr_rec_buf, 0, 512); + memset(super->migr_rec_buf, 0, MIGR_REC_BUF_SIZE); /* write the mpb for disks that compose raid devices */ for (d = super->disks; d ; d = d->next) { if (d->index < 0 || is_failed(&d->disk)) continue; - if (store_imsm_mpb(d->fd, mpb)) - fprintf(stderr, "%s: failed for device %d:%d %s\n", - __func__, d->major, d->minor, strerror(errno)); + if (clear_migration_record) { unsigned long long dsize; get_dev_size(d->fd, NULL, &dsize); if (lseek64(d->fd, dsize - 512, SEEK_SET) >= 0) { - if (write(d->fd, super->migr_rec_buf, 512) != 512) + if (write(d->fd, super->migr_rec_buf, + MIGR_REC_BUF_SIZE) != MIGR_REC_BUF_SIZE) perror("Write migr_rec failed"); } } + + if (store_imsm_mpb(d->fd, mpb)) + fprintf(stderr, + "%s: failed for device %d:%d (fd: %d)%s\n", + __func__, d->major, d->minor, + d->fd, strerror(errno)); + if (doclose) { close(d->fd); d->fd = -1; @@ -4773,7 +5168,7 @@ static int create_array(struct supertype *st, int dev_idx) struct imsm_update_create_array *u; struct intel_super *super = st->sb; struct imsm_dev *dev = get_imsm_dev(super, dev_idx); - struct imsm_map *map = get_imsm_map(dev, 0); + struct imsm_map *map = get_imsm_map(dev, MAP_0); struct disk_info *inf; struct imsm_disk *disk; int i; @@ -4792,7 +5187,7 @@ static int create_array(struct supertype *st, int dev_idx) imsm_copy_dev(&u->dev, dev); inf = get_disk_info(u); for (i = 0; i < map->num_members; i++) { - int idx = get_imsm_disk_idx(dev, i, -1); + int idx = get_imsm_disk_idx(dev, i, MAP_X); disk = get_imsm_disk(super, idx); serialcpy(inf[i].serial, disk->serial); @@ -5048,7 +5443,7 @@ static unsigned long long merge_extents(struct intel_super *super, int sum_exten if (maxsize < reserve) return 0; - super->create_offset = ~((__u32) 0); + super->create_offset = ~((unsigned long long) 0); if (start + reserve > super->create_offset) return 0; /* start overflows create_offset */ super->create_offset = start + reserve; @@ -5078,6 +5473,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<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. @@ -5088,7 +5854,6 @@ static int imsm_default_chunk(const struct imsm_orom *orom) return min(512, (1 << fs)); } -#define pr_vrb(fmt, arg...) (void) (verbose && fprintf(stderr, Name fmt, ##arg)) static int validate_geometry_imsm_orom(struct intel_super *super, int level, int layout, int raiddisks, int *chunk, int verbose) @@ -5140,7 +5905,7 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level, { struct stat stb; struct intel_super *super = st->sb; - struct imsm_super *mpb = super->anchor; + struct imsm_super *mpb; struct dl *dl; unsigned long long pos = 0; unsigned long long maxsize; @@ -5151,11 +5916,7 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level, if (!super) return 0; - if (mpb->num_raid_devs > 0 && mpb->num_disks != raiddisks) { - fprintf(stderr, Name ": the option-rom requires all " - "member disks to be a member of all volumes.\n"); - return 0; - } + mpb = super->anchor; if (!validate_geometry_imsm_orom(super, level, layout, raiddisks, chunk, verbose)) { fprintf(stderr, Name ": RAID gemetry validation failed. " @@ -5235,6 +5996,11 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level, fprintf(stderr, Name ": The option-rom requires all member" " disks to be a member of all volumes\n"); return 0; + } else if (super->orom && mpb->num_raid_devs > 0 && + mpb->num_disks != raiddisks) { + fprintf(stderr, Name ": The option-rom requires all member" + " disks to be a member of all volumes\n"); + return 0; } /* retrieve the largest free space block */ @@ -5273,15 +6039,40 @@ static int validate_geometry_imsm_volume(struct supertype *st, int level, i += dl->extent_cnt; maxsize = merge_extents(super, i); + + if (!check_env("IMSM_NO_PLATFORM") && + mpb->num_raid_devs > 0 && size && size != maxsize) { + fprintf(stderr, Name ": attempting to create a second " + "volume with size less then remaining space. " + "Aborting...\n"); + return 0; + } + if (maxsize < size || maxsize == 0) { - if (verbose) - fprintf(stderr, Name ": not enough space after merge (%llu < %llu)\n", - maxsize, size); + if (verbose) { + if (maxsize == 0) + fprintf(stderr, Name ": no free space" + " left on device. Aborting...\n"); + else + fprintf(stderr, Name ": not enough space" + " to create volume of given size" + " (%llu < %llu). Aborting...\n", + maxsize, size); + } return 0; } *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; } @@ -5347,8 +6138,15 @@ static int reserve_space(struct supertype *st, int raiddisks, size /= 2 * chunk; size *= 2 * chunk; } + maxsize = size; + } + if (!check_env("IMSM_NO_PLATFORM") && + mpb->num_raid_devs > 0 && size && size != maxsize) { + fprintf(stderr, Name ": attempting to create a second " + "volume with size less then remaining space. " + "Aborting...\n"); + return 0; } - cnt = 0; for (dl = super->disks; dl; dl = dl->next) if (dl->e) @@ -5380,9 +6178,14 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout, dev, freesize, verbose); } - + if (!dev) { - if (st->sb && freesize) { + if (st->sb) { + struct intel_super *super = st->sb; + if (!validate_geometry_imsm_orom(st->sb, level, layout, + raiddisks, chunk, + verbose)) + return 0; /* we are being asked to automatically layout a * new volume based on the current contents of * the container. If the the parameters can be @@ -5391,12 +6194,22 @@ 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. */ - if (!validate_geometry_imsm_orom(st->sb, level, layout, - raiddisks, chunk, - verbose)) - return 0; - return reserve_space(st, raiddisks, size, - chunk?*chunk:0, freesize); + /* 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); } return 1; } @@ -5443,7 +6256,7 @@ static int validate_geometry_imsm(struct supertype *st, int level, int layout, */ struct intel_super *super; - if (load_super_imsm_all(st, cfd, (void **) &super, NULL) == 0) { + if (load_super_imsm_all(st, cfd, (void **) &super, NULL, NULL, 1) == 0) { st->sb = super; st->container_dev = fd2devnum(cfd); close(cfd); @@ -5601,6 +6414,7 @@ static int update_subarray_imsm(struct supertype *st, char *subarray, return 0; } +#endif /* MDASSEMBLE */ static int is_gen_migration(struct imsm_dev *dev) { @@ -5615,7 +6429,6 @@ static int is_gen_migration(struct imsm_dev *dev) return 0; } -#endif /* MDASSEMBLE */ static int is_rebuilding(struct imsm_dev *dev) { @@ -5627,7 +6440,7 @@ static int is_rebuilding(struct imsm_dev *dev) if (migr_type(dev) != MIGR_REBUILD) return 0; - migr_map = get_imsm_map(dev, 1); + migr_map = get_imsm_map(dev, MAP_1); if (migr_map->map_state == IMSM_T_STATE_DEGRADED) return 1; @@ -5635,6 +6448,26 @@ static int is_rebuilding(struct imsm_dev *dev) return 0; } +#ifndef MDASSEMBLE +static int is_initializing(struct imsm_dev *dev) +{ + struct imsm_map *migr_map; + + if (!dev->vol.migr_state) + return 0; + + if (migr_type(dev) != MIGR_INIT) + return 0; + + migr_map = get_imsm_map(dev, MAP_1); + + if (migr_map->map_state == IMSM_T_STATE_UNINITIALIZED) + return 1; + + return 0; +} +#endif + static void update_recovery_start(struct intel_super *super, struct imsm_dev *dev, struct mdinfo *array) @@ -5686,20 +6519,24 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra struct imsm_super *mpb = super->anchor; struct mdinfo *rest = NULL; unsigned int i; - int bbm_errors = 0; + int sb_errors = 0; struct dl *d; int spare_disks = 0; /* do not assemble arrays when not all attributes are supported */ if (imsm_check_attributes(mpb->attributes) == 0) { - fprintf(stderr, Name ": IMSM metadata loading not allowed " - "due to attributes incompatibility.\n"); - return NULL; + sb_errors = 1; + fprintf(stderr, Name ": Unsupported attributes in IMSM metadata." + "Arrays activation is blocked.\n"); } /* check for bad blocks */ - if (imsm_bbm_log_size(super->anchor)) - bbm_errors = 1; + if (imsm_bbm_log_size(super->anchor)) { + fprintf(stderr, Name ": BBM log found in IMSM metadata." + "Arrays activation is blocked.\n"); + sb_errors = 1; + } + /* count spare devices, not used in maps */ @@ -5712,7 +6549,10 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra struct imsm_map *map; struct imsm_map *map2; struct mdinfo *this; - int slot, chunk; + int slot; +#ifndef MDASSEMBLE + int chunk; +#endif char *ep; if (subarray && @@ -5720,8 +6560,8 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra continue; dev = get_imsm_dev(super, i); - map = get_imsm_map(dev, 0); - map2 = get_imsm_map(dev, 1); + map = get_imsm_map(dev, MAP_0); + map2 = get_imsm_map(dev, MAP_1); /* do not publish arrays that are in the middle of an * unsupported migration @@ -5737,19 +6577,6 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra * OROM/EFI */ - chunk = __le16_to_cpu(map->blocks_per_strip) >> 1; -#ifndef MDASSEMBLE - if (!validate_geometry_imsm_orom(super, - get_imsm_raid_level(map), /* RAID level */ - imsm_level_to_layout(get_imsm_raid_level(map)), - map->num_members, /* raid disks */ - &chunk, - 1 /* verbose */)) { - fprintf(stderr, Name ": RAID gemetry validation failed. " - "Cannot proceed with the action(s).\n"); - continue; - } -#endif /* MDASSEMBLE */ this = malloc(sizeof(*this)); if (!this) { fprintf(stderr, Name ": failed to allocate %zu bytes\n", @@ -5760,6 +6587,30 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra super->current_vol = i; getinfo_super_imsm_volume(st, this, NULL); this->next = rest; +#ifndef MDASSEMBLE + chunk = __le16_to_cpu(map->blocks_per_strip) >> 1; + /* mdadm does not support all metadata features- set the bit in all arrays state */ + if (!validate_geometry_imsm_orom(super, + get_imsm_raid_level(map), /* RAID level */ + imsm_level_to_layout(get_imsm_raid_level(map)), + map->num_members, /* raid disks */ + &chunk, + 1 /* verbose */)) { + fprintf(stderr, Name ": IMSM RAID geometry validation" + " failed. Array %s activation is blocked.\n", + dev->volume); + this->array.state |= + (1<array.state |= + (1<num_members; slot++) { unsigned long long recovery_start; struct mdinfo *info_d; @@ -5769,8 +6620,8 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra __u32 ord; skip = 0; - idx = get_imsm_disk_idx(dev, slot, 0); - ord = get_imsm_ord_tbl_ent(dev, slot, -1); + idx = get_imsm_disk_idx(dev, slot, MAP_0); + ord = get_imsm_ord_tbl_ent(dev, slot, MAP_X); for (d = super->disks; d ; d = d->next) if (d->index == idx) break; @@ -5833,8 +6684,8 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra this->array.working_disks++; info_d->events = __le32_to_cpu(mpb->generation_num); - info_d->data_offset = __le32_to_cpu(map->pba_of_lba0); - info_d->component_size = __le32_to_cpu(map->blocks_per_member); + info_d->data_offset = pba_of_lba0(map); + info_d->component_size = blocks_per_member(map); } /* now that the disk list is up-to-date fixup recovery_start */ update_recovery_start(super, dev, this); @@ -5848,17 +6699,16 @@ static struct mdinfo *container_content_imsm(struct supertype *st, char *subarra rest = this; } - /* if array has bad blocks, set suitable bit in array status */ - if (bbm_errors) - rest->array.state |= (1<map_state == IMSM_T_STATE_UNINITIALIZED ? @@ -5886,7 +6736,7 @@ static __u8 imsm_check_degraded(struct intel_super *super, struct imsm_dev *dev, int insync = insync; for (i = 0; i < map->num_members; i++) { - __u32 ord = get_imsm_ord_tbl_ent(dev, i, -1); + __u32 ord = get_imsm_ord_tbl_ent(dev, i, MAP_X); int idx = ord_to_idx(ord); struct imsm_disk *disk; @@ -5922,33 +6772,55 @@ static __u8 imsm_check_degraded(struct intel_super *super, struct imsm_dev *dev, return map->map_state; } -static int imsm_count_failed(struct intel_super *super, struct imsm_dev *dev) +static int imsm_count_failed(struct intel_super *super, struct imsm_dev *dev, + int look_in_map) { int i; int failed = 0; struct imsm_disk *disk; - struct imsm_map *map = get_imsm_map(dev, 0); - struct imsm_map *prev = get_imsm_map(dev, dev->vol.migr_state); + struct imsm_map *map = get_imsm_map(dev, MAP_0); + struct imsm_map *prev = get_imsm_map(dev, MAP_1); + struct imsm_map *map_for_loop; __u32 ord; int idx; + int idx_1; /* at the beginning of migration we set IMSM_ORD_REBUILD on * disks that are being rebuilt. New failures are recorded to * map[0]. So we look through all the disks we started with and * see if any failures are still present, or if any new ones * have arrived - * - * FIXME add support for online capacity expansion and - * raid-level-migration */ - for (i = 0; i < prev->num_members; i++) { - ord = __le32_to_cpu(prev->disk_ord_tbl[i]); - ord |= __le32_to_cpu(map->disk_ord_tbl[i]); - idx = ord_to_idx(ord); + map_for_loop = map; + if (prev && (map->num_members < prev->num_members)) + map_for_loop = prev; - disk = get_imsm_disk(super, idx); - if (!disk || is_failed(disk) || ord & IMSM_ORD_REBUILD) - failed++; + for (i = 0; i < map_for_loop->num_members; i++) { + idx_1 = -255; + /* when MAP_X is passed both maps failures are counted + */ + if (prev && + ((look_in_map == MAP_1) || (look_in_map == MAP_X)) && + (i < prev->num_members)) { + ord = __le32_to_cpu(prev->disk_ord_tbl[i]); + idx_1 = ord_to_idx(ord); + + disk = get_imsm_disk(super, idx_1); + if (!disk || is_failed(disk) || ord & IMSM_ORD_REBUILD) + failed++; + } + if (((look_in_map == MAP_0) || (look_in_map == MAP_X)) && + (i < map->num_members)) { + ord = __le32_to_cpu(map->disk_ord_tbl[i]); + idx = ord_to_idx(ord); + + if (idx != idx_1) { + disk = get_imsm_disk(super, idx); + if (!disk || is_failed(disk) || + ord & IMSM_ORD_REBUILD) + failed++; + } + } } return failed; @@ -5960,7 +6832,7 @@ static int imsm_open_new(struct supertype *c, struct active_array *a, { struct intel_super *super = c->sb; struct imsm_super *mpb = super->anchor; - + if (atoi(inst) >= mpb->num_raid_devs) { fprintf(stderr, "%s: subarry index %d, out of range\n", __func__, atoi(inst)); @@ -5986,7 +6858,7 @@ static int is_resyncing(struct imsm_dev *dev) if (migr_type(dev) == MIGR_GEN_MIGR) return 0; - migr_map = get_imsm_map(dev, 1); + migr_map = get_imsm_map(dev, MAP_1); if ((migr_map->map_state == IMSM_T_STATE_NORMAL) && (dev->vol.migr_type != MIGR_GEN_MIGR)) @@ -6005,7 +6877,7 @@ static int mark_failure(struct imsm_dev *dev, struct imsm_disk *disk, int idx) unsigned int len, shift = 0; /* new failures are always set in map[0] */ - map = get_imsm_map(dev, 0); + map = get_imsm_map(dev, MAP_0); slot = get_imsm_disk_slot(map, idx); if (slot < 0) @@ -6024,6 +6896,19 @@ static int mark_failure(struct imsm_dev *dev, struct imsm_disk *disk, int idx) disk->status |= FAILED_DISK; set_imsm_ord_tbl_ent(map, slot, idx | IMSM_ORD_REBUILD); + /* mark failures in second map if second map exists and this disk + * in this slot. + * This is valid for migration, initialization and rebuild + */ + if (dev->vol.migr_state) { + struct imsm_map *map2 = get_imsm_map(dev, MAP_1); + int slot2 = get_imsm_disk_slot(map2, idx); + + if ((slot2 < map2->num_members) && + (slot2 >= 0)) + set_imsm_ord_tbl_ent(map2, slot2, + idx | IMSM_ORD_REBUILD); + } if (map->failed_disk_num == 0xff) map->failed_disk_num = slot; return 1; @@ -6042,17 +6927,23 @@ static void mark_missing(struct imsm_dev *dev, struct imsm_disk *disk, int idx) static void handle_missing(struct intel_super *super, struct imsm_dev *dev) { - __u8 map_state; struct dl *dl; - int failed; if (!super->missing) return; - failed = imsm_count_failed(super, dev); - map_state = imsm_check_degraded(super, dev, failed); dprintf("imsm: mark missing\n"); - end_migration(dev, map_state); + /* end process for initialization and rebuild only + */ + if (is_gen_migration(dev) == 0) { + __u8 map_state; + int failed; + + failed = imsm_count_failed(super, dev, MAP_0); + map_state = imsm_check_degraded(super, dev, failed, MAP_0); + + end_migration(dev, super, map_state); + } for (dl = super->missing; dl; dl = dl->next) mark_missing(dev, &dl->disk, dl->index); super->updates_pending++; @@ -6060,7 +6951,7 @@ static void handle_missing(struct intel_super *super, struct imsm_dev *dev) static unsigned long long imsm_set_array_size(struct imsm_dev *dev) { - int used_disks = imsm_num_data_members(dev, 0); + int used_disks = imsm_num_data_members(dev, MAP_0); unsigned long long array_blocks; struct imsm_map *map; @@ -6077,8 +6968,8 @@ static unsigned long long imsm_set_array_size(struct imsm_dev *dev) /* set array size in metadata */ - map = get_imsm_map(dev, 0); - array_blocks = map->blocks_per_member * used_disks; + map = get_imsm_map(dev, MAP_0); + array_blocks = blocks_per_member(map) * used_disks; /* round array size down to closest MB */ @@ -6105,7 +6996,7 @@ static void imsm_progress_container_reshape(struct intel_super *super) for (i = 0; i < mpb->num_raid_devs; i++) { struct imsm_dev *dev = get_imsm_dev(super, i); - struct imsm_map *map = get_imsm_map(dev, 0); + struct imsm_map *map = get_imsm_map(dev, MAP_0); struct imsm_map *map2; int prev_num_members; @@ -6130,12 +7021,13 @@ static void imsm_progress_container_reshape(struct intel_super *super) for (i = prev_num_members; i < map->num_members; i++) set_imsm_ord_tbl_ent(map, i, i); - map2 = get_imsm_map(dev, 1); + map2 = get_imsm_map(dev, MAP_1); /* Copy the current map */ memcpy(map2, map, copy_map_size); map2->num_members = prev_num_members; imsm_set_array_size(dev); + super->clean_migration_record_by_mdmon = 1; super->updates_pending++; } } @@ -6150,9 +7042,9 @@ static int imsm_set_array_state(struct active_array *a, int consistent) int inst = a->info.container_member; struct intel_super *super = a->container->sb; struct imsm_dev *dev = get_imsm_dev(super, inst); - struct imsm_map *map = get_imsm_map(dev, 0); - int failed = imsm_count_failed(super, dev); - __u8 map_state = imsm_check_degraded(super, dev, failed); + struct imsm_map *map = get_imsm_map(dev, MAP_0); + int failed = imsm_count_failed(super, dev, MAP_0); + __u8 map_state = imsm_check_degraded(super, dev, failed, MAP_0); __u32 blocks_per_unit; if (dev->vol.migr_state && @@ -6174,12 +7066,14 @@ static int imsm_set_array_state(struct active_array *a, int consistent) * user action is required to recover process */ if (0) { - struct imsm_map *map2 = get_imsm_map(dev, 1); - dev->vol.migr_state = 0; - set_migr_type(dev, 0); - dev->vol.curr_migr_unit = 0; - memcpy(map, map2, sizeof_imsm_map(map2)); - super->updates_pending++; + struct imsm_map *map2 = + get_imsm_map(dev, MAP_1); + dev->vol.migr_state = 0; + set_migr_type(dev, 0); + dev->vol.curr_migr_unit = 0; + memcpy(map, map2, + sizeof_imsm_map(map2)); + super->updates_pending++; } } if (a->last_checkpoint >= a->info.component_size) { @@ -6187,10 +7081,10 @@ static int imsm_set_array_state(struct active_array *a, int consistent) int used_disks; struct mdinfo *mdi; - used_disks = imsm_num_data_members(dev, 0); + used_disks = imsm_num_data_members(dev, MAP_0); if (used_disks > 0) { array_blocks = - map->blocks_per_member * + blocks_per_member(map) * used_disks; /* round array size down to closest MB */ @@ -6232,7 +7126,7 @@ static int imsm_set_array_state(struct active_array *a, int consistent) */ if (is_resyncing(dev)) { dprintf("imsm: mark resync done\n"); - end_migration(dev, map_state); + end_migration(dev, super, map_state); super->updates_pending++; a->last_checkpoint = 0; } @@ -6294,7 +7188,7 @@ static void imsm_set_disk(struct active_array *a, int n, int state) int inst = a->info.container_member; struct intel_super *super = a->container->sb; struct imsm_dev *dev = get_imsm_dev(super, inst); - struct imsm_map *map = get_imsm_map(dev, 0); + struct imsm_map *map = get_imsm_map(dev, MAP_0); struct imsm_disk *disk; int failed; __u32 ord; @@ -6309,7 +7203,7 @@ static void imsm_set_disk(struct active_array *a, int n, int state) dprintf("imsm: set_disk %d:%x\n", n, state); - ord = get_imsm_ord_tbl_ent(dev, n, -1); + ord = get_imsm_ord_tbl_ent(dev, n, MAP_0); disk = get_imsm_disk(super, ord_to_idx(ord)); /* check for new failures */ @@ -6320,53 +7214,99 @@ static void imsm_set_disk(struct active_array *a, int n, int state) /* check if in_sync */ if (state & DS_INSYNC && ord & IMSM_ORD_REBUILD && is_rebuilding(dev)) { - struct imsm_map *migr_map = get_imsm_map(dev, 1); + struct imsm_map *migr_map = get_imsm_map(dev, MAP_1); set_imsm_ord_tbl_ent(migr_map, n, ord_to_idx(ord)); super->updates_pending++; } - failed = imsm_count_failed(super, dev); - map_state = imsm_check_degraded(super, dev, failed); + failed = imsm_count_failed(super, dev, MAP_0); + map_state = imsm_check_degraded(super, dev, failed, MAP_0); /* check if recovery complete, newly degraded, or failed */ - if (map_state == IMSM_T_STATE_NORMAL && is_rebuilding(dev)) { - end_migration(dev, map_state); - map = get_imsm_map(dev, 0); - map->failed_disk_num = ~0; - super->updates_pending++; - a->last_checkpoint = 0; - } else if (map_state == IMSM_T_STATE_DEGRADED && - map->map_state != map_state && - !dev->vol.migr_state) { - dprintf("imsm: mark degraded\n"); - map->map_state = map_state; - super->updates_pending++; - a->last_checkpoint = 0; - } else if (map_state == IMSM_T_STATE_FAILED && - map->map_state != map_state) { - dprintf("imsm: mark failed\n"); - end_migration(dev, map_state); - super->updates_pending++; - a->last_checkpoint = 0; - } else if (is_gen_migration(dev)) { - dprintf("imsm: Detected General Migration in state: "); - if (map_state == IMSM_T_STATE_NORMAL) { - end_migration(dev, map_state); - map = get_imsm_map(dev, 0); + dprintf("imsm: Detected transition to state "); + switch (map_state) { + case IMSM_T_STATE_NORMAL: /* transition to normal state */ + dprintf("normal: "); + if (is_rebuilding(dev)) { + dprintf("while rebuilding"); + end_migration(dev, super, map_state); + map = get_imsm_map(dev, MAP_0); map->failed_disk_num = ~0; - dprintf("normal\n"); - } else { - if (map_state == IMSM_T_STATE_DEGRADED) { - printf("degraded\n"); - end_migration(dev, map_state); - } else { - dprintf("failed\n"); + super->updates_pending++; + a->last_checkpoint = 0; + break; + } + if (is_gen_migration(dev)) { + dprintf("while general migration"); + if (a->last_checkpoint >= a->info.component_size) + end_migration(dev, super, map_state); + else + map->map_state = map_state; + map = get_imsm_map(dev, MAP_0); + map->failed_disk_num = ~0; + super->updates_pending++; + break; + } + break; + case IMSM_T_STATE_DEGRADED: /* transition to degraded state */ + dprintf("degraded: "); + if ((map->map_state != map_state) && + !dev->vol.migr_state) { + dprintf("mark degraded"); + map->map_state = map_state; + super->updates_pending++; + a->last_checkpoint = 0; + break; + } + if (is_rebuilding(dev)) { + dprintf("while rebuilding."); + if (map->map_state != map_state) { + dprintf(" Map state change"); + end_migration(dev, super, map_state); + super->updates_pending++; } + break; + } + if (is_gen_migration(dev)) { + dprintf("while general migration"); + if (a->last_checkpoint >= a->info.component_size) + end_migration(dev, super, map_state); + else { + map->map_state = map_state; + manage_second_map(super, dev); + } + super->updates_pending++; + break; + } + if (is_initializing(dev)) { + dprintf("while initialization."); + map->map_state = map_state; + super->updates_pending++; + break; + } + break; + case IMSM_T_STATE_FAILED: /* transition to failed state */ + dprintf("failed: "); + if (is_gen_migration(dev)) { + dprintf("while general migration"); map->map_state = map_state; + super->updates_pending++; + break; } - super->updates_pending++; + if (map->map_state != map_state) { + dprintf("mark failed"); + end_migration(dev, super, map_state); + super->updates_pending++; + a->last_checkpoint = 0; + break; + } + break; + default: + dprintf("state %i\n", map_state); } + dprintf("\n"); + } static int store_imsm_mpb(int fd, struct imsm_super *mpb) @@ -6417,7 +7357,7 @@ static void imsm_sync_metadata(struct supertype *container) static struct dl *imsm_readd(struct intel_super *super, int idx, struct active_array *a) { struct imsm_dev *dev = get_imsm_dev(super, a->info.container_member); - int i = get_imsm_disk_idx(dev, idx, -1); + int i = get_imsm_disk_idx(dev, idx, MAP_X); struct dl *dl; for (dl = super->disks; dl; dl = dl->next) @@ -6438,7 +7378,7 @@ static struct dl *imsm_add_spare(struct intel_super *super, int slot, struct mdinfo *additional_test_list) { struct imsm_dev *dev = get_imsm_dev(super, a->info.container_member); - int idx = get_imsm_disk_idx(dev, slot, -1); + int idx = get_imsm_disk_idx(dev, slot, MAP_X); struct imsm_super *mpb = super->anchor; struct imsm_map *map; unsigned long long pos; @@ -6500,7 +7440,7 @@ static struct dl *imsm_add_spare(struct intel_super *super, int slot, } for (i = 0; i < mpb->num_raid_devs; i++) { dev = get_imsm_dev(super, i); - map = get_imsm_map(dev, 0); + map = get_imsm_map(dev, MAP_0); /* check if this disk is already a member of * this array @@ -6511,9 +7451,9 @@ static struct dl *imsm_add_spare(struct intel_super *super, int slot, found = 0; j = 0; pos = 0; - array_start = __le32_to_cpu(map->pba_of_lba0); + array_start = pba_of_lba0(map); array_end = array_start + - __le32_to_cpu(map->blocks_per_member) - 1; + blocks_per_member(map) - 1; do { /* check that we can start at pba_of_lba0 with @@ -6556,9 +7496,9 @@ static int imsm_rebuild_allowed(struct supertype *cont, int dev_idx, int failed) dev2 = get_imsm_dev(cont->sb, dev_idx); if (dev2) { - state = imsm_check_degraded(cont->sb, dev2, failed); + state = imsm_check_degraded(cont->sb, dev2, failed, MAP_0); if (state == IMSM_T_STATE_FAILED) { - map = get_imsm_map(dev2, 0); + map = get_imsm_map(dev2, MAP_0); if (!map) return 1; for (slot = 0; slot < map->num_members; slot++) { @@ -6566,7 +7506,7 @@ static int imsm_rebuild_allowed(struct supertype *cont, int dev_idx, int failed) * Check if failed disks are deleted from intel * disk list or are marked to be deleted */ - idx = get_imsm_disk_idx(dev2, slot, -1); + idx = get_imsm_disk_idx(dev2, slot, MAP_X); idisk = get_imsm_dl_disk(cont->sb, idx); /* * Do not rebuild the array if failed disks @@ -6600,7 +7540,7 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a, struct intel_super *super = a->container->sb; int inst = a->info.container_member; struct imsm_dev *dev = get_imsm_dev(super, inst); - struct imsm_map *map = get_imsm_map(dev, 0); + struct imsm_map *map = get_imsm_map(dev, MAP_0); int failed = a->info.array.raid_disks; struct mdinfo *rv = NULL; struct mdinfo *d; @@ -6627,13 +7567,22 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a, if (imsm_reshape_blocks_arrays_changes(super)) return NULL; + /* Cannot activate another spare if rebuild is in progress already + */ + if (is_rebuilding(dev)) { + dprintf("imsm: No spare activation allowed. " + "Rebuild in progress already.\n"); + return NULL; + } + if (a->info.array.level == 4) /* No repair for takeovered array * imsm doesn't support raid4 */ return NULL; - if (imsm_check_degraded(super, dev, failed) != IMSM_T_STATE_DEGRADED) + if (imsm_check_degraded(super, dev, failed, MAP_0) != + IMSM_T_STATE_DEGRADED) return NULL; /* @@ -6642,9 +7591,9 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a, * are removed from container. */ if (failed) { - dprintf("found failed disks in %s, check if there another" + dprintf("found failed disks in %.*s, check if there another" "failed sub-array.\n", - dev->volume); + MAX_RAID_SERIAL_LEN, dev->volume); /* check if states of the other volumes allow for rebuild */ for (i = 0; i < super->anchor->num_raid_devs; i++) { if (i != inst) { @@ -6704,7 +7653,7 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a, di->disk.minor = dl->minor; di->disk.state = 0; di->recovery_start = 0; - di->data_offset = __le32_to_cpu(map->pba_of_lba0); + di->data_offset = pba_of_lba0(map); di->component_size = a->info.component_size; di->container_member = inst; super->random = random32(); @@ -6739,7 +7688,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; @@ -6764,15 +7713,15 @@ static struct mdinfo *imsm_activate_spare(struct active_array *a, static int disks_overlap(struct intel_super *super, int idx, struct imsm_update_create_array *u) { struct imsm_dev *dev = get_imsm_dev(super, idx); - struct imsm_map *map = get_imsm_map(dev, 0); - struct imsm_map *new_map = get_imsm_map(&u->dev, 0); + struct imsm_map *map = get_imsm_map(dev, MAP_0); + struct imsm_map *new_map = get_imsm_map(&u->dev, MAP_0); struct disk_info *inf = get_disk_info(u); struct imsm_disk *disk; int i; int j; for (i = 0; i < map->num_members; i++) { - disk = get_imsm_disk(super, get_imsm_disk_idx(dev, i, -1)); + disk = get_imsm_disk(super, get_imsm_disk_idx(dev, i, MAP_X)); for (j = 0; j < new_map->num_members; j++) if (serialcmp(disk->serial, inf[j].serial) == 0) return 1; @@ -6885,7 +7834,7 @@ static int apply_reshape_migration_update(struct imsm_update_reshape_migration * struct imsm_map *map; struct imsm_dev *new_dev = (struct imsm_dev *)*space_list; - struct imsm_map *migr_map = get_imsm_map(dev, 1); + struct imsm_map *migr_map = get_imsm_map(dev, MAP_1); int to_state; struct dl *new_disk; @@ -6893,7 +7842,7 @@ static int apply_reshape_migration_update(struct imsm_update_reshape_migration * return ret_val; *space_list = **space_list; memcpy(new_dev, dev, sizeof_imsm_dev(dev, 0)); - map = get_imsm_map(new_dev, 0); + map = get_imsm_map(new_dev, MAP_0); if (migr_map) { dprintf("imsm: Error: migration in progress"); return ret_val; @@ -6913,7 +7862,7 @@ static int apply_reshape_migration_update(struct imsm_update_reshape_migration * migrate(new_dev, super, to_state, MIGR_GEN_MIGR); if (u->new_level > -1) map->raid_level = u->new_level; - migr_map = get_imsm_map(new_dev, 1); + migr_map = get_imsm_map(new_dev, MAP_1); if ((u->new_level == 5) && (migr_map->raid_level == 0)) { int ord = map->num_members - 1; @@ -6982,6 +7931,120 @@ error_disk_add: return ret_val; } +static int apply_update_activate_spare(struct imsm_update_activate_spare *u, + struct intel_super *super, + struct active_array *active_array) +{ + struct imsm_super *mpb = super->anchor; + struct imsm_dev *dev = get_imsm_dev(super, u->array); + struct imsm_map *map = get_imsm_map(dev, MAP_0); + struct imsm_map *migr_map; + struct active_array *a; + struct imsm_disk *disk; + __u8 to_state; + struct dl *dl; + unsigned int found; + int failed; + int victim; + int i; + int second_map_created = 0; + + for (; u; u = u->next) { + victim = get_imsm_disk_idx(dev, u->slot, MAP_X); + + if (victim < 0) + return 0; + + for (dl = super->disks; dl; dl = dl->next) + if (dl == u->dl) + break; + + if (!dl) { + fprintf(stderr, "error: imsm_activate_spare passed " + "an unknown disk (index: %d)\n", + u->dl->index); + return 0; + } + + /* count failures (excluding rebuilds and the victim) + * to determine map[0] state + */ + failed = 0; + for (i = 0; i < map->num_members; i++) { + if (i == u->slot) + continue; + disk = get_imsm_disk(super, + get_imsm_disk_idx(dev, i, MAP_X)); + if (!disk || is_failed(disk)) + failed++; + } + + /* adding a pristine spare, assign a new index */ + if (dl->index < 0) { + dl->index = super->anchor->num_disks; + super->anchor->num_disks++; + } + disk = &dl->disk; + disk->status |= CONFIGURED_DISK; + disk->status &= ~SPARE_DISK; + + /* mark rebuild */ + to_state = imsm_check_degraded(super, dev, failed, MAP_0); + if (!second_map_created) { + second_map_created = 1; + map->map_state = IMSM_T_STATE_DEGRADED; + migrate(dev, super, to_state, MIGR_REBUILD); + } else + map->map_state = to_state; + migr_map = get_imsm_map(dev, MAP_1); + set_imsm_ord_tbl_ent(map, u->slot, dl->index); + set_imsm_ord_tbl_ent(migr_map, u->slot, + dl->index | IMSM_ORD_REBUILD); + + /* update the family_num to mark a new container + * generation, being careful to record the existing + * family_num in orig_family_num to clean up after + * earlier mdadm versions that neglected to set it. + */ + if (mpb->orig_family_num == 0) + mpb->orig_family_num = mpb->family_num; + mpb->family_num += super->random; + + /* count arrays using the victim in the metadata */ + found = 0; + for (a = active_array; a ; a = a->next) { + dev = get_imsm_dev(super, a->info.container_member); + map = get_imsm_map(dev, MAP_0); + + if (get_imsm_disk_slot(map, victim) >= 0) + found++; + } + + /* delete the victim if it is no longer being + * utilized anywhere + */ + if (!found) { + struct dl **dlp; + + /* We know that 'manager' isn't touching anything, + * so it is safe to delete + */ + for (dlp = &super->disks; *dlp; dlp = &(*dlp)->next) + if ((*dlp)->index == victim) + break; + + /* victim may be on the missing list */ + if (!*dlp) + for (dlp = &super->missing; *dlp; + dlp = &(*dlp)->next) + if ((*dlp)->index == victim) + break; + imsm_delete(super, dlp, victim); + } + } + + return 1; +} static int apply_reshape_container_disks_update(struct imsm_update_reshape *u, struct intel_super *super, @@ -7043,8 +8106,8 @@ static int apply_reshape_container_disks_update(struct imsm_update_reshape *u, newdev = (void*)sp; /* Copy the dev, but not (all of) the map */ memcpy(newdev, id->dev, sizeof(*newdev)); - oldmap = get_imsm_map(id->dev, 0); - newmap = get_imsm_map(newdev, 0); + oldmap = get_imsm_map(id->dev, MAP_0); + newmap = get_imsm_map(newdev, MAP_0); /* Copy the current map */ memcpy(newmap, oldmap, sizeof_imsm_map(oldmap)); /* update one device only @@ -7064,7 +8127,7 @@ static int apply_reshape_container_disks_update(struct imsm_update_reshape *u, } /* New map is correct, now need to save old map */ - newmap = get_imsm_map(newdev, 1); + newmap = get_imsm_map(newdev, MAP_1); memcpy(newmap, oldmap, sizeof_imsm_map(oldmap)); imsm_set_array_size(newdev); @@ -7107,11 +8170,12 @@ static int apply_takeover_update(struct imsm_update_takeover *u, if (dev == NULL) return 0; - map = get_imsm_map(dev, 0); + map = get_imsm_map(dev, MAP_0); if (u->direction == R10_TO_R0) { /* Number of failed disks must be half of initial disk number */ - if (imsm_count_failed(super, dev) != (map->num_members / 2)) + if (imsm_count_failed(super, dev, MAP_0) != + (map->num_members / 2)) return 0; /* iterate through devices to mark removed disks as spare */ @@ -7170,7 +8234,7 @@ static int apply_takeover_update(struct imsm_update_takeover *u, dev_new = (void *)space; memcpy(dev_new, dev, sizeof(*dev)); /* update new map */ - map = get_imsm_map(dev_new, 0); + map = get_imsm_map(dev_new, MAP_0); map->num_members = map->num_members * 2; map->map_state = IMSM_T_STATE_DEGRADED; map->num_domains = 2; @@ -7276,99 +8340,8 @@ static void imsm_process_update(struct supertype *st, } case update_activate_spare: { struct imsm_update_activate_spare *u = (void *) update->buf; - struct imsm_dev *dev = get_imsm_dev(super, u->array); - struct imsm_map *map = get_imsm_map(dev, 0); - struct imsm_map *migr_map; - struct active_array *a; - struct imsm_disk *disk; - __u8 to_state; - struct dl *dl; - unsigned int found; - int failed; - int victim = get_imsm_disk_idx(dev, u->slot, -1); - int i; - - for (dl = super->disks; dl; dl = dl->next) - if (dl == u->dl) - break; - - if (!dl) { - fprintf(stderr, "error: imsm_activate_spare passed " - "an unknown disk (index: %d)\n", - u->dl->index); - return; - } - - super->updates_pending++; - /* count failures (excluding rebuilds and the victim) - * to determine map[0] state - */ - failed = 0; - for (i = 0; i < map->num_members; i++) { - if (i == u->slot) - continue; - disk = get_imsm_disk(super, - get_imsm_disk_idx(dev, i, -1)); - if (!disk || is_failed(disk)) - failed++; - } - - /* adding a pristine spare, assign a new index */ - if (dl->index < 0) { - dl->index = super->anchor->num_disks; - super->anchor->num_disks++; - } - disk = &dl->disk; - disk->status |= CONFIGURED_DISK; - disk->status &= ~SPARE_DISK; - - /* mark rebuild */ - to_state = imsm_check_degraded(super, dev, failed); - map->map_state = IMSM_T_STATE_DEGRADED; - migrate(dev, super, to_state, MIGR_REBUILD); - migr_map = get_imsm_map(dev, 1); - set_imsm_ord_tbl_ent(map, u->slot, dl->index); - set_imsm_ord_tbl_ent(migr_map, u->slot, dl->index | IMSM_ORD_REBUILD); - - /* update the family_num to mark a new container - * generation, being careful to record the existing - * family_num in orig_family_num to clean up after - * earlier mdadm versions that neglected to set it. - */ - if (mpb->orig_family_num == 0) - mpb->orig_family_num = mpb->family_num; - mpb->family_num += super->random; - - /* count arrays using the victim in the metadata */ - found = 0; - for (a = st->arrays; a ; a = a->next) { - dev = get_imsm_dev(super, a->info.container_member); - map = get_imsm_map(dev, 0); - - if (get_imsm_disk_slot(map, victim) >= 0) - found++; - } - - /* delete the victim if it is no longer being - * utilized anywhere - */ - if (!found) { - struct dl **dlp; - - /* We know that 'manager' isn't touching anything, - * so it is safe to delete - */ - for (dlp = &super->disks; *dlp; dlp = &(*dlp)->next) - if ((*dlp)->index == victim) - break; - - /* victim may be on the missing list */ - if (!*dlp) - for (dlp = &super->missing; *dlp; dlp = &(*dlp)->next) - if ((*dlp)->index == victim) - break; - imsm_delete(super, dlp, victim); - } + if (apply_update_activate_spare(u, super, st->arrays)) + super->updates_pending++; break; } case update_create_array: { @@ -7405,9 +8378,9 @@ static void imsm_process_update(struct supertype *st, goto create_error; } - new_map = get_imsm_map(&u->dev, 0); - new_start = __le32_to_cpu(new_map->pba_of_lba0); - new_end = new_start + __le32_to_cpu(new_map->blocks_per_member); + new_map = get_imsm_map(&u->dev, MAP_0); + new_start = pba_of_lba0(new_map); + new_end = new_start + blocks_per_member(new_map); inf = get_disk_info(u); /* handle activate_spare versus create race: @@ -7416,9 +8389,9 @@ static void imsm_process_update(struct supertype *st, */ for (i = 0; i < mpb->num_raid_devs; i++) { dev = get_imsm_dev(super, i); - map = get_imsm_map(dev, 0); - start = __le32_to_cpu(map->pba_of_lba0); - end = start + __le32_to_cpu(map->blocks_per_member); + map = get_imsm_map(dev, MAP_0); + start = pba_of_lba0(map); + end = start + blocks_per_member(map); if ((new_start >= start && new_start <= end) || (start >= new_start && start <= new_end)) /* overlap */; @@ -7597,7 +8570,7 @@ static void imsm_prepare_update(struct supertype *st, if (u->direction == R0_TO_R10) { void **tail = (void **)&update->space_list; struct imsm_dev *dev = get_imsm_dev(super, u->subarray); - struct imsm_map *map = get_imsm_map(dev, 0); + struct imsm_map *map = get_imsm_map(dev, MAP_0); int num_members = map->num_members; void *space; int size, i; @@ -7729,7 +8702,7 @@ static void imsm_prepare_update(struct supertype *st, struct imsm_map *map; dev = get_imsm_dev(super, u->subdev); - map = get_imsm_map(dev, 0); + map = get_imsm_map(dev, MAP_0); current_level = map->raid_level; break; } @@ -7763,7 +8736,7 @@ static void imsm_prepare_update(struct supertype *st, struct imsm_update_create_array *u = (void *) update->buf; struct intel_dev *dv; struct imsm_dev *dev = &u->dev; - struct imsm_map *map = get_imsm_map(dev, 0); + struct imsm_map *map = get_imsm_map(dev, MAP_0); struct dl *dl; struct disk_info *inf; int i; @@ -7848,20 +8821,20 @@ static void imsm_delete(struct intel_super *super, struct dl **dlp, unsigned ind for (i = 0; i < mpb->num_raid_devs; i++) { dev = get_imsm_dev(super, i); - map = get_imsm_map(dev, 0); + map = get_imsm_map(dev, MAP_0); num_members = map->num_members; for (j = 0; j < num_members; j++) { /* update ord entries being careful not to propagate * ord-flags to the first map */ - ord = get_imsm_ord_tbl_ent(dev, j, -1); + ord = get_imsm_ord_tbl_ent(dev, j, MAP_X); if (ord_to_idx(ord) <= index) continue; - map = get_imsm_map(dev, 0); + map = get_imsm_map(dev, MAP_0); set_imsm_ord_tbl_ent(map, j, ord_to_idx(ord - 1)); - map = get_imsm_map(dev, 1); + map = get_imsm_map(dev, MAP_1); if (map) set_imsm_ord_tbl_ent(map, j, ord - 1); } @@ -7877,6 +8850,76 @@ static void imsm_delete(struct intel_super *super, struct dl **dlp, unsigned ind } } #endif /* MDASSEMBLE */ + +static void close_targets(int *targets, int new_disks) +{ + int i; + + if (!targets) + return; + + for (i = 0; i < new_disks; i++) { + if (targets[i] >= 0) { + close(targets[i]); + targets[i] = -1; + } + } +} + +static int imsm_get_allowed_degradation(int level, int raid_disks, + struct intel_super *super, + struct imsm_dev *dev) +{ + switch (level) { + case 1: + case 10:{ + int ret_val = 0; + struct imsm_map *map; + int i; + + ret_val = raid_disks/2; + /* check map if all disks pairs not failed + * in both maps + */ + map = get_imsm_map(dev, MAP_0); + for (i = 0; i < ret_val; i++) { + int degradation = 0; + if (get_imsm_disk(super, i) == NULL) + degradation++; + if (get_imsm_disk(super, i + 1) == NULL) + degradation++; + if (degradation == 2) + return 0; + } + map = get_imsm_map(dev, MAP_1); + /* if there is no second map + * result can be returned + */ + if (map == NULL) + return ret_val; + /* check degradation in second map + */ + for (i = 0; i < ret_val; i++) { + int degradation = 0; + if (get_imsm_disk(super, i) == NULL) + degradation++; + if (get_imsm_disk(super, i + 1) == NULL) + degradation++; + if (degradation == 2) + return 0; + } + return ret_val; + } + case 5: + return 1; + case 6: + return 2; + default: + return 0; + } +} + + /******************************************************************************* * Function: open_backup_targets * Description: Function opens file descriptors for all devices given in @@ -7885,13 +8928,21 @@ static void imsm_delete(struct intel_super *super, struct dl **dlp, unsigned ind * info : general array info * raid_disks : number of disks * raid_fds : table of device's file descriptors + * super : intel super for raid10 degradation check + * dev : intel device for raid10 degradation check * Returns: * 0 : success * -1 : fail ******************************************************************************/ -int open_backup_targets(struct mdinfo *info, int raid_disks, int *raid_fds) +int open_backup_targets(struct mdinfo *info, int raid_disks, int *raid_fds, + struct intel_super *super, struct imsm_dev *dev) { struct mdinfo *sd; + int i; + int opened = 0; + + for (i = 0; i < raid_disks; i++) + raid_fds[i] = -1; for (sd = info->devs ; sd ; sd = sd->next) { char *dn; @@ -7910,8 +8961,19 @@ int open_backup_targets(struct mdinfo *info, int raid_disks, int *raid_fds) raid_fds[sd->disk.raid_disk] = dev_open(dn, O_RDWR); if (raid_fds[sd->disk.raid_disk] < 0) { fprintf(stderr, "cannot open component\n"); - return -1; + continue; } + opened++; + } + /* check if maximum array degradation level is not exceeded + */ + if ((raid_disks - opened) > + imsm_get_allowed_degradation(info->new_level, + raid_disks, + super, dev)) { + fprintf(stderr, "Not enough disks can be opened.\n"); + close_targets(raid_fds, raid_disks); + return -2; } return 0; } @@ -7938,8 +9000,8 @@ void init_migr_record_imsm(struct supertype *st, struct imsm_dev *dev, struct mdinfo *sd; char nm[30]; int fd; - struct imsm_map *map_dest = get_imsm_map(dev, 0); - struct imsm_map *map_src = get_imsm_map(dev, 1); + struct imsm_map *map_dest = get_imsm_map(dev, MAP_0); + struct imsm_map *map_src = get_imsm_map(dev, MAP_1); unsigned long long num_migr_units; unsigned long long array_blocks; @@ -7951,8 +9013,9 @@ void init_migr_record_imsm(struct supertype *st, struct imsm_dev *dev, migr_rec->dest_depth_per_unit = GEN_MIGR_AREA_SIZE / max(map_dest->blocks_per_strip, map_src->blocks_per_strip); - migr_rec->dest_depth_per_unit *= map_dest->blocks_per_strip; - new_data_disks = imsm_num_data_members(dev, 0); + migr_rec->dest_depth_per_unit *= + max(map_dest->blocks_per_strip, map_src->blocks_per_strip); + new_data_disks = imsm_num_data_members(dev, MAP_0); migr_rec->blocks_per_unit = __cpu_to_le32(migr_rec->dest_depth_per_unit * new_data_disks); migr_rec->dest_depth_per_unit = @@ -8016,12 +9079,12 @@ int save_backup_imsm(struct supertype *st, unsigned long long *target_offsets = NULL; int *targets = NULL; int i; - struct imsm_map *map_dest = get_imsm_map(dev, 0); + struct imsm_map *map_dest = get_imsm_map(dev, MAP_0); int new_disks = map_dest->num_members; int dest_layout = 0; int dest_chunk; unsigned long long start; - int data_disks = imsm_num_data_members(dev, 0); + int data_disks = imsm_num_data_members(dev, MAP_0); targets = malloc(new_disks * sizeof(int)); if (!targets) @@ -8044,7 +9107,8 @@ int save_backup_imsm(struct supertype *st, target_offsets[i] -= start/data_disks; } - if (open_backup_targets(info, new_disks, targets)) + if (open_backup_targets(info, new_disks, targets, + super, dev)) goto abort; dest_layout = imsm_level_to_layout(map_dest->raid_level); @@ -8070,9 +9134,7 @@ int save_backup_imsm(struct supertype *st, abort: if (targets) { - for (i = 0; i < new_disks; i++) - if (targets[i] >= 0) - close(targets[i]); + close_targets(targets, new_disks); free(targets); } free(target_offsets); @@ -8161,7 +9223,6 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info) unsigned long num_migr_units = __le32_to_cpu(migr_rec->num_migr_units); char buffer[20]; int skipped_disks = 0; - int max_degradation; err = sysfs_get_str(info, NULL, "array_state", (char *)buffer, 20); if (err < 1) @@ -8183,16 +9244,15 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info) if (id == NULL) return 1; - map_dest = get_imsm_map(id->dev, 0); + map_dest = get_imsm_map(id->dev, MAP_0); new_disks = map_dest->num_members; - max_degradation = new_disks - imsm_num_data_members(id->dev, 0); read_offset = (unsigned long long) __le32_to_cpu(migr_rec->ckpt_area_pba) * 512; write_offset = ((unsigned long long) __le32_to_cpu(migr_rec->dest_1st_member_lba) + - __le32_to_cpu(map_dest->pba_of_lba0)) * 512; + pba_of_lba0(map_dest)) * 512; unit_len = __le32_to_cpu(migr_rec->dest_depth_per_unit) * 512; if (posix_memalign((void **)&buf, 512, unit_len) != 0) @@ -8201,7 +9261,11 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info) if (!targets) goto abort; - open_backup_targets(info, new_disks, targets); + if (open_backup_targets(info, new_disks, targets, super, id->dev)) { + fprintf(stderr, + Name ": Cannot open some devices belonging to array.\n"); + goto abort; + } for (i = 0; i < new_disks; i++) { if (targets[i] < 0) { @@ -8212,29 +9276,36 @@ int recover_backup_imsm(struct supertype *st, struct mdinfo *info) fprintf(stderr, Name ": Cannot seek to block: %s\n", strerror(errno)); - goto abort; + skipped_disks++; + continue; } if ((unsigned)read(targets[i], buf, unit_len) != unit_len) { fprintf(stderr, Name ": Cannot read copy area block: %s\n", strerror(errno)); - goto abort; + skipped_disks++; + continue; } if (lseek64(targets[i], write_offset, SEEK_SET) < 0) { fprintf(stderr, Name ": Cannot seek to block: %s\n", strerror(errno)); - goto abort; + skipped_disks++; + continue; } if ((unsigned)write(targets[i], buf, unit_len) != unit_len) { fprintf(stderr, Name ": Cannot restore block: %s\n", strerror(errno)); - goto abort; + skipped_disks++; + continue; } } - if (skipped_disks > max_degradation) { + if (skipped_disks > imsm_get_allowed_degradation(info->new_level, + new_disks, + super, + id->dev)) { fprintf(stderr, Name ": Cannot restore data from backup." " Too many failed disks\n"); @@ -8558,7 +9629,7 @@ static int imsm_create_metadata_update_for_migration( if (dev) { struct imsm_map *map; - map = get_imsm_map(dev, 0); + map = get_imsm_map(dev, MAP_0); if (map) { int current_chunk_size = __le16_to_cpu(map->blocks_per_strip) / 2; @@ -8627,6 +9698,10 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st, int change = -1; int check_devs = 0; int chunk; + /* number of added/removed disks in operation result */ + int devNumChange = 0; + /* imsm compatible layout value for array geometry verification */ + int imsm_layout = -1; getinfo_super_imsm_volume(st, &info, NULL); if ((geo->level != info.array.level) && @@ -8644,23 +9719,23 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st, change = -1; goto analyse_change_exit; } + imsm_layout = geo->layout; check_devs = 1; - } - if (geo->level == 10) { + devNumChange = 1; /* parity disk added */ + } else if (geo->level == 10) { change = CH_TAKEOVER; check_devs = 1; + devNumChange = 2; /* two mirrors added */ + imsm_layout = 0x102; /* imsm supported layout */ } break; case 1: - if (geo->level == 0) { - change = CH_TAKEOVER; - check_devs = 1; - } - break; case 10: if (geo->level == 0) { change = CH_TAKEOVER; check_devs = 1; + devNumChange = -(geo->raid_disks/2); + imsm_layout = 0; /* imsm raid0 layout */ } break; } @@ -8695,8 +9770,11 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st, change = -1; goto analyse_change_exit; } - } else + } else { geo->layout = info.array.layout; + if (imsm_layout == -1) + imsm_layout = info.array.layout; + } if ((geo->chunksize > 0) && (geo->chunksize != UnSet) && (geo->chunksize != info.array.chunk_size)) @@ -8707,8 +9785,8 @@ enum imsm_reshape_type imsm_analyze_change(struct supertype *st, chunk = geo->chunksize / 1024; if (!validate_geometry_imsm(st, geo->level, - geo->layout, - geo->raid_disks, + imsm_layout, + geo->raid_disks + devNumChange, &chunk, geo->size, 0, 0, 1)) @@ -9068,12 +10146,12 @@ static int imsm_manage_reshape( goto abort; } - map_src = get_imsm_map(dev, 1); + map_src = get_imsm_map(dev, MAP_1); if (map_src == NULL) goto abort; - ndata = imsm_num_data_members(dev, 0); - odata = imsm_num_data_members(dev, 1); + ndata = imsm_num_data_members(dev, MAP_0); + odata = imsm_num_data_members(dev, MAP_1); chunk = __le16_to_cpu(map_src->blocks_per_strip) * 512; old_data_stripe_length = odata * chunk; @@ -9089,6 +10167,18 @@ static int imsm_manage_reshape( "are present in copy area.\n"); goto abort; } + /* Save checkpoint to update migration record for current + * reshape position (in md). It can be farther than current + * reshape position in metadata. + */ + if (save_checkpoint_imsm(st, sra, UNIT_SRC_NORMAL) == 1) { + /* ignore error == 2, this can mean end of reshape here + */ + dprintf("imsm: Cannot write checkpoint to " + "migration record (UNIT_SRC_NORMAL, " + "initial save)\n"); + goto abort; + } } /* size for data */