#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
/* 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
__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
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)
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 {
enum imsm_reshape_type {
CH_TAKEOVER,
CH_MIGRATION,
+ CH_ARRAY_SIZE,
};
/* definition of messages passed to imsm_process_update */
update_reshape_migration,
update_takeover,
update_general_migration_checkpoint,
+ update_size_change,
};
struct imsm_update_activate_spare {
int new_disks[1]; /* new_raid_disks - old_raid_disks makedev number */
};
+struct imsm_update_size_change {
+ enum imsm_update_type type;
+ int subdev;
+ long long new_size;
+};
+
struct imsm_update_general_migration_checkpoint {
enum imsm_update_type type;
__u32 curr_migr_unit;
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 */
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++;
}
}
*/
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
*/
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;
}
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);
{
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;
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)
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)
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));
}
imsm_orom_has_chunk(orom, 1024*16) ? " 16M" : "",
imsm_orom_has_chunk(orom, 1024*32) ? " 32M" : "",
imsm_orom_has_chunk(orom, 1024*64) ? " 64M" : "");
+ printf(" 2TB volumes :%s supported\n",
+ (orom->attr & IMSM_OROM_ATTR_2TB)?"":" not");
+ printf(" 2TB disks :%s supported\n",
+ (orom->attr & IMSM_OROM_ATTR_2TB_DISK)?"":" not");
printf(" Max Disks : %d\n", orom->tds);
- printf(" Max Volumes : %d\n", orom->vpa);
+ printf(" Max Volumes : %d per array, %d per controller\n",
+ orom->vpa, orom->vphba);
return;
}
switch (get_imsm_raid_level(map)) {
case 0:
+ return map->num_members;
+ break;
case 1:
case 10:
- return map->num_members;
+ return map->num_members/2;
case 5:
return map->num_members - 1;
default:
}
return rv;
}
+static unsigned long long imsm_component_size_aligment_check(int level,
+ int chunk_size,
+ unsigned long long component_size)
+{
+ unsigned int component_size_alligment;
+
+ /* check component size aligment
+ */
+ component_size_alligment = component_size % (chunk_size/512);
+
+ dprintf("imsm_component_size_aligment_check(Level: %i, "
+ "chunk_size = %i, component_size = %llu), "
+ "component_size_alligment = %u\n",
+ level, chunk_size, component_size,
+ component_size_alligment);
+
+ if (component_size_alligment && (level != 1) && (level != UnSet)) {
+ dprintf("imsm: reported component size alligned from %llu ",
+ component_size);
+ component_size -= component_size_alligment;
+ dprintf("to %llu (%i).\n",
+ component_size, component_size_alligment);
+ }
+
+ return component_size;
+}
static void getinfo_super_imsm_volume(struct supertype *st, struct mdinfo *info, char *dmap)
{
struct imsm_map *map_to_analyse = map;
struct dl *dl;
char *devname;
- unsigned int component_size_alligment;
int map_disks = info->array.raid_disks;
memset(info, 0, sizeof(*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);
-
- /* check component size aligment
- */
- component_size_alligment =
- info->component_size % (info->array.chunk_size/512);
+ info->data_offset = pba_of_lba0(map_to_analyse);
+ info->component_size = blocks_per_member(map_to_analyse);
- if (component_size_alligment &&
- (info->array.level != 1) && (info->array.level != UnSet)) {
- dprintf("imsm: reported component size alligned from %llu ",
- info->component_size);
- info->component_size -= component_size_alligment;
- dprintf("to %llu (%i).\n",
- info->component_size, component_size_alligment);
- }
+ info->component_size = imsm_component_size_aligment_check(
+ info->array.level,
+ info->array.chunk_size,
+ info->component_size);
memset(info->uuid, 0, sizeof(info->uuid));
info->recovery_start = MaxSector;
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
*/
__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
/* This device is migrating */
map0 = get_imsm_map(dev_iter, MAP_0);
map1 = get_imsm_map(dev_iter, MAP_1);
- if (map0->pba_of_lba0 != map1->pba_of_lba0)
+ if (pba_of_lba0(map0) != pba_of_lba0(map1))
/* migration optimization area was used */
return -1;
if (migr_rec->ascending_migr == 0
free(super->buf);
return 2;
}
+ super->clean_migration_record_by_mdmon = 0;
if (!sectors) {
check_sum = __gen_imsm_checksum(super->anchor);
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)
{
if (err)
return err;
err = parse_raid_devices(super);
-
+ clear_hi(super);
return err;
}
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;
}
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)
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 "
}
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;
vol->dirty = !info->state;
vol->curr_migr_unit = 0;
map = get_imsm_map(dev, MAP_0);
- map->pba_of_lba0 = __cpu_to_le32(super->create_offset);
- map->blocks_per_member = __cpu_to_le32(info_to_blocks_per_member(info));
+ 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;
if (info->level > 0)
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++) {
*/
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;
}
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);
continue;
spare->disk[0] = d->disk;
+ if (__le32_to_cpu(d->disk.total_blocks_hi) > 0)
+ spare->attributes |= MPB_ATTRIB_2TB_DISK;
+
sum = __gen_imsm_checksum(spare);
spare->family_num = __cpu_to_le32(sum);
spare->orig_family_num = 0;
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, MIGR_REC_BUF_SIZE);
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 (fd: %d)%s\n",
- __func__, d->major, d->minor, d->fd, strerror(errno));
if (clear_migration_record) {
unsigned long long dsize;
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;
return 0;
}
close(fd);
- if (super->orom && raiddisks > super->orom->tds) {
- if (verbose)
- fprintf(stderr, Name ": %d exceeds maximum number of"
- " platform supported disks: %d\n",
- raiddisks, super->orom->tds);
-
- free_imsm(super);
- return 0;
+ if (super->orom) {
+ if (raiddisks > super->orom->tds) {
+ if (verbose)
+ fprintf(stderr, Name ": %d exceeds maximum number of"
+ " platform supported disks: %d\n",
+ raiddisks, super->orom->tds);
+ free_imsm(super);
+ return 0;
+ }
+ if ((super->orom->attr & IMSM_OROM_ATTR_2TB_DISK) == 0 &&
+ (ldsize >> 9) >> 32 > 0) {
+ if (verbose)
+ fprintf(stderr, Name ": %s exceeds maximum platform supported size\n", dev);
+ free_imsm(super);
+ return 0;
+ }
}
*freesize = avail_size_imsm(st, ldsize >> 9);
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;
static int
validate_geometry_imsm_orom(struct intel_super *super, int level, int layout,
- int raiddisks, int *chunk, int verbose)
+ int raiddisks, int *chunk, unsigned long long size, int verbose)
{
/* check/set platform and metadata limits/defaults */
if (super->orom && raiddisks > super->orom->dpa) {
layout, level);
return 0;
}
+
+ if (super->orom && (super->orom->attr & IMSM_OROM_ATTR_2TB) == 0 && chunk &&
+ (calc_array_size(level, raiddisks, layout, *chunk, size) >> 32) > 0) {
+ pr_vrb(": platform does not support a volume size over 2TB\n");
+ return 0;
+ }
return 1;
}
mpb = super->anchor;
- if (!validate_geometry_imsm_orom(super, level, layout, raiddisks, chunk, verbose)) {
+ if (!validate_geometry_imsm_orom(super, level, layout, raiddisks, chunk, size, verbose)) {
fprintf(stderr, Name ": RAID gemetry validation failed. "
"Cannot proceed with the action(s).\n");
return 0;
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",
+ pr_vrb(": platform does not support more than %d raid volumes.\n",
super->orom->vphba);
return 0;
}
return 1;
}
-static int reserve_space(struct supertype *st, int raiddisks,
+static int imsm_get_free_size(struct supertype *st, int raiddisks,
unsigned long long size, int chunk,
unsigned long long *freesize)
{
*freesize = size;
+ dprintf("imsm: imsm_get_free_size() returns : %llu\n", size);
+
return 1;
}
+static int reserve_space(struct supertype *st, int raiddisks,
+ unsigned long long size, int chunk,
+ unsigned long long *freesize)
+{
+ struct intel_super *super = st->sb;
+ struct dl *dl;
+ int cnt;
+ int rv = 0;
+
+ rv = imsm_get_free_size(st, raiddisks, size, chunk, freesize);
+ if (rv) {
+ cnt = 0;
+ for (dl = super->disks; dl; dl = dl->next)
+ if (dl->e)
+ dl->raiddisk = cnt++;
+ rv = 1;
+ }
+
+ return rv;
+}
+
static int validate_geometry_imsm(struct supertype *st, int level, int layout,
int raiddisks, int *chunk, unsigned long long size,
char *dev, unsigned long long *freesize,
if (st->sb) {
struct intel_super *super = st->sb;
if (!validate_geometry_imsm_orom(st->sb, level, layout,
- raiddisks, chunk,
+ raiddisks, chunk, size,
verbose))
return 0;
/* we are being asked to automatically layout a
super->orom->dpa, verbose);
if (super->orom->vphba <= count) {
pr_vrb(": platform does not support more"
- "then %d raid volumes.\n",
+ " than %d raid volumes.\n",
super->orom->vphba);
return 0;
}
get_imsm_raid_level(map), /* RAID level */
imsm_level_to_layout(get_imsm_raid_level(map)),
map->num_members, /* raid disks */
- &chunk,
+ &chunk, join_u32(dev->size_low, dev->size_high),
1 /* verbose */)) {
fprintf(stderr, Name ": IMSM RAID geometry validation"
" failed. Array %s activation is blocked.\n",
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);
super->updates_pending++;
}
-static unsigned long long imsm_set_array_size(struct imsm_dev *dev)
+static unsigned long long imsm_set_array_size(struct imsm_dev *dev,
+ long long new_size)
{
int used_disks = imsm_num_data_members(dev, MAP_0);
unsigned long long array_blocks;
/* set array size in metadata
*/
- map = get_imsm_map(dev, MAP_0);
- array_blocks = map->blocks_per_member * used_disks;
+ if (new_size <= 0) {
+ /* OLCE size change is caused by added disks
+ */
+ map = get_imsm_map(dev, MAP_0);
+ array_blocks = blocks_per_member(map) * used_disks;
+ } else {
+ /* Online Volume Size Change
+ * Using available free space
+ */
+ array_blocks = new_size;
+ }
/* round array size down to closest MB
*/
memcpy(map2, map, copy_map_size);
map2->num_members = prev_num_members;
- imsm_set_array_size(dev);
+ imsm_set_array_size(dev, -1);
+ super->clean_migration_record_by_mdmon = 1;
super->updates_pending++;
}
}
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
*/
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
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();
*tofree = *space_list;
/* calculate new size
*/
- imsm_set_array_size(new_dev);
+ imsm_set_array_size(new_dev, -1);
ret_val = 1;
}
return ret_val;
}
+static int apply_size_change_update(struct imsm_update_size_change *u,
+ struct intel_super *super)
+{
+ struct intel_dev *id;
+ int ret_val = 0;
+
+ dprintf("apply_size_change_update()\n");
+ if ((u->subdev < 0) ||
+ (u->subdev > 1)) {
+ dprintf("imsm: Error: Wrong subdev: %i\n", u->subdev);
+ return ret_val;
+ }
+
+ for (id = super->devlist ; id; id = id->next) {
+ if (id->index == (unsigned)u->subdev) {
+ struct imsm_dev *dev = get_imsm_dev(super, u->subdev);
+ struct imsm_map *map = get_imsm_map(dev, MAP_0);
+ int used_disks = imsm_num_data_members(dev, MAP_0);
+ unsigned long long blocks_per_member;
+
+ /* calculate new size
+ */
+ blocks_per_member = u->new_size / used_disks;
+ dprintf("imsm: apply_size_change_update(size: %llu, "
+ "blocks per member: %llu)\n",
+ u->new_size, blocks_per_member);
+ set_blocks_per_member(map, blocks_per_member);
+ imsm_set_array_size(dev, u->new_size);
+
+ ret_val = 1;
+ break;
+ }
+ }
+
+ return ret_val;
+}
+
+
static int apply_update_activate_spare(struct imsm_update_activate_spare *u,
struct intel_super *super,
struct active_array *active_array)
newmap = get_imsm_map(newdev, MAP_1);
memcpy(newmap, oldmap, sizeof_imsm_map(oldmap));
- imsm_set_array_size(newdev);
+ imsm_set_array_size(newdev, -1);
}
sp = (void **)id->dev;
super->updates_pending++;
break;
}
+ case update_size_change: {
+ struct imsm_update_size_change *u = (void *)update->buf;
+ if (apply_size_change_update(u, super))
+ super->updates_pending++;
+ break;
+ }
case update_activate_spare: {
struct imsm_update_activate_spare *u = (void *) update->buf;
if (apply_update_activate_spare(u, super, st->arrays))
}
new_map = get_imsm_map(&u->dev, MAP_0);
- new_start = __le32_to_cpu(new_map->pba_of_lba0);
- new_end = new_start + __le32_to_cpu(new_map->blocks_per_member);
+ 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:
for (i = 0; i < mpb->num_raid_devs; i++) {
dev = get_imsm_dev(super, i);
map = get_imsm_map(dev, MAP_0);
- start = __le32_to_cpu(map->pba_of_lba0);
- end = start + __le32_to_cpu(map->blocks_per_member);
+ 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 */;
dprintf("New anchor length is %llu\n", (unsigned long long)len);
break;
}
+ case update_size_change: {
+ break;
+ }
case update_create_array: {
struct imsm_update_create_array *u = (void *) update->buf;
struct intel_dev *dv;
struct imsm_dev *dev)
{
switch (level) {
+ case 1:
case 10:{
int ret_val = 0;
struct imsm_map *map;
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;
+ 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);
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)
static int imsm_reshape_is_allowed_on_container(struct supertype *st,
struct geo_params *geo,
- int *old_raid_disks)
+ int *old_raid_disks,
+ int direction)
{
/* currently we only support increasing the number of devices
* for a container. This increases the number of device for each
return ret_val;
}
+ if (direction == ROLLBACK_METADATA_CHANGES) {
+ dprintf("imsm: Metadata changes rollback is not supported for "
+ "container operation.\n");
+ return ret_val;
+ }
+
info = container_content_imsm(st, NULL);
for (member = info; member; member = member->next) {
int result;
return 0;
}
+
+/******************************************************************************
+ * function: imsm_create_metadata_update_for_size_change()
+ * Creates update for IMSM array for array size change.
+ *
+ ******************************************************************************/
+static int imsm_create_metadata_update_for_size_change(
+ struct supertype *st,
+ struct geo_params *geo,
+ struct imsm_update_size_change **updatep)
+{
+ struct intel_super *super = st->sb;
+ int update_memory_size = 0;
+ struct imsm_update_size_change *u = NULL;
+
+ dprintf("imsm_create_metadata_update_for_size_change(enter)"
+ " New size = %llu\n", geo->size);
+
+ /* size of all update data without anchor */
+ update_memory_size = sizeof(struct imsm_update_size_change);
+
+ u = calloc(1, update_memory_size);
+ if (u == NULL) {
+ dprintf("error: cannot get memory for "
+ "imsm_create_metadata_update_for_size_change\n");
+ return 0;
+ }
+ u->type = update_size_change;
+ u->subdev = super->current_vol;
+ u->new_size = geo->size;
+
+ dprintf("imsm: reshape update preparation : OK\n");
+ *updatep = u;
+
+ return update_memory_size;
+}
+
/******************************************************************************
* function: imsm_create_metadata_update_for_migration()
* Creates update for IMSM array.
* Function: imsm_analyze_change
* Description: Function analyze change for single volume
* and validate if transition is supported
-* Parameters: Geometry parameters, supertype structure
+* Parameters: Geometry parameters, supertype structure,
+* metadata change direction (apply/rollback)
* Returns: Operation type code on success, -1 if fail
****************************************************************************/
enum imsm_reshape_type imsm_analyze_change(struct supertype *st,
- struct geo_params *geo)
+ struct geo_params *geo,
+ int direction)
{
struct mdinfo info;
int change = -1;
int devNumChange = 0;
/* imsm compatible layout value for array geometry verification */
int imsm_layout = -1;
+ int data_disks;
+ struct imsm_dev *dev;
+ struct intel_super *super;
+ long long current_size;
getinfo_super_imsm_volume(st, &info, NULL);
if ((geo->level != info.array.level) &&
geo->chunksize = info.array.chunk_size;
chunk = geo->chunksize / 1024;
+
+ super = st->sb;
+ dev = get_imsm_dev(super, super->current_vol);
+ data_disks = imsm_num_data_members(dev , MAP_0);
+ /* compute current size per disk member
+ */
+ current_size = info.custom_array_size / data_disks;
+
+ if (geo->size > 0) {
+ /* align component size
+ */
+ geo->size = imsm_component_size_aligment_check(
+ get_imsm_raid_level(dev->vol.map),
+ chunk * 1024,
+ geo->size * 2);
+ }
+
+ if ((current_size != geo->size) && (geo->size >= 0)) {
+ if (change != -1) {
+ fprintf(stderr,
+ Name " Error. Size change should be the only "
+ "one at a time.\n");
+ change = -1;
+ goto analyse_change_exit;
+ }
+ if ((super->current_vol + 1) != super->anchor->num_raid_devs) {
+ fprintf(stderr,
+ Name " Error. The last volume in container "
+ "can be expanded only (%i/%i).\n",
+ super->current_vol, st->devnum);
+ goto analyse_change_exit;
+ }
+ if (geo->size == 0) {
+ /* requested size change to the maximum available size
+ */
+ unsigned long long freesize;
+ int rv;
+
+ rv = imsm_get_free_size(st, dev->vol.map->num_members,
+ 0, chunk, &freesize);
+ if (rv == 0) {
+ fprintf(stderr, Name " Error. Cannot find "
+ "maximum available space.\n");
+ change = -1;
+ goto analyse_change_exit;
+ }
+ geo->size = freesize + current_size;
+
+ /* align component size
+ */
+ geo->size = imsm_component_size_aligment_check(
+ get_imsm_raid_level(dev->vol.map),
+ chunk * 1024,
+ geo->size);
+ }
+
+ if ((direction == ROLLBACK_METADATA_CHANGES)) {
+ /* accept size for rollback only
+ */
+ } else {
+ /* round size due to metadata compatibility
+ */
+ geo->size = (geo->size >> SECT_PER_MB_SHIFT)
+ << SECT_PER_MB_SHIFT;
+ dprintf("Prepare update for size change to %llu\n",
+ geo->size );
+ if (current_size >= geo->size) {
+ fprintf(stderr,
+ Name " Error. Size expanssion is "
+ "supported only (current size is %llu, "
+ "requested size /rounded/ is %llu).\n",
+ current_size, geo->size);
+ goto analyse_change_exit;
+ }
+ }
+ geo->size *= data_disks;
+ geo->raid_disks = dev->vol.map->num_members;
+ change = CH_ARRAY_SIZE;
+ }
if (!validate_geometry_imsm(st,
geo->level,
imsm_layout,
}
analyse_change_exit:
-
+ if ((direction == ROLLBACK_METADATA_CHANGES) &&
+ ((change == CH_MIGRATION) || (change == CH_TAKEOVER))) {
+ dprintf("imsm: Metadata changes rollback is not supported for "
+ "migration and takeover operations.\n");
+ change = -1;
+ }
return change;
}
static int imsm_reshape_super(struct supertype *st, long long size, int level,
int layout, int chunksize, int raid_disks,
int delta_disks, char *backup, char *dev,
- int verbose)
+ int direction, int verbose)
{
int ret_val = 1;
struct geo_params geo;
int old_raid_disks = 0;
if (imsm_reshape_is_allowed_on_container(
- st, &geo, &old_raid_disks)) {
+ st, &geo, &old_raid_disks, direction)) {
struct imsm_update_reshape *u = NULL;
int len;
goto exit_imsm_reshape_super;
}
super->current_vol = dev->index;
- change = imsm_analyze_change(st, &geo);
+ change = imsm_analyze_change(st, &geo, direction);
switch (change) {
case CH_TAKEOVER:
ret_val = imsm_takeover(st, &geo);
free(u);
}
break;
+ case CH_ARRAY_SIZE: {
+ struct imsm_update_size_change *u = NULL;
+ int len =
+ imsm_create_metadata_update_for_size_change(
+ st, &geo, &u);
+ if (len < 1) {
+ dprintf("imsm: "
+ "Cannot prepare update\n");
+ break;
+ }
+ ret_val = 0;
+ /* update metadata locally */
+ imsm_update_metadata_locally(st, u, len);
+ /* and possibly remotely */
+ if (st->update_tail)
+ append_metadata_update(st, u, len);
+ else
+ free(u);
+ }
+ break;
default:
ret_val = 1;
}
"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 */