Commit 8930a6c2 authored by Paolo Bonzini's avatar Paolo Bonzini Committed by Martin K. Petersen

scsi: core: add support for request batching

This allows a list of requests to be issued, with the LLD only writing the
hardware doorbell when necessary, after the last request was prepared.
This is more efficient if we have lists of requests to issue, particularly
on virtualized hardware, where writing the doorbell is more expensive than
on real hardware.

The use case for this is plugged IO, where blk-mq flushes a batch of
requests all at once.

The API is the same as for blk-mq, just with blk-mq concepts tweaked to
fit the SCSI subsystem API: the "last" flag in blk_mq_queue_data becomes a
flag in scsi_cmnd, while the queue_num in the commit_rqs callback is
extracted from the hctx and passed as a parameter.

The only complication is that blk-mq uses different plugging heuristics
depending on whether commit_rqs is present or not.  So we have two
different sets of blk_mq_ops and pick one depending on whether the
scsi_host template uses commit_rqs or not.
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
Reviewed-by: default avatarBart Van Assche <bvanassche@acm.org>
Reviewed-by: default avatarMing Lei <ming.lei@redhat.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent cf949bbe
...@@ -1666,10 +1666,11 @@ static blk_status_t scsi_queue_rq(struct blk_mq_hw_ctx *hctx, ...@@ -1666,10 +1666,11 @@ static blk_status_t scsi_queue_rq(struct blk_mq_hw_ctx *hctx,
blk_mq_start_request(req); blk_mq_start_request(req);
} }
cmd->flags &= SCMD_PRESERVED_FLAGS;
if (sdev->simple_tags) if (sdev->simple_tags)
cmd->flags |= SCMD_TAGGED; cmd->flags |= SCMD_TAGGED;
else if (bd->last)
cmd->flags &= ~SCMD_TAGGED; cmd->flags |= SCMD_LAST;
scsi_init_cmd_errh(cmd); scsi_init_cmd_errh(cmd);
cmd->scsi_done = scsi_mq_done; cmd->scsi_done = scsi_mq_done;
...@@ -1807,10 +1808,37 @@ void __scsi_init_queue(struct Scsi_Host *shost, struct request_queue *q) ...@@ -1807,10 +1808,37 @@ void __scsi_init_queue(struct Scsi_Host *shost, struct request_queue *q)
} }
EXPORT_SYMBOL_GPL(__scsi_init_queue); EXPORT_SYMBOL_GPL(__scsi_init_queue);
static const struct blk_mq_ops scsi_mq_ops_no_commit = {
.get_budget = scsi_mq_get_budget,
.put_budget = scsi_mq_put_budget,
.queue_rq = scsi_queue_rq,
.complete = scsi_softirq_done,
.timeout = scsi_timeout,
#ifdef CONFIG_BLK_DEBUG_FS
.show_rq = scsi_show_rq,
#endif
.init_request = scsi_mq_init_request,
.exit_request = scsi_mq_exit_request,
.initialize_rq_fn = scsi_initialize_rq,
.busy = scsi_mq_lld_busy,
.map_queues = scsi_map_queues,
};
static void scsi_commit_rqs(struct blk_mq_hw_ctx *hctx)
{
struct request_queue *q = hctx->queue;
struct scsi_device *sdev = q->queuedata;
struct Scsi_Host *shost = sdev->host;
shost->hostt->commit_rqs(shost, hctx->queue_num);
}
static const struct blk_mq_ops scsi_mq_ops = { static const struct blk_mq_ops scsi_mq_ops = {
.get_budget = scsi_mq_get_budget, .get_budget = scsi_mq_get_budget,
.put_budget = scsi_mq_put_budget, .put_budget = scsi_mq_put_budget,
.queue_rq = scsi_queue_rq, .queue_rq = scsi_queue_rq,
.commit_rqs = scsi_commit_rqs,
.complete = scsi_softirq_done, .complete = scsi_softirq_done,
.timeout = scsi_timeout, .timeout = scsi_timeout,
#ifdef CONFIG_BLK_DEBUG_FS #ifdef CONFIG_BLK_DEBUG_FS
...@@ -1846,7 +1874,10 @@ int scsi_mq_setup_tags(struct Scsi_Host *shost) ...@@ -1846,7 +1874,10 @@ int scsi_mq_setup_tags(struct Scsi_Host *shost)
sizeof(struct scatterlist) * SCSI_INLINE_PROT_SG_CNT; sizeof(struct scatterlist) * SCSI_INLINE_PROT_SG_CNT;
memset(&shost->tag_set, 0, sizeof(shost->tag_set)); memset(&shost->tag_set, 0, sizeof(shost->tag_set));
shost->tag_set.ops = &scsi_mq_ops; if (shost->hostt->commit_rqs)
shost->tag_set.ops = &scsi_mq_ops;
else
shost->tag_set.ops = &scsi_mq_ops_no_commit;
shost->tag_set.nr_hw_queues = shost->nr_hw_queues ? : 1; shost->tag_set.nr_hw_queues = shost->nr_hw_queues ? : 1;
shost->tag_set.queue_depth = shost->can_queue; shost->tag_set.queue_depth = shost->can_queue;
shost->tag_set.cmd_size = cmd_size; shost->tag_set.cmd_size = cmd_size;
......
...@@ -57,6 +57,7 @@ struct scsi_pointer { ...@@ -57,6 +57,7 @@ struct scsi_pointer {
#define SCMD_TAGGED (1 << 0) #define SCMD_TAGGED (1 << 0)
#define SCMD_UNCHECKED_ISA_DMA (1 << 1) #define SCMD_UNCHECKED_ISA_DMA (1 << 1)
#define SCMD_INITIALIZED (1 << 2) #define SCMD_INITIALIZED (1 << 2)
#define SCMD_LAST (1 << 3)
/* flags preserved across unprep / reprep */ /* flags preserved across unprep / reprep */
#define SCMD_PRESERVED_FLAGS (SCMD_UNCHECKED_ISA_DMA | SCMD_INITIALIZED) #define SCMD_PRESERVED_FLAGS (SCMD_UNCHECKED_ISA_DMA | SCMD_INITIALIZED)
......
...@@ -80,8 +80,10 @@ struct scsi_host_template { ...@@ -80,8 +80,10 @@ struct scsi_host_template {
* command block to the LLDD. When the driver finished * command block to the LLDD. When the driver finished
* processing the command the done callback is invoked. * processing the command the done callback is invoked.
* *
* If queuecommand returns 0, then the HBA has accepted the * If queuecommand returns 0, then the driver has accepted the
* command. The done() function must be called on the command * command. It must also push it to the HBA if the scsi_cmnd
* flag SCMD_LAST is set, or if the driver does not implement
* commit_rqs. The done() function must be called on the command
* when the driver has finished with it. (you may call done on the * when the driver has finished with it. (you may call done on the
* command before queuecommand returns, but in this case you * command before queuecommand returns, but in this case you
* *must* return 0 from queuecommand). * *must* return 0 from queuecommand).
...@@ -109,6 +111,16 @@ struct scsi_host_template { ...@@ -109,6 +111,16 @@ struct scsi_host_template {
*/ */
int (* queuecommand)(struct Scsi_Host *, struct scsi_cmnd *); int (* queuecommand)(struct Scsi_Host *, struct scsi_cmnd *);
/*
* The commit_rqs function is used to trigger a hardware
* doorbell after some requests have been queued with
* queuecommand, when an error is encountered before sending
* the request with SCMD_LAST set.
*
* STATUS: OPTIONAL
*/
void (*commit_rqs)(struct Scsi_Host *, u16);
/* /*
* This is an error handling strategy routine. You don't need to * This is an error handling strategy routine. You don't need to
* define one of these if you don't want to - there is a default * define one of these if you don't want to - there is a default
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment