lockref.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. #include <common/lockref.h>
  2. #include <common/compiler.h>
  3. #ifdef __LOCKREF_ENABLE_CMPXCHG__
  4. #include <arch/x86_64/asm/cmpxchg.h>
  5. #define CMPXCHG_LOOP(__lock_ref, CODE, SUCCESS) \
  6. { \
  7. int retry = 100; \
  8. struct lockref old; \
  9. BUILD_BUG_ON(sizeof(old) != sizeof(uint64_t)); \
  10. old.lock_count = READ_ONCE(__lock_ref->lock_count); \
  11. while (likely(!spin_is_locked(&old.lock))) \
  12. { \
  13. struct lockref new = old; \
  14. CODE; \
  15. if (likely(arch_try_cmpxchg(&__lock_ref->lock_count, &old.lock_count, new.lock_count))) \
  16. { \
  17. SUCCESS; \
  18. } \
  19. if (!--retry) \
  20. break; \
  21. pause(); \
  22. } \
  23. }
  24. #else
  25. #define CMPXCHG_LOOP(__lock_ref, CODE, SUCCESS) \
  26. do \
  27. { \
  28. } while (0)
  29. #endif
  30. /**
  31. * @brief 原子的将引用计数加1
  32. *
  33. * @param lock_ref 指向要被操作的lockref变量的指针
  34. */
  35. void lockref_inc(struct lockref *lock_ref)
  36. {
  37. // 先尝试使用cmpxchg进行无锁操作,若成功则返回
  38. CMPXCHG_LOOP(lock_ref, ++new.count;, return;);
  39. // 无锁操作超时,或当前是上锁的状态,则退化为有锁操作
  40. spin_lock(&lock_ref->lock);
  41. ++lock_ref->count;
  42. spin_unlock(&lock_ref->lock);
  43. }
  44. /**
  45. * @brief 原子地将引用计数加1.如果原来的count≤0,则操作失败。
  46. *
  47. * @param lock_ref 指向要被操作的lockref变量的指针
  48. * @return int 操作成功=>true
  49. * 操作失败=>false
  50. */
  51. bool lockref_inc_not_zero(struct lockref *lock_ref)
  52. {
  53. CMPXCHG_LOOP(lock_ref,
  54. if (old.count <= 0) return false;
  55. ++new.count;
  56. ,
  57. return true;)
  58. bool retval;
  59. spin_lock(&lock_ref->lock);
  60. retval = false;
  61. if (lock_ref->count > 0)
  62. {
  63. ++lock_ref->count;
  64. retval = true;
  65. }
  66. spin_unlock(&lock_ref->lock);
  67. return retval;
  68. }
  69. /**
  70. * @brief 原子地减少引用计数。如果已处于count≤0的状态,则返回-1
  71. *
  72. * 本函数与lockref_dec_return()的区别在于,当在cmpxchg()中检测到count<=0或已加锁,本函数会再次尝试通过加锁来执行操作
  73. * 而后者会直接返回错误
  74. *
  75. * @param lock_ref 指向要被操作的lockref变量的指针
  76. * @return int 操作成功 => 返回新的引用变量值
  77. * lockref处于count≤0的状态 => 返回-1
  78. */
  79. int lockref_dec(struct lockref *lock_ref)
  80. {
  81. CMPXCHG_LOOP(lock_ref,
  82. if (old.count <= 0) break;
  83. --new.count;
  84. ,
  85. return new.count;);
  86. // 如果xchg时,处于已加锁的状态或者检测到old.count <= 0,则采取加锁处理
  87. int retval = -1;
  88. spin_lock(&lock_ref->lock);
  89. if (lock_ref->count > 0)
  90. {
  91. --lock_ref->count;
  92. retval = lock_ref->count;
  93. }
  94. spin_unlock(&lock_ref->lock);
  95. return retval;
  96. }
  97. /**
  98. * @brief 原子地减少引用计数。如果处于已加锁或count≤0的状态,则返回-1
  99. *
  100. * 本函数与lockref_dec()的区别在于,当在cmpxchg()中检测到count<=0或已加锁,本函数会直接返回错误
  101. * 而后者会再次尝试通过加锁来执行操作
  102. *
  103. * @param lock_ref 指向要被操作的lockref变量的指针
  104. * @return int 操作成功 => 返回新的引用变量值
  105. * lockref处于已加锁或count≤0的状态 => 返回-1
  106. */
  107. int lockref_dec_return(struct lockref *lock_ref)
  108. {
  109. CMPXCHG_LOOP(lock_ref,
  110. if (old.count <= 0) return -1;
  111. --new.count;
  112. ,
  113. return new.count;);
  114. return -1;
  115. }
  116. /**
  117. * @brief 原子地减少引用计数。若当前的引用计数≤1,则操作失败
  118. *
  119. * 该函数与lockref_dec_or_lock_not_zero()的区别在于,当cmpxchg()时发现old.count≤1时,该函数会直接返回false.
  120. * 而后者在这种情况下,会尝试加锁来进行操作。
  121. *
  122. * @param lock_ref 指向要被操作的lockref变量的指针
  123. * @return true 成功将引用计数减1
  124. * @return false 如果当前的引用计数≤1,操作失败
  125. */
  126. bool lockref_dec_not_zero(struct lockref *lock_ref)
  127. {
  128. CMPXCHG_LOOP(lock_ref,
  129. if (old.count <= 1) return false;
  130. --new.count;
  131. ,
  132. return true;)
  133. bool retval = false;
  134. spin_lock(&lock_ref->lock);
  135. if (lock_ref->count > 1)
  136. {
  137. --lock_ref->count;
  138. retval = true;
  139. }
  140. spin_unlock(&lock_ref->lock);
  141. return retval;
  142. }
  143. /**
  144. * @brief 原子地减少引用计数。若当前的引用计数≤1,则操作失败
  145. *
  146. * 该函数与lockref_dec_not_zero()的区别在于,当cmpxchg()时发现old.count≤1时,该函数会尝试加锁来进行操作。
  147. * 而后者在这种情况下,会直接返回false.
  148. *
  149. * @param lock_ref 指向要被操作的lockref变量的指针
  150. * @return true 成功将引用计数减1
  151. * @return false 如果当前的引用计数≤1,操作失败
  152. */
  153. bool lockref_dec_or_lock_not_zero(struct lockref *lock_ref)
  154. {
  155. CMPXCHG_LOOP(lock_ref,
  156. if (old.count <= 1) break;
  157. --new.count;
  158. ,
  159. return true;);
  160. bool retval = false;
  161. spin_lock(&lock_ref->lock);
  162. if (lock_ref->count > 1)
  163. {
  164. --lock_ref->count;
  165. retval = true;
  166. }
  167. spin_unlock(&lock_ref->lock);
  168. return retval;
  169. }
  170. /**
  171. * @brief 将lockref变量标记为已经死亡(将count设置为负值)
  172. *
  173. * @param lock_ref 指向要被操作的lockref变量的指针
  174. */
  175. void lockref_mark_dead(struct lockref *lock_ref)
  176. {
  177. // 需要自旋锁先被加锁,若没有被加锁,则会抛出错误信息
  178. assert_spin_locked(&lock_ref->lock);
  179. lock_ref->count = -128;
  180. }
  181. /**
  182. * @brief 自增引用计数。(除非该lockref已经死亡)
  183. *
  184. * @param lock_ref 指向要被操作的lockref变量的指针
  185. * @return true 操作成功
  186. * @return false 操作失败,lockref已死亡
  187. */
  188. bool lockref_inc_not_dead(struct lockref *lock_ref)
  189. {
  190. CMPXCHG_LOOP(lock_ref,
  191. if (old.count < 0) return false;
  192. ++new.count;
  193. ,
  194. return true;)
  195. bool retval = false;
  196. // 快捷路径操作失败,尝试加锁
  197. spin_lock(&lock_ref->lock);
  198. if (lock_ref->count >= 0)
  199. {
  200. ++lock_ref->count;
  201. retval = true;
  202. }
  203. spin_unlock(&lock_ref->lock);
  204. return retval;
  205. }