linux-ide.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [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; 12+ 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] 12+ 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; 12+ 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] 12+ messages in thread

* [RFT][PATCHSET] hotplug polling, take 3
@ 2006-07-17  7:00 Tejun Heo
  2006-07-17  7:00 ` [PATCH 1/3] libata: implement hotplug by polling Tejun Heo
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Tejun Heo @ 2006-07-17  7:00 UTC (permalink / raw)
  To: jgarzik, alan, lkml, axboe, forrest.zhao, linux-ide, htejun

Hello, all.

This is the third take of hotplug polling patchset.  As the name
implies, this patchset implements hotplug by polling.  Changes from
the last take[L] are.

* updated to accomodate earlier fixes & updates

* hp-poll is automatically disabled if the port enters any of static
  powersave mode.

* hp-poll can be manually disabled by setting
  libata.hotplug_polling_interval to zero.

This patchset is also available in the following git tree.

  http://htj.dyndns.org/git/?p=libata-tj.git;a=shortlog;h=hp-poll
  git://htj.dyndns.org/libata-tj hp-poll

Thanks.

--
tejun

[L] http://article.gmane.org/gmane.linux.ide/11862
[1] http://article.gmane.org/gmane.linux.ide/11966
[2] http://article.gmane.org/gmane.linux.ide/12001
[3] http://article.gmane.org/gmane.linux.ide/12002
[4] http://article.gmane.org/gmane.linux.ide/11870
[5] http://article.gmane.org/gmane.linux.ide/12138



^ permalink raw reply	[flat|nested] 12+ 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
  2006-07-17  7:00 ` [PATCH 3/3] libata: add hp-poll support to controllers without hotplug interrupts Tejun Heo
  2006-07-17  7:00 ` [PATCH 2/3] libata: add hp-poll support to controllers with hotplug interrutps Tejun Heo
  2 siblings, 1 reply; 12+ 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] 12+ messages in thread

* [PATCH 3/3] libata: add hp-poll support to controllers without hotplug interrupts
  2006-07-17  7:00 [RFT][PATCHSET] hotplug polling, take 3 Tejun Heo
  2006-07-17  7:00 ` [PATCH 1/3] libata: implement hotplug by polling Tejun Heo
@ 2006-07-17  7:00 ` Tejun Heo
  2006-07-19 20:57   ` Jeff Garzik
  2006-07-17  7:00 ` [PATCH 2/3] libata: add hp-poll support to controllers with hotplug interrutps Tejun Heo
  2 siblings, 1 reply; 12+ 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 adds hp-poll support to the following drivers.

* sata_nv (generic)
* sata_sis
* sata_svw
* sata_uli
* sata_via
* 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.

Signed-off-by: Tejun Heo <htejun@gmail.com>

---

 drivers/scsi/sata_nv.c  |    5 ++++-
 drivers/scsi/sata_sis.c |    5 ++++-
 drivers/scsi/sata_svw.c |    4 +++-
 drivers/scsi/sata_uli.c |    6 +++++-
 drivers/scsi/sata_vsc.c |    4 +++-
 5 files changed, 19 insertions(+), 5 deletions(-)

3a5e2866b5e69d113c1a7740969b9847ed41b545
diff --git a/drivers/scsi/sata_nv.c b/drivers/scsi/sata_nv.c
index cdf3b65..7bd9305 100644
--- a/drivers/scsi/sata_nv.c
+++ b/drivers/scsi/sata_nv.c
@@ -189,6 +189,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,
@@ -261,7 +263,8 @@ static struct ata_port_info nv_port_info
 	/* generic */
 	{
 		.sht		= &nv_sht,
-		.host_flags	= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
+		.host_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/scsi/sata_sis.c b/drivers/scsi/sata_sis.c
index ee6b5df..3b4bc5a 100644
--- a/drivers/scsi/sata_sis.c
+++ b/drivers/scsi/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,
-	.host_flags	= ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
+	.host_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/scsi/sata_svw.c b/drivers/scsi/sata_svw.c
index 7d08580..9a446ad 100644
--- a/drivers/scsi/sata_svw.c
+++ b/drivers/scsi/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->host_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/scsi/sata_uli.c b/drivers/scsi/sata_uli.c
index 33cdb48..d3ab9ed 100644
--- a/drivers/scsi/sata_uli.c
+++ b/drivers/scsi/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,
-	.host_flags     = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY,
+	.host_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/scsi/sata_vsc.c b/drivers/scsi/sata_vsc.c
index ad37871..d7da4c2 100644
--- a/drivers/scsi/sata_vsc.c
+++ b/drivers/scsi/sata_vsc.c
@@ -302,6 +302,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,
@@ -396,7 +398,7 @@ static int __devinit vsc_sata_init_one (
 
 	probe_ent->sht = &vsc_sata_sht;
 	probe_ent->host_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.3.2



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/3] libata: add hp-poll support to controllers with hotplug interrutps
  2006-07-17  7:00 [RFT][PATCHSET] hotplug polling, take 3 Tejun Heo
  2006-07-17  7:00 ` [PATCH 1/3] libata: implement hotplug by polling Tejun Heo
  2006-07-17  7:00 ` [PATCH 3/3] libata: add hp-poll support to controllers without hotplug interrupts Tejun Heo
@ 2006-07-17  7:00 ` Tejun Heo
  2 siblings, 0 replies; 12+ 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 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/scsi/ahci.c       |    3 +++
 drivers/scsi/sata_nv.c    |    4 ++++
 drivers/scsi/sata_sil.c   |    2 ++
 drivers/scsi/sata_sil24.c |    3 +++
 4 files changed, 12 insertions(+), 0 deletions(-)

a7fe2cefdb1abfed43df9e12c3f05d885638b07b
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index b5fe19a..9fc2e57 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -261,6 +261,9 @@ static const struct ata_port_operations 
 	.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,
+
 	.set_powersave		= ahci_set_powersave,
 
 	.port_start		= ahci_port_start,
diff --git a/drivers/scsi/sata_nv.c b/drivers/scsi/sata_nv.c
index 56da255..cdf3b65 100644
--- a/drivers/scsi/sata_nv.c
+++ b/drivers/scsi/sata_nv.c
@@ -216,6 +216,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,
@@ -243,6 +245,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/scsi/sata_sil.c b/drivers/scsi/sata_sil.c
index d0a8507..62deca4 100644
--- a/drivers/scsi/sata_sil.c
+++ b/drivers/scsi/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/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index 0b5c3f1..0c1f082 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -407,6 +407,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,
+
 	.set_powersave		= sil24_set_powersave,
 
 	.port_start		= sil24_port_start,
-- 
1.3.2



^ permalink raw reply related	[flat|nested] 12+ 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; 12+ 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] 12+ messages in thread

* Re: [PATCH 3/3] libata: add hp-poll support to controllers without hotplug interrupts
  2006-07-17  7:00 ` [PATCH 3/3] libata: add hp-poll support to controllers without hotplug interrupts Tejun Heo
@ 2006-07-19 20:57   ` Jeff Garzik
  0 siblings, 0 replies; 12+ 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

ACK patches 2-3


^ permalink raw reply	[flat|nested] 12+ 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; 12+ 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] 12+ 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; 12+ 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] 12+ 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 ` Tejun Heo
  0 siblings, 0 replies; 12+ 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] 12+ 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; 12+ 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] 12+ messages in thread

end of thread, other threads:[~2007-04-25  6:28 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-07-17  7:00 [RFT][PATCHSET] hotplug polling, take 3 Tejun Heo
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
2006-07-17  7:00 ` [PATCH 3/3] libata: add hp-poll support to controllers without hotplug interrupts Tejun Heo
2006-07-19 20:57   ` Jeff Garzik
2006-07-17  7:00 ` [PATCH 2/3] libata: add hp-poll support to controllers with hotplug interrutps Tejun Heo
  -- strict thread matches above, loose matches on Subject: below --
2007-04-25  6:25 [PATCH 0/3] hotplug polling, respin Robin H. Johnson
2007-04-25  6:28 ` [PATCH 1/3] libata: implement hotplug by polling Robin H. Johnson
2006-10-15 22:37 [PATCHSET] hotplug polling, take 5 Tejun Heo
2006-10-15 22:37 ` [PATCH 1/3] libata: implement hotplug by polling Tejun Heo
2006-10-10  5:36 [PATCHSET] hotplug polling, take 4 Tejun Heo
2006-10-10  5:36 ` [PATCH 1/3] libata: implement hotplug by polling Tejun Heo
2006-07-05  6:06 [RFT][PATCHSET] hotplug polling, take 2 Tejun Heo
2006-07-05  6:06 ` [PATCH 1/3] libata: implement hotplug by polling Tejun Heo
2006-07-04 14:16 [RFT][PATCHSET] hotplug polling Tejun Heo
2006-07-04 14:16 ` [PATCH 1/3] libata: implement hotplug by polling Tejun Heo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).