Module Name:    src
Committed By:   jakllsch
Date:           Thu Jan 27 18:37:02 UTC 2022

Modified Files:
        src/sys/dev/scsipi: scsi_spc.h scsiconf.c

Log Message:
Try REPORT LUNS command to enumerate logical units.


To generate a diff of this commit:
cvs rdiff -u -r1.6 -r1.7 src/sys/dev/scsipi/scsi_spc.h
cvs rdiff -u -r1.293 -r1.294 src/sys/dev/scsipi/scsiconf.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/scsipi/scsi_spc.h
diff -u src/sys/dev/scsipi/scsi_spc.h:1.6 src/sys/dev/scsipi/scsi_spc.h:1.7
--- src/sys/dev/scsipi/scsi_spc.h:1.6	Thu Mar 28 10:44:29 2019
+++ src/sys/dev/scsipi/scsi_spc.h	Thu Jan 27 18:37:02 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: scsi_spc.h,v 1.6 2019/03/28 10:44:29 kardel Exp $	*/
+/*	$NetBSD: scsi_spc.h,v 1.7 2022/01/27 18:37:02 jakllsch Exp $	*/
 
 /*-
  * Copyright (c) 2005 The NetBSD Foundation, Inc.
@@ -404,6 +404,30 @@ struct scsi_reserve_release_10_idparam {
 /*
  * REPORT LUNS
  */
+#define SCSI_REPORT_LUNS		0xa0
+
+struct scsi_report_luns {
+	uint8_t opcode;
+	uint8_t reserved1;
+	uint8_t selectreport;
+#define SELECTREPORT_NORMAL		0x00
+#define SELECTREPORT_WELLKNOWN		0x01
+#define SELECTREPORT_ALL		0x02
+	uint8_t reserved3[3];
+	uint8_t alloclen[4];
+	uint8_t reserved10;
+	uint8_t control;
+};
+
+struct scsi_report_luns_header {
+	uint8_t	length[4];		/* in bytes, not including header */
+	uint8_t _res4[4];
+					/* followed by array of: */
+};
+
+struct scsi_report_luns_lun {
+	uint8_t lun[8];
+};
 
 /*
  * MAINTENANCE_IN[REPORT SUPPORTED OPERATION CODES]

Index: src/sys/dev/scsipi/scsiconf.c
diff -u src/sys/dev/scsipi/scsiconf.c:1.293 src/sys/dev/scsipi/scsiconf.c:1.294
--- src/sys/dev/scsipi/scsiconf.c:1.293	Tue Dec 21 22:53:21 2021
+++ src/sys/dev/scsipi/scsiconf.c	Thu Jan 27 18:37:02 2022
@@ -1,4 +1,4 @@
-/*	$NetBSD: scsiconf.c,v 1.293 2021/12/21 22:53:21 riastradh Exp $	*/
+/*	$NetBSD: scsiconf.c,v 1.294 2022/01/27 18:37:02 jakllsch Exp $	*/
 
 /*-
  * Copyright (c) 1998, 1999, 2004 The NetBSD Foundation, Inc.
@@ -48,7 +48,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: scsiconf.c,v 1.293 2021/12/21 22:53:21 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: scsiconf.c,v 1.294 2022/01/27 18:37:02 jakllsch Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -64,6 +64,7 @@ __KERNEL_RCSID(0, "$NetBSD: scsiconf.c,v
 #include <sys/scsiio.h>
 #include <sys/queue.h>
 #include <sys/atomic.h>
+#include <sys/kmem.h>
 
 #include <dev/scsipi/scsi_all.h>
 #include <dev/scsipi/scsipi_all.h>
@@ -370,6 +371,109 @@ scsibusdetach(device_t self, int flags)
 	return 0;
 }
 
+static int
+lun_compar(const void *a, const void *b)
+{
+	const uint16_t * const la = a, * const lb = b;
+
+	if (*la < *lb)
+		return -1;
+	if (*la > *lb)
+		return 1;
+	return 0;
+}
+
+static int
+scsi_report_luns(struct scsibus_softc *sc, int target,
+    uint16_t ** const luns, size_t *nluns)
+{
+	struct scsi_report_luns replun;
+	struct scsi_report_luns_header *rlr;
+	struct scsi_report_luns_lun *lunp;
+
+	struct scsipi_channel *chan = sc->sc_channel;
+	struct scsipi_inquiry_data inqbuf;
+	struct scsipi_periph *periph;
+	uint16_t tmp;
+
+	int error;
+	size_t i, rlrlen;
+
+	periph = scsipi_alloc_periph(M_WAITOK);
+	periph->periph_channel = chan;
+	periph->periph_switch = &scsi_probe_dev;
+
+	periph->periph_target = target;
+	periph->periph_lun = 0;
+	periph->periph_quirks = chan->chan_defquirks;
+
+	if ((error = scsipi_inquire(periph, &inqbuf,
+	    XS_CTL_DISCOVERY | XS_CTL_SILENT)))
+		goto end2;
+	periph->periph_version = inqbuf.version & SID_ANSII;
+	if (periph->periph_version < 3) {
+		error = ENOTSUP;
+		goto end2;
+	}
+
+	rlrlen = sizeof(*rlr) + sizeof(*lunp) * 1;
+
+again:
+	rlr = kmem_zalloc(rlrlen, KM_SLEEP);
+
+	replun.opcode = SCSI_REPORT_LUNS;
+	replun.selectreport = SELECTREPORT_NORMAL;
+	_lto4b(rlrlen, replun.alloclen);
+
+	error = scsipi_command(periph, (void *)&replun, sizeof(replun),
+	    (void *)rlr, rlrlen, SCSIPIRETRIES, 10000, NULL,
+	    XS_CTL_DATA_IN | XS_CTL_DISCOVERY | XS_CTL_SILENT);
+	if (error)
+		goto end;
+
+	if (sizeof(*rlr) + _4btol(rlr->length) > rlrlen &&
+	    sizeof(*rlr) + _4btol(rlr->length) <= 32) {
+	    	const size_t old_rlrlen = rlrlen;
+		rlrlen = sizeof(*rlr) + uimin(_4btol(rlr->length),
+		    16383 * sizeof(*lunp));
+		kmem_free(rlr, old_rlrlen);
+		rlr = NULL;
+		goto again;
+	}
+
+	KASSERT(nluns != NULL);
+	*nluns = (rlrlen - sizeof(*rlr)) / sizeof(*lunp);
+
+	KASSERT(luns != NULL);
+	*luns = kmem_alloc(*nluns * sizeof(**luns), KM_SLEEP);
+
+	for (i = 0; i < *nluns; i++) {
+		lunp = &((struct scsi_report_luns_lun *)&rlr[1])[i];
+		switch (lunp->lun[0] & 0xC0) {
+		default:
+			scsi_print_addr(periph);
+			printf("LUN %016"PRIx64" ignored\n", _8btol(lunp->lun));
+			(*luns)[i] = 0;
+			break;
+		case 0x40:
+			(*luns)[i] = _2btol(&lunp->lun[0]) & 0x3FFF;
+			break;
+		case 0x00:
+			(*luns)[i] = _2btol(&lunp->lun[0]) & 0x00FF;
+			break;
+		}
+	}
+
+	kheapsort(*luns, *nluns, sizeof(**luns), lun_compar, &tmp);
+
+end:
+	if (rlr)
+		kmem_free(rlr, rlrlen);
+end2:
+	scsipi_free_periph(periph);
+	return error;
+}
+
 /*
  * Probe the requested scsi bus. It must be already set up.
  * target and lun optionally narrow the search if not -1
@@ -405,11 +509,19 @@ scsi_probe_bus(struct scsibus_softc *sc,
 	 */
 	scsipi_adapter_ioctl(chan, SCBUSIOLLSCAN, NULL, 0, curproc);
 
+	uint16_t *luns;
+	size_t nluns;
+
 	if ((error = scsipi_adapter_addref(chan->chan_adapter)) != 0)
 		goto ret;
 	for (target = mintarget; target <= maxtarget; target++) {
 		if (target == chan->chan_id)
 			continue;
+		if (scsi_report_luns(sc, target, &luns, &nluns) == 0) {
+			for (size_t i = 0; i < nluns; i++)
+				if (luns[i] >= minlun && luns[i] <= maxlun)
+					scsi_probe_device(sc, target, luns[i]);
+		} else
 		for (lun = minlun; lun <= maxlun; lun++) {
 			/*
 			 * See if there's a device present, and configure it.
@@ -419,6 +531,12 @@ scsi_probe_bus(struct scsibus_softc *sc,
 			/* otherwise something says we should look further */
 		}
 
+		if (luns != NULL) {
+			kmem_free(luns, sizeof(*luns) * nluns);
+			luns = NULL;
+			nluns = 0;
+		}
+
 		/*
 		 * Now that we've discovered all of the LUNs on this
 		 * I_T Nexus, update the xfer mode for all of them

Reply via email to