diff options
author | John Keller <jpk@sgi.com> | 2007-05-02 10:09:18 -0400 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2007-05-08 14:06:41 -0400 |
commit | 0e17b560985afb5190e859d5d4609237a91bb732 (patch) | |
tree | d3177fa49c4b909dd83946037744eb8b5c3ad777 /arch/ia64/sn | |
parent | bb8416bf8b93d88e23cbbfde962ef85acda5bd5f (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/sn')
-rw-r--r-- | arch/ia64/sn/kernel/irq.c | 58 |
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 | ||
62 | u64 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 | |||
62 | static unsigned int sn_startup_irq(unsigned int irq) | 78 | static 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 | |||
198 | finish_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); |