at v5.2-rc7 440 lines 12 kB view raw
1/* 2 * Atomic operations that C can't guarantee us. Useful for 3 * resource counting etc.. 4 * 5 * But use these as seldom as possible since they are much more slower 6 * than regular operations. 7 * 8 * This file is subject to the terms and conditions of the GNU General Public 9 * License. See the file "COPYING" in the main directory of this archive 10 * for more details. 11 * 12 * Copyright (C) 1996, 97, 99, 2000, 03, 04, 06 by Ralf Baechle 13 */ 14#ifndef _ASM_ATOMIC_H 15#define _ASM_ATOMIC_H 16 17#include <linux/irqflags.h> 18#include <linux/types.h> 19#include <asm/barrier.h> 20#include <asm/compiler.h> 21#include <asm/cpu-features.h> 22#include <asm/cmpxchg.h> 23#include <asm/war.h> 24 25/* 26 * Using a branch-likely instruction to check the result of an sc instruction 27 * works around a bug present in R10000 CPUs prior to revision 3.0 that could 28 * cause ll-sc sequences to execute non-atomically. 29 */ 30#if R10000_LLSC_WAR 31# define __scbeqz "beqzl" 32#else 33# define __scbeqz "beqz" 34#endif 35 36#define ATOMIC_INIT(i) { (i) } 37 38/* 39 * atomic_read - read atomic variable 40 * @v: pointer of type atomic_t 41 * 42 * Atomically reads the value of @v. 43 */ 44#define atomic_read(v) READ_ONCE((v)->counter) 45 46/* 47 * atomic_set - set atomic variable 48 * @v: pointer of type atomic_t 49 * @i: required value 50 * 51 * Atomically sets the value of @v to @i. 52 */ 53#define atomic_set(v, i) WRITE_ONCE((v)->counter, (i)) 54 55#define ATOMIC_OP(op, c_op, asm_op) \ 56static __inline__ void atomic_##op(int i, atomic_t * v) \ 57{ \ 58 if (kernel_uses_llsc) { \ 59 int temp; \ 60 \ 61 loongson_llsc_mb(); \ 62 __asm__ __volatile__( \ 63 " .set push \n" \ 64 " .set "MIPS_ISA_LEVEL" \n" \ 65 "1: ll %0, %1 # atomic_" #op " \n" \ 66 " " #asm_op " %0, %2 \n" \ 67 " sc %0, %1 \n" \ 68 "\t" __scbeqz " %0, 1b \n" \ 69 " .set pop \n" \ 70 : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter) \ 71 : "Ir" (i)); \ 72 } else { \ 73 unsigned long flags; \ 74 \ 75 raw_local_irq_save(flags); \ 76 v->counter c_op i; \ 77 raw_local_irq_restore(flags); \ 78 } \ 79} 80 81#define ATOMIC_OP_RETURN(op, c_op, asm_op) \ 82static __inline__ int atomic_##op##_return_relaxed(int i, atomic_t * v) \ 83{ \ 84 int result; \ 85 \ 86 if (kernel_uses_llsc) { \ 87 int temp; \ 88 \ 89 loongson_llsc_mb(); \ 90 __asm__ __volatile__( \ 91 " .set push \n" \ 92 " .set "MIPS_ISA_LEVEL" \n" \ 93 "1: ll %1, %2 # atomic_" #op "_return \n" \ 94 " " #asm_op " %0, %1, %3 \n" \ 95 " sc %0, %2 \n" \ 96 "\t" __scbeqz " %0, 1b \n" \ 97 " " #asm_op " %0, %1, %3 \n" \ 98 " .set pop \n" \ 99 : "=&r" (result), "=&r" (temp), \ 100 "+" GCC_OFF_SMALL_ASM() (v->counter) \ 101 : "Ir" (i)); \ 102 } else { \ 103 unsigned long flags; \ 104 \ 105 raw_local_irq_save(flags); \ 106 result = v->counter; \ 107 result c_op i; \ 108 v->counter = result; \ 109 raw_local_irq_restore(flags); \ 110 } \ 111 \ 112 return result; \ 113} 114 115#define ATOMIC_FETCH_OP(op, c_op, asm_op) \ 116static __inline__ int atomic_fetch_##op##_relaxed(int i, atomic_t * v) \ 117{ \ 118 int result; \ 119 \ 120 if (kernel_uses_llsc) { \ 121 int temp; \ 122 \ 123 loongson_llsc_mb(); \ 124 __asm__ __volatile__( \ 125 " .set push \n" \ 126 " .set "MIPS_ISA_LEVEL" \n" \ 127 "1: ll %1, %2 # atomic_fetch_" #op " \n" \ 128 " " #asm_op " %0, %1, %3 \n" \ 129 " sc %0, %2 \n" \ 130 "\t" __scbeqz " %0, 1b \n" \ 131 " .set pop \n" \ 132 " move %0, %1 \n" \ 133 : "=&r" (result), "=&r" (temp), \ 134 "+" GCC_OFF_SMALL_ASM() (v->counter) \ 135 : "Ir" (i)); \ 136 } else { \ 137 unsigned long flags; \ 138 \ 139 raw_local_irq_save(flags); \ 140 result = v->counter; \ 141 v->counter c_op i; \ 142 raw_local_irq_restore(flags); \ 143 } \ 144 \ 145 return result; \ 146} 147 148#define ATOMIC_OPS(op, c_op, asm_op) \ 149 ATOMIC_OP(op, c_op, asm_op) \ 150 ATOMIC_OP_RETURN(op, c_op, asm_op) \ 151 ATOMIC_FETCH_OP(op, c_op, asm_op) 152 153ATOMIC_OPS(add, +=, addu) 154ATOMIC_OPS(sub, -=, subu) 155 156#define atomic_add_return_relaxed atomic_add_return_relaxed 157#define atomic_sub_return_relaxed atomic_sub_return_relaxed 158#define atomic_fetch_add_relaxed atomic_fetch_add_relaxed 159#define atomic_fetch_sub_relaxed atomic_fetch_sub_relaxed 160 161#undef ATOMIC_OPS 162#define ATOMIC_OPS(op, c_op, asm_op) \ 163 ATOMIC_OP(op, c_op, asm_op) \ 164 ATOMIC_FETCH_OP(op, c_op, asm_op) 165 166ATOMIC_OPS(and, &=, and) 167ATOMIC_OPS(or, |=, or) 168ATOMIC_OPS(xor, ^=, xor) 169 170#define atomic_fetch_and_relaxed atomic_fetch_and_relaxed 171#define atomic_fetch_or_relaxed atomic_fetch_or_relaxed 172#define atomic_fetch_xor_relaxed atomic_fetch_xor_relaxed 173 174#undef ATOMIC_OPS 175#undef ATOMIC_FETCH_OP 176#undef ATOMIC_OP_RETURN 177#undef ATOMIC_OP 178 179/* 180 * atomic_sub_if_positive - conditionally subtract integer from atomic variable 181 * @i: integer value to subtract 182 * @v: pointer of type atomic_t 183 * 184 * Atomically test @v and subtract @i if @v is greater or equal than @i. 185 * The function returns the old value of @v minus @i. 186 */ 187static __inline__ int atomic_sub_if_positive(int i, atomic_t * v) 188{ 189 int result; 190 191 smp_mb__before_llsc(); 192 193 if (kernel_uses_llsc) { 194 int temp; 195 196 __asm__ __volatile__( 197 " .set push \n" 198 " .set "MIPS_ISA_LEVEL" \n" 199 "1: ll %1, %2 # atomic_sub_if_positive\n" 200 " .set pop \n" 201 " subu %0, %1, %3 \n" 202 " move %1, %0 \n" 203 " bltz %0, 1f \n" 204 " .set push \n" 205 " .set "MIPS_ISA_LEVEL" \n" 206 " sc %1, %2 \n" 207 "\t" __scbeqz " %1, 1b \n" 208 "1: \n" 209 " .set pop \n" 210 : "=&r" (result), "=&r" (temp), 211 "+" GCC_OFF_SMALL_ASM() (v->counter) 212 : "Ir" (i)); 213 } else { 214 unsigned long flags; 215 216 raw_local_irq_save(flags); 217 result = v->counter; 218 result -= i; 219 if (result >= 0) 220 v->counter = result; 221 raw_local_irq_restore(flags); 222 } 223 224 smp_llsc_mb(); 225 226 return result; 227} 228 229#define atomic_cmpxchg(v, o, n) (cmpxchg(&((v)->counter), (o), (n))) 230#define atomic_xchg(v, new) (xchg(&((v)->counter), (new))) 231 232/* 233 * atomic_dec_if_positive - decrement by 1 if old value positive 234 * @v: pointer of type atomic_t 235 */ 236#define atomic_dec_if_positive(v) atomic_sub_if_positive(1, v) 237 238#ifdef CONFIG_64BIT 239 240#define ATOMIC64_INIT(i) { (i) } 241 242/* 243 * atomic64_read - read atomic variable 244 * @v: pointer of type atomic64_t 245 * 246 */ 247#define atomic64_read(v) READ_ONCE((v)->counter) 248 249/* 250 * atomic64_set - set atomic variable 251 * @v: pointer of type atomic64_t 252 * @i: required value 253 */ 254#define atomic64_set(v, i) WRITE_ONCE((v)->counter, (i)) 255 256#define ATOMIC64_OP(op, c_op, asm_op) \ 257static __inline__ void atomic64_##op(long i, atomic64_t * v) \ 258{ \ 259 if (kernel_uses_llsc) { \ 260 long temp; \ 261 \ 262 loongson_llsc_mb(); \ 263 __asm__ __volatile__( \ 264 " .set push \n" \ 265 " .set "MIPS_ISA_LEVEL" \n" \ 266 "1: lld %0, %1 # atomic64_" #op " \n" \ 267 " " #asm_op " %0, %2 \n" \ 268 " scd %0, %1 \n" \ 269 "\t" __scbeqz " %0, 1b \n" \ 270 " .set pop \n" \ 271 : "=&r" (temp), "+" GCC_OFF_SMALL_ASM() (v->counter) \ 272 : "Ir" (i)); \ 273 } else { \ 274 unsigned long flags; \ 275 \ 276 raw_local_irq_save(flags); \ 277 v->counter c_op i; \ 278 raw_local_irq_restore(flags); \ 279 } \ 280} 281 282#define ATOMIC64_OP_RETURN(op, c_op, asm_op) \ 283static __inline__ long atomic64_##op##_return_relaxed(long i, atomic64_t * v) \ 284{ \ 285 long result; \ 286 \ 287 if (kernel_uses_llsc) { \ 288 long temp; \ 289 \ 290 loongson_llsc_mb(); \ 291 __asm__ __volatile__( \ 292 " .set push \n" \ 293 " .set "MIPS_ISA_LEVEL" \n" \ 294 "1: lld %1, %2 # atomic64_" #op "_return\n" \ 295 " " #asm_op " %0, %1, %3 \n" \ 296 " scd %0, %2 \n" \ 297 "\t" __scbeqz " %0, 1b \n" \ 298 " " #asm_op " %0, %1, %3 \n" \ 299 " .set pop \n" \ 300 : "=&r" (result), "=&r" (temp), \ 301 "+" GCC_OFF_SMALL_ASM() (v->counter) \ 302 : "Ir" (i)); \ 303 } else { \ 304 unsigned long flags; \ 305 \ 306 raw_local_irq_save(flags); \ 307 result = v->counter; \ 308 result c_op i; \ 309 v->counter = result; \ 310 raw_local_irq_restore(flags); \ 311 } \ 312 \ 313 return result; \ 314} 315 316#define ATOMIC64_FETCH_OP(op, c_op, asm_op) \ 317static __inline__ long atomic64_fetch_##op##_relaxed(long i, atomic64_t * v) \ 318{ \ 319 long result; \ 320 \ 321 if (kernel_uses_llsc) { \ 322 long temp; \ 323 \ 324 loongson_llsc_mb(); \ 325 __asm__ __volatile__( \ 326 " .set push \n" \ 327 " .set "MIPS_ISA_LEVEL" \n" \ 328 "1: lld %1, %2 # atomic64_fetch_" #op "\n" \ 329 " " #asm_op " %0, %1, %3 \n" \ 330 " scd %0, %2 \n" \ 331 "\t" __scbeqz " %0, 1b \n" \ 332 " move %0, %1 \n" \ 333 " .set pop \n" \ 334 : "=&r" (result), "=&r" (temp), \ 335 "+" GCC_OFF_SMALL_ASM() (v->counter) \ 336 : "Ir" (i)); \ 337 } else { \ 338 unsigned long flags; \ 339 \ 340 raw_local_irq_save(flags); \ 341 result = v->counter; \ 342 v->counter c_op i; \ 343 raw_local_irq_restore(flags); \ 344 } \ 345 \ 346 return result; \ 347} 348 349#define ATOMIC64_OPS(op, c_op, asm_op) \ 350 ATOMIC64_OP(op, c_op, asm_op) \ 351 ATOMIC64_OP_RETURN(op, c_op, asm_op) \ 352 ATOMIC64_FETCH_OP(op, c_op, asm_op) 353 354ATOMIC64_OPS(add, +=, daddu) 355ATOMIC64_OPS(sub, -=, dsubu) 356 357#define atomic64_add_return_relaxed atomic64_add_return_relaxed 358#define atomic64_sub_return_relaxed atomic64_sub_return_relaxed 359#define atomic64_fetch_add_relaxed atomic64_fetch_add_relaxed 360#define atomic64_fetch_sub_relaxed atomic64_fetch_sub_relaxed 361 362#undef ATOMIC64_OPS 363#define ATOMIC64_OPS(op, c_op, asm_op) \ 364 ATOMIC64_OP(op, c_op, asm_op) \ 365 ATOMIC64_FETCH_OP(op, c_op, asm_op) 366 367ATOMIC64_OPS(and, &=, and) 368ATOMIC64_OPS(or, |=, or) 369ATOMIC64_OPS(xor, ^=, xor) 370 371#define atomic64_fetch_and_relaxed atomic64_fetch_and_relaxed 372#define atomic64_fetch_or_relaxed atomic64_fetch_or_relaxed 373#define atomic64_fetch_xor_relaxed atomic64_fetch_xor_relaxed 374 375#undef ATOMIC64_OPS 376#undef ATOMIC64_FETCH_OP 377#undef ATOMIC64_OP_RETURN 378#undef ATOMIC64_OP 379 380/* 381 * atomic64_sub_if_positive - conditionally subtract integer from atomic 382 * variable 383 * @i: integer value to subtract 384 * @v: pointer of type atomic64_t 385 * 386 * Atomically test @v and subtract @i if @v is greater or equal than @i. 387 * The function returns the old value of @v minus @i. 388 */ 389static __inline__ long atomic64_sub_if_positive(long i, atomic64_t * v) 390{ 391 long result; 392 393 smp_mb__before_llsc(); 394 395 if (kernel_uses_llsc) { 396 long temp; 397 398 __asm__ __volatile__( 399 " .set push \n" 400 " .set "MIPS_ISA_LEVEL" \n" 401 "1: lld %1, %2 # atomic64_sub_if_positive\n" 402 " dsubu %0, %1, %3 \n" 403 " move %1, %0 \n" 404 " bltz %0, 1f \n" 405 " scd %1, %2 \n" 406 "\t" __scbeqz " %1, 1b \n" 407 "1: \n" 408 " .set pop \n" 409 : "=&r" (result), "=&r" (temp), 410 "+" GCC_OFF_SMALL_ASM() (v->counter) 411 : "Ir" (i)); 412 } else { 413 unsigned long flags; 414 415 raw_local_irq_save(flags); 416 result = v->counter; 417 result -= i; 418 if (result >= 0) 419 v->counter = result; 420 raw_local_irq_restore(flags); 421 } 422 423 smp_llsc_mb(); 424 425 return result; 426} 427 428#define atomic64_cmpxchg(v, o, n) \ 429 ((__typeof__((v)->counter))cmpxchg(&((v)->counter), (o), (n))) 430#define atomic64_xchg(v, new) (xchg(&((v)->counter), (new))) 431 432/* 433 * atomic64_dec_if_positive - decrement by 1 if old value positive 434 * @v: pointer of type atomic64_t 435 */ 436#define atomic64_dec_if_positive(v) atomic64_sub_if_positive(1, v) 437 438#endif /* CONFIG_64BIT */ 439 440#endif /* _ASM_ATOMIC_H */