kern/103602: drive gets wedged on READ CD CAPACITY if no disc is in

Josh Carroll josh.carroll at gmail.com
Wed Apr 25 05:44:56 UTC 2007


Oops, I think gmail attached the patch as a MIME encoded attachment.
Here's the patch:


diff -urN sys.old/cam/cam_xpt.c sys/cam/cam_xpt.c
--- sys.old/cam/cam_xpt.c	Sat Sep 23 11:42:08 2006
+++ sys/cam/cam_xpt.c	Tue Apr 24 13:31:28 2007
@@ -254,6 +254,11 @@
 static struct xpt_quirk_entry xpt_quirk_table[] =
 {
 	{
+		/* Hangs on INQUIRY with EVPD flag??? */
+		{ T_CDROM, SIP_MEDIA_REMOVABLE, sony, "DVD RW DRU*", "*" },
+		CAM_QUIRK_NOSERIAL, /*mintags*/0, /*maxtags*/0
+	},
+	{
 		/* Reports QUEUE FULL for temporary resource shortages */
 		{ T_DIRECT, SIP_MEDIA_FIXED, quantum, "XP39100*", "*" },
 		/*quirks*/0, /*mintags*/24, /*maxtags*/32
diff -urN sys.old/cam/scsi/scsi_cd.c sys/cam/scsi/scsi_cd.c
--- sys.old/cam/scsi/scsi_cd.c	Wed Jul 26 00:48:51 2006
+++ sys/cam/scsi/scsi_cd.c	Tue Apr 24 13:31:28 2007
@@ -107,9 +107,10 @@
 } cd_flags;

 typedef enum {
-	CD_CCB_PROBE		= 0x01,
-	CD_CCB_BUFFER_IO	= 0x02,
-	CD_CCB_WAITING		= 0x03,
+	CD_CCB_PROBE_TUR	= 0x01,
+	CD_CCB_PROBE_RCAP	= 0x02,
+	CD_CCB_BUFFER_IO	= 0x03,
+	CD_CCB_WAITING		= 0x04,
 	CD_CCB_TYPE_MASK	= 0x0F,
 	CD_CCB_RETRY_UA		= 0x10
 } cd_ccb_state;
@@ -135,7 +136,8 @@
 };

 typedef enum {
-	CD_STATE_PROBE,
+	CD_STATE_PROBE_RCAP,
+	CD_STATE_PROBE_TUR,
 	CD_STATE_NORMAL
 } cd_state;

@@ -675,7 +677,7 @@
 	bzero(softc, sizeof(*softc));
 	LIST_INIT(&softc->pending_ccbs);
 	STAILQ_INIT(&softc->mode_queue);
-	softc->state = CD_STATE_PROBE;
+	softc->state = CD_STATE_PROBE_TUR;
 	bioq_init(&softc->bio_queue);
 	if (SID_IS_REMOVABLE(&cgd->inq_data))
 		softc->flags |= CD_FLAG_DISC_REMOVABLE;
@@ -1561,7 +1563,21 @@
 		}
 		break;
 	}
-	case CD_STATE_PROBE:
+	case CD_STATE_PROBE_TUR:
+	{
+		csio = &start_ccb->csio;
+		scsi_test_unit_ready(csio,
+				     /*retries*/4,
+				     cddone,
+				     MSG_SIMPLE_Q_TAG,
+				     SSD_FULL_SIZE,
+				     /*timeout*/10000);
+		start_ccb->ccb_h.ccb_bp = NULL;
+		start_ccb->ccb_h.ccb_state = CD_CCB_PROBE_TUR;
+		xpt_action(start_ccb);
+		break;
+	}
+	case CD_STATE_PROBE_RCAP:
 	{

 		rcap = (struct scsi_read_capacity_data *)malloc(sizeof(*rcap),
@@ -1582,7 +1598,7 @@
 				   SSD_FULL_SIZE,
 				   /*timeout*/20000);
 		start_ccb->ccb_h.ccb_bp = NULL;
-		start_ccb->ccb_h.ccb_state = CD_CCB_PROBE;
+		start_ccb->ccb_h.ccb_state = CD_CCB_PROBE_RCAP;
 		xpt_action(start_ccb);
 		break;
 	}
@@ -1673,7 +1689,35 @@
 		biofinish(bp, NULL, 0);
 		break;
 	}
-	case CD_CCB_PROBE:
+	case CD_CCB_PROBE_TUR:
+	{
+		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) {
+			softc->state = CD_STATE_PROBE_RCAP;
+			xpt_release_ccb(done_ccb);
+			xpt_schedule(periph, /*priority*/5);
+			return;
+		}
+		if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0) {
+			/* Don't wedge the queue */
+			cam_release_devq(done_ccb->ccb_h.path,
+					 /*relsim_flags*/0,
+					 /*reduction*/0,
+					 /*timeout*/0,
+					 /*getcount_only*/0);
+		}
+		if (bootverbose)
+			cam_error_print(done_ccb, CAM_ESF_ALL, CAM_EPF_ALL);
+		/*
+		 * The TUR generated an error, so there likely isn't media in
+		 * the drive.  Some drives lock up if they are sent a read
+		 * capacity command while in this state, so avoid doing that.
+		 */
+		softc->state = CD_STATE_NORMAL;
+		xpt_release_ccb(done_ccb);
+		cam_periph_unlock(periph);
+		return;
+	}
+	case CD_CCB_PROBE_RCAP:
 	{
 		struct	   scsi_read_capacity_data *rdcap;
 		char	   announce_buf[120]; /*
diff -urN sys.old/dev/ata/atapi-cam.c sys/dev/ata/atapi-cam.c
--- sys.old/dev/ata/atapi-cam.c	Tue Apr  4 09:07:42 2006
+++ sys/dev/ata/atapi-cam.c	Tue Apr 24 13:31:22 2007
@@ -505,10 +505,10 @@

 	switch (ccb_h->flags & CAM_DIR_MASK) {
 	case CAM_DIR_IN:
-	     request_flags |= ATA_R_READ|ATA_R_DMA;
+	     request_flags |= ATA_R_READ;
 	     break;
 	case CAM_DIR_OUT:
-	     request_flags |= ATA_R_WRITE|ATA_R_DMA;
+	     request_flags |= ATA_R_WRITE;
 	     break;
 	case CAM_DIR_NONE:
 	     /* No flags need to be set */
@@ -517,8 +517,6 @@
 	     device_printf(softc->dev, "unknown IO operation\n");
 	     goto action_invalid;
 	}
-	if (softc->atadev[tid]->mode < ATA_DMA)
-	    request_flags &= ~ATA_R_DMA;

 	if ((hcb = allocate_hcb(softc, unit, bus, ccb)) == NULL) {
 	    printf("cannot allocate ATAPI/CAM hcb\n");
@@ -580,7 +578,23 @@
 	    request->u.atapi.ccb[3] = request->u.atapi.ccb[1] & 0x1f;
 	    request->u.atapi.ccb[2] = 0;
 	    request->u.atapi.ccb[1] = 0;
+
+	case READ_10:
+	    /* FALLTHROUGH */
+	case WRITE_10:
+	    /* FALLTHROUGH */
+	case READ_12:
+	    /* FALLTHROUGH */
+	case WRITE_12:
+	    /*
+	     * Enable DMA (if target supports it) for READ and WRITE commands
+	     * only, as some combinations of drive, controller and chipset do
+	     * not behave correctly when DMA is enabled for other commands.
+	     */
+	    if (softc->atadev[tid]->mode >= ATA_DMA)
+		request_flags |= ATA_R_DMA;
 	    break;
+
 	}

 	if ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN && (len & 1)) {
@@ -746,7 +760,9 @@
     free_hcb(hcb);
     ccb->ccb_h.status =
 	status | (ccb->ccb_h.status & ~(CAM_STATUS_MASK | CAM_SIM_QUEUED));
+    mtx_lock(&Giant);
     xpt_done(ccb);
+    mtx_unlock(&Giant);
 }

 static void


More information about the freebsd-scsi mailing list