Skip to content

Commit

Permalink
dm mpath: add optional "queue_mode" feature
Browse files Browse the repository at this point in the history
Allow a user to specify an optional feature 'queue_mode <mode>' where
<mode> may be "bio", "rq" or "mq" -- which corresponds to bio-based,
request_fn rq-based, and blk-mq rq-based respectively.

If the queue_mode feature isn't specified the default for the
"multipath" target is still "rq" but if dm_mod.use_blk_mq is set to Y
it'll default to mode "mq".

This new queue_mode feature introduces the ability for each multipath
device to have its own queue_mode (whereas before this feature all
multipath devices effectively had to have the same queue_mode).

This commit also goes a long way to eliminate the awkward (ab)use of
DM_TYPE_*, the associated filter_md_type() and other relatively fragile
and difficult to maintain code.

Signed-off-by: Mike Snitzer <snitzer@redhat.com>
  • Loading branch information
Mike Snitzer committed Jun 10, 2016
1 parent bf661be commit e83068a
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 119 deletions.
149 changes: 80 additions & 69 deletions drivers/md/dm-mpath.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ struct multipath {
atomic_t pg_init_in_progress; /* Only one pg_init allowed at once */
atomic_t pg_init_count; /* Number of times pg_init called */

unsigned queue_mode;

/*
* We must use a mempool of dm_mpath_io structs so that we
* can resubmit bios on error.
Expand Down Expand Up @@ -131,7 +133,6 @@ static void process_queued_bios(struct work_struct *work);
#define MPATHF_PG_INIT_DISABLED 4 /* pg_init is not currently allowed */
#define MPATHF_PG_INIT_REQUIRED 5 /* pg_init needs calling? */
#define MPATHF_PG_INIT_DELAY_RETRY 6 /* Delay pg_init retry? */
#define MPATHF_BIO_BASED 7 /* Device is bio-based? */

/*-----------------------------------------------
* Allocation routines
Expand Down Expand Up @@ -191,8 +192,7 @@ static void free_priority_group(struct priority_group *pg,
kfree(pg);
}

static struct multipath *alloc_multipath(struct dm_target *ti, bool use_blk_mq,
bool bio_based)
static struct multipath *alloc_multipath(struct dm_target *ti)
{
struct multipath *m;

Expand All @@ -210,25 +210,7 @@ static struct multipath *alloc_multipath(struct dm_target *ti, bool use_blk_mq,
mutex_init(&m->work_mutex);

m->mpio_pool = NULL;
if (!use_blk_mq && !bio_based) {
unsigned min_ios = dm_get_reserved_rq_based_ios();

m->mpio_pool = mempool_create_slab_pool(min_ios, _mpio_cache);
if (!m->mpio_pool) {
kfree(m);
return NULL;
}
}

if (bio_based) {
INIT_WORK(&m->process_queued_bios, process_queued_bios);
set_bit(MPATHF_BIO_BASED, &m->flags);
/*
* bio-based doesn't support any direct scsi_dh management;
* it just discovers if a scsi_dh is attached.
*/
set_bit(MPATHF_RETAIN_ATTACHED_HW_HANDLER, &m->flags);
}
m->queue_mode = DM_TYPE_NONE;

m->ti = ti;
ti->private = m;
Expand All @@ -237,6 +219,39 @@ static struct multipath *alloc_multipath(struct dm_target *ti, bool use_blk_mq,
return m;
}

static int alloc_multipath_stage2(struct dm_target *ti, struct multipath *m)
{
if (m->queue_mode == DM_TYPE_NONE) {
/*
* Default to request-based.
*/
if (dm_use_blk_mq(dm_table_get_md(ti->table)))
m->queue_mode = DM_TYPE_MQ_REQUEST_BASED;
else
m->queue_mode = DM_TYPE_REQUEST_BASED;
}

if (m->queue_mode == DM_TYPE_REQUEST_BASED) {
unsigned min_ios = dm_get_reserved_rq_based_ios();

m->mpio_pool = mempool_create_slab_pool(min_ios, _mpio_cache);
if (!m->mpio_pool)
return -ENOMEM;
}
else if (m->queue_mode == DM_TYPE_BIO_BASED) {
INIT_WORK(&m->process_queued_bios, process_queued_bios);
/*
* bio-based doesn't support any direct scsi_dh management;
* it just discovers if a scsi_dh is attached.
*/
set_bit(MPATHF_RETAIN_ATTACHED_HW_HANDLER, &m->flags);
}

dm_table_set_type(ti->table, m->queue_mode);

return 0;
}

static void free_multipath(struct multipath *m)
{
struct priority_group *pg, *tmp;
Expand Down Expand Up @@ -653,7 +668,7 @@ static int multipath_map_bio(struct dm_target *ti, struct bio *bio)

static void process_queued_bios_list(struct multipath *m)
{
if (test_bit(MPATHF_BIO_BASED, &m->flags))
if (m->queue_mode == DM_TYPE_BIO_BASED)
queue_work(kmultipathd, &m->process_queued_bios);
}

Expand Down Expand Up @@ -964,7 +979,7 @@ static int parse_hw_handler(struct dm_arg_set *as, struct multipath *m)
if (!hw_argc)
return 0;

if (test_bit(MPATHF_BIO_BASED, &m->flags)) {
if (m->queue_mode == DM_TYPE_BIO_BASED) {
dm_consume_args(as, hw_argc);
DMERR("bio-based multipath doesn't allow hardware handler args");
return 0;
Expand Down Expand Up @@ -1005,7 +1020,7 @@ static int parse_features(struct dm_arg_set *as, struct multipath *m)
const char *arg_name;

static struct dm_arg _args[] = {
{0, 6, "invalid number of feature args"},
{0, 8, "invalid number of feature args"},
{1, 50, "pg_init_retries must be between 1 and 50"},
{0, 60000, "pg_init_delay_msecs must be between 0 and 60000"},
};
Expand Down Expand Up @@ -1045,15 +1060,32 @@ static int parse_features(struct dm_arg_set *as, struct multipath *m)
continue;
}

if (!strcasecmp(arg_name, "queue_mode") &&
(argc >= 1)) {
const char *queue_mode_name = dm_shift_arg(as);

if (!strcasecmp(queue_mode_name, "bio"))
m->queue_mode = DM_TYPE_BIO_BASED;
else if (!strcasecmp(queue_mode_name, "rq"))
m->queue_mode = DM_TYPE_REQUEST_BASED;
else if (!strcasecmp(queue_mode_name, "mq"))
m->queue_mode = DM_TYPE_MQ_REQUEST_BASED;
else {
ti->error = "Unknown 'queue_mode' requested";
r = -EINVAL;
}
argc--;
continue;
}

ti->error = "Unrecognised multipath feature request";
r = -EINVAL;
} while (argc && !r);

return r;
}

static int __multipath_ctr(struct dm_target *ti, unsigned int argc,
char **argv, bool bio_based)
static int multipath_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
/* target arguments */
static struct dm_arg _args[] = {
Expand All @@ -1066,12 +1098,11 @@ static int __multipath_ctr(struct dm_target *ti, unsigned int argc,
struct dm_arg_set as;
unsigned pg_count = 0;
unsigned next_pg_num;
bool use_blk_mq = dm_use_blk_mq(dm_table_get_md(ti->table));

as.argc = argc;
as.argv = argv;

m = alloc_multipath(ti, use_blk_mq, bio_based);
m = alloc_multipath(ti);
if (!m) {
ti->error = "can't allocate multipath";
return -EINVAL;
Expand All @@ -1081,6 +1112,10 @@ static int __multipath_ctr(struct dm_target *ti, unsigned int argc,
if (r)
goto bad;

r = alloc_multipath_stage2(ti, m);
if (r)
goto bad;

r = parse_hw_handler(&as, m);
if (r)
goto bad;
Expand Down Expand Up @@ -1130,9 +1165,9 @@ static int __multipath_ctr(struct dm_target *ti, unsigned int argc,
ti->num_flush_bios = 1;
ti->num_discard_bios = 1;
ti->num_write_same_bios = 1;
if (bio_based)
if (m->queue_mode == DM_TYPE_BIO_BASED)
ti->per_io_data_size = multipath_per_bio_data_size();
else if (use_blk_mq)
else if (m->queue_mode == DM_TYPE_MQ_REQUEST_BASED)
ti->per_io_data_size = sizeof(struct dm_mpath_io);

return 0;
Expand All @@ -1142,16 +1177,6 @@ static int __multipath_ctr(struct dm_target *ti, unsigned int argc,
return r;
}

static int multipath_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
return __multipath_ctr(ti, argc, argv, false);
}

static int multipath_bio_ctr(struct dm_target *ti, unsigned argc, char **argv)
{
return __multipath_ctr(ti, argc, argv, true);
}

static void multipath_wait_for_pg_init_completion(struct multipath *m)
{
DECLARE_WAITQUEUE(wait, current);
Expand Down Expand Up @@ -1700,7 +1725,9 @@ static void multipath_status(struct dm_target *ti, status_type_t type,
DMEMIT("%u ", test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags) +
(m->pg_init_retries > 0) * 2 +
(m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) * 2 +
test_bit(MPATHF_RETAIN_ATTACHED_HW_HANDLER, &m->flags));
test_bit(MPATHF_RETAIN_ATTACHED_HW_HANDLER, &m->flags) +
(m->queue_mode != DM_TYPE_REQUEST_BASED) * 2);

if (test_bit(MPATHF_QUEUE_IF_NO_PATH, &m->flags))
DMEMIT("queue_if_no_path ");
if (m->pg_init_retries)
Expand All @@ -1709,6 +1736,16 @@ static void multipath_status(struct dm_target *ti, status_type_t type,
DMEMIT("pg_init_delay_msecs %u ", m->pg_init_delay_msecs);
if (test_bit(MPATHF_RETAIN_ATTACHED_HW_HANDLER, &m->flags))
DMEMIT("retain_attached_hw_handler ");
if (m->queue_mode != DM_TYPE_REQUEST_BASED) {
switch(m->queue_mode) {
case DM_TYPE_BIO_BASED:
DMEMIT("queue_mode bio ");
break;
case DM_TYPE_MQ_REQUEST_BASED:
DMEMIT("queue_mode mq ");
break;
}
}
}

if (!m->hw_handler_name || type == STATUSTYPE_INFO)
Expand Down Expand Up @@ -1995,7 +2032,7 @@ static int multipath_busy(struct dm_target *ti)
*---------------------------------------------------------------*/
static struct target_type multipath_target = {
.name = "multipath",
.version = {1, 11, 0},
.version = {1, 12, 0},
.features = DM_TARGET_SINGLETON | DM_TARGET_IMMUTABLE,
.module = THIS_MODULE,
.ctr = multipath_ctr,
Expand All @@ -2004,22 +2041,6 @@ static struct target_type multipath_target = {
.clone_and_map_rq = multipath_clone_and_map,
.release_clone_rq = multipath_release_clone,
.rq_end_io = multipath_end_io,
.presuspend = multipath_presuspend,
.postsuspend = multipath_postsuspend,
.resume = multipath_resume,
.status = multipath_status,
.message = multipath_message,
.prepare_ioctl = multipath_prepare_ioctl,
.iterate_devices = multipath_iterate_devices,
.busy = multipath_busy,
};

static struct target_type multipath_bio_target = {
.name = "multipath-bio",
.version = {1, 0, 0},
.module = THIS_MODULE,
.ctr = multipath_bio_ctr,
.dtr = multipath_dtr,
.map = multipath_map_bio,
.end_io = multipath_end_io_bio,
.presuspend = multipath_presuspend,
Expand Down Expand Up @@ -2048,13 +2069,6 @@ static int __init dm_multipath_init(void)
goto bad_register_target;
}

r = dm_register_target(&multipath_bio_target);
if (r < 0) {
DMERR("bio-based register failed %d", r);
r = -EINVAL;
goto bad_register_bio_based_target;
}

kmultipathd = alloc_workqueue("kmpathd", WQ_MEM_RECLAIM, 0);
if (!kmultipathd) {
DMERR("failed to create workqueue kmpathd");
Expand All @@ -2081,8 +2095,6 @@ static int __init dm_multipath_init(void)
bad_alloc_kmpath_handlerd:
destroy_workqueue(kmultipathd);
bad_alloc_kmultipathd:
dm_unregister_target(&multipath_bio_target);
bad_register_bio_based_target:
dm_unregister_target(&multipath_target);
bad_register_target:
kmem_cache_destroy(_mpio_cache);
Expand All @@ -2096,7 +2108,6 @@ static void __exit dm_multipath_exit(void)
destroy_workqueue(kmultipathd);

dm_unregister_target(&multipath_target);
dm_unregister_target(&multipath_bio_target);
kmem_cache_destroy(_mpio_cache);
}

Expand Down
19 changes: 14 additions & 5 deletions drivers/md/dm-rq.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,14 @@ static void free_rq_clone(struct request *clone)

blk_rq_unprep_clone(clone);

if (md->type == DM_TYPE_MQ_REQUEST_BASED)
/*
* It is possible for a clone_old_rq() allocated clone to
* get passed in -- it may not yet have a request_queue.
* This is known to occur if the error target replaces
* a multipath target that has a request_fn queue stacked
* on blk-mq queue(s).
*/
if (clone->q && clone->q->mq_ops)
/* stacked on blk-mq queue(s) */
tio->ti->type->release_clone_rq(clone);
else if (!md->queue->mq_ops)
Expand Down Expand Up @@ -561,7 +568,7 @@ static struct dm_rq_target_io *dm_old_prep_tio(struct request *rq,
* Must clone a request if this .request_fn DM device
* is stacked on .request_fn device(s).
*/
if (!dm_table_mq_request_based(table)) {
if (!dm_table_all_blk_mq_devices(table)) {
if (!clone_old_rq(rq, md, tio, gfp_mask)) {
dm_put_live_table(md, srcu_idx);
free_old_rq_tio(tio);
Expand Down Expand Up @@ -711,7 +718,7 @@ ssize_t dm_attr_rq_based_seq_io_merge_deadline_store(struct mapped_device *md,
{
unsigned deadline;

if (!dm_request_based(md) || md->use_blk_mq)
if (dm_get_md_type(md) != DM_TYPE_REQUEST_BASED)
return count;

if (kstrtouint(buf, 10, &deadline))
Expand Down Expand Up @@ -886,12 +893,13 @@ static struct blk_mq_ops dm_mq_ops = {
.init_request = dm_mq_init_request,
};

int dm_mq_init_request_queue(struct mapped_device *md, struct dm_target *immutable_tgt)
int dm_mq_init_request_queue(struct mapped_device *md, struct dm_table *t)
{
struct request_queue *q;
struct dm_target *immutable_tgt;
int err;

if (dm_get_md_type(md) == DM_TYPE_REQUEST_BASED) {
if (!dm_table_all_blk_mq_devices(t)) {
DMERR("request-based dm-mq may only be stacked on blk-mq device(s)");
return -EINVAL;
}
Expand All @@ -908,6 +916,7 @@ int dm_mq_init_request_queue(struct mapped_device *md, struct dm_target *immutab
md->tag_set->driver_data = md;

md->tag_set->cmd_size = sizeof(struct dm_rq_target_io);
immutable_tgt = dm_table_get_immutable_target(t);
if (immutable_tgt && immutable_tgt->per_io_data_size) {
/* any target-specific per-io data is immediately after the tio */
md->tag_set->cmd_size += immutable_tgt->per_io_data_size;
Expand Down
2 changes: 1 addition & 1 deletion drivers/md/dm-rq.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ bool dm_use_blk_mq_default(void);
bool dm_use_blk_mq(struct mapped_device *md);

int dm_old_init_request_queue(struct mapped_device *md);
int dm_mq_init_request_queue(struct mapped_device *md, struct dm_target *immutable_tgt);
int dm_mq_init_request_queue(struct mapped_device *md, struct dm_table *t);
void dm_mq_cleanup_mapped_device(struct mapped_device *md);

void dm_start_queue(struct request_queue *q);
Expand Down
Loading

0 comments on commit e83068a

Please sign in to comment.