diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-04 10:50:50 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-04 10:50:50 -0400 |
commit | f4622045455faaac958ca35a0cf313f3d6c14d8b (patch) | |
tree | 96455f6f8a1afca46d939e9937913eb21782214b | |
parent | 0a6ba092d1c8c75ae0c0617ef7f4e6f5043af311 (diff) | |
parent | e419b4cc585680940bc42f8ca8a071d6023fb1bb (diff) |
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
-rw-r--r-- | arch/x86/Kconfig | 2 | ||||
-rw-r--r-- | arch/x86/include/asm/word-at-a-time.h | 33 | ||||
-rw-r--r-- | fs/dcache.c | 26 | ||||
-rw-r--r-- | fs/namei.c | 4 |
4 files changed, 58 insertions, 7 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 1d14cc6b79ad..c9866b0b77d8 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -81,7 +81,7 @@ config X86 | |||
81 | select CLKEVT_I8253 | 81 | select CLKEVT_I8253 |
82 | select ARCH_HAVE_NMI_SAFE_CMPXCHG | 82 | select ARCH_HAVE_NMI_SAFE_CMPXCHG |
83 | select GENERIC_IOMAP | 83 | select GENERIC_IOMAP |
84 | select DCACHE_WORD_ACCESS if !DEBUG_PAGEALLOC | 84 | select DCACHE_WORD_ACCESS |
85 | 85 | ||
86 | config INSTRUCTION_DECODER | 86 | config INSTRUCTION_DECODER |
87 | def_bool (KPROBES || PERF_EVENTS) | 87 | def_bool (KPROBES || PERF_EVENTS) |
diff --git a/arch/x86/include/asm/word-at-a-time.h b/arch/x86/include/asm/word-at-a-time.h index 6fe6767b7124..e58f03b206c3 100644 --- a/arch/x86/include/asm/word-at-a-time.h +++ b/arch/x86/include/asm/word-at-a-time.h | |||
@@ -43,4 +43,37 @@ static inline unsigned long has_zero(unsigned long a) | |||
43 | return ((a - REPEAT_BYTE(0x01)) & ~a) & REPEAT_BYTE(0x80); | 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 | #endif /* _ASM_WORD_AT_A_TIME_H */ | 79 | #endif /* _ASM_WORD_AT_A_TIME_H */ |
diff --git a/fs/dcache.c b/fs/dcache.c index b60ddc41d783..b80531c91779 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -141,18 +141,29 @@ int proc_nr_dentry(ctl_table *table, int write, void __user *buffer, | |||
141 | * Compare 2 name strings, return 0 if they match, otherwise non-zero. | 141 | * Compare 2 name strings, return 0 if they match, otherwise non-zero. |
142 | * The strings are both count bytes long, and count is non-zero. | 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 | static inline int dentry_cmp(const unsigned char *cs, size_t scount, | 156 | static inline int dentry_cmp(const unsigned char *cs, size_t scount, |
145 | const unsigned char *ct, size_t tcount) | 157 | const unsigned char *ct, size_t tcount) |
146 | { | 158 | { |
147 | #ifdef CONFIG_DCACHE_WORD_ACCESS | ||
148 | unsigned long a,b,mask; | 159 | unsigned long a,b,mask; |
149 | 160 | ||
150 | if (unlikely(scount != tcount)) | 161 | if (unlikely(scount != tcount)) |
151 | return 1; | 162 | return 1; |
152 | 163 | ||
153 | for (;;) { | 164 | for (;;) { |
154 | a = *(unsigned long *)cs; | 165 | a = load_unaligned_zeropad(cs); |
155 | b = *(unsigned long *)ct; | 166 | b = load_unaligned_zeropad(ct); |
156 | if (tcount < sizeof(unsigned long)) | 167 | if (tcount < sizeof(unsigned long)) |
157 | break; | 168 | break; |
158 | if (unlikely(a != b)) | 169 | if (unlikely(a != b)) |
@@ -165,7 +176,13 @@ static inline int dentry_cmp(const unsigned char *cs, size_t scount, | |||
165 | } | 176 | } |
166 | mask = ~(~0ul << tcount*8); | 177 | mask = ~(~0ul << tcount*8); |
167 | return unlikely(!!((a ^ b) & mask)); | 178 | return unlikely(!!((a ^ b) & mask)); |
179 | } | ||
180 | |||
168 | #else | 181 | #else |
182 | |||
183 | static inline int dentry_cmp(const unsigned char *cs, size_t scount, | ||
184 | const unsigned char *ct, size_t tcount) | ||
185 | { | ||
169 | if (scount != tcount) | 186 | if (scount != tcount) |
170 | return 1; | 187 | return 1; |
171 | 188 | ||
@@ -177,9 +194,10 @@ static inline int dentry_cmp(const unsigned char *cs, size_t scount, | |||
177 | tcount--; | 194 | tcount--; |
178 | } while (tcount); | 195 | } while (tcount); |
179 | return 0; | 196 | return 0; |
180 | #endif | ||
181 | } | 197 | } |
182 | 198 | ||
199 | #endif | ||
200 | |||
183 | static void __d_free(struct rcu_head *head) | 201 | static void __d_free(struct rcu_head *head) |
184 | { | 202 | { |
185 | struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu); | 203 | struct dentry *dentry = container_of(head, struct dentry, d_u.d_rcu); |
diff --git a/fs/namei.c b/fs/namei.c index 0062dd17eb55..c42791914f82 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -1429,7 +1429,7 @@ unsigned int full_name_hash(const unsigned char *name, unsigned int len) | |||
1429 | unsigned long hash = 0; | 1429 | unsigned long hash = 0; |
1430 | 1430 | ||
1431 | for (;;) { | 1431 | for (;;) { |
1432 | a = *(unsigned long *)name; | 1432 | a = load_unaligned_zeropad(name); |
1433 | if (len < sizeof(unsigned long)) | 1433 | if (len < sizeof(unsigned long)) |
1434 | break; | 1434 | break; |
1435 | hash += a; | 1435 | hash += a; |
@@ -1459,7 +1459,7 @@ static inline unsigned long hash_name(const char *name, unsigned int *hashp) | |||
1459 | do { | 1459 | do { |
1460 | hash = (hash + a) * 9; | 1460 | hash = (hash + a) * 9; |
1461 | len += sizeof(unsigned long); | 1461 | len += sizeof(unsigned long); |
1462 | a = *(unsigned long *)(name+len); | 1462 | a = load_unaligned_zeropad(name+len); |
1463 | /* Do we have any NUL or '/' bytes in this word? */ | 1463 | /* Do we have any NUL or '/' bytes in this word? */ |
1464 | mask = has_zero(a) | has_zero(a ^ REPEAT_BYTE('/')); | 1464 | mask = has_zero(a) | has_zero(a ^ REPEAT_BYTE('/')); |
1465 | } while (!mask); | 1465 | } while (!mask); |