diff options
author | Prarit Bhargava <prarit@sgi.com> | 2005-12-23 13:33:25 -0500 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2006-01-13 17:13:08 -0500 |
commit | 6d6e420005f3753392b608a614eee8475bdc16f7 (patch) | |
tree | a59860fc15ce4e92c00015d068de4aba12a9b889 | |
parent | cfbb1426bd76c4ba6ec4491c8df2a5dd3d984750 (diff) |
[IA64-SGI] Fix sn_flush_device_kernel & spinlock initialization
This patch separates the sn_flush_device_list struct into kernel and
common (both kernel and PROM accessible) structures. As it was, if the
size of a spinlock_t changed (due to additional CONFIG options, etc.) the
sal call which populated the sn_flush_device_list structs would erroneously
write data (and cause memory corruption and/or a panic).
This patch does the following:
1. Removes sn_flush_device_list and adds sn_flush_device_common and
sn_flush_device_kernel.
2. Adds a new SAL call to populate a sn_flush_device_common struct per
device, not per widget as previously done.
3. Correctly initializes each device's sn_flush_device_kernel spinlock_t
struct (before it was only doing each widget's first device).
Signed-off-by: Prarit Bhargava <prarit@sgi.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
-rw-r--r-- | arch/ia64/sn/include/xtalk/hubdev.h | 16 | ||||
-rw-r--r-- | arch/ia64/sn/kernel/io_init.c | 94 | ||||
-rw-r--r-- | arch/ia64/sn/pci/pcibr/pcibr_dma.c | 34 | ||||
-rw-r--r-- | arch/ia64/sn/pci/pcibr/pcibr_provider.c | 20 | ||||
-rw-r--r-- | include/asm-ia64/sn/sn_sal.h | 3 |
5 files changed, 95 insertions, 72 deletions
diff --git a/arch/ia64/sn/include/xtalk/hubdev.h b/arch/ia64/sn/include/xtalk/hubdev.h index 71c2b271b4c6..4d417c301201 100644 --- a/arch/ia64/sn/include/xtalk/hubdev.h +++ b/arch/ia64/sn/include/xtalk/hubdev.h | |||
@@ -26,11 +26,14 @@ | |||
26 | #define IIO_NUM_ITTES 7 | 26 | #define IIO_NUM_ITTES 7 |
27 | #define HUB_NUM_BIG_WINDOW (IIO_NUM_ITTES - 1) | 27 | #define HUB_NUM_BIG_WINDOW (IIO_NUM_ITTES - 1) |
28 | 28 | ||
29 | struct sn_flush_device_list { | 29 | /* This struct is shared between the PROM and the kernel. |
30 | * Changes to this struct will require corresponding changes to the kernel. | ||
31 | */ | ||
32 | struct sn_flush_device_common { | ||
30 | int sfdl_bus; | 33 | int sfdl_bus; |
31 | int sfdl_slot; | 34 | int sfdl_slot; |
32 | int sfdl_pin; | 35 | int sfdl_pin; |
33 | struct bar_list { | 36 | struct common_bar_list { |
34 | unsigned long start; | 37 | unsigned long start; |
35 | unsigned long end; | 38 | unsigned long end; |
36 | } sfdl_bar_list[6]; | 39 | } sfdl_bar_list[6]; |
@@ -40,14 +43,19 @@ struct sn_flush_device_list { | |||
40 | uint32_t sfdl_persistent_busnum; | 43 | uint32_t sfdl_persistent_busnum; |
41 | uint32_t sfdl_persistent_segment; | 44 | uint32_t sfdl_persistent_segment; |
42 | struct pcibus_info *sfdl_pcibus_info; | 45 | struct pcibus_info *sfdl_pcibus_info; |
46 | }; | ||
47 | |||
48 | /* This struct is kernel only and is not used by the PROM */ | ||
49 | struct sn_flush_device_kernel { | ||
43 | spinlock_t sfdl_flush_lock; | 50 | spinlock_t sfdl_flush_lock; |
51 | struct sn_flush_device_common *common; | ||
44 | }; | 52 | }; |
45 | 53 | ||
46 | /* | 54 | /* |
47 | * **widget_p - Used as an array[wid_num][device] of sn_flush_device_list. | 55 | * **widget_p - Used as an array[wid_num][device] of sn_flush_device_kernel. |
48 | */ | 56 | */ |
49 | struct sn_flush_nasid_entry { | 57 | struct sn_flush_nasid_entry { |
50 | struct sn_flush_device_list **widget_p; /* Used as a array of wid_num */ | 58 | struct sn_flush_device_kernel **widget_p; // Used as an array of wid_num |
51 | uint64_t iio_itte[8]; | 59 | uint64_t iio_itte[8]; |
52 | }; | 60 | }; |
53 | 61 | ||
diff --git a/arch/ia64/sn/kernel/io_init.c b/arch/ia64/sn/kernel/io_init.c index 318087e35b66..258d9d7aff98 100644 --- a/arch/ia64/sn/kernel/io_init.c +++ b/arch/ia64/sn/kernel/io_init.c | |||
@@ -76,11 +76,12 @@ static struct sn_pcibus_provider sn_pci_default_provider = { | |||
76 | }; | 76 | }; |
77 | 77 | ||
78 | /* | 78 | /* |
79 | * Retrieve the DMA Flush List given nasid. This list is needed | 79 | * Retrieve the DMA Flush List given nasid, widget, and device. |
80 | * to implement the WAR - Flush DMA data on PIO Reads. | 80 | * This list is needed to implement the WAR - Flush DMA data on PIO Reads. |
81 | */ | 81 | */ |
82 | static inline uint64_t | 82 | static inline u64 |
83 | sal_get_widget_dmaflush_list(u64 nasid, u64 widget_num, u64 address) | 83 | sal_get_device_dmaflush_list(u64 nasid, u64 widget_num, u64 device_num, |
84 | u64 address) | ||
84 | { | 85 | { |
85 | 86 | ||
86 | struct ia64_sal_retval ret_stuff; | 87 | struct ia64_sal_retval ret_stuff; |
@@ -88,17 +89,17 @@ sal_get_widget_dmaflush_list(u64 nasid, u64 widget_num, u64 address) | |||
88 | ret_stuff.v0 = 0; | 89 | ret_stuff.v0 = 0; |
89 | 90 | ||
90 | SAL_CALL_NOLOCK(ret_stuff, | 91 | SAL_CALL_NOLOCK(ret_stuff, |
91 | (u64) SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST, | 92 | (u64) SN_SAL_IOIF_GET_DEVICE_DMAFLUSH_LIST, |
92 | (u64) nasid, (u64) widget_num, (u64) address, 0, 0, 0, | 93 | (u64) nasid, (u64) widget_num, |
93 | 0); | 94 | (u64) device_num, (u64) address, 0, 0, 0); |
94 | return ret_stuff.v0; | 95 | return ret_stuff.status; |
95 | 96 | ||
96 | } | 97 | } |
97 | 98 | ||
98 | /* | 99 | /* |
99 | * Retrieve the hub device info structure for the given nasid. | 100 | * Retrieve the hub device info structure for the given nasid. |
100 | */ | 101 | */ |
101 | static inline uint64_t sal_get_hubdev_info(u64 handle, u64 address) | 102 | static inline u64 sal_get_hubdev_info(u64 handle, u64 address) |
102 | { | 103 | { |
103 | 104 | ||
104 | struct ia64_sal_retval ret_stuff; | 105 | struct ia64_sal_retval ret_stuff; |
@@ -114,7 +115,7 @@ static inline uint64_t sal_get_hubdev_info(u64 handle, u64 address) | |||
114 | /* | 115 | /* |
115 | * Retrieve the pci bus information given the bus number. | 116 | * Retrieve the pci bus information given the bus number. |
116 | */ | 117 | */ |
117 | static inline uint64_t sal_get_pcibus_info(u64 segment, u64 busnum, u64 address) | 118 | static inline u64 sal_get_pcibus_info(u64 segment, u64 busnum, u64 address) |
118 | { | 119 | { |
119 | 120 | ||
120 | struct ia64_sal_retval ret_stuff; | 121 | struct ia64_sal_retval ret_stuff; |
@@ -130,7 +131,7 @@ static inline uint64_t sal_get_pcibus_info(u64 segment, u64 busnum, u64 address) | |||
130 | /* | 131 | /* |
131 | * Retrieve the pci device information given the bus and device|function number. | 132 | * Retrieve the pci device information given the bus and device|function number. |
132 | */ | 133 | */ |
133 | static inline uint64_t | 134 | static inline u64 |
134 | sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev, | 135 | sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev, |
135 | u64 sn_irq_info) | 136 | u64 sn_irq_info) |
136 | { | 137 | { |
@@ -170,12 +171,12 @@ sn_pcidev_info_get(struct pci_dev *dev) | |||
170 | */ | 171 | */ |
171 | static void sn_fixup_ionodes(void) | 172 | static void sn_fixup_ionodes(void) |
172 | { | 173 | { |
173 | 174 | struct sn_flush_device_kernel *sn_flush_device_kernel; | |
174 | struct sn_flush_device_list *sn_flush_device_list; | 175 | struct sn_flush_device_kernel *dev_entry; |
175 | struct hubdev_info *hubdev; | 176 | struct hubdev_info *hubdev; |
176 | uint64_t status; | 177 | u64 status; |
177 | uint64_t nasid; | 178 | u64 nasid; |
178 | int i, widget; | 179 | int i, widget, device; |
179 | 180 | ||
180 | /* | 181 | /* |
181 | * Get SGI Specific HUB chipset information. | 182 | * Get SGI Specific HUB chipset information. |
@@ -186,7 +187,7 @@ static void sn_fixup_ionodes(void) | |||
186 | nasid = cnodeid_to_nasid(i); | 187 | nasid = cnodeid_to_nasid(i); |
187 | hubdev->max_segment_number = 0xffffffff; | 188 | hubdev->max_segment_number = 0xffffffff; |
188 | hubdev->max_pcibus_number = 0xff; | 189 | hubdev->max_pcibus_number = 0xff; |
189 | status = sal_get_hubdev_info(nasid, (uint64_t) __pa(hubdev)); | 190 | status = sal_get_hubdev_info(nasid, (u64) __pa(hubdev)); |
190 | if (status) | 191 | if (status) |
191 | continue; | 192 | continue; |
192 | 193 | ||
@@ -213,38 +214,49 @@ static void sn_fixup_ionodes(void) | |||
213 | 214 | ||
214 | hubdev->hdi_flush_nasid_list.widget_p = | 215 | hubdev->hdi_flush_nasid_list.widget_p = |
215 | kmalloc((HUB_WIDGET_ID_MAX + 1) * | 216 | kmalloc((HUB_WIDGET_ID_MAX + 1) * |
216 | sizeof(struct sn_flush_device_list *), GFP_KERNEL); | 217 | sizeof(struct sn_flush_device_kernel *), |
217 | 218 | GFP_KERNEL); | |
218 | memset(hubdev->hdi_flush_nasid_list.widget_p, 0x0, | 219 | memset(hubdev->hdi_flush_nasid_list.widget_p, 0x0, |
219 | (HUB_WIDGET_ID_MAX + 1) * | 220 | (HUB_WIDGET_ID_MAX + 1) * |
220 | sizeof(struct sn_flush_device_list *)); | 221 | sizeof(struct sn_flush_device_kernel *)); |
221 | 222 | ||
222 | for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) { | 223 | for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) { |
223 | sn_flush_device_list = kmalloc(DEV_PER_WIDGET * | 224 | sn_flush_device_kernel = kmalloc(DEV_PER_WIDGET * |
224 | sizeof(struct | 225 | sizeof(struct |
225 | sn_flush_device_list), | 226 | sn_flush_device_kernel), |
226 | GFP_KERNEL); | 227 | GFP_KERNEL); |
227 | memset(sn_flush_device_list, 0x0, | 228 | if (!sn_flush_device_kernel) |
229 | BUG(); | ||
230 | memset(sn_flush_device_kernel, 0x0, | ||
228 | DEV_PER_WIDGET * | 231 | DEV_PER_WIDGET * |
229 | sizeof(struct sn_flush_device_list)); | 232 | sizeof(struct sn_flush_device_kernel)); |
230 | 233 | ||
231 | status = | 234 | dev_entry = sn_flush_device_kernel; |
232 | sal_get_widget_dmaflush_list(nasid, widget, | 235 | for (device = 0; device < DEV_PER_WIDGET; |
233 | (uint64_t) | 236 | device++,dev_entry++) { |
234 | __pa | 237 | dev_entry->common = kmalloc(sizeof(struct |
235 | (sn_flush_device_list)); | 238 | sn_flush_device_common), |
236 | if (status) { | 239 | GFP_KERNEL); |
237 | kfree(sn_flush_device_list); | 240 | if (!dev_entry->common) |
238 | continue; | 241 | BUG(); |
242 | memset(dev_entry->common, 0x0, sizeof(struct | ||
243 | sn_flush_device_common)); | ||
244 | |||
245 | status = sal_get_device_dmaflush_list(nasid, | ||
246 | widget, | ||
247 | device, | ||
248 | (u64)(dev_entry->common)); | ||
249 | if (status) | ||
250 | BUG(); | ||
251 | |||
252 | spin_lock_init(&dev_entry->sfdl_flush_lock); | ||
239 | } | 253 | } |
240 | 254 | ||
241 | spin_lock_init(&sn_flush_device_list->sfdl_flush_lock); | 255 | if (sn_flush_device_kernel) |
242 | hubdev->hdi_flush_nasid_list.widget_p[widget] = | 256 | hubdev->hdi_flush_nasid_list.widget_p[widget] = |
243 | sn_flush_device_list; | 257 | sn_flush_device_kernel; |
244 | } | 258 | } |
245 | |||
246 | } | 259 | } |
247 | |||
248 | } | 260 | } |
249 | 261 | ||
250 | /* | 262 | /* |
diff --git a/arch/ia64/sn/pci/pcibr/pcibr_dma.c b/arch/ia64/sn/pci/pcibr/pcibr_dma.c index 34093476e965..e68332d93171 100644 --- a/arch/ia64/sn/pci/pcibr/pcibr_dma.c +++ b/arch/ia64/sn/pci/pcibr/pcibr_dma.c | |||
@@ -218,7 +218,9 @@ void sn_dma_flush(uint64_t addr) | |||
218 | uint64_t flags; | 218 | uint64_t flags; |
219 | uint64_t itte; | 219 | uint64_t itte; |
220 | struct hubdev_info *hubinfo; | 220 | struct hubdev_info *hubinfo; |
221 | volatile struct sn_flush_device_list *p; | 221 | volatile struct sn_flush_device_kernel *p; |
222 | volatile struct sn_flush_device_common *common; | ||
223 | |||
222 | struct sn_flush_nasid_entry *flush_nasid_list; | 224 | struct sn_flush_nasid_entry *flush_nasid_list; |
223 | 225 | ||
224 | if (!sn_ioif_inited) | 226 | if (!sn_ioif_inited) |
@@ -268,17 +270,17 @@ void sn_dma_flush(uint64_t addr) | |||
268 | p = &flush_nasid_list->widget_p[wid_num][0]; | 270 | p = &flush_nasid_list->widget_p[wid_num][0]; |
269 | 271 | ||
270 | /* find a matching BAR */ | 272 | /* find a matching BAR */ |
271 | for (i = 0; i < DEV_PER_WIDGET; i++) { | 273 | for (i = 0; i < DEV_PER_WIDGET; i++,p++) { |
274 | common = p->common; | ||
272 | for (j = 0; j < PCI_ROM_RESOURCE; j++) { | 275 | for (j = 0; j < PCI_ROM_RESOURCE; j++) { |
273 | if (p->sfdl_bar_list[j].start == 0) | 276 | if (common->sfdl_bar_list[j].start == 0) |
274 | break; | 277 | break; |
275 | if (addr >= p->sfdl_bar_list[j].start | 278 | if (addr >= common->sfdl_bar_list[j].start |
276 | && addr <= p->sfdl_bar_list[j].end) | 279 | && addr <= common->sfdl_bar_list[j].end) |
277 | break; | 280 | break; |
278 | } | 281 | } |
279 | if (j < PCI_ROM_RESOURCE && p->sfdl_bar_list[j].start != 0) | 282 | if (j < PCI_ROM_RESOURCE && common->sfdl_bar_list[j].start != 0) |
280 | break; | 283 | break; |
281 | p++; | ||
282 | } | 284 | } |
283 | 285 | ||
284 | /* if no matching BAR, return without doing anything. */ | 286 | /* if no matching BAR, return without doing anything. */ |
@@ -304,24 +306,24 @@ void sn_dma_flush(uint64_t addr) | |||
304 | if ((1 << XWIDGET_PART_REV_NUM_REV(revnum)) & PV907516) { | 306 | if ((1 << XWIDGET_PART_REV_NUM_REV(revnum)) & PV907516) { |
305 | return; | 307 | return; |
306 | } else { | 308 | } else { |
307 | pcireg_wrb_flush_get(p->sfdl_pcibus_info, | 309 | pcireg_wrb_flush_get(common->sfdl_pcibus_info, |
308 | (p->sfdl_slot - 1)); | 310 | (common->sfdl_slot - 1)); |
309 | } | 311 | } |
310 | } else { | 312 | } else { |
311 | spin_lock_irqsave(&((struct sn_flush_device_list *)p)-> | 313 | spin_lock_irqsave((spinlock_t *)&p->sfdl_flush_lock, |
312 | sfdl_flush_lock, flags); | 314 | flags); |
313 | 315 | *common->sfdl_flush_addr = 0; | |
314 | *p->sfdl_flush_addr = 0; | ||
315 | 316 | ||
316 | /* force an interrupt. */ | 317 | /* force an interrupt. */ |
317 | *(volatile uint32_t *)(p->sfdl_force_int_addr) = 1; | 318 | *(volatile uint32_t *)(common->sfdl_force_int_addr) = 1; |
318 | 319 | ||
319 | /* wait for the interrupt to come back. */ | 320 | /* wait for the interrupt to come back. */ |
320 | while (*(p->sfdl_flush_addr) != 0x10f) | 321 | while (*(common->sfdl_flush_addr) != 0x10f) |
321 | cpu_relax(); | 322 | cpu_relax(); |
322 | 323 | ||
323 | /* okay, everything is synched up. */ | 324 | /* okay, everything is synched up. */ |
324 | spin_unlock_irqrestore((spinlock_t *)&p->sfdl_flush_lock, flags); | 325 | spin_unlock_irqrestore((spinlock_t *)&p->sfdl_flush_lock, |
326 | flags); | ||
325 | } | 327 | } |
326 | return; | 328 | return; |
327 | } | 329 | } |
diff --git a/arch/ia64/sn/pci/pcibr/pcibr_provider.c b/arch/ia64/sn/pci/pcibr/pcibr_provider.c index 1f500c81002c..e328e948175d 100644 --- a/arch/ia64/sn/pci/pcibr/pcibr_provider.c +++ b/arch/ia64/sn/pci/pcibr/pcibr_provider.c | |||
@@ -92,7 +92,8 @@ pcibr_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *cont | |||
92 | cnodeid_t near_cnode; | 92 | cnodeid_t near_cnode; |
93 | struct hubdev_info *hubdev_info; | 93 | struct hubdev_info *hubdev_info; |
94 | struct pcibus_info *soft; | 94 | struct pcibus_info *soft; |
95 | struct sn_flush_device_list *sn_flush_device_list; | 95 | struct sn_flush_device_kernel *sn_flush_device_kernel; |
96 | struct sn_flush_device_common *common; | ||
96 | 97 | ||
97 | if (! IS_PCI_BRIDGE_ASIC(prom_bussoft->bs_asic_type)) { | 98 | if (! IS_PCI_BRIDGE_ASIC(prom_bussoft->bs_asic_type)) { |
98 | return NULL; | 99 | return NULL; |
@@ -137,20 +138,19 @@ pcibr_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *cont | |||
137 | hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); | 138 | hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); |
138 | 139 | ||
139 | if (hubdev_info->hdi_flush_nasid_list.widget_p) { | 140 | if (hubdev_info->hdi_flush_nasid_list.widget_p) { |
140 | sn_flush_device_list = hubdev_info->hdi_flush_nasid_list. | 141 | sn_flush_device_kernel = hubdev_info->hdi_flush_nasid_list. |
141 | widget_p[(int)soft->pbi_buscommon.bs_xid]; | 142 | widget_p[(int)soft->pbi_buscommon.bs_xid]; |
142 | if (sn_flush_device_list) { | 143 | if (sn_flush_device_kernel) { |
143 | for (j = 0; j < DEV_PER_WIDGET; | 144 | for (j = 0; j < DEV_PER_WIDGET; |
144 | j++, sn_flush_device_list++) { | 145 | j++, sn_flush_device_kernel++) { |
145 | if (sn_flush_device_list->sfdl_slot == -1) | 146 | common = sn_flush_device_kernel->common; |
147 | if (common->sfdl_slot == -1) | ||
146 | continue; | 148 | continue; |
147 | if ((sn_flush_device_list-> | 149 | if ((common->sfdl_persistent_segment == |
148 | sfdl_persistent_segment == | ||
149 | soft->pbi_buscommon.bs_persist_segment) && | 150 | soft->pbi_buscommon.bs_persist_segment) && |
150 | (sn_flush_device_list-> | 151 | (common->sfdl_persistent_busnum == |
151 | sfdl_persistent_busnum == | ||
152 | soft->pbi_buscommon.bs_persist_busnum)) | 152 | soft->pbi_buscommon.bs_persist_busnum)) |
153 | sn_flush_device_list->sfdl_pcibus_info = | 153 | common->sfdl_pcibus_info = |
154 | soft; | 154 | soft; |
155 | } | 155 | } |
156 | } | 156 | } |
diff --git a/include/asm-ia64/sn/sn_sal.h b/include/asm-ia64/sn/sn_sal.h index 4363ed3598ad..8b9e10e7cdba 100644 --- a/include/asm-ia64/sn/sn_sal.h +++ b/include/asm-ia64/sn/sn_sal.h | |||
@@ -75,7 +75,8 @@ | |||
75 | #define SN_SAL_IOIF_GET_HUBDEV_INFO 0x02000055 | 75 | #define SN_SAL_IOIF_GET_HUBDEV_INFO 0x02000055 |
76 | #define SN_SAL_IOIF_GET_PCIBUS_INFO 0x02000056 | 76 | #define SN_SAL_IOIF_GET_PCIBUS_INFO 0x02000056 |
77 | #define SN_SAL_IOIF_GET_PCIDEV_INFO 0x02000057 | 77 | #define SN_SAL_IOIF_GET_PCIDEV_INFO 0x02000057 |
78 | #define SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST 0x02000058 | 78 | #define SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST 0x02000058 // deprecated |
79 | #define SN_SAL_IOIF_GET_DEVICE_DMAFLUSH_LIST 0x0200005a | ||
79 | 80 | ||
80 | #define SN_SAL_HUB_ERROR_INTERRUPT 0x02000060 | 81 | #define SN_SAL_HUB_ERROR_INTERRUPT 0x02000060 |
81 | #define SN_SAL_BTE_RECOVER 0x02000061 | 82 | #define SN_SAL_BTE_RECOVER 0x02000061 |