* [PATCH 2/3] libata: add hp-poll support to controllers with hotplug interrutps
2006-10-15 22:37 [PATCHSET] hotplug polling, take 5 Tejun Heo
@ 2006-10-15 22:37 ` Tejun Heo
2006-10-15 22:37 ` [PATCH 1/3] libata: implement hotplug by polling Tejun Heo
` (2 subsequent siblings)
3 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-10-15 22:37 UTC (permalink / raw)
To: jgarzik, alan, linux-ide; +Cc: Tejun Heo
This patch adds hp-poll support to the following drivers.
* ahci
* sata_nv (nf2 and c804 only)
* sata_sil
* sata_sil24
All the above controllers are capable of hotplug-by-interrupt and
hp-poll will be used only for watching disabled ports.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/ata/ahci.c | 4 +++-
drivers/ata/sata_nv.c | 4 ++++
drivers/ata/sata_sil.c | 2 ++
drivers/ata/sata_sil24.c | 3 +++
4 files changed, 12 insertions(+), 1 deletions(-)
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 2592912..d95d99b 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -262,10 +262,12 @@ static const struct ata_port_operations
.freeze = ahci_freeze,
.thaw = ahci_thaw,
-
.error_handler = ahci_error_handler,
.post_internal_cmd = ahci_post_internal_cmd,
+ .hp_poll_activate = sata_std_hp_poll_activate,
+ .hp_poll = sata_std_hp_poll,
+
.port_suspend = ahci_port_suspend,
.port_resume = ahci_port_resume,
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index 323b607..a75d5cc 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -200,6 +200,8 @@ static const struct ata_port_operations
.thaw = nv_nf2_thaw,
.error_handler = nv_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .hp_poll_activate = sata_std_hp_poll_activate,
+ .hp_poll = sata_std_hp_poll,
.data_xfer = ata_pio_data_xfer,
.irq_handler = nv_nf2_interrupt,
.irq_clear = ata_bmdma_irq_clear,
@@ -227,6 +229,8 @@ static const struct ata_port_operations
.thaw = nv_ck804_thaw,
.error_handler = nv_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .hp_poll_activate = sata_std_hp_poll_activate,
+ .hp_poll = sata_std_hp_poll,
.data_xfer = ata_pio_data_xfer,
.irq_handler = nv_ck804_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c
index ca8d993..5c8a349 100644
--- a/drivers/ata/sata_sil.c
+++ b/drivers/ata/sata_sil.c
@@ -205,6 +205,8 @@ static const struct ata_port_operations
.thaw = sil_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .hp_poll_activate = sata_std_hp_poll_activate,
+ .hp_poll = sata_std_hp_poll,
.irq_handler = sil_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.scr_read = sil_scr_read,
diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c
index 169e200..c84efc3 100644
--- a/drivers/ata/sata_sil24.c
+++ b/drivers/ata/sata_sil24.c
@@ -410,6 +410,9 @@ static const struct ata_port_operations
.error_handler = sil24_error_handler,
.post_internal_cmd = sil24_post_internal_cmd,
+ .hp_poll_activate = sata_std_hp_poll_activate,
+ .hp_poll = sata_std_hp_poll,
+
.port_start = sil24_port_start,
.port_stop = sil24_port_stop,
.host_stop = sil24_host_stop,
--
1.4.2.3
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 1/3] libata: implement hotplug by polling
2006-10-15 22:37 [PATCHSET] hotplug polling, take 5 Tejun Heo
2006-10-15 22:37 ` [PATCH 2/3] libata: add hp-poll support to controllers with hotplug interrutps Tejun Heo
@ 2006-10-15 22:37 ` Tejun Heo
2006-10-15 22:37 ` [PATCH 3/3] libata: add hp-poll support to controllers without hotplug interrupts Tejun Heo
2006-11-01 2:07 ` [PATCHSET] hotplug polling, take 5 Jeff Garzik
3 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-10-15 22:37 UTC (permalink / raw)
To: jgarzik, alan, linux-ide; +Cc: Tejun Heo
This patch implements hotplug by polling - hp-poll. It is used for
* hotplug event detection on controllers which don't provide PHY
status changed interrupt.
* hotplug event detection on disabled ports after reset failure.
libata used to leave such ports in frozen state to protect the
system from malfunctioning controller/device. With hp-poll, hotplug
events no such ports are watched safely by polling, such that
removing/replacing the malfunctioning device triggers EH retry on
the port.
There are three port ops for hp-poll - hp_poll_activate, hp_poll,
hp_poll_deactivate. Only hp_poll is mandatory for hp-poll to work.
This patch also implements SATA standard polling callbacks which poll
SError.N/X bits - sata_std_hp_poll_activate() and sata_std_hp_poll().
By default, hp-poll is enabled only on disabled ports. If a LLD
doesn't support hotplug interrupts but can poll for hotplug events, it
should indicate so by setting ATA_FLAG_HP_POLLING which tells libata
to turn on hp-poll by default.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/ata/libata-core.c | 79 +++++++++++++++++++++++++++++
drivers/ata/libata-eh.c | 123 ++++++++++++++++++++++++++++++++++++++++++++-
drivers/ata/libata.h | 2 +
include/linux/libata.h | 10 ++++
4 files changed, 211 insertions(+), 3 deletions(-)
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index c127d6f..006ab12 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -2909,6 +2909,76 @@ void ata_std_postreset(struct ata_port *
}
/**
+ * sata_std_hp_poll_activate - standard SATA hotplug polling activation
+ * @ap: the target ata_port
+ *
+ * Activate SATA std hotplug polling.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void sata_std_hp_poll_activate(struct ata_port *ap)
+{
+ u32 serror;
+
+ sata_scr_read(ap, SCR_ERROR, &serror);
+ serror &= SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+ if (serror)
+ sata_scr_write(ap, SCR_ERROR, serror);
+
+ ap->hp_poll_data = 0;
+}
+
+/**
+ * sata_std_hp_poll - standard SATA hotplug polling callback
+ * @ap: the target ata_port
+ *
+ * Poll SError.N/X for hotplug event. Hotplug event is triggered
+ * only after PHY stays stable for at least one full polling
+ * interval after the first event detection.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * 1 if hotplug event has occurred, 0 otherwise.
+ */
+int sata_std_hp_poll(struct ata_port *ap)
+{
+ unsigned long state = (unsigned long)ap->hp_poll_data;
+ u32 serror;
+ int rc = 0;
+
+ sata_scr_read(ap, SCR_ERROR, &serror);
+ serror &= SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+
+ switch (state) {
+ case 0:
+ if (serror) {
+ /* PHY status could be bouncing crazy due to
+ * faulty controller or device. Don't fire
+ * hotplug event till hotplug event stays
+ * quiescent for one full polling interval.
+ */
+ sata_scr_write(ap, SCR_ERROR, serror);
+ state = 1;
+ }
+ break;
+
+ case 1:
+ if (!serror)
+ rc = 1;
+ else
+ sata_scr_write(ap, SCR_ERROR, serror);
+ break;
+ }
+
+ ap->hp_poll_data = (void *)state;
+
+ return rc;
+}
+
+/**
* ata_dev_same_device - Determine whether new ID matches configured device
* @dev: device to compare against
* @new_class: class of the new device
@@ -5437,6 +5507,7 @@ void ata_host_init(struct ata_host *host
host->dev = dev;
host->flags = flags;
host->ops = ops;
+ INIT_WORK(&host->hp_poll_task, ata_hp_poll_worker, host);
}
/**
@@ -5691,11 +5762,15 @@ void ata_port_detach(struct ata_port *ap
ata_port_wait_eh(ap);
- /* Flush hotplug task. The sequence is similar to
+ /* deactivate hotplug polling */
+ ata_hp_poll_deactivate(ap);
+
+ /* Flush hotplug tasks. The sequence is similar to
* ata_port_flush_task().
*/
flush_workqueue(ata_aux_wq);
cancel_delayed_work(&ap->hotplug_task);
+ cancel_delayed_work(&ap->host->hp_poll_task);
flush_workqueue(ata_aux_wq);
skip_eh:
@@ -6129,6 +6204,8 @@ EXPORT_SYMBOL_GPL(ata_std_prereset);
EXPORT_SYMBOL_GPL(ata_std_softreset);
EXPORT_SYMBOL_GPL(sata_std_hardreset);
EXPORT_SYMBOL_GPL(ata_std_postreset);
+EXPORT_SYMBOL_GPL(sata_std_hp_poll_activate);
+EXPORT_SYMBOL_GPL(sata_std_hp_poll);
EXPORT_SYMBOL_GPL(ata_dev_revalidate);
EXPORT_SYMBOL_GPL(ata_dev_classify);
EXPORT_SYMBOL_GPL(ata_dev_pair);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 02b2b27..6787fbc 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -33,6 +33,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_eh.h>
@@ -44,10 +45,17 @@ #include <linux/libata.h>
#include "libata.h"
+static unsigned long hotplug_polling_interval = 2000;
+module_param(hotplug_polling_interval, ulong, 0644);
+MODULE_PARM_DESC(hotplug_polling_interval,
+ "Hotplug polling interval in milliseconds (default 2000)");
+
static void __ata_port_freeze(struct ata_port *ap);
static void ata_eh_finish(struct ata_port *ap);
static void ata_eh_handle_port_suspend(struct ata_port *ap);
static void ata_eh_handle_port_resume(struct ata_port *ap);
+static void ata_hp_poll_activate(struct ata_port *ap);
+
static void ata_ering_record(struct ata_ering *ering, int is_io,
unsigned int err_mask)
@@ -616,7 +624,10 @@ int ata_port_freeze(struct ata_port *ap)
* ata_eh_freeze_port - EH helper to freeze port
* @ap: ATA port to freeze
*
- * Freeze @ap.
+ * Freeze @ap. As the 'freeze' operation means 'shutdown event
+ * reporting', it is a perfect place to deactivate hp-poll. Note
+ * that ata_port_freeze() always invokes EH and eventually this
+ * function, so deactivating hp-poll in this function is enough.
*
* LOCKING:
* None.
@@ -628,6 +639,8 @@ void ata_eh_freeze_port(struct ata_port
if (!ap->ops->error_handler)
return;
+ ata_hp_poll_deactivate(ap);
+
spin_lock_irqsave(ap->lock, flags);
__ata_port_freeze(ap);
spin_unlock_irqrestore(ap->lock, flags);
@@ -637,7 +650,7 @@ void ata_eh_freeze_port(struct ata_port
* ata_port_thaw_port - EH helper to thaw port
* @ap: ATA port to thaw
*
- * Thaw frozen port @ap.
+ * Thaw frozen port @ap and activate hp-poll if necessary.
*
* LOCKING:
* None.
@@ -658,6 +671,9 @@ void ata_eh_thaw_port(struct ata_port *a
spin_unlock_irqrestore(ap->lock, flags);
+ if (ap->flags & ATA_FLAG_HP_POLLING)
+ ata_hp_poll_activate(ap);
+
DPRINTK("ata%u port thawed\n", ap->id);
}
@@ -2059,6 +2075,9 @@ static int ata_eh_recover(struct ata_por
out:
if (rc) {
+ /* recovery failed, activate hp-poll */
+ ata_hp_poll_activate(ap);
+
for (i = 0; i < ATA_MAX_DEVICES; i++)
ata_dev_disable(&ap->device[i]);
}
@@ -2247,3 +2266,103 @@ static void ata_eh_handle_port_resume(st
}
spin_unlock_irqrestore(ap->lock, flags);
}
+
+static unsigned long ata_hp_poll_delay(void)
+{
+ unsigned long interval, now;
+
+ /* Group polls to polling interval boundaries to allow cpu to
+ * go idle as much as possible.
+ */
+ interval = hotplug_polling_interval * HZ / 1000;
+ now = jiffies;
+ return roundup(now + 1, interval) - now;
+}
+
+/**
+ * ata_hp_poll_activate - activate hotplug polling
+ * @ap: host port to activate hotplug polling for
+ *
+ * Activate hotplug probing for @ap.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+static void ata_hp_poll_activate(struct ata_port *ap)
+{
+ unsigned long flags;
+
+ if (!ap->ops->hp_poll || (ap->pflags & ATA_PFLAG_HP_POLL))
+ return;
+
+ if (ap->ops->hp_poll_activate)
+ ap->ops->hp_poll_activate(ap);
+
+ queue_delayed_work(ata_aux_wq, &ap->host->hp_poll_task,
+ ata_hp_poll_delay());
+ ap->hp_poll_data = 0;
+
+ spin_lock_irqsave(ap->lock, flags);
+ ap->pflags |= ATA_PFLAG_HP_POLL;
+ spin_unlock_irqrestore(ap->lock, flags);
+}
+
+/**
+ * ata_hp_poll_deactivate - deactivate hotplug polling
+ * @ap: host port to deactivate hotplug polling for
+ *
+ * Deactivate hotplug probing for @ap.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_hp_poll_deactivate(struct ata_port *ap)
+{
+ unsigned long flags;
+
+ if (!(ap->pflags & ATA_PFLAG_HP_POLL))
+ return;
+
+ spin_lock_irqsave(ap->lock, flags);
+ ap->pflags &= ~ATA_PFLAG_HP_POLL;
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ if (ap->ops->hp_poll_deactivate)
+ ap->ops->hp_poll_deactivate(ap);
+}
+
+void ata_hp_poll_worker(void *data)
+{
+ struct ata_host *host = data;
+ int i, nr_to_poll = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap = host->ports[i];
+ int rc;
+
+ if (!(ap->pflags & ATA_PFLAG_HP_POLL))
+ continue;
+
+ /* Poll. Positive return value indicates hotplug
+ * event while negative indicates error condition.
+ */
+ rc = ap->ops->hp_poll(ap);
+ if (rc) {
+ if (rc > 0) {
+ ata_ehi_hotplugged(&ap->eh_info);
+ ata_port_freeze(ap);
+ }
+ ap->pflags &= ~ATA_PFLAG_HP_POLL;
+ } else
+ nr_to_poll++;
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (nr_to_poll)
+ queue_delayed_work(ata_aux_wq, &host->hp_poll_task,
+ ata_hp_poll_delay());
+}
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index a5ecb71..017dc08 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -118,5 +118,7 @@ extern enum scsi_eh_timer_return ata_scs
extern void ata_scsi_error(struct Scsi_Host *host);
extern void ata_port_wait_eh(struct ata_port *ap);
extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc);
+extern void ata_hp_poll_worker(void *data);
+extern void ata_hp_poll_deactivate(struct ata_port *ap);
#endif /* __LIBATA_H__ */
diff --git a/include/linux/libata.h b/include/linux/libata.h
index d0a7ad5..4fc74c4 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -175,6 +175,7 @@ enum {
ATA_FLAG_SKIP_D2H_BSY = (1 << 12), /* can't wait for the first D2H
* Register FIS clearing BSY */
ATA_FLAG_DEBUGMSG = (1 << 13),
+ ATA_FLAG_HP_POLLING = (1 << 14), /* hotplug by polling */
/* The following flag belongs to ap->pflags but is kept in
* ap->flags because it's referenced in many LLDs and will be
@@ -192,6 +193,7 @@ enum {
ATA_PFLAG_LOADING = (1 << 4), /* boot/loading probe */
ATA_PFLAG_UNLOADING = (1 << 5), /* module is unloading */
ATA_PFLAG_SCSI_HOTPLUG = (1 << 6), /* SCSI hotplug scheduled */
+ ATA_PFLAG_HP_POLL = (1 << 7), /* hp-poll active */
ATA_PFLAG_FLUSH_PORT_TASK = (1 << 16), /* flush port task */
ATA_PFLAG_SUSPENDED = (1 << 17), /* port is suspended (power) */
@@ -401,6 +403,7 @@ struct ata_host {
unsigned long flags;
int simplex_claimed; /* Keep seperate in case we
ever need to do this locked */
+ struct work_struct hp_poll_task;
struct ata_port *ports[0];
};
@@ -581,6 +584,7 @@ struct ata_port {
pm_message_t pm_mesg;
int *pm_result;
+ void *hp_poll_data;
void *private_data;
u8 sector_buf[ATA_SECT_SIZE]; /* owned by EH */
@@ -628,6 +632,10 @@ struct ata_port_operations {
void (*error_handler) (struct ata_port *ap);
void (*post_internal_cmd) (struct ata_queued_cmd *qc);
+ void (*hp_poll_activate) (struct ata_port *ap);
+ void (*hp_poll_deactivate) (struct ata_port *ap);
+ int (*hp_poll) (struct ata_port *ap);
+
irq_handler_t irq_handler;
void (*irq_clear) (struct ata_port *);
@@ -702,6 +710,8 @@ extern int ata_std_prereset(struct ata_p
extern int ata_std_softreset(struct ata_port *ap, unsigned int *classes);
extern int sata_std_hardreset(struct ata_port *ap, unsigned int *class);
extern void ata_std_postreset(struct ata_port *ap, unsigned int *classes);
+extern void sata_std_hp_poll_activate(struct ata_port *ap);
+extern int sata_std_hp_poll(struct ata_port *ap);
extern int ata_dev_revalidate(struct ata_device *dev, int post_reset);
extern void ata_port_disable(struct ata_port *);
extern void ata_std_ports(struct ata_ioports *ioaddr);
--
1.4.2.3
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 3/3] libata: add hp-poll support to controllers without hotplug interrupts
2006-10-15 22:37 [PATCHSET] hotplug polling, take 5 Tejun Heo
2006-10-15 22:37 ` [PATCH 2/3] libata: add hp-poll support to controllers with hotplug interrutps Tejun Heo
2006-10-15 22:37 ` [PATCH 1/3] libata: implement hotplug by polling Tejun Heo
@ 2006-10-15 22:37 ` Tejun Heo
2006-11-01 2:07 ` [PATCHSET] hotplug polling, take 5 Jeff Garzik
3 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-10-15 22:37 UTC (permalink / raw)
To: jgarzik, alan, linux-ide; +Cc: Tejun Heo
This patch adds hp-poll support to the following drivers.
* sata_nv (generic)
* sata_sil (SIL_FLAG_NO_SATA_IRQ replaced w/ ATA_FLAG_HP_POLLING)
* sata_sis
* sata_svw
* sata_uli
* sata_vsc
All the above drivers currently don't support hotplug interrupts (H/W
restrictions or lack of doc/effort) and all hotplug operations should
be done via hp-poll; thus, ATA_FLAG_HP_POLLING is set for these
drivers.
sata_via is excluded because it randomly locks up on SCR register
access.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/ata/sata_nv.c | 5 ++++-
drivers/ata/sata_sil.c | 9 ++++-----
drivers/ata/sata_sis.c | 5 ++++-
drivers/ata/sata_svw.c | 4 +++-
drivers/ata/sata_uli.c | 6 +++++-
drivers/ata/sata_vsc.c | 4 +++-
6 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index a75d5cc..fcdae1a 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -173,6 +173,8 @@ static const struct ata_port_operations
.thaw = ata_bmdma_thaw,
.error_handler = nv_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .hp_poll_activate = sata_std_hp_poll_activate,
+ .hp_poll = sata_std_hp_poll,
.data_xfer = ata_pio_data_xfer,
.irq_handler = nv_generic_interrupt,
.irq_clear = ata_bmdma_irq_clear,
@@ -245,7 +247,8 @@ static struct ata_port_info nv_port_info
/* generic */
{
.sht = &nv_sht,
- .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
+ .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_HP_POLLING,
.pio_mask = NV_PIO_MASK,
.mwdma_mask = NV_MWDMA_MASK,
.udma_mask = NV_UDMA_MASK,
diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c
index 5c8a349..1b2e36a 100644
--- a/drivers/ata/sata_sil.c
+++ b/drivers/ata/sata_sil.c
@@ -52,7 +52,6 @@ enum {
/*
* host flags
*/
- SIL_FLAG_NO_SATA_IRQ = (1 << 28),
SIL_FLAG_RERR_ON_DMA_ACT = (1 << 29),
SIL_FLAG_MOD15WRITE = (1 << 30),
@@ -230,7 +229,7 @@ static const struct ata_port_info sil_po
{
.sht = &sil_sht,
.flags = SIL_DFL_PORT_FLAGS | SIL_FLAG_MOD15WRITE |
- SIL_FLAG_NO_SATA_IRQ,
+ ATA_FLAG_HP_POLLING,
.pio_mask = 0x1f, /* pio0-4 */
.mwdma_mask = 0x07, /* mwdma0-2 */
.udma_mask = 0x3f, /* udma0-5 */
@@ -454,8 +453,8 @@ static irqreturn_t sil_interrupt(int irq
if (unlikely(!ap || ap->flags & ATA_FLAG_DISABLED))
continue;
- /* turn off SATA_IRQ if not supported */
- if (ap->flags & SIL_FLAG_NO_SATA_IRQ)
+ /* ignore SATA_IRQ if not supported */
+ if (ap->flags & ATA_FLAG_HP_POLLING)
bmdma2 &= ~SIL_DMA_SATA_IRQ;
if (bmdma2 == 0xffffffff ||
@@ -496,7 +495,7 @@ static void sil_thaw(struct ata_port *ap
ata_bmdma_irq_clear(ap);
/* turn on SATA IRQ if supported */
- if (!(ap->flags & SIL_FLAG_NO_SATA_IRQ))
+ if (!(ap->flags & ATA_FLAG_HP_POLLING))
writel(SIL_SIEN_N, mmio_base + sil_port[ap->port_no].sien);
/* turn on IRQ */
diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c
index 0738f52..f31ff5f 100644
--- a/drivers/ata/sata_sis.c
+++ b/drivers/ata/sata_sis.c
@@ -117,6 +117,8 @@ static const struct ata_port_operations
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .hp_poll_activate = sata_std_hp_poll_activate,
+ .hp_poll = sata_std_hp_poll,
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.scr_read = sis_scr_read,
@@ -128,7 +130,8 @@ static const struct ata_port_operations
static struct ata_port_info sis_port_info = {
.sht = &sis_sht,
- .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
+ .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_HP_POLLING,
.pio_mask = 0x1f,
.mwdma_mask = 0x7,
.udma_mask = 0x7f,
diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c
index db32d15..301e42c 100644
--- a/drivers/ata/sata_svw.c
+++ b/drivers/ata/sata_svw.c
@@ -324,6 +324,8 @@ static const struct ata_port_operations
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .hp_poll_activate = sata_std_hp_poll_activate,
+ .hp_poll = sata_std_hp_poll,
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.scr_read = k2_sata_scr_read,
@@ -424,7 +426,7 @@ static int k2_sata_init_one (struct pci_
probe_ent->sht = &k2_sata_sht;
probe_ent->port_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
- ATA_FLAG_MMIO;
+ ATA_FLAG_MMIO | ATA_FLAG_HP_POLLING;
probe_ent->port_ops = &k2_sata_ops;
probe_ent->n_ports = 4;
probe_ent->irq = pdev->irq;
diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c
index 5c603ca..6e9a8dd 100644
--- a/drivers/ata/sata_uli.c
+++ b/drivers/ata/sata_uli.c
@@ -115,6 +115,9 @@ static const struct ata_port_operations
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .hp_poll_activate = sata_std_hp_poll_activate,
+ .hp_poll = sata_std_hp_poll,
+
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
@@ -128,7 +131,8 @@ static const struct ata_port_operations
static struct ata_port_info uli_port_info = {
.sht = &uli_sht,
- .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
+ .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+ ATA_FLAG_HP_POLLING,
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 */
.port_ops = &uli_ops,
diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c
index e654b99..c92fb0d 100644
--- a/drivers/ata/sata_vsc.c
+++ b/drivers/ata/sata_vsc.c
@@ -301,6 +301,8 @@ static const struct ata_port_operations
.thaw = ata_bmdma_thaw,
.error_handler = ata_bmdma_error_handler,
.post_internal_cmd = ata_bmdma_post_internal_cmd,
+ .hp_poll_activate = sata_std_hp_poll_activate,
+ .hp_poll = sata_std_hp_poll,
.irq_handler = vsc_sata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
.scr_read = vsc_sata_scr_read,
@@ -395,7 +397,7 @@ static int __devinit vsc_sata_init_one (
probe_ent->sht = &vsc_sata_sht;
probe_ent->port_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
- ATA_FLAG_MMIO;
+ ATA_FLAG_MMIO | ATA_FLAG_HP_POLLING;
probe_ent->port_ops = &vsc_sata_ops;
probe_ent->n_ports = 4;
probe_ent->irq = pdev->irq;
--
1.4.2.3
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCHSET] hotplug polling, take 5
2006-10-15 22:37 [PATCHSET] hotplug polling, take 5 Tejun Heo
` (2 preceding siblings ...)
2006-10-15 22:37 ` [PATCH 3/3] libata: add hp-poll support to controllers without hotplug interrupts Tejun Heo
@ 2006-11-01 2:07 ` Jeff Garzik
2006-11-01 3:02 ` Tejun Heo
3 siblings, 1 reply; 15+ messages in thread
From: Jeff Garzik @ 2006-11-01 2:07 UTC (permalink / raw)
To: Tejun Heo; +Cc: alan, linux-ide
Tejun Heo wrote:
> Hello, all.
>
> This is the fifth take of hotplug polling patchset. This take doesn't
> contain any real change than rebasing over the current upstream[U].
> PMP patchset will be posted soon on top of this patchset and I wanted
> to avoid confusion by posting patchsets in order.
>
> As the name implies, this patchset implements hotplug by polling.
> hp-poll is used to
>
> * Monitor ports EH gave up. When EH gives up on a port, it freezes
> the port to protect the rest of the system from it. The user used
> to have to issue manual scan to retry the port. hp-poll can monitor
> such port and retry it when hotplug event is detected. This is also
> used by PMP support.
>
> * Support hotplug on controllers which can report hotplug conditions
> but cannot raise interrupt.
Patchset seems sane. I'll need to re-read patch #1 in depth, but I give
everything a tentative ACK for now.
My biggest concern is power usage. On laptops for example, the 99%
common case is that the user will never hot[un]plug a drive, so we
shouldn't waste power bothering with poking disabled ports.
Jeff
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCHSET] hotplug polling, take 5
2006-11-01 2:07 ` [PATCHSET] hotplug polling, take 5 Jeff Garzik
@ 2006-11-01 3:02 ` Tejun Heo
2006-11-01 3:22 ` Jeff Garzik
0 siblings, 1 reply; 15+ messages in thread
From: Tejun Heo @ 2006-11-01 3:02 UTC (permalink / raw)
To: Jeff Garzik; +Cc: alan, linux-ide
Jeff Garzik wrote:
> Tejun Heo wrote:
>> Hello, all.
>>
>> This is the fifth take of hotplug polling patchset. This take doesn't
>> contain any real change than rebasing over the current upstream[U].
>> PMP patchset will be posted soon on top of this patchset and I wanted
>> to avoid confusion by posting patchsets in order.
>>
>> As the name implies, this patchset implements hotplug by polling.
>> hp-poll is used to
>>
>> * Monitor ports EH gave up. When EH gives up on a port, it freezes
>> the port to protect the rest of the system from it. The user used
>> to have to issue manual scan to retry the port. hp-poll can monitor
>> such port and retry it when hotplug event is detected. This is also
>> used by PMP support.
>>
>> * Support hotplug on controllers which can report hotplug conditions
>> but cannot raise interrupt.
>
> Patchset seems sane. I'll need to re-read patch #1 in depth, but I give
> everything a tentative ACK for now.
>
> My biggest concern is power usage. On laptops for example, the 99%
> common case is that the user will never hot[un]plug a drive, so we
> shouldn't waste power bothering with poking disabled ports.
Link powersave patchset should handle that together but I'm still not
sure what to do w/ user interface (which sysfs node to use). Also,
controllers which are used on laptops && use hp-poll by default are
sata_nv (old ones) and some variant of sata_sil used in ati chipset.
So, it shouldn't cause trouble for most laptop users.
--
tejun
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCHSET] hotplug polling, take 5
2006-11-01 3:02 ` Tejun Heo
@ 2006-11-01 3:22 ` Jeff Garzik
2006-11-01 3:38 ` Tejun Heo
0 siblings, 1 reply; 15+ messages in thread
From: Jeff Garzik @ 2006-11-01 3:22 UTC (permalink / raw)
To: Tejun Heo; +Cc: alan, linux-ide
Tejun Heo wrote:
> Jeff Garzik wrote:
>> Tejun Heo wrote:
>>> Hello, all.
>>>
>>> This is the fifth take of hotplug polling patchset. This take doesn't
>>> contain any real change than rebasing over the current upstream[U].
>>> PMP patchset will be posted soon on top of this patchset and I wanted
>>> to avoid confusion by posting patchsets in order.
>>>
>>> As the name implies, this patchset implements hotplug by polling.
>>> hp-poll is used to
>>>
>>> * Monitor ports EH gave up. When EH gives up on a port, it freezes
>>> the port to protect the rest of the system from it. The user used
>>> to have to issue manual scan to retry the port. hp-poll can monitor
>>> such port and retry it when hotplug event is detected. This is also
>>> used by PMP support.
>>>
>>> * Support hotplug on controllers which can report hotplug conditions
>>> but cannot raise interrupt.
>>
>> Patchset seems sane. I'll need to re-read patch #1 in depth, but I
>> give everything a tentative ACK for now.
>>
>> My biggest concern is power usage. On laptops for example, the 99%
>> common case is that the user will never hot[un]plug a drive, so we
>> shouldn't waste power bothering with poking disabled ports.
>
> Link powersave patchset should handle that together but I'm still not
> sure what to do w/ user interface (which sysfs node to use). Also,
> controllers which are used on laptops && use hp-poll by default are
> sata_nv (old ones) and some variant of sata_sil used in ati chipset. So,
> it shouldn't cause trouble for most laptop users.
I'm not saying it causes trouble. Just saying that polling unused ports
is practically guaranteed to require more power than never doing so.
It shouldn't be hard to figure out where to poke stuff into sysfs...
Jeff
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCHSET] hotplug polling, take 5
2006-11-01 3:22 ` Jeff Garzik
@ 2006-11-01 3:38 ` Tejun Heo
0 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-11-01 3:38 UTC (permalink / raw)
To: Jeff Garzik; +Cc: alan, linux-ide
Jeff Garzik wrote:
>> Link powersave patchset should handle that together but I'm still not
>> sure what to do w/ user interface (which sysfs node to use). Also,
>> controllers which are used on laptops && use hp-poll by default are
>> sata_nv (old ones) and some variant of sata_sil used in ati chipset.
>> So, it shouldn't cause trouble for most laptop users.
>
> I'm not saying it causes trouble. Just saying that polling unused ports
> is practically guaranteed to require more power than never doing so.
Yeap, no objection there.
> It shouldn't be hard to figure out where to poke stuff into sysfs...
I dunno. It's policy decision and we don't have ATA sysfs nodes yet.
So, where do we begin? Is it time to start /sys/class/ata_host/ataX tree?
Thanks.
--
tejun
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 1/3] libata: implement hotplug by polling
2007-04-25 6:25 [PATCH 0/3] hotplug polling, respin Robin H. Johnson
@ 2007-04-25 6:28 ` Robin H. Johnson
0 siblings, 0 replies; 15+ messages in thread
From: Robin H. Johnson @ 2007-04-25 6:28 UTC (permalink / raw)
To: linux-ide; +Cc: htejun, robbat2
[-- Attachment #1: Type: text/plain, Size: 13136 bytes --]
This patch implements hotplug by polling - hp-poll. It is used for
* hotplug event detection on controllers which don't provide PHY
status changed interrupt.
* hotplug event detection on disabled ports after reset failure.
libata used to leave such ports in frozen state to protect the
system from malfunctioning controller/device. With hp-poll, hotplug
events no such ports are watched safely by polling, such that
removing/replacing the malfunctioning device triggers EH retry on
the port.
There are three port ops for hp-poll - hp_poll_activate, hp_poll,
hp_poll_deactivate. Only hp_poll is mandatory for hp-poll to work.
This patch also implements SATA standard polling callbacks which poll
SError.N/X bits - sata_std_hp_poll_activate() and sata_std_hp_poll().
By default, hp-poll is enabled only on disabled ports. If a LLD
doesn't support hotplug interrupts but can poll for hotplug events, it
should indicate so by setting ATA_FLAG_HP_POLLING which tells libata
to turn on hp-poll by default.
(This revision contains cleanups that prevent the original patch from working
on newer kernels - robbat2).
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Robin H. Johnson <robbat2@gentoo.org>
---
drivers/ata/libata-core.c | 80 +++++++++++++++++++++++++++++-
drivers/ata/libata-eh.c | 122 ++++++++++++++++++++++++++++++++++++++++++++-
drivers/ata/libata.h | 2 +
include/linux/libata.h | 11 ++++
4 files changed, 212 insertions(+), 3 deletions(-)
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 6d0a946..68ea34b 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -3498,6 +3498,76 @@ void ata_std_postreset(struct ata_port *ap, unsigned int *classes)
}
/**
+ * sata_std_hp_poll_activate - standard SATA hotplug polling activation
+ * @ap: the target ata_port
+ *
+ * Activate SATA std hotplug polling.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void sata_std_hp_poll_activate(struct ata_port *ap)
+{
+ u32 serror;
+
+ sata_scr_read(ap, SCR_ERROR, &serror);
+ serror &= SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+ if (serror)
+ sata_scr_write(ap, SCR_ERROR, serror);
+
+ ap->hp_poll_data = 0;
+}
+
+/**
+ * sata_std_hp_poll - standard SATA hotplug polling callback
+ * @ap: the target ata_port
+ *
+ * Poll SError.N/X for hotplug event. Hotplug event is triggered
+ * only after PHY stays stable for at least one full polling
+ * interval after the first event detection.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * 1 if hotplug event has occurred, 0 otherwise.
+ */
+int sata_std_hp_poll(struct ata_port *ap)
+{
+ unsigned long state = (unsigned long)ap->hp_poll_data;
+ u32 serror;
+ int rc = 0;
+
+ sata_scr_read(ap, SCR_ERROR, &serror);
+ serror &= SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+
+ switch (state) {
+ case 0:
+ if (serror) {
+ /* PHY status could be bouncing crazy due to
+ * faulty controller or device. Don't fire
+ * hotplug event till hotplug event stays
+ * quiescent for one full polling interval.
+ */
+ sata_scr_write(ap, SCR_ERROR, serror);
+ state = 1;
+ }
+ break;
+
+ case 1:
+ if (!serror)
+ rc = 1;
+ else
+ sata_scr_write(ap, SCR_ERROR, serror);
+ break;
+ }
+
+ ap->hp_poll_data = (void *)state;
+
+ return rc;
+}
+
+/**
* ata_dev_same_device - Determine whether new ID matches configured device
* @dev: device to compare against
* @new_class: class of the new device
@@ -6160,6 +6230,7 @@ void ata_host_init(struct ata_host *host, struct device *dev,
host->dev = dev;
host->flags = flags;
host->ops = ops;
+ INIT_DELAYED_WORK(&host->hp_poll_task, ata_hp_poll_worker);
}
/**
@@ -6387,11 +6458,15 @@ void ata_port_detach(struct ata_port *ap)
ata_port_wait_eh(ap);
- /* Flush hotplug task. The sequence is similar to
+ /* deactivate hotplug polling */
+ ata_hp_poll_deactivate(ap);
+
+ /* Flush hotplug tasks. The sequence is similar to
* ata_port_flush_task().
*/
flush_workqueue(ata_aux_wq);
cancel_delayed_work(&ap->hotplug_task);
+ cancel_delayed_work(&ap->host->hp_poll_task);
flush_workqueue(ata_aux_wq);
skip_eh:
@@ -6752,6 +6827,9 @@ EXPORT_SYMBOL_GPL(ata_std_softreset);
EXPORT_SYMBOL_GPL(sata_port_hardreset);
EXPORT_SYMBOL_GPL(sata_std_hardreset);
EXPORT_SYMBOL_GPL(ata_std_postreset);
+EXPORT_SYMBOL_GPL(sata_std_hp_poll_activate);
+EXPORT_SYMBOL_GPL(sata_std_hp_poll);
+EXPORT_SYMBOL_GPL(ata_dev_revalidate);
EXPORT_SYMBOL_GPL(ata_dev_classify);
EXPORT_SYMBOL_GPL(ata_dev_pair);
EXPORT_SYMBOL_GPL(ata_port_disable);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 0dbee55..d70aa04 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -33,6 +33,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_eh.h>
@@ -49,6 +50,10 @@ enum {
ATA_EH_SPDN_SPEED_DOWN = (1 << 1),
ATA_EH_SPDN_FALLBACK_TO_PIO = (1 << 2),
};
+static unsigned long hotplug_polling_interval = 2000;
+module_param(hotplug_polling_interval, ulong, 0644);
+MODULE_PARM_DESC(hotplug_polling_interval,
+ "Hotplug polling interval in milliseconds (default 2000)");
static void __ata_port_freeze(struct ata_port *ap);
static void ata_eh_finish(struct ata_port *ap);
@@ -62,6 +67,7 @@ static int ata_eh_resume(struct ata_port *ap, struct ata_device **r_failed_dev);
#else /* CONFIG_PM */
static void ata_eh_handle_port_suspend(struct ata_port *ap)
{ }
+static void ata_hp_poll_activate(struct ata_port *ap);
static void ata_eh_handle_port_resume(struct ata_port *ap)
{ }
@@ -644,7 +650,10 @@ int ata_port_freeze(struct ata_port *ap)
* ata_eh_freeze_port - EH helper to freeze port
* @ap: ATA port to freeze
*
- * Freeze @ap.
+ * Freeze @ap. As the 'freeze' operation means 'shutdown event
+ * reporting', it is a perfect place to deactivate hp-poll. Note
+ * that ata_port_freeze() always invokes EH and eventually this
+ * function, so deactivating hp-poll in this function is enough.
*
* LOCKING:
* None.
@@ -656,6 +665,8 @@ void ata_eh_freeze_port(struct ata_port *ap)
if (!ap->ops->error_handler)
return;
+ ata_hp_poll_deactivate(ap);
+
spin_lock_irqsave(ap->lock, flags);
__ata_port_freeze(ap);
spin_unlock_irqrestore(ap->lock, flags);
@@ -665,7 +676,7 @@ void ata_eh_freeze_port(struct ata_port *ap)
* ata_port_thaw_port - EH helper to thaw port
* @ap: ATA port to thaw
*
- * Thaw frozen port @ap.
+ * Thaw frozen port @ap and activate hp-poll if necessary.
*
* LOCKING:
* None.
@@ -686,6 +697,9 @@ void ata_eh_thaw_port(struct ata_port *ap)
spin_unlock_irqrestore(ap->lock, flags);
+ if (ap->flags & ATA_FLAG_HP_POLLING)
+ ata_hp_poll_activate(ap);
+
DPRINTK("ata%u port thawed\n", ap->print_id);
}
@@ -2229,6 +2243,9 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
out:
if (rc) {
+ /* recovery failed, activate hp-poll */
+ ata_hp_poll_activate(ap);
+
for (i = 0; i < ATA_MAX_DEVICES; i++)
ata_dev_disable(&ap->device[i]);
}
@@ -2419,3 +2436,104 @@ static void ata_eh_handle_port_resume(struct ata_port *ap)
spin_unlock_irqrestore(ap->lock, flags);
}
#endif /* CONFIG_PM */
+
+static unsigned long ata_hp_poll_delay(void)
+{
+ unsigned long interval, now;
+
+ /* Group polls to polling interval boundaries to allow cpu to
+ * go idle as much as possible.
+ */
+ interval = hotplug_polling_interval * HZ / 1000;
+ now = jiffies;
+ return roundup(now + 1, interval) - now;
+}
+
+/**
+ * ata_hp_poll_activate - activate hotplug polling
+ * @ap: host port to activate hotplug polling for
+ *
+ * Activate hotplug probing for @ap.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+static void ata_hp_poll_activate(struct ata_port *ap)
+{
+ unsigned long flags;
+
+ if (!ap->ops->hp_poll || (ap->pflags & ATA_PFLAG_HP_POLL))
+ return;
+
+ if (ap->ops->hp_poll_activate)
+ ap->ops->hp_poll_activate(ap);
+
+ queue_delayed_work(ata_aux_wq, &ap->host->hp_poll_task,
+ ata_hp_poll_delay());
+ ap->hp_poll_data = 0;
+
+ spin_lock_irqsave(ap->lock, flags);
+ ap->pflags |= ATA_PFLAG_HP_POLL;
+ spin_unlock_irqrestore(ap->lock, flags);
+}
+
+/**
+ * ata_hp_poll_deactivate - deactivate hotplug polling
+ * @ap: host port to deactivate hotplug polling for
+ *
+ * Deactivate hotplug probing for @ap.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_hp_poll_deactivate(struct ata_port *ap)
+{
+ unsigned long flags;
+
+ if (!(ap->pflags & ATA_PFLAG_HP_POLL))
+ return;
+
+ spin_lock_irqsave(ap->lock, flags);
+ ap->pflags &= ~ATA_PFLAG_HP_POLL;
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ if (ap->ops->hp_poll_deactivate)
+ ap->ops->hp_poll_deactivate(ap);
+}
+
+void ata_hp_poll_worker(struct work_struct *work)
+{
+ struct ata_host *host =
+ container_of(work, struct ata_host, hp_poll_task.work);
+ int i, nr_to_poll = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap = host->ports[i];
+ int rc;
+
+ if (!(ap->pflags & ATA_PFLAG_HP_POLL))
+ continue;
+
+ /* Poll. Positive return value indicates hotplug
+ * event while negative indicates error condition.
+ */
+ rc = ap->ops->hp_poll(ap);
+ if (rc) {
+ if (rc > 0) {
+ ata_ehi_hotplugged(&ap->eh_info);
+ ata_port_freeze(ap);
+ }
+ ap->pflags &= ~ATA_PFLAG_HP_POLL;
+ } else
+ nr_to_poll++;
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (nr_to_poll)
+ queue_delayed_work(ata_aux_wq, &host->hp_poll_task,
+ ata_hp_poll_delay());
+}
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index 5f4d40c..52377dd 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -153,6 +153,8 @@ extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd);
extern void ata_scsi_error(struct Scsi_Host *host);
extern void ata_port_wait_eh(struct ata_port *ap);
extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc);
+extern void ata_hp_poll_worker(struct work_struct *work);
+extern void ata_hp_poll_deactivate(struct ata_port *ap);
/* libata-sff.c */
extern u8 ata_irq_on(struct ata_port *ap);
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 73b86dd..0fd2a8b 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -174,6 +174,7 @@ enum {
ATA_FLAG_SETXFER_POLLING= (1 << 14), /* use polling for SETXFER */
ATA_FLAG_IGN_SIMPLEX = (1 << 15), /* ignore SIMPLEX */
ATA_FLAG_NO_IORDY = (1 << 16), /* controller lacks iordy */
+ ATA_FLAG_HP_POLLING = (1 << 17), /* hotplug by polling */
/* The following flag belongs to ap->pflags but is kept in
* ap->flags because it's referenced in many LLDs and will be
@@ -191,6 +192,7 @@ enum {
ATA_PFLAG_LOADING = (1 << 4), /* boot/loading probe */
ATA_PFLAG_UNLOADING = (1 << 5), /* module is unloading */
ATA_PFLAG_SCSI_HOTPLUG = (1 << 6), /* SCSI hotplug scheduled */
+ ATA_PFLAG_HP_POLL = (1 << 7), /* hp-poll active */
ATA_PFLAG_FLUSH_PORT_TASK = (1 << 16), /* flush port task */
ATA_PFLAG_SUSPENDED = (1 << 17), /* port is suspended (power) */
@@ -379,6 +381,7 @@ struct ata_host {
const struct ata_port_operations *ops;
unsigned long flags;
struct ata_port *simplex_claimed; /* channel owning the DMA */
+ struct delayed_work hp_poll_task;
struct ata_port *ports[0];
};
@@ -564,6 +567,7 @@ struct ata_port {
pm_message_t pm_mesg;
int *pm_result;
+ void *hp_poll_data;
void *private_data;
u8 sector_buf[ATA_SECT_SIZE]; /* owned by EH */
@@ -613,6 +617,10 @@ struct ata_port_operations {
void (*error_handler) (struct ata_port *ap);
void (*post_internal_cmd) (struct ata_queued_cmd *qc);
+ void (*hp_poll_activate) (struct ata_port *ap);
+ void (*hp_poll_deactivate) (struct ata_port *ap);
+ int (*hp_poll) (struct ata_port *ap);
+
irq_handler_t irq_handler;
void (*irq_clear) (struct ata_port *);
u8 (*irq_on) (struct ata_port *);
@@ -694,6 +702,9 @@ extern int sata_port_hardreset(struct ata_port *ap,
const unsigned long *timing);
extern int sata_std_hardreset(struct ata_port *ap, unsigned int *class);
extern void ata_std_postreset(struct ata_port *ap, unsigned int *classes);
+extern void sata_std_hp_poll_activate(struct ata_port *ap);
+extern int sata_std_hp_poll(struct ata_port *ap);
+extern int ata_dev_revalidate(struct ata_device *dev, unsigned int flags);
extern void ata_port_disable(struct ata_port *);
extern void ata_std_ports(struct ata_ioports *ioaddr);
#ifdef CONFIG_PCI
--
Robin Hugh Johnson
Gentoo Linux Developer & Council Member
E-Mail : robbat2@gentoo.org
GnuPG FP : 11AC BA4F 4778 E3F6 E4ED F38E B27B 944E 3488 4E85
[-- Attachment #2: Type: application/pgp-signature, Size: 321 bytes --]
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 1/3] libata: implement hotplug by polling
2006-10-10 5:36 [PATCHSET] hotplug polling, take 4 Tejun Heo
@ 2006-10-10 5:36 ` Tejun Heo
0 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-10-10 5:36 UTC (permalink / raw)
To: jgarzik, alan, linux-ide; +Cc: Tejun Heo
This patch implements hotplug by polling - hp-poll. It is used for
* hotplug event detection on controllers which don't provide PHY
status changed interrupt.
* hotplug event detection on disabled ports after reset failure.
libata used to leave such ports in frozen state to protect the
system from malfunctioning controller/device. With hp-poll, hotplug
events no such ports are watched safely by polling, such that
removing/replacing the malfunctioning device triggers EH retry on
the port.
There are three port ops for hp-poll - hp_poll_activate, hp_poll,
hp_poll_deactivate. Only hp_poll is mandatory for hp-poll to work.
This patch also implements SATA standard polling callbacks which poll
SError.N/X bits - sata_std_hp_poll_activate() and sata_std_hp_poll().
By default, hp-poll is enabled only on disabled ports. If a LLD
doesn't support hotplug interrupts but can poll for hotplug events, it
should indicate so by setting ATA_FLAG_HP_POLLING which tells libata
to turn on hp-poll by default.
---
drivers/ata/libata-core.c | 79 +++++++++++++++++++++++++++++
drivers/ata/libata-eh.c | 123 ++++++++++++++++++++++++++++++++++++++++++++-
drivers/ata/libata.h | 2 +
include/linux/libata.h | 10 ++++
4 files changed, 211 insertions(+), 3 deletions(-)
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 83728a9..8f0b1b2 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -2909,6 +2909,76 @@ void ata_std_postreset(struct ata_port *
}
/**
+ * sata_std_hp_poll_activate - standard SATA hotplug polling activation
+ * @ap: the target ata_port
+ *
+ * Activate SATA std hotplug polling.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void sata_std_hp_poll_activate(struct ata_port *ap)
+{
+ u32 serror;
+
+ sata_scr_read(ap, SCR_ERROR, &serror);
+ serror &= SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+ if (serror)
+ sata_scr_write(ap, SCR_ERROR, serror);
+
+ ap->hp_poll_data = 0;
+}
+
+/**
+ * sata_std_hp_poll - standard SATA hotplug polling callback
+ * @ap: the target ata_port
+ *
+ * Poll SError.N/X for hotplug event. Hotplug event is triggered
+ * only after PHY stays stable for at least one full polling
+ * interval after the first event detection.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * 1 if hotplug event has occurred, 0 otherwise.
+ */
+int sata_std_hp_poll(struct ata_port *ap)
+{
+ unsigned long state = (unsigned long)ap->hp_poll_data;
+ u32 serror;
+ int rc = 0;
+
+ sata_scr_read(ap, SCR_ERROR, &serror);
+ serror &= SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+
+ switch (state) {
+ case 0:
+ if (serror) {
+ /* PHY status could be bouncing crazy due to
+ * faulty controller or device. Don't fire
+ * hotplug event till hotplug event stays
+ * quiescent for one full polling interval.
+ */
+ sata_scr_write(ap, SCR_ERROR, serror);
+ state = 1;
+ }
+ break;
+
+ case 1:
+ if (!serror)
+ rc = 1;
+ else
+ sata_scr_write(ap, SCR_ERROR, serror);
+ break;
+ }
+
+ ap->hp_poll_data = (void *)state;
+
+ return rc;
+}
+
+/**
* ata_dev_same_device - Determine whether new ID matches configured device
* @dev: device to compare against
* @new_class: class of the new device
@@ -5430,6 +5500,7 @@ void ata_host_init(struct ata_host *host
host->dev = dev;
host->flags = flags;
host->ops = ops;
+ INIT_WORK(&host->hp_poll_task, ata_hp_poll_worker, host);
}
/**
@@ -5684,11 +5755,15 @@ void ata_port_detach(struct ata_port *ap
ata_port_wait_eh(ap);
- /* Flush hotplug task. The sequence is similar to
+ /* deactivate hotplug polling */
+ ata_hp_poll_deactivate(ap);
+
+ /* Flush hotplug tasks. The sequence is similar to
* ata_port_flush_task().
*/
flush_workqueue(ata_aux_wq);
cancel_delayed_work(&ap->hotplug_task);
+ cancel_delayed_work(&ap->host->hp_poll_task);
flush_workqueue(ata_aux_wq);
skip_eh:
@@ -6122,6 +6197,8 @@ EXPORT_SYMBOL_GPL(ata_std_prereset);
EXPORT_SYMBOL_GPL(ata_std_softreset);
EXPORT_SYMBOL_GPL(sata_std_hardreset);
EXPORT_SYMBOL_GPL(ata_std_postreset);
+EXPORT_SYMBOL_GPL(sata_std_hp_poll_activate);
+EXPORT_SYMBOL_GPL(sata_std_hp_poll);
EXPORT_SYMBOL_GPL(ata_dev_revalidate);
EXPORT_SYMBOL_GPL(ata_dev_classify);
EXPORT_SYMBOL_GPL(ata_dev_pair);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 02b2b27..6787fbc 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -33,6 +33,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_eh.h>
@@ -44,10 +45,17 @@ #include <linux/libata.h>
#include "libata.h"
+static unsigned long hotplug_polling_interval = 2000;
+module_param(hotplug_polling_interval, ulong, 0644);
+MODULE_PARM_DESC(hotplug_polling_interval,
+ "Hotplug polling interval in milliseconds (default 2000)");
+
static void __ata_port_freeze(struct ata_port *ap);
static void ata_eh_finish(struct ata_port *ap);
static void ata_eh_handle_port_suspend(struct ata_port *ap);
static void ata_eh_handle_port_resume(struct ata_port *ap);
+static void ata_hp_poll_activate(struct ata_port *ap);
+
static void ata_ering_record(struct ata_ering *ering, int is_io,
unsigned int err_mask)
@@ -616,7 +624,10 @@ int ata_port_freeze(struct ata_port *ap)
* ata_eh_freeze_port - EH helper to freeze port
* @ap: ATA port to freeze
*
- * Freeze @ap.
+ * Freeze @ap. As the 'freeze' operation means 'shutdown event
+ * reporting', it is a perfect place to deactivate hp-poll. Note
+ * that ata_port_freeze() always invokes EH and eventually this
+ * function, so deactivating hp-poll in this function is enough.
*
* LOCKING:
* None.
@@ -628,6 +639,8 @@ void ata_eh_freeze_port(struct ata_port
if (!ap->ops->error_handler)
return;
+ ata_hp_poll_deactivate(ap);
+
spin_lock_irqsave(ap->lock, flags);
__ata_port_freeze(ap);
spin_unlock_irqrestore(ap->lock, flags);
@@ -637,7 +650,7 @@ void ata_eh_freeze_port(struct ata_port
* ata_port_thaw_port - EH helper to thaw port
* @ap: ATA port to thaw
*
- * Thaw frozen port @ap.
+ * Thaw frozen port @ap and activate hp-poll if necessary.
*
* LOCKING:
* None.
@@ -658,6 +671,9 @@ void ata_eh_thaw_port(struct ata_port *a
spin_unlock_irqrestore(ap->lock, flags);
+ if (ap->flags & ATA_FLAG_HP_POLLING)
+ ata_hp_poll_activate(ap);
+
DPRINTK("ata%u port thawed\n", ap->id);
}
@@ -2059,6 +2075,9 @@ static int ata_eh_recover(struct ata_por
out:
if (rc) {
+ /* recovery failed, activate hp-poll */
+ ata_hp_poll_activate(ap);
+
for (i = 0; i < ATA_MAX_DEVICES; i++)
ata_dev_disable(&ap->device[i]);
}
@@ -2247,3 +2266,103 @@ static void ata_eh_handle_port_resume(st
}
spin_unlock_irqrestore(ap->lock, flags);
}
+
+static unsigned long ata_hp_poll_delay(void)
+{
+ unsigned long interval, now;
+
+ /* Group polls to polling interval boundaries to allow cpu to
+ * go idle as much as possible.
+ */
+ interval = hotplug_polling_interval * HZ / 1000;
+ now = jiffies;
+ return roundup(now + 1, interval) - now;
+}
+
+/**
+ * ata_hp_poll_activate - activate hotplug polling
+ * @ap: host port to activate hotplug polling for
+ *
+ * Activate hotplug probing for @ap.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+static void ata_hp_poll_activate(struct ata_port *ap)
+{
+ unsigned long flags;
+
+ if (!ap->ops->hp_poll || (ap->pflags & ATA_PFLAG_HP_POLL))
+ return;
+
+ if (ap->ops->hp_poll_activate)
+ ap->ops->hp_poll_activate(ap);
+
+ queue_delayed_work(ata_aux_wq, &ap->host->hp_poll_task,
+ ata_hp_poll_delay());
+ ap->hp_poll_data = 0;
+
+ spin_lock_irqsave(ap->lock, flags);
+ ap->pflags |= ATA_PFLAG_HP_POLL;
+ spin_unlock_irqrestore(ap->lock, flags);
+}
+
+/**
+ * ata_hp_poll_deactivate - deactivate hotplug polling
+ * @ap: host port to deactivate hotplug polling for
+ *
+ * Deactivate hotplug probing for @ap.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_hp_poll_deactivate(struct ata_port *ap)
+{
+ unsigned long flags;
+
+ if (!(ap->pflags & ATA_PFLAG_HP_POLL))
+ return;
+
+ spin_lock_irqsave(ap->lock, flags);
+ ap->pflags &= ~ATA_PFLAG_HP_POLL;
+ spin_unlock_irqrestore(ap->lock, flags);
+
+ if (ap->ops->hp_poll_deactivate)
+ ap->ops->hp_poll_deactivate(ap);
+}
+
+void ata_hp_poll_worker(void *data)
+{
+ struct ata_host *host = data;
+ int i, nr_to_poll = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ for (i = 0; i < host->n_ports; i++) {
+ struct ata_port *ap = host->ports[i];
+ int rc;
+
+ if (!(ap->pflags & ATA_PFLAG_HP_POLL))
+ continue;
+
+ /* Poll. Positive return value indicates hotplug
+ * event while negative indicates error condition.
+ */
+ rc = ap->ops->hp_poll(ap);
+ if (rc) {
+ if (rc > 0) {
+ ata_ehi_hotplugged(&ap->eh_info);
+ ata_port_freeze(ap);
+ }
+ ap->pflags &= ~ATA_PFLAG_HP_POLL;
+ } else
+ nr_to_poll++;
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ if (nr_to_poll)
+ queue_delayed_work(ata_aux_wq, &host->hp_poll_task,
+ ata_hp_poll_delay());
+}
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index a5ecb71..017dc08 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -118,5 +118,7 @@ extern enum scsi_eh_timer_return ata_scs
extern void ata_scsi_error(struct Scsi_Host *host);
extern void ata_port_wait_eh(struct ata_port *ap);
extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc);
+extern void ata_hp_poll_worker(void *data);
+extern void ata_hp_poll_deactivate(struct ata_port *ap);
#endif /* __LIBATA_H__ */
diff --git a/include/linux/libata.h b/include/linux/libata.h
index d0a7ad5..4fc74c4 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -175,6 +175,7 @@ enum {
ATA_FLAG_SKIP_D2H_BSY = (1 << 12), /* can't wait for the first D2H
* Register FIS clearing BSY */
ATA_FLAG_DEBUGMSG = (1 << 13),
+ ATA_FLAG_HP_POLLING = (1 << 14), /* hotplug by polling */
/* The following flag belongs to ap->pflags but is kept in
* ap->flags because it's referenced in many LLDs and will be
@@ -192,6 +193,7 @@ enum {
ATA_PFLAG_LOADING = (1 << 4), /* boot/loading probe */
ATA_PFLAG_UNLOADING = (1 << 5), /* module is unloading */
ATA_PFLAG_SCSI_HOTPLUG = (1 << 6), /* SCSI hotplug scheduled */
+ ATA_PFLAG_HP_POLL = (1 << 7), /* hp-poll active */
ATA_PFLAG_FLUSH_PORT_TASK = (1 << 16), /* flush port task */
ATA_PFLAG_SUSPENDED = (1 << 17), /* port is suspended (power) */
@@ -401,6 +403,7 @@ struct ata_host {
unsigned long flags;
int simplex_claimed; /* Keep seperate in case we
ever need to do this locked */
+ struct work_struct hp_poll_task;
struct ata_port *ports[0];
};
@@ -581,6 +584,7 @@ struct ata_port {
pm_message_t pm_mesg;
int *pm_result;
+ void *hp_poll_data;
void *private_data;
u8 sector_buf[ATA_SECT_SIZE]; /* owned by EH */
@@ -628,6 +632,10 @@ struct ata_port_operations {
void (*error_handler) (struct ata_port *ap);
void (*post_internal_cmd) (struct ata_queued_cmd *qc);
+ void (*hp_poll_activate) (struct ata_port *ap);
+ void (*hp_poll_deactivate) (struct ata_port *ap);
+ int (*hp_poll) (struct ata_port *ap);
+
irq_handler_t irq_handler;
void (*irq_clear) (struct ata_port *);
@@ -702,6 +710,8 @@ extern int ata_std_prereset(struct ata_p
extern int ata_std_softreset(struct ata_port *ap, unsigned int *classes);
extern int sata_std_hardreset(struct ata_port *ap, unsigned int *class);
extern void ata_std_postreset(struct ata_port *ap, unsigned int *classes);
+extern void sata_std_hp_poll_activate(struct ata_port *ap);
+extern int sata_std_hp_poll(struct ata_port *ap);
extern int ata_dev_revalidate(struct ata_device *dev, int post_reset);
extern void ata_port_disable(struct ata_port *);
extern void ata_std_ports(struct ata_ioports *ioaddr);
--
1.4.2.3
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 1/3] libata: implement hotplug by polling
2006-07-17 7:00 [RFT][PATCHSET] hotplug polling, take 3 Tejun Heo
@ 2006-07-17 7:00 ` Tejun Heo
2006-07-19 20:57 ` Jeff Garzik
0 siblings, 1 reply; 15+ messages in thread
From: Tejun Heo @ 2006-07-17 7:00 UTC (permalink / raw)
To: jgarzik, alan, lkml, axboe, forrest.zhao, linux-ide; +Cc: Tejun Heo
This patch implements hotplug by polling - hp-poll. It is used for
* hotplug event detection on controllers which don't provide PHY
status changed interrupt.
* hotplug event detection on disabled ports after reset failure.
libata used to leave such ports in frozen state to protect the
system from malfunctioning controller/device. With hp-poll, hotplug
events no such ports are watched safely by polling, such that
removing/replacing the malfunctioning device triggers EH retry on
the port.
There are three port ops for hp-poll - hp_poll_activate, hp_poll,
hp_poll_deactivate. Only hp_poll is mandatory for hp-poll to work.
This patch also implements SATA standard polling callbacks which poll
SError.N/X bits - sata_std_hp_poll_activate() and sata_std_hp_poll().
By default, hp-poll is enabled only on disabled ports. If a LLD
doesn't support hotplug interrupts but can poll for hotplug events, it
should indicate it by setting ATA_FLAG_HP_POLLING which tells libata
to turn on hp-poll by default.
Putting port into any static powersave mode or setting
libata.hotplug_polling_interval to zero disables hp-poll.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/libata-core.c | 92 ++++++++++++++++++++++++++++
drivers/scsi/libata-eh.c | 143 +++++++++++++++++++++++++++++++++++++++++++-
drivers/scsi/libata.h | 5 ++
include/linux/libata.h | 10 +++
4 files changed, 246 insertions(+), 4 deletions(-)
5cc3fdf67263ca76fc930ab9400a26fb637b18ae
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 9890387..f1db8d4 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -3082,6 +3082,86 @@ void sata_std_set_powersave(struct ata_p
}
/**
+ * sata_std_hp_poll_activate - standard SATA hotplug polling activation
+ * @ap: the target ata_port
+ *
+ * Activate SATA std hotplug polling.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void sata_std_hp_poll_activate(struct ata_port *ap)
+{
+ u32 serror, mask;
+
+ /* watch .X and, if not in dynamic powersave, .N */
+ mask = SERR_DEV_XCHG;
+ if (ata_ps_dynamic(ap->ps_state) == ATA_PS_NONE)
+ mask |= SERR_PHYRDY_CHG;
+
+ sata_scr_read(ap, SCR_ERROR, &serror);
+ serror &= mask;
+ if (serror)
+ sata_scr_write(ap, SCR_ERROR, serror);
+
+ ap->hp_poll_data = 0;
+}
+
+/**
+ * sata_std_hp_poll - standard SATA hotplug polling callback
+ * @ap: the target ata_port
+ *
+ * Poll SError.N/X for hotplug event. Hotplug event is triggered
+ * only after PHY stays stable for at least one full polling
+ * interval after the first event detection.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * 1 if hotplug event has occurred, 0 otherwise.
+ */
+int sata_std_hp_poll(struct ata_port *ap)
+{
+ unsigned long state = (unsigned long)ap->hp_poll_data;
+ u32 serror, mask;
+ int rc = 0;
+
+ /* watch .X and, if not in dynamic powersave, .N */
+ mask = SERR_DEV_XCHG;
+ if (ata_ps_dynamic(ap->ps_state) == ATA_PS_NONE)
+ mask |= SERR_PHYRDY_CHG;
+
+ sata_scr_read(ap, SCR_ERROR, &serror);
+ serror &= mask;
+
+ switch (state) {
+ case 0:
+ if (serror) {
+ /* PHY status could be bouncing crazy due to
+ * faulty controller or device. Don't fire
+ * hotplug event till hotplug event stays
+ * quiescent for one full polling interval.
+ */
+ sata_scr_write(ap, SCR_ERROR, serror);
+ state = 1;
+ }
+ break;
+
+ case 1:
+ if (!serror)
+ rc = 1;
+ else
+ sata_scr_write(ap, SCR_ERROR, serror);
+ break;
+ }
+
+ ap->hp_poll_data = (void *)(unsigned long)state;
+
+ return rc;
+}
+
+/**
* ata_dev_same_device - Determine whether new ID matches configured device
* @dev: device to compare against
* @new_class: class of the new device
@@ -5798,6 +5878,7 @@ #endif
INIT_LIST_HEAD(&ap->eh_done_q);
init_waitqueue_head(&ap->eh_wait_q);
setup_timer(&ap->ps_timer, ata_ps_timer_worker, (unsigned long)ap);
+ INIT_LIST_HEAD(&ap->hp_poll_entry);
/* set cable type */
ap->cbl = ATA_CBL_NONE;
@@ -6119,6 +6200,9 @@ void ata_port_detach(struct ata_port *ap
list_del_init(&ap->all_ports_entry);
mutex_unlock(&ata_all_ports_mutex);
+ /* deactivate hotplug polling */
+ ata_hp_poll_deactivate(ap);
+
/* remove the associated SCSI host */
scsi_remove_host(ap->host);
}
@@ -6365,6 +6449,12 @@ static int __init ata_init(void)
static void __exit ata_exit(void)
{
+ /* hp_poll_list gotta be empty by now and thus hp_poll_work
+ * isn't rearming.
+ */
+ WARN_ON(!list_empty(&hp_poll_list));
+ cancel_delayed_work(&hp_poll_work);
+
destroy_workqueue(ata_wq);
destroy_workqueue(ata_aux_wq);
}
@@ -6504,6 +6594,8 @@ EXPORT_SYMBOL_GPL(sata_std_hips_timer_fn
EXPORT_SYMBOL_GPL(sata_do_set_powersave);
EXPORT_SYMBOL_GPL(sata_determine_hips_params);
EXPORT_SYMBOL_GPL(sata_std_set_powersave);
+EXPORT_SYMBOL_GPL(sata_std_hp_poll_activate);
+EXPORT_SYMBOL_GPL(sata_std_hp_poll);
EXPORT_SYMBOL_GPL(ata_dev_revalidate);
EXPORT_SYMBOL_GPL(ata_dev_classify);
EXPORT_SYMBOL_GPL(ata_dev_pair);
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index b19eaae..f9d9bb7 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -34,6 +34,8 @@
#include <linux/config.h>
#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_eh.h>
@@ -45,6 +47,17 @@ #include <linux/libata.h>
#include "libata.h"
+static unsigned long hotplug_polling_interval = 2000;
+module_param(hotplug_polling_interval, ulong, 0644);
+MODULE_PARM_DESC(hotplug_polling_interval, "Hotplug polling interval "
+ "(milliseconds, default 2000, 0 to disable)");
+
+static void ata_hp_poll_worker(void *data);
+
+static DEFINE_MUTEX(hp_poll_mutex);
+struct list_head hp_poll_list = LIST_HEAD_INIT(hp_poll_list);
+DECLARE_WORK(hp_poll_work, ata_hp_poll_worker, NULL);
+
static void __ata_port_freeze(struct ata_port *ap);
static void ata_eh_finish(struct ata_port *ap);
static void ata_eh_handle_port_suspend(struct ata_port *ap);
@@ -628,7 +641,10 @@ int ata_port_freeze(struct ata_port *ap)
* ata_eh_freeze_port - EH helper to freeze port
* @ap: ATA port to freeze
*
- * Freeze @ap.
+ * Freeze @ap. As the 'freeze' operation means 'shutdown event
+ * reporting', it is a perfect place to deactivate hp-poll. Note
+ * that ata_port_freeze() always invokes EH and eventually this
+ * function, so deactivating hp-poll in this function is enough.
*
* LOCKING:
* None.
@@ -640,6 +656,8 @@ void ata_eh_freeze_port(struct ata_port
if (!ap->ops->error_handler)
return;
+ ata_hp_poll_deactivate(ap);
+
spin_lock_irqsave(ap->lock, flags);
__ata_port_freeze(ap);
spin_unlock_irqrestore(ap->lock, flags);
@@ -649,7 +667,7 @@ void ata_eh_freeze_port(struct ata_port
* ata_port_thaw_port - EH helper to thaw port
* @ap: ATA port to thaw
*
- * Thaw frozen port @ap.
+ * Thaw frozen port @ap and activate hp-poll if necessary.
*
* LOCKING:
* None.
@@ -670,6 +688,9 @@ void ata_eh_thaw_port(struct ata_port *a
spin_unlock_irqrestore(ap->lock, flags);
+ if (ap->flags & ATA_FLAG_HP_POLLING)
+ ata_hp_poll_activate(ap);
+
DPRINTK("ata%u port thawed\n", ap->id);
}
@@ -1926,8 +1947,12 @@ static int ata_eh_set_powersave(struct a
/* At this point, we're in ATA_PS_NONE state and DIPS setting
* is unknown. Execute the requested PS state transition.
*/
- if (ps_state && set_ps)
- set_ps(ap, ps_state);
+ if (ps_state) {
+ if (set_ps)
+ set_ps(ap, ps_state);
+ if (ata_ps_static(ps_state))
+ ata_hp_poll_deactivate(ap);
+ }
for (i = 0; i < ATA_MAX_DEVICES; i++) {
struct ata_device *dev = &ap->device[i];
@@ -2172,6 +2197,9 @@ static int ata_eh_recover(struct ata_por
out:
if (rc) {
+ /* recovery failed, activate hp-poll */
+ ata_hp_poll_activate(ap);
+
for (i = 0; i < ATA_MAX_DEVICES; i++)
ata_dev_disable(&ap->device[i]);
}
@@ -2360,3 +2388,110 @@ static void ata_eh_handle_port_resume(st
}
spin_unlock_irqrestore(ap->lock, flags);
}
+
+static void ata_hp_poll_worker(void *data)
+{
+ unsigned long interval = msec_to_jiffies(hotplug_polling_interval);
+ unsigned long flags = 0; /* shut up, gcc */
+ struct ata_port *ap, *next;
+ spinlock_t *held_lock = NULL;
+
+ mutex_lock(&hp_poll_mutex);
+
+ list_for_each_entry_safe(ap, next, &hp_poll_list, hp_poll_entry) {
+ int rc;
+
+ /* Ports sharing a lock are registered sequentially.
+ * Cache locking.
+ */
+ if (ap->lock != held_lock) {
+ if (held_lock)
+ spin_unlock_irqrestore(held_lock, flags);
+ spin_lock_irqsave(ap->lock, flags);
+ held_lock = ap->lock;
+ }
+
+ /* Poll. Positive return value indicates hotplug
+ * event while negative indicates error condition.
+ */
+ rc = ap->ops->hp_poll(ap);
+ if (rc) {
+ if (rc > 0) {
+ ata_ehi_hotplugged(&ap->eh_info);
+ ata_port_freeze(ap);
+ }
+ list_del_init(&ap->hp_poll_entry);
+ }
+ }
+
+ if (held_lock)
+ spin_unlock_irqrestore(held_lock, flags);
+
+ if (!list_empty(&hp_poll_list) && interval)
+ schedule_delayed_work(&hp_poll_work, interval);
+
+ mutex_unlock(&hp_poll_mutex);
+}
+
+/**
+ * ata_hp_poll_activate - activate hotplug polling
+ * @ap: host port to activate hotplug polling for
+ *
+ * Activate hotplug probing for @ap.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_hp_poll_activate(struct ata_port *ap)
+{
+ unsigned long interval = msec_to_jiffies(hotplug_polling_interval);
+ struct list_head *pos;
+
+ if (!ap->ops->hp_poll || !hotplug_polling_interval ||
+ !list_empty(&ap->hp_poll_entry))
+ return;
+
+ if (ap->ops->hp_poll_activate)
+ ap->ops->hp_poll_activate(ap);
+
+ mutex_lock(&hp_poll_mutex);
+
+ if (list_empty(&hp_poll_list) && interval)
+ schedule_delayed_work(&hp_poll_work, interval);
+
+ /* group poll entries by lock to cache locking when polling */
+ list_for_each(pos, &hp_poll_list) {
+ struct ata_port *pos_ap =
+ list_entry(pos, struct ata_port, hp_poll_entry);
+
+ if (ap->lock == pos_ap->lock)
+ break;
+ }
+
+ list_add(&ap->hp_poll_entry, pos);
+ ap->hp_poll_data = 0;
+
+ mutex_unlock(&hp_poll_mutex);
+}
+
+/**
+ * ata_hp_poll_deactivate - deactivate hotplug polling
+ * @ap: host port to deactivate hotplug polling for
+ *
+ * Deactivate hotplug probing for @ap.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_hp_poll_deactivate(struct ata_port *ap)
+{
+ if (list_empty(&ap->hp_poll_entry))
+ return;
+
+ mutex_lock(&hp_poll_mutex);
+ list_del_init(&ap->hp_poll_entry);
+ mutex_unlock(&hp_poll_mutex);
+
+ if (ap->ops->hp_poll_deactivate)
+ ap->ops->hp_poll_deactivate(ap);
+}
diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h
index 9e44f56..bc71b3f 100644
--- a/drivers/scsi/libata.h
+++ b/drivers/scsi/libata.h
@@ -118,9 +118,14 @@ extern void ata_schedule_scsi_eh(struct
extern void ata_scsi_dev_rescan(void *data);
/* libata-eh.c */
+extern struct list_head hp_poll_list;
+extern struct work_struct hp_poll_work;
+
extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd);
extern void ata_scsi_error(struct Scsi_Host *host);
extern void ata_port_wait_eh(struct ata_port *ap);
extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc);
+extern void ata_hp_poll_activate(struct ata_port *ap);
+extern void ata_hp_poll_deactivate(struct ata_port *ap);
#endif /* __LIBATA_H__ */
diff --git a/include/linux/libata.h b/include/linux/libata.h
index b8bee5f..39d6186 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -164,6 +164,7 @@ enum {
ATA_FLAG_DEBUGMSG = (1 << 13),
ATA_FLAG_HIPS = (1 << 14), /* SATA host-initiated powersave */
ATA_FLAG_DIPS = (1 << 15), /* SATA dev-initiated powersave */
+ ATA_FLAG_HP_POLLING = (1 << 16), /* hotplug by polling */
/* The following flag belongs to ap->pflags but is kept in
* ap->flags because it's referenced in many LLDs and will be
@@ -586,6 +587,9 @@ struct ata_port {
struct list_head all_ports_entry;
+ struct list_head hp_poll_entry; /* hotplug poll list entry */
+ void *hp_poll_data;
+
void *private_data;
u8 sector_buf[ATA_SECT_SIZE]; /* owned by EH */
@@ -633,6 +637,10 @@ struct ata_port_operations {
void (*error_handler) (struct ata_port *ap);
void (*post_internal_cmd) (struct ata_queued_cmd *qc);
+ void (*hp_poll_activate) (struct ata_port *ap);
+ void (*hp_poll_deactivate) (struct ata_port *ap);
+ int (*hp_poll) (struct ata_port *ap);
+
irqreturn_t (*irq_handler)(int, void *, struct pt_regs *);
void (*irq_clear) (struct ata_port *);
@@ -702,6 +710,8 @@ extern int ata_std_prereset(struct ata_p
extern int ata_std_softreset(struct ata_port *ap, unsigned int *classes);
extern int sata_std_hardreset(struct ata_port *ap, unsigned int *class);
extern void ata_std_postreset(struct ata_port *ap, unsigned int *classes);
+extern void sata_std_hp_poll_activate(struct ata_port *ap);
+extern int sata_std_hp_poll(struct ata_port *ap);
extern unsigned long sata_do_hips_timer_fn(struct ata_port *ap, int seq,
u8 sctl_ipm, ata_update_sctl_spm_fn_t update_sctl_spm);
extern unsigned long sata_std_hips_timer_fn(struct ata_port *ap, int seq);
--
1.3.2
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH 1/3] libata: implement hotplug by polling
2006-07-17 7:00 ` [PATCH 1/3] libata: implement hotplug by polling Tejun Heo
@ 2006-07-19 20:57 ` Jeff Garzik
2006-07-24 7:06 ` Tejun Heo
0 siblings, 1 reply; 15+ messages in thread
From: Jeff Garzik @ 2006-07-19 20:57 UTC (permalink / raw)
To: Tejun Heo; +Cc: alan, lkml, axboe, forrest.zhao, linux-ide
Tejun Heo wrote:
> This patch implements hotplug by polling - hp-poll. It is used for
>
> * hotplug event detection on controllers which don't provide PHY
> status changed interrupt.
>
> * hotplug event detection on disabled ports after reset failure.
> libata used to leave such ports in frozen state to protect the
> system from malfunctioning controller/device. With hp-poll, hotplug
> events no such ports are watched safely by polling, such that
> removing/replacing the malfunctioning device triggers EH retry on
> the port.
>
> There are three port ops for hp-poll - hp_poll_activate, hp_poll,
> hp_poll_deactivate. Only hp_poll is mandatory for hp-poll to work.
> This patch also implements SATA standard polling callbacks which poll
> SError.N/X bits - sata_std_hp_poll_activate() and sata_std_hp_poll().
>
> By default, hp-poll is enabled only on disabled ports. If a LLD
> doesn't support hotplug interrupts but can poll for hotplug events, it
> should indicate it by setting ATA_FLAG_HP_POLLING which tells libata
> to turn on hp-poll by default.
>
> Putting port into any static powersave mode or setting
> libata.hotplug_polling_interval to zero disables hp-poll.
>
> Signed-off-by: Tejun Heo <htejun@gmail.com>
NAK, there is no reason why a global poll list is needed. Simply having
a struct workqueue in each schedule-able entity implicitly creates such
a list. But regardless, this is another example of adding
cross-controller synchronization, where none is needed.
If the worker fired per-host_set, then you could use an ata_port dynamic
flag to indicate the poll-active ports, for the ata_hp_poll_worker() loop.
If the worker fires per-port, then no loop or mutex is needed at all.
You could simply call the hp_poll hook.
> @@ -633,6 +637,10 @@ struct ata_port_operations {
> void (*error_handler) (struct ata_port *ap);
> void (*post_internal_cmd) (struct ata_queued_cmd *qc);
>
> + void (*hp_poll_activate) (struct ata_port *ap);
> + void (*hp_poll_deactivate) (struct ata_port *ap);
> + int (*hp_poll) (struct ata_port *ap);
> +
> irqreturn_t (*irq_handler)(int, void *, struct pt_regs *);
> void (*irq_clear) (struct ata_port *);
>
All new hooks require at least a one-two sentence description in the
DocBook docs, telling driver writers how to use them.
Jeff
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH 1/3] libata: implement hotplug by polling
2006-07-19 20:57 ` Jeff Garzik
@ 2006-07-24 7:06 ` Tejun Heo
0 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-07-24 7:06 UTC (permalink / raw)
To: Jeff Garzik; +Cc: alan, lkml, axboe, forrest.zhao, linux-ide
Jeff Garzik wrote:
> NAK, there is no reason why a global poll list is needed. Simply having
> a struct workqueue in each schedule-able entity implicitly creates such
> a list. But regardless, this is another example of adding
> cross-controller synchronization, where none is needed.
>
> If the worker fired per-host_set, then you could use an ata_port dynamic
> flag to indicate the poll-active ports, for the ata_hp_poll_worker() loop.
>
> If the worker fires per-port, then no loop or mutex is needed at all.
> You could simply call the hp_poll hook.
It was basically an optimization to reduce storage usage and spread all
polling operations at least by polling interval even when there are many
ports to poll. Cross-port synchronization aside, it's very simple too.
Hmm... I don't think it makes much difference either way though. I'll
convert to per-port workqueue + timer.
>> @@ -633,6 +637,10 @@ struct ata_port_operations {
>> void (*error_handler) (struct ata_port *ap);
>> void (*post_internal_cmd) (struct ata_queued_cmd *qc);
>>
>> + void (*hp_poll_activate) (struct ata_port *ap);
>> + void (*hp_poll_deactivate) (struct ata_port *ap);
>> + int (*hp_poll) (struct ata_port *ap);
>> +
>> irqreturn_t (*irq_handler)(int, void *, struct pt_regs *);
>> void (*irq_clear) (struct ata_port *);
>>
>
> All new hooks require at least a one-two sentence description in the
> DocBook docs, telling driver writers how to use them.
Will do.
--
tejun
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 1/3] libata: implement hotplug by polling
2006-07-05 6:06 [RFT][PATCHSET] hotplug polling, take 2 Tejun Heo
@ 2006-07-05 6:06 ` Tejun Heo
0 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-07-05 6:06 UTC (permalink / raw)
To: jgarzik, lkml, axboe, forrest.zhao, alan, linux-ide; +Cc: Tejun Heo
This patch implements hotplug by polling - hp-poll. It is used for
* hotplug event detection on controllers which don't provide PHY
status changed interrupt.
* hotplug event detection on disabled ports after reset failure.
libata used to leave such ports in frozen state to protect the
system from malfunctioning controller/device. With hp-poll, hotplug
events no such ports are watched safely by polling, such that
removing/replacing the malfunctioning device triggers EH retry on
the port.
There are three port ops for hp-poll - hp_poll_activate, hp_poll,
hp_poll_deactivate. Only hp_poll is mandatory for hp-poll to work.
This patch also implements SATA standard polling callbacks which poll
SError.N/X bits - sata_std_hp_poll_activate() and sata_std_hp_poll().
By default, hp-poll is enabled only on disabled ports. If a LLD
doesn't support hotplug interrupts but can poll for hotplug events, it
should indicate it by setting ATA_FLAG_HP_POLLING which tells libata
to turn on hp-poll by default.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/libata-core.c | 82 +++++++++++++++++++++++++++
drivers/scsi/libata-eh.c | 134 +++++++++++++++++++++++++++++++++++++++++++-
drivers/scsi/libata.h | 5 ++
include/linux/libata.h | 10 +++
4 files changed, 229 insertions(+), 2 deletions(-)
fb6848884560d691fc69c6994f490d0918712eae
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 46ae4bc..3d4ea35 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -2840,6 +2840,76 @@ void ata_std_postreset(struct ata_port *
}
/**
+ * sata_std_hp_poll_activate - standard SATA hotplug polling activation
+ * @ap: the target ata_port
+ *
+ * Activate SATA std hotplug polling.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void sata_std_hp_poll_activate(struct ata_port *ap)
+{
+ u32 serror;
+
+ sata_scr_read(ap, SCR_ERROR, &serror);
+ serror &= SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+ if (serror)
+ sata_scr_write(ap, SCR_ERROR, serror);
+
+ ap->hp_poll_data = 0;
+}
+
+/**
+ * sata_std_hp_poll - standard SATA hotplug polling callback
+ * @ap: the target ata_port
+ *
+ * Poll SError.N/X for hotplug event. Hotplug event is triggered
+ * only after PHY stays stable for at least one full polling
+ * interval after the first event detection.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * 1 if hotplug event has occurred, 0 otherwise.
+ */
+int sata_std_hp_poll(struct ata_port *ap)
+{
+ unsigned long state = (unsigned long)ap->hp_poll_data;
+ u32 serror;
+ int rc = 0;
+
+ sata_scr_read(ap, SCR_ERROR, &serror);
+ serror &= SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+
+ switch (state) {
+ case 0:
+ if (serror) {
+ /* PHY status could be bouncing crazy due to
+ * faulty controller or device. Don't fire
+ * hotplug event till hotplug event stays
+ * quiescent for one full polling interval.
+ */
+ sata_scr_write(ap, SCR_ERROR, serror);
+ state = 1;
+ }
+ break;
+
+ case 1:
+ if (!serror)
+ rc = 1;
+ else
+ sata_scr_write(ap, SCR_ERROR, serror);
+ break;
+ }
+
+ ap->hp_poll_data = (void *)(unsigned long)state;
+
+ return rc;
+}
+
+/**
* ata_dev_same_device - Determine whether new ID matches configured device
* @dev: device to compare against
* @new_class: class of the new device
@@ -5290,6 +5360,7 @@ #endif
INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan, ap);
INIT_LIST_HEAD(&ap->eh_done_q);
init_waitqueue_head(&ap->eh_wait_q);
+ INIT_LIST_HEAD(&ap->hp_poll_entry);
/* set cable type */
ap->cbl = ATA_CBL_NONE;
@@ -5586,6 +5657,9 @@ void ata_port_detach(struct ata_port *ap
cancel_delayed_work(&ap->hotplug_task);
flush_workqueue(ata_aux_wq);
+ /* deactivate hotplug polling */
+ ata_hp_poll_deactivate(ap);
+
/* remove the associated SCSI host */
scsi_remove_host(ap->host);
}
@@ -5832,6 +5906,12 @@ static int __init ata_init(void)
static void __exit ata_exit(void)
{
+ /* hp_poll_list gotta be empty by now and thus hp_poll_work
+ * isn't rearming.
+ */
+ WARN_ON(!list_empty(&hp_poll_list));
+ cancel_delayed_work(&hp_poll_work);
+
destroy_workqueue(ata_wq);
destroy_workqueue(ata_aux_wq);
}
@@ -5966,6 +6046,8 @@ EXPORT_SYMBOL_GPL(ata_std_prereset);
EXPORT_SYMBOL_GPL(ata_std_softreset);
EXPORT_SYMBOL_GPL(sata_std_hardreset);
EXPORT_SYMBOL_GPL(ata_std_postreset);
+EXPORT_SYMBOL_GPL(sata_std_hp_poll_activate);
+EXPORT_SYMBOL_GPL(sata_std_hp_poll);
EXPORT_SYMBOL_GPL(ata_dev_revalidate);
EXPORT_SYMBOL_GPL(ata_dev_classify);
EXPORT_SYMBOL_GPL(ata_dev_pair);
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index 4b6aa30..bd89608 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -34,6 +34,8 @@
#include <linux/config.h>
#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_eh.h>
@@ -45,6 +47,17 @@ #include <linux/libata.h>
#include "libata.h"
+static unsigned long hotplug_polling_interval = 2000;
+module_param(hotplug_polling_interval, ulong, 0644);
+MODULE_PARM_DESC(hotplug_polling_interval,
+ "Hotplug polling interval in milliseconds (default 2000)");
+
+static void ata_hp_poll_worker(void *data);
+
+static DEFINE_MUTEX(hp_poll_mutex);
+struct list_head hp_poll_list = LIST_HEAD_INIT(hp_poll_list);
+DECLARE_WORK(hp_poll_work, ata_hp_poll_worker, NULL);
+
static void __ata_port_freeze(struct ata_port *ap);
static void ata_eh_finish(struct ata_port *ap);
static void ata_eh_handle_port_suspend(struct ata_port *ap);
@@ -617,7 +630,10 @@ int ata_port_freeze(struct ata_port *ap)
* ata_eh_freeze_port - EH helper to freeze port
* @ap: ATA port to freeze
*
- * Freeze @ap.
+ * Freeze @ap. As the 'freeze' operation means 'shutdown event
+ * reporting', it is a perfect place to deactivate hp-poll. Note
+ * that ata_port_freeze() always invokes EH and eventually this
+ * function, so deactivating hp-poll in this function is enough.
*
* LOCKING:
* None.
@@ -629,6 +645,8 @@ void ata_eh_freeze_port(struct ata_port
if (!ap->ops->error_handler)
return;
+ ata_hp_poll_deactivate(ap);
+
spin_lock_irqsave(ap->lock, flags);
__ata_port_freeze(ap);
spin_unlock_irqrestore(ap->lock, flags);
@@ -638,7 +656,7 @@ void ata_eh_freeze_port(struct ata_port
* ata_port_thaw_port - EH helper to thaw port
* @ap: ATA port to thaw
*
- * Thaw frozen port @ap.
+ * Thaw frozen port @ap and activate hp-poll if necessary.
*
* LOCKING:
* None.
@@ -659,6 +677,9 @@ void ata_eh_thaw_port(struct ata_port *a
spin_unlock_irqrestore(ap->lock, flags);
+ if (ap->flags & ATA_FLAG_HP_POLLING)
+ ata_hp_poll_activate(ap);
+
DPRINTK("ata%u port thawed\n", ap->id);
}
@@ -2037,6 +2058,9 @@ static int ata_eh_recover(struct ata_por
out:
if (rc) {
+ /* recovery failed, activate hp-poll */
+ ata_hp_poll_activate(ap);
+
for (i = 0; i < ATA_MAX_DEVICES; i++)
ata_dev_disable(&ap->device[i]);
}
@@ -2225,3 +2249,109 @@ static void ata_eh_handle_port_resume(st
}
spin_unlock_irqrestore(ap->lock, flags);
}
+
+static void ata_hp_poll_worker(void *data)
+{
+ unsigned long flags = 0; /* shut up, gcc */
+ struct ata_port *ap, *next;
+ spinlock_t *held_lock = NULL;
+
+ mutex_lock(&hp_poll_mutex);
+
+ list_for_each_entry_safe(ap, next, &hp_poll_list, hp_poll_entry) {
+ int rc;
+
+ /* Ports belonging to sharing a lock are registered
+ * sequentially. Cache locking.
+ */
+ if (ap->lock != held_lock) {
+ if (held_lock)
+ spin_unlock_irqrestore(held_lock, flags);
+ spin_lock_irqsave(ap->lock, flags);
+ held_lock = ap->lock;
+ }
+
+ /* Poll. Positive return value indicates hotplug
+ * event while negative indicates error condition.
+ */
+ rc = ap->ops->hp_poll(ap);
+ if (rc) {
+ if (rc > 0) {
+ ata_ehi_hotplugged(&ap->eh_info);
+ ata_port_freeze(ap);
+ }
+ list_del_init(&ap->hp_poll_entry);
+ }
+ }
+
+ if (held_lock)
+ spin_unlock_irqrestore(held_lock, flags);
+
+ if (!list_empty(&hp_poll_list))
+ schedule_delayed_work(&hp_poll_work,
+ hotplug_polling_interval * HZ / 1000);
+
+ mutex_unlock(&hp_poll_mutex);
+}
+
+/**
+ * ata_hp_poll_activate - activate hotplug polling
+ * @ap: host port to activate hotplug polling for
+ *
+ * Activate hotplug probing for @ap.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_hp_poll_activate(struct ata_port *ap)
+{
+ struct list_head *pos;
+
+ if (!ap->ops->hp_poll || !list_empty(&ap->hp_poll_entry))
+ return;
+
+ if (ap->ops->hp_poll_activate)
+ ap->ops->hp_poll_activate(ap);
+
+ mutex_lock(&hp_poll_mutex);
+
+ if (list_empty(&hp_poll_list))
+ schedule_delayed_work(&hp_poll_work,
+ hotplug_polling_interval * HZ / 1000);
+
+ /* group poll entries by lock to cache locking when polling */
+ list_for_each(pos, &hp_poll_list) {
+ struct ata_port *pos_ap =
+ list_entry(pos, struct ata_port, hp_poll_entry);
+
+ if (ap->lock == pos_ap->lock)
+ break;
+ }
+
+ list_add(&ap->hp_poll_entry, pos);
+ ap->hp_poll_data = 0;
+
+ mutex_unlock(&hp_poll_mutex);
+}
+
+/**
+ * ata_hp_poll_deactivate - deactivate hotplug polling
+ * @ap: host port to deactivate hotplug polling for
+ *
+ * Deactivate hotplug probing for @ap.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_hp_poll_deactivate(struct ata_port *ap)
+{
+ if (list_empty(&ap->hp_poll_entry))
+ return;
+
+ mutex_lock(&hp_poll_mutex);
+ list_del_init(&ap->hp_poll_entry);
+ mutex_unlock(&hp_poll_mutex);
+
+ if (ap->ops->hp_poll_deactivate)
+ ap->ops->hp_poll_deactivate(ap);
+}
diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h
index c325679..3171dde 100644
--- a/drivers/scsi/libata.h
+++ b/drivers/scsi/libata.h
@@ -109,9 +109,14 @@ extern void ata_schedule_scsi_eh(struct
extern void ata_scsi_dev_rescan(void *data);
/* libata-eh.c */
+extern struct list_head hp_poll_list;
+extern struct work_struct hp_poll_work;
+
extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd);
extern void ata_scsi_error(struct Scsi_Host *host);
extern void ata_port_wait_eh(struct ata_port *ap);
extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc);
+extern void ata_hp_poll_activate(struct ata_port *ap);
+extern void ata_hp_poll_deactivate(struct ata_port *ap);
#endif /* __LIBATA_H__ */
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 6cc497a..1609309 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -162,6 +162,7 @@ enum {
ATA_FLAG_SKIP_D2H_BSY = (1 << 12), /* can't wait for the first D2H
* Register FIS clearing BSY */
ATA_FLAG_DEBUGMSG = (1 << 13),
+ ATA_FLAG_HP_POLLING = (1 << 14), /* hotplug by polling */
/* The following flag belongs to ap->pflags but is kept in
* ap->flags because it's referenced in many LLDs and will be
@@ -553,6 +554,9 @@ struct ata_port {
pm_message_t pm_mesg;
int *pm_result;
+ struct list_head hp_poll_entry; /* hotplug poll list entry */
+ void *hp_poll_data;
+
void *private_data;
u8 sector_buf[ATA_SECT_SIZE]; /* owned by EH */
@@ -600,6 +604,10 @@ struct ata_port_operations {
void (*error_handler) (struct ata_port *ap);
void (*post_internal_cmd) (struct ata_queued_cmd *qc);
+ void (*hp_poll_activate) (struct ata_port *ap);
+ void (*hp_poll_deactivate) (struct ata_port *ap);
+ int (*hp_poll) (struct ata_port *ap);
+
irqreturn_t (*irq_handler)(int, void *, struct pt_regs *);
void (*irq_clear) (struct ata_port *);
@@ -667,6 +675,8 @@ extern int ata_std_prereset(struct ata_p
extern int ata_std_softreset(struct ata_port *ap, unsigned int *classes);
extern int sata_std_hardreset(struct ata_port *ap, unsigned int *class);
extern void ata_std_postreset(struct ata_port *ap, unsigned int *classes);
+extern void sata_std_hp_poll_activate(struct ata_port *ap);
+extern int sata_std_hp_poll(struct ata_port *ap);
extern int ata_dev_revalidate(struct ata_device *dev, int post_reset);
extern void ata_port_disable(struct ata_port *);
extern void ata_std_ports(struct ata_ioports *ioaddr);
--
1.3.2
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 1/3] libata: implement hotplug by polling
2006-07-04 14:16 [RFT][PATCHSET] hotplug polling Tejun Heo
@ 2006-07-04 14:16 ` Tejun Heo
0 siblings, 0 replies; 15+ messages in thread
From: Tejun Heo @ 2006-07-04 14:16 UTC (permalink / raw)
To: jgarzik, lkml, axboe, forrest.zhao, alan, linux-ide; +Cc: Tejun Heo
This patch implements hotplug by polling - hp-poll. It is used for
* hotplug event detection on controllers which don't provide PHY
status changed interrupt.
* hotplug event detection on disabled ports after reset failure.
libata used to leave such ports in frozen state to protect the
system from malfunctioning controller/device. With hp-poll, hotplug
events no such ports are watched safely by polling, such that
removing/replacing the malfunctioning device triggers EH retry on
the port.
There are three port ops for hp-poll - hp_poll_activate, hp_poll,
hp_poll_deactivate. Only hp_poll is mandatory for hp-poll to work.
This patch also implements SATA standard polling callbacks which poll
SError.N/X bits - sata_std_hp_poll_activate(), sata_std_hp_poll(),
sata_std_hp_poll_port_excl() and sata_std_hp_poll_hostset_excl().
By default, hp-poll is enabled only on disabled ports. If a LLD
doesn't support hotplug interrupts but can poll for hotplug events, it
should indicate it by setting ATA_FLAG_HP_POLLING which tells libata
to turn on hp-poll by default.
Signed-off-by: Tejun Heo <htejun@gmail.com>
---
drivers/scsi/libata-core.c | 134 ++++++++++++++++++++++++++++++++++++++++++++
drivers/scsi/libata-eh.c | 134 +++++++++++++++++++++++++++++++++++++++++++-
drivers/scsi/libata.h | 5 ++
include/linux/libata.h | 12 ++++
4 files changed, 283 insertions(+), 2 deletions(-)
7af09db1d75b71f1e45ec9ea1163edab03af56c9
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 46ae4bc..8ed0c37 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -2840,6 +2840,126 @@ void ata_std_postreset(struct ata_port *
}
/**
+ * sata_std_hp_poll_activate - standard SATA hotplug polling activation
+ * @ap: the target ata_port
+ *
+ * Activate SATA std hotplug polling.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void sata_std_hp_poll_activate(struct ata_port *ap)
+{
+ u32 serror;
+
+ sata_scr_read(ap, SCR_ERROR, &serror);
+ serror &= SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+ if (serror)
+ sata_scr_write(ap, SCR_ERROR, serror);
+
+ ap->hp_poll_data = 0;
+}
+
+/**
+ * sata_std_hp_poll - standard SATA hotplug polling callback
+ * @ap: the target ata_port
+ *
+ * Poll SError.N/X for hotplug event. Hotplug event is triggered
+ * only after PHY stays stable for at least one full polling
+ * interval after the first event detection.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * 1 if hotplug event has occurred, 0 otherwise.
+ */
+int sata_std_hp_poll(struct ata_port *ap)
+{
+ unsigned long state = (unsigned long)ap->hp_poll_data;
+ u32 serror;
+ int rc = 0;
+
+ sata_scr_read(ap, SCR_ERROR, &serror);
+ serror &= SERR_PHYRDY_CHG | SERR_DEV_XCHG;
+
+ switch (state) {
+ case 0:
+ if (serror) {
+ /* PHY status could be bouncing crazy due to
+ * faulty controller or device. Don't fire
+ * hotplug event till hotplug event stays
+ * quiescent for one full polling interval.
+ */
+ sata_scr_write(ap, SCR_ERROR, serror);
+ state = 1;
+ }
+ break;
+
+ case 1:
+ if (!serror)
+ rc = 1;
+ else
+ sata_scr_write(ap, SCR_ERROR, serror);
+ break;
+ }
+
+ ap->hp_poll_data = (void *)(unsigned long)state;
+
+ return rc;
+}
+
+/**
+ * sata_std_hp_poll_port_excl - sata_std_hp_poll with port exclusion
+ * @ap: the target ata_port
+ *
+ * SATA standard hp_poll callback for controllers which don't
+ * allow access to SCR registers while the port is active.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * 1 if hotplug event has occurred, 0 otherwise.
+ */
+int sata_std_hp_poll_port_excl(struct ata_port *ap)
+{
+ if (ap->qc_active)
+ return 0;
+
+ return sata_std_hp_poll(ap);
+}
+
+/**
+ * sata_std_hp_poll_hostset_excl - sata_std_hp_poll with host_set exclusion
+ * @ap: the target ata_port
+ *
+ * SATA standard hp_poll callback for controllers which don't
+ * allow access to SCR registers while any port in the host_set
+ * is active. How dumb can you get? Surprise, VIA did it.
+ *
+ * LOCKING:
+ * spin_lock_irqsave(host_set lock)
+ *
+ * RETURNS:
+ * 1 if hotplug event has occurred, 0 otherwise.
+ */
+int sata_std_hp_poll_hostset_excl(struct ata_port *ap)
+{
+ struct ata_host_set *host_set = ap->host_set;
+ int i;
+
+ for (i = 0; i < host_set->n_ports; i++) {
+ struct ata_port *t_ap = host_set->ports[i];
+
+ if (t_ap->qc_active)
+ return 0;
+ }
+
+ return sata_std_hp_poll(ap);
+}
+
+/**
* ata_dev_same_device - Determine whether new ID matches configured device
* @dev: device to compare against
* @new_class: class of the new device
@@ -5290,6 +5410,7 @@ #endif
INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan, ap);
INIT_LIST_HEAD(&ap->eh_done_q);
init_waitqueue_head(&ap->eh_wait_q);
+ INIT_LIST_HEAD(&ap->hp_poll_entry);
/* set cable type */
ap->cbl = ATA_CBL_NONE;
@@ -5586,6 +5707,9 @@ void ata_port_detach(struct ata_port *ap
cancel_delayed_work(&ap->hotplug_task);
flush_workqueue(ata_aux_wq);
+ /* deactivate hotplug polling */
+ ata_hp_poll_deactivate(ap);
+
/* remove the associated SCSI host */
scsi_remove_host(ap->host);
}
@@ -5832,6 +5956,12 @@ static int __init ata_init(void)
static void __exit ata_exit(void)
{
+ /* hp_poll_list gotta be empty by now and thus hp_poll_work
+ * isn't rearming.
+ */
+ WARN_ON(!list_empty(&hp_poll_list));
+ cancel_delayed_work(&hp_poll_work);
+
destroy_workqueue(ata_wq);
destroy_workqueue(ata_aux_wq);
}
@@ -5966,6 +6096,10 @@ EXPORT_SYMBOL_GPL(ata_std_prereset);
EXPORT_SYMBOL_GPL(ata_std_softreset);
EXPORT_SYMBOL_GPL(sata_std_hardreset);
EXPORT_SYMBOL_GPL(ata_std_postreset);
+EXPORT_SYMBOL_GPL(sata_std_hp_poll_activate);
+EXPORT_SYMBOL_GPL(sata_std_hp_poll);
+EXPORT_SYMBOL_GPL(sata_std_hp_poll_port_excl);
+EXPORT_SYMBOL_GPL(sata_std_hp_poll_hostset_excl);
EXPORT_SYMBOL_GPL(ata_dev_revalidate);
EXPORT_SYMBOL_GPL(ata_dev_classify);
EXPORT_SYMBOL_GPL(ata_dev_pair);
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index 4b6aa30..bd89608 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -34,6 +34,8 @@
#include <linux/config.h>
#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_eh.h>
@@ -45,6 +47,17 @@ #include <linux/libata.h>
#include "libata.h"
+static unsigned long hotplug_polling_interval = 2000;
+module_param(hotplug_polling_interval, ulong, 0644);
+MODULE_PARM_DESC(hotplug_polling_interval,
+ "Hotplug polling interval in milliseconds (default 2000)");
+
+static void ata_hp_poll_worker(void *data);
+
+static DEFINE_MUTEX(hp_poll_mutex);
+struct list_head hp_poll_list = LIST_HEAD_INIT(hp_poll_list);
+DECLARE_WORK(hp_poll_work, ata_hp_poll_worker, NULL);
+
static void __ata_port_freeze(struct ata_port *ap);
static void ata_eh_finish(struct ata_port *ap);
static void ata_eh_handle_port_suspend(struct ata_port *ap);
@@ -617,7 +630,10 @@ int ata_port_freeze(struct ata_port *ap)
* ata_eh_freeze_port - EH helper to freeze port
* @ap: ATA port to freeze
*
- * Freeze @ap.
+ * Freeze @ap. As the 'freeze' operation means 'shutdown event
+ * reporting', it is a perfect place to deactivate hp-poll. Note
+ * that ata_port_freeze() always invokes EH and eventually this
+ * function, so deactivating hp-poll in this function is enough.
*
* LOCKING:
* None.
@@ -629,6 +645,8 @@ void ata_eh_freeze_port(struct ata_port
if (!ap->ops->error_handler)
return;
+ ata_hp_poll_deactivate(ap);
+
spin_lock_irqsave(ap->lock, flags);
__ata_port_freeze(ap);
spin_unlock_irqrestore(ap->lock, flags);
@@ -638,7 +656,7 @@ void ata_eh_freeze_port(struct ata_port
* ata_port_thaw_port - EH helper to thaw port
* @ap: ATA port to thaw
*
- * Thaw frozen port @ap.
+ * Thaw frozen port @ap and activate hp-poll if necessary.
*
* LOCKING:
* None.
@@ -659,6 +677,9 @@ void ata_eh_thaw_port(struct ata_port *a
spin_unlock_irqrestore(ap->lock, flags);
+ if (ap->flags & ATA_FLAG_HP_POLLING)
+ ata_hp_poll_activate(ap);
+
DPRINTK("ata%u port thawed\n", ap->id);
}
@@ -2037,6 +2058,9 @@ static int ata_eh_recover(struct ata_por
out:
if (rc) {
+ /* recovery failed, activate hp-poll */
+ ata_hp_poll_activate(ap);
+
for (i = 0; i < ATA_MAX_DEVICES; i++)
ata_dev_disable(&ap->device[i]);
}
@@ -2225,3 +2249,109 @@ static void ata_eh_handle_port_resume(st
}
spin_unlock_irqrestore(ap->lock, flags);
}
+
+static void ata_hp_poll_worker(void *data)
+{
+ unsigned long flags = 0; /* shut up, gcc */
+ struct ata_port *ap, *next;
+ spinlock_t *held_lock = NULL;
+
+ mutex_lock(&hp_poll_mutex);
+
+ list_for_each_entry_safe(ap, next, &hp_poll_list, hp_poll_entry) {
+ int rc;
+
+ /* Ports belonging to sharing a lock are registered
+ * sequentially. Cache locking.
+ */
+ if (ap->lock != held_lock) {
+ if (held_lock)
+ spin_unlock_irqrestore(held_lock, flags);
+ spin_lock_irqsave(ap->lock, flags);
+ held_lock = ap->lock;
+ }
+
+ /* Poll. Positive return value indicates hotplug
+ * event while negative indicates error condition.
+ */
+ rc = ap->ops->hp_poll(ap);
+ if (rc) {
+ if (rc > 0) {
+ ata_ehi_hotplugged(&ap->eh_info);
+ ata_port_freeze(ap);
+ }
+ list_del_init(&ap->hp_poll_entry);
+ }
+ }
+
+ if (held_lock)
+ spin_unlock_irqrestore(held_lock, flags);
+
+ if (!list_empty(&hp_poll_list))
+ schedule_delayed_work(&hp_poll_work,
+ hotplug_polling_interval * HZ / 1000);
+
+ mutex_unlock(&hp_poll_mutex);
+}
+
+/**
+ * ata_hp_poll_activate - activate hotplug polling
+ * @ap: host port to activate hotplug polling for
+ *
+ * Activate hotplug probing for @ap.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_hp_poll_activate(struct ata_port *ap)
+{
+ struct list_head *pos;
+
+ if (!ap->ops->hp_poll || !list_empty(&ap->hp_poll_entry))
+ return;
+
+ if (ap->ops->hp_poll_activate)
+ ap->ops->hp_poll_activate(ap);
+
+ mutex_lock(&hp_poll_mutex);
+
+ if (list_empty(&hp_poll_list))
+ schedule_delayed_work(&hp_poll_work,
+ hotplug_polling_interval * HZ / 1000);
+
+ /* group poll entries by lock to cache locking when polling */
+ list_for_each(pos, &hp_poll_list) {
+ struct ata_port *pos_ap =
+ list_entry(pos, struct ata_port, hp_poll_entry);
+
+ if (ap->lock == pos_ap->lock)
+ break;
+ }
+
+ list_add(&ap->hp_poll_entry, pos);
+ ap->hp_poll_data = 0;
+
+ mutex_unlock(&hp_poll_mutex);
+}
+
+/**
+ * ata_hp_poll_deactivate - deactivate hotplug polling
+ * @ap: host port to deactivate hotplug polling for
+ *
+ * Deactivate hotplug probing for @ap.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_hp_poll_deactivate(struct ata_port *ap)
+{
+ if (list_empty(&ap->hp_poll_entry))
+ return;
+
+ mutex_lock(&hp_poll_mutex);
+ list_del_init(&ap->hp_poll_entry);
+ mutex_unlock(&hp_poll_mutex);
+
+ if (ap->ops->hp_poll_deactivate)
+ ap->ops->hp_poll_deactivate(ap);
+}
diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h
index c325679..3171dde 100644
--- a/drivers/scsi/libata.h
+++ b/drivers/scsi/libata.h
@@ -109,9 +109,14 @@ extern void ata_schedule_scsi_eh(struct
extern void ata_scsi_dev_rescan(void *data);
/* libata-eh.c */
+extern struct list_head hp_poll_list;
+extern struct work_struct hp_poll_work;
+
extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd);
extern void ata_scsi_error(struct Scsi_Host *host);
extern void ata_port_wait_eh(struct ata_port *ap);
extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc);
+extern void ata_hp_poll_activate(struct ata_port *ap);
+extern void ata_hp_poll_deactivate(struct ata_port *ap);
#endif /* __LIBATA_H__ */
diff --git a/include/linux/libata.h b/include/linux/libata.h
index 6cc497a..2ceca5f 100644
--- a/include/linux/libata.h
+++ b/include/linux/libata.h
@@ -162,6 +162,7 @@ enum {
ATA_FLAG_SKIP_D2H_BSY = (1 << 12), /* can't wait for the first D2H
* Register FIS clearing BSY */
ATA_FLAG_DEBUGMSG = (1 << 13),
+ ATA_FLAG_HP_POLLING = (1 << 14), /* hotplug by polling */
/* The following flag belongs to ap->pflags but is kept in
* ap->flags because it's referenced in many LLDs and will be
@@ -553,6 +554,9 @@ struct ata_port {
pm_message_t pm_mesg;
int *pm_result;
+ struct list_head hp_poll_entry; /* hotplug poll list entry */
+ void *hp_poll_data;
+
void *private_data;
u8 sector_buf[ATA_SECT_SIZE]; /* owned by EH */
@@ -600,6 +604,10 @@ struct ata_port_operations {
void (*error_handler) (struct ata_port *ap);
void (*post_internal_cmd) (struct ata_queued_cmd *qc);
+ void (*hp_poll_activate) (struct ata_port *ap);
+ void (*hp_poll_deactivate) (struct ata_port *ap);
+ int (*hp_poll) (struct ata_port *ap);
+
irqreturn_t (*irq_handler)(int, void *, struct pt_regs *);
void (*irq_clear) (struct ata_port *);
@@ -667,6 +675,10 @@ extern int ata_std_prereset(struct ata_p
extern int ata_std_softreset(struct ata_port *ap, unsigned int *classes);
extern int sata_std_hardreset(struct ata_port *ap, unsigned int *class);
extern void ata_std_postreset(struct ata_port *ap, unsigned int *classes);
+extern void sata_std_hp_poll_activate(struct ata_port *ap);
+extern int sata_std_hp_poll(struct ata_port *ap);
+extern int sata_std_hp_poll_port_excl(struct ata_port *ap);
+extern int sata_std_hp_poll_hostset_excl(struct ata_port *ap);
extern int ata_dev_revalidate(struct ata_device *dev, int post_reset);
extern void ata_port_disable(struct ata_port *);
extern void ata_std_ports(struct ata_ioports *ioaddr);
--
1.3.2
^ permalink raw reply related [flat|nested] 15+ messages in thread