10d163575Sopenharmony_ci/*
2 * Copyright (c) 2013-2019, Huawei Technologies Co., Ltd. All rights reserved.
3 * Copyright (c) 2020, Huawei Device Co., Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this list of
9 *    conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 *    of conditions and the following disclaimer in the documentation and/or other materials
13 *    provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16 *    to endorse or promote products derived from this software without specific prior written
17 *    permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "linux/workqueue.h"
33#include "unistd.h"
34#include "los_event.h"
35#include "los_init.h"
36#include "los_swtmr_pri.h"
37
38#define DELAY_TIME  10000
39
40struct workqueue_struct *g_pstSystemWq = NULL;
41
42/* spinlock for workqueue module only available on SMP mode */
43LITE_OS_SEC_BSS SPIN_LOCK_INIT(g_workqueueSpin);
44
45STATIC cpu_workqueue_struct *InitCpuWorkqueue(struct workqueue_struct *wq, INT32 cpu);
46STATIC UINT32 CreateWorkqueueThread(cpu_workqueue_struct *cwq, INT32 cpu);
47STATIC VOID WorkerThread(cpu_workqueue_struct *cwq);
48STATIC VOID RunWorkqueue(cpu_workqueue_struct *cwq);
49STATIC VOID DelayedWorkTimerFunc(unsigned long data);
50
51/*
52 * @ingroup workqueue
53 * Obtain the first work in a workqueue.
54 */
55#define worklist_entry(ptr, type, member)  ((type *)((char *)(ptr)-((UINTPTR)&(((type*)0)->member))))
56
57/*
58 * @ingroup workqueue
59 * Traverse a workqueue.
60 */
61#define LIST_FOR_WORK(pos, listObject, type, field)                   \
62    for ((pos) = LOS_DL_LIST_ENTRY((listObject)->next, type, field);  \
63         &(pos)->field != (listObject);                               \
64         (pos) = LOS_DL_LIST_ENTRY((pos)->field.next, type, field))
65
66#define LIST_FOR_WORK_DEL(pos, nextNode, listObject, type, field)         \
67    for ((pos) = LOS_DL_LIST_ENTRY((listObject)->next, type, field),      \
68         (nextNode) = LOS_DL_LIST_ENTRY((pos)->field.next, type, field);  \
69         &(pos)->field != (listObject);                                   \
70         (pos) = (nextNode), (nextNode) = LOS_DL_LIST_ENTRY((pos)->field.next, type, field))
71
72
73void linux_init_delayed_work(struct delayed_work *dwork, work_func_t func)
74{
75    if ((dwork == NULL) || (func == NULL)) {
76        return;
77    }
78    INIT_WORK(&(dwork->work), func);
79    init_timer(&dwork->timer);
80    dwork->timer.function = DelayedWorkTimerFunc;
81    dwork->timer.data = (unsigned long)(UINTPTR)(dwork);
82    dwork->work.work_status = 0;
83}
84
85STATIC UINT32 WorkqueueIsEmpty(cpu_workqueue_struct *cwq)
86{
87    UINT32 ret;
88    ret = list_empty(&cwq->worklist);
89    return ret;
90}
91
92struct workqueue_struct *__create_workqueue_key(char *name,
93                                                int singleThread,
94                                                int freezeable,
95                                                int rt,
96                                                struct lock_class_key *key,
97                                                const char *lockName)
98{
99    struct workqueue_struct *wq = NULL;
100    cpu_workqueue_struct *cwq   = NULL;
101    UINT32 ret;
102    (VOID)key;
103    (VOID)lockName;
104
105    if (name == NULL) {
106        return NULL;
107    }
108
109    wq = (struct workqueue_struct *)LOS_MemAlloc(m_aucSysMem0, sizeof(struct workqueue_struct));
110    if (wq == NULL) {
111        return NULL;
112    }
113
114    wq->cpu_wq = (cpu_workqueue_struct *)LOS_MemAlloc(m_aucSysMem0, sizeof(cpu_workqueue_struct));
115    if (wq->cpu_wq == NULL) {
116        (VOID)LOS_MemFree(m_aucSysMem0, wq);
117        return NULL;
118    }
119
120    wq->name = name;
121    wq->singlethread = singleThread;
122    wq->freezeable = freezeable;
123    wq->rt = rt;
124    wq->delayed_work_count = 0;
125    INIT_LIST_HEAD(&wq->list);
126    (VOID)LOS_EventInit(&wq->wq_event);
127
128    if (singleThread) {
129        cwq = InitCpuWorkqueue(wq, singleThread);
130        ret = CreateWorkqueueThread(cwq, singleThread);
131    } else {
132        LOS_MemFree(m_aucSysMem0, wq->cpu_wq);
133        LOS_MemFree(m_aucSysMem0, wq);
134        return NULL;
135    }
136
137    if (ret) {
138        destroy_workqueue(wq);
139        wq = NULL;
140    }
141
142    return wq;
143}
144
145struct workqueue_struct *linux_create_singlethread_workqueue(char *name)
146{
147    return __create_workqueue_key(name, 1, 0, 0, NULL, NULL);
148}
149
150STATIC cpu_workqueue_struct *InitCpuWorkqueue(struct workqueue_struct *wq, INT32 cpu)
151{
152    cpu_workqueue_struct *cwq = wq->cpu_wq;
153    (VOID)cpu;
154
155    cwq->wq = wq;
156    INIT_LIST_HEAD(&cwq->worklist);
157
158    return cwq;
159}
160
161STATIC UINT32 CreateWorkqueueThread(cpu_workqueue_struct *cwq, INT32 cpu)
162{
163    struct workqueue_struct *wq = cwq->wq;
164    TSK_INIT_PARAM_S taskInitParam = {0};
165    UINT32 ret;
166    (VOID)cpu;
167
168    taskInitParam.pfnTaskEntry = (TSK_ENTRY_FUNC)WorkerThread;
169    taskInitParam.uwStackSize  = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
170    taskInitParam.pcName       = wq->name;
171    taskInitParam.usTaskPrio   = 1;
172    taskInitParam.auwArgs[0]   = (UINTPTR)cwq;
173    taskInitParam.uwResved     = LOS_TASK_STATUS_DETACHED;
174
175    ret = LOS_TaskCreate(&cwq->wq->wq_id, &taskInitParam);
176    if (ret != LOS_OK) {
177        return LOS_NOK;
178    }
179
180    cwq->thread = (task_struct*)OS_TCB_FROM_TID(cwq->wq->wq_id);
181    (VOID)LOS_TaskYield();
182
183    return LOS_OK;
184}
185
186STATIC VOID WorkerThread(cpu_workqueue_struct *cwqParam)
187{
188    cpu_workqueue_struct *cwq = cwqParam;
189
190    for (;;) {
191        if (WorkqueueIsEmpty(cwq)) {
192            (VOID)LOS_EventRead(&(cwq->wq->wq_event), 0x01, LOS_WAITMODE_OR | LOS_WAITMODE_CLR, LOS_WAIT_FOREVER);
193        }
194        RunWorkqueue(cwq);
195    }
196}
197
198STATIC VOID RunWorkqueue(cpu_workqueue_struct *cwq)
199{
200    struct work_struct *work = NULL;
201    UINT32 intSave;
202    work_func_t func = NULL;
203
204    LOS_SpinLockSave(&g_workqueueSpin, &intSave);
205
206    if (!WorkqueueIsEmpty(cwq)) {
207        work = worklist_entry(cwq->worklist.next, struct work_struct, entry);
208        work->work_status |= WORK_STRUCT_RUNNING;
209        list_del_init(cwq->worklist.next);
210        func = work->func;
211
212        cwq->current_work = work;
213        LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
214        func(work);
215        LOS_SpinLockSave(&g_workqueueSpin, &intSave);
216        cwq->current_work = NULL;
217
218        if (work->work_status & WORK_STRUCT_RUNNING) {
219            work->work_status &= ~(WORK_STRUCT_RUNNING | WORK_STRUCT_PENDING);
220        }
221    }
222
223    LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
224}
225
226STATIC VOID ListAdd(struct list_head *newNode, struct list_head *prev, struct list_head *next)
227{
228    next->prev = newNode;
229    newNode->next = next;
230    newNode->prev = prev;
231    prev->next = newNode;
232}
233
234#ifdef WORKQUEUE_SUPPORT_PRIORITY
235STATIC VOID WorkListAdd(struct list_head *newNode, struct list_head *head, UINT32 workPri)
236{
237    struct work_struct *work = NULL;
238    struct list_head *list = head;
239    do {
240        list = list->next;
241        if (list == head) {
242            break;
243        }
244        work = worklist_entry(list, struct work_struct, entry);
245    } while (work->work_pri <= workPri);
246
247    ListAdd(newNode, list->prev, list);
248}
249#else
250STATIC VOID WorkListAddTail(struct list_head *newNode, struct list_head *head)
251{
252    ListAdd(newNode, head->prev, head);
253}
254#endif
255
256STATIC VOID InsertWork(cpu_workqueue_struct *cwq, struct work_struct *work, struct list_head *head, UINT32 *intSave)
257{
258#ifdef WORKQUEUE_SUPPORT_PRIORITY
259    WorkListAdd(&work->entry, head, work->work_pri);
260#else
261    WorkListAddTail(&work->entry, head);
262#endif
263    LOS_SpinUnlockRestore(&g_workqueueSpin, *intSave);
264    (VOID)LOS_EventWrite(&(cwq->wq->wq_event), 0x01);
265    LOS_SpinLockSave(&g_workqueueSpin, intSave);
266}
267
268STATIC VOID QueueWork(cpu_workqueue_struct *cwq, struct work_struct *work, UINT32 *intSave)
269{
270    InsertWork(cwq, work, &cwq->worklist, intSave);
271}
272
273STATIC bool QueueWorkOn(struct workqueue_struct *wq, struct work_struct *work, UINT32 *intSave)
274{
275    bool ret = FALSE;
276    struct work_struct *tmpWork = NULL;
277
278    if (WorkqueueIsEmpty(wq->cpu_wq)) {
279        ret = TRUE;
280    } else {
281        LIST_FOR_WORK(tmpWork, &wq->cpu_wq->worklist, struct work_struct, entry) {
282            if (tmpWork == work) {
283                return FALSE;
284            }
285        }
286        ret = TRUE;
287    }
288    QueueWork(wq->cpu_wq, work, intSave);
289
290    return ret;
291}
292
293bool linux_queue_work(struct workqueue_struct *wq, struct work_struct *work)
294{
295    bool ret = FALSE;
296    UINT32 intSave;
297
298    if ((wq == NULL) || (wq->name == NULL) || (work == NULL)) {
299        return FALSE;
300    }
301    LOS_SpinLockSave(&g_workqueueSpin, &intSave);
302
303    work->work_status = WORK_STRUCT_PENDING;
304    ret = QueueWorkOn(wq, work, &intSave);
305
306    LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
307    return ret;
308}
309
310bool linux_cancel_work_sync(struct work_struct *work)
311{
312    bool ret = FALSE;
313
314    if (work == NULL) {
315        return FALSE;
316    }
317
318    if (!work->work_status) {
319        ret = FALSE;
320    } else if (work->work_status & WORK_STRUCT_RUNNING) {
321        ret = FALSE;
322    } else {
323        ret = TRUE;
324    }
325    while (work->work_status) {
326        (VOID)usleep(DELAY_TIME);
327    }
328    return ret;
329}
330
331bool linux_flush_work(struct work_struct *work)
332{
333    if (work == NULL) {
334        return FALSE;
335    }
336
337    if (work->work_status & WORK_STRUCT_PENDING) {
338        while (work->work_status) {
339            (VOID)usleep(DELAY_TIME);
340        }
341        return TRUE;
342    }
343    return FALSE;
344}
345
346STATIC VOID DelayedWorkTimerFunc(unsigned long data)
347{
348    struct delayed_work *dwork = (struct delayed_work *)data;
349    UINT32 intSave;
350
351    LOS_SpinLockSave(&g_workqueueSpin, &intSave);
352    /* It should have been called from irqsafe timer with irq already off. */
353    dwork->wq->delayed_work_count--;
354    (VOID)del_timer(&dwork->timer);
355    (VOID)QueueWorkOn(dwork->wq, &dwork->work, &intSave);
356    LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
357}
358
359STATIC BOOL OsDelayWorkQueueCond(UINTPTR sortList, UINTPTR dwork)
360{
361    SWTMR_CTRL_S *swtmr = LOS_DL_LIST_ENTRY(sortList, SWTMR_CTRL_S, stSortList);
362    return (((struct delayed_work *)swtmr->uwArg) == (struct delayed_work *)dwork);
363}
364
365bool queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned int delayTime)
366{
367    UINT32 intSave;
368    struct work_struct *work = NULL;
369
370    if ((wq == NULL) || (wq->name == NULL) || (wq->cpu_wq == NULL) || (dwork == NULL)) {
371        return FALSE;
372    }
373
374    dwork->wq = wq;
375    if (delayTime == 0) {
376        return queue_work(dwork->wq, &dwork->work);
377    }
378
379    LOS_SpinLockSave(&g_workqueueSpin, &intSave);
380    if (OsSwtmrWorkQueueFind(OsDelayWorkQueueCond, (UINTPTR)dwork)) {
381        LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
382        return FALSE;
383    }
384
385    if (!WorkqueueIsEmpty(wq->cpu_wq)) {
386        LIST_FOR_WORK(work, &wq->cpu_wq->worklist, struct work_struct, entry) {
387            if (work == &dwork->work) {
388                LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
389                return FALSE;
390            }
391        }
392    }
393    dwork->timer.expires = delayTime;
394    add_timer(&dwork->timer);
395    wq->delayed_work_count++;
396    dwork->work.work_status = WORK_STRUCT_PENDING;
397    LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
398    return TRUE;
399}
400
401
402STATIC BOOL OsDelayWorkCancelCond(UINTPTR sortList, UINTPTR dwork)
403{
404    SWTMR_CTRL_S *swtmr = LOS_DL_LIST_ENTRY(sortList, SWTMR_CTRL_S, stSortList);
405    if ((swtmr->usTimerID == ((struct delayed_work *)dwork)->timer.timerid) &&
406        (swtmr->ucState == OS_SWTMR_STATUS_TICKING)) {
407        return TRUE;
408    }
409    return FALSE;
410}
411
412bool linux_cancel_delayed_work(struct delayed_work *dwork)
413{
414    struct work_struct *work = NULL;
415    struct work_struct *workNext = NULL;
416    UINT32 intSave;
417    bool ret = FALSE;
418
419    if ((dwork == NULL) || (dwork->wq == NULL)) {
420        return FALSE;
421    }
422
423    LOS_SpinLockSave(&g_workqueueSpin, &intSave);
424
425    if (dwork->work.work_status & WORK_STRUCT_PENDING) {
426        if (OsSwtmrWorkQueueFind(OsDelayWorkCancelCond, (UINTPTR)dwork)) {
427            (VOID)del_timer(&dwork->timer);
428            dwork->work.work_status = 0;
429            dwork->wq->delayed_work_count--;
430            LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
431            return TRUE;
432        }
433
434        if (dwork->work.work_status & WORK_STRUCT_RUNNING) {
435            ret = FALSE;
436        } else if (dwork->work.work_status & WORK_STRUCT_PENDING) {
437            LIST_FOR_WORK_DEL(work, workNext, &dwork->wq->cpu_wq->worklist, struct work_struct, entry) {
438                if (work == &dwork->work) {
439                    list_del_init(&work->entry);
440                    dwork->work.work_status = 0;
441                    ret = TRUE;
442                    break;
443                }
444            }
445        }
446    }
447
448    LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
449    return ret;
450}
451
452bool linux_cancel_delayed_work_sync(struct delayed_work *dwork)
453{
454    return cancel_delayed_work(dwork);
455}
456
457bool linux_flush_delayed_work(struct delayed_work *dwork)
458{
459    UINT32 intSave;
460
461    if ((dwork == NULL) || (dwork->wq == NULL)) {
462        return FALSE;
463    }
464
465    LOS_SpinLockSave(&g_workqueueSpin, &intSave);
466    if (!(dwork->work.work_status & WORK_STRUCT_PENDING)) {
467        LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
468        return FALSE;
469    }
470
471    if (OsSwtmrWorkQueueFind(OsDelayWorkCancelCond, (UINTPTR)dwork)) {
472        (VOID)del_timer(&dwork->timer);
473        dwork->wq->delayed_work_count--;
474        (VOID)queue_work(dwork->wq, &dwork->work);
475    } else {
476        LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
477    }
478    (VOID)flush_work(&dwork->work);
479    return TRUE;
480}
481
482unsigned int linux_work_busy(struct work_struct *work)
483{
484    UINT32 ret = 0;
485
486    if (work == NULL) {
487        return FALSE;
488    }
489
490    if (work->work_status & WORK_STRUCT_PENDING) {
491        ret |= WORK_BUSY_PENDING;
492    }
493    if (work->work_status & WORK_STRUCT_RUNNING) {
494        ret |= WORK_BUSY_RUNNING;
495    }
496    return ret;
497}
498
499bool linux_schedule_work(struct work_struct *work)
500{
501    bool ret = FALSE;
502    UINT32 intSave;
503
504    if ((g_pstSystemWq == NULL) || (g_pstSystemWq->name == NULL) || (work == NULL)) {
505        return FALSE;
506    }
507
508    LOS_SpinLockSave(&g_workqueueSpin, &intSave);
509
510    work->work_status = WORK_STRUCT_PENDING;
511    ret = QueueWorkOn(g_pstSystemWq, work, &intSave);
512
513    LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
514    return ret;
515}
516
517bool linux_schedule_delayed_work(struct delayed_work *dwork, unsigned int delayTime)
518{
519    bool ret = FALSE;
520
521    if ((g_pstSystemWq == NULL) || (dwork == NULL)) {
522        return FALSE;
523    }
524
525    ret = queue_delayed_work(g_pstSystemWq, dwork, delayTime);
526
527    return ret;
528}
529
530static void drain_workqueue(struct workqueue_struct *wq)
531{
532    UINT32 intSave;
533    while (1) {
534        (VOID)usleep(DELAY_TIME);
535        LOS_SpinLockSave(&g_workqueueSpin, &intSave);
536        if (WorkqueueIsEmpty(wq->cpu_wq) && (wq->delayed_work_count == 0)) {
537            break;
538        }
539
540        LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
541    }
542    LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
543}
544
545void linux_destroy_workqueue(struct workqueue_struct *wq)
546{
547    UINT32 intSave;
548    if (wq == NULL) {
549        return;
550    }
551
552    /* Drain it before proceeding with destruction */
553    drain_workqueue(wq);
554
555    (VOID)LOS_TaskDelete(wq->wq_id);
556
557    LOS_SpinLockSave(&g_workqueueSpin, &intSave);
558    wq->name = NULL;
559    list_del_init(&wq->list);
560    (VOID)LOS_EventDestroy(&(wq->wq_event));
561
562    (VOID)LOS_MemFree(m_aucSysMem0, wq->cpu_wq);
563    (VOID)LOS_MemFree(m_aucSysMem0, wq);
564    LOS_SpinUnlockRestore(&g_workqueueSpin, intSave);
565}
566
567UINT32 OsSysWorkQueueInit(VOID)
568{
569    g_pstSystemWq = create_workqueue("system_wq");
570
571    return LOS_OK;
572}
573
574LOS_MODULE_INIT(OsSysWorkQueueInit, LOS_INIT_LEVEL_KMOD_EXTENDED);
575