hotplug polling
PATCH 1/1 hotplug polling
Hello, all.
This is the first take of hotplug polling patchset. As the name implies, this patchset implements hotplug by polling.
libata core layer implements hp-poll infrastructure. It interacts with LLDs with three callbacks - hp_poll_activate(), hp_poll_deactivate() and hp_poll(). libata core layer is responsible for activating and deactivating at the right time, invoking the poll callback periodically while activated, and generate hotplug event when poll callback indicates something has happened.
hp-poll is used for the following cases.
* To detect hotplug events on frozen ports after reset fails such that replacing or replugging the failing device make libata automatically retry the port.
* To detect hotplug events on LLDs without hotplug interrupt.
The following drivers are converted to use hp-poll on failed ports.
* ahci
* sata_nv (nf2 and ck804 only) [untested]
* sata_sil
* sata_sil24
The following drivers are converted to use hp-poll for hotplug
* sata_nv (generic) [untested]
* sata_sis [untested]
* sata_svw [untested]
* sata_uli [untested]
* sata_via
* sata_vsc [untested]
There are three variants of sata_std_poll() - regular, port-exclusive and host_set-exclusive. port-exclusive skips polling if command is in progress to the port. host_set-exclusive skips polling if one or more commands are in progress to any port in the host_set. This is because some controllers lock up if SCR regs are accessed while command is in progress.
Well, the port-exclusive case is dumb but understandable; however, sata_via (vt8237) takes it to the next level and may lock up if any available port in the host_set is active when a SCR reg is accessed, so it needs host_set-exclusive version. It took me two days of boredom to figure out that things can be made THAT dumb. Their new AHCI controller also seems to require host_set-wide EH ops to recover from errors. Well, I guess they consider requiring host_set-wide ops a feature or something. :-( Note that this means that the current libata EH may lock up the whole machine on vt8237 if EH occurs at a bad time.
Currently, all 'untested' LLDs are using the regular version and some of them might need to use a dumber version. So, we need some testing here.
This patchset is against
upstream (2154cfa6ba560401d25f6cc083fe3fb996cbb571)
+ add-ap-pflags [1]
+ fix-ehc_i_action-setting-in-ata_eh_autopsy [2]
+ cosmetic-replace-ap_lock [3]
+ new PM patchset [4]
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
[1] http://article.gmane.org/gmane.linux.ide/11717
[2] http://article.gmane.org/gmane.linux.ide/11801
[3] http://article.gmane.org/gmane.linux.ide/11802
[4] http://article.gmane.org/gmane.linux.ide/11816
PATCH 1/3 libata: implement hotplug by polling
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.
---
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
drivers/scsi/libata-core.c +134 -0
@@ -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 @@ #endifINIT_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 *apcancel_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);
drivers/scsi/libata-eh.c +132 -2
179 lines not on this page · raw message
drivers/scsi/libata.h +5 -0
@@ -109,9 +109,14 @@ extern void ata_schedule_scsi_eh(structextern 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__ */
include/linux/libata.h +12 -0
@@ -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_pextern 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);
Signature · 1 line
1.3.2
PATCH 2/3 libata: add hp-poll support to controllers with hotplug interrutps
In reply to Tejun HeoThis 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.
---
drivers/scsi/ahci.c | 4 +++- drivers/scsi/sata_nv.c | 4 ++++ drivers/scsi/sata_sil.c | 2 ++ drivers/scsi/sata_sil24.c | 3 +++ 4 files changed, 12 insertions(+), 1 deletions(-)
15decf1370fe778e6df39cb8944cf5568e46b5b5
drivers/scsi/ahci.c +3 -1
@@ -253,10 +253,12 @@ static const struct ata_port_operations .freeze = ahci_freeze, .thaw = ahci_thaw, - .error_handler = ahci_error_handler, .post_internal_cmd = ahci_post_internal_cmd, + .hp_poll_activate = sata_std_hp_poll_activate, + .hp_poll = sata_std_hp_poll, + .port_start = ahci_port_start, .port_stop = ahci_port_stop, };
drivers/scsi/sata_nv.c +4 -0
@@ -217,6 +217,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,
@@ -244,6 +246,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,
drivers/scsi/sata_sil.c +2 -0
@@ -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,
drivers/scsi/sata_sil24.c +3 -0
@@ -405,6 +405,9 @@ static const struct ata_port_operations .error_handler = sil24_error_handler, .post_internal_cmd = sil24_post_internal_cmd, + .hp_poll_activate = sata_std_hp_poll_activate, + .hp_poll = sata_std_hp_poll, + .port_start = sil24_port_start, .port_stop = sil24_port_stop, .host_stop = sil24_host_stop,
Signature · 1 line
1.3.2
PATCH 3/3 libata: add hp-poll support to controllers without hotplug interrupts
In reply to Tejun HeoThis 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.
---
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_via.c | 9 +++++++-- drivers/scsi/sata_vsc.c | 4 +++- 6 files changed, 26 insertions(+), 7 deletions(-)
38dc5ac2f958a42537f07b763b1a9478602f2af0
drivers/scsi/sata_nv.c +4 -1
@@ -190,6 +190,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,
@@ -262,7 +264,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,
drivers/scsi/sata_sis.c +4 -1
@@ -118,6 +118,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,
@@ -129,7 +131,8 @@ static const struct ata_port_operationsstatic 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 ,
drivers/scsi/sata_svw.c +3 -1
@@ -325,6 +325,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,
@@ -425,7 +427,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;
drivers/scsi/sata_uli.c +5 -1
@@ -116,6 +116,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,
@@ -129,7 +132,8 @@ static const struct ata_port_operationsstatic 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,
drivers/scsi/sata_via.c +7 -2
@@ -130,6 +130,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_hostset_excl, + .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear,
@@ -143,7 +146,8 @@ static const struct ata_port_operationsstatic struct ata_port_info svia_port_info = { .sht = &svia_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 =0x07 , .udma_mask =0x7f ,
@@ -238,7 +242,8 @@ static struct ata_probe_ent *vt6421_initINIT_LIST_HEAD (&probe_ent->node); probe_ent->sht = &svia_sht; - probe_ent->host_flags =ATA_FLAG_SATA |ATA_FLAG_NO_LEGACY ; + probe_ent->host_flags =ATA_FLAG_SATA |ATA_FLAG_NO_LEGACY | +ATA_FLAG_HP_POLLING ; probe_ent->port_ops = &svia_sata_ops; probe_ent->n_ports =N_PORTS ; probe_ent->irq = pdev->irq;
drivers/scsi/sata_vsc.c +3 -1
@@ -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;
Signature · 1 line
1.3.2
Quoted 10 lines · Tejun Heo
Well, the port-exclusive case is dumb but understandable; however,
sata_via(vt8237) takes it to the next level and may lock up if any available port in thehost_setis active when a SCR reg is accessed, so it needs host_set-exclusive version. It took me two days of boredom to figure out that things can be made THAT dumb. Their new AHCI controller also seems to require host_set-wide EH ops to recover from errors. Well, I guess they consider requiring host_set-wide ops a feature or something. :-( Note that this means that the current libata EH may lock up the whole machine on vt8237 if EH occurs at a bad time.
Update: vt8237 failed to survive over-night test. It died even with host_set-wide exclusion. I'm dropping hp-poll support from sata_via.
Signature · 1 line
tejun