Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 8 Jun 2010 16:17:26 +0000 (UTC)
From:      Matt Jacob <mjacob@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r208911 - in head/sys/cam: . scsi
Message-ID:  <201006081617.o58GHQIE050026@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mjacob
Date: Tue Jun  8 16:17:25 2010
New Revision: 208911
URL: http://svn.freebsd.org/changeset/base/208911

Log:
  Implement the usage of Report Luns as part of SCSI probing for SCP3 or
  better devices. This can be disabled on a per-device basis using quirks as
  well.
  
  This also handles the case where there is actually no connected LUN 0
  (which can definitely be the case for storage arrays).
  
  Reviewed by:	scsi@
  MFC after:	1 month

Modified:
  head/sys/cam/cam_debug.h
  head/sys/cam/cam_xpt.c
  head/sys/cam/cam_xpt_internal.h
  head/sys/cam/scsi/scsi_xpt.c

Modified: head/sys/cam/cam_debug.h
==============================================================================
--- head/sys/cam/cam_debug.h	Tue Jun  8 15:45:38 2010	(r208910)
+++ head/sys/cam/cam_debug.h	Tue Jun  8 16:17:25 2010	(r208911)
@@ -40,7 +40,8 @@ typedef enum {
 	CAM_DEBUG_SUBTRACE	= 0x04,	/* internal to routine flows */
 	CAM_DEBUG_CDB		= 0x08, /* print out SCSI CDBs only */
 	CAM_DEBUG_XPT		= 0x10,	/* print out xpt scheduling */
-	CAM_DEBUG_PERIPH	= 0x20  /* print out peripheral calls */
+	CAM_DEBUG_PERIPH	= 0x20, /* print out peripheral calls */
+	CAM_DEBUG_PROBE		= 0x40  /* print out probe actions */
 } cam_debug_flags;
 
 #if defined(CAMDEBUG) && defined(_KERNEL)
@@ -58,6 +59,7 @@ extern u_int32_t cam_debug_delay;
 	 && (cam_dpath != NULL)				\
 	 && (xpt_path_comp(cam_dpath, path) >= 0)	\
 	 && (xpt_path_comp(cam_dpath, path) < 2))
+
 #define	CAM_DEBUG(path, flag, printfargs)		\
 	if ((cam_dflags & (flag))			\
 	 && (cam_dpath != NULL)				\
@@ -68,6 +70,7 @@ extern u_int32_t cam_debug_delay;
 		if (cam_debug_delay != 0)		\
 			DELAY(cam_debug_delay);		\
 	}
+
 #define	CAM_DEBUG_PRINT(flag, printfargs)		\
 	if (cam_dflags & (flag)) {			\
 		printf("cam_debug: ");			\
@@ -76,11 +79,20 @@ extern u_int32_t cam_debug_delay;
 			DELAY(cam_debug_delay);		\
 	}
 
+#define	CAM_DEBUG_PATH_PRINT(flag, path, printfargs)	\
+	if (cam_dflags & (flag)) {			\
+		xpt_print(path, "cam_debug: ");		\
+ 		printf printfargs;			\
+		if (cam_debug_delay != 0)		\
+			DELAY(cam_debug_delay);		\
+	}
+
 #else /* !CAMDEBUG || !_KERNEL */
 
 #define	CAM_DEBUGGED(A, B)	0
 #define	CAM_DEBUG(A, B, C)
 #define	CAM_DEBUG_PRINT(A, B)
+#define	CAM_DEBUG_PATH_PRINT(A, B, C)
 
 #endif /* CAMDEBUG && _KERNEL */
 

Modified: head/sys/cam/cam_xpt.c
==============================================================================
--- head/sys/cam/cam_xpt.c	Tue Jun  8 15:45:38 2010	(r208910)
+++ head/sys/cam/cam_xpt.c	Tue Jun  8 16:17:25 2010	(r208911)
@@ -196,8 +196,18 @@ static struct cdevsw xpt_cdevsw = {
 /* Storage for debugging datastructures */
 #ifdef	CAMDEBUG
 struct cam_path *cam_dpath;
-u_int32_t cam_dflags;
+#ifdef	CAM_DEBUG_FLAGS
+u_int32_t cam_dflags = CAM_DEBUG_FLAGS;
+#else
+u_int32_t cam_dflags = CAM_DEBUG_NONE;
+#endif
+TUNABLE_INT("kern.cam.dflags", &cam_dflags);
+SYSCTL_INT(_kern_cam, OID_AUTO, dflags, CTLFLAG_RW,
+	&cam_dflags, 0, "Cam Debug Flags");
 u_int32_t cam_debug_delay;
+TUNABLE_INT("kern.cam.debug_delay", &cam_debug_delay);
+SYSCTL_INT(_kern_cam, OID_AUTO, debug_delay, CTLFLAG_RW,
+	&cam_debug_delay, 0, "Cam Debug Flags");
 #endif
 
 /* Our boot-time initialization hook */
@@ -4212,6 +4222,7 @@ xpt_alloc_target(struct cam_eb *bus, tar
 		target->target_id = target_id;
 		target->refcount = 1;
 		target->generation = 0;
+		target->luns = NULL;
 		timevalclear(&target->last_reset);
 		/*
 		 * Hold a reference to our parent bus so it
@@ -4243,6 +4254,8 @@ xpt_release_target(struct cam_et *target
 		TAILQ_REMOVE(&target->bus->et_entries, target, links);
 		target->bus->generation++;
 		xpt_release_bus(target->bus);
+		if (target->luns)
+			free(target->luns, M_CAMXPT);
 		free(target, M_CAMXPT);
 	}
 }
@@ -4505,11 +4518,6 @@ xpt_config(void *arg)
 
 #ifdef CAMDEBUG
 	/* Setup debugging flags and path */
-#ifdef CAM_DEBUG_FLAGS
-	cam_dflags = CAM_DEBUG_FLAGS;
-#else /* !CAM_DEBUG_FLAGS */
-	cam_dflags = CAM_DEBUG_NONE;
-#endif /* CAM_DEBUG_FLAGS */
 #ifdef CAM_DEBUG_BUS
 	if (cam_dflags != CAM_DEBUG_NONE) {
 		/*

Modified: head/sys/cam/cam_xpt_internal.h
==============================================================================
--- head/sys/cam/cam_xpt_internal.h	Tue Jun  8 15:45:38 2010	(r208910)
+++ head/sys/cam/cam_xpt_internal.h	Tue Jun  8 16:17:25 2010	(r208911)
@@ -135,6 +135,8 @@ struct cam_et {
 	u_int32_t	refcount;
 	u_int		generation;
 	struct		timeval last_reset;
+	u_int		rpl_size;
+	struct scsi_report_luns_data *luns;
 };
 
 /*

Modified: head/sys/cam/scsi/scsi_xpt.c
==============================================================================
--- head/sys/cam/scsi/scsi_xpt.c	Tue Jun  8 15:45:38 2010	(r208910)
+++ head/sys/cam/scsi/scsi_xpt.c	Tue Jun  8 16:17:25 2010	(r208911)
@@ -71,6 +71,7 @@ struct scsi_quirk_entry {
 #define	CAM_QUIRK_NOSERIAL	0x02
 #define	CAM_QUIRK_HILUNS	0x04
 #define	CAM_QUIRK_NOHILUNS	0x08
+#define	CAM_QUIRK_NORPTLUNS	0x10
 	u_int mintags;
 	u_int maxtags;
 };
@@ -84,6 +85,21 @@ SYSCTL_PROC(_kern_cam, OID_AUTO, cam_src
     "allow search above LUN 7 for SCSI3 and greater devices");
 
 #define	CAM_SCSI2_MAXLUN	8
+#define	CAM_CAN_GET_SIMPLE_LUN(x, i)				\
+	((((x)->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK) ==	\
+	RPL_LUNDATA_ATYP_PERIPH) ||				\
+	(((x)->luns[i].lundata[0] & RPL_LUNDATA_ATYP_MASK) ==	\
+	RPL_LUNDATA_ATYP_FLAT))
+#define	CAM_GET_SIMPLE_LUN(lp, i, lval)					\
+	if (((lp)->luns[(i)].lundata[0] & RPL_LUNDATA_ATYP_MASK) == 	\
+	    RPL_LUNDATA_ATYP_PERIPH) {					\
+		(lval) = (lp)->luns[(i)].lundata[1];			\
+	} else {							\
+		(lval) = (lp)->luns[(i)].lundata[0];			\
+		(lval) &= RPL_LUNDATA_FLAT_LUN_MASK;			\
+		(lval) <<= 8;						\
+		(lval) |=  (lp)->luns[(i)].lundata[1];			\
+	}
 /*
  * If we're not quirked to search <= the first 8 luns
  * and we are either quirked to search above lun 8,
@@ -116,6 +132,7 @@ typedef enum {
 	PROBE_TUR,
 	PROBE_INQUIRY,	/* this counts as DV0 for Basic Domain Validation */
 	PROBE_FULL_INQUIRY,
+	PROBE_REPORT_LUNS,
 	PROBE_MODE_SENSE,
 	PROBE_SERIAL_NUM_0,
 	PROBE_SERIAL_NUM_1,
@@ -130,6 +147,7 @@ static char *probe_action_text[] = {
 	"PROBE_TUR",
 	"PROBE_INQUIRY",
 	"PROBE_FULL_INQUIRY",
+	"PROBE_REPORT_LUNS",
 	"PROBE_MODE_SENSE",
 	"PROBE_SERIAL_NUM_0",
 	"PROBE_SERIAL_NUM_1",
@@ -527,6 +545,10 @@ static void	 proberequestdefaultnegotiat
 static int       proberequestbackoff(struct cam_periph *periph,
 				     struct cam_ed *device);
 static void	 probedone(struct cam_periph *periph, union ccb *done_ccb);
+static int	 probe_strange_rpl_data(struct scsi_report_luns_data *rp,
+					uint32_t maxlun);
+static void	 probe_purge_old(struct cam_path *path,
+				 struct scsi_report_luns_data *new);
 static void	 probecleanup(struct cam_periph *periph);
 static void	 scsi_find_quirk(struct cam_ed *device);
 static void	 scsi_scan_bus(struct cam_periph *periph, union ccb *ccb);
@@ -685,6 +707,7 @@ probestart(struct cam_periph *periph, un
 
 	softc = (probe_softc *)periph->softc;
 	csio = &start_ccb->csio;
+again:
 
 	switch (softc->action) {
 	case PROBE_TUR:
@@ -773,6 +796,28 @@ probestart(struct cam_periph *periph, un
 			     /*timeout*/60 * 1000);
 		break;
 	}
+	case PROBE_REPORT_LUNS:
+	{
+		void *rp;
+
+		rp = malloc(periph->path->target->rpl_size,
+		    M_CAMXPT, M_NOWAIT | M_ZERO);
+		if (rp == NULL) {
+			struct scsi_inquiry_data *inq_buf;
+			inq_buf = &periph->path->device->inq_data;
+			xpt_print(periph->path,
+			    "Unable to alloc report luns storage\n");
+			if (INQ_DATA_TQ_ENABLED(inq_buf))
+				PROBE_SET_ACTION(softc, PROBE_MODE_SENSE);
+			else
+				PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0);
+			goto again;
+		}
+		scsi_report_luns(csio, 5, probedone, MSG_SIMPLE_Q_TAG,
+		    RPL_REPORT_DEFAULT, rp, periph->path->target->rpl_size,
+		    SSD_FULL_SIZE, 60000); break;
+		break;
+	}
 	case PROBE_MODE_SENSE:
 	{
 		void  *mode_buf;
@@ -1045,9 +1090,7 @@ probedone(struct cam_periph *periph, uni
 
 			periph_qual = SID_QUAL(inq_buf);
 
-			switch(periph_qual) {
-			case SID_QUAL_LU_CONNECTED:
-			{
+			if (periph_qual == SID_QUAL_LU_CONNECTED) {
 				u_int8_t len;
 
 				/*
@@ -1074,10 +1117,23 @@ probedone(struct cam_periph *periph, uni
 				scsi_find_quirk(path->device);
 
 				scsi_devise_transport(path);
-				if (INQ_DATA_TQ_ENABLED(inq_buf))
-					PROBE_SET_ACTION(softc, PROBE_MODE_SENSE);
+
+				if (path->device->lun_id == 0 &&
+				    SID_ANSI_REV(inq_buf) > SCSI_REV_SPC2 &&
+				    (SCSI_QUIRK(path->device)->quirks &
+				     CAM_QUIRK_NORPTLUNS) == 0) {
+					PROBE_SET_ACTION(softc,
+					    PROBE_REPORT_LUNS);
+					/*
+					 * Start with room for *one* lun.
+					 */
+					periph->path->target->rpl_size = 16;
+				} else if (INQ_DATA_TQ_ENABLED(inq_buf))
+					PROBE_SET_ACTION(softc,
+					    PROBE_MODE_SENSE);
 				else
-					PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0);
+					PROBE_SET_ACTION(softc,
+					    PROBE_SERIAL_NUM_0);
 
 				if (path->device->flags & CAM_DEV_UNCONFIGURED) {
 					path->device->flags &= ~CAM_DEV_UNCONFIGURED;
@@ -1086,9 +1142,21 @@ probedone(struct cam_periph *periph, uni
 				xpt_release_ccb(done_ccb);
 				xpt_schedule(periph, priority);
 				return;
-			}
-			default:
-				break;
+			} else if (path->device->lun_id == 0 &&
+			    SID_ANSI_REV(inq_buf) > SCSI_REV_SPC2 &&
+			    (SCSI_QUIRK(path->device)->quirks &
+			     CAM_QUIRK_NORPTLUNS) == 0) {
+				if (path->device->flags &
+				    CAM_DEV_UNCONFIGURED) {
+					path->device->flags &=
+					    ~CAM_DEV_UNCONFIGURED;
+					xpt_acquire_device(path->device);
+				}
+				PROBE_SET_ACTION(softc, PROBE_REPORT_LUNS);
+				periph->path->target->rpl_size = 16;
+				xpt_release_ccb(done_ccb);
+				xpt_schedule(periph, priority);
+				return;
 			}
 		} else if (cam_periph_error(done_ccb, 0,
 					    done_ccb->ccb_h.target_lun > 0
@@ -1118,6 +1186,120 @@ probedone(struct cam_periph *periph, uni
 		xpt_release_ccb(done_ccb);
 		break;
 	}
+	case PROBE_REPORT_LUNS:
+	{
+		struct ccb_scsiio *csio;
+		struct scsi_report_luns_data *lp;
+		u_int nlun, maxlun;
+
+		csio = &done_ccb->csio;
+
+		lp = (struct scsi_report_luns_data *)csio->data_ptr;
+		nlun = scsi_4btoul(lp->length) / 8;
+		maxlun = (csio->dxfer_len / 8) - 1;
+
+		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+			if (cam_periph_error(done_ccb, 0,
+			    done_ccb->ccb_h.target_lun > 0 ?
+			    SF_RETRY_UA|SF_QUIET_IR : SF_RETRY_UA,
+			    &softc->saved_ccb) == ERESTART) {
+				return;
+			}
+			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
+				xpt_release_devq(done_ccb->ccb_h.path, 1,
+				    TRUE);
+			}
+			free(lp, M_CAMXPT);
+			lp = NULL;
+		} else if (nlun > maxlun) {
+			/*
+			 * Reallocate and retry to cover all luns
+			 */
+			CAM_DEBUG_PATH_PRINT(CAM_DEBUG_PROBE, path,
+			    ("reallocating REPORT_LUNS for %u luns\n", nlun));
+			free(lp, M_CAMXPT);
+			path->target->rpl_size = (nlun << 3) + 8;
+			xpt_release_ccb(done_ccb);
+			xpt_schedule(periph, priority);
+			return;
+		} else if (nlun == 0) {
+			/*
+			 * If there don't appear to be any luns, bail.
+			 */
+			free(lp, M_CAMXPT);
+			lp = NULL;
+		} else if (probe_strange_rpl_data(lp, maxlun)) {
+			/*
+			 * If we can't understand the lun format
+			 * of any entry, bail.
+			 */
+			free(lp, M_CAMXPT);
+			lp = NULL;
+		} else {
+			lun_id_t lun;
+			int idx;
+
+			CAM_DEBUG_PATH_PRINT(CAM_DEBUG_PROBE, path,
+			   ("%u luns reported\n", nlun));
+
+			CAM_GET_SIMPLE_LUN(lp, 0, lun);
+			/*
+			 * If the first lun is not lun 0, then either there
+			 * is no lun 0 in the list, or the list is unsorted.
+			 */
+			if (lun != 0) {
+				for (idx = 0; idx < nlun; idx++) {
+					CAM_GET_SIMPLE_LUN(lp, idx, lun);
+					if (lun == 0) {
+						break;
+					}
+				}
+				if (idx != nlun) {
+					uint8_t tlun[8];
+					memcpy(tlun,
+					    lp->luns[0].lundata, 8);
+					memcpy(lp->luns[0].lundata,
+					    lp->luns[idx].lundata, 8);
+					memcpy(lp->luns[idx].lundata,
+					    tlun, 8);
+					CAM_DEBUG_PATH_PRINT(CAM_DEBUG_PROBE,
+					    path, ("lun 0 in position %u\n", idx));
+				} else {
+					/*
+					 * There is no lun 0 in our list. Destroy
+					 * the validity of the inquiry data so we
+					 * bail here and now.
+					 */
+					path->device->flags &=
+					    ~CAM_DEV_INQUIRY_DATA_VALID;
+				}
+			}
+			/*
+			 * If we have an old lun list, We can either
+			 * retest luns that appear to have been dropped,
+			 * or just nuke them.  We'll opt for the latter.
+			 * This function will also install the new list
+			 * in the target structure.
+			 */
+			probe_purge_old(path, lp);
+			lp = NULL;
+		}
+		if (path->device->flags & CAM_DEV_INQUIRY_DATA_VALID) {
+			struct scsi_inquiry_data *inq_buf;
+			inq_buf = &path->device->inq_data;
+			if (INQ_DATA_TQ_ENABLED(inq_buf))
+				PROBE_SET_ACTION(softc, PROBE_MODE_SENSE);
+			else
+				PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0);
+			xpt_release_ccb(done_ccb);
+			xpt_schedule(periph, priority);
+			return;
+		}
+		if (lp) {
+			free(lp, M_CAMXPT);
+		}
+		break;
+	}
 	case PROBE_MODE_SENSE:
 	{
 		struct ccb_scsiio *csio;
@@ -1432,6 +1614,80 @@ probedone(struct cam_periph *periph, uni
 	}
 }
 
+static int
+probe_strange_rpl_data(struct scsi_report_luns_data *rp, uint32_t maxlun)
+{
+	uint32_t idx;
+	uint32_t nlun = MIN(maxlun, (scsi_4btoul(rp->length) / 8));
+
+	for (idx = 0; idx < nlun; idx++) {
+		if (!CAM_CAN_GET_SIMPLE_LUN(rp, idx)) {
+			return (-1);
+		}
+	}
+	return (0);
+}
+
+static void
+probe_purge_old(struct cam_path *path, struct scsi_report_luns_data *new)
+{
+	struct cam_path *tp;
+	struct scsi_report_luns_data *old;
+	u_int idx1, idx2, nlun_old, nlun_new, this_lun;
+	u_int8_t *ol, *nl;
+
+	if (path->target == NULL) {
+		return;
+	}
+	if (path->target->luns == NULL) {
+		path->target->luns = new;
+		return;
+	}
+	old = path->target->luns;
+	nlun_old = scsi_4btoul(old->length) / 8;
+	nlun_new = scsi_4btoul(new->length) / 8;
+
+	/*
+	 * We are not going to assume sorted lists. Deal.
+	 */
+	for (idx1 = 0; idx1 < nlun_old; idx1++) {
+		ol = old->luns[idx1].lundata;
+		for (idx2 = 0; idx2 < nlun_new; idx2++) {
+			nl = new->luns[idx2].lundata;
+			if (memcmp(nl, ol, 8) == 0) {
+				break;
+			}
+		}
+		if (idx2 < nlun_new) {
+			continue;
+		}
+		/*
+		 * An 'old' item not in the 'new' list.
+		 * Nuke it. Except that if it is lun 0,
+		 * that would be what the probe state
+		 * machine is currently working on,
+		 * so we won't do that.
+		 *
+		 * We also cannot nuke it if it is
+		 * not in a lun format we understand.
+		 */
+		if (!CAM_CAN_GET_SIMPLE_LUN(old, idx1)) {
+			continue;
+		}
+		CAM_GET_SIMPLE_LUN(old, idx1, this_lun);
+		if (this_lun == 0) {
+			continue;
+		}
+		if (xpt_create_path(&tp, NULL, xpt_path_path_id(path),
+		    xpt_path_target_id(path), this_lun) == CAM_REQ_CMP) {
+			xpt_async(AC_LOST_DEVICE, tp, NULL);
+			xpt_free_path(tp);
+		}
+	}
+	free(old, M_CAMXPT);
+	path->target->luns = new;
+}
+
 static void
 probecleanup(struct cam_periph *periph)
 {
@@ -1480,6 +1736,7 @@ typedef struct {
 	union	ccb *request_ccb;
 	struct 	ccb_pathinq *cpi;
 	int	counter;
+	int	lunindex[0];
 } scsi_scan_bus_info;
 
 /*
@@ -1553,13 +1810,15 @@ scsi_scan_bus(struct cam_periph *periph,
 		}
 
 		/* Save some state for use while we probe for devices */
-		scan_info = (scsi_scan_bus_info *)
-		    malloc(sizeof(scsi_scan_bus_info), M_CAMXPT, M_NOWAIT);
+		scan_info = (scsi_scan_bus_info *) malloc(sizeof(scsi_scan_bus_info) +
+		    (work_ccb->cpi.max_target * sizeof (u_int)), M_CAMXPT, M_ZERO|M_NOWAIT);
 		if (scan_info == NULL) {
 			request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
 			xpt_done(request_ccb);
 			return;
 		}
+		CAM_DEBUG_PATH_PRINT(CAM_DEBUG_PROBE, request_ccb->ccb_h.path,
+		   ("SCAN start for %p\n", scan_info));
 		scan_info->request_ccb = request_ccb;
 		scan_info->cpi = &work_ccb->cpi;
 
@@ -1626,26 +1885,76 @@ scsi_scan_bus(struct cam_periph *periph,
 	case XPT_SCAN_LUN:
 	{
 		cam_status status;
-		struct cam_path *path;
+		struct cam_path *path, *oldpath;
 		scsi_scan_bus_info *scan_info;
+		struct cam_et *target;
+		struct cam_ed *device;
+		int next_target;
 		path_id_t path_id;
 		target_id_t target_id;
 		lun_id_t lun_id;
 
+		oldpath = request_ccb->ccb_h.path;
+
+		status = request_ccb->ccb_h.status & CAM_STATUS_MASK;
 		/* Reuse the same CCB to query if a device was really found */
 		scan_info = (scsi_scan_bus_info *)request_ccb->ccb_h.ppriv_ptr0;
 		xpt_setup_ccb(&request_ccb->ccb_h, request_ccb->ccb_h.path,
 			      request_ccb->ccb_h.pinfo.priority);
 		request_ccb->ccb_h.func_code = XPT_GDEV_TYPE;
 
+
 		path_id = request_ccb->ccb_h.path_id;
 		target_id = request_ccb->ccb_h.target_id;
 		lun_id = request_ccb->ccb_h.target_lun;
 		xpt_action(request_ccb);
 
-		if (request_ccb->ccb_h.status != CAM_REQ_CMP) {
-			struct cam_ed *device;
-			struct cam_et *target;
+		target = request_ccb->ccb_h.path->target;
+		next_target = 1;
+
+		if (target->luns) {
+			uint32_t first;
+			u_int nluns = scsi_4btoul(target->luns->length) / 8;
+
+			/*
+			 * Make sure we skip over lun 0 if it's the first member
+			 * of the list as we've actually just finished probing
+			 * it.
+			 */
+			CAM_GET_SIMPLE_LUN(target->luns, 0, first);
+			if (first == 0 && scan_info->lunindex[target_id] == 0) {
+				scan_info->lunindex[target_id]++;
+			} 
+
+			if (scan_info->lunindex[target_id] < nluns) {
+				CAM_GET_SIMPLE_LUN(target->luns,
+				    scan_info->lunindex[target_id], lun_id);
+				next_target = 0;
+				CAM_DEBUG_PATH_PRINT(CAM_DEBUG_PROBE,
+				    request_ccb->ccb_h.path,
+				   ("next lun to try at index %u is %u\n",
+				   scan_info->lunindex[target_id], lun_id));
+				scan_info->lunindex[target_id]++;
+			} else {
+				/*
+				 * We're done with scanning all luns.
+				 *
+				 * Nuke the bogus device for lun 0 if lun 0
+				 * wasn't on the list.
+				 */
+				if (first != 0) {
+					TAILQ_FOREACH(device,
+					    &target->ed_entries, links) {
+						if (device->lun_id == 0) {
+							break;
+						}
+					}
+					if (device) {
+						xpt_release_device(device);
+					}
+				}
+			}
+		} else if (request_ccb->ccb_h.status != CAM_REQ_CMP) {
 			int phl;
 
 			/*
@@ -1654,7 +1963,6 @@ scsi_scan_bus(struct cam_periph *periph,
 			 * target that might have "gone away", go onto
 			 * the next lun.
 			 */
-			target = request_ccb->ccb_h.path->target;
 			/*
 			 * We may touch devices that we don't
 			 * hold references too, so ensure they
@@ -1670,11 +1978,15 @@ scsi_scan_bus(struct cam_periph *periph,
 					device = TAILQ_NEXT(device, links);
 			}
 			if ((lun_id != 0) || (device != NULL)) {
-				if (lun_id < (CAM_SCSI2_MAXLUN-1) || phl)
+				if (lun_id < (CAM_SCSI2_MAXLUN-1) || phl) {
 					lun_id++;
+					next_target = 0;
+				}
 			}
+			if (lun_id == request_ccb->ccb_h.target_lun
+			    || lun_id > scan_info->cpi->max_lun)
+				next_target = 1;
 		} else {
-			struct cam_ed *device;
 
 			device = request_ccb->ccb_h.path->device;
 
@@ -1682,23 +1994,26 @@ scsi_scan_bus(struct cam_periph *periph,
 			    CAM_QUIRK_NOLUNS) == 0) {
 				/* Try the next lun */
 				if (lun_id < (CAM_SCSI2_MAXLUN-1)
-				  || CAN_SRCH_HI_DENSE(device))
+				  || CAN_SRCH_HI_DENSE(device)) {
 					lun_id++;
+					next_target = 0;
+				}
 			}
+			if (lun_id == request_ccb->ccb_h.target_lun
+			    || lun_id > scan_info->cpi->max_lun)
+				next_target = 1;
 		}
 
 		/*
-		 * Free the current request path- we're done with it.
-		 */
-		xpt_free_path(request_ccb->ccb_h.path);
-
-		/*
 		 * Check to see if we scan any further luns.
 		 */
-		if (lun_id == request_ccb->ccb_h.target_lun
-                 || lun_id > scan_info->cpi->max_lun) {
+		if (next_target) {
 			int done;
 
+			/*
+			 * Free the current request path- we're done with it.
+			 */
+			xpt_free_path(oldpath);
  hop_again:
 			done = 0;
 			if (scan_info->request_ccb->ccb_h.func_code == XPT_SCAN_TGT) {
@@ -1723,6 +2038,9 @@ scsi_scan_bus(struct cam_periph *periph,
 				xpt_free_ccb(request_ccb);
 				xpt_free_ccb((union ccb *)scan_info->cpi);
 				request_ccb = scan_info->request_ccb;
+				CAM_DEBUG_PATH_PRINT(CAM_DEBUG_PROBE,
+				    request_ccb->ccb_h.path,
+				   ("SCAN done for %p\n", scan_info));
 				free(scan_info, M_CAMXPT);
 				request_ccb->ccb_h.status = CAM_REQ_CMP;
 				xpt_done(request_ccb);
@@ -1758,6 +2076,13 @@ scsi_scan_bus(struct cam_periph *periph,
 		} else {
 			status = xpt_create_path(&path, xpt_periph,
 						 path_id, target_id, lun_id);
+			/*
+			 * Free the old request path- we're done with it. We
+			 * do this *after* creating the new path so that
+			 * we don't remove a target that has our lun list
+			 * in the case that lun 0 is not present.
+			 */
+			xpt_free_path(oldpath);
 			if (status != CAM_REQ_CMP) {
 				printf("scsi_scan_bus: xpt_create_path failed "
 				       "with status %#x, halting LUN scan\n",



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201006081617.o58GHQIE050026>