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

Commit e6ee8c0b authored by Kiyoshi Ueda's avatar Kiyoshi Ueda Committed by Alasdair G Kergon
Browse files

dm: enable request based option



This patch enables request-based dm.

o Request-based dm and bio-based dm coexist, since there are
  some target drivers which are more fitting to bio-based dm.
  Also, there are other bio-based devices in the kernel
  (e.g. md, loop).
  Since bio-based device can't receive struct request,
  there are some limitations on device stacking between
  bio-based and request-based.

                     type of underlying device
                   bio-based      request-based
   ----------------------------------------------
    bio-based         OK                OK
    request-based     --                OK

  The device type is recognized by the queue flag in the kernel,
  so dm follows that.

o The type of a dm device is decided at the first table binding time.
  Once the type of a dm device is decided, the type can't be changed.

o Mempool allocations are deferred to at the table loading time, since
  mempools for request-based dm are different from those for bio-based
  dm and needed mempool type is fixed by the type of table.

o Currently, request-based dm supports only tables that have a single
  target.  To support multiple targets, we need to support request
  splitting or prevent bio/request from spanning multiple targets.
  The former needs lots of changes in the block layer, and the latter
  needs that all target drivers support merge() function.
  Both will take a time.

Signed-off-by: default avatarKiyoshi Ueda <k-ueda@ct.jp.nec.com>
Signed-off-by: default avatarJun'ichi Nomura <j-nomura@ce.jp.nec.com>
Signed-off-by: default avatarAlasdair G Kergon <agk@redhat.com>
parent cec47e3d
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -1050,6 +1050,12 @@ static int populate_table(struct dm_table *table,
		next = spec->next;
	}

	r = dm_table_set_type(table);
	if (r) {
		DMWARN("unable to set table type");
		return r;
	}

	return dm_table_complete(table);
}

@@ -1095,6 +1101,13 @@ static int table_load(struct dm_ioctl *param, size_t param_size)
		goto out;
	}

	r = dm_table_alloc_md_mempools(t);
	if (r) {
		DMWARN("unable to allocate mempools for this table");
		dm_table_destroy(t);
		goto out;
	}

	down_write(&_hash_lock);
	hc = dm_get_mdptr(md);
	if (!hc || hc->md != md) {
+111 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@
struct dm_table {
	struct mapped_device *md;
	atomic_t holders;
	unsigned type;

	/* btree table */
	unsigned int depth;
@@ -65,6 +66,8 @@ struct dm_table {
	/* events get handed up using this callback */
	void (*event_fn)(void *);
	void *event_context;

	struct dm_md_mempools *mempools;
};

/*
@@ -258,6 +261,8 @@ void dm_table_destroy(struct dm_table *t)
	if (t->devices.next != &t->devices)
		free_devices(&t->devices);

	dm_free_md_mempools(t->mempools);

	kfree(t);
}

@@ -764,6 +769,99 @@ int dm_table_add_target(struct dm_table *t, const char *type,
	return r;
}

int dm_table_set_type(struct dm_table *t)
{
	unsigned i;
	unsigned bio_based = 0, request_based = 0;
	struct dm_target *tgt;
	struct dm_dev_internal *dd;
	struct list_head *devices;

	for (i = 0; i < t->num_targets; i++) {
		tgt = t->targets + i;
		if (dm_target_request_based(tgt))
			request_based = 1;
		else
			bio_based = 1;

		if (bio_based && request_based) {
			DMWARN("Inconsistent table: different target types"
			       " can't be mixed up");
			return -EINVAL;
		}
	}

	if (bio_based) {
		/* We must use this table as bio-based */
		t->type = DM_TYPE_BIO_BASED;
		return 0;
	}

	BUG_ON(!request_based); /* No targets in this table */

	/* Non-request-stackable devices can't be used for request-based dm */
	devices = dm_table_get_devices(t);
	list_for_each_entry(dd, devices, list) {
		if (!blk_queue_stackable(bdev_get_queue(dd->dm_dev.bdev))) {
			DMWARN("table load rejected: including"
			       " non-request-stackable devices");
			return -EINVAL;
		}
	}

	/*
	 * Request-based dm supports only tables that have a single target now.
	 * To support multiple targets, request splitting support is needed,
	 * and that needs lots of changes in the block-layer.
	 * (e.g. request completion process for partial completion.)
	 */
	if (t->num_targets > 1) {
		DMWARN("Request-based dm doesn't support multiple targets yet");
		return -EINVAL;
	}

	t->type = DM_TYPE_REQUEST_BASED;

	return 0;
}

unsigned dm_table_get_type(struct dm_table *t)
{
	return t->type;
}

bool dm_table_request_based(struct dm_table *t)
{
	return dm_table_get_type(t) == DM_TYPE_REQUEST_BASED;
}

int dm_table_alloc_md_mempools(struct dm_table *t)
{
	unsigned type = dm_table_get_type(t);

	if (unlikely(type == DM_TYPE_NONE)) {
		DMWARN("no table type is set, can't allocate mempools");
		return -EINVAL;
	}

	t->mempools = dm_alloc_md_mempools(type);
	if (!t->mempools)
		return -ENOMEM;

	return 0;
}

void dm_table_free_md_mempools(struct dm_table *t)
{
	dm_free_md_mempools(t->mempools);
	t->mempools = NULL;
}

struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t)
{
	return t->mempools;
}

static int setup_indexes(struct dm_table *t)
{
	int i;
@@ -985,6 +1083,19 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
		queue_flag_set_unlocked(QUEUE_FLAG_CLUSTER, q);

	dm_table_set_integrity(t);

	/*
	 * QUEUE_FLAG_STACKABLE must be set after all queue settings are
	 * visible to other CPUs because, once the flag is set, incoming bios
	 * are processed by request-based dm, which refers to the queue
	 * settings.
	 * Until the flag set, bios are passed to bio-based dm and queued to
	 * md->deferred where queue settings are not needed yet.
	 * Those bios are passed to request-based dm at the resume time.
	 */
	smp_mb();
	if (dm_table_request_based(t))
		queue_flag_set_unlocked(QUEUE_FLAG_STACKABLE, q);
}

unsigned int dm_table_get_num_targets(struct dm_table *t)
+136 −26
Original line number Diff line number Diff line
@@ -190,6 +190,15 @@ struct mapped_device {
	struct bio barrier_bio;
};

/*
 * For mempools pre-allocation at the table loading time.
 */
struct dm_md_mempools {
	mempool_t *io_pool;
	mempool_t *tio_pool;
	struct bio_set *bs;
};

#define MIN_IOS 256
static struct kmem_cache *_io_cache;
static struct kmem_cache *_tio_cache;
@@ -1739,10 +1748,22 @@ static struct mapped_device *alloc_dev(int minor)
	INIT_LIST_HEAD(&md->uevent_list);
	spin_lock_init(&md->uevent_lock);

	md->queue = blk_alloc_queue(GFP_KERNEL);
	md->queue = blk_init_queue(dm_request_fn, NULL);
	if (!md->queue)
		goto bad_queue;

	/*
	 * Request-based dm devices cannot be stacked on top of bio-based dm
	 * devices.  The type of this dm device has not been decided yet,
	 * although we initialized the queue using blk_init_queue().
	 * The type is decided at the first table loading time.
	 * To prevent problematic device stacking, clear the queue flag
	 * for request stacking support until then.
	 *
	 * This queue is new, so no concurrency on the queue_flags.
	 */
	queue_flag_clear_unlocked(QUEUE_FLAG_STACKABLE, md->queue);
	md->saved_make_request_fn = md->queue->make_request_fn;
	md->queue->queuedata = md;
	md->queue->backing_dev_info.congested_fn = dm_any_congested;
	md->queue->backing_dev_info.congested_data = md;
@@ -1751,18 +1772,9 @@ static struct mapped_device *alloc_dev(int minor)
	blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY);
	md->queue->unplug_fn = dm_unplug_all;
	blk_queue_merge_bvec(md->queue, dm_merge_bvec);

	md->io_pool = mempool_create_slab_pool(MIN_IOS, _io_cache);
	if (!md->io_pool)
		goto bad_io_pool;

	md->tio_pool = mempool_create_slab_pool(MIN_IOS, _tio_cache);
	if (!md->tio_pool)
		goto bad_tio_pool;

	md->bs = bioset_create(16, 0);
	if (!md->bs)
		goto bad_no_bioset;
	blk_queue_softirq_done(md->queue, dm_softirq_done);
	blk_queue_prep_rq(md->queue, dm_prep_fn);
	blk_queue_lld_busy(md->queue, dm_lld_busy);

	md->disk = alloc_disk(1);
	if (!md->disk)
@@ -1804,12 +1816,6 @@ bad_bdev:
bad_thread:
	put_disk(md->disk);
bad_disk:
	bioset_free(md->bs);
bad_no_bioset:
	mempool_destroy(md->tio_pool);
bad_tio_pool:
	mempool_destroy(md->io_pool);
bad_io_pool:
	blk_cleanup_queue(md->queue);
bad_queue:
	free_minor(minor);
@@ -1829,8 +1835,11 @@ static void free_dev(struct mapped_device *md)
	unlock_fs(md);
	bdput(md->bdev);
	destroy_workqueue(md->wq);
	if (md->tio_pool)
		mempool_destroy(md->tio_pool);
	if (md->io_pool)
		mempool_destroy(md->io_pool);
	if (md->bs)
		bioset_free(md->bs);
	blk_integrity_unregister(md->disk);
	del_gendisk(md->disk);
@@ -1846,6 +1855,29 @@ static void free_dev(struct mapped_device *md)
	kfree(md);
}

static void __bind_mempools(struct mapped_device *md, struct dm_table *t)
{
	struct dm_md_mempools *p;

	if (md->io_pool && md->tio_pool && md->bs)
		/* the md already has necessary mempools */
		goto out;

	p = dm_table_get_md_mempools(t);
	BUG_ON(!p || md->io_pool || md->tio_pool || md->bs);

	md->io_pool = p->io_pool;
	p->io_pool = NULL;
	md->tio_pool = p->tio_pool;
	p->tio_pool = NULL;
	md->bs = p->bs;
	p->bs = NULL;

out:
	/* mempool bind completed, now no need any mempools in the table */
	dm_table_free_md_mempools(t);
}

/*
 * Bind a table to the device.
 */
@@ -1897,6 +1929,18 @@ static int __bind(struct mapped_device *md, struct dm_table *t,

	dm_table_event_callback(t, event_callback, md);

	/*
	 * The queue hasn't been stopped yet, if the old table type wasn't
	 * for request-based during suspension.  So stop it to prevent
	 * I/O mapping before resume.
	 * This must be done before setting the queue restrictions,
	 * because request-based dm may be run just after the setting.
	 */
	if (dm_table_request_based(t) && !blk_queue_stopped(q))
		stop_queue(q);

	__bind_mempools(md, t);

	write_lock(&md->map_lock);
	md->map = t;
	dm_table_set_restrictions(t, q, limits);
@@ -2110,10 +2154,14 @@ static void dm_wq_work(struct work_struct *work)

		up_write(&md->io_lock);

		if (dm_request_based(md))
			generic_make_request(c);
		else {
			if (bio_barrier(c))
				process_barrier(md, c);
			else
				__split_and_process_bio(md, c);
		}

		down_write(&md->io_lock);
	}
@@ -2146,6 +2194,13 @@ int dm_swap_table(struct mapped_device *md, struct dm_table *table)
	if (r)
		goto out;

	/* cannot change the device type, once a table is bound */
	if (md->map &&
	    (dm_table_get_type(md->map) != dm_table_get_type(table))) {
		DMWARN("can't change the device type after a table is bound");
		goto out;
	}

	__unbind(md);
	r = __bind(md, table, &limits);

@@ -2542,6 +2597,61 @@ int dm_noflush_suspending(struct dm_target *ti)
}
EXPORT_SYMBOL_GPL(dm_noflush_suspending);

struct dm_md_mempools *dm_alloc_md_mempools(unsigned type)
{
	struct dm_md_mempools *pools = kmalloc(sizeof(*pools), GFP_KERNEL);

	if (!pools)
		return NULL;

	pools->io_pool = (type == DM_TYPE_BIO_BASED) ?
			 mempool_create_slab_pool(MIN_IOS, _io_cache) :
			 mempool_create_slab_pool(MIN_IOS, _rq_bio_info_cache);
	if (!pools->io_pool)
		goto free_pools_and_out;

	pools->tio_pool = (type == DM_TYPE_BIO_BASED) ?
			  mempool_create_slab_pool(MIN_IOS, _tio_cache) :
			  mempool_create_slab_pool(MIN_IOS, _rq_tio_cache);
	if (!pools->tio_pool)
		goto free_io_pool_and_out;

	pools->bs = (type == DM_TYPE_BIO_BASED) ?
		    bioset_create(16, 0) : bioset_create(MIN_IOS, 0);
	if (!pools->bs)
		goto free_tio_pool_and_out;

	return pools;

free_tio_pool_and_out:
	mempool_destroy(pools->tio_pool);

free_io_pool_and_out:
	mempool_destroy(pools->io_pool);

free_pools_and_out:
	kfree(pools);

	return NULL;
}

void dm_free_md_mempools(struct dm_md_mempools *pools)
{
	if (!pools)
		return;

	if (pools->io_pool)
		mempool_destroy(pools->io_pool);

	if (pools->tio_pool)
		mempool_destroy(pools->tio_pool);

	if (pools->bs)
		bioset_free(pools->bs);

	kfree(pools);
}

static struct block_device_operations dm_blk_dops = {
	.open = dm_blk_open,
	.release = dm_blk_close,
+25 −0
Original line number Diff line number Diff line
@@ -22,6 +22,13 @@
#define DM_SUSPEND_LOCKFS_FLAG		(1 << 0)
#define DM_SUSPEND_NOFLUSH_FLAG		(1 << 1)

/*
 * Type of table and mapped_device's mempool
 */
#define DM_TYPE_NONE		0
#define DM_TYPE_BIO_BASED	1
#define DM_TYPE_REQUEST_BASED	2

/*
 * List of devices that a metadevice uses and should open/close.
 */
@@ -32,6 +39,7 @@ struct dm_dev_internal {
};

struct dm_table;
struct dm_md_mempools;

/*-----------------------------------------------------------------
 * Internal table functions.
@@ -51,12 +59,23 @@ void dm_table_postsuspend_targets(struct dm_table *t);
int dm_table_resume_targets(struct dm_table *t);
int dm_table_any_congested(struct dm_table *t, int bdi_bits);
int dm_table_any_busy_target(struct dm_table *t);
int dm_table_set_type(struct dm_table *t);
unsigned dm_table_get_type(struct dm_table *t);
bool dm_table_request_based(struct dm_table *t);
int dm_table_alloc_md_mempools(struct dm_table *t);
void dm_table_free_md_mempools(struct dm_table *t);
struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t);

/*
 * To check the return value from dm_table_find_target().
 */
#define dm_target_is_valid(t) ((t)->table)

/*
 * To check whether the target type is request-based or not (bio-based).
 */
#define dm_target_request_based(t) ((t)->type->map_rq != NULL)

/*-----------------------------------------------------------------
 * A registry of target types.
 *---------------------------------------------------------------*/
@@ -102,4 +121,10 @@ void dm_kobject_uevent(struct mapped_device *md, enum kobject_action action,
int dm_kcopyd_init(void);
void dm_kcopyd_exit(void);

/*
 * Mempool operations
 */
struct dm_md_mempools *dm_alloc_md_mempools(unsigned type);
void dm_free_md_mempools(struct dm_md_mempools *pools);

#endif