1 /*
2  * Copyright (c) 2016, 2020 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @brief Workqueue Tests
9  * @defgroup kernel_workqueue_tests Workqueue
10  * @ingroup all_tests
11  * @{
12  * @}
13  */
14 
15 #include <zephyr/ztest.h>
16 #include <zephyr/irq_offload.h>
17 
18 #define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
19 #define NUM_OF_WORK 2
20 #define SYNC_SEM_INIT_VAL (0U)
21 
22 static K_THREAD_STACK_DEFINE(user_tstack, STACK_SIZE);
23 static struct k_work_user_q user_workq;
24 static ZTEST_BMEM struct k_work_user work[NUM_OF_WORK];
25 static struct k_sem sync_sema;
26 static struct k_sem dummy_sema;
27 static struct k_thread *main_thread;
28 
29 /**
30  * @brief Common function using like a handler for workqueue tests
31  * API call in it means successful execution of that function
32  *
33  * @param unused of type k_work to make handler function accepted
34  * by k_work_init
35  */
common_work_handler(struct k_work_user * unused)36 static void common_work_handler(struct k_work_user *unused)
37 {
38 	k_sem_give(&sync_sema);
39 }
40 
test_k_work_user_init(void)41 static void test_k_work_user_init(void)
42 {
43 	K_WORK_USER_DEFINE(local, common_work_handler);
44 	zassert_equal(local.handler, common_work_handler);
45 	zassert_equal(local.flags, 0);
46 }
47 
48 /**
49  * @brief Test k_work_user_submit_to_queue API
50  *
51  * @details Function k_work_user_submit_to_queue() will return
52  * -EBUSY: if the work item was already in some workqueue and
53  * -ENOMEM: if no memory for thread resource pool allocation.
54  * Create two situation to meet the error return value.
55  *
56  * @see k_work_user_submit_to_queue()
57  * @ingroup kernel_workqueue_tests
58  */
test_k_work_user_submit_to_queue_fail(void)59 static void test_k_work_user_submit_to_queue_fail(void)
60 {
61 	int ret = 0;
62 
63 	k_sem_reset(&sync_sema);
64 	k_work_user_init(&work[0], common_work_handler);
65 	k_work_user_init(&work[1], common_work_handler);
66 
67 	/* TESTPOINT: When a work item be added to a workqueue, its flag will
68 	 * be in pending state, before the work item be processed, it cannot
69 	 * be append to a workqueue another time.
70 	 */
71 	k_work_user_submit_to_queue(&user_workq, &work[0]);
72 	zassert_true(k_work_user_is_pending(&work[0]));
73 	k_work_user_submit_to_queue(&user_workq, &work[0]);
74 
75 	/* Test the work item's callback function can only be invoked once */
76 	k_sem_take(&sync_sema, K_FOREVER);
77 	zassert_true(k_queue_is_empty(&user_workq.queue));
78 	zassert_false(k_work_user_is_pending(&work[0]));
79 
80 	/* use up the memory in resource pool */
81 	do {
82 		ret = k_queue_alloc_append(&user_workq.queue, &work[1]);
83 		if (ret == -ENOMEM) {
84 			break;
85 		}
86 	} while (true);
87 
88 	k_work_user_submit_to_queue(&user_workq, &work[0]);
89 	/* if memory is used up, the work cannot be append into the workqueue */
90 	zassert_false(k_work_user_is_pending(&work[0]));
91 }
92 
93 
work_handler(struct k_work_user * w)94 static void work_handler(struct k_work_user *w)
95 {
96 	/* Just to show an API call on this will succeed */
97 	k_sem_init(&dummy_sema, 0, 1);
98 
99 	k_sem_give(&sync_sema);
100 }
101 
twork_submit_1(struct k_work_user_q * work_q,struct k_work_user * w,k_work_user_handler_t handler)102 static void twork_submit_1(struct k_work_user_q *work_q, struct k_work_user *w,
103 			   k_work_user_handler_t handler)
104 {
105 	/**TESTPOINT: init via k_work_init*/
106 	k_work_user_init(w, handler);
107 	/**TESTPOINT: check pending after work init*/
108 	zassert_false(k_work_user_is_pending(w));
109 
110 	/**TESTPOINT: work submit to queue*/
111 	zassert_false(k_work_user_submit_to_queue(work_q, w),
112 		      "failed to submit to queue");
113 }
114 
twork_submit(const void * data)115 static void twork_submit(const void *data)
116 {
117 	struct k_work_user_q *work_q = (struct k_work_user_q *)data;
118 
119 	for (int i = 0; i < NUM_OF_WORK; i++) {
120 		twork_submit_1(work_q, &work[i], work_handler);
121 	}
122 }
123 
124 /**
125  * @brief Test user mode work queue start before submit
126  *
127  * @ingroup kernel_workqueue_tests
128  *
129  * @see k_work_user_queue_start()
130  */
test_work_user_queue_start_before_submit(void)131 static void test_work_user_queue_start_before_submit(void)
132 {
133 	k_work_user_queue_start(&user_workq, user_tstack, STACK_SIZE,
134 				CONFIG_MAIN_THREAD_PRIORITY, "user.wq");
135 }
136 
137 /**
138  * @brief Setup object permissions before test_user_workq_granted_access()
139  *
140  * @ingroup kernel_workqueue_tests
141  */
test_user_workq_granted_access_setup(void)142 static void test_user_workq_granted_access_setup(void)
143 {
144 	/* Subsequent test cases will have access to the dummy_sema,
145 	 * but not the user workqueue since it already started.
146 	 */
147 	k_object_access_grant(&dummy_sema, main_thread);
148 }
149 
150 /**
151  * @brief Test user mode grant workqueue permissions
152  *
153  * @ingroup kernel_workqueue_tests
154  *
155  * @see k_work_q_object_access_grant()
156  */
test_user_workq_granted_access(void)157 static void test_user_workq_granted_access(void)
158 {
159 	k_object_access_grant(&dummy_sema, &user_workq.thread);
160 }
161 
162 /**
163  * @brief Test work submission to work queue (user mode)
164  *
165  * @ingroup kernel_workqueue_tests
166  *
167  * @see k_work_init(), k_work_is_pending(), k_work_submit_to_queue(),
168  * k_work_submit()
169  */
test_user_work_submit_to_queue_thread(void)170 static void test_user_work_submit_to_queue_thread(void)
171 {
172 	k_sem_reset(&sync_sema);
173 	twork_submit(&user_workq);
174 	for (int i = 0; i < NUM_OF_WORK; i++) {
175 		k_sem_take(&sync_sema, K_FOREVER);
176 	}
177 }
178 
workq_setup(void)179 void *workq_setup(void)
180 {
181 	main_thread = k_current_get();
182 	k_thread_access_grant(main_thread, &sync_sema, &user_workq.thread,
183 			      &user_workq.queue,
184 			      &user_tstack);
185 	k_sem_init(&sync_sema, SYNC_SEM_INIT_VAL, NUM_OF_WORK);
186 	k_thread_system_pool_assign(k_current_get());
187 
188 	test_user_workq_granted_access_setup();
189 	test_k_work_user_init();
190 
191 	return NULL;
192 }
193 
ZTEST_USER(workqueue_api,test_workq_user_mode)194 ZTEST_USER(workqueue_api, test_workq_user_mode)
195 {
196 	/* Do not disturb the ordering of these test cases */
197 	test_work_user_queue_start_before_submit();
198 	test_user_workq_granted_access();
199 
200 	/* End order-important tests */
201 	test_user_work_submit_to_queue_thread();
202 	test_k_work_user_submit_to_queue_fail();
203 }
204 
205 ZTEST_SUITE(workqueue_api, NULL, workq_setup, NULL, NULL, NULL);
206