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