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