Commit 55a8e2c8 authored by Tejun Heo's avatar Tejun Heo Committed by Jeff Garzik

[PATCH] libata: implement presence detection via polling IDENTIFY

On some controllers (ICHs in piix mode), there is *NO* reliable way to
determine device presence other than issuing IDENTIFY and see how the
transaction proceeds by watching the TF status register.

libata acted this way before irq-pio and phantom devices caused very
little problem but now that IDENTIFY is performed using IRQ drive PIO,
such phantom devices now result in multiple 30sec timeouts during
boot.

This patch implements ATA_FLAG_DETECT_POLLING.  If a LLD sets this
flag, libata core issues the initial IDENTIFY in polling mode and if
the initial data transfer fails w/ HSM violation, the port is
considered to be empty thus replicating the old libata and IDE
behavior.
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent bff04647
...@@ -1272,9 +1272,20 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, ...@@ -1272,9 +1272,20 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class,
tf.protocol = ATA_PROT_PIO; tf.protocol = ATA_PROT_PIO;
/* presence detection using polling IDENTIFY? */
if (flags & ATA_READID_DETECT)
tf.flags |= ATA_TFLAG_POLLING;
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_FROM_DEVICE, err_mask = ata_exec_internal(dev, &tf, NULL, DMA_FROM_DEVICE,
id, sizeof(id[0]) * ATA_ID_WORDS); id, sizeof(id[0]) * ATA_ID_WORDS);
if (err_mask) { if (err_mask) {
if ((flags & ATA_READID_DETECT) &&
(err_mask & AC_ERR_NODEV_HINT)) {
DPRINTK("ata%u.%d: NODEV after polling detection\n",
ap->id, dev->devno);
return -ENOENT;
}
rc = -EIO; rc = -EIO;
reason = "I/O error"; reason = "I/O error";
goto err_out; goto err_out;
...@@ -4285,8 +4296,12 @@ int ata_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc, ...@@ -4285,8 +4296,12 @@ int ata_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc,
/* device stops HSM for abort/error */ /* device stops HSM for abort/error */
qc->err_mask |= AC_ERR_DEV; qc->err_mask |= AC_ERR_DEV;
else else
/* HSM violation. Let EH handle this */ /* HSM violation. Let EH handle this.
qc->err_mask |= AC_ERR_HSM; * Phantom devices also trigger this
* condition. Mark hint.
*/
qc->err_mask |= AC_ERR_HSM |
AC_ERR_NODEV_HINT;
ap->hsm_task_state = HSM_ST_ERR; ap->hsm_task_state = HSM_ST_ERR;
goto fsm_start; goto fsm_start;
......
...@@ -1667,12 +1667,23 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap, ...@@ -1667,12 +1667,23 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap,
ata_class_enabled(ehc->classes[dev->devno])) { ata_class_enabled(ehc->classes[dev->devno])) {
dev->class = ehc->classes[dev->devno]; dev->class = ehc->classes[dev->devno];
if (ap->flags & ATA_FLAG_DETECT_POLLING)
readid_flags |= ATA_READID_DETECT;
rc = ata_dev_read_id(dev, &dev->class, readid_flags, rc = ata_dev_read_id(dev, &dev->class, readid_flags,
dev->id); dev->id);
if (rc == 0) { if (rc == 0) {
ehc->i.flags |= ATA_EHI_PRINTINFO; ehc->i.flags |= ATA_EHI_PRINTINFO;
rc = ata_dev_configure(dev); rc = ata_dev_configure(dev);
ehc->i.flags &= ~ATA_EHI_PRINTINFO; ehc->i.flags &= ~ATA_EHI_PRINTINFO;
} else if (rc == -ENOENT) {
/* IDENTIFY was issued to non-existent
* device. No need to reset. Just
* thaw and kill the device.
*/
ata_eh_thaw_port(ap);
dev->class = ATA_DEV_UNKNOWN;
rc = 0;
} }
if (rc) { if (rc) {
...@@ -1680,12 +1691,14 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap, ...@@ -1680,12 +1691,14 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap,
break; break;
} }
spin_lock_irqsave(ap->lock, flags); if (ata_dev_enabled(dev)) {
ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG; spin_lock_irqsave(ap->lock, flags);
spin_unlock_irqrestore(ap->lock, flags); ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG;
spin_unlock_irqrestore(ap->lock, flags);
/* new device discovered, configure transfer mode */ /* new device discovered, configure xfermode */
ehc->i.flags |= ATA_EHI_SETMODE; ehc->i.flags |= ATA_EHI_SETMODE;
}
} }
} }
......
...@@ -42,6 +42,8 @@ struct ata_scsi_args { ...@@ -42,6 +42,8 @@ struct ata_scsi_args {
enum { enum {
/* flags for ata_dev_read_id() */ /* flags for ata_dev_read_id() */
ATA_READID_POSTRESET = (1 << 0), /* reading ID after reset */ ATA_READID_POSTRESET = (1 << 0), /* reading ID after reset */
ATA_READID_DETECT = (1 << 1), /* perform presence detection
* using polling IDENTIFY */
}; };
extern struct workqueue_struct *ata_aux_wq; extern struct workqueue_struct *ata_aux_wq;
......
...@@ -176,6 +176,8 @@ enum { ...@@ -176,6 +176,8 @@ enum {
ATA_FLAG_SKIP_D2H_BSY = (1 << 12), /* can't wait for the first D2H ATA_FLAG_SKIP_D2H_BSY = (1 << 12), /* can't wait for the first D2H
* Register FIS clearing BSY */ * Register FIS clearing BSY */
ATA_FLAG_DEBUGMSG = (1 << 13), ATA_FLAG_DEBUGMSG = (1 << 13),
ATA_FLAG_DETECT_POLLING = (1 << 14), /* detect device presence by
* polling IDENTIFY */
/* The following flag belongs to ap->pflags but is kept in /* The following flag belongs to ap->pflags but is kept in
* ap->flags because it's referenced in many LLDs and will be * ap->flags because it's referenced in many LLDs and will be
...@@ -335,6 +337,7 @@ enum ata_completion_errors { ...@@ -335,6 +337,7 @@ enum ata_completion_errors {
AC_ERR_SYSTEM = (1 << 6), /* system error */ AC_ERR_SYSTEM = (1 << 6), /* system error */
AC_ERR_INVALID = (1 << 7), /* invalid argument */ AC_ERR_INVALID = (1 << 7), /* invalid argument */
AC_ERR_OTHER = (1 << 8), /* unknown */ AC_ERR_OTHER = (1 << 8), /* unknown */
AC_ERR_NODEV_HINT = (1 << 9), /* polling device detection hint */
}; };
/* forward declarations */ /* forward declarations */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment