
From: Simon Glass <sjg@chromium.org> The current algortihm scans all LUNs on all targets. Where the potential number is very large, as with QEMU (256 targets each with 4K LUNs) this is a substantial waste of time. Use the REPORT LUNS command to determine the number of LUNs on each target. Where that fails, fall back to scanning everything. Signed-off-by: Simon Glass <sjg@chromium.org> --- drivers/scsi/scsi.c | 77 ++++++++++++++++++++++++++++++++++++++++++--- include/scsi.h | 1 + 2 files changed, 73 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 560f7fe35ec..fee0471c2e4 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -17,6 +17,7 @@ #include <part.h> #include <pci.h> #include <scsi.h> +#include <asm/unaligned.h> #include <dm/device-internal.h> #include <dm/uclass-internal.h> @@ -383,6 +384,58 @@ static void scsi_init_dev_desc_priv(struct blk_desc *dev_desc) #endif /* CONFIG_BOUNCE_BUFFER */ } +/** + * scsi_count_luns() - Count the number of LUNs on a given target + * + * @dev: SCSI device + * @target: Target to look up + * Return: Number of LUNs on this target, or 0 if None, or -ve on error + */ +static int scsi_count_luns(struct udevice *dev, uint target) +{ + struct scsi_cmd *pccb = (struct scsi_cmd *)&tempccb; + uint rec_count; + int ret, i, max_lun; + u8 *luns; + + memset(pccb->cmd, '\0', sizeof(pccb->cmd)); + pccb->cmd[0] = SCSI_REPORT_LUNS; + pccb->target = target; + pccb->lun = 0; + + /* Select Report: 0x00 for all LUNs */ + pccb->cmd[2] = 0; + put_unaligned_be32(TEMPBUFF_SIZE, &pccb->cmd[6]); + + pccb->cmdlen = 12; + pccb->pdata = tempbuff; + pccb->datalen = TEMPBUFF_SIZE; + pccb->dma_dir = DMA_FROM_DEVICE; + + ret = scsi_exec(dev, pccb); + if (ret == -ENODEV) { + /* target doesn't exist, return 0 */ + return 0; + } + if (ret) { + scsi_print_error(pccb); + return -EINVAL; + } + + /* find the maximum LUN */ + rec_count = get_unaligned_be32(tempbuff) / 8; + luns = &tempbuff[8]; + + max_lun = -1; + for (i = 0; i < rec_count; i++, luns += 8) { + int lun = get_unaligned_be16(luns) & 0x3fff; + + max_lun = max(max_lun, lun); + } + + return max_lun + 1; +} + /** * scsi_detect_dev - Detect scsi device * @@ -539,9 +592,7 @@ static int do_scsi_scan_one(struct udevice *dev, int id, int lun, bool verbose) int scsi_scan_dev(struct udevice *dev, bool verbose) { struct scsi_plat *uc_plat; /* scsi controller plat */ - int ret; - int i; - int lun; + int ret, i; /* probe SCSI controller driver */ ret = device_probe(dev); @@ -551,9 +602,25 @@ int scsi_scan_dev(struct udevice *dev, bool verbose) /* Get controller plat */ uc_plat = dev_get_uclass_plat(dev); - for (i = 0; i <= uc_plat->max_id; i++) - for (lun = 0; lun <= uc_plat->max_lun; lun++) + log_debug("max_id %lx max_lun %lx\n", uc_plat->max_id, + uc_plat->max_lun); + + for (i = 0; i <= uc_plat->max_id; i++) { + uint lun_count, lun; + + /* try to count the number of LUNs */ + ret = scsi_count_luns(dev, i); + if (ret < 0) + lun_count = uc_plat->max_lun + 1; + else + lun_count = ret; + if (!lun_count) + continue; + log_debug("Target %x: scanning up to LUN %x\n", i, + lun_count - 1); + for (lun = 0; lun < lun_count; lun++) do_scsi_scan_one(dev, i, lun, verbose); + } return 0; } diff --git a/include/scsi.h b/include/scsi.h index d3bf4ed2b9c..b81bc762677 100644 --- a/include/scsi.h +++ b/include/scsi.h @@ -184,6 +184,7 @@ struct scsi_cmd { #define SCSI_WRT_VERIFY 0x2E /* Write and Verify (O) */ #define SCSI_WRITE_LONG 0x3F /* Write Long (O) */ #define SCSI_WRITE_SAME 0x41 /* Write Same (O) */ +#define SCSI_REPORT_LUNS 0xa0 /* Report LUNs */ /** * enum scsi_cmd_phase - current phase of the SCSI protocol -- 2.43.0