[PATCH] Prevent flushing of locked PROM ITLB entry (RED State Exception or hang on reboot)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On UltraSPARC systems, the PROM establishes a locked ITLB entry that needs
to be present on return to the PROM (which happens when the system reboots).
If the kernel tries to reboot with this ITLB entry not being present, a
RED State Exception is caused, which *may* cause a restart as well, but some
internal state may be corrupted. (It was observed that PCI resource information
from the PROM was badly damaged when a linux kernel was started after such a
RED State Exception, causing subsequent resource allocation errors in the
kernel.) In other cases, the machine might also hang after the RED State Exception.

The locked PROM ITLB entry is flushed by flush_tlb_kernel_range(start, end) when
the flushed interval contains the PROM region from 0xf0000000 to 0x100000000UL.
This seems to happen when __vunmap() is called and triggers a __purge_vmap_area_lazy()
call, where both virtual mappings below 0xf0000000 (kernel modules) and above
0x100000000UL (data mappings) are to be flushed. The kernel at present does not consider
that there might also be a PROM mapping in between, and forces all existing mappings
in the interval determined by __purge_vmap_area_lazy() to be removed.

The flushing of the locked PROM ITLB entry happens in our case already during the boot
process when modules are loaded and unloaded for probing purposes.

With this patch, the affected SunBlade 2000 was able to reboot without problems again.

The patch was originally developed against a 3.13 backport kernel from Debian.
Both the patch against 3.13 and a recent 3.16-rc6 are included below. Please note
that I could only test the 3.13 version as I do not have access to the affected
machine anymore.

PATCH 1 - KERNEL VERSION 3.13 #####################################################

diff -Naupr linux-source-3.13-orig/arch/sparc/include/asm/tlbflush_64.h linux-source-3.13-patched/arch/sparc/include/asm/tlbflush_64.h
--- linux-source-3.13-orig/arch/sparc/include/asm/tlbflush_64.h 2014-04-14 15:48:24.000000000 +0200
+++ linux-source-3.13-patched/arch/sparc/include/asm/tlbflush_64.h      2014-07-27 14:29:58.000000000 +0200
@@ -49,8 +49,20 @@ extern void __flush_tlb_kernel_range(uns
 #ifndef CONFIG_SMP
 
 #define flush_tlb_kernel_range(start,end) \
-do {   flush_tsb_kernel_range(start,end); \
-       __flush_tlb_kernel_range(start,end); \
+do { \
+       if((start < 0x100000000UL) && (end > 0xf0000000)) { \
+               if(start < 0xf0000000) { \
+                         flush_tsb_kernel_range(start, 0xf0000000); \
+                       __flush_tlb_kernel_range(start, 0xf0000000); \
+               } \
+               if(end > 0x100000000UL) { \
+                         flush_tsb_kernel_range(0x100000000UL, end); \
+                       __flush_tlb_kernel_range(0x100000000UL, end); \
+               } \
+       } else { \
+                 flush_tsb_kernel_range(start, end); \
+               __flush_tlb_kernel_range(start, end); \
+       } \
 } while (0)
 
 static inline void global_flush_tlb_page(struct mm_struct *mm, unsigned long vaddr)
@@ -64,8 +76,20 @@ extern void smp_flush_tlb_kernel_range(u
 extern void smp_flush_tlb_page(struct mm_struct *mm, unsigned long vaddr);
 
 #define flush_tlb_kernel_range(start, end) \
-do {   flush_tsb_kernel_range(start,end); \
-       smp_flush_tlb_kernel_range(start, end); \
+do { \
+       if((start < 0x100000000UL) && (end > 0xf0000000)) { \
+               if(start < 0xf0000000) { \
+                           flush_tsb_kernel_range(start, 0xf0000000); \
+                       smp_flush_tlb_kernel_range(start, 0xf0000000); \
+               } \
+               if(end > 0x100000000UL) { \
+                           flush_tsb_kernel_range(0x100000000UL, end); \
+                       smp_flush_tlb_kernel_range(0x100000000UL, end); \
+               } \
+       } else { \
+                   flush_tsb_kernel_range(start, end); \
+               smp_flush_tlb_kernel_range(start, end); \
+       } \
 } while (0)
 
 #define global_flush_tlb_page(mm, vaddr) \

PATCH 2 - KERNEL VERSION 3.16 #####################################################

diff -Naupr linux-3.16-rc6-orig/arch/sparc/include/asm/tlbflush_64.h linux-3.16-rc6-patched/arch/sparc/include/asm/tlbflush_64.h
--- linux-3.16-rc6-orig/arch/sparc/include/asm/tlbflush_64.h    2014-07-27 11:46:05.000000000 +0200
+++ linux-3.16-rc6-patched/arch/sparc/include/asm/tlbflush_64.h 2014-07-27 14:28:12.000000000 +0200
@@ -49,8 +49,20 @@ void __flush_tlb_kernel_range(unsigned l
 #ifndef CONFIG_SMP
 
 #define flush_tlb_kernel_range(start,end) \
-do {   flush_tsb_kernel_range(start,end); \
-       __flush_tlb_kernel_range(start,end); \
+do { \
+       if((start < 0x100000000UL) && (end > 0xf0000000)) { \
+               if(start < 0xf0000000) { \
+                         flush_tsb_kernel_range(start, 0xf0000000); \
+                       __flush_tlb_kernel_range(start, 0xf0000000); \
+               } \
+               if(end > 0x100000000UL) { \
+                         flush_tsb_kernel_range(0x100000000UL, end); \
+                       __flush_tlb_kernel_range(0x100000000UL, end); \
+               } \
+       } else { \
+                 flush_tsb_kernel_range(start, end); \
+               __flush_tlb_kernel_range(start, end); \
+       } \
 } while (0)
 
 static inline void global_flush_tlb_page(struct mm_struct *mm, unsigned long vaddr)
@@ -64,8 +76,20 @@ void smp_flush_tlb_kernel_range(unsigned
 void smp_flush_tlb_page(struct mm_struct *mm, unsigned long vaddr);
 
 #define flush_tlb_kernel_range(start, end) \
-do {   flush_tsb_kernel_range(start,end); \
-       smp_flush_tlb_kernel_range(start, end); \
+do { \
+       if((start < 0x100000000UL) && (end > 0xf0000000)) { \
+               if(start < 0xf0000000) { \
+                           flush_tsb_kernel_range(start, 0xf0000000); \
+                       smp_flush_tlb_kernel_range(start, 0xf0000000); \
+               } \
+               if(end > 0x100000000UL) { \
+                           flush_tsb_kernel_range(0x100000000UL, end); \
+                       smp_flush_tlb_kernel_range(0x100000000UL, end); \
+               } \
+       } else { \
+                   flush_tsb_kernel_range(start, end); \
+               smp_flush_tlb_kernel_range(start, end); \
+       } \
 } while (0)
 
Regards,
Alexander Schulze

--
To unsubscribe from this list: send the line "unsubscribe sparclinux" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Kernel Development]     [DCCP]     [Linux ARM Development]     [Linux]     [Photo]     [Yosemite Help]     [Linux ARM Kernel]     [Linux SCSI]     [Linux x86_64]     [Linux Hams]

  Powered by Linux