1 /*
2  * Copyright (C) 2015-2017 Alibaba Group Holding Limited
3  */
4 
5 #include "k_api.h"
6 
7 #if (RHINO_CONFIG_WORKQUEUE > 0)
workqueue_is_exist(kworkqueue_t * workqueue)8 static kstat_t workqueue_is_exist(kworkqueue_t *workqueue)
9 {
10     CPSR_ALLOC();
11     kworkqueue_t *pos;
12 
13     RHINO_CRITICAL_ENTER();
14 
15     for (pos = krhino_list_entry(g_workqueue_list_head.next, kworkqueue_t, workqueue_node);
16         &pos->workqueue_node != &g_workqueue_list_head;
17          pos = krhino_list_entry(pos->workqueue_node.next, kworkqueue_t, workqueue_node)) {
18         if (pos == workqueue) {
19             RHINO_CRITICAL_EXIT();
20             return RHINO_WORKQUEUE_EXIST;
21         }
22     }
23 
24     RHINO_CRITICAL_EXIT();
25     return RHINO_WORKQUEUE_NOT_EXIST;
26 }
27 
worker_task(void * arg)28 static void worker_task(void *arg)
29 {
30     CPSR_ALLOC();
31     kstat_t       ret;
32     kwork_t      *work  = NULL;
33     kworkqueue_t *queue = (kworkqueue_t *)arg;
34 
35     while (1) {
36 
37         ret = krhino_sem_take(&(queue->sem), RHINO_WAIT_FOREVER);
38         if (ret != RHINO_SUCCESS) {
39             k_err_proc(ret);
40         }
41 
42         RHINO_CRITICAL_ENTER();
43 
44         /* have work to do. */
45         work = krhino_list_entry(queue->work_list.next, kwork_t, work_node);
46         klist_rm_init(&(work->work_node));
47         queue->work_current = work;
48         work->work_exit = 0;
49         RHINO_CRITICAL_EXIT();
50 
51         /* do work */
52         work->handle(work->arg);
53         RHINO_CRITICAL_ENTER();
54         /* clean current work */
55         queue->work_current = NULL;
56 
57         RHINO_CRITICAL_EXIT();
58     }
59 }
60 
krhino_workqueue_create(kworkqueue_t * workqueue,const name_t * name,uint8_t pri,cpu_stack_t * stack_buf,size_t stack_size)61 kstat_t krhino_workqueue_create(kworkqueue_t *workqueue, const name_t *name,
62                                 uint8_t pri, cpu_stack_t *stack_buf, size_t stack_size)
63 {
64     CPSR_ALLOC();
65     kstat_t ret;
66 
67     NULL_PARA_CHK(workqueue);
68     NULL_PARA_CHK(name);
69     NULL_PARA_CHK(stack_buf);
70 
71     if (pri >= RHINO_CONFIG_PRI_MAX) {
72         return RHINO_BEYOND_MAX_PRI;
73     }
74 
75     if (stack_size == 0u) {
76         return RHINO_TASK_INV_STACK_SIZE;
77     }
78 
79     ret = workqueue_is_exist(workqueue);
80     if (ret == RHINO_WORKQUEUE_EXIST) {
81         return RHINO_WORKQUEUE_EXIST;
82     }
83 
84     klist_init(&(workqueue->workqueue_node));
85     klist_init(&(workqueue->work_list));
86     workqueue->work_current = NULL;
87     workqueue->name      = name;
88 
89     ret = krhino_sem_create(&(workqueue->sem), "WORKQUEUE-SEM", 0);
90     if (ret != RHINO_SUCCESS) {
91         return ret;
92     }
93 
94     RHINO_CRITICAL_ENTER();
95     klist_insert(&g_workqueue_list_head, &(workqueue->workqueue_node));
96     RHINO_CRITICAL_EXIT();
97 
98     ret = krhino_task_create(&(workqueue->worker), name, (void *)workqueue, pri,
99                              0, stack_buf, stack_size, worker_task, 1);
100     if (ret != RHINO_SUCCESS) {
101         RHINO_CRITICAL_ENTER();
102         klist_rm_init(&(workqueue->workqueue_node));
103         RHINO_CRITICAL_EXIT();
104         krhino_sem_del(&(workqueue->sem));
105         return ret;
106     }
107 
108     TRACE_WORKQUEUE_CREATE(krhino_cur_task_get(), workqueue);
109 
110     return RHINO_SUCCESS;
111 }
112 
krhino_workqueue_del(kworkqueue_t * workqueue)113 kstat_t krhino_workqueue_del(kworkqueue_t *workqueue)
114 {
115     CPSR_ALLOC();
116     kstat_t ret;
117 
118     NULL_PARA_CHK(workqueue);
119 
120     ret = workqueue_is_exist(workqueue);
121     if (ret == RHINO_WORKQUEUE_NOT_EXIST) {
122         return RHINO_WORKQUEUE_NOT_EXIST;
123     }
124 
125     RHINO_CRITICAL_ENTER();
126 
127     if (!is_klist_empty(&(workqueue->work_list))) {
128         RHINO_CRITICAL_EXIT();
129         return RHINO_WORKQUEUE_BUSY;
130     }
131 
132     if (workqueue->work_current != NULL) {
133         RHINO_CRITICAL_EXIT();
134         return RHINO_WORKQUEUE_BUSY;
135     }
136 
137     RHINO_CRITICAL_EXIT();
138 
139     ret = krhino_task_del(&(workqueue->worker));
140     if (ret != RHINO_SUCCESS) {
141         return ret;
142     }
143 
144     ret = krhino_sem_del(&(workqueue->sem));
145     if (ret != RHINO_SUCCESS) {
146         return ret;
147     }
148 
149     RHINO_CRITICAL_ENTER();
150     klist_rm_init(&(workqueue->workqueue_node));
151     TRACE_WORKQUEUE_DEL(g_active_task[cpu_cur_get()], workqueue);
152     RHINO_CRITICAL_EXIT();
153 
154     return RHINO_SUCCESS;
155 }
156 
work_timer_cb(void * timer,void * arg)157 static void work_timer_cb(void *timer, void *arg)
158 {
159     CPSR_ALLOC();
160     kstat_t       ret;
161     kwork_t      *work = ((ktimer_t *)timer)->priv;
162     kworkqueue_t *wq   = (kworkqueue_t *)arg;
163 
164     RHINO_CRITICAL_ENTER();
165     if (wq->work_current == work) {
166         RHINO_CRITICAL_EXIT();
167         return;
168     }
169 
170     if (work->work_exit == 1) {
171         RHINO_CRITICAL_EXIT();
172         return;
173     }
174 
175     /* NOTE: the work MUST be initialized firstly */
176     klist_rm_init(&(work->work_node));
177     klist_insert(&(wq->work_list), &(work->work_node));
178 
179     work->wq = wq;
180     work->work_exit = 1;
181     RHINO_CRITICAL_EXIT();
182 
183     ret = krhino_sem_give(&(wq->sem));
184     if (ret != RHINO_SUCCESS) {
185         return;
186     }
187 
188 }
189 
krhino_work_init(kwork_t * work,work_handle_t handle,void * arg,tick_t dly)190 kstat_t krhino_work_init(kwork_t *work, work_handle_t handle, void *arg,
191                          tick_t dly)
192 {
193     kstat_t ret;
194 
195     if (work == NULL) {
196         return RHINO_NULL_PTR;
197     }
198 
199     if (handle == NULL) {
200         return RHINO_NULL_PTR;
201     }
202 
203     NULL_PARA_CHK(work);
204     NULL_PARA_CHK(handle);
205 
206     memset(work, 0, sizeof(kwork_t));
207 
208     klist_init(&(work->work_node));
209     work->handle  = handle;
210     work->arg     = arg;
211     work->dly     = dly;
212     work->wq      = NULL;
213 
214     if (dly > 0) {
215         ret = krhino_timer_dyn_create((ktimer_t **)(&work->timer), "WORK-TIMER", work_timer_cb,
216                                        work->dly, 0, (void *)work, 0);
217         if (ret != RHINO_SUCCESS) {
218             return ret;
219         }
220     }
221 
222     TRACE_WORK_INIT(krhino_cur_task_get(), work);
223 
224     return RHINO_SUCCESS;
225 }
226 
krhino_work_run(kworkqueue_t * workqueue,kwork_t * work)227 kstat_t krhino_work_run(kworkqueue_t *workqueue, kwork_t *work)
228 {
229     CPSR_ALLOC();
230     kstat_t ret;
231 
232     NULL_PARA_CHK(workqueue);
233     NULL_PARA_CHK(work);
234 
235     RHINO_CRITICAL_ENTER();
236 
237     if (work->dly == 0) {
238         if (workqueue->work_current == work) {
239             RHINO_CRITICAL_EXIT();
240             return RHINO_WORKQUEUE_WORK_RUNNING;
241         }
242 
243         if (work->work_exit == 1) {
244             RHINO_CRITICAL_EXIT();
245             return RHINO_WORKQUEUE_WORK_EXIST;
246         }
247 
248         /* NOTE: the work MUST be initialized firstly */
249         klist_rm_init(&(work->work_node));
250         klist_insert(&(workqueue->work_list), &(work->work_node));
251 
252         work->wq = workqueue;
253         work->work_exit = 1;
254 
255         RHINO_CRITICAL_EXIT();
256         ret = krhino_sem_give(&(workqueue->sem));
257         if (ret != RHINO_SUCCESS) {
258             return ret;
259         }
260 
261     } else {
262         work->timer->priv = work;
263         RHINO_CRITICAL_EXIT();
264         ret = krhino_timer_arg_change_auto(work->timer, (void *)workqueue);
265         if (ret != RHINO_SUCCESS) {
266             return ret;
267         }
268     }
269 
270     return RHINO_SUCCESS;
271 }
272 
krhino_work_sched(kwork_t * work)273 kstat_t krhino_work_sched(kwork_t *work)
274 {
275     return krhino_work_run(&g_workqueue_default, work);
276 }
277 
krhino_work_cancel(kwork_t * work)278 kstat_t krhino_work_cancel(kwork_t *work)
279 {
280     CPSR_ALLOC();
281     kworkqueue_t *wq;
282 
283     NULL_PARA_CHK(work);
284 
285     wq = (kworkqueue_t *)work->wq;
286 
287     if (wq == NULL) {
288         if (work->dly > 0) {
289             krhino_timer_stop(work->timer);
290         }
291         return RHINO_SUCCESS;
292     }
293 
294     RHINO_CRITICAL_ENTER();
295     if (wq->work_current == work) {
296         RHINO_CRITICAL_EXIT();
297         return RHINO_WORKQUEUE_WORK_RUNNING;
298     }
299 
300     if (work->work_exit == 1) {
301         RHINO_CRITICAL_EXIT();
302         return RHINO_WORKQUEUE_WORK_EXIST;
303     }
304 
305     klist_rm_init(&(work->work_node));
306     work->wq      = NULL;
307     RHINO_CRITICAL_EXIT();
308 
309     return RHINO_SUCCESS;
310 }
311 
workqueue_init(void)312 void workqueue_init(void)
313 {
314     klist_init(&g_workqueue_list_head);
315 
316     krhino_workqueue_create(&g_workqueue_default, "DEFAULT-WORKQUEUE",
317                             RHINO_CONFIG_WORKQUEUE_TASK_PRIO, g_workqueue_stack,
318                             RHINO_CONFIG_WORKQUEUE_STACK_SIZE);
319 }
320 #endif
321 
322