123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- #include "common/completion.h"
- #include "common/kthread.h"
- void completion_init(struct completion *x)
- {
- x->done = 0;
- wait_queue_head_init(&x->wait_queue);
- }
- void complete(struct completion *x)
- {
- spin_lock(&x->wait_queue.lock);
- if (x->done != COMPLETE_ALL)
- ++(x->done);
- wait_queue_wakeup_on_stack(&x->wait_queue, -1UL);
- spin_unlock(&x->wait_queue.lock);
- }
- void complete_all(struct completion *x)
- {
- spin_lock(&x->wait_queue.lock);
- x->done = COMPLETE_ALL;
- while (!list_empty(&x->wait_queue.wait_list))
- wait_queue_wakeup_on_stack(&x->wait_queue, -1UL);
- spin_unlock(&x->wait_queue.lock);
- }
- static long __wait_for_common(struct completion *x, long (*action)(long), long timeout, int state)
- {
- if (!x->done)
- {
- DECLARE_WAIT_ON_STACK_SELF(wait);
- while (!x->done && timeout > 0)
- {
-
- if (list_empty(&wait.wait_list))
- list_append(&x->wait_queue.wait_list, &wait.wait_list);
- wait.pcb->state = state;
- spin_unlock(&x->wait_queue.lock);
- timeout = action(timeout);
- spin_lock(&x->wait_queue.lock);
- }
- if (!x->done)
- return timeout;
- wait.pcb->state = PROC_RUNNING;
- if (!list_empty(&wait.wait_list))
- list_del_init(&wait.wait_list);
- }
- if (x->done != COMPLETE_ALL)
- --(x->done);
- return timeout ? timeout : 1;
- }
- void wait_for_completion(struct completion *x)
- {
- spin_lock(&x->wait_queue.lock);
- __wait_for_common(x, &schedule_timeout_ms, MAX_TIMEOUT, PROC_UNINTERRUPTIBLE);
- spin_unlock(&x->wait_queue.lock);
- }
- long wait_for_completion_timeout(struct completion *x, long timeout)
- {
- BUG_ON(timeout < 0);
- spin_lock(&x->wait_queue.lock);
- timeout = __wait_for_common(x, &schedule_timeout_ms, timeout, PROC_UNINTERRUPTIBLE);
- spin_unlock(&x->wait_queue.lock);
- return timeout;
- }
- void wait_for_completion_interruptible(struct completion *x)
- {
- spin_lock(&x->wait_queue.lock);
- __wait_for_common(x, &schedule_timeout_ms, MAX_TIMEOUT, PROC_INTERRUPTIBLE);
- spin_unlock(&x->wait_queue.lock);
- }
- long wait_for_completion_interruptible_timeout(struct completion *x, long timeout)
- {
- BUG_ON(timeout < 0);
- spin_lock(&x->wait_queue.lock);
- timeout = __wait_for_common(x, &schedule_timeout_ms, timeout, PROC_INTERRUPTIBLE);
- spin_unlock(&x->wait_queue.lock);
- return timeout;
- }
- bool try_wait_for_completion(struct completion *x)
- {
- if (!READ_ONCE(x->done))
- return false;
- bool ret = true;
- spin_lock(&x->wait_queue.lock);
- if (!x->done)
- ret = false;
- else if (x->done != COMPLETE_ALL)
- --(x->done);
- spin_unlock(&x->wait_queue.lock);
- return ret;
- }
- bool completion_done(struct completion *x)
- {
- if (!READ_ONCE(x->done))
- return false;
-
- spin_lock(&x->wait_queue.lock);
- if (!READ_ONCE(x->done))
- {
- spin_unlock(&x->wait_queue.lock);
- return false;
- }
- spin_unlock(&x->wait_queue.lock);
- return true;
- }
- void wait_for_multicompletion(struct completion x[], int n)
- {
- for (int i = 0; i < n; i++)
- {
- if (!completion_done(&x[i]))
- {
- wait_for_completion(&x[i]);
- }
- else if (!try_wait_for_completion(&x[i]))
- {
- wait_for_completion(&x[i]);
- }
- }
- }
- int __test_completion_waiter(void *input_data)
- {
- struct __test_data *data = (struct __test_data *)input_data;
-
-
- if (!try_wait_for_completion(data->one_to_many))
- {
- wait_for_completion(data->one_to_many);
- }
-
- if (!try_wait_for_completion(data->one_to_many))
- {
- wait_for_completion(data->one_to_many);
- }
-
- complete(data->many_to_one);
-
- return true;
- }
- int __test_completion_worker(void *input_data)
- {
- struct __test_data *data = (struct __test_data *)input_data;
-
-
- if (!try_wait_for_completion(data->one_to_many))
- {
- wait_for_completion(data->one_to_many);
- }
- schedule_timeout_ms(50);
-
-
- complete(data->one_to_one);
-
- complete(data->many_to_one);
-
- return true;
- }
- void __test_completion()
- {
-
- const int N = 100;
- struct completion *one_to_one = kzalloc(sizeof(struct completion) * N, 0);
- struct completion *one_to_many = kzalloc(sizeof(struct completion), 0);
- struct completion *waiter_many_to_one = kzalloc(sizeof(struct completion) * N, 0);
- struct completion *worker_many_to_one = kzalloc(sizeof(struct completion) * N, 0);
- struct __test_data *waiter_data = kzalloc(sizeof(struct __test_data) * N, 0);
- struct __test_data *worker_data = kzalloc(sizeof(struct __test_data) * N, 0);
- completion_init(one_to_many);
- for (int i = 0; i < N; i++)
- {
- completion_init(&one_to_one[i]);
- completion_init(&waiter_many_to_one[i]);
- completion_init(&worker_many_to_one[i]);
- }
- for (int i = 0; i < N; i++)
- {
- waiter_data[i].id = -i;
- waiter_data[i].many_to_one = &waiter_many_to_one[i];
- waiter_data[i].one_to_one = &one_to_one[i];
- waiter_data[i].one_to_many = one_to_many;
- kthread_run(__test_completion_waiter, &waiter_data[i], "the %dth waiter", i);
- }
- for (int i = 0; i < N; i++)
- {
- worker_data[i].id = i;
- worker_data[i].many_to_one = &worker_many_to_one[i];
- worker_data[i].one_to_one = &one_to_one[i];
- worker_data[i].one_to_many = one_to_many;
- kthread_run(__test_completion_worker, &worker_data[i], "the %dth worker", i);
- }
- complete_all(one_to_many);
-
-
- wait_for_multicompletion(waiter_many_to_one, N);
- wait_for_multicompletion(worker_many_to_one, N);
-
- kfree(one_to_one);
- kfree(one_to_many);
- kfree(waiter_many_to_one);
- kfree(worker_many_to_one);
- kfree(waiter_data);
- kfree(worker_data);
-
- }
|