diff options
author | Christoph Hellwig <hch@lst.de> | 2014-01-17 12:06:53 +0100 |
---|---|---|
committer | Christoph Hellwig <hch@lst.de> | 2014-07-25 23:16:28 +0200 |
commit | d285203cf647d7c97db3a1c33794315c9008593f (patch) | |
tree | a28a0a902cf3f467e326a02f5632b917b75c56a3 /include/scsi | |
parent | scatterlist: allow chaining to preallocated chunks (diff) | |
download | linux-d285203cf647d7c97db3a1c33794315c9008593f.tar.xz linux-d285203cf647d7c97db3a1c33794315c9008593f.zip |
scsi: add support for a blk-mq based I/O path.
This patch adds support for an alternate I/O path in the scsi midlayer
which uses the blk-mq infrastructure instead of the legacy request code.
Use of blk-mq is fully transparent to drivers, although for now a host
template field is provided to opt out of blk-mq usage in case any unforseen
incompatibilities arise.
In general replacing the legacy request code with blk-mq is a simple and
mostly mechanical transformation. The biggest exception is the new code
that deals with the fact the I/O submissions in blk-mq must happen from
process context, which slightly complicates the I/O completion handler.
The second biggest differences is that blk-mq is build around the concept
of preallocated requests that also include driver specific data, which
in SCSI context means the scsi_cmnd structure. This completely avoids
dynamic memory allocations for the fast path through I/O submission.
Due the preallocated requests the MQ code path exclusively uses the
host-wide shared tag allocator instead of a per-LUN one. This only
affects drivers actually using the block layer provided tag allocator
instead of their own. Unlike the old path blk-mq always provides a tag,
although drivers don't have to use it.
For now the blk-mq path is disable by defauly and must be enabled using
the "use_blk_mq" module parameter. Once the remaining work in the block
layer to make blk-mq more suitable for slow devices is complete I hope
to make it the default and eventually even remove the old code path.
Based on the earlier scsi-mq prototype by Nicholas Bellinger.
Thanks to Bart Van Assche and Robert Elliot for testing, benchmarking and
various sugestions and code contributions.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Reviewed-by: Webb Scales <webbnh@hp.com>
Acked-by: Jens Axboe <axboe@kernel.dk>
Tested-by: Bart Van Assche <bvanassche@acm.org>
Tested-by: Robert Elliott <elliott@hp.com>
Diffstat (limited to 'include/scsi')
-rw-r--r-- | include/scsi/scsi_host.h | 18 | ||||
-rw-r--r-- | include/scsi/scsi_tcq.h | 28 |
2 files changed, 42 insertions, 4 deletions
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h index 5e8ebc1ac12b..ba2034779961 100644 --- a/include/scsi/scsi_host.h +++ b/include/scsi/scsi_host.h @@ -7,6 +7,7 @@ #include <linux/workqueue.h> #include <linux/mutex.h> #include <linux/seq_file.h> +#include <linux/blk-mq.h> #include <scsi/scsi.h> struct request_queue; @@ -510,6 +511,9 @@ struct scsi_host_template { */ unsigned int cmd_size; struct scsi_host_cmd_pool *cmd_pool; + + /* temporary flag to disable blk-mq I/O path */ + bool disable_blk_mq; }; /* @@ -580,7 +584,10 @@ struct Scsi_Host { * Area to keep a shared tag map (if needed, will be * NULL if not). */ - struct blk_queue_tag *bqt; + union { + struct blk_queue_tag *bqt; + struct blk_mq_tag_set tag_set; + }; atomic_t host_busy; /* commands actually active on low-level */ atomic_t host_blocked; @@ -672,6 +679,8 @@ struct Scsi_Host { /* The controller does not support WRITE SAME */ unsigned no_write_same:1; + unsigned use_blk_mq:1; + /* * Optional work queue to be utilized by the transport */ @@ -772,6 +781,13 @@ static inline int scsi_host_in_recovery(struct Scsi_Host *shost) shost->tmf_in_progress; } +extern bool scsi_use_blk_mq; + +static inline bool shost_use_blk_mq(struct Scsi_Host *shost) +{ + return shost->use_blk_mq; +} + extern int scsi_queue_work(struct Scsi_Host *, struct work_struct *); extern void scsi_flush_work(struct Scsi_Host *); diff --git a/include/scsi/scsi_tcq.h b/include/scsi/scsi_tcq.h index 81dd12edc38c..cdcc90b07ecb 100644 --- a/include/scsi/scsi_tcq.h +++ b/include/scsi/scsi_tcq.h @@ -67,7 +67,8 @@ static inline void scsi_activate_tcq(struct scsi_device *sdev, int depth) if (!sdev->tagged_supported) return; - if (!blk_queue_tagged(sdev->request_queue)) + if (!shost_use_blk_mq(sdev->host) && + blk_queue_tagged(sdev->request_queue)) blk_queue_init_tags(sdev->request_queue, depth, sdev->host->bqt); @@ -80,7 +81,8 @@ static inline void scsi_activate_tcq(struct scsi_device *sdev, int depth) **/ static inline void scsi_deactivate_tcq(struct scsi_device *sdev, int depth) { - if (blk_queue_tagged(sdev->request_queue)) + if (!shost_use_blk_mq(sdev->host) && + blk_queue_tagged(sdev->request_queue)) blk_queue_free_tags(sdev->request_queue); scsi_adjust_queue_depth(sdev, 0, depth); } @@ -108,6 +110,15 @@ static inline int scsi_populate_tag_msg(struct scsi_cmnd *cmd, char *msg) return 0; } +static inline struct scsi_cmnd *scsi_mq_find_tag(struct Scsi_Host *shost, + unsigned int hw_ctx, int tag) +{ + struct request *req; + + req = blk_mq_tag_to_rq(shost->tag_set.tags[hw_ctx], tag); + return req ? (struct scsi_cmnd *)req->special : NULL; +} + /** * scsi_find_tag - find a tagged command by device * @SDpnt: pointer to the ScSI device @@ -118,10 +129,12 @@ static inline int scsi_populate_tag_msg(struct scsi_cmnd *cmd, char *msg) **/ static inline struct scsi_cmnd *scsi_find_tag(struct scsi_device *sdev, int tag) { - struct request *req; if (tag != SCSI_NO_TAG) { + if (shost_use_blk_mq(sdev->host)) + return scsi_mq_find_tag(sdev->host, 0, tag); + req = blk_queue_find_tag(sdev->request_queue, tag); return req ? (struct scsi_cmnd *)req->special : NULL; } @@ -130,6 +143,7 @@ static inline struct scsi_cmnd *scsi_find_tag(struct scsi_device *sdev, int tag) return sdev->current_cmnd; } + /** * scsi_init_shared_tag_map - create a shared tag map * @shost: the host to share the tag map among all devices @@ -138,6 +152,12 @@ static inline struct scsi_cmnd *scsi_find_tag(struct scsi_device *sdev, int tag) static inline int scsi_init_shared_tag_map(struct Scsi_Host *shost, int depth) { /* + * We always have a shared tag map around when using blk-mq. + */ + if (shost_use_blk_mq(shost)) + return 0; + + /* * If the shared tag map isn't already initialized, do it now. * This saves callers from having to check ->bqt when setting up * devices on the shared host (for libata) @@ -165,6 +185,8 @@ static inline struct scsi_cmnd *scsi_host_find_tag(struct Scsi_Host *shost, struct request *req; if (tag != SCSI_NO_TAG) { + if (shost_use_blk_mq(shost)) + return scsi_mq_find_tag(shost, 0, tag); req = blk_map_queue_find_tag(shost->bqt, tag); return req ? (struct scsi_cmnd *)req->special : NULL; } |