aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ia64
diff options
context:
space:
mode:
authorJohn Keller <jpk@sgi.com>2007-05-02 10:09:18 -0400
committerTony Luck <tony.luck@intel.com>2007-05-08 14:06:41 -0400
commit0e17b560985afb5190e859d5d4609237a91bb732 (patch)
treed3177fa49c4b909dd83946037744eb8b5c3ad777 /arch/ia64
parentbb8416bf8b93d88e23cbbfde962ef85acda5bd5f (diff)
[IA64] - Altix: hotplug after intr redirect can crash system
When redirecting a device interrupt on SN, not all links between platform specific structures are being updated. This can result in a system crash if an interrupt redirection is followed by an unplug of that device. The complete fix also requires a prom update. Though, this patch is backward compatable and not dependent on the prom patch. Signed-off-by: John Keller <jpk@sgi.com> Signed-off-by: Tony Luck <tony.luck@intel.com>
Diffstat (limited to 'arch/ia64')
-rw-r--r--arch/ia64/sn/kernel/irq.c58
1 files changed, 43 insertions, 15 deletions
diff --git a/arch/ia64/sn/kernel/irq.c b/arch/ia64/sn/kernel/irq.c
index 8d2a1bfbfe7c..7f6d2360a262 100644
--- a/arch/ia64/sn/kernel/irq.c
+++ b/arch/ia64/sn/kernel/irq.c
@@ -59,6 +59,22 @@ void sn_intr_free(nasid_t local_nasid, int local_widget,
59 (u64) sn_irq_info->irq_cookie, 0, 0); 59 (u64) sn_irq_info->irq_cookie, 0, 0);
60} 60}
61 61
62u64 sn_intr_redirect(nasid_t local_nasid, int local_widget,
63 struct sn_irq_info *sn_irq_info,
64 nasid_t req_nasid, int req_slice)
65{
66 struct ia64_sal_retval ret_stuff;
67 ret_stuff.status = 0;
68 ret_stuff.v0 = 0;
69
70 SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_INTERRUPT,
71 (u64) SAL_INTR_REDIRECT, (u64) local_nasid,
72 (u64) local_widget, __pa(sn_irq_info),
73 (u64) req_nasid, (u64) req_slice, 0);
74
75 return ret_stuff.status;
76}
77
62static unsigned int sn_startup_irq(unsigned int irq) 78static unsigned int sn_startup_irq(unsigned int irq)
63{ 79{
64 return 0; 80 return 0;
@@ -127,15 +143,8 @@ struct sn_irq_info *sn_retarget_vector(struct sn_irq_info *sn_irq_info,
127 struct sn_irq_info *new_irq_info; 143 struct sn_irq_info *new_irq_info;
128 struct sn_pcibus_provider *pci_provider; 144 struct sn_pcibus_provider *pci_provider;
129 145
130 new_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_ATOMIC); 146 bridge = (u64) sn_irq_info->irq_bridge;
131 if (new_irq_info == NULL)
132 return NULL;
133
134 memcpy(new_irq_info, sn_irq_info, sizeof(struct sn_irq_info));
135
136 bridge = (u64) new_irq_info->irq_bridge;
137 if (!bridge) { 147 if (!bridge) {
138 kfree(new_irq_info);
139 return NULL; /* irq is not a device interrupt */ 148 return NULL; /* irq is not a device interrupt */
140 } 149 }
141 150
@@ -145,8 +154,25 @@ struct sn_irq_info *sn_retarget_vector(struct sn_irq_info *sn_irq_info,
145 local_widget = TIO_SWIN_WIDGETNUM(bridge); 154 local_widget = TIO_SWIN_WIDGETNUM(bridge);
146 else 155 else
147 local_widget = SWIN_WIDGETNUM(bridge); 156 local_widget = SWIN_WIDGETNUM(bridge);
148
149 vector = sn_irq_info->irq_irq; 157 vector = sn_irq_info->irq_irq;
158
159 /* Make use of SAL_INTR_REDIRECT if PROM supports it */
160 status = sn_intr_redirect(local_nasid, local_widget, sn_irq_info, nasid, slice);
161 if (!status) {
162 new_irq_info = sn_irq_info;
163 goto finish_up;
164 }
165
166 /*
167 * PROM does not support SAL_INTR_REDIRECT, or it failed.
168 * Revert to old method.
169 */
170 new_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_ATOMIC);
171 if (new_irq_info == NULL)
172 return NULL;
173
174 memcpy(new_irq_info, sn_irq_info, sizeof(struct sn_irq_info));
175
150 /* Free the old PROM new_irq_info structure */ 176 /* Free the old PROM new_irq_info structure */
151 sn_intr_free(local_nasid, local_widget, new_irq_info); 177 sn_intr_free(local_nasid, local_widget, new_irq_info);
152 unregister_intr_pda(new_irq_info); 178 unregister_intr_pda(new_irq_info);
@@ -162,11 +188,18 @@ struct sn_irq_info *sn_retarget_vector(struct sn_irq_info *sn_irq_info,
162 return NULL; 188 return NULL;
163 } 189 }
164 190
191 register_intr_pda(new_irq_info);
192 spin_lock(&sn_irq_info_lock);
193 list_replace_rcu(&sn_irq_info->list, &new_irq_info->list);
194 spin_unlock(&sn_irq_info_lock);
195 call_rcu(&sn_irq_info->rcu, sn_irq_info_free);
196
197
198finish_up:
165 /* Update kernels new_irq_info with new target info */ 199 /* Update kernels new_irq_info with new target info */
166 cpuid = nasid_slice_to_cpuid(new_irq_info->irq_nasid, 200 cpuid = nasid_slice_to_cpuid(new_irq_info->irq_nasid,
167 new_irq_info->irq_slice); 201 new_irq_info->irq_slice);
168 new_irq_info->irq_cpuid = cpuid; 202 new_irq_info->irq_cpuid = cpuid;
169 register_intr_pda(new_irq_info);
170 203
171 pci_provider = sn_pci_provider[new_irq_info->irq_bridge_type]; 204 pci_provider = sn_pci_provider[new_irq_info->irq_bridge_type];
172 205
@@ -178,11 +211,6 @@ struct sn_irq_info *sn_retarget_vector(struct sn_irq_info *sn_irq_info,
178 pci_provider && pci_provider->target_interrupt) 211 pci_provider && pci_provider->target_interrupt)
179 (pci_provider->target_interrupt)(new_irq_info); 212 (pci_provider->target_interrupt)(new_irq_info);
180 213
181 spin_lock(&sn_irq_info_lock);
182 list_replace_rcu(&sn_irq_info->list, &new_irq_info->list);
183 spin_unlock(&sn_irq_info_lock);
184 call_rcu(&sn_irq_info->rcu, sn_irq_info_free);
185
186#ifdef CONFIG_SMP 214#ifdef CONFIG_SMP
187 cpuphys = cpu_physical_id(cpuid); 215 cpuphys = cpu_physical_id(cpuid);
188 set_irq_affinity_info((vector & 0xff), cpuphys, 0); 216 set_irq_affinity_info((vector & 0xff), cpuphys, 0);