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

Commit 5c675f83 authored by NeilBrown's avatar NeilBrown
Browse files

md: make ->congested robust against personality changes.



There is currently no locking around calls to the 'congested'
bdi function.  If called at an awkward time while an array is
being converted from one level (or personality) to another, there
is a tiny chance of running code in an unreferenced module etc.

So add a 'congested' function to the md_personality operations
structure, and call it with appropriate locking from a central
'mddev_congested'.

When the array personality is changing the array will be 'suspended'
so no IO is processed.
If mddev_congested detects this, it simply reports that the
array is congested, which is a safe guess.
As mddev_suspend calls synchronize_rcu(), mddev_congested can
avoid races by included the whole call inside an rcu_read_lock()
region.
This require that the congested functions for all subordinate devices
can be run under rcu_lock.  Fortunately this is the case.

Signed-off-by: default avatarNeilBrown <neilb@suse.de>
parent 85572d7c
Loading
Loading
Loading
Loading
+1 −7
Original line number Diff line number Diff line
@@ -746,13 +746,7 @@ static int raid_is_congested(struct dm_target_callbacks *cb, int bits)
{
	struct raid_set *rs = container_of(cb, struct raid_set, callbacks);

	if (rs->raid_type->level == 1)
		return md_raid1_congested(&rs->md, bits);

	if (rs->raid_type->level == 10)
		return md_raid10_congested(&rs->md, bits);

	return md_raid5_congested(&rs->md, bits);
	return mddev_congested(&rs->md, bits);
}

/*
+2 −7
Original line number Diff line number Diff line
@@ -97,15 +97,11 @@ static int linear_mergeable_bvec(struct request_queue *q,
		return maxsectors << 9;
}

static int linear_congested(void *data, int bits)
static int linear_congested(struct mddev *mddev, int bits)
{
	struct mddev *mddev = data;
	struct linear_conf *conf;
	int i, ret = 0;

	if (mddev_congested(mddev, bits))
		return 1;

	rcu_read_lock();
	conf = rcu_dereference(mddev->private);

@@ -218,8 +214,6 @@ static int linear_run (struct mddev *mddev)
	md_set_array_sectors(mddev, linear_size(mddev, 0, 0));

	blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec);
	mddev->queue->backing_dev_info.congested_fn = linear_congested;
	mddev->queue->backing_dev_info.congested_data = mddev;

	ret =  md_integrity_register(mddev);
	if (ret) {
@@ -366,6 +360,7 @@ static struct md_personality linear_personality =
	.status		= linear_status,
	.hot_add_disk	= linear_add,
	.size		= linear_size,
	.congested	= linear_congested,
};

static int __init linear_init (void)
+20 −2
Original line number Diff line number Diff line
@@ -321,9 +321,23 @@ EXPORT_SYMBOL_GPL(mddev_resume);

int mddev_congested(struct mddev *mddev, int bits)
{
	return mddev->suspended;
	struct md_personality *pers = mddev->pers;
	int ret = 0;

	rcu_read_lock();
	if (mddev->suspended)
		ret = 1;
	else if (pers && pers->congested)
		ret = pers->congested(mddev, bits);
	rcu_read_unlock();
	return ret;
}
EXPORT_SYMBOL_GPL(mddev_congested);
static int md_congested(void *data, int bits)
{
	struct mddev *mddev = data;
	return mddev_congested(mddev, bits);
}
EXPORT_SYMBOL(mddev_congested);

/*
 * Generic flush handling for md
@@ -4908,6 +4922,10 @@ int md_run(struct mddev *mddev)
		bitmap_destroy(mddev);
		return err;
	}
	if (mddev->queue) {
		mddev->queue->backing_dev_info.congested_data = mddev;
		mddev->queue->backing_dev_info.congested_fn = md_congested;
	}
	if (mddev->pers->sync_request) {
		if (mddev->kobj.sd &&
		    sysfs_create_group(&mddev->kobj, &md_redundancy_group))
+3 −0
Original line number Diff line number Diff line
@@ -496,6 +496,9 @@ struct md_personality
	 * array.
	 */
	void *(*takeover) (struct mddev *mddev);
	/* congested implements bdi.congested_fn().
	 * Will not be called while array is 'suspended' */
	int (*congested)(struct mddev *mddev, int bits);
};

struct md_sysfs_entry {
+2 −8
Original line number Diff line number Diff line
@@ -153,15 +153,11 @@ static void multipath_status (struct seq_file *seq, struct mddev *mddev)
	seq_printf (seq, "]");
}

static int multipath_congested(void *data, int bits)
static int multipath_congested(struct mddev *mddev, int bits)
{
	struct mddev *mddev = data;
	struct mpconf *conf = mddev->private;
	int i, ret = 0;

	if (mddev_congested(mddev, bits))
		return 1;

	rcu_read_lock();
	for (i = 0; i < mddev->raid_disks ; i++) {
		struct md_rdev *rdev = rcu_dereference(conf->multipaths[i].rdev);
@@ -489,9 +485,6 @@ static int multipath_run (struct mddev *mddev)
	 */
	md_set_array_sectors(mddev, multipath_size(mddev, 0, 0));

	mddev->queue->backing_dev_info.congested_fn = multipath_congested;
	mddev->queue->backing_dev_info.congested_data = mddev;

	if (md_integrity_register(mddev))
		goto out_free_conf;

@@ -533,6 +526,7 @@ static struct md_personality multipath_personality =
	.hot_add_disk	= multipath_add_disk,
	.hot_remove_disk= multipath_remove_disk,
	.size		= multipath_size,
	.congested	= multipath_congested,
};

static int __init multipath_init (void)
Loading