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>
This commit is contained in:
@@ -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 *);
|
||||
|
||||
|
@@ -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
|
||||
@@ -137,6 +151,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
|
||||
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user