bakery_lock_normal.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /*
  2. * Copyright (c) 2015-2020, Arm Limited and Contributors. All rights reserved.
  3. * Copyright (c) 2020, NVIDIA Corporation. All rights reserved.
  4. *
  5. * SPDX-License-Identifier: BSD-3-Clause
  6. */
  7. #include <assert.h>
  8. #include <string.h>
  9. #include <arch_helpers.h>
  10. #include <lib/bakery_lock.h>
  11. #include <lib/el3_runtime/cpu_data.h>
  12. #include <lib/utils_def.h>
  13. #include <plat/common/platform.h>
  14. /*
  15. * Functions in this file implement Bakery Algorithm for mutual exclusion with the
  16. * bakery lock data structures in cacheable and Normal memory.
  17. *
  18. * ARM architecture offers a family of exclusive access instructions to
  19. * efficiently implement mutual exclusion with hardware support. However, as
  20. * well as depending on external hardware, these instructions have defined
  21. * behavior only on certain memory types (cacheable and Normal memory in
  22. * particular; see ARMv8 Architecture Reference Manual section B2.10). Use cases
  23. * in trusted firmware are such that mutual exclusion implementation cannot
  24. * expect that accesses to the lock have the specific type required by the
  25. * architecture for these primitives to function (for example, not all
  26. * contenders may have address translation enabled).
  27. *
  28. * This implementation does not use mutual exclusion primitives. It expects
  29. * memory regions where the locks reside to be cacheable and Normal.
  30. *
  31. * Note that the ARM architecture guarantees single-copy atomicity for aligned
  32. * accesses regardless of status of address translation.
  33. */
  34. #ifdef PLAT_PERCPU_BAKERY_LOCK_SIZE
  35. /*
  36. * Verify that the platform defined value for the per-cpu space for bakery locks is
  37. * a multiple of the cache line size, to prevent multiple CPUs writing to the same
  38. * bakery lock cache line
  39. *
  40. * Using this value, if provided, rather than the linker generated value results in
  41. * more efficient code
  42. */
  43. CASSERT((PLAT_PERCPU_BAKERY_LOCK_SIZE & (CACHE_WRITEBACK_GRANULE - 1)) == 0,
  44. PLAT_PERCPU_BAKERY_LOCK_SIZE_not_cacheline_multiple);
  45. #define PERCPU_BAKERY_LOCK_SIZE (PLAT_PERCPU_BAKERY_LOCK_SIZE)
  46. #else
  47. /*
  48. * Use the linker defined symbol which has evaluated the size reqiurement.
  49. * This is not as efficient as using a platform defined constant
  50. */
  51. IMPORT_SYM(uintptr_t, __PERCPU_BAKERY_LOCK_START__, BAKERY_LOCK_START);
  52. IMPORT_SYM(uintptr_t, __PERCPU_BAKERY_LOCK_END__, BAKERY_LOCK_END);
  53. #define PERCPU_BAKERY_LOCK_SIZE (BAKERY_LOCK_END - BAKERY_LOCK_START)
  54. #endif
  55. static inline bakery_lock_t *get_bakery_info(unsigned int cpu_ix,
  56. bakery_lock_t *lock)
  57. {
  58. return (bakery_info_t *)((uintptr_t)lock +
  59. cpu_ix * PERCPU_BAKERY_LOCK_SIZE);
  60. }
  61. static inline void write_cache_op(uintptr_t addr, bool cached)
  62. {
  63. if (cached)
  64. dccvac(addr);
  65. else
  66. dcivac(addr);
  67. dsbish();
  68. }
  69. static inline void read_cache_op(uintptr_t addr, bool cached)
  70. {
  71. if (cached)
  72. dccivac(addr);
  73. dmbish();
  74. }
  75. /* Helper function to check if the lock is acquired */
  76. static inline __unused bool is_lock_acquired(const bakery_info_t *my_bakery_info,
  77. bool is_cached)
  78. {
  79. /*
  80. * Even though lock data is updated only by the owning cpu and
  81. * appropriate cache maintenance operations are performed,
  82. * if the previous update was done when the cpu was not participating
  83. * in coherency, then there is a chance that cache maintenance
  84. * operations were not propagated to all the caches in the system.
  85. * Hence do a `read_cache_op()` prior to read.
  86. */
  87. read_cache_op((uintptr_t)my_bakery_info, is_cached);
  88. return bakery_ticket_number(my_bakery_info->lock_data) != 0U;
  89. }
  90. static unsigned int bakery_get_ticket(bakery_lock_t *lock,
  91. unsigned int me, bool is_cached)
  92. {
  93. unsigned int my_ticket, their_ticket;
  94. unsigned int they;
  95. bakery_info_t *my_bakery_info, *their_bakery_info;
  96. /*
  97. * Obtain a reference to the bakery information for this cpu and ensure
  98. * it is not NULL.
  99. */
  100. my_bakery_info = get_bakery_info(me, lock);
  101. assert(my_bakery_info != NULL);
  102. /* Prevent recursive acquisition.*/
  103. assert(!is_lock_acquired(my_bakery_info, is_cached));
  104. /*
  105. * Tell other contenders that we are through the bakery doorway i.e.
  106. * going to allocate a ticket for this cpu.
  107. */
  108. my_ticket = 0U;
  109. my_bakery_info->lock_data = make_bakery_data(CHOOSING_TICKET, my_ticket);
  110. write_cache_op((uintptr_t)my_bakery_info, is_cached);
  111. /*
  112. * Iterate through the bakery information of each contender to allocate
  113. * the highest ticket number for this cpu.
  114. */
  115. for (they = 0U; they < BAKERY_LOCK_MAX_CPUS; they++) {
  116. if (me == they)
  117. continue;
  118. /*
  119. * Get a reference to the other contender's bakery info and
  120. * ensure that a stale copy is not read.
  121. */
  122. their_bakery_info = get_bakery_info(they, lock);
  123. assert(their_bakery_info != NULL);
  124. read_cache_op((uintptr_t)their_bakery_info, is_cached);
  125. /*
  126. * Update this cpu's ticket number if a higher ticket number is
  127. * seen
  128. */
  129. their_ticket = bakery_ticket_number(their_bakery_info->lock_data);
  130. if (their_ticket > my_ticket)
  131. my_ticket = their_ticket;
  132. }
  133. /*
  134. * Compute ticket; then signal to other contenders waiting for us to
  135. * finish calculating our ticket value that we're done
  136. */
  137. ++my_ticket;
  138. my_bakery_info->lock_data = make_bakery_data(CHOSEN_TICKET, my_ticket);
  139. write_cache_op((uintptr_t)my_bakery_info, is_cached);
  140. return my_ticket;
  141. }
  142. void bakery_lock_get(bakery_lock_t *lock)
  143. {
  144. unsigned int they, me;
  145. unsigned int my_ticket, my_prio, their_ticket;
  146. bakery_info_t *their_bakery_info;
  147. unsigned int their_bakery_data;
  148. bool is_cached;
  149. me = plat_my_core_pos();
  150. is_cached = is_dcache_enabled();
  151. /* Get a ticket */
  152. my_ticket = bakery_get_ticket(lock, me, is_cached);
  153. /*
  154. * Now that we got our ticket, compute our priority value, then compare
  155. * with that of others, and proceed to acquire the lock
  156. */
  157. my_prio = bakery_get_priority(my_ticket, me);
  158. for (they = 0U; they < BAKERY_LOCK_MAX_CPUS; they++) {
  159. if (me == they)
  160. continue;
  161. /*
  162. * Get a reference to the other contender's bakery info and
  163. * ensure that a stale copy is not read.
  164. */
  165. their_bakery_info = get_bakery_info(they, lock);
  166. assert(their_bakery_info != NULL);
  167. /* Wait for the contender to get their ticket */
  168. do {
  169. read_cache_op((uintptr_t)their_bakery_info, is_cached);
  170. their_bakery_data = their_bakery_info->lock_data;
  171. } while (bakery_is_choosing(their_bakery_data));
  172. /*
  173. * If the other party is a contender, they'll have non-zero
  174. * (valid) ticket value. If they do, compare priorities
  175. */
  176. their_ticket = bakery_ticket_number(their_bakery_data);
  177. if (their_ticket && (bakery_get_priority(their_ticket, they) < my_prio)) {
  178. /*
  179. * They have higher priority (lower value). Wait for
  180. * their ticket value to change (either release the lock
  181. * to have it dropped to 0; or drop and probably content
  182. * again for the same lock to have an even higher value)
  183. */
  184. do {
  185. wfe();
  186. read_cache_op((uintptr_t)their_bakery_info, is_cached);
  187. } while (their_ticket
  188. == bakery_ticket_number(their_bakery_info->lock_data));
  189. }
  190. }
  191. /*
  192. * Lock acquired. Ensure that any reads and writes from a shared
  193. * resource in the critical section read/write values after the lock is
  194. * acquired.
  195. */
  196. dmbish();
  197. }
  198. void bakery_lock_release(bakery_lock_t *lock)
  199. {
  200. bakery_info_t *my_bakery_info;
  201. bool is_cached = is_dcache_enabled();
  202. my_bakery_info = get_bakery_info(plat_my_core_pos(), lock);
  203. assert(is_lock_acquired(my_bakery_info, is_cached));
  204. /*
  205. * Ensure that other observers see any stores in the critical section
  206. * before releasing the lock. Also ensure all loads in the critical
  207. * section are complete before releasing the lock. Release the lock by
  208. * resetting ticket. Then signal other waiting contenders.
  209. */
  210. dmbish();
  211. my_bakery_info->lock_data = 0U;
  212. write_cache_op((uintptr_t)my_bakery_info, is_cached);
  213. /* This sev is ordered by the dsbish in write_cahce_op */
  214. sev();
  215. }