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