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 | |
| 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>
| -rw-r--r-- | arch/ia64/sn/kernel/irq.c | 58 | ||||
| -rw-r--r-- | include/asm-ia64/sn/sn_sal.h | 1 |
2 files changed, 44 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); |
diff --git a/include/asm-ia64/sn/sn_sal.h b/include/asm-ia64/sn/sn_sal.h index 2c4004eb5a68..291e8ceed6e6 100644 --- a/include/asm-ia64/sn/sn_sal.h +++ b/include/asm-ia64/sn/sn_sal.h | |||
| @@ -106,6 +106,7 @@ | |||
| 106 | /* interrupt handling */ | 106 | /* interrupt handling */ |
| 107 | #define SAL_INTR_ALLOC 1 | 107 | #define SAL_INTR_ALLOC 1 |
| 108 | #define SAL_INTR_FREE 2 | 108 | #define SAL_INTR_FREE 2 |
| 109 | #define SAL_INTR_REDIRECT 3 | ||
| 109 | 110 | ||
| 110 | /* | 111 | /* |
| 111 | * operations available on the generic SN_SAL_SYSCTL_OP | 112 | * operations available on the generic SN_SAL_SYSCTL_OP |
