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