fix(initcall,kworkqueue): Optimize kernel initialization process, optimize kernel workqueue hook.

1.Kernel initialization abandons the section feature and uses it only for user initialization.
2.The kernel work queue is optimized, and the transfer of the work queue in the threading environment can be completed by obtaining the semaphore in the hook and releasing the semaphore by the wakeup node.
This commit is contained in:
MacRsh
2025-03-13 20:34:08 +08:00
parent e0342228ad
commit 9547c02b82
10 changed files with 138 additions and 327 deletions

View File

@@ -1,7 +1,6 @@
menu "Kernel options"
# ENTRY
config MR_USE_ENTRY
depends on !MR_USE_KOS
bool "Use entry"
default n
help
@@ -23,7 +22,7 @@ menu "Kernel options"
# KPRINTF
config MR_USE_KPRINTF
bool "Use kprintf"
default n
default y
help
This option controls whether to use kprintf.
config MR_CFG_KPRINTF_BUF_SIZE
@@ -42,7 +41,7 @@ menu "Kernel options"
# KTIMER
config MR_USE_KTIMER
bool "Use ktimer"
default n
default y
help
This option controls whether to use ktimer.
config MR_USE_KTIMER_ATTR
@@ -54,7 +53,7 @@ menu "Kernel options"
config MR_USE_KWORKQUEUE
depends on MR_USE_KTIMER
bool "Use kworkqueue"
default n
default y
help
This option controls whether to use kworkqueue.
config MR_USE_KWORKQUEUE_HOOK

View File

@@ -21,8 +21,7 @@ extern "C" {
*/
/* Initcall level definition */
#define MR_INIT_LEVEL_KERNEL "1"
#define MR_INIT_LEVEL_USER(_level) "2."_level
#define MR_INIT_LEVEL_USER(_level) "1."_level
/* Initcall type */
typedef struct mr_initcall {
@@ -46,12 +45,14 @@ typedef struct mr_initcall {
*
* @note User does not need to call this function.
*/
void mr_autoinit_kernel(void);
void mr_kernel_init(void);
/**
* @brief This function does initcalls(user).
*
* @note If the kernel does not start, it will start here.
*/
void mr_autoinit(void);
void mr_user_init(void);
/** @} */

View File

@@ -1,160 +0,0 @@
/**
* @copyright (c) 2024, MacRsh
*
* @license SPDX-License-Identifier: Apache-2.0
*
* @date 2024-09-06 MacRsh First version
*/
#ifndef __MR_KFIFO_H__
#define __MR_KFIFO_H__
#include <kernel/mr_kservice.h>
#include <libc/mr_compiler.h>
#include <libc/mr_types.h>
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/**
* @addtogroup Kfifo
* @{
*/
/* Kfifo type */
typedef struct mr_kfifo {
mr_uint8_t *buf;
mr_uint32_t size;
mr_uint32_t in;
mr_uint32_t out;
} mr_kfifo_t;
/**
* @brief This function initializes a kfifo.
*
* @param kfifo The kfifo to initialize.
* @param buf The buffer of the kfifo.
* @param size The size of the kfifo.
*/
void mr_kfifo_init(mr_kfifo_t *kfifo, void *buf, mr_uint32_t size);
/**
* @brief This function puts data into a kfifo.
*
* @param kfifo The kfifo to put data into.
* @param buf The data to put into the kfifo.
* @param size The size of the data.
* @return The number of bytes put into the kfifo.
*/
mr_uint32_t mr_kfifo_in(mr_kfifo_t *kfifo, const void *buf, mr_uint32_t size);
/**
* @brief This function takes data out of a kfifo.
*
* @param kfifo The kfifo to take data out of.
* @param buf The data to take out of the kfifo.
* @param size The size of the data.
* @return The number of bytes taken out of the kfifo.
*/
mr_uint32_t mr_kfifo_out(mr_kfifo_t *kfifo, void *buf, mr_uint32_t size);
/**
* @brief This function puts data into a kfifo, overwriting old data.
*
* @param kfifo The kfifo to put data into.
* @param buf The data to put into the kfifo.
* @param size The size of the data.
* @return The number of bytes put into the kfifo.
*/
mr_uint32_t mr_kfifo_in_overwrite(mr_kfifo_t *kfifo, const void *buf,
mr_uint32_t size);
/**
* @brief This function peeks data out of a kfifo.
*
* @param kfifo The kfifo to peek data out of.
* @param buf The data to peek out of the kfifo.
* @param size The size of the data.
* @return The number of bytes peeked out of the kfifo.
*/
mr_uint32_t mr_kfifo_peek(mr_kfifo_t *kfifo, void *buf, mr_uint32_t size);
/**
* @brief This function gets the size of a kfifo.
*
* @param kfifo The kfifo to get the size of.
* @return The size of the kfifo.
*/
MR_INLINE mr_uint32_t mr_kfifo_size(mr_kfifo_t *kfifo) {
return kfifo->size;
}
/**
* @brief This function gets the number of available space bytes in a kfifo.
*
* @param kfifo The kfifo to get the number of available space bytes in.
* @return The number of available space bytes in the kfifo.
*/
MR_INLINE mr_uint32_t mr_kfifo_avail(mr_kfifo_t *kfifo) {
return kfifo->size - (kfifo->in - kfifo->out);
}
/**
* @brief This function gets the number of data bytes in a kfifo.
*
* @param kfifo The kfifo to get the number of data bytes in.
* @return The number of data bytes in the kfifo.
*/
MR_INLINE mr_uint32_t mr_kfifo_len(mr_kfifo_t *kfifo) {
return kfifo->in - kfifo->out;
}
/**
* @brief This function resets a kfifo.
*
* @param kfifo The kfifo to reset.
*/
MR_INLINE void mr_kfifo_reset(mr_kfifo_t *kfifo) {
kfifo->in = kfifo->out = 0;
}
/**
* @brief This function skips data in a kfifo.
*
* @param kfifo The kfifo to skip data in.
* @param size The size of the data to skip.
* @return The number of bytes skipped.
*/
MR_INLINE mr_uint32_t mr_kfifo_skip(mr_kfifo_t *kfifo, mr_uint32_t size) {
kfifo->out += MR_MIN(size, mr_kfifo_len(kfifo));
return size;
}
/**
* @brief This function checks if a kfifo is empty.
*
* @param kfifo The kfifo to check.
* @return MR_TRUE if the kfifo is empty, MR_FALSE otherwise.
*/
MR_INLINE mr_bool_t mr_kfifo_is_empty(mr_kfifo_t *kfifo) {
return (kfifo->in == kfifo->out) ? MR_TRUE : MR_FALSE;
}
/**
* @brief This function checks if a kfifo is full.
*
* @param kfifo The kfifo to check.
* @return MR_TRUE if the kfifo is full, MR_FALSE otherwise.
*/
MR_INLINE mr_bool_t mr_kfifo_is_full(mr_kfifo_t *kfifo) {
return (kfifo->in == (kfifo->out + kfifo->size)) ? MR_TRUE : MR_FALSE;
}
/** @} */
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __MR_KFIFO_H__ */

View File

@@ -29,6 +29,7 @@ typedef struct mr_kworkqueue {
#if defined(MR_USE_KWORKQUEUE_HOOK)
mr_ptr_t suspend;
mr_ptr_t wakeup;
mr_ptr_t args;
#endif /* defined(MR_USE_KWORKQUEUE_HOOK) */
} mr_kworkqueue_t;
@@ -100,8 +101,8 @@ mr_err_t mr_kworkqueue_execute(mr_kworkqueue_t *kqueue);
*/
#define MR_KWORK_INIT(_kwork, _entry, _args) \
{ \
.list = MR_KLIST_INIT(&(_kwork)->list), .tick = 0, .entry = (_entry), \
.args = (_args) \
.list = MR_KLIST_INIT(&(_kwork)->list), .kqueue = MR_NULL, .tick = 0, \
.entry = (_entry), .args = (_args) \
}
/**
@@ -175,8 +176,9 @@ mr_err_t mr_kwork_delayed_schedule(mr_kwork_t *kwork, mr_tick_t tick);
* @param hook The kworkqueue wakeup hook.
* @return 0 on success, a negative error code on failure.
*/
mr_err_t mr_kworkqueue_wakeup_hook_set(mr_kworkqueue_t *kqueue,
void (*hook)(mr_kworkqueue_t *));
mr_err_t mr_kworkqueue_hook_wakeup_set(mr_kworkqueue_t *kqueue,
void (*hook)(mr_kworkqueue_t *,
void *));
/**
* @brief This function set a kworkqueue suspend hook.
@@ -185,8 +187,18 @@ mr_err_t mr_kworkqueue_wakeup_hook_set(mr_kworkqueue_t *kqueue,
* @param hook The kworkqueue suspend hook.
* @return 0 on success, a negative error code on failure.
*/
mr_err_t mr_kworkqueue_suspend_hook_set(mr_kworkqueue_t *kqueue,
void (*hook)(mr_kworkqueue_t *));
mr_err_t mr_kworkqueue_hook_suspend_set(mr_kworkqueue_t *kqueue,
void (*hook)(mr_kworkqueue_t *,
void *));
/**
* @brief This function set a kworkqueue hook args.
*
* @param kqueue The kworkqueue.
* @param args The hook args.
* @return 0 on success, a negative error code on failure.
*/
mr_err_t mr_kworkqueue_hook_args_set(mr_kworkqueue_t *kqueue, void *args);
#endif /* defined(MR_USE_KWORKQUEUE_HOOK) */
/**

View File

@@ -16,7 +16,6 @@
#include <kernel/mr_initcall.h>
#include <kernel/mr_kclock.h>
#include <kernel/mr_kfifo.h>
#include <kernel/mr_khook.h>
#include <kernel/mr_klist.h>
#include <kernel/mr_kobject.h>

View File

@@ -7,14 +7,14 @@
*/
#include <mr_config.h>
#if defined(MR_USE_ENTRY) && !defined(MR_USE_KOS)
#if defined(MR_USE_ENTRY)
#include <kernel/mr_initcall.h>
MR_INLINE void entry(void) {
extern int main(void);
/* Run autoinit(kernel) */
mr_autoinit_kernel();
/* Kernel init */
mr_kernel_init();
/* Run main */
main();
@@ -33,4 +33,4 @@ int $Sub$$main(void) {
return 0;
}
#endif /* defined(__GNUC__) || defined(__clang__) */
#endif /* defined(MR_USE_ENTRY) && !defined(MR_USE_KOS) */
#endif /* defined(MR_USE_ENTRY) */

View File

@@ -9,64 +9,57 @@
#include <kernel/mr_initcall.h>
#include <libc/mr_atomic.h>
/* Init status definition */
#define INIT_STATUS_UNINIT (0)
#define INIT_STATUS_INITED (1)
/* Kernel and user init status */
static mr_atomic_t kstatus = MR_ATOMIC_INIT(INIT_STATUS_UNINIT);
static mr_atomic_t ustatus = MR_ATOMIC_INIT(INIT_STATUS_UNINIT);
static mr_atomic_t kstatus = MR_ATOMIC_INIT(MR_FALSE);
static mr_atomic_t ustatus = MR_ATOMIC_INIT(MR_FALSE);
MR_INLINE void kernel_start(void) {
MR_INLINE void start(void) {
}
MR_INIT_EXPORT(kernel_start, "0.end");
MR_INIT_EXPORT(start, "0.end");
MR_INLINE void kernel_end(void) {
MR_INLINE void end(void) {
}
MR_INIT_EXPORT(kernel_end, "1.end");
MR_INIT_EXPORT(end, "9.end");
MR_INLINE void user_start(void) {
}
MR_INIT_EXPORT(user_start, "1.end");
MR_INLINE void user_end(void) {
}
MR_INIT_EXPORT(user_end, "9.end");
void mr_autoinit_kernel(void) {
const mr_initcall_t *ic;
void mr_kernel_init(void) {
mr_atomic_t inited;
/* Check kernel init status */
mr_atomic_store(&inited, INIT_STATUS_UNINIT);
if (!mr_atomic_compare_exchange(&kstatus, &inited, INIT_STATUS_INITED)) {
mr_atomic_store(&inited, MR_FALSE);
if (!mr_atomic_compare_exchange(&kstatus, &inited, MR_TRUE)) {
/* Kernel already init */
return;
}
/* Run initcalls(kernel) */
for (ic = &__mr_initcall_kernel_start; ic <= &__mr_initcall_kernel_end;
ic++) {
ic->entry();
}
/* Kernel init */
#if defined(MR_USE_KTIMER)
/* Init ktimer */
extern void mr_ktimer_kernel_init(void);
mr_ktimer_kernel_init();
#endif /* defined(MR_USE_KTIMER) */
#if defined(MR_USE_KWORKQUEUE)
/* Init kworkqueue */
extern void mr_kworkqueue_kernel_init(void);
mr_kworkqueue_kernel_init();
#endif /* defined(MR_USE_KWORKQUEUE) */
}
void mr_autoinit(void) {
const mr_initcall_t *ic;
void mr_user_init(void) {
const mr_initcall_t *call;
mr_atomic_t inited;
/* Run autoinit(kernel) */
mr_autoinit_kernel();
/* Kernel */
mr_kernel_init();
/* Check user init status */
mr_atomic_store(&inited, INIT_STATUS_UNINIT);
if (!mr_atomic_compare_exchange(&ustatus, &inited, INIT_STATUS_INITED)) {
mr_atomic_store(&inited, MR_FALSE);
if (!mr_atomic_compare_exchange(&ustatus, &inited, MR_TRUE)) {
/* User already init */
return;
}
/* Run initcalls(user) */
for (ic = &__mr_initcall_user_start; ic <= &__mr_initcall_user_end; ic++) {
ic->entry();
/* Execute initcalls */
for (call = &__mr_initcall_start; call <= &__mr_initcall_end; call++) {
call->entry();
}
}

View File

@@ -1,101 +0,0 @@
/**
* @copyright (c) 2024, MacRsh
*
* @license SPDX-License-Identifier: Apache-2.0
*
* @date 2024-09-06 MacRsh First version
*/
#include <kernel/mr_kfifo.h>
#include <libc/mr_string.h>
MR_INLINE mr_uint32_t kfifo_align_down_pow2(mr_uint32_t val) {
if (val == 0) {
return 0;
}
val |= val >> 1;
val |= val >> 2;
val |= val >> 4;
val |= val >> 8;
val |= val >> 16;
return val - (val >> 1);
}
void mr_kfifo_init(mr_kfifo_t *kfifo, void *buf, mr_uint32_t size) {
/* Init kfifo */
kfifo->buf = buf;
kfifo->in = 0;
kfifo->out = 0;
/* Align 2^n(down) */
kfifo->size = kfifo_align_down_pow2(size);
}
mr_uint32_t mr_kfifo_in(mr_kfifo_t *kfifo, const void *buf, mr_uint32_t size) {
mr_uint32_t s;
/* Get available size */
size = MR_MIN(size, mr_kfifo_avail(kfifo));
if (size == 0) {
return 0;
}
/* In end of kfifo */
s = MR_MIN(size, kfifo->size - (kfifo->in & (kfifo->size - 1)));
mr_memcpy(kfifo->buf + (kfifo->in & (kfifo->size - 1)), buf, s);
/* In start of kfifo */
mr_memcpy(kfifo->buf, (mr_uint8_t *)buf + s, size - s);
/* Update in index */
kfifo->in += size;
return size;
}
mr_uint32_t mr_kfifo_out(mr_kfifo_t *kfifo, void *buf, mr_uint32_t size) {
/* Peek kfifo */
size = mr_kfifo_peek(kfifo, buf, size);
/* Update out index */
kfifo->out += size;
return size;
}
mr_uint32_t mr_kfifo_in_overwrite(mr_kfifo_t *kfifo, const void *buf,
mr_uint32_t size) {
mr_uint32_t s;
/* Skip the useless data */
s = MR_MIN(size, mr_kfifo_size(kfifo));
if (s > size) {
buf = (mr_uint8_t *)buf + (size - s);
size = s;
}
/* Give space to kfifo */
s = mr_kfifo_avail(kfifo);
if (s < size) {
mr_kfifo_skip(kfifo, size - s);
}
/* In kfifo */
return mr_kfifo_in(kfifo, buf, size);
}
mr_uint32_t mr_kfifo_peek(mr_kfifo_t *kfifo, void *buf, mr_uint32_t size) {
mr_uint32_t s;
/* Get available size */
size = MR_MIN(size, mr_kfifo_len(kfifo));
if (size == 0) {
return 0;
}
/* Out end of kfifo */
s = MR_MIN(size, kfifo->size - (kfifo->out & (kfifo->size - 1)));
mr_memcpy(buf, kfifo->buf + (kfifo->out & (kfifo->size - 1)), s);
/* Out start of kfifo */
mr_memcpy((mr_uint8_t *)buf + s, kfifo->buf, size - s);
return size;
}

View File

@@ -372,7 +372,7 @@ MR_INLINE mr_ssize_t ktimer_attr_store(struct mr_kobject *kobj,
}
#endif /* defined(MR_USE_KTIMER_ATTR) */
MR_INLINE void mr_ktimer_kset_init(void) {
void mr_ktimer_kernel_init(void) {
#if defined(MR_USE_KTIMER_ATTR)
static mr_kattr_t attr[]
= {MR_KATTR_INIT("tick", ktimer_attr_show, ktimer_attr_store),
@@ -398,5 +398,4 @@ MR_INLINE void mr_ktimer_kset_init(void) {
/* Add kclock entry */
mr_kclock_hook_add(ktimer_timeout_check);
}
MR_INIT_EXPORT(mr_ktimer_kset_init, MR_INIT_LEVEL_KERNEL);
#endif /* defined(MR_USE_KTIMER) */

View File

@@ -82,7 +82,8 @@ MR_INLINE void kqueue_timer_entry(mr_ktimer_t *ktimer, void *args) {
mr_kworkqueue_get(kqueue);
#if defined(MR_USE_KWORKQUEUE_HOOK)
if (kqueue->wakeup) {
((void (*)(mr_kworkqueue_t *))kqueue->wakeup)(kqueue);
((void (*)(mr_kworkqueue_t *, void *))kqueue->wakeup)(kqueue,
kqueue->args);
}
#endif /* defined(MR_USE_KWORKQUEUE_HOOK) */
}
@@ -255,7 +256,8 @@ MR_INLINE void kqueue_execute(mr_kworkqueue_t *kqueue) {
mr_kworkqueue_put(kqueue);
#if defined(MR_USE_KWORKQUEUE_HOOK)
if (kqueue->suspend) {
((void (*)(mr_kworkqueue_t *))kqueue->suspend)(kqueue);
((void (*)(mr_kworkqueue_t *, void *))kqueue->suspend)(kqueue,
kqueue->args);
}
#endif /* defined(MR_USE_KWORKQUEUE_HOOK) */
}
@@ -281,6 +283,7 @@ mr_err_t mr_kwork_init(mr_kwork_t *kwork,
/* Init kwork */
mr_klist_init(&kwork->list);
kwork->kqueue = MR_NULL;
kwork->entry = entry;
kwork->args = args;
return 0;
@@ -299,6 +302,7 @@ MR_INLINE void kwork_del(mr_kwork_t *kwork) {
}
/* Mark kwork as deleted */
kwork->kqueue = MR_NULL;
kwork->entry = MR_NULL;
/* Unlock */
@@ -346,6 +350,7 @@ MR_INLINE mr_err_t kqueue_work(mr_kworkqueue_t *kqueue, mr_kwork_t *kwork) {
/* Add kwork to kworkqueue list */
mr_klist_add(&kqueue->list, &kwork->list);
kwork->kqueue = kqueue;
/* Unlock */
mr_kspinlock_unlock_irqrestore(&klock, mask);
@@ -357,7 +362,8 @@ MR_INLINE mr_err_t kqueue_work(mr_kworkqueue_t *kqueue, mr_kwork_t *kwork) {
#if defined(MR_USE_KWORKQUEUE_HOOK)
if (kqueue->wakeup) {
/* Call kworkqueue wakeup hook */
((void (*)(mr_kworkqueue_t *))kqueue->wakeup)(kqueue);
((void (*)(mr_kworkqueue_t *, void *))kqueue->wakeup)(kqueue,
kqueue->args);
}
#endif /* defined(MR_USE_KWORKQUEUE_HOOK) */
}
@@ -433,6 +439,9 @@ _set:
mr_ktimer_tick_set(&kqueue->timer, tick | MR_KTIMER_PERIODIC);
mr_ktimer_start(&kqueue->timer);
}
/* Mark kwork as in kworkqueue */
kwork->kqueue = kqueue;
ret = 0;
_exit:
/* Unlock */
@@ -452,9 +461,57 @@ mr_err_t mr_kworkqueue_delayed_work(mr_kworkqueue_t *kqueue, mr_kwork_t *kwork,
return kqueue_delayed_work(kqueue, kwork, tick);
}
mr_err_t mr_kwork_schedule(mr_kwork_t *kwork) {
mr_kworkqueue_t *kqueue;
mr_err_t ret;
/* Check arguments */
if ((!kwork) || (!MR_KWORK_IS_INITED(kwork)) || (!kwork->kqueue)) {
return -MR_EINVAL;
}
/* Get kworkqueue */
kqueue = mr_kworkqueue_get(kwork->kqueue);
if (!kqueue) {
return -MR_EINVAL;
}
/* Schedule kwork to the used kworkqueue */
ret = mr_kworkqueue_work(kwork->kqueue, kwork);
/* Put kworkqueue */
mr_kworkqueue_put(kqueue);
return ret;
}
mr_err_t mr_kwork_delayed_schedule(mr_kwork_t *kwork, mr_tick_t tick) {
mr_kworkqueue_t *kqueue;
mr_err_t ret;
/* Check arguments */
if ((!kwork) || (!MR_KWORK_IS_INITED(kwork)) || (!kwork->kqueue)
|| (tick == 0)) {
return -MR_EINVAL;
}
/* Get kworkqueue */
kqueue = mr_kworkqueue_get(kwork->kqueue);
if (!kqueue) {
return -MR_EINVAL;
}
/* Schedule kwork to the used kworkqueue */
ret = mr_kworkqueue_delayed_work(kwork->kqueue, kwork, tick);
/* Put kworkqueue */
mr_kworkqueue_put(kqueue);
return ret;
}
#if defined(MR_USE_KWORKQUEUE_HOOK)
mr_err_t mr_kworkqueue_wakeup_hook_set(mr_kworkqueue_t *kqueue,
void (*hook)(mr_kworkqueue_t *)) {
mr_err_t mr_kworkqueue_hook_wakeup_set(mr_kworkqueue_t *kqueue,
void (*hook)(mr_kworkqueue_t *,
void *)) {
/* Check arguments */
if ((!kqueue) || (!MR_KWORKQUEUE_IS_INITED(kqueue))) {
return -MR_EINVAL;
@@ -465,8 +522,9 @@ mr_err_t mr_kworkqueue_wakeup_hook_set(mr_kworkqueue_t *kqueue,
return 0;
}
mr_err_t mr_kworkqueue_suspend_hook_set(mr_kworkqueue_t *kqueue,
void (*hook)(mr_kworkqueue_t *)) {
mr_err_t mr_kworkqueue_hook_suspend_set(mr_kworkqueue_t *kqueue,
void (*hook)(mr_kworkqueue_t *,
void *)) {
/* Check arguments */
if ((!kqueue) || (!MR_KWORKQUEUE_IS_INITED(kqueue))) {
return -MR_EINVAL;
@@ -476,6 +534,18 @@ mr_err_t mr_kworkqueue_suspend_hook_set(mr_kworkqueue_t *kqueue,
kqueue->suspend = hook;
return 0;
}
mr_err_t mr_kworkqueue_hook_args_set(mr_kworkqueue_t *kqueue, void *args)
{
/* Check arguments */
if ((!kqueue) || (!MR_KWORKQUEUE_IS_INITED(kqueue))) {
return -MR_EINVAL;
}
/* Set kworkqueue hook args */
kqueue->args = args;
return 0;
}
#endif /* defined(MR_USE_KWORKQUEUE_HOOK) */
mr_kworkqueue_t *mr_kworkqueue_find(const char *name) {
@@ -509,7 +579,7 @@ MR_INLINE void kqueue_release2_kobj(mr_kobject_t *kobj) {
mr_free(kqueue);
}
MR_INLINE void mr_kworkqueue_kset_init(void) {
void mr_kworkqueue_kernel_init(void) {
static mr_kattr_t *attrs[] = {MR_NULL};
mr_err_t ret;
@@ -523,5 +593,4 @@ MR_INLINE void mr_kworkqueue_kset_init(void) {
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) */