[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>

authored by Prarit Bhargava and committed by Tony Luck 6d6e4200 cfbb1426

+94 -71
+12 -4
arch/ia64/sn/include/xtalk/hubdev.h
··· 26 #define IIO_NUM_ITTES 7 27 #define HUB_NUM_BIG_WINDOW (IIO_NUM_ITTES - 1) 28 29 - struct sn_flush_device_list { 30 int sfdl_bus; 31 int sfdl_slot; 32 int sfdl_pin; 33 - struct bar_list { 34 unsigned long start; 35 unsigned long end; 36 } sfdl_bar_list[6]; ··· 43 uint32_t sfdl_persistent_busnum; 44 uint32_t sfdl_persistent_segment; 45 struct pcibus_info *sfdl_pcibus_info; 46 spinlock_t sfdl_flush_lock; 47 }; 48 49 /* 50 - * **widget_p - Used as an array[wid_num][device] of sn_flush_device_list. 51 */ 52 struct sn_flush_nasid_entry { 53 - struct sn_flush_device_list **widget_p; /* Used as a array of wid_num */ 54 uint64_t iio_itte[8]; 55 }; 56
··· 26 #define IIO_NUM_ITTES 7 27 #define HUB_NUM_BIG_WINDOW (IIO_NUM_ITTES - 1) 28 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 { 33 int sfdl_bus; 34 int sfdl_slot; 35 int sfdl_pin; 36 + struct common_bar_list { 37 unsigned long start; 38 unsigned long end; 39 } sfdl_bar_list[6]; ··· 40 uint32_t sfdl_persistent_busnum; 41 uint32_t sfdl_persistent_segment; 42 struct pcibus_info *sfdl_pcibus_info; 43 + }; 44 + 45 + /* This struct is kernel only and is not used by the PROM */ 46 + struct sn_flush_device_kernel { 47 spinlock_t sfdl_flush_lock; 48 + struct sn_flush_device_common *common; 49 }; 50 51 /* 52 + * **widget_p - Used as an array[wid_num][device] of sn_flush_device_kernel. 53 */ 54 struct sn_flush_nasid_entry { 55 + struct sn_flush_device_kernel **widget_p; // Used as an array of wid_num 56 uint64_t iio_itte[8]; 57 }; 58
+52 -40
arch/ia64/sn/kernel/io_init.c
··· 76 }; 77 78 /* 79 - * Retrieve the DMA Flush List given nasid. This list is needed 80 - * to implement the WAR - Flush DMA data on PIO Reads. 81 */ 82 - static inline uint64_t 83 - sal_get_widget_dmaflush_list(u64 nasid, u64 widget_num, u64 address) 84 { 85 86 struct ia64_sal_retval ret_stuff; ··· 89 ret_stuff.v0 = 0; 90 91 SAL_CALL_NOLOCK(ret_stuff, 92 - (u64) SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST, 93 - (u64) nasid, (u64) widget_num, (u64) address, 0, 0, 0, 94 - 0); 95 - return ret_stuff.v0; 96 97 } 98 99 /* 100 * Retrieve the hub device info structure for the given nasid. 101 */ 102 - static inline uint64_t sal_get_hubdev_info(u64 handle, u64 address) 103 { 104 105 struct ia64_sal_retval ret_stuff; ··· 115 /* 116 * Retrieve the pci bus information given the bus number. 117 */ 118 - static inline uint64_t sal_get_pcibus_info(u64 segment, u64 busnum, u64 address) 119 { 120 121 struct ia64_sal_retval ret_stuff; ··· 131 /* 132 * Retrieve the pci device information given the bus and device|function number. 133 */ 134 - static inline uint64_t 135 sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev, 136 u64 sn_irq_info) 137 { ··· 171 */ 172 static void sn_fixup_ionodes(void) 173 { 174 - 175 - struct sn_flush_device_list *sn_flush_device_list; 176 struct hubdev_info *hubdev; 177 - uint64_t status; 178 - uint64_t nasid; 179 - int i, widget; 180 181 /* 182 * Get SGI Specific HUB chipset information. ··· 187 nasid = cnodeid_to_nasid(i); 188 hubdev->max_segment_number = 0xffffffff; 189 hubdev->max_pcibus_number = 0xff; 190 - status = sal_get_hubdev_info(nasid, (uint64_t) __pa(hubdev)); 191 if (status) 192 continue; 193 ··· 214 215 hubdev->hdi_flush_nasid_list.widget_p = 216 kmalloc((HUB_WIDGET_ID_MAX + 1) * 217 - sizeof(struct sn_flush_device_list *), GFP_KERNEL); 218 - 219 memset(hubdev->hdi_flush_nasid_list.widget_p, 0x0, 220 (HUB_WIDGET_ID_MAX + 1) * 221 - sizeof(struct sn_flush_device_list *)); 222 223 for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) { 224 - sn_flush_device_list = kmalloc(DEV_PER_WIDGET * 225 - sizeof(struct 226 - sn_flush_device_list), 227 - GFP_KERNEL); 228 - memset(sn_flush_device_list, 0x0, 229 DEV_PER_WIDGET * 230 - sizeof(struct sn_flush_device_list)); 231 232 - status = 233 - sal_get_widget_dmaflush_list(nasid, widget, 234 - (uint64_t) 235 - __pa 236 - (sn_flush_device_list)); 237 - if (status) { 238 - kfree(sn_flush_device_list); 239 - continue; 240 } 241 242 - spin_lock_init(&sn_flush_device_list->sfdl_flush_lock); 243 - hubdev->hdi_flush_nasid_list.widget_p[widget] = 244 - sn_flush_device_list; 245 - } 246 - 247 } 248 - 249 } 250 251 /*
··· 76 }; 77 78 /* 79 + * Retrieve the DMA Flush List given nasid, widget, and device. 80 + * This list is needed to implement the WAR - Flush DMA data on PIO Reads. 81 */ 82 + static inline u64 83 + sal_get_device_dmaflush_list(u64 nasid, u64 widget_num, u64 device_num, 84 + u64 address) 85 { 86 87 struct ia64_sal_retval ret_stuff; ··· 88 ret_stuff.v0 = 0; 89 90 SAL_CALL_NOLOCK(ret_stuff, 91 + (u64) SN_SAL_IOIF_GET_DEVICE_DMAFLUSH_LIST, 92 + (u64) nasid, (u64) widget_num, 93 + (u64) device_num, (u64) address, 0, 0, 0); 94 + return ret_stuff.status; 95 96 } 97 98 /* 99 * Retrieve the hub device info structure for the given nasid. 100 */ 101 + static inline u64 sal_get_hubdev_info(u64 handle, u64 address) 102 { 103 104 struct ia64_sal_retval ret_stuff; ··· 114 /* 115 * Retrieve the pci bus information given the bus number. 116 */ 117 + static inline u64 sal_get_pcibus_info(u64 segment, u64 busnum, u64 address) 118 { 119 120 struct ia64_sal_retval ret_stuff; ··· 130 /* 131 * Retrieve the pci device information given the bus and device|function number. 132 */ 133 + static inline u64 134 sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev, 135 u64 sn_irq_info) 136 { ··· 170 */ 171 static void sn_fixup_ionodes(void) 172 { 173 + struct sn_flush_device_kernel *sn_flush_device_kernel; 174 + struct sn_flush_device_kernel *dev_entry; 175 struct hubdev_info *hubdev; 176 + u64 status; 177 + u64 nasid; 178 + int i, widget, device; 179 180 /* 181 * Get SGI Specific HUB chipset information. ··· 186 nasid = cnodeid_to_nasid(i); 187 hubdev->max_segment_number = 0xffffffff; 188 hubdev->max_pcibus_number = 0xff; 189 + status = sal_get_hubdev_info(nasid, (u64) __pa(hubdev)); 190 if (status) 191 continue; 192 ··· 213 214 hubdev->hdi_flush_nasid_list.widget_p = 215 kmalloc((HUB_WIDGET_ID_MAX + 1) * 216 + sizeof(struct sn_flush_device_kernel *), 217 + GFP_KERNEL); 218 memset(hubdev->hdi_flush_nasid_list.widget_p, 0x0, 219 (HUB_WIDGET_ID_MAX + 1) * 220 + sizeof(struct sn_flush_device_kernel *)); 221 222 for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) { 223 + sn_flush_device_kernel = kmalloc(DEV_PER_WIDGET * 224 + sizeof(struct 225 + sn_flush_device_kernel), 226 + GFP_KERNEL); 227 + if (!sn_flush_device_kernel) 228 + BUG(); 229 + memset(sn_flush_device_kernel, 0x0, 230 DEV_PER_WIDGET * 231 + sizeof(struct sn_flush_device_kernel)); 232 233 + dev_entry = sn_flush_device_kernel; 234 + for (device = 0; device < DEV_PER_WIDGET; 235 + device++,dev_entry++) { 236 + dev_entry->common = kmalloc(sizeof(struct 237 + sn_flush_device_common), 238 + GFP_KERNEL); 239 + if (!dev_entry->common) 240 + BUG(); 241 + memset(dev_entry->common, 0x0, sizeof(struct 242 + sn_flush_device_common)); 243 + 244 + status = sal_get_device_dmaflush_list(nasid, 245 + widget, 246 + device, 247 + (u64)(dev_entry->common)); 248 + if (status) 249 + BUG(); 250 + 251 + spin_lock_init(&dev_entry->sfdl_flush_lock); 252 } 253 254 + if (sn_flush_device_kernel) 255 + hubdev->hdi_flush_nasid_list.widget_p[widget] = 256 + sn_flush_device_kernel; 257 + } 258 } 259 } 260 261 /*
+18 -16
arch/ia64/sn/pci/pcibr/pcibr_dma.c
··· 218 uint64_t flags; 219 uint64_t itte; 220 struct hubdev_info *hubinfo; 221 - volatile struct sn_flush_device_list *p; 222 struct sn_flush_nasid_entry *flush_nasid_list; 223 224 if (!sn_ioif_inited) ··· 270 p = &flush_nasid_list->widget_p[wid_num][0]; 271 272 /* find a matching BAR */ 273 - for (i = 0; i < DEV_PER_WIDGET; i++) { 274 for (j = 0; j < PCI_ROM_RESOURCE; j++) { 275 - if (p->sfdl_bar_list[j].start == 0) 276 break; 277 - if (addr >= p->sfdl_bar_list[j].start 278 - && addr <= p->sfdl_bar_list[j].end) 279 break; 280 } 281 - if (j < PCI_ROM_RESOURCE && p->sfdl_bar_list[j].start != 0) 282 break; 283 - p++; 284 } 285 286 /* if no matching BAR, return without doing anything. */ ··· 306 if ((1 << XWIDGET_PART_REV_NUM_REV(revnum)) & PV907516) { 307 return; 308 } else { 309 - pcireg_wrb_flush_get(p->sfdl_pcibus_info, 310 - (p->sfdl_slot - 1)); 311 } 312 } else { 313 - spin_lock_irqsave(&((struct sn_flush_device_list *)p)-> 314 - sfdl_flush_lock, flags); 315 - 316 - *p->sfdl_flush_addr = 0; 317 318 /* force an interrupt. */ 319 - *(volatile uint32_t *)(p->sfdl_force_int_addr) = 1; 320 321 /* wait for the interrupt to come back. */ 322 - while (*(p->sfdl_flush_addr) != 0x10f) 323 cpu_relax(); 324 325 /* okay, everything is synched up. */ 326 - spin_unlock_irqrestore((spinlock_t *)&p->sfdl_flush_lock, flags); 327 } 328 return; 329 }
··· 218 uint64_t flags; 219 uint64_t itte; 220 struct hubdev_info *hubinfo; 221 + volatile struct sn_flush_device_kernel *p; 222 + volatile struct sn_flush_device_common *common; 223 + 224 struct sn_flush_nasid_entry *flush_nasid_list; 225 226 if (!sn_ioif_inited) ··· 268 p = &flush_nasid_list->widget_p[wid_num][0]; 269 270 /* find a matching BAR */ 271 + for (i = 0; i < DEV_PER_WIDGET; i++,p++) { 272 + common = p->common; 273 for (j = 0; j < PCI_ROM_RESOURCE; j++) { 274 + if (common->sfdl_bar_list[j].start == 0) 275 break; 276 + if (addr >= common->sfdl_bar_list[j].start 277 + && addr <= common->sfdl_bar_list[j].end) 278 break; 279 } 280 + if (j < PCI_ROM_RESOURCE && common->sfdl_bar_list[j].start != 0) 281 break; 282 } 283 284 /* if no matching BAR, return without doing anything. */ ··· 304 if ((1 << XWIDGET_PART_REV_NUM_REV(revnum)) & PV907516) { 305 return; 306 } else { 307 + pcireg_wrb_flush_get(common->sfdl_pcibus_info, 308 + (common->sfdl_slot - 1)); 309 } 310 } else { 311 + spin_lock_irqsave((spinlock_t *)&p->sfdl_flush_lock, 312 + flags); 313 + *common->sfdl_flush_addr = 0; 314 315 /* force an interrupt. */ 316 + *(volatile uint32_t *)(common->sfdl_force_int_addr) = 1; 317 318 /* wait for the interrupt to come back. */ 319 + while (*(common->sfdl_flush_addr) != 0x10f) 320 cpu_relax(); 321 322 /* okay, everything is synched up. */ 323 + spin_unlock_irqrestore((spinlock_t *)&p->sfdl_flush_lock, 324 + flags); 325 } 326 return; 327 }
+10 -10
arch/ia64/sn/pci/pcibr/pcibr_provider.c
··· 92 cnodeid_t near_cnode; 93 struct hubdev_info *hubdev_info; 94 struct pcibus_info *soft; 95 - struct sn_flush_device_list *sn_flush_device_list; 96 97 if (! IS_PCI_BRIDGE_ASIC(prom_bussoft->bs_asic_type)) { 98 return NULL; ··· 138 hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); 139 140 if (hubdev_info->hdi_flush_nasid_list.widget_p) { 141 - sn_flush_device_list = hubdev_info->hdi_flush_nasid_list. 142 widget_p[(int)soft->pbi_buscommon.bs_xid]; 143 - if (sn_flush_device_list) { 144 for (j = 0; j < DEV_PER_WIDGET; 145 - j++, sn_flush_device_list++) { 146 - if (sn_flush_device_list->sfdl_slot == -1) 147 continue; 148 - if ((sn_flush_device_list-> 149 - sfdl_persistent_segment == 150 soft->pbi_buscommon.bs_persist_segment) && 151 - (sn_flush_device_list-> 152 - sfdl_persistent_busnum == 153 soft->pbi_buscommon.bs_persist_busnum)) 154 - sn_flush_device_list->sfdl_pcibus_info = 155 soft; 156 } 157 }
··· 92 cnodeid_t near_cnode; 93 struct hubdev_info *hubdev_info; 94 struct pcibus_info *soft; 95 + struct sn_flush_device_kernel *sn_flush_device_kernel; 96 + struct sn_flush_device_common *common; 97 98 if (! IS_PCI_BRIDGE_ASIC(prom_bussoft->bs_asic_type)) { 99 return NULL; ··· 137 hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); 138 139 if (hubdev_info->hdi_flush_nasid_list.widget_p) { 140 + sn_flush_device_kernel = hubdev_info->hdi_flush_nasid_list. 141 widget_p[(int)soft->pbi_buscommon.bs_xid]; 142 + if (sn_flush_device_kernel) { 143 for (j = 0; j < DEV_PER_WIDGET; 144 + j++, sn_flush_device_kernel++) { 145 + common = sn_flush_device_kernel->common; 146 + if (common->sfdl_slot == -1) 147 continue; 148 + if ((common->sfdl_persistent_segment == 149 soft->pbi_buscommon.bs_persist_segment) && 150 + (common->sfdl_persistent_busnum == 151 soft->pbi_buscommon.bs_persist_busnum)) 152 + common->sfdl_pcibus_info = 153 soft; 154 } 155 }
+2 -1
include/asm-ia64/sn/sn_sal.h
··· 75 #define SN_SAL_IOIF_GET_HUBDEV_INFO 0x02000055 76 #define SN_SAL_IOIF_GET_PCIBUS_INFO 0x02000056 77 #define SN_SAL_IOIF_GET_PCIDEV_INFO 0x02000057 78 - #define SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST 0x02000058 79 80 #define SN_SAL_HUB_ERROR_INTERRUPT 0x02000060 81 #define SN_SAL_BTE_RECOVER 0x02000061
··· 75 #define SN_SAL_IOIF_GET_HUBDEV_INFO 0x02000055 76 #define SN_SAL_IOIF_GET_PCIBUS_INFO 0x02000056 77 #define SN_SAL_IOIF_GET_PCIDEV_INFO 0x02000057 78 + #define SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST 0x02000058 // deprecated 79 + #define SN_SAL_IOIF_GET_DEVICE_DMAFLUSH_LIST 0x0200005a 80 81 #define SN_SAL_HUB_ERROR_INTERRUPT 0x02000060 82 #define SN_SAL_BTE_RECOVER 0x02000061