scsi: core: Reallocate device's budget map on queue depth change

We currently use ->cmd_per_lun as initial queue depth for setting up the
budget_map. Martin Wilck reported that it is common for the queue_depth to
be subsequently updated in slave_configure() based on detected hardware
characteristics.

As a result, for some drivers, the static host template settings for
cmd_per_lun and can_queue won't actually get used in practice. And if the
default values are used to allocate the budget_map, memory may be consumed
unnecessarily.

Fix the issue by reallocating the budget_map after ->slave_configure()
returns. At that time the device queue_depth should accurately reflect what
the hardware needs.

Link: https://lore.kernel.org/r/20220127153733.409132-1-ming.lei@redhat.com
Cc: Bart Van Assche <bvanassche@acm.org>
Reported-by: Martin Wilck <martin.wilck@suse.com>
Suggested-by: Martin Wilck <martin.wilck@suse.com>
Tested-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
Ming Lei 2022-01-27 23:37:33 +08:00 committed by Martin K. Petersen
parent 936bd03405
commit edb854a368

View File

@ -214,6 +214,48 @@ static void scsi_unlock_floptical(struct scsi_device *sdev,
SCSI_TIMEOUT, 3, NULL); SCSI_TIMEOUT, 3, NULL);
} }
static int scsi_realloc_sdev_budget_map(struct scsi_device *sdev,
unsigned int depth)
{
int new_shift = sbitmap_calculate_shift(depth);
bool need_alloc = !sdev->budget_map.map;
bool need_free = false;
int ret;
struct sbitmap sb_backup;
/*
* realloc if new shift is calculated, which is caused by setting
* up one new default queue depth after calling ->slave_configure
*/
if (!need_alloc && new_shift != sdev->budget_map.shift)
need_alloc = need_free = true;
if (!need_alloc)
return 0;
/*
* Request queue has to be frozen for reallocating budget map,
* and here disk isn't added yet, so freezing is pretty fast
*/
if (need_free) {
blk_mq_freeze_queue(sdev->request_queue);
sb_backup = sdev->budget_map;
}
ret = sbitmap_init_node(&sdev->budget_map,
scsi_device_max_queue_depth(sdev),
new_shift, GFP_KERNEL,
sdev->request_queue->node, false, true);
if (need_free) {
if (ret)
sdev->budget_map = sb_backup;
else
sbitmap_free(&sb_backup);
ret = 0;
blk_mq_unfreeze_queue(sdev->request_queue);
}
return ret;
}
/** /**
* scsi_alloc_sdev - allocate and setup a scsi_Device * scsi_alloc_sdev - allocate and setup a scsi_Device
* @starget: which target to allocate a &scsi_device for * @starget: which target to allocate a &scsi_device for
@ -306,11 +348,7 @@ static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
* default device queue depth to figure out sbitmap shift * default device queue depth to figure out sbitmap shift
* since we use this queue depth most of times. * since we use this queue depth most of times.
*/ */
if (sbitmap_init_node(&sdev->budget_map, if (scsi_realloc_sdev_budget_map(sdev, depth)) {
scsi_device_max_queue_depth(sdev),
sbitmap_calculate_shift(depth),
GFP_KERNEL, sdev->request_queue->node,
false, true)) {
put_device(&starget->dev); put_device(&starget->dev);
kfree(sdev); kfree(sdev);
goto out; goto out;
@ -1017,6 +1055,13 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
} }
return SCSI_SCAN_NO_RESPONSE; return SCSI_SCAN_NO_RESPONSE;
} }
/*
* The queue_depth is often changed in ->slave_configure.
* Set up budget map again since memory consumption of
* the map depends on actual queue depth.
*/
scsi_realloc_sdev_budget_map(sdev, sdev->queue_depth);
} }
if (sdev->scsi_level >= SCSI_3) if (sdev->scsi_level >= SCSI_3)