[PATCH 03/23] imsm: update_super() implementation for adding disks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



(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



[Index of Archives]     [Linux RAID Wiki]     [ATA RAID]     [Linux SCSI Target Infrastructure]     [Linux Block]     [Linux IDE]     [Linux SCSI]     [Linux Hams]     [Device Mapper]     [Device Mapper Cryptographics]     [Kernel]     [Linux Admin]     [Linux Net]     [GFS]     [RPM]     [git]     [Yosemite Forum]


  Powered by Linux