Commit 6d6e4200 authored by Prarit Bhargava's avatar Prarit Bhargava Committed by Tony Luck

[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: default avatarPrarit Bhargava <prarit@sgi.com>
Signed-off-by: default avatarTony Luck <tony.luck@intel.com>
parent cfbb1426
...@@ -26,11 +26,14 @@ ...@@ -26,11 +26,14 @@
#define IIO_NUM_ITTES 7 #define IIO_NUM_ITTES 7
#define HUB_NUM_BIG_WINDOW (IIO_NUM_ITTES - 1) #define HUB_NUM_BIG_WINDOW (IIO_NUM_ITTES - 1)
struct sn_flush_device_list { /* This struct is shared between the PROM and the kernel.
* Changes to this struct will require corresponding changes to the kernel.
*/
struct sn_flush_device_common {
int sfdl_bus; int sfdl_bus;
int sfdl_slot; int sfdl_slot;
int sfdl_pin; int sfdl_pin;
struct bar_list { struct common_bar_list {
unsigned long start; unsigned long start;
unsigned long end; unsigned long end;
} sfdl_bar_list[6]; } sfdl_bar_list[6];
...@@ -40,14 +43,19 @@ struct sn_flush_device_list { ...@@ -40,14 +43,19 @@ struct sn_flush_device_list {
uint32_t sfdl_persistent_busnum; uint32_t sfdl_persistent_busnum;
uint32_t sfdl_persistent_segment; uint32_t sfdl_persistent_segment;
struct pcibus_info *sfdl_pcibus_info; struct pcibus_info *sfdl_pcibus_info;
};
/* This struct is kernel only and is not used by the PROM */
struct sn_flush_device_kernel {
spinlock_t sfdl_flush_lock; spinlock_t sfdl_flush_lock;
struct sn_flush_device_common *common;
}; };
/* /*
* **widget_p - Used as an array[wid_num][device] of sn_flush_device_list. * **widget_p - Used as an array[wid_num][device] of sn_flush_device_kernel.
*/ */
struct sn_flush_nasid_entry { struct sn_flush_nasid_entry {
struct sn_flush_device_list **widget_p; /* Used as a array of wid_num */ struct sn_flush_device_kernel **widget_p; // Used as an array of wid_num
uint64_t iio_itte[8]; uint64_t iio_itte[8];
}; };
......
...@@ -76,11 +76,12 @@ static struct sn_pcibus_provider sn_pci_default_provider = { ...@@ -76,11 +76,12 @@ static struct sn_pcibus_provider sn_pci_default_provider = {
}; };
/* /*
* Retrieve the DMA Flush List given nasid. This list is needed * Retrieve the DMA Flush List given nasid, widget, and device.
* to implement the WAR - Flush DMA data on PIO Reads. * This list is needed to implement the WAR - Flush DMA data on PIO Reads.
*/ */
static inline uint64_t static inline u64
sal_get_widget_dmaflush_list(u64 nasid, u64 widget_num, u64 address) sal_get_device_dmaflush_list(u64 nasid, u64 widget_num, u64 device_num,
u64 address)
{ {
struct ia64_sal_retval ret_stuff; struct ia64_sal_retval ret_stuff;
...@@ -88,17 +89,17 @@ sal_get_widget_dmaflush_list(u64 nasid, u64 widget_num, u64 address) ...@@ -88,17 +89,17 @@ sal_get_widget_dmaflush_list(u64 nasid, u64 widget_num, u64 address)
ret_stuff.v0 = 0; ret_stuff.v0 = 0;
SAL_CALL_NOLOCK(ret_stuff, SAL_CALL_NOLOCK(ret_stuff,
(u64) SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST, (u64) SN_SAL_IOIF_GET_DEVICE_DMAFLUSH_LIST,
(u64) nasid, (u64) widget_num, (u64) address, 0, 0, 0, (u64) nasid, (u64) widget_num,
0); (u64) device_num, (u64) address, 0, 0, 0);
return ret_stuff.v0; return ret_stuff.status;
} }
/* /*
* Retrieve the hub device info structure for the given nasid. * Retrieve the hub device info structure for the given nasid.
*/ */
static inline uint64_t sal_get_hubdev_info(u64 handle, u64 address) static inline u64 sal_get_hubdev_info(u64 handle, u64 address)
{ {
struct ia64_sal_retval ret_stuff; struct ia64_sal_retval ret_stuff;
...@@ -114,7 +115,7 @@ static inline uint64_t sal_get_hubdev_info(u64 handle, u64 address) ...@@ -114,7 +115,7 @@ static inline uint64_t sal_get_hubdev_info(u64 handle, u64 address)
/* /*
* Retrieve the pci bus information given the bus number. * Retrieve the pci bus information given the bus number.
*/ */
static inline uint64_t sal_get_pcibus_info(u64 segment, u64 busnum, u64 address) static inline u64 sal_get_pcibus_info(u64 segment, u64 busnum, u64 address)
{ {
struct ia64_sal_retval ret_stuff; 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,7 +131,7 @@ static inline uint64_t sal_get_pcibus_info(u64 segment, u64 busnum, u64 address)
/* /*
* Retrieve the pci device information given the bus and device|function number. * Retrieve the pci device information given the bus and device|function number.
*/ */
static inline uint64_t static inline u64
sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev, sal_get_pcidev_info(u64 segment, u64 bus_number, u64 devfn, u64 pci_dev,
u64 sn_irq_info) u64 sn_irq_info)
{ {
...@@ -170,12 +171,12 @@ sn_pcidev_info_get(struct pci_dev *dev) ...@@ -170,12 +171,12 @@ sn_pcidev_info_get(struct pci_dev *dev)
*/ */
static void sn_fixup_ionodes(void) static void sn_fixup_ionodes(void)
{ {
struct sn_flush_device_kernel *sn_flush_device_kernel;
struct sn_flush_device_list *sn_flush_device_list; struct sn_flush_device_kernel *dev_entry;
struct hubdev_info *hubdev; struct hubdev_info *hubdev;
uint64_t status; u64 status;
uint64_t nasid; u64 nasid;
int i, widget; int i, widget, device;
/* /*
* Get SGI Specific HUB chipset information. * Get SGI Specific HUB chipset information.
...@@ -186,7 +187,7 @@ static void sn_fixup_ionodes(void) ...@@ -186,7 +187,7 @@ static void sn_fixup_ionodes(void)
nasid = cnodeid_to_nasid(i); nasid = cnodeid_to_nasid(i);
hubdev->max_segment_number = 0xffffffff; hubdev->max_segment_number = 0xffffffff;
hubdev->max_pcibus_number = 0xff; hubdev->max_pcibus_number = 0xff;
status = sal_get_hubdev_info(nasid, (uint64_t) __pa(hubdev)); status = sal_get_hubdev_info(nasid, (u64) __pa(hubdev));
if (status) if (status)
continue; continue;
...@@ -213,38 +214,49 @@ static void sn_fixup_ionodes(void) ...@@ -213,38 +214,49 @@ static void sn_fixup_ionodes(void)
hubdev->hdi_flush_nasid_list.widget_p = hubdev->hdi_flush_nasid_list.widget_p =
kmalloc((HUB_WIDGET_ID_MAX + 1) * kmalloc((HUB_WIDGET_ID_MAX + 1) *
sizeof(struct sn_flush_device_list *), GFP_KERNEL); sizeof(struct sn_flush_device_kernel *),
GFP_KERNEL);
memset(hubdev->hdi_flush_nasid_list.widget_p, 0x0, memset(hubdev->hdi_flush_nasid_list.widget_p, 0x0,
(HUB_WIDGET_ID_MAX + 1) * (HUB_WIDGET_ID_MAX + 1) *
sizeof(struct sn_flush_device_list *)); sizeof(struct sn_flush_device_kernel *));
for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) { for (widget = 0; widget <= HUB_WIDGET_ID_MAX; widget++) {
sn_flush_device_list = kmalloc(DEV_PER_WIDGET * sn_flush_device_kernel = kmalloc(DEV_PER_WIDGET *
sizeof(struct sizeof(struct
sn_flush_device_list), sn_flush_device_kernel),
GFP_KERNEL); GFP_KERNEL);
memset(sn_flush_device_list, 0x0, if (!sn_flush_device_kernel)
BUG();
memset(sn_flush_device_kernel, 0x0,
DEV_PER_WIDGET * DEV_PER_WIDGET *
sizeof(struct sn_flush_device_list)); sizeof(struct sn_flush_device_kernel));
status = dev_entry = sn_flush_device_kernel;
sal_get_widget_dmaflush_list(nasid, widget, for (device = 0; device < DEV_PER_WIDGET;
(uint64_t) device++,dev_entry++) {
__pa dev_entry->common = kmalloc(sizeof(struct
(sn_flush_device_list)); sn_flush_device_common),
if (status) { GFP_KERNEL);
kfree(sn_flush_device_list); if (!dev_entry->common)
continue; BUG();
memset(dev_entry->common, 0x0, sizeof(struct
sn_flush_device_common));
status = sal_get_device_dmaflush_list(nasid,
widget,
device,
(u64)(dev_entry->common));
if (status)
BUG();
spin_lock_init(&dev_entry->sfdl_flush_lock);
} }
spin_lock_init(&sn_flush_device_list->sfdl_flush_lock); if (sn_flush_device_kernel)
hubdev->hdi_flush_nasid_list.widget_p[widget] = hubdev->hdi_flush_nasid_list.widget_p[widget] =
sn_flush_device_list; sn_flush_device_kernel;
} }
} }
} }
/* /*
......
...@@ -218,7 +218,9 @@ void sn_dma_flush(uint64_t addr) ...@@ -218,7 +218,9 @@ void sn_dma_flush(uint64_t addr)
uint64_t flags; uint64_t flags;
uint64_t itte; uint64_t itte;
struct hubdev_info *hubinfo; struct hubdev_info *hubinfo;
volatile struct sn_flush_device_list *p; volatile struct sn_flush_device_kernel *p;
volatile struct sn_flush_device_common *common;
struct sn_flush_nasid_entry *flush_nasid_list; struct sn_flush_nasid_entry *flush_nasid_list;
if (!sn_ioif_inited) if (!sn_ioif_inited)
...@@ -268,17 +270,17 @@ void sn_dma_flush(uint64_t addr) ...@@ -268,17 +270,17 @@ void sn_dma_flush(uint64_t addr)
p = &flush_nasid_list->widget_p[wid_num][0]; p = &flush_nasid_list->widget_p[wid_num][0];
/* find a matching BAR */ /* find a matching BAR */
for (i = 0; i < DEV_PER_WIDGET; i++) { for (i = 0; i < DEV_PER_WIDGET; i++,p++) {
common = p->common;
for (j = 0; j < PCI_ROM_RESOURCE; j++) { for (j = 0; j < PCI_ROM_RESOURCE; j++) {
if (p->sfdl_bar_list[j].start == 0) if (common->sfdl_bar_list[j].start == 0)
break; break;
if (addr >= p->sfdl_bar_list[j].start if (addr >= common->sfdl_bar_list[j].start
&& addr <= p->sfdl_bar_list[j].end) && addr <= common->sfdl_bar_list[j].end)
break; break;
} }
if (j < PCI_ROM_RESOURCE && p->sfdl_bar_list[j].start != 0) if (j < PCI_ROM_RESOURCE && common->sfdl_bar_list[j].start != 0)
break; break;
p++;
} }
/* if no matching BAR, return without doing anything. */ /* if no matching BAR, return without doing anything. */
...@@ -304,24 +306,24 @@ void sn_dma_flush(uint64_t addr) ...@@ -304,24 +306,24 @@ void sn_dma_flush(uint64_t addr)
if ((1 << XWIDGET_PART_REV_NUM_REV(revnum)) & PV907516) { if ((1 << XWIDGET_PART_REV_NUM_REV(revnum)) & PV907516) {
return; return;
} else { } else {
pcireg_wrb_flush_get(p->sfdl_pcibus_info, pcireg_wrb_flush_get(common->sfdl_pcibus_info,
(p->sfdl_slot - 1)); (common->sfdl_slot - 1));
} }
} else { } else {
spin_lock_irqsave(&((struct sn_flush_device_list *)p)-> spin_lock_irqsave((spinlock_t *)&p->sfdl_flush_lock,
sfdl_flush_lock, flags); flags);
*common->sfdl_flush_addr = 0;
*p->sfdl_flush_addr = 0;
/* force an interrupt. */ /* force an interrupt. */
*(volatile uint32_t *)(p->sfdl_force_int_addr) = 1; *(volatile uint32_t *)(common->sfdl_force_int_addr) = 1;
/* wait for the interrupt to come back. */ /* wait for the interrupt to come back. */
while (*(p->sfdl_flush_addr) != 0x10f) while (*(common->sfdl_flush_addr) != 0x10f)
cpu_relax(); cpu_relax();
/* okay, everything is synched up. */ /* okay, everything is synched up. */
spin_unlock_irqrestore((spinlock_t *)&p->sfdl_flush_lock, flags); spin_unlock_irqrestore((spinlock_t *)&p->sfdl_flush_lock,
flags);
} }
return; return;
} }
......
...@@ -92,7 +92,8 @@ pcibr_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *cont ...@@ -92,7 +92,8 @@ pcibr_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *cont
cnodeid_t near_cnode; cnodeid_t near_cnode;
struct hubdev_info *hubdev_info; struct hubdev_info *hubdev_info;
struct pcibus_info *soft; struct pcibus_info *soft;
struct sn_flush_device_list *sn_flush_device_list; struct sn_flush_device_kernel *sn_flush_device_kernel;
struct sn_flush_device_common *common;
if (! IS_PCI_BRIDGE_ASIC(prom_bussoft->bs_asic_type)) { if (! IS_PCI_BRIDGE_ASIC(prom_bussoft->bs_asic_type)) {
return NULL; return NULL;
...@@ -137,20 +138,19 @@ pcibr_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *cont ...@@ -137,20 +138,19 @@ pcibr_bus_fixup(struct pcibus_bussoft *prom_bussoft, struct pci_controller *cont
hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo); hubdev_info = (struct hubdev_info *)(NODEPDA(cnode)->pdinfo);
if (hubdev_info->hdi_flush_nasid_list.widget_p) { if (hubdev_info->hdi_flush_nasid_list.widget_p) {
sn_flush_device_list = hubdev_info->hdi_flush_nasid_list. sn_flush_device_kernel = hubdev_info->hdi_flush_nasid_list.
widget_p[(int)soft->pbi_buscommon.bs_xid]; widget_p[(int)soft->pbi_buscommon.bs_xid];
if (sn_flush_device_list) { if (sn_flush_device_kernel) {
for (j = 0; j < DEV_PER_WIDGET; for (j = 0; j < DEV_PER_WIDGET;
j++, sn_flush_device_list++) { j++, sn_flush_device_kernel++) {
if (sn_flush_device_list->sfdl_slot == -1) common = sn_flush_device_kernel->common;
if (common->sfdl_slot == -1)
continue; continue;
if ((sn_flush_device_list-> if ((common->sfdl_persistent_segment ==
sfdl_persistent_segment ==
soft->pbi_buscommon.bs_persist_segment) && soft->pbi_buscommon.bs_persist_segment) &&
(sn_flush_device_list-> (common->sfdl_persistent_busnum ==
sfdl_persistent_busnum ==
soft->pbi_buscommon.bs_persist_busnum)) soft->pbi_buscommon.bs_persist_busnum))
sn_flush_device_list->sfdl_pcibus_info = common->sfdl_pcibus_info =
soft; soft;
} }
} }
......
...@@ -75,7 +75,8 @@ ...@@ -75,7 +75,8 @@
#define SN_SAL_IOIF_GET_HUBDEV_INFO 0x02000055 #define SN_SAL_IOIF_GET_HUBDEV_INFO 0x02000055
#define SN_SAL_IOIF_GET_PCIBUS_INFO 0x02000056 #define SN_SAL_IOIF_GET_PCIBUS_INFO 0x02000056
#define SN_SAL_IOIF_GET_PCIDEV_INFO 0x02000057 #define SN_SAL_IOIF_GET_PCIDEV_INFO 0x02000057
#define SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST 0x02000058 #define SN_SAL_IOIF_GET_WIDGET_DMAFLUSH_LIST 0x02000058 // deprecated
#define SN_SAL_IOIF_GET_DEVICE_DMAFLUSH_LIST 0x0200005a
#define SN_SAL_HUB_ERROR_INTERRUPT 0x02000060 #define SN_SAL_HUB_ERROR_INTERRUPT 0x02000060
#define SN_SAL_BTE_RECOVER 0x02000061 #define SN_SAL_BTE_RECOVER 0x02000061
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment