Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'fix-unmapped-word-at-a-time'

Jana Saout confirmed that this fixes the page faults he saw.

His problem was triggered by ocfs2 and autofs symlink lookups, where the
symlink allocation was at the end of a page. But the deeper reason
seems to be the use of Xen-PV, which is what then causes him to have all
these unmapped pages, which is what then makes it a problem when the
unaligned word-at-a-time code fetches data past the end of a page.

* fix-unmapped-word-at-a-time:
vfs: make word-at-a-time accesses handle a non-existing page

+58 -7
+1 -1
arch/x86/Kconfig
··· 81 81 select CLKEVT_I8253 82 82 select ARCH_HAVE_NMI_SAFE_CMPXCHG 83 83 select GENERIC_IOMAP 84 - select DCACHE_WORD_ACCESS if !DEBUG_PAGEALLOC 84 + select DCACHE_WORD_ACCESS 85 85 86 86 config INSTRUCTION_DECODER 87 87 def_bool (KPROBES || PERF_EVENTS)
+33
arch/x86/include/asm/word-at-a-time.h
··· 43 43 return ((a - REPEAT_BYTE(0x01)) & ~a) & REPEAT_BYTE(0x80); 44 44 } 45 45 46 + /* 47 + * Load an unaligned word from kernel space. 48 + * 49 + * In the (very unlikely) case of the word being a page-crosser 50 + * and the next page not being mapped, take the exception and 51 + * return zeroes in the non-existing part. 52 + */ 53 + static inline unsigned long load_unaligned_zeropad(const void *addr) 54 + { 55 + unsigned long ret, dummy; 56 + 57 + asm( 58 + "1:\tmov %2,%0\n" 59 + "2:\n" 60 + ".section .fixup,\"ax\"\n" 61 + "3:\t" 62 + "lea %2,%1\n\t" 63 + "and %3,%1\n\t" 64 + "mov (%1),%0\n\t" 65 + "leal %2,%%ecx\n\t" 66 + "andl %4,%%ecx\n\t" 67 + "shll $3,%%ecx\n\t" 68 + "shr %%cl,%0\n\t" 69 + "jmp 2b\n" 70 + ".previous\n" 71 + _ASM_EXTABLE(1b, 3b) 72 + :"=&r" (ret),"=&c" (dummy) 73 + :"m" (*(unsigned long *)addr), 74 + "i" (-sizeof(unsigned long)), 75 + "i" (sizeof(unsigned long)-1)); 76 + return ret; 77 + } 78 + 46 79 #endif /* _ASM_WORD_AT_A_TIME_H */
+22 -4
fs/dcache.c
··· 141 141 * Compare 2 name strings, return 0 if they match, otherwise non-zero. 142 142 * The strings are both count bytes long, and count is non-zero. 143 143 */ 144 + #ifdef CONFIG_DCACHE_WORD_ACCESS 145 + 146 + #include <asm/word-at-a-time.h> 147 + /* 148 + * NOTE! 'cs' and 'scount' come from a dentry, so it has a 149 + * aligned allocation for this particular component. We don't 150 + * strictly need the load_unaligned_zeropad() safety, but it 151 + * doesn't hurt either. 152 + * 153 + * In contrast, 'ct' and 'tcount' can be from a pathname, and do 154 + * need the careful unaligned handling. 155 + */ 144 156 static inline int dentry_cmp(const unsigned char *cs, size_t scount, 145 157 const unsigned char *ct, size_t tcount) 146 158 { 147 - #ifdef CONFIG_DCACHE_WORD_ACCESS 148 159 unsigned long a,b,mask; 149 160 150 161 if (unlikely(scount != tcount)) 151 162 return 1; 152 163 153 164 for (;;) { 154 - a = *(unsigned long *)cs; 155 - b = *(unsigned long *)ct; 165 + a = load_unaligned_zeropad(cs); 166 + b = load_unaligned_zeropad(ct); 156 167 if (tcount < sizeof(unsigned long)) 157 168 break; 158 169 if (unlikely(a != b)) ··· 176 165 } 177 166 mask = ~(~0ul << tcount*8); 178 167 return unlikely(!!((a ^ b) & mask)); 168 + } 169 + 179 170 #else 171 + 172 + static inline int dentry_cmp(const unsigned char *cs, size_t scount, 173 + const unsigned char *ct, size_t tcount) 174 + { 180 175 if (scount != tcount) 181 176 return 1; 182 177 ··· 194 177 tcount--; 195 178 } while (tcount); 196 179 return 0; 197 - #endif 198 180 } 181 + 182 + #endif 199 183 200 184 static void __d_free(struct rcu_head *head) 201 185 {
+2 -2
fs/namei.c
··· 1429 1429 unsigned long hash = 0; 1430 1430 1431 1431 for (;;) { 1432 - a = *(unsigned long *)name; 1432 + a = load_unaligned_zeropad(name); 1433 1433 if (len < sizeof(unsigned long)) 1434 1434 break; 1435 1435 hash += a; ··· 1459 1459 do { 1460 1460 hash = (hash + a) * 9; 1461 1461 len += sizeof(unsigned long); 1462 - a = *(unsigned long *)(name+len); 1462 + a = load_unaligned_zeropad(name+len); 1463 1463 /* Do we have any NUL or '/' bytes in this word? */ 1464 1464 mask = has_zero(a) | has_zero(a ^ REPEAT_BYTE('/')); 1465 1465 } while (!mask);