atomic64.c 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. /*
  2. * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
  3. *
  4. * License: GNU GPL, version 2 or later.
  5. * See the COPYING file in the top-level directory.
  6. */
  7. #include "qemu/osdep.h"
  8. #include "qemu/atomic.h"
  9. #include "qemu/thread.h"
  10. #include "qemu/cacheinfo.h"
  11. #include "qemu/memalign.h"
  12. #ifdef CONFIG_ATOMIC64
  13. #error This file must only be compiled if !CONFIG_ATOMIC64
  14. #endif
  15. /*
  16. * When !CONFIG_ATOMIC64, we serialize both reads and writes with spinlocks.
  17. * We use an array of spinlocks, with padding computed at run-time based on
  18. * the host's dcache line size.
  19. * We point to the array with a void * to simplify the padding's computation.
  20. * Each spinlock is located every lock_size bytes.
  21. */
  22. static void *lock_array;
  23. static size_t lock_size;
  24. /*
  25. * Systems without CONFIG_ATOMIC64 are unlikely to have many cores, so we use a
  26. * small array of locks.
  27. */
  28. #define NR_LOCKS 16
  29. static QemuSpin *addr_to_lock(const void *addr)
  30. {
  31. uintptr_t a = (uintptr_t)addr;
  32. uintptr_t idx;
  33. idx = a >> qemu_dcache_linesize_log;
  34. idx ^= (idx >> 8) ^ (idx >> 16);
  35. idx &= NR_LOCKS - 1;
  36. return lock_array + idx * lock_size;
  37. }
  38. #define GEN_READ(name, type) \
  39. type name(const type *ptr) \
  40. { \
  41. QemuSpin *lock = addr_to_lock(ptr); \
  42. type ret; \
  43. \
  44. qemu_spin_lock(lock); \
  45. ret = *ptr; \
  46. qemu_spin_unlock(lock); \
  47. return ret; \
  48. }
  49. GEN_READ(qatomic_read_i64, int64_t)
  50. GEN_READ(qatomic_read_u64, uint64_t)
  51. #undef GEN_READ
  52. #define GEN_SET(name, type) \
  53. void name(type *ptr, type val) \
  54. { \
  55. QemuSpin *lock = addr_to_lock(ptr); \
  56. \
  57. qemu_spin_lock(lock); \
  58. *ptr = val; \
  59. qemu_spin_unlock(lock); \
  60. }
  61. GEN_SET(qatomic_set_i64, int64_t)
  62. GEN_SET(qatomic_set_u64, uint64_t)
  63. #undef GEN_SET
  64. void qatomic64_init(void)
  65. {
  66. int i;
  67. lock_size = ROUND_UP(sizeof(QemuSpin), qemu_dcache_linesize);
  68. lock_array = qemu_memalign(qemu_dcache_linesize, lock_size * NR_LOCKS);
  69. for (i = 0; i < NR_LOCKS; i++) {
  70. QemuSpin *lock = lock_array + i * lock_size;
  71. qemu_spin_init(lock);
  72. }
  73. }