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 bool  操作成功=>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(
61         lock_ref,
62         {
63             if (old.count <= 0)
64                 return false;
65             ++new.count;
66         },
67         { return true; })
68 
69     bool retval;
70     spin_lock(&lock_ref->lock);
71     retval = false;
72     if (lock_ref->count > 0)
73     {
74         ++lock_ref->count;
75         retval = true;
76     }
77     spin_unlock(&lock_ref->lock);
78     return retval;
79 }
80 
81 /**
82  * @brief 原子地减少引用计数。如果已处于count≤0的状态,则返回-1
83  *
84  * 本函数与lockref_dec_return()的区别在于,当在cmpxchg()中检测到count<=0或已加锁,本函数会再次尝试通过加锁来执行操作
85  * 而后者会直接返回错误
86  *
87  * @param lock_ref 指向要被操作的lockref变量的指针
88  * @return int 操作成功 => 返回新的引用变量值
89  *             lockref处于count≤0的状态 => 返回-1
90  */
lockref_dec(struct lockref * lock_ref)91 int lockref_dec(struct lockref *lock_ref)
92 {
93     CMPXCHG_LOOP(
94         lock_ref,
95         {
96             if (old.count <= 0)
97                 break;
98             --new.count;
99         },
100         { return new.count; })
101 
102     // 如果xchg时,处于已加锁的状态或者检测到old.count <= 0,则采取加锁处理
103     int retval = -1;
104     spin_lock(&lock_ref->lock);
105     if (lock_ref->count > 0)
106     {
107         --lock_ref->count;
108         retval = lock_ref->count;
109     }
110     spin_unlock(&lock_ref->lock);
111 
112     return retval;
113 }
114 
115 /**
116  * @brief 原子地减少引用计数。如果处于已加锁或count≤0的状态,则返回-1
117  *
118  * 本函数与lockref_dec()的区别在于,当在cmpxchg()中检测到count<=0或已加锁,本函数会直接返回错误
119  * 而后者会再次尝试通过加锁来执行操作
120  *
121  * @param lock_ref 指向要被操作的lockref变量的指针
122  * @return int  操作成功 => 返回新的引用变量值
123  *              lockref处于已加锁或count≤0的状态 => 返回-1
124  */
lockref_dec_return(struct lockref * lock_ref)125 int lockref_dec_return(struct lockref *lock_ref)
126 {
127     CMPXCHG_LOOP(
128         lock_ref,
129         {
130             if (old.count <= 0)
131                 return -1;
132             --new.count;
133         },
134         { return new.count; })
135 
136     return -1;
137 }
138 
139 /**
140  * @brief 原子地减少引用计数。若当前的引用计数≤1,则操作失败
141  *
142  * 该函数与lockref_dec_or_lock_not_zero()的区别在于,当cmpxchg()时发现old.count≤1时,该函数会直接返回false.
143  * 而后者在这种情况下,会尝试加锁来进行操作。
144  *
145  * @param lock_ref 指向要被操作的lockref变量的指针
146  * @return true 成功将引用计数减1
147  * @return false 如果当前的引用计数≤1,操作失败
148  */
lockref_dec_not_zero(struct lockref * lock_ref)149 bool lockref_dec_not_zero(struct lockref *lock_ref)
150 {
151     CMPXCHG_LOOP(
152         lock_ref,
153         {
154             if (old.count <= 1)
155                 return false;
156             --new.count;
157         },
158         { return true; })
159 
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 /**
172  * @brief 原子地减少引用计数。若当前的引用计数≤1,则操作失败
173  *
174  * 该函数与lockref_dec_not_zero()的区别在于,当cmpxchg()时发现old.count≤1时,该函数会尝试加锁来进行操作。
175  * 而后者在这种情况下,会直接返回false.
176  *
177  * @param lock_ref 指向要被操作的lockref变量的指针
178  * @return true 成功将引用计数减1
179  * @return false 如果当前的引用计数≤1,操作失败
180  */
lockref_dec_or_lock_not_zero(struct lockref * lock_ref)181 bool lockref_dec_or_lock_not_zero(struct lockref *lock_ref)
182 {
183     CMPXCHG_LOOP(
184         lock_ref,
185         {
186             if (old.count <= 1)
187                 break;
188             --new.count;
189         },
190         { return true; });
191 
192     bool retval = false;
193     spin_lock(&lock_ref->lock);
194     if (lock_ref->count > 1)
195     {
196         --lock_ref->count;
197         retval = true;
198     }
199     spin_unlock(&lock_ref->lock);
200     return retval;
201 }
202 
203 /**
204  * @brief 将lockref变量标记为已经死亡(将count设置为负值)
205  *
206  * @param lock_ref 指向要被操作的lockref变量的指针
207  */
lockref_mark_dead(struct lockref * lock_ref)208 void lockref_mark_dead(struct lockref *lock_ref)
209 {
210     // 需要自旋锁先被加锁,若没有被加锁,则会抛出错误信息
211     assert_spin_locked(&lock_ref->lock);
212     lock_ref->count = -128;
213 }
214 
215 /**
216  * @brief 自增引用计数。(除非该lockref已经死亡)
217  *
218  * @param lock_ref 指向要被操作的lockref变量的指针
219  * @return true 操作成功
220  * @return false 操作失败,lockref已死亡
221  */
lockref_inc_not_dead(struct lockref * lock_ref)222 bool lockref_inc_not_dead(struct lockref *lock_ref)
223 {
224     CMPXCHG_LOOP(
225         lock_ref,
226         {
227             if (old.count < 0)
228                 return false;
229             ++new.count;
230         },
231         { return true; })
232 
233     bool retval = false;
234     // 快捷路径操作失败,尝试加锁
235     spin_lock(&lock_ref->lock);
236     if (lock_ref->count >= 0)
237     {
238         ++lock_ref->count;
239         retval = true;
240     }
241     spin_unlock(&lock_ref->lock);
242     return retval;
243 }
244