feat(kobject): Optimize kobject lookup memory.
1.Optimize the short path registration and lookup process for kernel objects using stack memory.
This commit is contained in:
@@ -22,7 +22,6 @@ extern "C" {
|
||||
|
||||
/* Initcall level definition */
|
||||
#define MR_INIT_LEVEL_KERNEL "1"
|
||||
#define MR_INIT_LEVEL_KOS "1.1"
|
||||
#define MR_INIT_LEVEL_USER(_level) "2."_level
|
||||
|
||||
/* Initcall type */
|
||||
|
||||
160
include/kernel/mr_kfifo.h
Normal file
160
include/kernel/mr_kfifo.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* @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__ */
|
||||
52
include/kernel/mr_kversion.h
Normal file
52
include/kernel/mr_kversion.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* @copyright (c) 2024, MacRsh
|
||||
*
|
||||
* @license SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* @date 2024-09-06 MacRsh First version
|
||||
*/
|
||||
|
||||
#ifndef __MR_KVERSION_H__
|
||||
#define __MR_KVERSION_H__
|
||||
|
||||
/**
|
||||
* @addtogroup Kversion
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* MR-X kernel version definition */
|
||||
#define MR_KVERSION_MAJOR 1
|
||||
#define MR_KVERSION_MINOR 0
|
||||
#define MR_KVERSION_PATCH 1
|
||||
|
||||
/**
|
||||
* @brief This macro function constructs a kernel version code.
|
||||
*
|
||||
* @param _major The major version.
|
||||
* @param _minor The minor version.
|
||||
* @param _patch The patch version.
|
||||
* @return The kernel version code.
|
||||
*/
|
||||
#define MR_KVERSION(_major, _minor, _patch) \
|
||||
((_major) << 16 | (_minor) << 8 | (_patch))
|
||||
|
||||
/* Kversion code */
|
||||
#define MR_KVERSION_CODE \
|
||||
MR_KVERSION(MR_KVERSION_MAJOR, MR_KVERSION_MINOR, MR_KVERSION_PATCH)
|
||||
|
||||
/**
|
||||
* @brief This macro function checks if the kernel version is the same as the
|
||||
* given version.
|
||||
*
|
||||
* @param _major The major version.
|
||||
* @param _minor The minor version.
|
||||
* @param _patch The patch version.
|
||||
* @return MR_TRUE if the kernel version is the same as the given version,
|
||||
* MR_FALSE otherwise.
|
||||
*/
|
||||
#define MR_KVERSION_IS(_major, _minor, _patch) \
|
||||
(MR_KVERSION_CODE == MR_KVERSION(_major, _minor, _patch))
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif /* __MR_KVERSION_H__ */
|
||||
@@ -27,7 +27,7 @@ extern "C" {
|
||||
#if defined(MR_USE_KOS) && defined(MR_USE_KMUTEX)
|
||||
/* Kmutex type */
|
||||
typedef struct mr_kmutex {
|
||||
struct mr_kobject parent;
|
||||
mr_kobject_t parent;
|
||||
mr_ptr_t mutex;
|
||||
#if !defined(MR_CFG_KMUTEX_IMUTEX_SIZE)
|
||||
#define MR_CFG_KMUTEX_IMUTEX_SIZE (64)
|
||||
|
||||
@@ -45,6 +45,14 @@ extern "C" {
|
||||
#define MR_ASSERT(_expr) ((void)0)
|
||||
#endif /* defined(MR_USE_LIBC_ASSERT) */
|
||||
|
||||
/**
|
||||
* @brief This macro function defines a static assert.
|
||||
*
|
||||
* @param _expr The expression.
|
||||
*/
|
||||
#define MR_STATIC_ASSERT(_expr) \
|
||||
typedef char __mr_static_assert[(_expr) ? 1 : -1];
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -41,6 +41,7 @@ typedef struct mr_memblk {
|
||||
mr_size_t size;
|
||||
} mr_memblk_t;
|
||||
#endif /* !defined(MR_USE_3PARTY_MALLOC) */
|
||||
|
||||
/**
|
||||
* @brief This function allocates memory.
|
||||
*
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <kernel/mr_kservice.h>
|
||||
#include <kernel/mr_kspinlock.h>
|
||||
#include <kernel/mr_ktimer.h>
|
||||
#include <kernel/mr_kversion.h>
|
||||
#include <kernel/os/mr_kmutex.h>
|
||||
#include <kernel/os/mr_kthread.h>
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ extern "C" {
|
||||
* @brief This function initializes a thread.
|
||||
*
|
||||
* @param thread The thread to be initialized.
|
||||
* @param name The thread name.
|
||||
* @param kthread The kthread to thread arguments.
|
||||
* @param stack The thread stack.
|
||||
* @param stack_size The thread stack size.
|
||||
|
||||
101
kernel/kfifo.c
Normal file
101
kernel/kfifo.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
@@ -57,7 +57,8 @@ void mr_kobject_init(mr_kobject_t *kobj, mr_ktype_t *type) {
|
||||
}
|
||||
|
||||
MR_INLINE mr_err_t path_alloc_varg(const char *fmt, mr_va_list args,
|
||||
const char **path) {
|
||||
const char **path, char *ipath,
|
||||
mr_size_t ipath_len) {
|
||||
int path_len;
|
||||
|
||||
/* Get path length */
|
||||
@@ -67,9 +68,14 @@ MR_INLINE mr_err_t path_alloc_varg(const char *fmt, mr_va_list args,
|
||||
}
|
||||
|
||||
/* Allocate path */
|
||||
*path = mr_malloc(path_len + 1);
|
||||
if (!(*path)) {
|
||||
return -MR_ENOMEM;
|
||||
if (path_len < ipath_len) {
|
||||
/* Use inline path if possible */
|
||||
*path = ipath;
|
||||
} else {
|
||||
*path = mr_malloc(path_len + 1);
|
||||
if (!(*path)) {
|
||||
return -MR_ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
/* Format path */
|
||||
@@ -212,7 +218,11 @@ MR_INLINE mr_err_t kobject_name_alloc(mr_kobject_t *kobj, const char *name) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
MR_INLINE void path_free(const char *path) {
|
||||
MR_INLINE void path_free(const char *path, char *ipath) {
|
||||
if (path == ipath) {
|
||||
/* Inline path not need to free */
|
||||
return;
|
||||
}
|
||||
mr_free((char *)path);
|
||||
}
|
||||
|
||||
@@ -246,6 +256,7 @@ MR_INLINE mr_err_t kobject_add(mr_kobject_t *kobj, mr_kobject_t *parent,
|
||||
|
||||
MR_INLINE mr_err_t kobject_add_varg(mr_kobject_t *kobj, mr_kobject_t *parent,
|
||||
const char *fmt, mr_va_list args) {
|
||||
char ipath[MR_CFG_KOBJECT_INAME_SIZE * 2];
|
||||
const char *path;
|
||||
mr_err_t ret;
|
||||
int mask;
|
||||
@@ -260,7 +271,7 @@ MR_INLINE mr_err_t kobject_add_varg(mr_kobject_t *kobj, mr_kobject_t *parent,
|
||||
}
|
||||
|
||||
/* Allocate path */
|
||||
ret = path_alloc_varg(fmt, args, &path);
|
||||
ret = path_alloc_varg(fmt, args, &path, ipath, sizeof(ipath));
|
||||
if (ret != 0) {
|
||||
goto _exit;
|
||||
}
|
||||
@@ -269,7 +280,7 @@ MR_INLINE mr_err_t kobject_add_varg(mr_kobject_t *kobj, mr_kobject_t *parent,
|
||||
ret = kobject_add(kobj, parent, path);
|
||||
|
||||
/* Free path */
|
||||
path_free(path);
|
||||
path_free(path, ipath);
|
||||
_exit:
|
||||
/* Unlock */
|
||||
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
||||
@@ -327,6 +338,7 @@ 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];
|
||||
const char *path, *path2;
|
||||
mr_kobject_t *kobj;
|
||||
mr_err_t ret;
|
||||
@@ -336,7 +348,7 @@ MR_INLINE mr_kobject_t *kobject_lookup_varg(mr_kobject_t *parent,
|
||||
mask = mr_kspinlock_lock_irqsave(&klock);
|
||||
|
||||
/* Allocate path */
|
||||
ret = path_alloc_varg(fmt, args, &path);
|
||||
ret = path_alloc_varg(fmt, args, &path, ipath, sizeof(ipath));
|
||||
if (ret != 0) {
|
||||
kobj = MR_NULL;
|
||||
goto _exit;
|
||||
@@ -349,7 +361,7 @@ MR_INLINE mr_kobject_t *kobject_lookup_varg(mr_kobject_t *parent,
|
||||
kobj = kobject_lookup_path(&parent, &path, parent);
|
||||
|
||||
/* Free path */
|
||||
path_free(path2);
|
||||
path_free(path2, ipath);
|
||||
_exit:
|
||||
/* Unlock */
|
||||
mr_kspinlock_unlock_irqrestore(&klock, mask);
|
||||
@@ -431,7 +443,8 @@ void mr_kobject_put(mr_kobject_t *kobj) {
|
||||
mr_kref_put(&kobj->ref, kobject_release_ref);
|
||||
}
|
||||
|
||||
MR_INLINE mr_kattr_t *kobject_attr_lookup(mr_kobject_t *kobj, const char *name) {
|
||||
MR_INLINE mr_kattr_t *kobject_attr_lookup(mr_kobject_t *kobj,
|
||||
const char *name) {
|
||||
mr_kattr_t **attrs;
|
||||
mr_size_t i;
|
||||
|
||||
@@ -484,7 +497,7 @@ mr_ssize_t mr_kobject_attr_store(mr_kobject_t *kobj, const char *name,
|
||||
if ((!kobj) || (!MR_KOBJECT_IS_INITED(kobj)) || (!name)
|
||||
|| ((!buf) && (size > 0))) {
|
||||
return -MR_EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Lookup attribute */
|
||||
attr = kobject_attr_lookup(kobj, name);
|
||||
@@ -506,7 +519,6 @@ mr_err_t mr_kset_register(mr_kset_t *kset, const char *name) {
|
||||
return mr_kobject_add((mr_kobject_t *)kset, (mr_kobject_t *)&kroot, name);
|
||||
}
|
||||
|
||||
|
||||
mr_kset_t *mr_kset_find(const char *name) {
|
||||
/* Lookup kset in kroot */
|
||||
return (mr_kset_t *)mr_kobject_lookup((mr_kobject_t *)&kroot, name);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#if defined(MR_USE_KOS)
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include <libc/mr_assert.h>
|
||||
#include <port/mr_port_thread.h>
|
||||
|
||||
static mr_err_t err_shift(BaseType_t ret) {
|
||||
@@ -54,10 +55,7 @@ mr_err_t mr_port_thread_init(void *thread, const char *name, void *kthread,
|
||||
BaseType_t ret;
|
||||
|
||||
/* Check inline thread size */
|
||||
if (MR_CFG_KTHREAD_ITHREAD_SIZE < sizeof(StaticTask_t)) {
|
||||
ret = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
|
||||
return err_shift(ret);
|
||||
}
|
||||
MR_STATIC_ASSERT(MR_CFG_KTHREAD_ITHREAD_SIZE >= sizeof(StaticTask_t));
|
||||
|
||||
/* Shift arguments */
|
||||
task_arguments_shift(&stack_size, &priority);
|
||||
|
||||
@@ -73,10 +73,7 @@ mr_err_t mr_port_thread_init(void *thread, const char *name, void *kthread,
|
||||
rt_err_t ret;
|
||||
|
||||
/* Check inline thread size */
|
||||
if (MR_CFG_KTHREAD_ITHREAD_SIZE < sizeof(struct rt_thread)) {
|
||||
ret = -RT_ENOMEM;
|
||||
return err_shift(ret);
|
||||
}
|
||||
MR_STATIC_ASSERT(MR_CFG_KTHREAD_ITHREAD_SIZE >= sizeof(struct rt_thread));
|
||||
|
||||
/* Init thread */
|
||||
ret = rt_thread_init(thread, name, thread_entry, kthread, stack, stack_size,
|
||||
@@ -185,5 +182,5 @@ static void mr_main_thread_takeover(void) {
|
||||
main->parameter = &kth;
|
||||
mr_kthread_takeover(&kth, "main", main);
|
||||
}
|
||||
MR_INIT_EXPORT(mr_main_thread_takeover, MR_INIT_LEVEL_KOS);
|
||||
MR_INIT_EXPORT(mr_main_thread_takeover, MR_INIT_LEVEL_USER("0"));
|
||||
#endif /* defined(MR_USE_KOS) */
|
||||
|
||||
Reference in New Issue
Block a user