hotplug polling, take 3
PATCH 1/1 hotplug polling, take 3
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
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() 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.
---
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
drivers/scsi/libata-core.c +92 -0
@@ -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 @@ #endifINIT_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 *aplist_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_fnEXPORT_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);
drivers/scsi/libata-eh.c +139 -4
194 lines not on this page · raw message
drivers/scsi/libata.h +5 -0
@@ -118,9 +118,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 +10 -0
@@ -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_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 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);
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 | 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
drivers/scsi/ahci.c +3 -0
@@ -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,
drivers/scsi/sata_nv.c +4 -0
@@ -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,
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
@@ -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,
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_vsc.c | 4 +++- 5 files changed, 19 insertions(+), 5 deletions(-)
3a5e2866b5e69d113c1a7740969b9847ed41b545
drivers/scsi/sata_nv.c +4 -1
@@ -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,
drivers/scsi/sata_sis.c +4 -1
@@ -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_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
@@ -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;
drivers/scsi/sata_uli.c +5 -1
@@ -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_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_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
Jeff Garzik
NAKRe: [PATCH 1/3] libata: implement hotplug by polling
In reply to Tejun HeoQuoted 22 lines · 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. Onlyhp_pollis 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()andsata_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_POLLINGwhich tells libata to turn on hp-poll by default.
Quoted 4 lines
Putting port into any static powersave mode or setting libata.hotplug_polling_interval to zero disables hp-poll.
Signed-off-by Tejun Heo <[email protected]>
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.
include/linux/libata.h struct ata_port_operations { · line 646
Quoted 11 lines
include/linux/libata.h +4
-0@@ -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
Re: [PATCH 3/3] libata: add hp-poll support to controllers without hotplug interrupts
In reply to Tejun HeoACK patches 2-3
Re: [PATCH 1/3] libata: implement hotplug by polling
In reply to Jeff GarzikQuoted 5 lines · Jeff Garzik
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_portdynamic flag to indicate the poll-active ports, for theata_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_pollhook.
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.
Quoted 4 lines
Quoted 4 lines
include/linux/libata.h +0
-0@@ -633,6 +637,10 @@ struct ata_port_operations {void (*error_handler) (struct ata_port *ap);void (*post_internal_cmd) (struct ata_queued_cmd *qc);
Quoted 9 lines
include/linux/libata.h struct ata_port_operations { · line 646
Quoted 7 lines
include/linux/libata.h +4
-0+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.
Signature · 1 line
tejun