1 /*
2  * Copyright (c) 2019, Google Inc. All rights reserved
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include <err.h>
25 #include <kernel/timer.h>
26 #include <lib/dpc.h>
27 #include <lib/unittest/unittest.h>
28 #include <lk/init.h>
29 
30 #define TEST_REQUEUE_CNT 10
31 
32 struct dpc_test_ctx {
33     struct dpc work;
34     uint32_t count;
35     uint32_t in_atomic_requeue_cnt;
36     uint32_t in_thread_requeue_cnt;
37     struct event evt;
38     struct timer tmr;
39 };
40 
dpc_test_timer_callback(struct timer * t,lk_time_ns_t now,void * arg)41 static enum handler_return dpc_test_timer_callback(struct timer* t,
42                                                    lk_time_ns_t now,
43                                                    void* arg) {
44     struct dpc_test_ctx* ctx = arg;
45 
46     ctx->in_atomic_requeue_cnt++;
47     dpc_enqueue_work(NULL, &ctx->work, false);
48     return INT_RESCHEDULE;
49 }
50 
dpc_test_callback(struct dpc * work)51 static void dpc_test_callback(struct dpc* work) {
52     struct dpc_test_ctx* ctx = containerof(work, struct dpc_test_ctx, work);
53 
54     if (ctx->count > TEST_REQUEUE_CNT / 2) {
55         /* requeue work from irq context */
56         ctx->count--;
57         timer_set_oneshot_ns(&ctx->tmr, 10 * 1000 * 1000,
58                              dpc_test_timer_callback, ctx);
59     } else if (ctx->count) {
60         /* requeue work from thread context */
61         ctx->count--;
62         ctx->in_thread_requeue_cnt++;
63         dpc_enqueue_work(NULL, &ctx->work, false);
64     } else {
65         /* we are done here */
66         event_signal(&ctx->evt, true);
67     }
68 }
69 
TEST(dpctest,test1)70 TEST(dpctest, test1) {
71     status_t rc;
72     struct dpc_test_ctx test_ctx;
73 
74     /* Init test context */
75     test_ctx.count = TEST_REQUEUE_CNT;
76     test_ctx.in_atomic_requeue_cnt = 0;
77     test_ctx.in_thread_requeue_cnt = 0;
78     timer_initialize(&test_ctx.tmr);
79     event_init(&test_ctx.evt, false, EVENT_FLAG_AUTOUNSIGNAL);
80     dpc_work_init(&test_ctx.work, dpc_test_callback, 0);
81 
82     /* init dpc work and queue it on default queue */
83     dpc_enqueue_work(NULL, &test_ctx.work, false);
84 
85     /* wait for complete */
86     rc = event_wait_timeout(&test_ctx.evt, 1000);
87 
88     /* check results */
89     EXPECT_EQ(NO_ERROR, rc);
90     EXPECT_EQ(0, test_ctx.count);
91     EXPECT_EQ(TEST_REQUEUE_CNT / 2, test_ctx.in_atomic_requeue_cnt);
92     EXPECT_EQ(TEST_REQUEUE_CNT / 2, test_ctx.in_thread_requeue_cnt);
93 }
94 
95 PORT_TEST(dpctest, "com.android.kernel.dpc-unittest");
96