feat(kworkqueue): Added work queue, fixed object lookup error put.
1.Added a work queue that supports instant and delayed start of multithreading (can be used without os, multithreading needs to create its own thread) to improve concurrency. 2.By adding parent2 to save the parent node, fixed an issue where forgetting the parent object changed when looking for an object, resulting in the wrong parent object being put when exiting.
This commit is contained in:
@@ -50,6 +50,13 @@ menu "Kernel options"
|
||||
default n
|
||||
help
|
||||
This option controls whether to use ktimer attribute.
|
||||
# WORKQUEUE
|
||||
config MR_USE_KWORKQUEUE
|
||||
depends on MR_USE_KTIMER
|
||||
bool "Use kworkqueue"
|
||||
default n
|
||||
help
|
||||
This option controls whether to use kworkqueue.
|
||||
# OS
|
||||
source "include/kernel/os/Kconfig"
|
||||
endmenu
|
||||
|
||||
@@ -109,14 +109,12 @@ mr_err_t mr_ktimer_stop(mr_ktimer_t *ktimer);
|
||||
mr_err_t mr_ktimer_tick_set(mr_ktimer_t *ktimer, mr_tick_t tick);
|
||||
|
||||
/**
|
||||
* @brief This function check if a ktimer is running.
|
||||
* @brief This macro function gets the tick of a ktimer.
|
||||
*
|
||||
* @param ktimer The ktimer.
|
||||
* @return MR_TRUE if the ktimer is running, MR_FALSE otherwise.
|
||||
* @return The tick of the ktimer.
|
||||
*/
|
||||
MR_INLINE mr_bool_t mr_ktimer_is_running(mr_ktimer_t *ktimer) {
|
||||
return mr_klist_is_empty(&ktimer->list);
|
||||
}
|
||||
#define MR_KTIMER_TICK(_ktimer) (((mr_ktimer_t *)(_ktimer))->init_tick)
|
||||
|
||||
/**
|
||||
* @brief This function find a ktimer.
|
||||
|
||||
166
include/kernel/mr_kworkqueue.h
Normal file
166
include/kernel/mr_kworkqueue.h
Normal file
@@ -0,0 +1,166 @@
|
||||
/**
|
||||
* @copyright (c) 2024, MacRsh
|
||||
*
|
||||
* @license SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @date 2024-09-06 MacRsh First version
|
||||
*/
|
||||
|
||||
#ifndef __MR_KWORKQUEUE_H__
|
||||
#define __MR_KWORKQUEUE_H__
|
||||
|
||||
#include <mr_config.h>
|
||||
#if defined(MR_USE_KWORKQUEUE) && defined(MR_USE_KTIMER)
|
||||
#include <kernel/mr_ktimer.h>
|
||||
#endif /* defined(MR_USE_KWORKQUEUE) && defined(MR_USE_KTIMER) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#if defined(MR_USE_KWORKQUEUE) && defined(MR_USE_KTIMER)
|
||||
/* Kworkqueue type */
|
||||
typedef struct mr_kworkqueue {
|
||||
mr_kobject_t parent;
|
||||
mr_ktimer_t timer;
|
||||
mr_atomic_t dying;
|
||||
mr_klist_t tlist;
|
||||
mr_klist_t list;
|
||||
} mr_kworkqueue_t;
|
||||
|
||||
/* Kwork type */
|
||||
typedef struct mr_kwork {
|
||||
mr_klist_t list;
|
||||
mr_tick_t tick;
|
||||
mr_ptr_t entry;
|
||||
mr_ptr_t args;
|
||||
} mr_kwork_t;
|
||||
|
||||
/**
|
||||
* @brief This macro function checks if a kworkqueue is initialized.
|
||||
*
|
||||
* @param _kqueue The kworkqueue.
|
||||
* @return MR_TRUE if the kworkqueue is initialized, MR_FALSE otherwise.
|
||||
*/
|
||||
#define MR_KWORKQUEUE_IS_INITED(_kqueue) \
|
||||
MR_KOBJECT_IS_INITED(_kqueue)
|
||||
|
||||
/**
|
||||
* @brief This macro function initialize a kworkqueue.
|
||||
*
|
||||
* @param kqueue The kworkqueue.
|
||||
* @param name The kworkqueue name.
|
||||
* @return 0 on success, or a negative error code on failure.
|
||||
*/
|
||||
mr_err_t mr_kworkqueue_init(mr_kworkqueue_t *kqueue, const char *name);
|
||||
|
||||
/**
|
||||
* @brief This macro function create a kworkqueue.
|
||||
*
|
||||
* @param name The kworkqueue name.
|
||||
* @return The kworkqueue on success, or MR_NULL on failure.
|
||||
*/
|
||||
mr_kworkqueue_t *mr_kworkqueue_create(const char *name);
|
||||
|
||||
/**
|
||||
* @brief This function delete a kworkqueue.
|
||||
*
|
||||
* @param kqueue The kworkqueue.
|
||||
* @return 0 on success, or a negative error code on failure.
|
||||
*/
|
||||
mr_err_t mr_kworkqueue_del(mr_kworkqueue_t *kqueue);
|
||||
|
||||
/**
|
||||
* @brief This function execute a kworkqueue.
|
||||
*
|
||||
* @param kqueue The kworkqueue.
|
||||
* return 0 on success, or a negative error code on failure.
|
||||
*/
|
||||
mr_err_t mr_kworkqueue_execute(mr_kworkqueue_t *kqueue);
|
||||
|
||||
/**
|
||||
* @brief This macro function initialize a kwork.
|
||||
*
|
||||
* @param _kwork The kwork.
|
||||
* @param _entry The kwork entry.
|
||||
* @param _args The kwork args.
|
||||
*/
|
||||
#define MR_KWORK_INIT(_kwork, _entry, _args) \
|
||||
{ \
|
||||
.list = MR_KLIST_INIT(&(_kwork)->list), .tick = 0, .entry = (_entry), \
|
||||
.args = (_args) \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function initialize a kwork.
|
||||
*
|
||||
* @param kwork The kwork.
|
||||
* @param entry The kwork entry.
|
||||
* @param args The kwork args.
|
||||
* @return 0 on success, or a negative error code on failure.
|
||||
*/
|
||||
mr_err_t mr_kwork_init(mr_kwork_t *kwork,
|
||||
void (*entry)(mr_kwork_t *kwork, void *args),
|
||||
void *args);
|
||||
|
||||
/**
|
||||
* @brief This function delete a kwork.
|
||||
*
|
||||
* @param kwork The kwork.
|
||||
* @return 0 on success, or a negative error code on failure.
|
||||
*/
|
||||
mr_err_t mr_kwork_del(mr_kwork_t *kwork);
|
||||
|
||||
/**
|
||||
* @brief This function add a kwork to a kworkqueue.
|
||||
*
|
||||
* @param kqueue The kworkqueue.
|
||||
* @param kwork The kwork.
|
||||
* @return 0 on success, or a negative error code on failure.
|
||||
*/
|
||||
mr_err_t mr_kworkqueue_work(mr_kworkqueue_t *kqueue, mr_kwork_t *kwork);
|
||||
|
||||
/**
|
||||
* @brief This function add a kdelayed-work to a kworkqueue.
|
||||
*
|
||||
* @param kqueue The kworkqueue.
|
||||
* @param kwork The kdelayed-work.
|
||||
* @param tick The kdelayed-work tick.
|
||||
* @return 0 on success, or a negative error code on failure.
|
||||
*/
|
||||
mr_err_t mr_kworkqueue_delayed_work(mr_kworkqueue_t *kqueue, mr_kwork_t *kwork,
|
||||
mr_tick_t tick);
|
||||
|
||||
/**
|
||||
* @brief This function find a kworkqueue.
|
||||
*
|
||||
* @param name The kworkqueue name.
|
||||
* @return The kworkqueue on success, or MR_NULL on failure.
|
||||
*/
|
||||
mr_kworkqueue_t *mr_kworkqueue_find(const char *name);
|
||||
|
||||
/**
|
||||
* @brief This function get a kworkqueue.
|
||||
*
|
||||
* @param kqueue The kworkqueue.
|
||||
* @return The kworkqueue on success, or MR_NULL on failure.
|
||||
*/
|
||||
MR_INLINE mr_kworkqueue_t *mr_kworkqueue_get(mr_kworkqueue_t *kqueue) {
|
||||
return (mr_kworkqueue_t *)mr_kobject_get((mr_kobject_t *)kqueue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function put a kworkqueue.
|
||||
*
|
||||
* @param kqueue The kworkqueue.
|
||||
*/
|
||||
MR_INLINE void mr_kworkqueue_put(mr_kworkqueue_t *kqueue) {
|
||||
mr_kobject_put((mr_kobject_t *)kqueue);
|
||||
}
|
||||
#endif /* defined(MR_USE_KWORKQUEUE) && defined(MR_USE_KTIMER) */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __MR_KWORKQUEUE_H__ */
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <kernel/mr_kspinlock.h>
|
||||
#include <kernel/mr_ktimer.h>
|
||||
#include <kernel/mr_kversion.h>
|
||||
#include <kernel/mr_kworkqueue.h>
|
||||
#include <kernel/os/mr_kmutex.h>
|
||||
#include <kernel/os/mr_kthread.h>
|
||||
|
||||
|
||||
@@ -341,8 +341,8 @@ void mr_kobject_del(mr_kobject_t *kobj) {
|
||||
MR_INLINE mr_kobject_t *kobject_lookup_varg(mr_kobject_t *parent,
|
||||
const char *fmt, mr_va_list args) {
|
||||
char ipath[MR_CFG_KOBJECT_INAME_SIZE * 2];
|
||||
mr_kobject_t *parent2, *kobj;
|
||||
const char *path, *path2;
|
||||
mr_kobject_t *kobj;
|
||||
mr_err_t ret;
|
||||
int mask;
|
||||
|
||||
@@ -350,6 +350,9 @@ MR_INLINE mr_kobject_t *kobject_lookup_varg(mr_kobject_t *parent,
|
||||
parent = mr_kobject_get(parent);
|
||||
if (!parent) {
|
||||
return MR_NULL;
|
||||
} else {
|
||||
/* Save parent for put */
|
||||
parent2 = parent;
|
||||
}
|
||||
|
||||
/* Lock */
|
||||
@@ -375,7 +378,7 @@ _exit:
|
||||
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
||||
|
||||
/* Put parent */
|
||||
mr_kobject_put(parent);
|
||||
mr_kobject_put(parent2);
|
||||
return kobj;
|
||||
}
|
||||
|
||||
|
||||
@@ -241,6 +241,8 @@ MR_INLINE void ktimer_release2_kobj(mr_kobject_t *kobj) {
|
||||
MR_INLINE void ktimer_timeout_check(void) {
|
||||
mr_ktimer_t *ktimer;
|
||||
mr_klist_t list;
|
||||
mr_ptr_t entry;
|
||||
mr_ptr_t args;
|
||||
int mask;
|
||||
|
||||
/* Operation list */
|
||||
@@ -262,11 +264,15 @@ MR_INLINE void ktimer_timeout_check(void) {
|
||||
/* Move ktimer into operation list */
|
||||
mr_klist_move(&ktimer->list, &list);
|
||||
|
||||
/* Save ktimer entry and args */
|
||||
entry = ktimer->entry;
|
||||
args = ktimer->args;
|
||||
|
||||
/* Unlock */
|
||||
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
||||
|
||||
/* Call ktimer entry */
|
||||
((void (*)(mr_ktimer_t *, void *))ktimer->entry)(ktimer, ktimer->args);
|
||||
((void (*)(mr_ktimer_t *, void *))entry)(ktimer, args);
|
||||
|
||||
/* Lock */
|
||||
mask = mr_kspinlock_lock_irqsave(&klock);
|
||||
|
||||
447
kernel/kworkqueue.c
Normal file
447
kernel/kworkqueue.c
Normal file
@@ -0,0 +1,447 @@
|
||||
/**
|
||||
* @copyright (c) 2024, MacRsh
|
||||
*
|
||||
* @license SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @date 2024-09-06 MacRsh First version
|
||||
*/
|
||||
|
||||
#include <kernel/mr_kworkqueue.h>
|
||||
#if defined(MR_USE_KWORKQUEUE) && defined(MR_USE_KTIMER)
|
||||
#include <kernel/mr_initcall.h>
|
||||
#include <kernel/mr_kspinlock.h>
|
||||
#include <libc/mr_malloc.h>
|
||||
|
||||
/* Kworkqueue ktype */
|
||||
static mr_ktype_t ktype1, ktype2;
|
||||
/* Kworkqueue lock */
|
||||
static mr_kspinlock_t klock = MR_KSPINLOCK_INIT();
|
||||
/* Kworkqueue kset */
|
||||
static mr_kset_t kroot = MR_KSET_INIT(&kroot, MR_NULL);
|
||||
|
||||
MR_INLINE void kqueue_timer_entry(mr_ktimer_t *ktimer, void *args) {
|
||||
mr_kworkqueue_t *kqueue;
|
||||
mr_kwork_t *kwork;
|
||||
mr_tick_t tick;
|
||||
int mask;
|
||||
|
||||
/* Get kworkqueue */
|
||||
kqueue = mr_kworkqueue_get(MR_CONTAINER_OF(ktimer, mr_kworkqueue_t, timer));
|
||||
if (!kqueue) {
|
||||
/* Kworkqueue is not exist */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Lock */
|
||||
mask = mr_kspinlock_lock_irqsave(&klock);
|
||||
|
||||
/* Check timeout kwork */
|
||||
for (tick = 0; !mr_klist_is_empty(&kqueue->tlist);) {
|
||||
kwork = MR_CONTAINER_OF(kqueue->tlist.next, mr_kwork_t, list);
|
||||
|
||||
/* Check timeout tick */
|
||||
if ((mr_kclock_tick() - kwork->tick) < (MR_TICK_MAX / 2)) {
|
||||
/* Check kworkqueue is started(work) for the first time */
|
||||
if (mr_klist_is_empty(&kqueue->list)) {
|
||||
/* Get kworkqueue run-time(work) */
|
||||
mr_kworkqueue_get(kqueue);
|
||||
}
|
||||
|
||||
/* Move kwork to kworkqueue work list */
|
||||
mr_klist_move(&kwork->list, &kqueue->list);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* There will be no timeout kwork after that */
|
||||
tick = kwork->tick - mr_kclock_tick();
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check is need timer */
|
||||
if ((tick != 0) && (tick != MR_KTIMER_TICK(&kqueue->timer))) {
|
||||
/* Set to optimal tick */
|
||||
mr_ktimer_tick_set(&kqueue->timer, tick | MR_KTIMER_PERIODIC);
|
||||
mr_ktimer_start(&kqueue->timer);
|
||||
|
||||
/* Check kworkqueue is stopped(delay-work) for the last time */
|
||||
} else if (mr_klist_is_empty(&kqueue->tlist)) {
|
||||
/* Don't need timer */
|
||||
mr_ktimer_stop(&kqueue->timer);
|
||||
|
||||
/* Put kworkqueue run-time(delay-work) */
|
||||
mr_kworkqueue_put(kqueue);
|
||||
}
|
||||
|
||||
/* Unlock */
|
||||
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
||||
|
||||
/* Put kworkqueue */
|
||||
mr_kworkqueue_put(kqueue);
|
||||
}
|
||||
|
||||
MR_INLINE mr_err_t kqueue_init(mr_kworkqueue_t *kqueue, const char *name) {
|
||||
mr_err_t ret;
|
||||
|
||||
/* Init kworkqueue timer */
|
||||
ret = mr_ktimer_init(&kqueue->timer, name, kqueue_timer_entry, MR_NULL, 1);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Init kworkqueue */
|
||||
mr_atomic_init(&kqueue->dying, MR_FALSE);
|
||||
mr_klist_init(&kqueue->tlist);
|
||||
mr_klist_init(&kqueue->list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MR_INLINE void kqueue_del(mr_kworkqueue_t *kqueue) {
|
||||
mr_kwork_t *w;
|
||||
int mask;
|
||||
|
||||
/* Delete kworkqueue timer */
|
||||
mr_ktimer_del(&kqueue->timer);
|
||||
|
||||
/* Mark as dying */
|
||||
mr_atomic_store(&kqueue->dying, MR_TRUE);
|
||||
|
||||
/* Lock */
|
||||
mask = mr_kspinlock_lock_irqsave(&klock);
|
||||
|
||||
/* Remove kwork from kworkqueue timer list */
|
||||
while (!mr_klist_is_empty(&kqueue->tlist)) {
|
||||
w = MR_KLIST_ENTRY(&kqueue->tlist.next, mr_kwork_t, list);
|
||||
mr_klist_del(&w->list);
|
||||
}
|
||||
|
||||
/* Remove kwork from kworkqueue list */
|
||||
while (!mr_klist_is_empty(&kqueue->list)) {
|
||||
w = MR_KLIST_ENTRY(&kqueue->list.next, mr_kwork_t, list);
|
||||
mr_klist_del(&w->list);
|
||||
}
|
||||
|
||||
/* Unlock */
|
||||
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
||||
}
|
||||
|
||||
MR_INLINE mr_err_t kqueue_init_add(mr_kworkqueue_t *kqueue, mr_ktype_t *ktype,
|
||||
const char *name) {
|
||||
mr_err_t ret;
|
||||
|
||||
/* Init kworkqueue */
|
||||
ret = kqueue_init(kqueue, name);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Init kobject */
|
||||
mr_kobject_init((mr_kobject_t *)kqueue, ktype);
|
||||
|
||||
/* Add kobject to kroot */
|
||||
ret = mr_kobject_add((mr_kobject_t *)kqueue, (mr_kobject_t *)&kroot, name);
|
||||
if (ret != 0) {
|
||||
kqueue_del(kqueue);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
mr_err_t mr_kworkqueue_init(mr_kworkqueue_t *kqueue, const char *name) {
|
||||
/* Check arguments */
|
||||
if ((!kqueue) || MR_KWORKQUEUE_IS_INITED(kqueue) || (!name)) {
|
||||
return -MR_EINVAL;
|
||||
}
|
||||
|
||||
/* Init and add kworkqueue */
|
||||
return kqueue_init_add(kqueue, &ktype1, name);
|
||||
}
|
||||
|
||||
mr_kworkqueue_t *mr_kworkqueue_create(const char *name) {
|
||||
mr_kworkqueue_t *kqueue;
|
||||
mr_err_t ret;
|
||||
|
||||
/* Check arguments */
|
||||
if (!name) {
|
||||
return MR_NULL;
|
||||
}
|
||||
|
||||
/* Create kworkqueue */
|
||||
kqueue = mr_malloc(sizeof(mr_kworkqueue_t));
|
||||
if (!kqueue) {
|
||||
return MR_NULL;
|
||||
}
|
||||
|
||||
/* Init and add kworkqueue */
|
||||
ret = kqueue_init_add(kqueue, &ktype2, name);
|
||||
if (ret != 0) {
|
||||
mr_free(kqueue);
|
||||
return MR_NULL;
|
||||
}
|
||||
return kqueue;
|
||||
}
|
||||
|
||||
mr_err_t mr_kworkqueue_del(mr_kworkqueue_t *kqueue) {
|
||||
/* Check arguments */
|
||||
if ((!kqueue) || (!MR_KWORKQUEUE_IS_INITED(kqueue))) {
|
||||
return -MR_EINVAL;
|
||||
}
|
||||
|
||||
/* Mark as dying */
|
||||
mr_atomic_store(&kqueue->dying, MR_TRUE);
|
||||
|
||||
/* Delete kobject */
|
||||
mr_kobject_del((mr_kobject_t *)kqueue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MR_INLINE void kqueue_execute(mr_kworkqueue_t *kqueue) {
|
||||
mr_kwork_t *kwork;
|
||||
mr_ptr_t entry;
|
||||
void *args;
|
||||
int mask;
|
||||
|
||||
/* Lock */
|
||||
mask = mr_kspinlock_lock_irqsave(&klock);
|
||||
|
||||
/* Check kworkqueue work list */
|
||||
while (!mr_klist_is_empty(&kqueue->list)) {
|
||||
kwork = MR_KLIST_ENTRY(kqueue->list.next, mr_kwork_t, list);
|
||||
|
||||
/* Remove kwork from kworkqueue list */
|
||||
mr_klist_del(&kwork->list);
|
||||
|
||||
/* Check kworkqueue is stopped(work) for the last time */
|
||||
if (mr_klist_is_empty(&kqueue->list)) {
|
||||
/* Put kworkqueue run-time(work) */
|
||||
mr_kworkqueue_put(kqueue);
|
||||
}
|
||||
|
||||
/* Save kwork entry and args */
|
||||
entry = kwork->entry;
|
||||
args = kwork->args;
|
||||
|
||||
/* Unlock */
|
||||
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
||||
|
||||
/* Call kwork entry */
|
||||
((void (*)(mr_kwork_t *, void *))entry)(kwork, args);
|
||||
}
|
||||
|
||||
/* Unlock */
|
||||
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
||||
}
|
||||
|
||||
mr_err_t mr_kworkqueue_execute(mr_kworkqueue_t *kqueue) {
|
||||
/* Check arguments */
|
||||
if ((!kqueue) || (!MR_KWORKQUEUE_IS_INITED(kqueue))) {
|
||||
return -MR_EINVAL;
|
||||
}
|
||||
|
||||
/* Execute kworkqueue */
|
||||
kqueue_execute(kqueue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mr_err_t mr_kwork_init(mr_kwork_t *kwork,
|
||||
void (*entry)(mr_kwork_t *kwork, void *args),
|
||||
void *args) {
|
||||
/* Check arguments */
|
||||
if ((!kwork) || (!entry)) {
|
||||
return -MR_EINVAL;
|
||||
}
|
||||
|
||||
/* Init kwork */
|
||||
mr_klist_init(&kwork->list);
|
||||
kwork->entry = entry;
|
||||
kwork->args = args;
|
||||
return 0;
|
||||
}
|
||||
|
||||
MR_INLINE void kwork_del(mr_kwork_t *kwork) {
|
||||
int mask;
|
||||
|
||||
/* Lock */
|
||||
mask = mr_kspinlock_lock_irqsave(&klock);
|
||||
|
||||
/* Check kwork is deleted */
|
||||
if (!mr_klist_is_empty(&kwork->list)) {
|
||||
/* Remove kwork from kworkqueue list */
|
||||
mr_klist_del(&kwork->list);
|
||||
|
||||
/* Mark kwork as deleted */
|
||||
kwork->entry = MR_NULL;
|
||||
}
|
||||
|
||||
/* Unlock */
|
||||
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
||||
}
|
||||
|
||||
mr_err_t mr_kwork_del(mr_kwork_t *kwork) {
|
||||
/* Check arguments */
|
||||
if ((!kwork) || (!kwork->entry)) {
|
||||
return -MR_EINVAL;
|
||||
}
|
||||
|
||||
/* Delete kwork */
|
||||
kwork_del(kwork);
|
||||
return 0;
|
||||
}
|
||||
|
||||
MR_INLINE mr_err_t kqueue_work(mr_kworkqueue_t *kqueue, mr_kwork_t *kwork) {
|
||||
mr_err_t ret;
|
||||
int mask;
|
||||
|
||||
/* Lock */
|
||||
mask = mr_kspinlock_lock_irqsave(&klock);
|
||||
|
||||
/* Check kwork is working */
|
||||
if (!mr_klist_is_empty(&kwork->list)) {
|
||||
ret = -MR_EBUSY;
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
/* Check kworkqueue is dying */
|
||||
if (mr_atomic_load(&kqueue->dying)) {
|
||||
ret = -MR_EINVAL;
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
/* Add kwork to kworkqueue list */
|
||||
mr_klist_add(&kqueue->list, &kwork->list);
|
||||
ret = 0;
|
||||
_exit:
|
||||
/* Unlock */
|
||||
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mr_err_t mr_kworkqueue_work(mr_kworkqueue_t *kqueue, mr_kwork_t *kwork) {
|
||||
/* Check arguments */
|
||||
if ((!kqueue) || (!MR_KWORKQUEUE_IS_INITED(kqueue)) || (!kwork)
|
||||
|| (!kwork->entry)) {
|
||||
return -MR_EINVAL;
|
||||
}
|
||||
|
||||
/* Add kwork to kworkqueue */
|
||||
return kqueue_work(kqueue, kwork);
|
||||
}
|
||||
|
||||
MR_INLINE mr_err_t kqueue_delayed_work(mr_kworkqueue_t *kqueue,
|
||||
mr_kwork_t *kwork, mr_tick_t tick) {
|
||||
mr_klist_t *l;
|
||||
mr_kwork_t *w;
|
||||
mr_err_t ret;
|
||||
int mask;
|
||||
|
||||
/* Lock */
|
||||
mask = mr_kspinlock_lock_irqsave(&klock);
|
||||
|
||||
/* Check kwork is working */
|
||||
if (!mr_klist_is_empty(&((struct mr_kwork *)kwork)->list)) {
|
||||
ret = -MR_EBUSY;
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
/* Check kworkqueue is dying */
|
||||
if (mr_atomic_load(&kqueue->dying)) {
|
||||
ret = -MR_EINVAL;
|
||||
goto _exit;
|
||||
}
|
||||
|
||||
/* Check kworkqueue is started for the first time */
|
||||
if (mr_klist_is_empty(&kqueue->tlist)) {
|
||||
/* Get kworkqueue run-time(delay-work) */
|
||||
mr_kworkqueue_get(kqueue);
|
||||
}
|
||||
|
||||
/* Set timeout tick */
|
||||
tick = tick & ~MR_KTIMER_PERIODIC;
|
||||
kwork->tick = mr_kclock_tick() + tick;
|
||||
|
||||
/* Insert kwork into kworkqueue timer list */
|
||||
MR_KLIST_FOR_EACH(l, &kqueue->tlist) {
|
||||
w = MR_KLIST_ENTRY(l, mr_kwork_t, list);
|
||||
if ((kwork->tick - w->tick) < (MR_TICK_MAX / 2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Move kwork into position */
|
||||
mr_klist_move(&kwork->list, &w->list);
|
||||
goto _set;
|
||||
}
|
||||
|
||||
/* It is the last kwork */
|
||||
mr_klist_move(&kwork->list, &kqueue->tlist);
|
||||
_set:
|
||||
/* Check kwork is first */
|
||||
if (&kwork->list == kqueue->tlist.next) {
|
||||
/* Set to optimal tick */
|
||||
tick = kwork->tick - mr_kclock_tick();
|
||||
mr_ktimer_tick_set(&kqueue->timer, tick | MR_KTIMER_PERIODIC);
|
||||
mr_ktimer_start(&kqueue->timer);
|
||||
}
|
||||
ret = 0;
|
||||
_exit:
|
||||
/* Unlock */
|
||||
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mr_err_t mr_kworkqueue_delayed_work(mr_kworkqueue_t *kqueue, mr_kwork_t *kwork,
|
||||
mr_tick_t tick) {
|
||||
/* Check arguments */
|
||||
if ((!kqueue) || (!MR_KWORKQUEUE_IS_INITED(kqueue)) || (!kwork)
|
||||
|| (!kwork->entry) || (tick == 0)) {
|
||||
return -MR_EINVAL;
|
||||
}
|
||||
|
||||
/* Add kwork to kworkqueue */
|
||||
return kqueue_delayed_work(kqueue, kwork, tick);
|
||||
}
|
||||
|
||||
mr_kworkqueue_t *mr_kworkqueue_find(const char *name) {
|
||||
/* Lookup kworkqueue in kroot */
|
||||
return (mr_kworkqueue_t *)mr_kobject_lookup((mr_kobject_t *)&kroot, name);
|
||||
}
|
||||
|
||||
MR_INLINE mr_kworkqueue_t *kqueue_release_kobj(mr_kobject_t *kobj) {
|
||||
mr_kworkqueue_t *kqueue;
|
||||
|
||||
/* Get kworkqueue */
|
||||
kqueue = MR_CONTAINER_OF(kobj, mr_kworkqueue_t, parent);
|
||||
|
||||
/* Delete kworkqueue */
|
||||
kqueue_del(kqueue);
|
||||
return kqueue;
|
||||
}
|
||||
|
||||
MR_INLINE void kqueue_release1_kobj(mr_kobject_t *kobj) {
|
||||
/* Release kworkqueue */
|
||||
kqueue_release_kobj(kobj);
|
||||
}
|
||||
|
||||
MR_INLINE void kqueue_release2_kobj(mr_kobject_t *kobj) {
|
||||
mr_kworkqueue_t *kqueue;
|
||||
|
||||
/* Release kworkqueue */
|
||||
kqueue = kqueue_release_kobj(kobj);
|
||||
|
||||
/* Free kworkqueue */
|
||||
mr_free(kqueue);
|
||||
}
|
||||
|
||||
MR_INLINE void mr_kworkqueue_kset_init(void) {
|
||||
static mr_kattr_t *attrs[] = {MR_NULL};
|
||||
mr_err_t ret;
|
||||
|
||||
/* Register kset */
|
||||
ret = mr_kset_register(&kroot, "workqueue");
|
||||
if (ret != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Init ktype */
|
||||
mr_ktype_init(&ktype1, "workqueue", kqueue_release1_kobj, attrs);
|
||||
mr_ktype_init(&ktype2, "workqueue", kqueue_release2_kobj, attrs);
|
||||
}
|
||||
MR_INIT_EXPORT(mr_kworkqueue_kset_init, MR_INIT_LEVEL_KERNEL);
|
||||
#endif /* defined(MR_USE_KWORKQUEUE) && defined(MR_USE_KTIMER) */
|
||||
Reference in New Issue
Block a user