Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7c7546cc authored by NeilBrown's avatar NeilBrown Committed by Linus Torvalds
Browse files

[PATCH] md: allow a linear array to have drives added while active



Signed-off-by: default avatarNeil Brown <neilb@suse.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 5fd6c1dc
Loading
Loading
Loading
Loading
+61 −13
Original line number Diff line number Diff line
@@ -111,7 +111,7 @@ static int linear_issue_flush(request_queue_t *q, struct gendisk *disk,
	return ret;
}

static int linear_run (mddev_t *mddev)
static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks)
{
	linear_conf_t *conf;
	dev_info_t **table;
@@ -121,20 +121,21 @@ static int linear_run (mddev_t *mddev)
	sector_t curr_offset;
	struct list_head *tmp;

	conf = kzalloc (sizeof (*conf) + mddev->raid_disks*sizeof(dev_info_t),
	conf = kzalloc (sizeof (*conf) + raid_disks*sizeof(dev_info_t),
			GFP_KERNEL);
	if (!conf)
		goto out;
		return NULL;

	mddev->private = conf;

	cnt = 0;
	mddev->array_size = 0;
	conf->array_size = 0;

	ITERATE_RDEV(mddev,rdev,tmp) {
		int j = rdev->raid_disk;
		dev_info_t *disk = conf->disks + j;

		if (j < 0 || j > mddev->raid_disks || disk->rdev) {
		if (j < 0 || j > raid_disks || disk->rdev) {
			printk("linear: disk numbering problem. Aborting!\n");
			goto out;
		}
@@ -152,11 +153,11 @@ static int linear_run (mddev_t *mddev)
			blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);

		disk->size = rdev->size;
		mddev->array_size += rdev->size;
		conf->array_size += rdev->size;

		cnt++;
	}
	if (cnt != mddev->raid_disks) {
	if (cnt != raid_disks) {
		printk("linear: not enough drives present. Aborting!\n");
		goto out;
	}
@@ -200,7 +201,7 @@ static int linear_run (mddev_t *mddev)
		unsigned round;
		unsigned long base;

		sz = mddev->array_size >> conf->preshift;
		sz = conf->array_size >> conf->preshift;
		sz += 1; /* force round-up */
		base = conf->hash_spacing >> conf->preshift;
		round = sector_div(sz, base);
@@ -247,14 +248,56 @@ static int linear_run (mddev_t *mddev)

	BUG_ON(table - conf->hash_table > nb_zone);

	return conf;

out:
	kfree(conf);
	return NULL;
}

static int linear_run (mddev_t *mddev)
{
	linear_conf_t *conf;

	conf = linear_conf(mddev, mddev->raid_disks);

	if (!conf)
		return 1;
	mddev->private = conf;
	mddev->array_size = conf->array_size;

	blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec);
	mddev->queue->unplug_fn = linear_unplug;
	mddev->queue->issue_flush_fn = linear_issue_flush;
	return 0;
}

out:
	kfree(conf);
	return 1;
static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
{
	/* Adding a drive to a linear array allows the array to grow.
	 * It is permitted if the new drive has a matching superblock
	 * already on it, with raid_disk equal to raid_disks.
	 * It is achieved by creating a new linear_private_data structure
	 * and swapping it in in-place of the current one.
	 * The current one is never freed until the array is stopped.
	 * This avoids races.
	 */
	linear_conf_t *newconf;

	if (rdev->raid_disk != mddev->raid_disks)
		return -EINVAL;

	newconf = linear_conf(mddev,mddev->raid_disks+1);

	if (!newconf)
		return -ENOMEM;

	newconf->prev = mddev_to_conf(mddev);
	mddev->private = newconf;
	mddev->raid_disks++;
	mddev->array_size = newconf->array_size;
	set_capacity(mddev->gendisk, mddev->array_size << 1);
	return 0;
}

static int linear_stop (mddev_t *mddev)
@@ -262,8 +305,12 @@ static int linear_stop (mddev_t *mddev)
	linear_conf_t *conf = mddev_to_conf(mddev);
  
	blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
	do {
		linear_conf_t *t = conf->prev;
		kfree(conf->hash_table);
		kfree(conf);
		conf = t;
	} while (conf);

	return 0;
}
@@ -360,6 +407,7 @@ static struct mdk_personality linear_personality =
	.run		= linear_run,
	.stop		= linear_stop,
	.status		= linear_status,
	.hot_add_disk	= linear_add,
};

static int __init linear_init (void)
+13 −2
Original line number Diff line number Diff line
@@ -817,8 +817,8 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)

		if (desc->state & (1<<MD_DISK_FAULTY))
			set_bit(Faulty, &rdev->flags);
		else if (desc->state & (1<<MD_DISK_SYNC) &&
			 desc->raid_disk < mddev->raid_disks) {
		else if (desc->state & (1<<MD_DISK_SYNC) /* &&
			    desc->raid_disk < mddev->raid_disks */) {
			set_bit(In_sync, &rdev->flags);
			rdev->raid_disk = desc->raid_disk;
		}
@@ -3359,6 +3359,17 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)

		rdev->raid_disk = -1;
		err = bind_rdev_to_array(rdev, mddev);
		if (!err && !mddev->pers->hot_remove_disk) {
			/* If there is hot_add_disk but no hot_remove_disk
			 * then added disks for geometry changes,
			 * and should be added immediately.
			 */
			super_types[mddev->major_version].
				validate_super(mddev, rdev);
			err = mddev->pers->hot_add_disk(mddev, rdev);
			if (err)
				unbind_rdev_from_array(rdev);
		}
		if (err)
			export_rdev(rdev);

+2 −0
Original line number Diff line number Diff line
@@ -13,8 +13,10 @@ typedef struct dev_info dev_info_t;

struct linear_private_data
{
	struct linear_private_data *prev;	/* earlier version */
	dev_info_t		**hash_table;
	sector_t		hash_spacing;
	sector_t		array_size;
	int			preshift; /* shift before dividing by hash_spacing */
	dev_info_t		disks[0];
};