Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 31 Mar 2008 09:30:46 GMT
From:      Scott Long <scottl@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 139053 for review
Message-ID:  <200803310930.m2V9Uk2S093318@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=139053

Change 139053 by scottl@scottl-deimos on 2008/03/31 09:30:05

	Fix compilation of the split-out scsi_xpt.c by remerging the scsi_probe
	periph back into it.  Keeping them separate meant exposing too many
	functions and structures, and the probe periph is really meant to be
	tightly integrated into the transport anyways.
	
	Also create a new malloc type for use in scsi_xpt.c

Affected files ...

.. //depot/projects/scottl-camlock/src/sys/cam/cam_xpt.c#78 edit
.. //depot/projects/scottl-camlock/src/sys/cam/scsi/scsi_probe.c#8 delete
.. //depot/projects/scottl-camlock/src/sys/cam/scsi/scsi_xpt.c#6 edit
.. //depot/projects/scottl-camlock/src/sys/modules/cam/Makefile#11 edit

Differences ...

==== //depot/projects/scottl-camlock/src/sys/cam/cam_xpt.c#78 (text+ko) ====

@@ -67,7 +67,6 @@
 #include <cam/scsi/scsi_message.h>
 #include <cam/scsi/scsi_pass.h>
 #include <cam/scsi/scsi_xpt.h>
-#include <cam/scsi/scsi_probe.h>
 #include <machine/stdarg.h>	/* for xpt_print below */
 #include "opt_cam.h"
 
@@ -4655,6 +4654,7 @@
 	return (device);
 }
 
+void
 xpt_devise_transport(struct cam_path *path)
 {
 	struct ccb_pathinq cpi;

==== //depot/projects/scottl-camlock/src/sys/cam/scsi/scsi_xpt.c#6 (text+ko) ====

@@ -64,7 +64,6 @@
 #include <cam/scsi/scsi_message.h>
 #include <cam/scsi/scsi_pass.h>
 #include <cam/scsi/scsi_xpt.h>
-#include <cam/scsi/scsi_probe.h>
 #include <machine/stdarg.h>	/* for xpt_print below */
 #include "opt_cam.h"
 
@@ -446,7 +445,56 @@
     sysctl_cam_search_luns, "I",
     "allow search above LUN 7 for SCSI3 and greater devices");
 
+MALLOC_DEFINE(M_SCSIXPT, "CAM SCSI XPT", "CAM SCSI XPT Buffers");
+
+typedef enum {
+	PROBE_TUR,
+	PROBE_INQUIRY,	/* this counts as DV0 for Basic Domain Validation */
+	PROBE_FULL_INQUIRY,
+	PROBE_MODE_SENSE,
+	PROBE_SERIAL_NUM_0,
+	PROBE_SERIAL_NUM_1,
+	PROBE_TUR_FOR_NEGOTIATION,
+	PROBE_INQUIRY_BASIC_DV1,
+	PROBE_INQUIRY_BASIC_DV2,
+	PROBE_DV_EXIT
+} probe_action;
+
+typedef enum {
+	PROBE_INQUIRY_CKSUM	= 0x01,
+	PROBE_SERIAL_CKSUM	= 0x02,
+	PROBE_NO_ANNOUNCE	= 0x04
+} probe_flags;
+
+typedef struct {
+	TAILQ_HEAD(, ccb_hdr) request_ccbs;
+	probe_action	action;
+	union ccb	saved_ccb;
+	probe_flags	flags;
+	MD5_CTX		context;
+	u_int8_t	digest[16];
+} probe_softc;
+
+static void	 probeschedule(struct cam_periph *probe_periph);
+static void	 proberequestdefaultnegotiation(struct cam_periph *periph);
+static int       proberequestbackoff(struct cam_periph *periph,
+				     struct cam_ed *device);
+static void	 probedone(struct cam_periph *periph, union ccb *done_ccb);
+static cam_status proberegister(struct cam_periph *periph, void *arg);
+static void	 probestart(struct cam_periph *periph, union ccb *start_ccb);
+static void	 probecleanup(struct cam_periph *periph);
+static void 	 probe_insert_ccbq(struct cam_periph *periph,
+				   struct ccb_hdr *ccb);
+static periph_init_t probe_periph_init;
 
+static struct periph_driver probe_driver =
+{
+	probe_periph_init, "probe",
+	TAILQ_HEAD_INITIALIZER(probe_driver.units)
+};
+
+PERIPHDRIVER_DECLARE(probe, probe_driver);
+
 void
 xpt_find_quirk(struct cam_ed *device)
 {
@@ -520,7 +568,7 @@
 
 		/* Save some state for use while we probe for devices */
 		scan_info = (xpt_scan_bus_info *)
-		    malloc(sizeof(xpt_scan_bus_info), M_TEMP, M_NOWAIT);
+		    malloc(sizeof(xpt_scan_bus_info), M_SCSIXPT, M_NOWAIT);
 		scan_info->request_ccb = request_ccb;
 		scan_info->cpi = &work_ccb->cpi;
 
@@ -554,7 +602,7 @@
 				printf("xpt_scan_bus: xpt_create_path failed"
 				       " with status %#x, bus scan halted\n",
 				       status);
-				free(scan_info, M_TEMP);
+				free(scan_info, M_SCSIXPT);
 				request_ccb->ccb_h.status = status;
 				xpt_free_ccb(work_ccb);
 				xpt_done(request_ccb);
@@ -562,7 +610,7 @@
 			}
 			work_ccb = xpt_alloc_ccb_nowait();
 			if (work_ccb == NULL) {
-				free(scan_info, M_TEMP);
+				free(scan_info, M_SCSIXPT);
 				xpt_free_path(path);
 				request_ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
 				xpt_done(request_ccb);
@@ -675,7 +723,7 @@
 				xpt_free_ccb(request_ccb);
 				xpt_free_ccb((union ccb *)scan_info->cpi);
 				request_ccb = scan_info->request_ccb;
-				free(scan_info, M_TEMP);
+				free(scan_info, M_SCSIXPT);
 				request_ccb->ccb_h.status = CAM_REQ_CMP;
 				xpt_done(request_ccb);
 				break;
@@ -694,7 +742,7 @@
 				xpt_free_ccb(request_ccb);
 				xpt_free_ccb((union ccb *)scan_info->cpi);
 				request_ccb = scan_info->request_ccb;
-				free(scan_info, M_TEMP);
+				free(scan_info, M_SCSIXPT);
 				request_ccb->ccb_h.status = status;
 				xpt_done(request_ccb);
 				break;
@@ -768,17 +816,17 @@
 	}
 
 	if (request_ccb == NULL) {
-		request_ccb = malloc(sizeof(union ccb), M_TEMP, M_NOWAIT);
+		request_ccb = malloc(sizeof(union ccb), M_SCSIXPT, M_NOWAIT);
 		if (request_ccb == NULL) {
 			xpt_print(path, "xpt_scan_lun: can't allocate CCB, "
 			    "can't continue\n");
 			return;
 		}
-		new_path = malloc(sizeof(*new_path), M_TEMP, M_NOWAIT);
+		new_path = malloc(sizeof(*new_path), M_SCSIXPT, M_NOWAIT);
 		if (new_path == NULL) {
 			xpt_print(path, "xpt_scan_lun: can't allocate path, "
 			    "can't continue\n");
-			free(request_ccb, M_TEMP);
+			free(request_ccb, M_SCSIXPT);
 			return;
 		}
 		status = xpt_compile_path(new_path, xpt_periph,
@@ -789,8 +837,8 @@
 		if (status != CAM_REQ_CMP) {
 			xpt_print(path, "xpt_scan_lun: can't compile path, "
 			    "can't continue\n");
-			free(request_ccb, M_TEMP);
-			free(new_path, M_TEMP);
+			free(request_ccb, M_SCSIXPT);
+			free(new_path, M_SCSIXPT);
 			return;
 		}
 		xpt_setup_ccb(&request_ccb->ccb_h, new_path, /*priority*/ 1);
@@ -821,8 +869,8 @@
 xptscandone(struct cam_periph *periph, union ccb *done_ccb)
 {
 	xpt_release_path(done_ccb->ccb_h.path);
-	free(done_ccb->ccb_h.path, M_TEMP);
-	free(done_ccb, M_TEMP);
+	free(done_ccb->ccb_h.path, M_SCSIXPT);
+	free(done_ccb, M_SCSIXPT);
 }
 
 static int
@@ -841,3 +889,850 @@
 		return (EINVAL);
 	}
 }
+
+static void
+probe_periph_init()
+{
+}
+
+static cam_status
+proberegister(struct cam_periph *periph, void *arg)
+{
+	union ccb *request_ccb;	/* CCB representing the probe request */
+	cam_status status;
+	probe_softc *softc;
+
+	request_ccb = (union ccb *)arg;
+	if (periph == NULL) {
+		printf("proberegister: periph was NULL!!\n");
+		return(CAM_REQ_CMP_ERR);
+	}
+
+	if (request_ccb == NULL) {
+		printf("proberegister: no probe CCB, "
+		       "can't register device\n");
+		return(CAM_REQ_CMP_ERR);
+	}
+
+	softc = (probe_softc *)malloc(sizeof(*softc), M_SCSIXPT, M_NOWAIT);
+
+	if (softc == NULL) {
+		printf("proberegister: Unable to probe new device. "
+		       "Unable to allocate softc\n");				
+		return(CAM_REQ_CMP_ERR);
+	}
+	TAILQ_INIT(&softc->request_ccbs);
+	TAILQ_INSERT_TAIL(&softc->request_ccbs, &request_ccb->ccb_h,
+			  periph_links.tqe);
+	softc->flags = 0;
+	periph->softc = softc;
+	status = cam_periph_acquire(periph);
+	if (status != CAM_REQ_CMP) {
+		return (status);
+	}
+
+
+	/*
+	 * Ensure we've waited at least a bus settle
+	 * delay before attempting to probe the device.
+	 * For HBAs that don't do bus resets, this won't make a difference.
+	 */
+	cam_periph_freeze_after_event(periph, &periph->path->bus->last_reset,
+				      scsi_delay);
+	probeschedule(periph);
+	return(CAM_REQ_CMP);
+}
+
+static void
+probeschedule(struct cam_periph *periph)
+{
+	struct ccb_pathinq cpi;
+	union ccb *ccb;
+	probe_softc *softc;
+
+	softc = (probe_softc *)periph->softc;
+	ccb = (union ccb *)TAILQ_FIRST(&softc->request_ccbs);
+
+	xpt_setup_ccb(&cpi.ccb_h, periph->path, /*priority*/1);
+	cpi.ccb_h.func_code = XPT_PATH_INQ;
+	xpt_action((union ccb *)&cpi);
+
+	/*
+	 * If a device has gone away and another device, or the same one,
+	 * is back in the same place, it should have a unit attention
+	 * condition pending.  It will not report the unit attention in
+	 * response to an inquiry, which may leave invalid transfer
+	 * negotiations in effect.  The TUR will reveal the unit attention
+	 * condition.  Only send the TUR for lun 0, since some devices 
+	 * will get confused by commands other than inquiry to non-existent
+	 * luns.  If you think a device has gone away start your scan from
+	 * lun 0.  This will insure that any bogus transfer settings are
+	 * invalidated.
+	 *
+	 * If we haven't seen the device before and the controller supports
+	 * some kind of transfer negotiation, negotiate with the first
+	 * sent command if no bus reset was performed at startup.  This
+	 * ensures that the device is not confused by transfer negotiation
+	 * settings left over by loader or BIOS action.
+	 */
+	if (((ccb->ccb_h.path->device->flags & CAM_DEV_UNCONFIGURED) == 0)
+	 && (ccb->ccb_h.target_lun == 0)) {
+		softc->action = PROBE_TUR;
+	} else if ((cpi.hba_inquiry & (PI_WIDE_32|PI_WIDE_16|PI_SDTR_ABLE)) != 0
+	      && (cpi.hba_misc & PIM_NOBUSRESET) != 0) {
+		proberequestdefaultnegotiation(periph);
+		softc->action = PROBE_INQUIRY;
+	} else {
+		softc->action = PROBE_INQUIRY;
+	}
+
+	if (ccb->crcn.flags & CAM_EXPECT_INQ_CHANGE)
+		softc->flags |= PROBE_NO_ANNOUNCE;
+	else
+		softc->flags &= ~PROBE_NO_ANNOUNCE;
+
+	xpt_schedule(periph, ccb->ccb_h.pinfo.priority);
+}
+
+static void
+probestart(struct cam_periph *periph, union ccb *start_ccb)
+{
+	/* Probe the device that our peripheral driver points to */
+	struct ccb_scsiio *csio;
+	probe_softc *softc;
+
+	CAM_DEBUG(start_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probestart\n"));
+
+	softc = (probe_softc *)periph->softc;
+	csio = &start_ccb->csio;
+
+	switch (softc->action) {
+	case PROBE_TUR:
+	case PROBE_TUR_FOR_NEGOTIATION:
+	case PROBE_DV_EXIT:
+	{
+		scsi_test_unit_ready(csio,
+				     /*retries*/4,
+				     probedone,
+				     MSG_SIMPLE_Q_TAG,
+				     SSD_FULL_SIZE,
+				     /*timeout*/60000);
+		break;
+	}
+	case PROBE_INQUIRY:
+	case PROBE_FULL_INQUIRY:
+	case PROBE_INQUIRY_BASIC_DV1:
+	case PROBE_INQUIRY_BASIC_DV2:
+	{
+		u_int inquiry_len;
+		struct scsi_inquiry_data *inq_buf;
+
+		inq_buf = &periph->path->device->inq_data;
+
+		/*
+		 * If the device is currently configured, we calculate an
+		 * MD5 checksum of the inquiry data, and if the serial number
+		 * length is greater than 0, add the serial number data
+		 * into the checksum as well.  Once the inquiry and the
+		 * serial number check finish, we attempt to figure out
+		 * whether we still have the same device.
+		 */
+		if ((periph->path->device->flags & CAM_DEV_UNCONFIGURED) == 0) {
+			
+			MD5Init(&softc->context);
+			MD5Update(&softc->context, (unsigned char *)inq_buf,
+				  sizeof(struct scsi_inquiry_data));
+			softc->flags |= PROBE_INQUIRY_CKSUM;
+			if (periph->path->device->serial_num_len > 0) {
+				MD5Update(&softc->context,
+					  periph->path->device->serial_num,
+					  periph->path->device->serial_num_len);
+				softc->flags |= PROBE_SERIAL_CKSUM;
+			}
+			MD5Final(softc->digest, &softc->context);
+		} 
+
+		if (softc->action == PROBE_INQUIRY)
+			inquiry_len = SHORT_INQUIRY_LENGTH;
+		else
+			inquiry_len = SID_ADDITIONAL_LENGTH(inq_buf);
+
+		/*
+		 * Some parallel SCSI devices fail to send an
+		 * ignore wide residue message when dealing with
+		 * odd length inquiry requests.  Round up to be
+		 * safe.
+		 */
+		inquiry_len = roundup2(inquiry_len, 2);
+	
+		if (softc->action == PROBE_INQUIRY_BASIC_DV1
+		 || softc->action == PROBE_INQUIRY_BASIC_DV2) {
+			inq_buf = malloc(inquiry_len, M_SCSIXPT, M_NOWAIT);
+		}
+		if (inq_buf == NULL) {
+			xpt_print(periph->path, "malloc failure- skipping Basic"
+			    "Domain Validation\n");
+			softc->action = PROBE_DV_EXIT;
+			scsi_test_unit_ready(csio,
+					     /*retries*/4,
+					     probedone,
+					     MSG_SIMPLE_Q_TAG,
+					     SSD_FULL_SIZE,
+					     /*timeout*/60000);
+			break;
+		}
+		scsi_inquiry(csio,
+			     /*retries*/4,
+			     probedone,
+			     MSG_SIMPLE_Q_TAG,
+			     (u_int8_t *)inq_buf,
+			     inquiry_len,
+			     /*evpd*/FALSE,
+			     /*page_code*/0,
+			     SSD_MIN_SIZE,
+			     /*timeout*/60 * 1000);
+		break;
+	}
+	case PROBE_MODE_SENSE:
+	{
+		void  *mode_buf;
+		int    mode_buf_len;
+
+		mode_buf_len = sizeof(struct scsi_mode_header_6)
+			     + sizeof(struct scsi_mode_blk_desc)
+			     + sizeof(struct scsi_control_page);
+		mode_buf = malloc(mode_buf_len, M_SCSIXPT, M_NOWAIT);
+		if (mode_buf != NULL) {
+	                scsi_mode_sense(csio,
+					/*retries*/4,
+					probedone,
+					MSG_SIMPLE_Q_TAG,
+					/*dbd*/FALSE,
+					SMS_PAGE_CTRL_CURRENT,
+					SMS_CONTROL_MODE_PAGE,
+					mode_buf,
+					mode_buf_len,
+					SSD_FULL_SIZE,
+					/*timeout*/60000);
+			break;
+		}
+		xpt_print(periph->path, "Unable to mode sense control page - "
+		    "malloc failure\n");
+		softc->action = PROBE_SERIAL_NUM_0;
+	}
+	/* FALLTHROUGH */
+	case PROBE_SERIAL_NUM_0:
+	{
+		struct scsi_vpd_supported_page_list *vpd_list = NULL;
+		struct cam_ed *device;
+
+		device = periph->path->device;
+		if ((device->quirk->quirks & CAM_QUIRK_NOSERIAL) == 0) {
+			vpd_list = malloc(sizeof(*vpd_list), M_SCSIXPT,
+			    M_NOWAIT | M_ZERO);
+		}
+
+		if (vpd_list != NULL) {
+			scsi_inquiry(csio,
+				     /*retries*/4,
+				     probedone,
+				     MSG_SIMPLE_Q_TAG,
+				     (u_int8_t *)vpd_list,
+				     sizeof(*vpd_list),
+				     /*evpd*/TRUE,
+				     SVPD_SUPPORTED_PAGE_LIST,
+				     SSD_MIN_SIZE,
+				     /*timeout*/60 * 1000);
+			break;
+		}
+		/*
+		 * We'll have to do without, let our probedone
+		 * routine finish up for us.
+		 */
+		start_ccb->csio.data_ptr = NULL;
+		probedone(periph, start_ccb);
+		return;
+	}
+	case PROBE_SERIAL_NUM_1:
+	{
+		struct scsi_vpd_unit_serial_number *serial_buf;
+		struct cam_ed* device;
+
+		serial_buf = NULL;
+		device = periph->path->device;
+		device->serial_num = NULL;
+		device->serial_num_len = 0;
+
+		serial_buf = (struct scsi_vpd_unit_serial_number *)
+			malloc(sizeof(*serial_buf), M_SCSIXPT,
+			    M_NOWAIT | M_ZERO);
+
+		if (serial_buf != NULL) {
+			scsi_inquiry(csio,
+				     /*retries*/4,
+				     probedone,
+				     MSG_SIMPLE_Q_TAG,
+				     (u_int8_t *)serial_buf,
+				     sizeof(*serial_buf),
+				     /*evpd*/TRUE,
+				     SVPD_UNIT_SERIAL_NUMBER,
+				     SSD_MIN_SIZE,
+				     /*timeout*/60 * 1000);
+			break;
+		}
+		/*
+		 * We'll have to do without, let our probedone
+		 * routine finish up for us.
+		 */
+		start_ccb->csio.data_ptr = NULL;
+		probedone(periph, start_ccb);
+		return;
+	}
+	}
+	xpt_action(start_ccb);
+}
+
+static void
+proberequestdefaultnegotiation(struct cam_periph *periph)
+{
+	struct ccb_trans_settings cts;
+
+	xpt_setup_ccb(&cts.ccb_h, periph->path, /*priority*/1);
+	cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
+	cts.type = CTS_TYPE_USER_SETTINGS;
+	xpt_action((union ccb *)&cts);
+	if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+		return;
+	}
+	cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
+	cts.type = CTS_TYPE_CURRENT_SETTINGS;
+	xpt_action((union ccb *)&cts);
+}
+
+/*
+ * Backoff Negotiation Code- only pertinent for SPI devices.
+ */
+static int
+proberequestbackoff(struct cam_periph *periph, struct cam_ed *device)
+{
+	struct ccb_trans_settings cts;
+	struct ccb_trans_settings_spi *spi;
+
+	memset(&cts, 0, sizeof (cts));
+	xpt_setup_ccb(&cts.ccb_h, periph->path, /*priority*/1);
+	cts.ccb_h.func_code = XPT_GET_TRAN_SETTINGS;
+	cts.type = CTS_TYPE_CURRENT_SETTINGS;
+	xpt_action((union ccb *)&cts);
+	if ((cts.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+		if (bootverbose) {
+			xpt_print(periph->path,
+			    "failed to get current device settings\n");
+		}
+		return (0);
+	}
+	if (cts.transport != XPORT_SPI) {
+		if (bootverbose) {
+			xpt_print(periph->path, "not SPI transport\n");
+		}
+		return (0);
+	}
+	spi = &cts.xport_specific.spi;
+
+	/*
+	 * We cannot renegotiate sync rate if we don't have one.
+	 */
+	if ((spi->valid & CTS_SPI_VALID_SYNC_RATE) == 0) {
+		if (bootverbose) {
+			xpt_print(periph->path, "no sync rate known\n");
+		}
+		return (0);
+	}
+
+	/*
+	 * We'll assert that we don't have to touch PPR options- the
+	 * SIM will see what we do with period and offset and adjust
+	 * the PPR options as appropriate.
+	 */
+
+	/*
+	 * A sync rate with unknown or zero offset is nonsensical.
+	 * A sync period of zero means Async.
+	 */
+	if ((spi->valid & CTS_SPI_VALID_SYNC_OFFSET) == 0
+	 || spi->sync_offset == 0 || spi->sync_period == 0) {
+		if (bootverbose) {
+			xpt_print(periph->path, "no sync rate available\n");
+		}
+		return (0);
+	}
+
+	if (device->flags & CAM_DEV_DV_HIT_BOTTOM) {
+		CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+		    ("hit async: giving up on DV\n"));
+		return (0);
+	}
+
+
+	/*
+	 * Jump sync_period up by one, but stop at 5MHz and fall back to Async.
+	 * We don't try to remember 'last' settings to see if the SIM actually
+	 * gets into the speed we want to set. We check on the SIM telling
+	 * us that a requested speed is bad, but otherwise don't try and
+	 * check the speed due to the asynchronous and handshake nature
+	 * of speed setting.
+	 */
+	spi->valid = CTS_SPI_VALID_SYNC_RATE | CTS_SPI_VALID_SYNC_OFFSET;
+	for (;;) {
+		spi->sync_period++;
+		if (spi->sync_period >= 0xf) {
+			spi->sync_period = 0;
+			spi->sync_offset = 0;
+			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+			    ("setting to async for DV\n"));
+			/*
+			 * Once we hit async, we don't want to try
+			 * any more settings.
+			 */
+			device->flags |= CAM_DEV_DV_HIT_BOTTOM;
+		} else if (bootverbose) {
+			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+			    ("DV: period 0x%x\n", spi->sync_period));
+			printf("setting period to 0x%x\n", spi->sync_period);
+		}
+		cts.ccb_h.func_code = XPT_SET_TRAN_SETTINGS;
+		cts.type = CTS_TYPE_CURRENT_SETTINGS;
+		xpt_action((union ccb *)&cts);
+		if ((cts.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
+			break;
+		}
+		CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+		    ("DV: failed to set period 0x%x\n", spi->sync_period));
+		if (spi->sync_period == 0) {
+			return (0);
+		}
+	}
+	return (1);
+}
+
+static void
+probedone(struct cam_periph *periph, union ccb *done_ccb)
+{
+	probe_softc *softc;
+	struct cam_path *path;
+	u_int32_t  priority;
+
+	CAM_DEBUG(done_ccb->ccb_h.path, CAM_DEBUG_TRACE, ("probedone\n"));
+
+	softc = (probe_softc *)periph->softc;
+	path = done_ccb->ccb_h.path;
+	priority = done_ccb->ccb_h.pinfo.priority;
+
+	switch (softc->action) {
+	case PROBE_TUR:
+	{
+		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
+
+			if (cam_periph_error(done_ccb, 0,
+					     SF_NO_PRINT, NULL) == ERESTART)
+				return;
+			else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
+				/* Don't wedge the queue */
+				xpt_release_devq(done_ccb->ccb_h.path,
+						 /*count*/1,
+						 /*run_queue*/TRUE);
+		}
+		softc->action = PROBE_INQUIRY;
+		xpt_release_ccb(done_ccb);
+		xpt_schedule(periph, priority);
+		return;
+	}
+	case PROBE_INQUIRY:
+	case PROBE_FULL_INQUIRY:
+	{
+		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
+			struct scsi_inquiry_data *inq_buf;
+			u_int8_t periph_qual;
+
+			path->device->flags |= CAM_DEV_INQUIRY_DATA_VALID;
+			inq_buf = &path->device->inq_data;
+
+			periph_qual = SID_QUAL(inq_buf);
+			
+			switch(periph_qual) {
+			case SID_QUAL_LU_CONNECTED:
+			{
+				u_int8_t len;
+
+				/*
+				 * We conservatively request only
+				 * SHORT_INQUIRY_LEN bytes of inquiry
+				 * information during our first try
+				 * at sending an INQUIRY. If the device
+				 * has more information to give,
+				 * perform a second request specifying
+				 * the amount of information the device
+				 * is willing to give.
+				 */
+				len = inq_buf->additional_length
+				    + offsetof(struct scsi_inquiry_data,
+                                               additional_length) + 1;
+				if (softc->action == PROBE_INQUIRY
+				 && len > SHORT_INQUIRY_LENGTH) {
+					softc->action = PROBE_FULL_INQUIRY;
+					xpt_release_ccb(done_ccb);
+					xpt_schedule(periph, priority);
+					return;
+				}
+
+				xpt_find_quirk(path->device);
+
+				xpt_devise_transport(path);
+				if (INQ_DATA_TQ_ENABLED(inq_buf))
+					softc->action = PROBE_MODE_SENSE;
+				else
+					softc->action = PROBE_SERIAL_NUM_0;
+
+				path->device->flags &= ~CAM_DEV_UNCONFIGURED;
+
+				xpt_release_ccb(done_ccb);
+				xpt_schedule(periph, priority);
+				return;
+			}
+			default:
+				break;
+			}
+		} else 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;
+		} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
+			/* Don't wedge the queue */
+			xpt_release_devq(done_ccb->ccb_h.path, /*count*/1,
+					 /*run_queue*/TRUE);
+		}
+		/*
+		 * If we get to this point, we got an error status back
+		 * from the inquiry and the error status doesn't require
+		 * automatically retrying the command.  Therefore, the
+		 * inquiry failed.  If we had inquiry information before
+		 * for this device, but this latest inquiry command failed,
+		 * the device has probably gone away.  If this device isn't
+		 * already marked unconfigured, notify the peripheral
+		 * drivers that this device is no more.
+		 */
+		if ((path->device->flags & CAM_DEV_UNCONFIGURED) == 0)
+			/* Send the async notification. */
+			xpt_async(AC_LOST_DEVICE, path, NULL);
+
+		xpt_release_ccb(done_ccb);
+		break;
+	}
+	case PROBE_MODE_SENSE:
+	{
+		struct ccb_scsiio *csio;
+		struct scsi_mode_header_6 *mode_hdr;
+
+		csio = &done_ccb->csio;
+		mode_hdr = (struct scsi_mode_header_6 *)csio->data_ptr;
+		if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
+			struct scsi_control_page *page;
+			u_int8_t *offset;
+
+			offset = ((u_int8_t *)&mode_hdr[1])
+			    + mode_hdr->blk_desc_len;
+			page = (struct scsi_control_page *)offset;
+			path->device->queue_flags = page->queue_flags;
+		} else if (cam_periph_error(done_ccb, 0,
+					    SF_RETRY_UA|SF_NO_PRINT,
+					    &softc->saved_ccb) == ERESTART) {
+			return;
+		} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
+			/* Don't wedge the queue */
+			xpt_release_devq(done_ccb->ccb_h.path,
+					 /*count*/1, /*run_queue*/TRUE);
+		}
+		xpt_release_ccb(done_ccb);
+		free(mode_hdr, M_SCSIXPT);
+		softc->action = PROBE_SERIAL_NUM_0;
+		xpt_schedule(periph, priority);
+		return;
+	}
+	case PROBE_SERIAL_NUM_0:
+	{
+		struct ccb_scsiio *csio;
+		struct scsi_vpd_supported_page_list *page_list;
+		int length, serialnum_supported, i;
+
+		serialnum_supported = 0;
+		csio = &done_ccb->csio;
+		page_list =
+		    (struct scsi_vpd_supported_page_list *)csio->data_ptr;
+
+		if (page_list == NULL) {
+			/*
+			 * Don't process the command as it was never sent
+			 */
+		} else if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP
+		    && (page_list->length > 0)) {
+			length = min(page_list->length,
+			    SVPD_SUPPORTED_PAGES_SIZE);
+			for (i = 0; i < length; i++) {
+				if (page_list->list[i] ==
+				    SVPD_UNIT_SERIAL_NUMBER) {
+					serialnum_supported = 1;
+					break;
+				}
+			}
+		} else if (cam_periph_error(done_ccb, 0,
+					    SF_RETRY_UA|SF_NO_PRINT,
+					    &softc->saved_ccb) == ERESTART) {
+			return;
+		} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
+			/* Don't wedge the queue */
+			xpt_release_devq(done_ccb->ccb_h.path, /*count*/1,
+					 /*run_queue*/TRUE);
+		}
+
+		if (page_list != NULL)
+			free(page_list, M_DEVBUF);
+
+		if (serialnum_supported) {
+			xpt_release_ccb(done_ccb);
+			softc->action = PROBE_SERIAL_NUM_1;
+			xpt_schedule(periph, priority);
+			return;
+		}
+		xpt_release_ccb(done_ccb);
+		softc->action = PROBE_TUR_FOR_NEGOTIATION;
+		xpt_schedule(periph, done_ccb->ccb_h.pinfo.priority);
+		return;
+	}
+			
+	case PROBE_SERIAL_NUM_1:
+	{
+		struct ccb_scsiio *csio;
+		struct scsi_vpd_unit_serial_number *serial_buf;
+		u_int32_t  priority;
+		int changed;
+		int have_serialnum;
+
+		changed = 1;
+		have_serialnum = 0;
+		csio = &done_ccb->csio;
+		priority = done_ccb->ccb_h.pinfo.priority;
+		serial_buf =
+		    (struct scsi_vpd_unit_serial_number *)csio->data_ptr;
+
+		/* Clean up from previous instance of this device */
+		if (path->device->serial_num != NULL) {
+			free(path->device->serial_num, M_SCSIXPT);
+			path->device->serial_num = NULL;
+			path->device->serial_num_len = 0;
+		}
+
+		if (serial_buf == NULL) {
+			/*
+			 * Don't process the command as it was never sent
+			 */
+		} else if ((csio->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP
+			&& (serial_buf->length > 0)) {
+
+			have_serialnum = 1;
+			path->device->serial_num =
+				(u_int8_t *)malloc((serial_buf->length + 1),
+						   M_SCSIXPT, M_NOWAIT);
+			if (path->device->serial_num != NULL) {
+				bcopy(serial_buf->serial_num,
+				      path->device->serial_num,
+				      serial_buf->length);
+				path->device->serial_num_len =
+				    serial_buf->length;
+				path->device->serial_num[serial_buf->length]
+				    = '\0';
+			}
+		} else if (cam_periph_error(done_ccb, 0,
+					    SF_RETRY_UA|SF_NO_PRINT,
+					    &softc->saved_ccb) == ERESTART) {
+			return;
+		} else if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
+			/* Don't wedge the queue */
+			xpt_release_devq(done_ccb->ccb_h.path, /*count*/1,
+					 /*run_queue*/TRUE);
+		}
+		
+		/*
+		 * Let's see if we have seen this device before.
+		 */
+		if ((softc->flags & PROBE_INQUIRY_CKSUM) != 0) {
+			MD5_CTX context;
+			u_int8_t digest[16];
+
+			MD5Init(&context);
+			
+			MD5Update(&context,
+				  (unsigned char *)&path->device->inq_data,
+				  sizeof(struct scsi_inquiry_data));
+
+			if (have_serialnum)
+				MD5Update(&context, serial_buf->serial_num,
+					  serial_buf->length);
+
+			MD5Final(digest, &context);
+			if (bcmp(softc->digest, digest, 16) == 0)
+				changed = 0;
+
+			/*
+			 * XXX Do we need to do a TUR in order to ensure
+			 *     that the device really hasn't changed???
+			 */
+			if ((changed != 0)
+			 && ((softc->flags & PROBE_NO_ANNOUNCE) == 0))
+				xpt_async(AC_LOST_DEVICE, path, NULL);
+		}
+		if (serial_buf != NULL)
+			free(serial_buf, M_SCSIXPT);
+
+		if (changed != 0) {
+			/*
+			 * Now that we have all the necessary
+			 * information to safely perform transfer
+			 * negotiations... Controllers don't perform
+			 * any negotiation or tagged queuing until
+			 * after the first XPT_SET_TRAN_SETTINGS ccb is
+			 * received.  So, on a new device, just retrieve
+			 * the user settings, and set them as the current
+			 * settings to set the device up.
+			 */
+			proberequestdefaultnegotiation(periph);
+			xpt_release_ccb(done_ccb);
+
+			/*
+			 * Perform a TUR to allow the controller to
+			 * perform any necessary transfer negotiation.
+			 */
+			softc->action = PROBE_TUR_FOR_NEGOTIATION;
+			xpt_schedule(periph, priority);
+			return;
+		}
+		xpt_release_ccb(done_ccb);
+		break;
+	}
+	case PROBE_TUR_FOR_NEGOTIATION:
+	case PROBE_DV_EXIT:
+		if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
+			/* Don't wedge the queue */
+			xpt_release_devq(done_ccb->ccb_h.path, /*count*/1,
+					 /*run_queue*/TRUE);
+		}
+		/*
+		 * Do Domain Validation for lun 0 on devices that claim
+		 * to support Synchronous Transfer modes.
+		 */
+	 	if (softc->action == PROBE_TUR_FOR_NEGOTIATION
+		 && done_ccb->ccb_h.target_lun == 0
+		 && (path->device->inq_data.flags & SID_Sync) != 0
+                 && (path->device->flags & CAM_DEV_IN_DV) == 0) {
+			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+			    ("Begin Domain Validation\n"));
+			path->device->flags |= CAM_DEV_IN_DV;
+			xpt_release_ccb(done_ccb);
+			softc->action = PROBE_INQUIRY_BASIC_DV1;
+			xpt_schedule(periph, priority);
+			return;
+		}
+		if (softc->action == PROBE_DV_EXIT) {
+			CAM_DEBUG(periph->path, CAM_DEBUG_INFO,
+			    ("Leave Domain Validation\n"));
+		}
+		path->device->flags &=
+		    ~(CAM_DEV_UNCONFIGURED|CAM_DEV_IN_DV|CAM_DEV_DV_HIT_BOTTOM);
+		if ((softc->flags & PROBE_NO_ANNOUNCE) == 0) {
+			/* Inform the XPT that a new device has been found */
+			done_ccb->ccb_h.func_code = XPT_GDEV_TYPE;
+			xpt_action(done_ccb);
+			xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path,
+				  done_ccb);
+		}
+		xpt_release_ccb(done_ccb);
+		break;
+	case PROBE_INQUIRY_BASIC_DV1:
+	case PROBE_INQUIRY_BASIC_DV2:
+	{
+		struct scsi_inquiry_data *nbuf;
+		struct ccb_scsiio *csio;
+
+		if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
+			/* Don't wedge the queue */
+			xpt_release_devq(done_ccb->ccb_h.path, /*count*/1,
+					 /*run_queue*/TRUE);
+		}
+		csio = &done_ccb->csio;
+		nbuf = (struct scsi_inquiry_data *)csio->data_ptr;
+		if (bcmp(nbuf, &path->device->inq_data, SHORT_INQUIRY_LENGTH)) {
+			xpt_print(path,
+			    "inquiry data fails comparison at DV%d step\n",
+			    softc->action == PROBE_INQUIRY_BASIC_DV1? 1 : 2);
+			if (proberequestbackoff(periph, path->device)) {
+				path->device->flags &= ~CAM_DEV_IN_DV;
+				softc->action = PROBE_TUR_FOR_NEGOTIATION;
+			} else {
+				/* give up */
+				softc->action = PROBE_DV_EXIT;
+			}
+			free(nbuf, M_SCSIXPT);
+			xpt_release_ccb(done_ccb);
+			xpt_schedule(periph, priority);
+			return;
+		}
+		free(nbuf, M_SCSIXPT);
+		if (softc->action == PROBE_INQUIRY_BASIC_DV1) {
+			softc->action = PROBE_INQUIRY_BASIC_DV2;
+			xpt_release_ccb(done_ccb);
+			xpt_schedule(periph, priority);
+			return;
+		}

>>> TRUNCATED FOR MAIL (1000 lines) <<<



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