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

x86: Adjust asm constraints in atomic64 wrappers

Eric pointed out overly restrictive constraints in atomic64_set(), but
there are issues throughout the file. In the cited case, %ebx and %ecx
are inputs only (don't get changed by either of the two low level
implementations). This was also the case elsewhere.

Further in many cases early-clobber indicators were missing.

Finally, the previous implementation rolled a custom alternative
instruction macro from scratch, rather than using alternative_call()
(which was introduced with the commit that the description of the
change in question actually refers to). Adjusting has the benefit of
not hiding referenced symbols from the compiler, which however requires
them to be declared not just in the exporting source file (which, as a
desirable side effect, in turn allows that exporting file to become a
real 5-line stub).

This patch does not eliminate the overly restrictive memory clobbers,
however: Doing so would occasionally make the compiler set up a second
register for accessing the memory object (to satisfy the added "m"
constraint), and it's not clear which of the two non-optimal
alternatives is better.

v2: Re-do the declaration and exporting of the internal symbols.

Reported-by: Eric Dumazet <eric.dumazet@gmail.com>
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Link: http://lkml.kernel.org/r/4F19A2A5020000780006E0D9@nat28.tlf.novell.com
Cc: Luca Barbieri <luca@luca-barbieri.com>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>

authored by

Jan Beulich and committed by
H. Peter Anvin
819165fb dcd6c922

+89 -125
+6
arch/x86/include/asm/alternative.h
··· 145 145 */ 146 146 #define ASM_OUTPUT2(a...) a 147 147 148 + /* 149 + * use this macro if you need clobbers but no inputs in 150 + * alternative_{input,io,call}() 151 + */ 152 + #define ASM_NO_INPUT_CLOBBER(clbr...) "i" (0) : clbr 153 + 148 154 struct paravirt_patch_site; 149 155 #ifdef CONFIG_PARAVIRT 150 156 void apply_paravirt(struct paravirt_patch_site *start,
+81 -68
arch/x86/include/asm/atomic64_32.h
··· 14 14 15 15 #define ATOMIC64_INIT(val) { (val) } 16 16 17 - #ifdef CONFIG_X86_CMPXCHG64 18 - #define ATOMIC64_ALTERNATIVE_(f, g) "call atomic64_" #g "_cx8" 17 + #define __ATOMIC64_DECL(sym) void atomic64_##sym(atomic64_t *, ...) 18 + #ifndef ATOMIC64_EXPORT 19 + #define ATOMIC64_DECL_ONE __ATOMIC64_DECL 19 20 #else 20 - #define ATOMIC64_ALTERNATIVE_(f, g) ALTERNATIVE("call atomic64_" #f "_386", "call atomic64_" #g "_cx8", X86_FEATURE_CX8) 21 + #define ATOMIC64_DECL_ONE(sym) __ATOMIC64_DECL(sym); \ 22 + ATOMIC64_EXPORT(atomic64_##sym) 21 23 #endif 22 24 23 - #define ATOMIC64_ALTERNATIVE(f) ATOMIC64_ALTERNATIVE_(f, f) 25 + #ifdef CONFIG_X86_CMPXCHG64 26 + #define __alternative_atomic64(f, g, out, in...) \ 27 + asm volatile("call %P[func]" \ 28 + : out : [func] "i" (atomic64_##g##_cx8), ## in) 29 + 30 + #define ATOMIC64_DECL(sym) ATOMIC64_DECL_ONE(sym##_cx8) 31 + #else 32 + #define __alternative_atomic64(f, g, out, in...) \ 33 + alternative_call(atomic64_##f##_386, atomic64_##g##_cx8, \ 34 + X86_FEATURE_CX8, ASM_OUTPUT2(out), ## in) 35 + 36 + #define ATOMIC64_DECL(sym) ATOMIC64_DECL_ONE(sym##_cx8); \ 37 + ATOMIC64_DECL_ONE(sym##_386) 38 + 39 + ATOMIC64_DECL_ONE(add_386); 40 + ATOMIC64_DECL_ONE(sub_386); 41 + ATOMIC64_DECL_ONE(inc_386); 42 + ATOMIC64_DECL_ONE(dec_386); 43 + #endif 44 + 45 + #define alternative_atomic64(f, out, in...) \ 46 + __alternative_atomic64(f, f, ASM_OUTPUT2(out), ## in) 47 + 48 + ATOMIC64_DECL(read); 49 + ATOMIC64_DECL(set); 50 + ATOMIC64_DECL(xchg); 51 + ATOMIC64_DECL(add_return); 52 + ATOMIC64_DECL(sub_return); 53 + ATOMIC64_DECL(inc_return); 54 + ATOMIC64_DECL(dec_return); 55 + ATOMIC64_DECL(dec_if_positive); 56 + ATOMIC64_DECL(inc_not_zero); 57 + ATOMIC64_DECL(add_unless); 58 + 59 + #undef ATOMIC64_DECL 60 + #undef ATOMIC64_DECL_ONE 61 + #undef __ATOMIC64_DECL 62 + #undef ATOMIC64_EXPORT 24 63 25 64 /** 26 65 * atomic64_cmpxchg - cmpxchg atomic64 variable ··· 89 50 long long o; 90 51 unsigned high = (unsigned)(n >> 32); 91 52 unsigned low = (unsigned)n; 92 - asm volatile(ATOMIC64_ALTERNATIVE(xchg) 93 - : "=A" (o), "+b" (low), "+c" (high) 94 - : "S" (v) 95 - : "memory" 96 - ); 53 + alternative_atomic64(xchg, "=&A" (o), 54 + "S" (v), "b" (low), "c" (high) 55 + : "memory"); 97 56 return o; 98 57 } 99 58 ··· 106 69 { 107 70 unsigned high = (unsigned)(i >> 32); 108 71 unsigned low = (unsigned)i; 109 - asm volatile(ATOMIC64_ALTERNATIVE(set) 110 - : "+b" (low), "+c" (high) 111 - : "S" (v) 112 - : "eax", "edx", "memory" 113 - ); 72 + alternative_atomic64(set, /* no output */, 73 + "S" (v), "b" (low), "c" (high) 74 + : "eax", "edx", "memory"); 114 75 } 115 76 116 77 /** ··· 120 85 static inline long long atomic64_read(const atomic64_t *v) 121 86 { 122 87 long long r; 123 - asm volatile(ATOMIC64_ALTERNATIVE(read) 124 - : "=A" (r), "+c" (v) 125 - : : "memory" 126 - ); 88 + alternative_atomic64(read, "=&A" (r), "c" (v) : "memory"); 127 89 return r; 128 90 } 129 91 ··· 133 101 */ 134 102 static inline long long atomic64_add_return(long long i, atomic64_t *v) 135 103 { 136 - asm volatile(ATOMIC64_ALTERNATIVE(add_return) 137 - : "+A" (i), "+c" (v) 138 - : : "memory" 139 - ); 104 + alternative_atomic64(add_return, 105 + ASM_OUTPUT2("+A" (i), "+c" (v)), 106 + ASM_NO_INPUT_CLOBBER("memory")); 140 107 return i; 141 108 } 142 109 ··· 144 113 */ 145 114 static inline long long atomic64_sub_return(long long i, atomic64_t *v) 146 115 { 147 - asm volatile(ATOMIC64_ALTERNATIVE(sub_return) 148 - : "+A" (i), "+c" (v) 149 - : : "memory" 150 - ); 116 + alternative_atomic64(sub_return, 117 + ASM_OUTPUT2("+A" (i), "+c" (v)), 118 + ASM_NO_INPUT_CLOBBER("memory")); 151 119 return i; 152 120 } 153 121 154 122 static inline long long atomic64_inc_return(atomic64_t *v) 155 123 { 156 124 long long a; 157 - asm volatile(ATOMIC64_ALTERNATIVE(inc_return) 158 - : "=A" (a) 159 - : "S" (v) 160 - : "memory", "ecx" 161 - ); 125 + alternative_atomic64(inc_return, "=&A" (a), 126 + "S" (v) : "memory", "ecx"); 162 127 return a; 163 128 } 164 129 165 130 static inline long long atomic64_dec_return(atomic64_t *v) 166 131 { 167 132 long long a; 168 - asm volatile(ATOMIC64_ALTERNATIVE(dec_return) 169 - : "=A" (a) 170 - : "S" (v) 171 - : "memory", "ecx" 172 - ); 133 + alternative_atomic64(dec_return, "=&A" (a), 134 + "S" (v) : "memory", "ecx"); 173 135 return a; 174 136 } 175 137 ··· 175 151 */ 176 152 static inline long long atomic64_add(long long i, atomic64_t *v) 177 153 { 178 - asm volatile(ATOMIC64_ALTERNATIVE_(add, add_return) 179 - : "+A" (i), "+c" (v) 180 - : : "memory" 181 - ); 154 + __alternative_atomic64(add, add_return, 155 + ASM_OUTPUT2("+A" (i), "+c" (v)), 156 + ASM_NO_INPUT_CLOBBER("memory")); 182 157 return i; 183 158 } 184 159 ··· 190 167 */ 191 168 static inline long long atomic64_sub(long long i, atomic64_t *v) 192 169 { 193 - asm volatile(ATOMIC64_ALTERNATIVE_(sub, sub_return) 194 - : "+A" (i), "+c" (v) 195 - : : "memory" 196 - ); 170 + __alternative_atomic64(sub, sub_return, 171 + ASM_OUTPUT2("+A" (i), "+c" (v)), 172 + ASM_NO_INPUT_CLOBBER("memory")); 197 173 return i; 198 174 } 199 175 ··· 218 196 */ 219 197 static inline void atomic64_inc(atomic64_t *v) 220 198 { 221 - asm volatile(ATOMIC64_ALTERNATIVE_(inc, inc_return) 222 - : : "S" (v) 223 - : "memory", "eax", "ecx", "edx" 224 - ); 199 + __alternative_atomic64(inc, inc_return, /* no output */, 200 + "S" (v) : "memory", "eax", "ecx", "edx"); 225 201 } 226 202 227 203 /** ··· 230 210 */ 231 211 static inline void atomic64_dec(atomic64_t *v) 232 212 { 233 - asm volatile(ATOMIC64_ALTERNATIVE_(dec, dec_return) 234 - : : "S" (v) 235 - : "memory", "eax", "ecx", "edx" 236 - ); 213 + __alternative_atomic64(dec, dec_return, /* no output */, 214 + "S" (v) : "memory", "eax", "ecx", "edx"); 237 215 } 238 216 239 217 /** ··· 281 263 * @u: ...unless v is equal to u. 282 264 * 283 265 * Atomically adds @a to @v, so long as it was not @u. 284 - * Returns the old value of @v. 266 + * Returns non-zero if the add was done, zero otherwise. 285 267 */ 286 268 static inline int atomic64_add_unless(atomic64_t *v, long long a, long long u) 287 269 { 288 270 unsigned low = (unsigned)u; 289 271 unsigned high = (unsigned)(u >> 32); 290 - asm volatile(ATOMIC64_ALTERNATIVE(add_unless) "\n\t" 291 - : "+A" (a), "+c" (v), "+S" (low), "+D" (high) 292 - : : "memory"); 272 + alternative_atomic64(add_unless, 273 + ASM_OUTPUT2("+A" (a), "+c" (v), 274 + "+S" (low), "+D" (high)), 275 + ASM_NO_INPUT_CLOBBER("memory")); 293 276 return (int)a; 294 277 } 295 278 ··· 298 279 static inline int atomic64_inc_not_zero(atomic64_t *v) 299 280 { 300 281 int r; 301 - asm volatile(ATOMIC64_ALTERNATIVE(inc_not_zero) 302 - : "=a" (r) 303 - : "S" (v) 304 - : "ecx", "edx", "memory" 305 - ); 282 + alternative_atomic64(inc_not_zero, "=&a" (r), 283 + "S" (v) : "ecx", "edx", "memory"); 306 284 return r; 307 285 } 308 286 309 287 static inline long long atomic64_dec_if_positive(atomic64_t *v) 310 288 { 311 289 long long r; 312 - asm volatile(ATOMIC64_ALTERNATIVE(dec_if_positive) 313 - : "=A" (r) 314 - : "S" (v) 315 - : "ecx", "memory" 316 - ); 290 + alternative_atomic64(dec_if_positive, "=&A" (r), 291 + "S" (v) : "ecx", "memory"); 317 292 return r; 318 293 } 319 294 320 - #undef ATOMIC64_ALTERNATIVE 321 - #undef ATOMIC64_ALTERNATIVE_ 295 + #undef alternative_atomic64 296 + #undef __alternative_atomic64 322 297 323 298 #endif /* _ASM_X86_ATOMIC64_32_H */
+2 -57
arch/x86/lib/atomic64_32.c
··· 1 - #include <linux/compiler.h> 2 - #include <linux/module.h> 3 - #include <linux/types.h> 1 + #define ATOMIC64_EXPORT EXPORT_SYMBOL 4 2 5 - #include <asm/processor.h> 6 - #include <asm/cmpxchg.h> 3 + #include <linux/export.h> 7 4 #include <linux/atomic.h> 8 - 9 - long long atomic64_read_cx8(long long, const atomic64_t *v); 10 - EXPORT_SYMBOL(atomic64_read_cx8); 11 - long long atomic64_set_cx8(long long, const atomic64_t *v); 12 - EXPORT_SYMBOL(atomic64_set_cx8); 13 - long long atomic64_xchg_cx8(long long, unsigned high); 14 - EXPORT_SYMBOL(atomic64_xchg_cx8); 15 - long long atomic64_add_return_cx8(long long a, atomic64_t *v); 16 - EXPORT_SYMBOL(atomic64_add_return_cx8); 17 - long long atomic64_sub_return_cx8(long long a, atomic64_t *v); 18 - EXPORT_SYMBOL(atomic64_sub_return_cx8); 19 - long long atomic64_inc_return_cx8(long long a, atomic64_t *v); 20 - EXPORT_SYMBOL(atomic64_inc_return_cx8); 21 - long long atomic64_dec_return_cx8(long long a, atomic64_t *v); 22 - EXPORT_SYMBOL(atomic64_dec_return_cx8); 23 - long long atomic64_dec_if_positive_cx8(atomic64_t *v); 24 - EXPORT_SYMBOL(atomic64_dec_if_positive_cx8); 25 - int atomic64_inc_not_zero_cx8(atomic64_t *v); 26 - EXPORT_SYMBOL(atomic64_inc_not_zero_cx8); 27 - int atomic64_add_unless_cx8(atomic64_t *v, long long a, long long u); 28 - EXPORT_SYMBOL(atomic64_add_unless_cx8); 29 - 30 - #ifndef CONFIG_X86_CMPXCHG64 31 - long long atomic64_read_386(long long, const atomic64_t *v); 32 - EXPORT_SYMBOL(atomic64_read_386); 33 - long long atomic64_set_386(long long, const atomic64_t *v); 34 - EXPORT_SYMBOL(atomic64_set_386); 35 - long long atomic64_xchg_386(long long, unsigned high); 36 - EXPORT_SYMBOL(atomic64_xchg_386); 37 - long long atomic64_add_return_386(long long a, atomic64_t *v); 38 - EXPORT_SYMBOL(atomic64_add_return_386); 39 - long long atomic64_sub_return_386(long long a, atomic64_t *v); 40 - EXPORT_SYMBOL(atomic64_sub_return_386); 41 - long long atomic64_inc_return_386(long long a, atomic64_t *v); 42 - EXPORT_SYMBOL(atomic64_inc_return_386); 43 - long long atomic64_dec_return_386(long long a, atomic64_t *v); 44 - EXPORT_SYMBOL(atomic64_dec_return_386); 45 - long long atomic64_add_386(long long a, atomic64_t *v); 46 - EXPORT_SYMBOL(atomic64_add_386); 47 - long long atomic64_sub_386(long long a, atomic64_t *v); 48 - EXPORT_SYMBOL(atomic64_sub_386); 49 - long long atomic64_inc_386(long long a, atomic64_t *v); 50 - EXPORT_SYMBOL(atomic64_inc_386); 51 - long long atomic64_dec_386(long long a, atomic64_t *v); 52 - EXPORT_SYMBOL(atomic64_dec_386); 53 - long long atomic64_dec_if_positive_386(atomic64_t *v); 54 - EXPORT_SYMBOL(atomic64_dec_if_positive_386); 55 - int atomic64_inc_not_zero_386(atomic64_t *v); 56 - EXPORT_SYMBOL(atomic64_inc_not_zero_386); 57 - int atomic64_add_unless_386(atomic64_t *v, long long a, long long u); 58 - EXPORT_SYMBOL(atomic64_add_unless_386); 59 - #endif