| From 213373cf974fe69e78ec894b07f45ae2f5a3a078 Mon Sep 17 00:00:00 2001 |
| From: Tejun Heo <tj@kernel.org> |
| Date: Tue, 20 Jul 2010 16:20:01 +0200 |
| Subject: ata_piix: fix locking around SIDPR access |
| |
| From: Tejun Heo <tj@kernel.org> |
| |
| commit 213373cf974fe69e78ec894b07f45ae2f5a3a078 upstream. |
| |
| SIDPR window registers are shared across ports and as each access is |
| done in two steps, accesses to different ports under EH may race. |
| This primarily is caused by incorrect host locking in EH context and |
| should be fixed by defining locking requirements for each EH operation |
| which can be used during EH and enforcing them but for now work around |
| the problem by adding a dedicated SIDPR lock and grabbing it for each |
| SIDPR access. |
| |
| Signed-off-by: Tejun Heo <tj@kernel.org> |
| Reported-by: Mark Knecht <markknecht@gmail.com> |
| Reported-by: Paul Check <paul@thechecks.ca> |
| Signed-off-by: Jeff Garzik <jgarzik@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| drivers/ata/ata_piix.c | 8 ++++++++ |
| 1 file changed, 8 insertions(+) |
| |
| --- a/drivers/ata/ata_piix.c |
| +++ b/drivers/ata/ata_piix.c |
| @@ -158,6 +158,7 @@ struct piix_map_db { |
| struct piix_host_priv { |
| const int *map; |
| u32 saved_iocfg; |
| + spinlock_t sidpr_lock; /* FIXME: remove once locking in EH is fixed */ |
| void __iomem *sidpr; |
| }; |
| |
| @@ -951,12 +952,15 @@ static int piix_sidpr_scr_read(struct at |
| unsigned int reg, u32 *val) |
| { |
| struct piix_host_priv *hpriv = link->ap->host->private_data; |
| + unsigned long flags; |
| |
| if (reg >= ARRAY_SIZE(piix_sidx_map)) |
| return -EINVAL; |
| |
| + spin_lock_irqsave(&hpriv->sidpr_lock, flags); |
| piix_sidpr_sel(link, reg); |
| *val = ioread32(hpriv->sidpr + PIIX_SIDPR_DATA); |
| + spin_unlock_irqrestore(&hpriv->sidpr_lock, flags); |
| return 0; |
| } |
| |
| @@ -964,12 +968,15 @@ static int piix_sidpr_scr_write(struct a |
| unsigned int reg, u32 val) |
| { |
| struct piix_host_priv *hpriv = link->ap->host->private_data; |
| + unsigned long flags; |
| |
| if (reg >= ARRAY_SIZE(piix_sidx_map)) |
| return -EINVAL; |
| |
| + spin_lock_irqsave(&hpriv->sidpr_lock, flags); |
| piix_sidpr_sel(link, reg); |
| iowrite32(val, hpriv->sidpr + PIIX_SIDPR_DATA); |
| + spin_unlock_irqrestore(&hpriv->sidpr_lock, flags); |
| return 0; |
| } |
| |
| @@ -1566,6 +1573,7 @@ static int __devinit piix_init_one(struc |
| hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); |
| if (!hpriv) |
| return -ENOMEM; |
| + spin_lock_init(&hpriv->sidpr_lock); |
| |
| /* Save IOCFG, this will be used for cable detection, quirk |
| * detection and restoration on detach. This is necessary |