Skip to content

Commit ab57a18

Browse files
Merge patch series "Optimize the hot path in the UFS driver"
Bart Van Assche <bvanassche@acm.org> says: Hi Martin, This patch series optimizes the hot path of the UFS driver by making struct scsi_cmnd and struct ufshcd_lrb adjacent. Making these two data structures adjacent is realized as follows: @@ -9040,6 +9046,7 @@ static const struct scsi_host_template ufshcd_driver_template = { .name = UFSHCD, .proc_name = UFSHCD, .map_queues = ufshcd_map_queues, + .cmd_size = sizeof(struct ufshcd_lrb), .init_cmd_priv = ufshcd_init_cmd_priv, .queuecommand = ufshcd_queuecommand, .mq_poll = ufshcd_poll, The following changes had to be made prior to making these two data structures adjacent: * Add support for driver-internal and reserved commands in the SCSI core. * Instead of making the reserved command slot (hba->reserved_slot) invisible to the SCSI core, let the SCSI core allocate a reserved command. * Remove all UFS data structure members that are no longer needed because struct scsi_cmnd and struct ufshcd_lrb are now adjacent * Call ufshcd_init_lrb() from inside the code for queueing a command instead of calling this function before I/O starts. This is necessary because ufshcd_memory_alloc() allocates fewer instances than the block layer allocates requests. See also the following code in the block layer core: if (blk_mq_init_request(set, hctx->fq->flush_rq, hctx_idx, hctx->numa_node)) Although the UFS driver could be modified such that ufshcd_init_lrb() is called from ufshcd_init_cmd_priv(), realizing this would require moving the memory allocations that happen from inside ufshcd_memory_alloc() into ufshcd_init_cmd_priv(). That would make this patch series even larger. Although ufshcd_init_lrb() is called for each command, the benefits of reduced indirection and better cache efficiency outweigh the small overhead of per-command lrb initialization. * ufshcd_add_scsi_host() happens now before any device management commands are submitted. This change is necessary because this patch makes device management command allocation happen when the SCSI host is allocated. * Allocate as many command slots as the host controller supports. Decrease host->cmds_per_lun if necessary once it is clear whether or not the UFS device supports less command slots than the host controller. Please consider this patch series for the next merge window. Thanks, Bart. Link: https://patch.msgid.link/20251031204029.2883185-1-bvanassche@acm.org Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
2 parents c53a741 + 08b12cd commit ab57a18

15 files changed

Lines changed: 815 additions & 474 deletions

File tree

drivers/scsi/hosts.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,12 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
231231
goto fail;
232232
}
233233

234+
if (shost->nr_reserved_cmds && !sht->queue_reserved_command) {
235+
shost_printk(KERN_ERR, shost,
236+
"nr_reserved_cmds set but no method to queue\n");
237+
goto fail;
238+
}
239+
234240
/* Use min_t(int, ...) in case shost->can_queue exceeds SHRT_MAX */
235241
shost->cmd_per_lun = min_t(int, shost->cmd_per_lun,
236242
shost->can_queue);
@@ -307,6 +313,14 @@ int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
307313
if (error)
308314
goto out_del_dev;
309315

316+
if (shost->nr_reserved_cmds) {
317+
shost->pseudo_sdev = scsi_get_pseudo_sdev(shost);
318+
if (!shost->pseudo_sdev) {
319+
error = -ENOMEM;
320+
goto out_del_dev;
321+
}
322+
}
323+
310324
scsi_proc_host_add(shost);
311325
scsi_autopm_put_host(shost);
312326
return error;
@@ -436,6 +450,7 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv
436450
shost->hostt = sht;
437451
shost->this_id = sht->this_id;
438452
shost->can_queue = sht->can_queue;
453+
shost->nr_reserved_cmds = sht->nr_reserved_cmds;
439454
shost->sg_tablesize = sht->sg_tablesize;
440455
shost->sg_prot_tablesize = sht->sg_prot_tablesize;
441456
shost->cmd_per_lun = sht->cmd_per_lun;

drivers/scsi/scsi.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,9 @@ int scsi_device_max_queue_depth(struct scsi_device *sdev)
216216
*/
217217
int scsi_change_queue_depth(struct scsi_device *sdev, int depth)
218218
{
219+
if (!sdev->budget_map.map)
220+
return -EINVAL;
221+
219222
depth = min_t(int, depth, scsi_device_max_queue_depth(sdev));
220223

221224
if (depth > 0) {
@@ -255,6 +258,8 @@ EXPORT_SYMBOL(scsi_change_queue_depth);
255258
*/
256259
int scsi_track_queue_full(struct scsi_device *sdev, int depth)
257260
{
261+
if (!sdev->budget_map.map)
262+
return 0;
258263

259264
/*
260265
* Don't let QUEUE_FULLs on the same
@@ -826,8 +831,11 @@ struct scsi_device *__scsi_iterate_devices(struct Scsi_Host *shost,
826831
spin_lock_irqsave(shost->host_lock, flags);
827832
while (list->next != &shost->__devices) {
828833
next = list_entry(list->next, struct scsi_device, siblings);
829-
/* skip devices that we can't get a reference to */
830-
if (!scsi_device_get(next))
834+
/*
835+
* Skip pseudo devices and also devices we can't get a
836+
* reference to.
837+
*/
838+
if (!scsi_device_is_pseudo_dev(next) && !scsi_device_get(next))
831839
break;
832840
next = NULL;
833841
list = list->next;

drivers/scsi/scsi_debug.c

Lines changed: 105 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6752,20 +6752,59 @@ static bool scsi_debug_stop_cmnd(struct scsi_cmnd *cmnd)
67526752
return false;
67536753
}
67546754

6755+
struct sdebug_abort_cmd {
6756+
u32 unique_tag;
6757+
};
6758+
6759+
enum sdebug_internal_cmd_type {
6760+
SCSI_DEBUG_ABORT_CMD,
6761+
};
6762+
6763+
struct sdebug_internal_cmd {
6764+
enum sdebug_internal_cmd_type type;
6765+
6766+
union {
6767+
struct sdebug_abort_cmd abort_cmd;
6768+
};
6769+
};
6770+
6771+
union sdebug_priv {
6772+
struct sdebug_scsi_cmd cmd;
6773+
struct sdebug_internal_cmd internal_cmd;
6774+
};
6775+
67556776
/*
6756-
* Called from scsi_debug_abort() only, which is for timed-out cmd.
6777+
* Abort SCSI command @cmnd. Only called from scsi_debug_abort(). Although
6778+
* it would be possible to call scsi_debug_stop_cmnd() directly, an internal
6779+
* command is allocated and submitted to trigger the reserved command
6780+
* infrastructure.
67576781
*/
67586782
static bool scsi_debug_abort_cmnd(struct scsi_cmnd *cmnd)
67596783
{
6760-
struct sdebug_scsi_cmd *sdsc = scsi_cmd_priv(cmnd);
6761-
unsigned long flags;
6762-
bool res;
6763-
6764-
spin_lock_irqsave(&sdsc->lock, flags);
6765-
res = scsi_debug_stop_cmnd(cmnd);
6766-
spin_unlock_irqrestore(&sdsc->lock, flags);
6767-
6768-
return res;
6784+
struct Scsi_Host *shost = cmnd->device->host;
6785+
struct request *rq = scsi_cmd_to_rq(cmnd);
6786+
u32 unique_tag = blk_mq_unique_tag(rq);
6787+
struct sdebug_internal_cmd *internal_cmd;
6788+
struct scsi_cmnd *abort_cmd;
6789+
struct request *abort_rq;
6790+
blk_status_t res;
6791+
6792+
abort_cmd = scsi_get_internal_cmd(shost->pseudo_sdev, DMA_NONE,
6793+
BLK_MQ_REQ_RESERVED);
6794+
if (!abort_cmd)
6795+
return false;
6796+
internal_cmd = scsi_cmd_priv(abort_cmd);
6797+
*internal_cmd = (struct sdebug_internal_cmd) {
6798+
.type = SCSI_DEBUG_ABORT_CMD,
6799+
.abort_cmd = {
6800+
.unique_tag = unique_tag,
6801+
},
6802+
};
6803+
abort_rq = scsi_cmd_to_rq(abort_cmd);
6804+
abort_rq->timeout = secs_to_jiffies(3);
6805+
res = blk_execute_rq(abort_rq, true);
6806+
scsi_put_internal_cmd(abort_cmd);
6807+
return res == BLK_STS_OK;
67696808
}
67706809

67716810
/*
@@ -9220,6 +9259,56 @@ static int sdebug_fail_cmd(struct scsi_cmnd *cmnd, int *retval,
92209259
return ret;
92219260
}
92229261

9262+
/* Process @scp, a request to abort a SCSI command by tag. */
9263+
static void scsi_debug_abort_cmd(struct Scsi_Host *shost, struct scsi_cmnd *scp)
9264+
{
9265+
struct sdebug_internal_cmd *internal_cmd = scsi_cmd_priv(scp);
9266+
struct sdebug_abort_cmd *abort_cmd = &internal_cmd->abort_cmd;
9267+
const u32 unique_tag = abort_cmd->unique_tag;
9268+
struct scsi_cmnd *to_be_aborted_scmd =
9269+
scsi_host_find_tag(shost, unique_tag);
9270+
struct sdebug_scsi_cmd *to_be_aborted_sdsc =
9271+
scsi_cmd_priv(to_be_aborted_scmd);
9272+
bool res = false;
9273+
9274+
if (!to_be_aborted_scmd) {
9275+
pr_err("%s: command with tag %#x not found\n", __func__,
9276+
unique_tag);
9277+
return;
9278+
}
9279+
9280+
scoped_guard(spinlock_irqsave, &to_be_aborted_sdsc->lock)
9281+
res = scsi_debug_stop_cmnd(to_be_aborted_scmd);
9282+
9283+
if (res)
9284+
pr_info("%s: aborted command with tag %#x\n",
9285+
__func__, unique_tag);
9286+
else
9287+
pr_err("%s: failed to abort command with tag %#x\n",
9288+
__func__, unique_tag);
9289+
9290+
set_host_byte(scp, res ? DID_OK : DID_ERROR);
9291+
}
9292+
9293+
static int scsi_debug_process_reserved_command(struct Scsi_Host *shost,
9294+
struct scsi_cmnd *scp)
9295+
{
9296+
struct sdebug_internal_cmd *internal_cmd = scsi_cmd_priv(scp);
9297+
9298+
switch (internal_cmd->type) {
9299+
case SCSI_DEBUG_ABORT_CMD:
9300+
scsi_debug_abort_cmd(shost, scp);
9301+
break;
9302+
default:
9303+
WARN_ON_ONCE(true);
9304+
set_host_byte(scp, DID_ERROR);
9305+
break;
9306+
}
9307+
9308+
scsi_done(scp);
9309+
return 0;
9310+
}
9311+
92239312
static int scsi_debug_queuecommand(struct Scsi_Host *shost,
92249313
struct scsi_cmnd *scp)
92259314
{
@@ -9420,6 +9509,9 @@ static int sdebug_init_cmd_priv(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
94209509
struct sdebug_scsi_cmd *sdsc = scsi_cmd_priv(cmd);
94219510
struct sdebug_defer *sd_dp = &sdsc->sd_dp;
94229511

9512+
if (blk_mq_is_reserved_rq(scsi_cmd_to_rq(cmd)))
9513+
return 0;
9514+
94239515
spin_lock_init(&sdsc->lock);
94249516
hrtimer_setup(&sd_dp->hrt, sdebug_q_cmd_hrt_complete, CLOCK_MONOTONIC,
94259517
HRTIMER_MODE_REL_PINNED);
@@ -9439,6 +9531,7 @@ static const struct scsi_host_template sdebug_driver_template = {
94399531
.sdev_destroy = scsi_debug_sdev_destroy,
94409532
.ioctl = scsi_debug_ioctl,
94419533
.queuecommand = scsi_debug_queuecommand,
9534+
.queue_reserved_command = scsi_debug_process_reserved_command,
94429535
.change_queue_depth = sdebug_change_qdepth,
94439536
.map_queues = sdebug_map_queues,
94449537
.mq_poll = sdebug_blk_mq_poll,
@@ -9448,6 +9541,7 @@ static const struct scsi_host_template sdebug_driver_template = {
94489541
.eh_bus_reset_handler = scsi_debug_bus_reset,
94499542
.eh_host_reset_handler = scsi_debug_host_reset,
94509543
.can_queue = SDEBUG_CANQUEUE,
9544+
.nr_reserved_cmds = 1,
94519545
.this_id = 7,
94529546
.sg_tablesize = SG_MAX_SEGMENTS,
94539547
.cmd_per_lun = DEF_CMD_PER_LUN,
@@ -9456,7 +9550,7 @@ static const struct scsi_host_template sdebug_driver_template = {
94569550
.module = THIS_MODULE,
94579551
.skip_settle_delay = 1,
94589552
.track_queue_depth = 1,
9459-
.cmd_size = sizeof(struct sdebug_scsi_cmd),
9553+
.cmd_size = sizeof(union sdebug_priv),
94609554
.init_cmd_priv = sdebug_init_cmd_priv,
94619555
.target_alloc = sdebug_target_alloc,
94629556
.target_destroy = sdebug_target_destroy,

drivers/scsi/scsi_error.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,9 @@ static void scsi_handle_queue_ramp_up(struct scsi_device *sdev)
749749
const struct scsi_host_template *sht = sdev->host->hostt;
750750
struct scsi_device *tmp_sdev;
751751

752+
if (!sdev->budget_map.map)
753+
return;
754+
752755
if (!sht->track_queue_depth ||
753756
sdev->queue_depth >= sdev->max_queue_depth)
754757
return;

0 commit comments

Comments
 (0)