]>
Commit | Line | Data |
---|---|---|
4b8c693b GKH |
1 | From 213373cf974fe69e78ec894b07f45ae2f5a3a078 Mon Sep 17 00:00:00 2001 |
2 | From: Tejun Heo <tj@kernel.org> | |
3 | Date: Tue, 20 Jul 2010 16:20:01 +0200 | |
4 | Subject: ata_piix: fix locking around SIDPR access | |
5 | ||
6 | From: Tejun Heo <tj@kernel.org> | |
7 | ||
8 | commit 213373cf974fe69e78ec894b07f45ae2f5a3a078 upstream. | |
9 | ||
10 | SIDPR window registers are shared across ports and as each access is | |
11 | done in two steps, accesses to different ports under EH may race. | |
12 | This primarily is caused by incorrect host locking in EH context and | |
13 | should be fixed by defining locking requirements for each EH operation | |
14 | which can be used during EH and enforcing them but for now work around | |
15 | the problem by adding a dedicated SIDPR lock and grabbing it for each | |
16 | SIDPR access. | |
17 | ||
18 | Signed-off-by: Tejun Heo <tj@kernel.org> | |
19 | Reported-by: Mark Knecht <markknecht@gmail.com> | |
20 | Reported-by: Paul Check <paul@thechecks.ca> | |
21 | Signed-off-by: Jeff Garzik <jgarzik@redhat.com> | |
22 | Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> | |
23 | ||
24 | --- | |
25 | drivers/ata/ata_piix.c | 8 ++++++++ | |
26 | 1 file changed, 8 insertions(+) | |
27 | ||
28 | --- a/drivers/ata/ata_piix.c | |
29 | +++ b/drivers/ata/ata_piix.c | |
30 | @@ -157,6 +157,7 @@ struct piix_map_db { | |
31 | struct piix_host_priv { | |
32 | const int *map; | |
33 | u32 saved_iocfg; | |
34 | + spinlock_t sidpr_lock; /* FIXME: remove once locking in EH is fixed */ | |
35 | void __iomem *sidpr; | |
36 | }; | |
37 | ||
38 | @@ -948,12 +949,15 @@ static int piix_sidpr_scr_read(struct at | |
39 | unsigned int reg, u32 *val) | |
40 | { | |
41 | struct piix_host_priv *hpriv = link->ap->host->private_data; | |
42 | + unsigned long flags; | |
43 | ||
44 | if (reg >= ARRAY_SIZE(piix_sidx_map)) | |
45 | return -EINVAL; | |
46 | ||
47 | + spin_lock_irqsave(&hpriv->sidpr_lock, flags); | |
48 | piix_sidpr_sel(link, reg); | |
49 | *val = ioread32(hpriv->sidpr + PIIX_SIDPR_DATA); | |
50 | + spin_unlock_irqrestore(&hpriv->sidpr_lock, flags); | |
51 | return 0; | |
52 | } | |
53 | ||
54 | @@ -961,12 +965,15 @@ static int piix_sidpr_scr_write(struct a | |
55 | unsigned int reg, u32 val) | |
56 | { | |
57 | struct piix_host_priv *hpriv = link->ap->host->private_data; | |
58 | + unsigned long flags; | |
59 | ||
60 | if (reg >= ARRAY_SIZE(piix_sidx_map)) | |
61 | return -EINVAL; | |
62 | ||
63 | + spin_lock_irqsave(&hpriv->sidpr_lock, flags); | |
64 | piix_sidpr_sel(link, reg); | |
65 | iowrite32(val, hpriv->sidpr + PIIX_SIDPR_DATA); | |
66 | + spin_unlock_irqrestore(&hpriv->sidpr_lock, flags); | |
67 | return 0; | |
68 | } | |
69 | ||
70 | @@ -1555,6 +1562,7 @@ static int __devinit piix_init_one(struc | |
71 | hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); | |
72 | if (!hpriv) | |
73 | return -ENOMEM; | |
74 | + spin_lock_init(&hpriv->sidpr_lock); | |
75 | ||
76 | /* Save IOCFG, this will be used for cable detection, quirk | |
77 | * detection and restoration on detach. This is necessary |