(Online Capacity Expansion for IMSM) To extend number of disks update_super() has to prepare md configuration and metadata update. To do this update_super() from spare disks pool adds new disks to md up to requested disks limit and prepares metadata update. Update contains information about new array configuration and migration parameters. Signed-off-by: Adam Kwolek <adam.kwolek@xxxxxxxxx> --- mdadm.h | 1 super-intel.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ sysfs.c | 142 +++++++++++++++++++++++++++++++++ 3 files changed, 386 insertions(+), 1 deletions(-) diff --git a/mdadm.h b/mdadm.h index 84d5496..ba01e34 100644 --- a/mdadm.h +++ b/mdadm.h @@ -430,6 +430,7 @@ extern int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd, int resume); extern int sysfs_disk_to_scsi_id(int fd, __u32 *id); extern int sysfs_unique_holder(int devnum, long rdev); extern int load_sys(char *path, char *buf); +extern struct mdinfo *sysfs_get_unused_spares(int container_fd, int +fd); extern int save_stripes(int *source, unsigned long long *offsets, diff --git a/super-intel.c b/super-intel.c index b0bd0e6..a4ffb3f 100644 --- a/super-intel.c +++ b/super-intel.c @@ -284,6 +284,7 @@ enum imsm_update_type { update_create_array, update_add_disk, update_level, + update_grow_array, }; struct imsm_update_activate_spare { @@ -294,6 +295,14 @@ struct imsm_update_activate_spare { struct imsm_update_activate_spare *next; }; +struct imsm_update_grow_array { + enum imsm_update_type type; + int slot; + int reshape_delta_disks; + int devnum; + struct imsm_dev dev; +}; + struct disk_info { __u8 serial[MAX_RAID_SERIAL_LEN]; }; @@ -1598,6 +1607,7 @@ static void getinfo_super_imsm(struct supertype *st, struct mdinfo *info) static int is_raid_level_supported(const struct imsm_orom *orom, int level, int raiddisks); static void imsm_copy_dev(struct imsm_dev *dest, struct imsm_dev *src); +struct mdinfo *get_spares_imsm(int devnum); static int update_super_imsm(struct supertype *st, struct mdinfo *info, char *update, char *devname, int verbose, @@ -1715,7 +1725,171 @@ static int update_super_imsm(struct supertype *st, struct mdinfo *info, u->delta_disks = info->array.raid_disks - map->num_members; u->container_member = info->container_member; append_metadata_update(st, u, len); + } + + if ((strcmp(update, "update_grow_array") == 0) && + (info->delta_disks > 0)) { + struct imsm_update_grow_array *u = NULL; + struct mdinfo *sra = NULL; + struct mdinfo *vol = NULL; + struct mdinfo *spare = NULL; + struct imsm_dev *dev = NULL; + struct imsm_dev *dev_new = NULL; + struct imsm_map *map; + struct imsm_map *map_new; + struct imsm_map *map_migr_new; + char buf[1024]; + int fd = -1; + int disks = 0; + int i; + int idx; + int len; + char *p; + + /* prepare metadata update + */ + + if ((devname == NULL) || (strlen(devname) == 0)) + devname = info->sys_name; + + for (i = 0; i < mpb->num_raid_devs; i++) { + dev = get_imsm_dev(super, i); + if (dev == NULL) { + dprintf("imsm: Error: Cannot get device (mdadm: update_grow_array)\n"); + return 1; + } + if (strcmp(devname, (char *)dev->volume) == 0) + break; + } + + if ((dev == NULL) || + (i == super->anchor->num_raid_devs)) { + dprintf("imsm: Error: Wrong input parameters (mdadm: update_grow_array)\n"); + return 1; + } + + map = get_imsm_map(dev, 0); + if (map == NULL) { + dprintf("imsm: Error: Cannot get map (mdadm: update_grow_array)\n"); + return 1; + } + + disks = info->array.raid_disks + info->delta_disks; + disks = (disks > map->num_members) ? + disks : map->num_members; + + /* allocate device that can receive new disks number + * and reserve place for second map during migration + */ + len = sizeof(struct imsm_update_grow_array) + + ((disks - 1) * sizeof(__u32)) + + sizeof(struct imsm_map) + ((disks-1) * sizeof(__u32)); + u = calloc(1, len); + + if (u == NULL) { + dprintf("imsm: Error: Cannot allocate memory for update (mdadm: update_grow_array)\n"); + return 1; + } + u->type = update_grow_array; + u->reshape_delta_disks = info->delta_disks; + u->devnum = devname2devnum(info->sys_name); + + dev_new = &u->dev; + imsm_copy_dev(dev_new, dev); + map_new = get_imsm_map(dev_new, 0); + + /* reconfigure md + */ + sra = get_spares_imsm(u->devnum); + if (sra == NULL) { + dprintf("imsm: ERROR: Cannot get spares.\n"); + goto exit_update_grow_array; + } + + p = devnum2devname(u->devnum); + if (p == NULL) { + dprintf("imsm: Error: Cannot get device name.\n"); + return 1; + } + sprintf(buf, "/dev/%s", p); + free(p); + fd = open(buf , O_RDONLY); + if (fd < 0) { + dprintf("imsm: ERROR: Cannot open volume.\n"); + goto exit_update_grow_array; + } + + vol = sysfs_read(fd, 0, GET_LEVEL|GET_VERSION|GET_DEVS|GET_STATE); + if (vol == NULL) { + dprintf("imsm: ERROR: Cannot read volume.\n"); + goto exit_update_grow_array; + } + + spare = sra->devs; + if (sra->array.spare_disks < info->delta_disks) { + dprintf("imsm: ERROR: Not enough spare drives for grow.\n"); + goto exit_update_grow_array; + } + + disks = 0; + while (spare) { + spare->disk.raid_disk = map_new->num_members; + spare->data_offset = __le32_to_cpu(map->pba_of_lba0); + spare->component_size = __le32_to_cpu(map_new->blocks_per_member); + if (sysfs_add_disk(vol, spare, 0) >= 0) { + disks++; + info->array.raid_disks++; + + /* Add information to map + */ + idx = map_new->num_members; + map_new->num_members++; + set_imsm_ord_tbl_ent(map_new, idx, idx); + } + + /* next disk please + */ + if (disks < info->delta_disks) + spare = spare->next; + else + spare = NULL; + } + + /* migration has to be in progress, + * to get second map, copy it from current device map + */ + dev_new->vol.migr_state = MIGR_GEN_MIGR; + map_migr_new = get_imsm_map(dev_new, 1); + + map = get_imsm_map(dev, 0); + if ((map == NULL) || (map_migr_new == NULL)) + goto exit_update_grow_array; + memcpy(map_migr_new, map, sizeof_imsm_map(map)); + + map = get_imsm_map(dev_new, 0); + disks = map->num_members - map_migr_new->num_members; + if (disks != u->reshape_delta_disks) { + /* wrong number of disks added to md + */ + dprintf("imsm: ERROR: Not enough drives added to md (added = %i, required = %i).\n", + disks, u->reshape_delta_disks); + goto exit_update_grow_array; + } + + /* update ready, + * append it + */ + append_metadata_update(st, u, len); + u = NULL; rv = 0; + +exit_update_grow_array: + if (u) + free(u); + sysfs_free(vol); + sysfs_free(sra); + close(fd); + goto completed; } @@ -5031,6 +5205,9 @@ static void imsm_process_update(struct supertype *st, super->updates_pending++; break; } + case update_grow_array: { + break; + } case update_activate_spare: { struct imsm_update_activate_spare *u = (void *) update->buf; struct imsm_dev *dev = get_imsm_dev(super, u->array); @@ -5306,6 +5483,9 @@ static void imsm_prepare_update(struct supertype *st, a->info.array.raid_disks); break; } + case update_grow_array: { + break; + } case update_create_array: { struct imsm_update_create_array *u = (void *) update->buf; struct intel_dev *dv; @@ -5423,6 +5603,69 @@ static void imsm_delete(struct intel_super *super, struct dl **dlp, int index) __free_imsm_disk(dl); } } + +struct mdinfo *get_spares_imsm(int devnum) { + int fd = -1; + char buf[1024]; + struct mdinfo *sra = NULL; + struct mdinfo *ret_val = NULL; + int cont_id = -1; + char *devname; + char *a; + int len; + + + dprintf("imsm: get_spares_imsm for device: %i.\n", devnum); + if (devnum < 0) + goto abort; + + devname = devnum2devname(devnum); + if (devname == NULL) { + dprintf("imsm: ERROR: Cannot get device name.\n"); + goto abort; + } + sprintf(buf, "/dev/%s", devname); + free(devname); + + fd = open(buf, O_RDONLY); + if (fd < 0) { + dprintf("imsm: ERROR: Cannot open device.\n"); + goto abort; + } + + sra = sysfs_read(fd, 0, GET_LEVEL|GET_VERSION|GET_DEVS|GET_STATE); + if (!sra) { + dprintf("imsm: ERROR: Container not found.\n"); + goto abort; + } + len = strlen(sra->text_version); + if (len < 2) { + dprintf("imsm: ERROR: Cannot get devine name length.\n"); + goto abort; + } + *(sra->text_version + len - 2) = 0; + cont_id = (int)strtol(sra->text_version+3, &a, 10); + + sprintf(buf, "/dev/md%i", cont_id); + cont_id = open(buf, O_RDONLY); + if (cont_id < 0) { + dprintf("imsm: ERROR: Cannot open container.\n"); + goto abort; + } + + ret_val = sysfs_get_unused_spares(cont_id, fd); + + +abort: + sysfs_free(sra); + if (fd > -1) + close(fd); + if (cont_id > -1) + close(cont_id); + + return ret_val; +} #endif /* MDASSEMBLE */ struct superswitch super_imsm = { @@ -5454,7 +5697,6 @@ struct superswitch super_imsm = { .match_metadata_desc = match_metadata_desc_imsm, .container_content = container_content_imsm, .default_layout = imsm_level_to_layout, - .external = 1, .name = "imsm", diff --git a/sysfs.c b/sysfs.c index ebf9d8a..4c3613e 100644 --- a/sysfs.c +++ b/sysfs.c @@ -791,6 +791,148 @@ int sysfs_unique_holder(int devnum, long rdev) return found; } +int sysfs_is_device_belongs_to(int fd, char *devname) { + int ret_val = -1; + char fname[PATH_MAX]; + char *base; + char *dbase; + struct mdinfo *sra; + DIR *dir = NULL; + struct dirent *de; + + + sra = malloc(sizeof(*sra)); + if (sra == NULL) + goto abort; + memset(sra, 0, sizeof(*sra)); + sysfs_init(sra, fd, -1); + if (sra->sys_name[0] == 0) + goto abort; + + + memset(fname, PATH_MAX, 0); + sprintf(fname, "/sys/block/%s/md/", sra->sys_name); + base = fname + strlen(fname); + + /* Get all the devices as well */ + *base = 0; + dir = opendir(fname); + if (!dir) + goto abort; + while ((de = readdir(dir)) != NULL) { + if (de->d_ino == 0 || + strncmp(de->d_name, "dev-", 4) != 0) + continue; + strcpy(base, de->d_name); + dbase = base + strlen(base); + *dbase = '\0'; + dbase = strstr(fname, "/md/"); + if (dbase && strcmp(devname, dbase) == 0) { + ret_val = 1; + goto abort; + } + } +abort: + if (dir) + closedir(dir); + sysfs_free(sra); + + return ret_val; +} + + +struct mdinfo *sysfs_get_unused_spares(int container_fd, int fd) { + char fname[PATH_MAX]; + char buf[PATH_MAX]; + char *base; + char *dbase; + struct mdinfo *ret_val; + struct mdinfo *dev; + DIR *dir = NULL; + struct dirent *de; + int is_in; + char *to_check; + + + ret_val = malloc(sizeof(*ret_val)); + if (ret_val == NULL) + goto abort; + memset(ret_val, 0, sizeof(*ret_val)); + sysfs_init(ret_val, container_fd, -1); + if (ret_val->sys_name[0] == 0) + goto abort; + + sprintf(fname, "/sys/block/%s/md/", ret_val->sys_name); + base = fname + strlen(fname); + + strcpy(base, "raid_disks"); + if (load_sys(fname, buf)) + goto abort; + ret_val->array.raid_disks = strtoul(buf, NULL, 0); + + /* Get all the devices as well */ + *base = 0; + dir = opendir(fname); + if (!dir) + goto abort; + ret_val->array.spare_disks = 0; + while ((de = readdir(dir)) != NULL) { + char *ep; + if (de->d_ino == 0 || + strncmp(de->d_name, "dev-", 4) != 0) + continue; + strcpy(base, de->d_name); + dbase = base + strlen(base); + *dbase = '\0'; + + to_check = strstr(fname, "/md/"); + is_in = sysfs_is_device_belongs_to(fd, to_check); + if (is_in == -1) { + dev = malloc(sizeof(*dev)); + if (!dev) + goto abort; + strcpy(dev->text_version, fname); + + *dbase++ = '/'; + + dev->disk.raid_disk = strtoul(buf, &ep, 10); + dev->disk.raid_disk = -1; + + strcpy(dbase, "block/dev"); + if (load_sys(fname, buf)) { + free(dev); + continue; + } + sscanf(buf, "%d:%d", &dev->disk.major, &dev->disk.minor); + strcpy(dbase, "block/device/state"); + if (load_sys(fname, buf) == 0 && + strncmp(buf, "offline", 7) == 0) { + free(dev); + continue; + } + + /* add this disk to spares list */ + dev->next = ret_val->devs; + ret_val->devs = dev; + ret_val->array.spare_disks++; + *(dbase-1) = '\0'; + dprintf("sysfs: found spare: (%s)\n", fname); + } + } + closedir(dir); + return ret_val; + +abort: + if (dir) + closedir(dir); + sysfs_free(ret_val); + + return NULL; + +} + #ifndef MDASSEMBLE static char *clean_states[] = { ��.n��������+%������w��{.n�����{����w��ܨ}���Ơz�j:+v�����w����ޙ��&�)ߡ�a����z�ޗ���ݢj��w�f