1 #include "common/completion.h"
2 #include "common/kthread.h"
3
4 /**
5 * @brief 初始化一个completion变量
6 *
7 * @param x completion
8 */
completion_init(struct completion * x)9 void completion_init(struct completion *x)
10 {
11 x->done = 0;
12 wait_queue_head_init(&x->wait_queue);
13 }
14
15 /**
16 * @brief 唤醒一个wait_queue中的节点
17 *
18 * @param x completion
19 */
complete(struct completion * x)20 void complete(struct completion *x)
21 {
22
23 spin_lock(&x->wait_queue.lock);
24
25 if (x->done != COMPLETE_ALL)
26 ++(x->done);
27 wait_queue_wakeup_on_stack(&x->wait_queue, -1UL); // -1UL代表所有节点都满足条件,暂时这么写
28
29 spin_unlock(&x->wait_queue.lock);
30 }
31
32 /**
33 * @brief 永久标记done为Complete_All, 并从wait_queue中删除所有节点
34 *
35 * @param x completion
36 */
complete_all(struct completion * x)37 void complete_all(struct completion *x)
38 {
39 spin_lock(&x->wait_queue.lock);
40
41 x->done = COMPLETE_ALL; // 永久赋值
42 while (!list_empty(&x->wait_queue.wait_list))
43 wait_queue_wakeup_on_stack(&x->wait_queue, -1UL); // -1UL代表所有节点都满足条件,暂时这么写
44
45 spin_unlock(&x->wait_queue.lock);
46 }
47
48 /**
49 * @brief 辅助函数:通用的处理wait命令的函数(即所有wait_for_completion函数最核心部分在这里)
50 *
51 * @param x completion
52 * @param action 函数指针
53 * @param timeout 一个非负整数
54 * @param state 你要设置进程的状态为state
55 * @return long - 返回剩余的timeout
56 */
__wait_for_common(struct completion * x,long (* action)(long),long timeout,int state)57 static long __wait_for_common(struct completion *x, long (*action)(long), long timeout, int state)
58 {
59 if (!x->done)
60 {
61 DECLARE_WAIT_ON_STACK_SELF(wait);
62
63 while (!x->done && timeout > 0)
64 {
65 // 加入等待队列, 但是不会调度走
66 if (list_empty(&wait.wait_list))
67 list_append(&x->wait_queue.wait_list, &wait.wait_list);
68 wait.pcb->state = state; // 清除运行位, 并设置为interuptible/uninteruptible
69
70 spin_unlock(&x->wait_queue.lock);
71
72 timeout = action(timeout);
73 spin_lock(&x->wait_queue.lock);
74 }
75 if (!x->done)
76 return timeout; // 仍然没有complete, 但是被其他进程唤醒
77
78 wait.pcb->state = PROC_RUNNING; // 设置为运行, 并清空state, 所以使用等号赋值
79 if (!list_empty(&wait.wait_list))
80 list_del_init(&wait.wait_list); // 必须使用del_init
81 }
82 if (x->done != COMPLETE_ALL)
83 --(x->done);
84 return timeout ? timeout : 1; // 这里linux返回1,不知道为啥
85 }
86
87 /**
88 * @brief 等待completion命令唤醒进程, 同时设置pcb->state为uninteruptible.
89 *
90 * @param x completion
91 */
wait_for_completion(struct completion * x)92 void wait_for_completion(struct completion *x)
93 {
94 spin_lock(&x->wait_queue.lock);
95 __wait_for_common(x, &schedule_timeout_ms, MAX_TIMEOUT, PROC_UNINTERRUPTIBLE);
96 spin_unlock(&x->wait_queue.lock);
97 }
98
99 /**
100 * @brief 等待指定时间,超时后就返回, 同时设置pcb->state为uninteruptible.
101 *
102 * @param x completion
103 * @param timeout 非负整数,等待指定时间,超时后就返回/ 或者提前done,则返回剩余timeout时间
104 * @return long - 返回剩余的timeout
105 */
wait_for_completion_timeout(struct completion * x,long timeout)106 long wait_for_completion_timeout(struct completion *x, long timeout)
107 {
108 BUG_ON(timeout < 0);
109 spin_lock(&x->wait_queue.lock);
110 timeout = __wait_for_common(x, &schedule_timeout_ms, timeout, PROC_UNINTERRUPTIBLE);
111 spin_unlock(&x->wait_queue.lock);
112 return timeout;
113 }
114
115 /**
116 * @brief 等待completion的完成,但是可以被中断(我也不太懂可以被中断是什么意思,就是pcb->state=interuptible)
117 *
118 * @param x completion
119 */
wait_for_completion_interruptible(struct completion * x)120 void wait_for_completion_interruptible(struct completion *x)
121 {
122 spin_lock(&x->wait_queue.lock);
123 __wait_for_common(x, &schedule_timeout_ms, MAX_TIMEOUT, PROC_INTERRUPTIBLE);
124 spin_unlock(&x->wait_queue.lock);
125 }
126
127 /**
128 * @brief 等待指定时间,超时后就返回, 等待completion的完成,但是可以被中断.
129 *
130 * @param x completion
131 * @param timeout 非负整数,等待指定时间,超时后就返回/ 或者提前done,则返回剩余timeout时间
132 * @return long - 返回剩余的timeout
133 */
wait_for_completion_interruptible_timeout(struct completion * x,long timeout)134 long wait_for_completion_interruptible_timeout(struct completion *x, long timeout)
135 {
136 BUG_ON(timeout < 0);
137
138 spin_lock(&x->wait_queue.lock);
139 timeout = __wait_for_common(x, &schedule_timeout_ms, timeout, PROC_INTERRUPTIBLE);
140 spin_unlock(&x->wait_queue.lock);
141 return timeout;
142 }
143
144 /**
145 * @brief 尝试获取completion的一个done!如果您在wait之前加上这个函数作为判断,说不定会加快运行速度。
146 *
147 * @param x completion
148 * @return true - 表示不需要wait_for_completion,并且已经获取到了一个completion(即返回true意味着done已经被 减1 ) \
149 * @return false - 表示当前done=0,您需要进入等待,即wait_for_completion
150 */
try_wait_for_completion(struct completion * x)151 bool try_wait_for_completion(struct completion *x)
152 {
153 if (!READ_ONCE(x->done))
154 return false;
155
156 bool ret = true;
157 spin_lock(&x->wait_queue.lock);
158
159 if (!x->done)
160 ret = false;
161 else if (x->done != COMPLETE_ALL)
162 --(x->done);
163
164 spin_unlock(&x->wait_queue.lock);
165 return ret;
166 }
167
168 /**
169 * @brief 测试一个completion是否有waiter。(即done是不是等于0)
170 *
171 * @param x completion
172 * @return true
173 * @return false
174 */
completion_done(struct completion * x)175 bool completion_done(struct completion *x)
176 {
177
178 if (!READ_ONCE(x->done))
179 return false;
180
181 // 这里的意义是: 如果是多线程的情况下,您有可能需要等待另一个进程的complete操作, 才算真正意义上的completed!
182 spin_lock(&x->wait_queue.lock);
183
184 if (!READ_ONCE(x->done))
185 {
186 spin_unlock(&x->wait_queue.lock);
187 return false;
188 }
189 spin_unlock(&x->wait_queue.lock);
190 return true;
191 }
192
193 /**
194 * @brief 对completion数组进行wait操作
195 *
196 * @param x completion array
197 * @param n len of the array
198 */
wait_for_multicompletion(struct completion x[],int n)199 void wait_for_multicompletion(struct completion x[], int n)
200 {
201 for (int i = 0; i < n; i++) // 对每一个completion都等一遍
202 {
203 if (!completion_done(&x[i])) // 如果没有done,直接wait
204 {
205 wait_for_completion(&x[i]);
206 }
207 else if (!try_wait_for_completion(&x[i])) // 上面测试过done>0,那么这里尝试去获取一个done,如果失败了,就继续wait
208 {
209 wait_for_completion(&x[i]);
210 }
211 }
212 }
213
214 /**
215 * @brief 等待者, 等待wait_for_completion
216 *
217 * @param one_to_one
218 * @param one_to_many
219 * @param many_to_one
220 */
__test_completion_waiter(void * input_data)221 int __test_completion_waiter(void *input_data)
222 {
223 struct __test_data *data = (struct __test_data *)input_data;
224 // kdebug("THE %d WAITER BEGIN", -data->id);
225 // 测试一对多能不能实现等待 - 由外部统一放闸一起跑
226 if (!try_wait_for_completion(data->one_to_many))
227 {
228 wait_for_completion(data->one_to_many);
229 }
230
231 // 测试一对一能不能实现等待
232 if (!try_wait_for_completion(data->one_to_many))
233 {
234 wait_for_completion(data->one_to_many);
235 }
236
237 // 完成上面两个等待, 执行complete声明自己已经完成
238 complete(data->many_to_one);
239 // kdebug("THE %d WAITER SOLVED", -data->id);
240 return true;
241 }
242
243 /**
244 * @brief 执行者,执行complete
245 *
246 * @param one_to_one
247 * @param one_to_many
248 * @param many_to_one
249 */
__test_completion_worker(void * input_data)250 int __test_completion_worker(void *input_data)
251 {
252 struct __test_data *data = (struct __test_data *)input_data;
253 // kdebug("THE %d WORKER BEGIN", data->id);
254 // 测试一对多能不能实现等待 - 由外部统一放闸一起跑
255 if (!try_wait_for_completion(data->one_to_many))
256 {
257 wait_for_completion(data->one_to_many);
258 }
259
260 schedule_timeout_ms(50);
261 // for(uint64_t i=0;i<1e7;++i)
262 // pause();
263 complete(data->one_to_one);
264
265 // 完成上面两个等待, 执行complete声明自己已经完成
266 complete(data->many_to_one);
267 // kdebug("THE %d WORKER SOLVED", data->id);
268 return true;
269 }
270
271 /**
272 * @brief 测试函数
273 *
274 */
__test_completion()275 void __test_completion()
276 {
277 // kdebug("BEGIN COMPLETION TEST");
278 const int N = 100;
279 struct completion *one_to_one = kzalloc(sizeof(struct completion) * N, 0);
280 struct completion *one_to_many = kzalloc(sizeof(struct completion), 0);
281 struct completion *waiter_many_to_one = kzalloc(sizeof(struct completion) * N, 0);
282 struct completion *worker_many_to_one = kzalloc(sizeof(struct completion) * N, 0);
283 struct __test_data *waiter_data = kzalloc(sizeof(struct __test_data) * N, 0);
284 struct __test_data *worker_data = kzalloc(sizeof(struct __test_data) * N, 0);
285
286 completion_init(one_to_many);
287 for (int i = 0; i < N; i++)
288 {
289 completion_init(&one_to_one[i]);
290 completion_init(&waiter_many_to_one[i]);
291 completion_init(&worker_many_to_one[i]);
292 }
293
294 for (int i = 0; i < N; i++)
295 {
296 waiter_data[i].id = -i; // waiter
297 waiter_data[i].many_to_one = &waiter_many_to_one[i];
298 waiter_data[i].one_to_one = &one_to_one[i];
299 waiter_data[i].one_to_many = one_to_many;
300 kthread_run(__test_completion_waiter, &waiter_data[i], "the %dth waiter", i);
301 }
302
303 for (int i = 0; i < N; i++)
304 {
305 worker_data[i].id = i; // worker
306 worker_data[i].many_to_one = &worker_many_to_one[i];
307 worker_data[i].one_to_one = &one_to_one[i];
308 worker_data[i].one_to_many = one_to_many;
309 kthread_run(__test_completion_worker, &worker_data[i], "the %dth worker", i);
310 }
311
312 complete_all(one_to_many);
313 // kdebug("all of the waiters and workers begin running");
314
315 // kdebug("BEGIN COUNTING");
316
317 wait_for_multicompletion(waiter_many_to_one, N);
318 wait_for_multicompletion(worker_many_to_one, N);
319 // kdebug("all of the waiters and workers complete");
320
321 kfree(one_to_one);
322 kfree(one_to_many);
323 kfree(waiter_many_to_one);
324 kfree(worker_many_to_one);
325 kfree(waiter_data);
326 kfree(worker_data);
327 // kdebug("completion test done.");
328 }
329
330 /**
331 * @brief rust 获取completion
332 */
completion_alloc()333 struct completion *completion_alloc()
334 {
335 struct completion *cmpl = kzalloc(sizeof(struct completion), 0);
336 completion_init(cmpl);
337 return cmpl;
338 }