创建,添加sdram

This commit is contained in:
sc committed 2025-11-16 15:44:34 +08:00
commit 90c74bc31a
2944 files changed
+1430509

No files matched your search

+462
View File
@@ -0,0 +1,462 @@
#include "rtconfig.h"
menu "RT-Thread Kernel"
config RT_NAME_MAX
int "The maximal size of kernel object name"
range 1 64
default 8
help
Each kernel object, such as thread, timer, semaphore etc, has a name,
the RT_NAME_MAX is the maximal size of this object name.
config RT_USING_ARCH_DATA_TYPE
bool "Use the data types defined in ARCH_CPU"
default n
help
For the data type like, `rt_uint8/int8_t, rt_uint16/int16_t, rt_uint32/int32_t`,
BSP can define these basic data types in ARCH_CPU level.
Please re-define these data types in rtconfig_project.h file.
config RT_USING_SMART
bool "Enable RT-Thread Smart (microkernel on kernel/userland)"
default n
select RT_USING_LWP
select RT_USING_DFS
select RT_USING_POSIX_CLOCKTIME
select RT_USING_DEVICE
select RT_USING_NULL
select RT_USING_ZERO
select RT_USING_RANDOM
select RT_USING_RTC
select RT_USING_POSIX_TIMER
select RT_USING_POSIX_CLOCK
select RT_USING_POSIX_FS
select RT_USING_POSIX_TERMIOS
select RT_USING_KTIME
select RT_USING_STDC_ATOMIC
depends on ARCH_ARM_CORTEX_M || ARCH_ARM_ARM9 || ARCH_ARM_CORTEX_A || ARCH_ARMV8 || ARCH_RISCV64
depends on !RT_USING_NANO
help
RT-Thread Smart is a microkernel based operating system on RT-Thread.
config RT_USING_NANO
bool "Enable RT-Thread Nano"
default n
help
RT-Thread Nano with a very small size and refined hard real-time kernel,
which is very suited for the resource-constrained MCU system.
config RT_USING_AMP
bool "Enable AMP (Asymmetric Multi-Processing)"
default n
if RT_USING_AMP
choice
prompt "Select the AMP role"
default RT_AMP_SLAVE
config RT_AMP_MASTER
bool "amp role MASTER"
config RT_AMP_SLAVE
bool "amp role SLAVE"
endchoice
endif
config RT_USING_SMP
bool "Enable SMP (Symmetric multiprocessing)"
default n
select RT_USING_SCHED_THREAD_CTX
help
This option should be selected by machines which have an SMP-
capable CPU.
The only effect of this option is to make the SMP-related
options available to the user for configuration.
config RT_CPUS_NR
int "Number of CPUs"
default 1
help
Number of CPUs in the system
config RT_ALIGN_SIZE
int "Alignment size for CPU architecture data access"
default 8
help
Alignment size for CPU architecture data access
choice
prompt "The maximal level value of priority of thread"
default RT_THREAD_PRIORITY_32
config RT_THREAD_PRIORITY_8
bool "8"
config RT_THREAD_PRIORITY_32
bool "32"
config RT_THREAD_PRIORITY_256
bool "256"
endchoice
config RT_THREAD_PRIORITY_MAX
int
default 8 if RT_THREAD_PRIORITY_8
default 32 if RT_THREAD_PRIORITY_32
default 256 if RT_THREAD_PRIORITY_256
config RT_TICK_PER_SECOND
int "Tick frequency, Hz"
range 10 1000
default 1000
help
System's tick frequency, Hz.
config RT_USING_OVERFLOW_CHECK
bool "Using stack overflow checking"
default y
help
Enable thread stack overflow checking. The stack overflow is checking when
each thread switch.
config RT_USING_HOOK
bool "Enable system hook"
default y
select RT_USING_IDLE_HOOK
help
Enable the hook function when system running, such as idle thread hook,
thread context switch etc.
if RT_USING_HOOK
config RT_HOOK_USING_FUNC_PTR
bool "Using function pointers as system hook"
default y
endif
config RT_USING_HOOKLIST
bool "Enable hook list"
default n
help
Enable the hook list feature for rt-thread packages. With this, they can
plug in to the system on run-time.
config RT_USING_IDLE_HOOK
bool "Enable IDLE Task hook"
default y if RT_USING_HOOK
if RT_USING_IDLE_HOOK
config RT_IDLE_HOOK_LIST_SIZE
int "The max size of idle hook list"
default 4
range 1 16
help
The system has a hook list. This is the hook list size.
endif
config IDLE_THREAD_STACK_SIZE
int "The stack size of idle thread"
default 1024 if ARCH_CPU_64BIT
default 256
config SYSTEM_THREAD_STACK_SIZE
int "The stack size of system thread (for defunct etc.)"
depends on RT_USING_SMP
default IDLE_THREAD_STACK_SIZE
config RT_USING_TIMER_SOFT
bool "Enable software timer with a timer thread"
default y
help
the timeout function context of soft-timer is under a high priority timer
thread.
if RT_USING_TIMER_SOFT
config RT_TIMER_THREAD_PRIO
int "The priority level value of timer thread"
default 4
config RT_TIMER_THREAD_STACK_SIZE
int "The stack size of timer thread"
default 2048 if ARCH_CPU_64BIT
default 512
endif
menu "kservice optimization"
config RT_KSERVICE_USING_STDLIB
bool "Enable kservice to use standard C library"
default y
if RT_KSERVICE_USING_STDLIB
config RT_KSERVICE_USING_STDLIB_MEMORY
bool "Use stdlib memory functions to replace (faster, but not safe)"
default n
help
e.g. use memcpy to replace rt_memcpy
endif
config RT_KSERVICE_USING_TINY_SIZE
bool "Enable kservice to use tiny size"
default n
config RT_USING_TINY_FFS
bool "Enable kservice to use tiny finding first bit set method"
default n
config RT_KPRINTF_USING_LONGLONG
bool "Enable rt_printf-family functions to support long-long format"
default y if ARCH_CPU_64BIT
default n
help
Enable rt_printf()/rt_snprintf()/rt_sprintf()/rt_vsnprintf()/rt_vsprintf()
functions to support long-long format
endmenu
menuconfig RT_USING_DEBUG
bool "Enable debugging features"
default y
if RT_USING_DEBUG
config RT_DEBUGING_COLOR
bool "Enable color debugging log"
default y
config RT_DEBUGING_CONTEXT
bool "Enable debugging of environment and context check"
default y
config RT_DEBUGING_AUTO_INIT
bool "Enable debugging of components automatic initialization"
default n
config RT_DEBUGING_PAGE_LEAK
bool "Enable page leaking tracer"
depends on ARCH_MM_MMU
default n
config RT_DEBUGING_SPINLOCK
bool "Enable spinlock debugging"
depends on RT_USING_SMP
default n
endif
menu "Inter-Thread communication"
config RT_USING_SEMAPHORE
bool "Enable semaphore"
default y
config RT_USING_MUTEX
bool "Enable mutex"
default y
config RT_USING_EVENT
bool "Enable event flag"
default y
config RT_USING_MAILBOX
bool "Enable mailbox"
default y
config RT_USING_MESSAGEQUEUE
bool "Enable message queue"
default y
config RT_USING_MESSAGEQUEUE_PRIORITY
bool "Enable message queue priority"
depends on RT_USING_MESSAGEQUEUE
default n
config RT_USING_SIGNALS
bool "Enable signals"
select RT_USING_MEMPOOL
default n
help
A signal is an asynchronous notification sent to a specific thread
in order to notify it of an event that occurred.
endmenu
menu "Memory Management"
config RT_PAGE_MAX_ORDER
int "Max order of pages allocatable by page allocator"
default 11
depends on ARCH_MM_MMU
help
For example, A value of 11 means the maximum chunk of contiguous memory
allocatable by page system is 2^(11 + ARCH_PAGE_BITS - 1) Bytes.
Large memory requirement can consume all system resource, and should
consider reserved memory instead to enhance system endurance.
Max order should at least satisfied usage by huge page.
config RT_USING_MEMPOOL
bool "Using memory pool"
default y
help
Using static memory fixed partition
config RT_USING_SMALL_MEM
bool "Using Small Memory Algorithm"
default n
help
Using Small Memory Algorithm
config RT_USING_SLAB
bool "Using SLAB Memory Algorithm"
default n
help
The slab allocator of RT-Thread is a memory allocation algorithm
optimizedfor embedded systems based on the slab allocator
implemented by Matthew Dillon, founder of dragonfly BSD.
The original slab algorithm is an efficient kernel memory
allocation algorithm introduced by Jeff bonwick for
Solaris Operating System.
menuconfig RT_USING_MEMHEAP
bool "Using memheap Memory Algorithm"
default n
if RT_USING_MEMHEAP
choice
prompt "Memheap memory allocation mode"
default RT_MEMHEAP_FAST_MODE
config RT_MEMHEAP_FAST_MODE
bool "fast mode"
help
Speed priority mode.
As long as the memory block size meets the requirements, the search ends immediately.
config RT_MEMHEAP_BEST_MODE
bool "best mode"
help
Best size first.
The search does not end until the memory block of the most appropriate size is found
endchoice
endif
choice
prompt "System Heap Memory Management"
default RT_USING_SMALL_MEM_AS_HEAP
config RT_USING_SMALL_MEM_AS_HEAP
bool "Small Memory Algorithm"
select RT_USING_SMALL_MEM
config RT_USING_MEMHEAP_AS_HEAP
bool "Use memheap objects as heap"
select RT_USING_MEMHEAP
if RT_USING_MEMHEAP_AS_HEAP
config RT_USING_MEMHEAP_AUTO_BINDING
bool "Use all of memheap objects as heap"
default y
endif
config RT_USING_SLAB_AS_HEAP
bool "SLAB Algorithm for large memory"
select RT_USING_SLAB
config RT_USING_USERHEAP
bool "Use user heap"
help
If this option is selected, please implement these functions:
rt_malloc(), rt_malloc_sethook()
rt_free(), rt_free_sethook()
rt_calloc(), rt_realloc()
rt_memory_info()
rt_system_heap_init()
config RT_USING_NOHEAP
bool "Disable Heap"
endchoice
config RT_USING_MEMTRACE
bool "Enable memory trace"
default n
help
When enable RT_USING_MEMTRACE with shell, developer can call cmd:
1. memtrace
to dump memory block information.
2. memcheck
to check memory block to avoid memory overwritten.
And developer also can call memcheck() in each of scheduling
to check memory block to find which thread has wrongly modified
memory.
config RT_USING_HEAP_ISR
bool "Using heap in ISR"
default n
help
When this option is enabled, the critical zone will be protected with disable interrupt.
config RT_USING_HEAP
bool
default n if RT_USING_NOHEAP
default y if RT_USING_SMALL_MEM
default y if RT_USING_SLAB
default y if RT_USING_MEMHEAP_AS_HEAP
default y if RT_USING_USERHEAP
endmenu
config RT_USING_DEVICE
bool "Using device object"
depends on !RT_USING_NANO
default y
config RT_USING_DEVICE_OPS
bool "Using ops for each device object"
depends on RT_USING_DEVICE
default n
config RT_USING_INTERRUPT_INFO
bool "Enable additional interrupt trace information"
default n
help
Add name and counter information for interrupt trace.
config RT_USING_THREADSAFE_PRINTF
bool "Enable thread safe kernel print service"
default y if RT_USING_SMP && RT_USING_SMART
config RT_USING_SCHED_THREAD_CTX
bool "Using the scheduler thread context"
help
Using the scheduler thread context embedded in the thread object.
This options is only for backward compatible codes. Maybe use as a
mandatory option in the future.
default y if RT_USING_SMP
config RT_USING_CONSOLE
bool "Using console for rt_kprintf"
default y
if RT_USING_CONSOLE
config RT_CONSOLEBUF_SIZE
int "the buffer size for console log printf"
default 128
config RT_CONSOLE_DEVICE_NAME
string "the device name for console"
default "uart1"
endif
config RT_VER_NUM
hex
default 0x50100
help
RT-Thread version number
config RT_USING_STDC_ATOMIC
bool "Use atomic implemented in stdatomic.h"
default n
config RT_BACKTRACE_LEVEL_MAX_NR
int "Max number of backtrace level"
default 32
endmenu
+52
View File
@@ -0,0 +1,52 @@
from building import *
import os
src = Glob('*.c')
src += Glob('klibc/*.c')
cwd = GetCurrentDir()
inc = [os.path.join(cwd, '..', 'include')]
if GetDepend('RT_USING_SMALL_MEM') == False:
SrcRemove(src, ['mem.c'])
if GetDepend('RT_USING_SLAB') == False:
SrcRemove(src, ['slab.c'])
if GetDepend('RT_USING_MEMPOOL') == False:
SrcRemove(src, ['mempool.c'])
if GetDepend('RT_USING_MEMHEAP') == False:
SrcRemove(src, ['memheap.c'])
if GetDepend('RT_USING_SIGNALS') == False:
SrcRemove(src, ['signal.c'])
if GetDepend('RT_USING_DEVICE') == False:
SrcRemove(src, ['device.c'])
if GetDepend('RT_USING_SMP') == False:
SrcRemove(src, ['cpu.c', 'scheduler_mp.c'])
else:
SrcRemove(src, ['scheduler_up.c'])
LOCAL_CFLAGS = ''
LINKFLAGS = ''
if rtconfig.PLATFORM in ['gcc']: # only for GCC
LOCAL_CFLAGS += ' -Wunused' # unused warning
LOCAL_CFLAGS += ' -Wformat -Wformat-security' # printf/scanf format warning
LOCAL_CFLAGS += ' -Warray-bounds -Wuninitialized' # memory access warning
LOCAL_CFLAGS += ' -Wreturn-type -Wcomment -Wswitch' # code style warning
LOCAL_CFLAGS += ' -Wparentheses -Wlogical-op ' # operation warning
LOCAL_CFLAGS += ' -Wmissing-declarations -Wmissing-prototypes -Wstrict-prototypes' # function declaration warning
if 'mips' not in rtconfig.PREFIX: # mips toolchain does not support
LOCAL_CFLAGS += ' -Wimplicit-fallthrough' # implicit fallthrough warning
LOCAL_CFLAGS += ' -Wduplicated-cond -Wduplicated-branches' # duplicated condition warning
if rtconfig.ARCH not in ['sim']:
LINKFLAGS += ' -Wl,--gc-sections,--print-memory-usage' # remove unused sections and print memory usage
group = DefineGroup('Kernel', src, depend=[''], CPPPATH=inc,
LINKFLAGS=LINKFLAGS, LOCAL_CFLAGS=LOCAL_CFLAGS,
CPPDEFINES=['__RTTHREAD__'], LOCAL_CPPDEFINES=['__RT_KERNEL_SOURCE__'])
Return('group')
+165
View File
@@ -0,0 +1,165 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2006-03-12 Bernard first version
* 2006-05-27 Bernard add support for same priority thread schedule
* 2006-08-10 Bernard remove the last rt_schedule in rt_tick_increase
* 2010-03-08 Bernard remove rt_passed_second
* 2010-05-20 Bernard fix the tick exceeds the maximum limits
* 2010-07-13 Bernard fix rt_tick_from_millisecond issue found by kuronca
* 2011-06-26 Bernard add rt_tick_set function.
* 2018-11-22 Jesven add per cpu tick
* 2020-12-29 Meco Man implement rt_tick_get_millisecond()
* 2021-06-01 Meco Man add critical section projection for rt_tick_increase()
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2023-10-16 RiceChen fix: only the main core detection rt_timer_check(), in SMP mode
*/
#include <rthw.h>
#include <rtthread.h>
#include <rtatomic.h>
#ifdef RT_USING_SMP
#define rt_tick rt_cpu_index(0)->tick
#else
static volatile rt_atomic_t rt_tick = 0;
#endif /* RT_USING_SMP */
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
static void (*rt_tick_hook)(void);
/**
* @addtogroup Hook
*/
/**@{*/
/**
* @brief This function will set a hook function, which will be invoked when tick increase
*
*
* @param hook the hook function
*/
void rt_tick_sethook(void (*hook)(void))
{
rt_tick_hook = hook;
}
/**@}*/
#endif /* RT_USING_HOOK */
/**
* @addtogroup Clock
*/
/**@{*/
/**
* @brief This function will return current tick from operating system startup.
*
* @return Return current tick.
*/
rt_tick_t rt_tick_get(void)
{
/* return the global tick */
return (rt_tick_t)rt_atomic_load(&(rt_tick));
}
RTM_EXPORT(rt_tick_get);
/**
* @brief This function will set current tick.
*
* @param tick is the value that you will set.
*/
void rt_tick_set(rt_tick_t tick)
{
rt_atomic_store(&(rt_tick), tick);
}
/**
* @brief This function will notify kernel there is one tick passed.
* Normally, this function is invoked by clock ISR.
*/
void rt_tick_increase(void)
{
RT_ASSERT(rt_interrupt_get_nest() > 0);
RT_OBJECT_HOOK_CALL(rt_tick_hook, ());
/* increase the global tick */
#ifdef RT_USING_SMP
/* get percpu and increase the tick */
rt_atomic_add(&(rt_cpu_self()->tick), 1);
#else
rt_atomic_add(&(rt_tick), 1);
#endif /* RT_USING_SMP */
/* check time slice */
rt_sched_tick_increase();
/* check timer */
#ifdef RT_USING_SMP
if (rt_hw_cpu_id() != 0)
{
return;
}
#endif
rt_timer_check();
}
/**
* @brief This function will calculate the tick from millisecond.
*
* @param ms is the specified millisecond.
* - Negative Number wait forever
* - Zero not wait
* - Max 0x7fffffff
*
* @return Return the calculated tick.
*/
rt_tick_t rt_tick_from_millisecond(rt_int32_t ms)
{
rt_tick_t tick;
if (ms < 0)
{
tick = (rt_tick_t)RT_WAITING_FOREVER;
}
else
{
tick = RT_TICK_PER_SECOND * (ms / 1000);
tick += (RT_TICK_PER_SECOND * (ms % 1000) + 999) / 1000;
}
/* return the calculated tick */
return tick;
}
RTM_EXPORT(rt_tick_from_millisecond);
/**
* @brief This function will return the passed millisecond from boot.
*
* @note if the value of RT_TICK_PER_SECOND is lower than 1000 or
* is not an integral multiple of 1000, this function will not
* provide the correct 1ms-based tick.
*
* @return Return passed millisecond from boot.
*/
rt_weak rt_tick_t rt_tick_get_millisecond(void)
{
#if RT_TICK_PER_SECOND == 0 // make cppcheck happy
#error "RT_TICK_PER_SECOND must be greater than zero"
#endif
#if 1000 % RT_TICK_PER_SECOND == 0u
return rt_tick_get() * (1000u / RT_TICK_PER_SECOND);
#else
#warning "rt-thread cannot provide a correct 1ms-based tick any longer,\
please redefine this function in another file by using a high-precision hard-timer."
return 0;
#endif /* 1000 % RT_TICK_PER_SECOND == 0u */
}
/**@}*/
+283
View File
@@ -0,0 +1,283 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2012-09-20 Bernard Change the name to components.c
* And all components related header files.
* 2012-12-23 Bernard fix the pthread initialization issue.
* 2013-06-23 Bernard Add the init_call for components initialization.
* 2013-07-05 Bernard Remove initialization feature for MS VC++ compiler
* 2015-02-06 Bernard Remove the MS VC++ support and move to the kernel
* 2015-05-04 Bernard Rename it to components.c because compiling issue
* in some IDEs.
* 2015-07-29 Arda.Fu Add support to use RT_USING_USER_MAIN with IAR
* 2018-11-22 Jesven Add secondary cpu boot up
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
*/
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_USER_MAIN
#ifndef RT_MAIN_THREAD_STACK_SIZE
#define RT_MAIN_THREAD_STACK_SIZE 2048
#endif /* RT_MAIN_THREAD_STACK_SIZE */
#ifndef RT_MAIN_THREAD_PRIORITY
#define RT_MAIN_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX / 3)
#endif /* RT_MAIN_THREAD_PRIORITY */
#endif /* RT_USING_USER_MAIN */
#ifdef RT_USING_COMPONENTS_INIT
/*
* Components Initialization will initialize some driver and components as following
* order:
* rti_start --> 0
* BOARD_EXPORT --> 1
* rti_board_end --> 1.end
*
* DEVICE_EXPORT --> 2
* COMPONENT_EXPORT --> 3
* FS_EXPORT --> 4
* ENV_EXPORT --> 5
* APP_EXPORT --> 6
*
* rti_end --> 6.end
*
* These automatically initialization, the driver or component initial function must
* be defined with:
* INIT_BOARD_EXPORT(fn);
* INIT_DEVICE_EXPORT(fn);
* ...
* INIT_APP_EXPORT(fn);
* etc.
*/
static int rti_start(void)
{
return 0;
}
INIT_EXPORT(rti_start, "0");
static int rti_board_start(void)
{
return 0;
}
INIT_EXPORT(rti_board_start, "0.end");
static int rti_board_end(void)
{
return 0;
}
INIT_EXPORT(rti_board_end, "1.end");
static int rti_end(void)
{
return 0;
}
INIT_EXPORT(rti_end, "6.end");
/**
* @brief Onboard components initialization. In this function, the board-level
* initialization function will be called to complete the initialization
* of the on-board peripherals.
*/
void rt_components_board_init(void)
{
#ifdef RT_DEBUGING_AUTO_INIT
int result;
const struct rt_init_desc *desc;
for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
{
(*fn_ptr)();
}
#endif /* RT_DEBUGING_AUTO_INIT */
}
/**
* @brief RT-Thread Components Initialization.
*/
void rt_components_init(void)
{
#ifdef RT_DEBUGING_AUTO_INIT
int result;
const struct rt_init_desc *desc;
rt_kprintf("do components initialization.\n");
for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
{
(*fn_ptr)();
}
#endif /* RT_DEBUGING_AUTO_INIT */
}
#endif /* RT_USING_COMPONENTS_INIT */
#ifdef RT_USING_USER_MAIN
void rt_application_init(void);
void rt_hw_board_init(void);
int rtthread_startup(void);
#ifdef __ARMCC_VERSION
extern int $Super$$main(void);
/* re-define main function */
int $Sub$$main(void)
{
rtthread_startup();
return 0;
}
#elif defined(__ICCARM__)
/* __low_level_init will auto called by IAR cstartup */
extern void __iar_data_init3(void);
int __low_level_init(void)
{
// call IAR table copy function.
__iar_data_init3();
rtthread_startup();
return 0;
}
#elif defined(__GNUC__)
/* Add -eentry to arm-none-eabi-gcc argument */
int entry(void)
{
rtthread_startup();
return 0;
}
#endif
#ifndef RT_USING_HEAP
/* if there is not enable heap, we should use static thread and stack. */
rt_align(RT_ALIGN_SIZE)
static rt_uint8_t main_thread_stack[RT_MAIN_THREAD_STACK_SIZE];
struct rt_thread main_thread;
#endif /* RT_USING_HEAP */
/**
* @brief The system main thread. In this thread will call the rt_components_init()
* for initialization of RT-Thread Components and call the user's programming
* entry main().
*
* @param parameter is the arg of the thread.
*/
static void main_thread_entry(void *parameter)
{
extern int main(void);
RT_UNUSED(parameter);
#ifdef RT_USING_COMPONENTS_INIT
/* RT-Thread components initialization */
rt_components_init();
#endif /* RT_USING_COMPONENTS_INIT */
#ifdef RT_USING_SMP
rt_hw_secondary_cpu_up();
#endif /* RT_USING_SMP */
/* invoke system main function */
#ifdef __ARMCC_VERSION
{
extern int $Super$$main(void);
$Super$$main(); /* for ARMCC. */
}
#elif defined(__ICCARM__) || defined(__GNUC__) || defined(__TASKING__) || defined(__TI_COMPILER_VERSION__)
main();
#endif /* __ARMCC_VERSION */
}
/**
* @brief This function will create and start the main thread, but this thread
* will not run until the scheduler starts.
*/
void rt_application_init(void)
{
rt_thread_t tid;
#ifdef RT_USING_HEAP
tid = rt_thread_create("main", main_thread_entry, RT_NULL,
RT_MAIN_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);
RT_ASSERT(tid != RT_NULL);
#else
rt_err_t result;
tid = &main_thread;
result = rt_thread_init(tid, "main", main_thread_entry, RT_NULL,
main_thread_stack, sizeof(main_thread_stack), RT_MAIN_THREAD_PRIORITY, 20);
RT_ASSERT(result == RT_EOK);
/* if not define RT_USING_HEAP, using to eliminate the warning */
(void)result;
#endif /* RT_USING_HEAP */
rt_thread_startup(tid);
}
/**
* @brief This function will call all levels of initialization functions to complete
* the initialization of the system, and finally start the scheduler.
*
* @return Normally never returns. If 0 is returned, the scheduler failed.
*/
int rtthread_startup(void)
{
#ifdef RT_USING_SMP
rt_hw_spin_lock_init(&_cpus_lock);
#endif
rt_hw_local_irq_disable();
/* board level initialization
* NOTE: please initialize heap inside board initialization.
*/
rt_hw_board_init();
/* show RT-Thread version */
rt_show_version();
/* timer system initialization */
rt_system_timer_init();
/* scheduler system initialization */
rt_system_scheduler_init();
#ifdef RT_USING_SIGNALS
/* signal system initialization */
rt_system_signal_init();
#endif /* RT_USING_SIGNALS */
/* create init_thread */
rt_application_init();
/* timer thread initialization */
rt_system_timer_thread_init();
/* idle thread initialization */
rt_thread_idle_init();
#ifdef RT_USING_SMP
rt_hw_spin_lock(&_cpus_lock);
#endif /* RT_USING_SMP */
/* start scheduler */
rt_system_scheduler_start();
/* never reach here */
return 0;
}
#endif /* RT_USING_USER_MAIN */
+220
View File
@@ -0,0 +1,220 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2018-10-30 Bernard The first version
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2023-12-10 xqyjlj spinlock should lock sched
* 2024-01-25 Shell Using rt_exit_critical_safe
*/
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_SMART
#include <lwp.h>
#endif
#ifdef RT_USING_DEBUG
rt_base_t _cpus_critical_level;
#endif /* RT_USING_DEBUG */
#ifdef RT_USING_SMP
static struct rt_cpu _cpus[RT_CPUS_NR];
rt_hw_spinlock_t _cpus_lock;
#if defined(RT_DEBUGING_SPINLOCK)
void *_cpus_lock_owner = 0;
void *_cpus_lock_pc = 0;
#endif /* RT_DEBUGING_SPINLOCK */
/**
* @brief Initialize a static spinlock object.
*
* @param lock is a pointer to the spinlock to initialize.
*/
void rt_spin_lock_init(struct rt_spinlock *lock)
{
rt_hw_spin_lock_init(&lock->lock);
}
RTM_EXPORT(rt_spin_lock_init)
/**
* @brief This function will lock the spinlock, will lock the thread scheduler.
*
* @note If the spinlock is locked, the current CPU will keep polling the spinlock state
* until the spinlock is unlocked.
*
* @param lock is a pointer to the spinlock.
*/
void rt_spin_lock(struct rt_spinlock *lock)
{
rt_enter_critical();
rt_hw_spin_lock(&lock->lock);
RT_SPIN_LOCK_DEBUG(lock);
}
RTM_EXPORT(rt_spin_lock)
/**
* @brief This function will unlock the spinlock, will unlock the thread scheduler.
*
* @param lock is a pointer to the spinlock.
*/
void rt_spin_unlock(struct rt_spinlock *lock)
{
rt_base_t critical_level;
RT_SPIN_UNLOCK_DEBUG(lock, critical_level);
rt_hw_spin_unlock(&lock->lock);
rt_exit_critical_safe(critical_level);
}
RTM_EXPORT(rt_spin_unlock)
/**
* @brief This function will disable the local interrupt and then lock the spinlock, will lock the thread scheduler.
*
* @note If the spinlock is locked, the current CPU will keep polling the spinlock state
* until the spinlock is unlocked.
*
* @param lock is a pointer to the spinlock.
*
* @return Return current cpu interrupt status.
*/
rt_base_t rt_spin_lock_irqsave(struct rt_spinlock *lock)
{
rt_base_t level;
level = rt_hw_local_irq_disable();
rt_enter_critical();
rt_hw_spin_lock(&lock->lock);
RT_SPIN_LOCK_DEBUG(lock);
return level;
}
RTM_EXPORT(rt_spin_lock_irqsave)
/**
* @brief This function will unlock the spinlock and then restore current cpu interrupt status, will unlock the thread scheduler.
*
* @param lock is a pointer to the spinlock.
*
* @param level is interrupt status returned by rt_spin_lock_irqsave().
*/
void rt_spin_unlock_irqrestore(struct rt_spinlock *lock, rt_base_t level)
{
rt_base_t critical_level;
RT_SPIN_UNLOCK_DEBUG(lock, critical_level);
rt_hw_spin_unlock(&lock->lock);
rt_hw_local_irq_enable(level);
rt_exit_critical_safe(critical_level);
}
RTM_EXPORT(rt_spin_unlock_irqrestore)
/**
* @brief This fucntion will return current cpu object.
*
* @return Return a pointer to the current cpu object.
*/
struct rt_cpu *rt_cpu_self(void)
{
return &_cpus[rt_hw_cpu_id()];
}
/**
* @brief This fucntion will return the cpu object corresponding to index.
*
* @param index is the index of target cpu object.
*
* @return Return a pointer to the cpu object corresponding to index.
*/
struct rt_cpu *rt_cpu_index(int index)
{
return &_cpus[index];
}
/**
* @brief This function will lock all cpus's scheduler and disable local irq.
*
* @return Return current cpu interrupt status.
*/
rt_base_t rt_cpus_lock(void)
{
rt_base_t level;
struct rt_cpu* pcpu;
level = rt_hw_local_irq_disable();
pcpu = rt_cpu_self();
if (pcpu->current_thread != RT_NULL)
{
register rt_ubase_t lock_nest = rt_atomic_load(&(pcpu->current_thread->cpus_lock_nest));
rt_atomic_add(&(pcpu->current_thread->cpus_lock_nest), 1);
if (lock_nest == 0)
{
rt_enter_critical();
rt_hw_spin_lock(&_cpus_lock);
#ifdef RT_USING_DEBUG
_cpus_critical_level = rt_critical_level();
#endif /* RT_USING_DEBUG */
#ifdef RT_DEBUGING_SPINLOCK
_cpus_lock_owner = pcpu->current_thread;
_cpus_lock_pc = __GET_RETURN_ADDRESS;
#endif /* RT_DEBUGING_SPINLOCK */
}
}
return level;
}
RTM_EXPORT(rt_cpus_lock);
/**
* @brief This function will restore all cpus's scheduler and restore local irq.
*
* @param level is interrupt status returned by rt_cpus_lock().
*/
void rt_cpus_unlock(rt_base_t level)
{
struct rt_cpu* pcpu = rt_cpu_self();
if (pcpu->current_thread != RT_NULL)
{
rt_base_t critical_level = 0;
RT_ASSERT(rt_atomic_load(&(pcpu->current_thread->cpus_lock_nest)) > 0);
rt_atomic_sub(&(pcpu->current_thread->cpus_lock_nest), 1);
if (pcpu->current_thread->cpus_lock_nest == 0)
{
#if defined(RT_DEBUGING_SPINLOCK)
_cpus_lock_owner = __OWNER_MAGIC;
_cpus_lock_pc = RT_NULL;
#endif /* RT_DEBUGING_SPINLOCK */
#ifdef RT_USING_DEBUG
critical_level = _cpus_critical_level;
_cpus_critical_level = 0;
#endif /* RT_USING_DEBUG */
rt_hw_spin_unlock(&_cpus_lock);
rt_exit_critical_safe(critical_level);
}
}
rt_hw_local_irq_enable(level);
}
RTM_EXPORT(rt_cpus_unlock);
/**
* This function is invoked by scheduler.
* It will restore the lock state to whatever the thread's counter expects.
* If target thread not locked the cpus then unlock the cpus lock.
*
* @param thread is a pointer to the target thread.
*/
void rt_cpus_lock_status_restore(struct rt_thread *thread)
{
#if defined(ARCH_MM_MMU) && defined(RT_USING_SMART)
lwp_aspace_switch(thread);
#endif
rt_sched_post_ctx_switch(thread);
}
RTM_EXPORT(rt_cpus_lock_status_restore);
#endif /* RT_USING_SMP */
+390
View File
@@ -0,0 +1,390 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2006-03-23 Bernard the first version
* 2010-11-10 Bernard add cleanup callback function in thread exit.
* 2012-12-29 Bernard fix compiling warning.
* 2013-12-21 Grissiom let rt_thread_idle_excute loop until there is no
* dead thread.
* 2016-08-09 ArdaFu add method to get the handler of the idle thread.
* 2018-02-07 Bernard lock scheduler to protect tid->cleanup.
* 2018-07-14 armink add idle hook list
* 2018-11-22 Jesven add per cpu idle task
* combine the code of primary and secondary cpu
* 2021-11-15 THEWON Remove duplicate work between idle and _thread_exit
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2023-11-07 xqyjlj fix thread exit
* 2023-12-10 xqyjlj add _hook_spinlock
*/
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_MODULE
#include <dlmodule.h>
#endif /* RT_USING_MODULE */
#ifdef RT_USING_HOOK
#ifndef RT_USING_IDLE_HOOK
#define RT_USING_IDLE_HOOK
#endif /* RT_USING_IDLE_HOOK */
#endif /* RT_USING_HOOK */
#ifndef IDLE_THREAD_STACK_SIZE
#if defined (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP)
#define IDLE_THREAD_STACK_SIZE 256
#else
#define IDLE_THREAD_STACK_SIZE 128
#endif /* (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP) */
#endif /* IDLE_THREAD_STACK_SIZE */
#define _CPUS_NR RT_CPUS_NR
static rt_list_t _rt_thread_defunct = RT_LIST_OBJECT_INIT(_rt_thread_defunct);
static struct rt_spinlock _defunct_spinlock;
static struct rt_thread idle_thread[_CPUS_NR];
rt_align(RT_ALIGN_SIZE)
static rt_uint8_t idle_thread_stack[_CPUS_NR][IDLE_THREAD_STACK_SIZE];
#ifdef RT_USING_SMP
#ifndef SYSTEM_THREAD_STACK_SIZE
#define SYSTEM_THREAD_STACK_SIZE IDLE_THREAD_STACK_SIZE
#endif
static struct rt_thread rt_system_thread;
rt_align(RT_ALIGN_SIZE)
static rt_uint8_t rt_system_stack[SYSTEM_THREAD_STACK_SIZE];
static struct rt_semaphore system_sem;
#endif
#ifdef RT_USING_IDLE_HOOK
#ifndef RT_IDLE_HOOK_LIST_SIZE
#define RT_IDLE_HOOK_LIST_SIZE 4
#endif /* RT_IDLE_HOOK_LIST_SIZE */
static void (*idle_hook_list[RT_IDLE_HOOK_LIST_SIZE])(void);
static struct rt_spinlock _hook_spinlock;
/**
* @brief This function sets a hook function to idle thread loop. When the system performs
* idle loop, this hook function should be invoked.
*
* @param hook the specified hook function.
*
* @return RT_EOK: set OK.
* -RT_EFULL: hook list is full.
*
* @note the hook function must be simple and never be blocked or suspend.
*/
rt_err_t rt_thread_idle_sethook(void (*hook)(void))
{
rt_size_t i;
rt_err_t ret = -RT_EFULL;
rt_base_t level;
level = rt_spin_lock_irqsave(&_hook_spinlock);
for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++)
{
if (idle_hook_list[i] == RT_NULL)
{
idle_hook_list[i] = hook;
ret = RT_EOK;
break;
}
}
rt_spin_unlock_irqrestore(&_hook_spinlock, level);
return ret;
}
/**
* @brief delete the idle hook on hook list.
*
* @param hook the specified hook function.
*
* @return RT_EOK: delete OK.
* -RT_ENOSYS: hook was not found.
*/
rt_err_t rt_thread_idle_delhook(void (*hook)(void))
{
rt_size_t i;
rt_err_t ret = -RT_ENOSYS;
rt_base_t level;
level = rt_spin_lock_irqsave(&_hook_spinlock);
for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++)
{
if (idle_hook_list[i] == hook)
{
idle_hook_list[i] = RT_NULL;
ret = RT_EOK;
break;
}
}
rt_spin_unlock_irqrestore(&_hook_spinlock, level);
return ret;
}
#endif /* RT_USING_IDLE_HOOK */
/**
* @brief Enqueue a thread to defunct queue.
*
* @param thread the thread to be enqueued.
*
* @note It must be called between rt_hw_interrupt_disable and rt_hw_interrupt_enable
*/
void rt_thread_defunct_enqueue(rt_thread_t thread)
{
rt_base_t level;
level = rt_spin_lock_irqsave(&_defunct_spinlock);
rt_list_insert_after(&_rt_thread_defunct, &RT_THREAD_LIST_NODE(thread));
rt_spin_unlock_irqrestore(&_defunct_spinlock, level);
#ifdef RT_USING_SMP
rt_sem_release(&system_sem);
#endif
}
/**
* @brief Dequeue a thread from defunct queue.
*/
rt_thread_t rt_thread_defunct_dequeue(void)
{
rt_base_t level;
rt_thread_t thread = RT_NULL;
rt_list_t *l = &_rt_thread_defunct;
#ifdef RT_USING_SMP
level = rt_spin_lock_irqsave(&_defunct_spinlock);
if (l->next != l)
{
thread = RT_THREAD_LIST_NODE_ENTRY(l->next);
rt_list_remove(&RT_THREAD_LIST_NODE(thread));
}
rt_spin_unlock_irqrestore(&_defunct_spinlock, level);
#else
if (l->next != l)
{
thread = RT_THREAD_LIST_NODE_ENTRY(l->next);
level = rt_hw_interrupt_disable();
rt_list_remove(&RT_THREAD_LIST_NODE(thread));
rt_hw_interrupt_enable(level);
}
#endif
return thread;
}
/**
* @brief This function will perform system background job when system idle.
*/
static void rt_defunct_execute(void)
{
/* Loop until there is no dead thread. So one call to rt_defunct_execute
* will do all the cleanups. */
while (1)
{
rt_thread_t thread;
rt_bool_t object_is_systemobject;
void (*cleanup)(struct rt_thread *tid);
#ifdef RT_USING_MODULE
struct rt_dlmodule *module = RT_NULL;
#endif
/* get defunct thread */
thread = rt_thread_defunct_dequeue();
if (thread == RT_NULL)
{
break;
}
#ifdef RT_USING_MODULE
module = (struct rt_dlmodule*)thread->parent.module_id;
if (module)
{
dlmodule_destroy(module);
}
#endif
#ifdef RT_USING_SIGNALS
rt_thread_free_sig(thread);
#endif
/* store the point of "thread->cleanup" avoid to lose */
cleanup = thread->cleanup;
/* if it's a system object, not delete it */
object_is_systemobject = rt_object_is_systemobject((rt_object_t)thread);
if (object_is_systemobject == RT_TRUE)
{
/* detach this object */
rt_object_detach((rt_object_t)thread);
}
/* invoke thread cleanup */
if (cleanup != RT_NULL)
{
cleanup(thread);
}
#ifdef RT_USING_HEAP
#ifdef RT_USING_MEM_PROTECTION
if (thread->mem_regions != RT_NULL)
{
RT_KERNEL_FREE(thread->mem_regions);
}
#endif
/* if need free, delete it */
if (object_is_systemobject == RT_FALSE)
{
/* release thread's stack */
#ifdef RT_USING_HW_STACK_GUARD
RT_KERNEL_FREE(thread->stack_buf);
#else
RT_KERNEL_FREE(thread->stack_addr);
#endif
/* delete thread object */
rt_object_delete((rt_object_t)thread);
}
#endif
}
}
static void idle_thread_entry(void *parameter)
{
RT_UNUSED(parameter);
#ifdef RT_USING_SMP
if (rt_hw_cpu_id() != 0)
{
while (1)
{
rt_hw_secondary_cpu_idle_exec();
}
}
#endif /* RT_USING_SMP */
while (1)
{
#ifdef RT_USING_IDLE_HOOK
rt_size_t i;
void (*idle_hook)(void);
for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++)
{
idle_hook = idle_hook_list[i];
if (idle_hook != RT_NULL)
{
idle_hook();
}
}
#endif /* RT_USING_IDLE_HOOK */
#ifndef RT_USING_SMP
rt_defunct_execute();
#endif /* RT_USING_SMP */
#ifdef RT_USING_PM
void rt_system_power_manager(void);
rt_system_power_manager();
#endif /* RT_USING_PM */
}
}
#ifdef RT_USING_SMP
static void rt_thread_system_entry(void *parameter)
{
RT_UNUSED(parameter);
while (1)
{
int ret = rt_sem_take(&system_sem, RT_WAITING_FOREVER);
if (ret != RT_EOK)
{
rt_kprintf("failed to sem_take() error %d\n", ret);
RT_ASSERT(0);
}
rt_defunct_execute();
}
}
#endif
/**
* @brief This function will initialize idle thread, then start it.
*
* @note this function must be invoked when system init.
*/
void rt_thread_idle_init(void)
{
rt_ubase_t i;
#if RT_NAME_MAX > 0
char idle_thread_name[RT_NAME_MAX];
#endif /* RT_NAME_MAX > 0 */
for (i = 0; i < _CPUS_NR; i++)
{
#if RT_NAME_MAX > 0
rt_snprintf(idle_thread_name, RT_NAME_MAX, "tidle%d", i);
#endif /* RT_NAME_MAX > 0 */
rt_thread_init(&idle_thread[i],
#if RT_NAME_MAX > 0
idle_thread_name,
#else
"tidle",
#endif /* RT_NAME_MAX > 0 */
idle_thread_entry,
RT_NULL,
&idle_thread_stack[i][0],
sizeof(idle_thread_stack[i]),
RT_THREAD_PRIORITY_MAX - 1,
32);
#ifdef RT_USING_SMP
rt_thread_control(&idle_thread[i], RT_THREAD_CTRL_BIND_CPU, (void*)i);
rt_cpu_index(i)->idle_thread = &idle_thread[i];
#endif /* RT_USING_SMP */
/* startup */
rt_thread_startup(&idle_thread[i]);
}
#ifdef RT_USING_SMP
RT_ASSERT(RT_THREAD_PRIORITY_MAX > 2);
rt_spin_lock_init(&_defunct_spinlock);
rt_spin_lock_init(&_hook_spinlock);
rt_sem_init(&system_sem, "defunct", 0, RT_IPC_FLAG_FIFO);
/* create defunct thread */
rt_thread_init(&rt_system_thread,
"tsystem",
rt_thread_system_entry,
RT_NULL,
rt_system_stack,
sizeof(rt_system_stack),
RT_THREAD_PRIORITY_MAX - 2,
32);
/* startup */
rt_thread_startup(&rt_system_thread);
#endif
}
/**
* @brief This function will get the handler of the idle thread.
*/
rt_thread_t rt_thread_idle_gethandler(void)
{
#ifdef RT_USING_SMP
int id = rt_hw_cpu_id();
#else
int id = 0;
#endif /* RT_USING_SMP */
return (rt_thread_t)(&idle_thread[id]);
}
+4025
View File
File diff suppressed because it is too large. Load diff
+137
View File
@@ -0,0 +1,137 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2006-02-24 Bernard first version
* 2006-05-03 Bernard add IRQ_DEBUG
* 2016-08-09 ArdaFu add interrupt enter and leave hook.
* 2018-11-22 Jesven rt_interrupt_get_nest function add disable irq
* 2021-08-15 Supperthomas fix the comment
* 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to irq.c
* 2022-07-04 Yunjie fix RT_DEBUG_LOG
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2024-01-05 Shell Fixup of data racing in rt_interrupt_get_nest
*/
#include <rthw.h>
#include <rtthread.h>
#define DBG_TAG "kernel.irq"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
static void (*rt_interrupt_enter_hook)(void);
static void (*rt_interrupt_leave_hook)(void);
/**
* @ingroup Hook
*
* @brief This function set a hook function when the system enter a interrupt
*
* @note The hook function must be simple and never be blocked or suspend.
*
* @param hook the function point to be called
*/
void rt_interrupt_enter_sethook(void (*hook)(void))
{
rt_interrupt_enter_hook = hook;
}
/**
* @ingroup Hook
*
* @brief This function set a hook function when the system exit a interrupt.
*
* @note The hook function must be simple and never be blocked or suspend.
*
* @param hook the function point to be called
*/
void rt_interrupt_leave_sethook(void (*hook)(void))
{
rt_interrupt_leave_hook = hook;
}
#endif /* RT_USING_HOOK */
/**
* @addtogroup Kernel
*/
/**@{*/
#ifdef RT_USING_SMP
#define rt_interrupt_nest rt_cpu_self()->irq_nest
#else
volatile rt_atomic_t rt_interrupt_nest = 0;
#endif /* RT_USING_SMP */
/**
* @brief This function will be invoked by BSP, when enter interrupt service routine
*
* @note Please don't invoke this routine in application
*
* @see rt_interrupt_leave
*/
rt_weak void rt_interrupt_enter(void)
{
rt_atomic_add(&(rt_interrupt_nest), 1);
RT_OBJECT_HOOK_CALL(rt_interrupt_enter_hook,());
LOG_D("irq has come..., irq current nest:%d",
(rt_int32_t)rt_atomic_load(&(rt_interrupt_nest)));
}
RTM_EXPORT(rt_interrupt_enter);
/**
* @brief This function will be invoked by BSP, when leave interrupt service routine
*
* @note Please don't invoke this routine in application
*
* @see rt_interrupt_enter
*/
rt_weak void rt_interrupt_leave(void)
{
LOG_D("irq is going to leave, irq current nest:%d",
(rt_int32_t)rt_atomic_load(&(rt_interrupt_nest)));
RT_OBJECT_HOOK_CALL(rt_interrupt_leave_hook,());
rt_atomic_sub(&(rt_interrupt_nest), 1);
}
RTM_EXPORT(rt_interrupt_leave);
/**
* @brief This function will return the nest of interrupt.
*
* User application can invoke this function to get whether current
* context is interrupt context.
*
* @return the number of nested interrupts.
*/
rt_weak rt_uint8_t rt_interrupt_get_nest(void)
{
rt_uint8_t ret;
rt_base_t level;
level = rt_hw_local_irq_disable();
ret = rt_atomic_load(&rt_interrupt_nest);
rt_hw_local_irq_enable(level);
return ret;
}
RTM_EXPORT(rt_interrupt_get_nest);
RTM_EXPORT(rt_hw_interrupt_disable);
RTM_EXPORT(rt_hw_interrupt_enable);
rt_weak rt_bool_t rt_hw_interrupt_is_disabled(void)
{
return RT_FALSE;
}
RTM_EXPORT(rt_hw_interrupt_is_disabled);
/**@}*/
+686
View File
@@ -0,0 +1,686 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-03-10 Meco Man the first version
*/
#include <rtdef.h>
#include <rtcompiler.h>
#include <rtklibc.h>
#include <rtm.h>
#define DBG_TAG "kernel.stdio"
#ifdef RT_DEBUG_DEVICE
#define DBG_LVL DBG_LOG
#else
#define DBG_LVL DBG_WARNING
#endif /* defined (RT_DEBUG_DEVICE) */
#include <rtdbg.h>
/* private function */
#define _ISDIGIT(c) ((unsigned)((c) - '0') < 10)
/**
* @brief This function will duplicate a string.
*
* @param n is the string to be duplicated.
*
* @param base is support divide instructions value.
*
* @return the duplicated string pointer.
*/
#ifdef RT_KPRINTF_USING_LONGLONG
rt_inline int divide(unsigned long long *n, int base)
#else
rt_inline int divide(unsigned long *n, int base)
#endif /* RT_KPRINTF_USING_LONGLONG */
{
int res;
/* optimized for processor which does not support divide instructions. */
#ifdef RT_KPRINTF_USING_LONGLONG
res = (int)((*n) % base);
*n = (long long)((*n) / base);
#else
res = (int)((*n) % base);
*n = (long)((*n) / base);
#endif
return res;
}
rt_inline int skip_atoi(const char **s)
{
int i = 0;
while (_ISDIGIT(**s))
i = i * 10 + *((*s)++) - '0';
return i;
}
#define ZEROPAD (1 << 0) /* pad with zero */
#define SIGN (1 << 1) /* unsigned/signed long */
#define PLUS (1 << 2) /* show plus */
#define SPACE (1 << 3) /* space if plus */
#define LEFT (1 << 4) /* left justified */
#define SPECIAL (1 << 5) /* 0x */
#define LARGE (1 << 6) /* use 'ABCDEF' instead of 'abcdef' */
static char *print_number(char *buf,
char *end,
#ifdef RT_KPRINTF_USING_LONGLONG
unsigned long long num,
#else
unsigned long num,
#endif /* RT_KPRINTF_USING_LONGLONG */
int base,
int qualifier,
int s,
int precision,
int type)
{
char c = 0, sign = 0;
#ifdef RT_KPRINTF_USING_LONGLONG
char tmp[64] = {0};
#else
char tmp[32] = {0};
#endif /* RT_KPRINTF_USING_LONGLONG */
int precision_bak = precision;
const char *digits = RT_NULL;
static const char small_digits[] = "0123456789abcdef";
static const char large_digits[] = "0123456789ABCDEF";
int i = 0;
int size = 0;
size = s;
digits = (type & LARGE) ? large_digits : small_digits;
if (type & LEFT)
{
type &= ~ZEROPAD;
}
c = (type & ZEROPAD) ? '0' : ' ';
/* get sign */
sign = 0;
if (type & SIGN)
{
switch (qualifier)
{
case 'h':
if ((rt_int16_t)num < 0)
{
sign = '-';
num = (rt_uint16_t)-num;
}
break;
case 'L':
case 'l':
if ((long)num < 0)
{
sign = '-';
num = (unsigned long)-num;
}
break;
case 0:
default:
if ((rt_int32_t)num < 0)
{
sign = '-';
num = (rt_uint32_t)-num;
}
break;
}
if (sign != '-')
{
if (type & PLUS)
{
sign = '+';
}
else if (type & SPACE)
{
sign = ' ';
}
}
}
if (type & SPECIAL)
{
if (base == 2 || base == 16)
{
size -= 2;
}
else if (base == 8)
{
size--;
}
}
i = 0;
if (num == 0)
{
tmp[i++] = '0';
}
else
{
while (num != 0)
tmp[i++] = digits[divide(&num, base)];
}
if (i > precision)
{
precision = i;
}
size -= precision;
if (!(type & (ZEROPAD | LEFT)))
{
if ((sign) && (size > 0))
{
size--;
}
while (size-- > 0)
{
if (buf < end)
{
*buf = ' ';
}
++ buf;
}
}
if (sign)
{
if (buf < end)
{
*buf = sign;
}
-- size;
++ buf;
}
if (type & SPECIAL)
{
if (base == 2)
{
if (buf < end)
*buf = '0';
++ buf;
if (buf < end)
*buf = 'b';
++ buf;
}
else if (base == 8)
{
if (buf < end)
*buf = '0';
++ buf;
}
else if (base == 16)
{
if (buf < end)
{
*buf = '0';
}
++ buf;
if (buf < end)
{
*buf = type & LARGE ? 'X' : 'x';
}
++ buf;
}
}
/* no align to the left */
if (!(type & LEFT))
{
while (size-- > 0)
{
if (buf < end)
{
*buf = c;
}
++ buf;
}
}
while (i < precision--)
{
if (buf < end)
{
*buf = '0';
}
++ buf;
}
/* put number in the temporary buffer */
while (i-- > 0 && (precision_bak != 0))
{
if (buf < end)
{
*buf = tmp[i];
}
++ buf;
}
while (size-- > 0)
{
if (buf < end)
{
*buf = ' ';
}
++ buf;
}
return buf;
}
#if defined(__GNUC__) && !defined(__ARMCC_VERSION) /* GCC */
#pragma GCC diagnostic push
/* ignore warning: this statement may fall through */
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
#endif /* defined(__GNUC__) && !defined(__ARMCC_VERSION) */
/**
* @brief This function will fill a formatted string to buffer.
*
* @param buf is the buffer to save formatted string.
*
* @param size is the size of buffer.
*
* @param fmt is the format parameters.
*
* @param args is a list of variable parameters.
*
* @return The number of characters actually written to buffer.
*/
rt_weak int rt_vsnprintf(char *buf, rt_size_t size, const char *fmt, va_list args)
{
#ifdef RT_KPRINTF_USING_LONGLONG
unsigned long long num = 0;
#else
unsigned long num = 0;
#endif /* RT_KPRINTF_USING_LONGLONG */
int i = 0, len = 0;
char *str = RT_NULL, *end = RT_NULL, c = 0;
const char *s = RT_NULL;
rt_uint8_t base = 0; /* the base of number */
rt_uint8_t flags = 0; /* flags to print number */
rt_uint8_t qualifier = 0; /* 'h', 'l', or 'L' for integer fields */
rt_int32_t field_width = 0; /* width of output field */
int precision = 0; /* min. # of digits for integers and max for a string */
str = buf;
end = buf + size;
/* Make sure end is always >= buf */
if (end < buf)
{
end = ((char *) - 1);
size = end - buf;
}
for (; *fmt ; ++fmt)
{
if (*fmt != '%')
{
if (str < end)
{
*str = *fmt;
}
++ str;
continue;
}
/* process flags */
flags = 0;
while (1)
{
/* skips the first '%' also */
++fmt;
if (*fmt == '-') flags |= LEFT;
else if (*fmt == '+') flags |= PLUS;
else if (*fmt == ' ') flags |= SPACE;
else if (*fmt == '#') flags |= SPECIAL;
else if (*fmt == '0') flags |= ZEROPAD;
else break;
}
/* get field width */
field_width = -1;
if (_ISDIGIT(*fmt))
{
field_width = skip_atoi(&fmt);
}
else if (*fmt == '*')
{
++fmt;
/* it's the next argument */
field_width = va_arg(args, int);
if (field_width < 0)
{
field_width = -field_width;
flags |= LEFT;
}
}
/* get the precision */
precision = -1;
if (*fmt == '.')
{
++fmt;
if (_ISDIGIT(*fmt))
{
precision = skip_atoi(&fmt);
}
else if (*fmt == '*')
{
++fmt;
/* it's the next argument */
precision = va_arg(args, int);
}
if (precision < 0)
{
precision = 0;
}
}
qualifier = 0; /* get the conversion qualifier */
if (*fmt == 'h' || *fmt == 'l' ||
#ifdef RT_KPRINTF_USING_LONGLONG
*fmt == 'L' ||
#endif /* RT_KPRINTF_USING_LONGLONG */
*fmt == 'z')
{
qualifier = *fmt;
++fmt;
#ifdef RT_KPRINTF_USING_LONGLONG
if (qualifier == 'l' && *fmt == 'l')
{
qualifier = 'L';
++fmt;
}
#endif /* RT_KPRINTF_USING_LONGLONG */
if (qualifier == 'h' && *fmt == 'h')
{
qualifier = 'H';
++fmt;
}
}
/* the default base */
base = 10;
switch (*fmt)
{
case 'c':
if (!(flags & LEFT))
{
while (--field_width > 0)
{
if (str < end) *str = ' ';
++ str;
}
}
/* get character */
c = (rt_uint8_t)va_arg(args, int);
if (str < end)
{
*str = c;
}
++ str;
/* put width */
while (--field_width > 0)
{
if (str < end) *str = ' ';
++ str;
}
continue;
case 's':
s = va_arg(args, char *);
if (!s)
{
s = "(NULL)";
}
for (len = 0; (len != field_width) && (s[len] != '\0'); len++);
if (precision > 0 && len > precision)
{
len = precision;
}
if (!(flags & LEFT))
{
while (len < field_width--)
{
if (str < end) *str = ' ';
++ str;
}
}
for (i = 0; i < len; ++i)
{
if (str < end) *str = *s;
++ str;
++ s;
}
while (len < field_width--)
{
if (str < end) *str = ' ';
++ str;
}
continue;
case 'p':
if (field_width == -1)
{
field_width = sizeof(void *) << 1;
field_width += 2; /* `0x` prefix */
flags |= SPECIAL;
flags |= ZEROPAD;
}
str = print_number(str, end, (unsigned long)va_arg(args, void *),
16, qualifier, field_width, precision, flags);
continue;
case '%':
if (str < end)
{
*str = '%';
}
++ str;
continue;
/* integer number formats - set up the flags and "break" */
case 'b':
base = 2;
break;
case 'o':
base = 8;
break;
case 'X':
flags |= LARGE;
case 'x':
base = 16;
break;
case 'd':
case 'i':
flags |= SIGN;
case 'u':
break;
case 'e':
case 'E':
case 'G':
case 'g':
case 'f':
case 'F':
va_arg(args, double);
default:
if (str < end)
{
*str = '%';
}
++ str;
if (*fmt)
{
if (str < end)
{
*str = *fmt;
}
++ str;
}
else
{
-- fmt;
}
continue;
}
if (qualifier == 'L')
{
num = va_arg(args, unsigned long long);
}
else if (qualifier == 'l')
{
num = va_arg(args, unsigned long);
}
else if (qualifier == 'H')
{
num = (rt_int8_t)va_arg(args, rt_int32_t);
if (flags & SIGN)
{
num = (rt_int8_t)num;
}
}
else if (qualifier == 'h')
{
num = (rt_uint16_t)va_arg(args, rt_int32_t);
if (flags & SIGN)
{
num = (rt_int16_t)num;
}
}
else if (qualifier == 'z')
{
num = va_arg(args, rt_size_t);
if (flags & SIGN)
{
num = (rt_ssize_t)num;
}
}
else
{
num = (rt_uint32_t)va_arg(args, unsigned long);
}
str = print_number(str, end, num, base, qualifier, field_width, precision, flags);
}
if (size > 0)
{
if (str < end)
{
*str = '\0';
}
else
{
end[-1] = '\0';
}
}
/* the trailing null byte doesn't count towards the total
* ++str;
*/
return str - buf;
}
RTM_EXPORT(rt_vsnprintf);
#if defined(__GNUC__) && !defined(__ARMCC_VERSION) /* GCC */
#pragma GCC diagnostic pop /* ignored "-Wimplicit-fallthrough" */
#endif /* defined(__GNUC__) && !defined(__ARMCC_VERSION) */
/**
* @brief This function will fill a formatted string to buffer.
*
* @param buf is the buffer to save formatted string.
*
* @param size is the size of buffer.
*
* @param fmt is the format parameters.
*
* @return The number of characters actually written to buffer.
*/
int rt_snprintf(char *buf, rt_size_t size, const char *fmt, ...)
{
rt_int32_t n = 0;
va_list args;
va_start(args, fmt);
n = rt_vsnprintf(buf, size, fmt, args);
va_end(args);
return n;
}
RTM_EXPORT(rt_snprintf);
/**
* @brief This function will fill a formatted string to buffer.
*
* @param buf is the buffer to save formatted string.
*
* @param format is the format parameters.
*
* @param arg_ptr is a list of variable parameters.
*
* @return The number of characters actually written to buffer.
*/
int rt_vsprintf(char *buf, const char *format, va_list arg_ptr)
{
return rt_vsnprintf(buf, (rt_size_t) - 1, format, arg_ptr);
}
RTM_EXPORT(rt_vsprintf);
/**
* @brief This function will fill a formatted string to buffer
*
* @param buf the buffer to save formatted string.
*
* @param format is the format parameters.
*
* @return The number of characters actually written to buffer.
*/
int rt_sprintf(char *buf, const char *format, ...)
{
rt_int32_t n = 0;
va_list arg_ptr;
va_start(arg_ptr, format);
n = rt_vsprintf(buf, format, arg_ptr);
va_end(arg_ptr);
return n;
}
RTM_EXPORT(rt_sprintf);
+507
View File
@@ -0,0 +1,507 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-03-10 Meco Man the first version
*/
#include <rtdef.h>
#include <rtcompiler.h>
#include <rtklibc.h>
#include <rtm.h>
#define DBG_TAG "kernel.string"
#ifdef RT_DEBUG_DEVICE
#define DBG_LVL DBG_LOG
#else
#define DBG_LVL DBG_WARNING
#endif /* defined (RT_DEBUG_DEVICE) */
#include <rtdbg.h>
#ifndef RT_KSERVICE_USING_STDLIB_MEMORY
/**
* @brief This function will set the content of memory to specified value.
*
* @param s is the address of source memory, point to the memory block to be filled.
*
* @param c is the value to be set. The value is passed in int form, but the function
* uses the unsigned character form of the value when filling the memory block.
*
* @param count number of bytes to be set.
*
* @return The address of source memory.
*/
rt_weak void *rt_memset(void *s, int c, rt_ubase_t count)
{
#ifdef RT_KSERVICE_USING_TINY_SIZE
char *xs = (char *)s;
while (count--)
*xs++ = c;
return s;
#else
#define LBLOCKSIZE (sizeof(rt_ubase_t))
#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1))
#define TOO_SMALL(LEN) ((LEN) < LBLOCKSIZE)
unsigned int i = 0;
char *m = (char *)s;
unsigned long buffer = 0;
unsigned long *aligned_addr = RT_NULL;
unsigned char d = (unsigned int)c & (unsigned char)(-1); /* To avoid sign extension, copy C to an
unsigned variable. (unsigned)((char)(-1))=0xFF for 8bit and =0xFFFF for 16bit: word independent */
RT_ASSERT(LBLOCKSIZE == 2 || LBLOCKSIZE == 4 || LBLOCKSIZE == 8);
if (!TOO_SMALL(count) && !UNALIGNED(s))
{
/* If we get this far, we know that count is large and s is word-aligned. */
aligned_addr = (unsigned long *)s;
/* Store d into each char sized location in buffer so that
* we can set large blocks quickly.
*/
for (i = 0; i < LBLOCKSIZE; i++)
{
*(((unsigned char *)&buffer)+i) = d;
}
while (count >= LBLOCKSIZE * 4)
{
*aligned_addr++ = buffer;
*aligned_addr++ = buffer;
*aligned_addr++ = buffer;
*aligned_addr++ = buffer;
count -= 4 * LBLOCKSIZE;
}
while (count >= LBLOCKSIZE)
{
*aligned_addr++ = buffer;
count -= LBLOCKSIZE;
}
/* Pick up the remainder with a bytewise loop. */
m = (char *)aligned_addr;
}
while (count--)
{
*m++ = (char)d;
}
return s;
#undef LBLOCKSIZE
#undef UNALIGNED
#undef TOO_SMALL
#endif /* RT_KSERVICE_USING_TINY_SIZE */
}
RTM_EXPORT(rt_memset);
/**
* @brief This function will copy memory content from source address to destination address.
*
* @param dst is the address of destination memory, points to the copied content.
*
* @param src is the address of source memory, pointing to the data source to be copied.
*
* @param count is the copied length.
*
* @return The address of destination memory
*/
rt_weak void *rt_memcpy(void *dst, const void *src, rt_ubase_t count)
{
#ifdef RT_KSERVICE_USING_TINY_SIZE
char *tmp = (char *)dst, *s = (char *)src;
rt_ubase_t len = 0;
if (tmp <= s || tmp > (s + count))
{
while (count--)
*tmp ++ = *s ++;
}
else
{
for (len = count; len > 0; len --)
tmp[len - 1] = s[len - 1];
}
return dst;
#else
#define UNALIGNED(X, Y) \
(((long)X & (sizeof (long) - 1)) | ((long)Y & (sizeof (long) - 1)))
#define BIGBLOCKSIZE (sizeof (long) << 2)
#define LITTLEBLOCKSIZE (sizeof (long))
#define TOO_SMALL(LEN) ((LEN) < BIGBLOCKSIZE)
char *dst_ptr = (char *)dst;
char *src_ptr = (char *)src;
long *aligned_dst = RT_NULL;
long *aligned_src = RT_NULL;
rt_ubase_t len = count;
/* If the size is small, or either SRC or DST is unaligned,
then punt into the byte copy loop. This should be rare. */
if (!TOO_SMALL(len) && !UNALIGNED(src_ptr, dst_ptr))
{
aligned_dst = (long *)dst_ptr;
aligned_src = (long *)src_ptr;
/* Copy 4X long words at a time if possible. */
while (len >= BIGBLOCKSIZE)
{
*aligned_dst++ = *aligned_src++;
*aligned_dst++ = *aligned_src++;
*aligned_dst++ = *aligned_src++;
*aligned_dst++ = *aligned_src++;
len -= BIGBLOCKSIZE;
}
/* Copy one long word at a time if possible. */
while (len >= LITTLEBLOCKSIZE)
{
*aligned_dst++ = *aligned_src++;
len -= LITTLEBLOCKSIZE;
}
/* Pick up any residual with a byte copier. */
dst_ptr = (char *)aligned_dst;
src_ptr = (char *)aligned_src;
}
while (len--)
*dst_ptr++ = *src_ptr++;
return dst;
#undef UNALIGNED
#undef BIGBLOCKSIZE
#undef LITTLEBLOCKSIZE
#undef TOO_SMALL
#endif /* RT_KSERVICE_USING_TINY_SIZE */
}
RTM_EXPORT(rt_memcpy);
/**
* @brief This function will move memory content from source address to destination
* address. If the destination memory does not overlap with the source memory,
* the function is the same as memcpy().
*
* @param dest is the address of destination memory, points to the copied content.
*
* @param src is the address of source memory, point to the data source to be copied.
*
* @param n is the copied length.
*
* @return The address of destination memory.
*/
void *rt_memmove(void *dest, const void *src, rt_size_t n)
{
char *tmp = (char *)dest, *s = (char *)src;
if (s < tmp && tmp < s + n)
{
tmp += n;
s += n;
while (n--)
*(--tmp) = *(--s);
}
else
{
while (n--)
*tmp++ = *s++;
}
return dest;
}
RTM_EXPORT(rt_memmove);
/**
* @brief This function will compare two areas of memory.
*
* @param cs is a block of memory.
*
* @param ct is another block of memory.
*
* @param count is the size of the area.
*
* @return Compare the results:
* If the result < 0, cs is smaller than ct.
* If the result > 0, cs is greater than ct.
* If the result = 0, cs is equal to ct.
*/
rt_int32_t rt_memcmp(const void *cs, const void *ct, rt_size_t count)
{
const unsigned char *su1 = RT_NULL, *su2 = RT_NULL;
int res = 0;
for (su1 = (const unsigned char *)cs, su2 = (const unsigned char *)ct; 0 < count; ++su1, ++su2, count--)
if ((res = *su1 - *su2) != 0)
break;
return res;
}
RTM_EXPORT(rt_memcmp);
#endif /* RT_KSERVICE_USING_STDLIB_MEMORY*/
#ifndef RT_KSERVICE_USING_STDLIB
/**
* @brief This function will return the first occurrence of a string, without the
* terminator '\0'.
*
* @param s1 is the source string.
*
* @param s2 is the find string.
*
* @return The first occurrence of a s2 in s1, or RT_NULL if no found.
*/
char *rt_strstr(const char *s1, const char *s2)
{
int l1 = 0, l2 = 0;
l2 = rt_strlen(s2);
if (!l2)
{
return (char *)s1;
}
l1 = rt_strlen(s1);
while (l1 >= l2)
{
l1 --;
if (!rt_memcmp(s1, s2, l2))
{
return (char *)s1;
}
s1 ++;
}
return RT_NULL;
}
RTM_EXPORT(rt_strstr);
/**
* @brief This function will compare two strings while ignoring differences in case
*
* @param a is the string to be compared.
*
* @param b is the string to be compared.
*
* @return Compare the results:
* If the result < 0, a is smaller than a.
* If the result > 0, a is greater than a.
* If the result = 0, a is equal to a.
*/
rt_int32_t rt_strcasecmp(const char *a, const char *b)
{
int ca = 0, cb = 0;
do
{
ca = *a++ & 0xff;
cb = *b++ & 0xff;
if (ca >= 'A' && ca <= 'Z')
ca += 'a' - 'A';
if (cb >= 'A' && cb <= 'Z')
cb += 'a' - 'A';
}
while (ca == cb && ca != '\0');
return ca - cb;
}
RTM_EXPORT(rt_strcasecmp);
/**
* @brief This function will copy string no more than n bytes.
*
* @param dst points to the address used to store the copied content.
*
* @param src is the string to be copied.
*
* @param n is the maximum copied length.
*
* @return The address where the copied content is stored.
*/
char *rt_strncpy(char *dst, const char *src, rt_size_t n)
{
if (n != 0)
{
char *d = dst;
const char *s = src;
do
{
if ((*d++ = *s++) == 0)
{
/* NUL pad the remaining n-1 bytes */
while (--n != 0)
{
*d++ = 0;
}
break;
}
} while (--n != 0);
}
return (dst);
}
RTM_EXPORT(rt_strncpy);
/**
* @brief This function will copy string.
*
* @param dst points to the address used to store the copied content.
*
* @param src is the string to be copied.
*
* @return The address where the copied content is stored.
*/
char *rt_strcpy(char *dst, const char *src)
{
char *dest = dst;
while (*src != '\0')
{
*dst = *src;
dst++;
src++;
}
*dst = '\0';
return dest;
}
RTM_EXPORT(rt_strcpy);
/**
* @brief This function will compare two strings with specified maximum length.
*
* @param cs is the string to be compared.
*
* @param ct is the string to be compared.
*
* @param count is the maximum compare length.
*
* @return Compare the results:
* If the result < 0, cs is smaller than ct.
* If the result > 0, cs is greater than ct.
* If the result = 0, cs is equal to ct.
*/
rt_int32_t rt_strncmp(const char *cs, const char *ct, rt_size_t count)
{
signed char __res = 0;
while (count)
{
if ((__res = *cs - *ct++) != 0 || !*cs++)
{
break;
}
count --;
}
return __res;
}
RTM_EXPORT(rt_strncmp);
/**
* @brief This function will compare two strings without specified length.
*
* @param cs is the string to be compared.
*
* @param ct is the string to be compared.
*
* @return Compare the results:
* If the result < 0, cs is smaller than ct.
* If the result > 0, cs is greater than ct.
* If the result = 0, cs is equal to ct.
*/
rt_int32_t rt_strcmp(const char *cs, const char *ct)
{
while (*cs && *cs == *ct)
{
cs++;
ct++;
}
return (*cs - *ct);
}
RTM_EXPORT(rt_strcmp);
/**
* @brief This function will return the length of a string, which terminate will
* null character.
*
* @param s is the string
*
* @return The length of string.
*/
rt_size_t rt_strlen(const char *s)
{
const char *sc = RT_NULL;
for (sc = s; *sc != '\0'; ++sc) /* nothing */
;
return sc - s;
}
RTM_EXPORT(rt_strlen);
#endif /* RT_KSERVICE_USING_STDLIB */
/**
* @brief The strnlen() function returns the number of characters in the
* string pointed to by s, excluding the terminating null byte ('\0'),
* but at most maxlen. In doing this, strnlen() looks only at the
* first maxlen characters in the string pointed to by s and never
* beyond s+maxlen.
*
* @param s is the string.
*
* @param maxlen is the max size.
*
* @return The length of string.
*/
rt_size_t rt_strnlen(const char *s, rt_ubase_t maxlen)
{
const char *sc;
for (sc = s; *sc != '\0' && (rt_ubase_t)(sc - s) < maxlen; ++sc) /* nothing */
;
return sc - s;
}
RTM_EXPORT(rt_strnlen);
#ifdef RT_USING_HEAP
/**
* @brief This function will duplicate a string.
*
* @param s is the string to be duplicated.
*
* @return The string address of the copy.
*/
char *rt_strdup(const char *s)
{
rt_size_t len = rt_strlen(s) + 1;
char *tmp = (char *)rt_malloc(len);
if (!tmp)
{
return RT_NULL;
}
rt_memcpy(tmp, s, len);
return tmp;
}
RTM_EXPORT(rt_strdup);
#endif /* RT_USING_HEAP */
File diff suppressed because it is too large. Load diff
+654
View File
@@ -0,0 +1,654 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2008-7-12 Bernard the first version
* 2010-06-09 Bernard fix the end stub of heap
* fix memory check in rt_realloc function
* 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca
* 2010-10-14 Bernard fix rt_realloc issue when realloc a NULL pointer.
* 2017-07-14 armink fix rt_realloc issue when new size is 0
* 2018-10-02 Bernard Add 64bit support
*/
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Adam Dunkels <adam@sics.se>
* Simon Goldschmidt
*
*/
#include <rthw.h>
#include <rtthread.h>
#if defined (RT_USING_SMALL_MEM)
#define DBG_TAG "kernel.mem"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
struct rt_small_mem_item
{
rt_ubase_t pool_ptr; /**< small memory object addr */
rt_size_t next; /**< next free item */
rt_size_t prev; /**< prev free item */
#ifdef RT_USING_MEMTRACE
#ifdef ARCH_CPU_64BIT
rt_uint8_t thread[8]; /**< thread name */
#else
rt_uint8_t thread[4]; /**< thread name */
#endif /* ARCH_CPU_64BIT */
#endif /* RT_USING_MEMTRACE */
};
/**
* Base structure of small memory object
*/
struct rt_small_mem
{
struct rt_memory parent; /**< inherit from rt_memory */
rt_uint8_t *heap_ptr; /**< pointer to the heap */
struct rt_small_mem_item *heap_end;
struct rt_small_mem_item *lfree;
rt_size_t mem_size_aligned; /**< aligned memory size */
};
#define MIN_SIZE (sizeof(rt_ubase_t) + sizeof(rt_size_t) + sizeof(rt_size_t))
#define MEM_MASK ((~(rt_size_t)0) - 1)
#define MEM_USED(_mem) ((((rt_base_t)(_mem)) & MEM_MASK) | 0x1)
#define MEM_FREED(_mem) ((((rt_base_t)(_mem)) & MEM_MASK) | 0x0)
#define MEM_ISUSED(_mem) \
(((rt_base_t)(((struct rt_small_mem_item *)(_mem))->pool_ptr)) & (~MEM_MASK))
#define MEM_POOL(_mem) \
((struct rt_small_mem *)(((rt_base_t)(((struct rt_small_mem_item *)(_mem))->pool_ptr)) & (MEM_MASK)))
#define MEM_SIZE(_heap, _mem) \
(((struct rt_small_mem_item *)(_mem))->next - ((rt_ubase_t)(_mem) - \
(rt_ubase_t)((_heap)->heap_ptr)) - RT_ALIGN(sizeof(struct rt_small_mem_item), RT_ALIGN_SIZE))
#define MIN_SIZE_ALIGNED RT_ALIGN(MIN_SIZE, RT_ALIGN_SIZE)
#define SIZEOF_STRUCT_MEM RT_ALIGN(sizeof(struct rt_small_mem_item), RT_ALIGN_SIZE)
#ifdef RT_USING_MEMTRACE
rt_inline void rt_smem_setname(struct rt_small_mem_item *mem, const char *name)
{
int index;
for (index = 0; index < sizeof(mem->thread); index ++)
{
if (name[index] == '\0') break;
mem->thread[index] = name[index];
}
for (; index < sizeof(mem->thread); index ++)
{
mem->thread[index] = ' ';
}
}
#endif /* RT_USING_MEMTRACE */
static void plug_holes(struct rt_small_mem *m, struct rt_small_mem_item *mem)
{
struct rt_small_mem_item *nmem;
struct rt_small_mem_item *pmem;
RT_ASSERT((rt_uint8_t *)mem >= m->heap_ptr);
RT_ASSERT((rt_uint8_t *)mem < (rt_uint8_t *)m->heap_end);
/* plug hole forward */
nmem = (struct rt_small_mem_item *)&m->heap_ptr[mem->next];
if (mem != nmem && !MEM_ISUSED(nmem) &&
(rt_uint8_t *)nmem != (rt_uint8_t *)m->heap_end)
{
/* if mem->next is unused and not end of m->heap_ptr,
* combine mem and mem->next
*/
if (m->lfree == nmem)
{
m->lfree = mem;
}
nmem->pool_ptr = 0;
mem->next = nmem->next;
((struct rt_small_mem_item *)&m->heap_ptr[nmem->next])->prev = (rt_uint8_t *)mem - m->heap_ptr;
}
/* plug hole backward */
pmem = (struct rt_small_mem_item *)&m->heap_ptr[mem->prev];
if (pmem != mem && !MEM_ISUSED(pmem))
{
/* if mem->prev is unused, combine mem and mem->prev */
if (m->lfree == mem)
{
m->lfree = pmem;
}
mem->pool_ptr = 0;
pmem->next = mem->next;
((struct rt_small_mem_item *)&m->heap_ptr[mem->next])->prev = (rt_uint8_t *)pmem - m->heap_ptr;
}
}
/**
* @brief This function will initialize small memory management algorithm.
*
* @param name is the name of the small memory management object.
*
* @param begin_addr the beginning address of memory.
*
* @param size is the size of the memory.
*
* @return Return a pointer to the memory object. When the return value is RT_NULL, it means the init failed.
*/
rt_smem_t rt_smem_init(const char *name,
void *begin_addr,
rt_size_t size)
{
struct rt_small_mem_item *mem;
struct rt_small_mem *small_mem;
rt_ubase_t start_addr, begin_align, end_align, mem_size;
small_mem = (struct rt_small_mem *)RT_ALIGN((rt_ubase_t)begin_addr, RT_ALIGN_SIZE);
start_addr = (rt_ubase_t)small_mem + sizeof(*small_mem);
begin_align = RT_ALIGN((rt_ubase_t)start_addr, RT_ALIGN_SIZE);
end_align = RT_ALIGN_DOWN((rt_ubase_t)begin_addr + size, RT_ALIGN_SIZE);
/* alignment addr */
if ((end_align > (2 * SIZEOF_STRUCT_MEM)) &&
((end_align - 2 * SIZEOF_STRUCT_MEM) >= start_addr))
{
/* calculate the aligned memory size */
mem_size = end_align - begin_align - 2 * SIZEOF_STRUCT_MEM;
}
else
{
rt_kprintf("mem init, error begin address 0x%x, and end address 0x%x\n",
(rt_ubase_t)begin_addr, (rt_ubase_t)begin_addr + size);
return RT_NULL;
}
rt_memset(small_mem, 0, sizeof(*small_mem));
/* initialize small memory object */
rt_object_init(&(small_mem->parent.parent), RT_Object_Class_Memory, name);
small_mem->parent.algorithm = "small";
small_mem->parent.address = begin_align;
small_mem->parent.total = mem_size;
small_mem->mem_size_aligned = mem_size;
/* point to begin address of heap */
small_mem->heap_ptr = (rt_uint8_t *)begin_align;
LOG_D("mem init, heap begin address 0x%x, size %d",
(rt_ubase_t)small_mem->heap_ptr, small_mem->mem_size_aligned);
/* initialize the start of the heap */
mem = (struct rt_small_mem_item *)small_mem->heap_ptr;
mem->pool_ptr = MEM_FREED(small_mem);
mem->next = small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM;
mem->prev = 0;
#ifdef RT_USING_MEMTRACE
rt_smem_setname(mem, "INIT");
#endif /* RT_USING_MEMTRACE */
/* initialize the end of the heap */
small_mem->heap_end = (struct rt_small_mem_item *)&small_mem->heap_ptr[mem->next];
small_mem->heap_end->pool_ptr = MEM_USED(small_mem);
small_mem->heap_end->next = small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM;
small_mem->heap_end->prev = small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM;
#ifdef RT_USING_MEMTRACE
rt_smem_setname(small_mem->heap_end, "INIT");
#endif /* RT_USING_MEMTRACE */
/* initialize the lowest-free pointer to the start of the heap */
small_mem->lfree = (struct rt_small_mem_item *)small_mem->heap_ptr;
return &small_mem->parent;
}
RTM_EXPORT(rt_smem_init);
/**
* @brief This function will remove a small mem from the system.
*
* @param m the small memory management object.
*
* @return RT_EOK
*/
rt_err_t rt_smem_detach(rt_smem_t m)
{
RT_ASSERT(m != RT_NULL);
RT_ASSERT(rt_object_get_type(&m->parent) == RT_Object_Class_Memory);
RT_ASSERT(rt_object_is_systemobject(&m->parent));
rt_object_detach(&(m->parent));
return RT_EOK;
}
RTM_EXPORT(rt_smem_detach);
/**
* @addtogroup MM
*/
/**@{*/
/**
* @brief Allocate a block of memory with a minimum of 'size' bytes.
*
* @param m the small memory management object.
*
* @param size is the minimum size of the requested block in bytes.
*
* @return the pointer to allocated memory or NULL if no free memory was found.
*/
void *rt_smem_alloc(rt_smem_t m, rt_size_t size)
{
rt_size_t ptr, ptr2;
struct rt_small_mem_item *mem, *mem2;
struct rt_small_mem *small_mem;
if (size == 0)
return RT_NULL;
RT_ASSERT(m != RT_NULL);
RT_ASSERT(rt_object_get_type(&m->parent) == RT_Object_Class_Memory);
RT_ASSERT(rt_object_is_systemobject(&m->parent));
small_mem = (struct rt_small_mem *)m;
/* alignment size */
size = RT_ALIGN(size, RT_ALIGN_SIZE);
/* every data block must be at least MIN_SIZE_ALIGNED long */
if (size < MIN_SIZE_ALIGNED)
size = MIN_SIZE_ALIGNED;
if (size > small_mem->mem_size_aligned)
{
LOG_D("no memory");
return RT_NULL;
}
for (ptr = (rt_uint8_t *)small_mem->lfree - small_mem->heap_ptr;
ptr <= small_mem->mem_size_aligned - size;
ptr = ((struct rt_small_mem_item *)&small_mem->heap_ptr[ptr])->next)
{
mem = (struct rt_small_mem_item *)&small_mem->heap_ptr[ptr];
if ((!MEM_ISUSED(mem)) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size)
{
/* mem is not used and at least perfect fit is possible:
* mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */
if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >=
(size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED))
{
/* (in addition to the above, we test if another struct rt_small_mem_item (SIZEOF_STRUCT_MEM) containing
* at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem')
* -> split large block, create empty remainder,
* remainder must be large enough to contain MIN_SIZE_ALIGNED data: if
* mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size,
* struct rt_small_mem_item would fit in but no data between mem2 and mem2->next
* @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
* region that couldn't hold data, but when mem->next gets freed,
* the 2 regions would be combined, resulting in more free memory
*/
ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
/* create mem2 struct */
mem2 = (struct rt_small_mem_item *)&small_mem->heap_ptr[ptr2];
mem2->pool_ptr = MEM_FREED(small_mem);
mem2->next = mem->next;
mem2->prev = ptr;
#ifdef RT_USING_MEMTRACE
rt_smem_setname(mem2, " ");
#endif /* RT_USING_MEMTRACE */
/* and insert it between mem and mem->next */
mem->next = ptr2;
if (mem2->next != small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM)
{
((struct rt_small_mem_item *)&small_mem->heap_ptr[mem2->next])->prev = ptr2;
}
small_mem->parent.used += (size + SIZEOF_STRUCT_MEM);
if (small_mem->parent.max < small_mem->parent.used)
small_mem->parent.max = small_mem->parent.used;
}
else
{
/* (a mem2 struct does no fit into the user data space of mem and mem->next will always
* be used at this point: if not we have 2 unused structs in a row, plug_holes should have
* take care of this).
* -> near fit or excact fit: do not split, no mem2 creation
* also can't move mem->next directly behind mem, since mem->next
* will always be used at this point!
*/
small_mem->parent.used += mem->next - ((rt_uint8_t *)mem - small_mem->heap_ptr);
if (small_mem->parent.max < small_mem->parent.used)
small_mem->parent.max = small_mem->parent.used;
}
/* set small memory object */
mem->pool_ptr = MEM_USED(small_mem);
#ifdef RT_USING_MEMTRACE
if (rt_thread_self())
rt_smem_setname(mem, rt_thread_self()->parent.name);
else
rt_smem_setname(mem, "NONE");
#endif /* RT_USING_MEMTRACE */
if (mem == small_mem->lfree)
{
/* Find next free block after mem and update lowest free pointer */
while (MEM_ISUSED(small_mem->lfree) && small_mem->lfree != small_mem->heap_end)
small_mem->lfree = (struct rt_small_mem_item *)&small_mem->heap_ptr[small_mem->lfree->next];
RT_ASSERT(((small_mem->lfree == small_mem->heap_end) || (!MEM_ISUSED(small_mem->lfree))));
}
RT_ASSERT((rt_ubase_t)mem + SIZEOF_STRUCT_MEM + size <= (rt_ubase_t)small_mem->heap_end);
RT_ASSERT((rt_ubase_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM) % RT_ALIGN_SIZE == 0);
RT_ASSERT((((rt_ubase_t)mem) & (RT_ALIGN_SIZE - 1)) == 0);
LOG_D("allocate memory at 0x%x, size: %d",
(rt_ubase_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM),
(rt_ubase_t)(mem->next - ((rt_uint8_t *)mem - small_mem->heap_ptr)));
/* return the memory data except mem struct */
return (rt_uint8_t *)mem + SIZEOF_STRUCT_MEM;
}
}
return RT_NULL;
}
RTM_EXPORT(rt_smem_alloc);
/**
* @brief This function will change the size of previously allocated memory block.
*
* @param m the small memory management object.
*
* @param rmem is the pointer to memory allocated by rt_mem_alloc.
*
* @param newsize is the required new size.
*
* @return the changed memory block address.
*/
void *rt_smem_realloc(rt_smem_t m, void *rmem, rt_size_t newsize)
{
rt_size_t size;
rt_size_t ptr, ptr2;
struct rt_small_mem_item *mem, *mem2;
struct rt_small_mem *small_mem;
void *nmem;
RT_ASSERT(m != RT_NULL);
RT_ASSERT(rt_object_get_type(&m->parent) == RT_Object_Class_Memory);
RT_ASSERT(rt_object_is_systemobject(&m->parent));
small_mem = (struct rt_small_mem *)m;
/* alignment size */
newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE);
if (newsize > small_mem->mem_size_aligned)
{
LOG_D("realloc: out of memory");
return RT_NULL;
}
else if (newsize == 0)
{
rt_smem_free(rmem);
return RT_NULL;
}
/* allocate a new memory block */
if (rmem == RT_NULL)
return rt_smem_alloc(&small_mem->parent, newsize);
RT_ASSERT((((rt_ubase_t)rmem) & (RT_ALIGN_SIZE - 1)) == 0);
RT_ASSERT((rt_uint8_t *)rmem >= (rt_uint8_t *)small_mem->heap_ptr);
RT_ASSERT((rt_uint8_t *)rmem < (rt_uint8_t *)small_mem->heap_end);
mem = (struct rt_small_mem_item *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM);
/* current memory block size */
ptr = (rt_uint8_t *)mem - small_mem->heap_ptr;
size = mem->next - ptr - SIZEOF_STRUCT_MEM;
if (size == newsize)
{
/* the size is the same as */
return rmem;
}
if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size)
{
/* split memory block */
small_mem->parent.used -= (size - newsize);
ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
mem2 = (struct rt_small_mem_item *)&small_mem->heap_ptr[ptr2];
mem2->pool_ptr = MEM_FREED(small_mem);
mem2->next = mem->next;
mem2->prev = ptr;
#ifdef RT_USING_MEMTRACE
rt_smem_setname(mem2, " ");
#endif /* RT_USING_MEMTRACE */
mem->next = ptr2;
if (mem2->next != small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM)
{
((struct rt_small_mem_item *)&small_mem->heap_ptr[mem2->next])->prev = ptr2;
}
if (mem2 < small_mem->lfree)
{
/* the splited struct is now the lowest */
small_mem->lfree = mem2;
}
plug_holes(small_mem, mem2);
return rmem;
}
/* expand memory */
nmem = rt_smem_alloc(&small_mem->parent, newsize);
if (nmem != RT_NULL) /* check memory */
{
rt_memcpy(nmem, rmem, size < newsize ? size : newsize);
rt_smem_free(rmem);
}
return nmem;
}
RTM_EXPORT(rt_smem_realloc);
/**
* @brief This function will release the previously allocated memory block by
* rt_mem_alloc. The released memory block is taken back to system heap.
*
* @param rmem the address of memory which will be released.
*/
void rt_smem_free(void *rmem)
{
struct rt_small_mem_item *mem;
struct rt_small_mem *small_mem;
if (rmem == RT_NULL)
return;
RT_ASSERT((((rt_ubase_t)rmem) & (RT_ALIGN_SIZE - 1)) == 0);
/* Get the corresponding struct rt_small_mem_item ... */
mem = (struct rt_small_mem_item *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM);
/* ... which has to be in a used state ... */
small_mem = MEM_POOL(mem);
RT_ASSERT(small_mem != RT_NULL);
RT_ASSERT(MEM_ISUSED(mem));
RT_ASSERT(rt_object_get_type(&small_mem->parent.parent) == RT_Object_Class_Memory);
RT_ASSERT(rt_object_is_systemobject(&small_mem->parent.parent));
RT_ASSERT((rt_uint8_t *)rmem >= (rt_uint8_t *)small_mem->heap_ptr &&
(rt_uint8_t *)rmem < (rt_uint8_t *)small_mem->heap_end);
RT_ASSERT(MEM_POOL(&small_mem->heap_ptr[mem->next]) == small_mem);
LOG_D("release memory 0x%x, size: %d",
(rt_ubase_t)rmem,
(rt_ubase_t)(mem->next - ((rt_uint8_t *)mem - small_mem->heap_ptr)));
/* ... and is now unused. */
mem->pool_ptr = MEM_FREED(small_mem);
#ifdef RT_USING_MEMTRACE
rt_smem_setname(mem, " ");
#endif /* RT_USING_MEMTRACE */
if (mem < small_mem->lfree)
{
/* the newly freed struct is now the lowest */
small_mem->lfree = mem;
}
small_mem->parent.used -= (mem->next - ((rt_uint8_t *)mem - small_mem->heap_ptr));
/* finally, see if prev or next are free also */
plug_holes(small_mem, mem);
}
RTM_EXPORT(rt_smem_free);
#ifdef RT_USING_FINSH
#include <finsh.h>
#ifdef RT_USING_MEMTRACE
static int memcheck(int argc, char *argv[])
{
int position;
rt_base_t level;
struct rt_small_mem_item *mem;
struct rt_small_mem *m;
struct rt_object_information *information;
struct rt_list_node *node;
struct rt_object *object;
char *name;
name = argc > 1 ? argv[1] : RT_NULL;
level = rt_hw_interrupt_disable();
/* get mem object */
information = rt_object_get_information(RT_Object_Class_Memory);
for (node = information->object_list.next;
node != &(information->object_list);
node = node->next)
{
object = rt_list_entry(node, struct rt_object, list);
/* find the specified object */
if (name != RT_NULL && rt_strncmp(name, object->name, RT_NAME_MAX) != 0)
continue;
/* mem object */
m = (struct rt_small_mem *)object;
/* check mem */
for (mem = (struct rt_small_mem_item *)m->heap_ptr; mem != m->heap_end; mem = (struct rt_small_mem_item *)&m->heap_ptr[mem->next])
{
position = (rt_ubase_t)mem - (rt_ubase_t)m->heap_ptr;
if (position < 0) goto __exit;
if (position > (int)m->mem_size_aligned) goto __exit;
if (MEM_POOL(mem) != m) goto __exit;
}
}
rt_hw_interrupt_enable(level);
return 0;
__exit:
rt_kprintf("Memory block wrong:\n");
rt_kprintf(" name: %s\n", m->parent.parent.name);
rt_kprintf("address: 0x%08x\n", mem);
rt_kprintf(" pool: 0x%04x\n", mem->pool_ptr);
rt_kprintf(" size: %d\n", mem->next - position - SIZEOF_STRUCT_MEM);
rt_hw_interrupt_enable(level);
return 0;
}
MSH_CMD_EXPORT(memcheck, check memory data);
static int memtrace(int argc, char **argv)
{
struct rt_small_mem_item *mem;
struct rt_small_mem *m;
struct rt_object_information *information;
struct rt_list_node *node;
struct rt_object *object;
char *name;
name = argc > 1 ? argv[1] : RT_NULL;
/* get mem object */
information = rt_object_get_information(RT_Object_Class_Memory);
for (node = information->object_list.next;
node != &(information->object_list);
node = node->next)
{
object = rt_list_entry(node, struct rt_object, list);
/* find the specified object */
if (name != RT_NULL && rt_strncmp(name, object->name, RT_NAME_MAX) != 0)
continue;
/* mem object */
m = (struct rt_small_mem *)object;
/* show memory information */
rt_kprintf("\nmemory heap address:\n");
rt_kprintf("name : %s\n", m->parent.parent.name);
rt_kprintf("total : 0x%d\n", m->parent.total);
rt_kprintf("used : 0x%d\n", m->parent.used);
rt_kprintf("max_used: 0x%d\n", m->parent.max);
rt_kprintf("heap_ptr: 0x%08x\n", m->heap_ptr);
rt_kprintf("lfree : 0x%08x\n", m->lfree);
rt_kprintf("heap_end: 0x%08x\n", m->heap_end);
rt_kprintf("\n--memory item information --\n");
for (mem = (struct rt_small_mem_item *)m->heap_ptr; mem != m->heap_end; mem = (struct rt_small_mem_item *)&m->heap_ptr[mem->next])
{
int size = MEM_SIZE(m, mem);
rt_kprintf("[0x%08x - ", mem);
if (size < 1024)
rt_kprintf("%5d", size);
else if (size < 1024 * 1024)
rt_kprintf("%4dK", size / 1024);
else
rt_kprintf("%4dM", size / (1024 * 1024));
rt_kprintf("] %c%c%c%c", mem->thread[0], mem->thread[1], mem->thread[2], mem->thread[3]);
if (MEM_POOL(mem) != m)
rt_kprintf(": ***\n");
else
rt_kprintf("\n");
}
}
return 0;
}
MSH_CMD_EXPORT(memtrace, dump memory trace information);
#endif /* RT_USING_MEMTRACE */
#endif /* RT_USING_FINSH */
#endif /* defined (RT_USING_SMALL_MEM) */
/**@}*/
+998
View File
@@ -0,0 +1,998 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* File : memheap.c
*
* Change Logs:
* Date Author Notes
* 2012-04-10 Bernard first implementation
* 2012-10-16 Bernard add the mutex lock for heap object.
* 2012-12-29 Bernard memheap can be used as system heap.
* change mutex lock to semaphore lock.
* 2013-04-10 Bernard add rt_memheap_realloc function.
* 2013-05-24 Bernard fix the rt_memheap_realloc issue.
* 2013-07-11 Grissiom fix the memory block splitting issue.
* 2013-07-15 Grissiom optimize rt_memheap_realloc
* 2021-06-03 Flybreak Fix the crash problem after opening Oz optimization on ac6.
* 2023-03-01 Bernard Fix the alignment issue for minimal size
*/
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_MEMHEAP
#define DBG_TAG "kernel.memheap"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
/* dynamic pool magic and mask */
#define RT_MEMHEAP_MAGIC 0x1ea01ea0
#define RT_MEMHEAP_MASK 0xFFFFFFFE
#define RT_MEMHEAP_USED 0x01
#define RT_MEMHEAP_FREED 0x00
#define RT_MEMHEAP_IS_USED(i) ((i)->magic & RT_MEMHEAP_USED)
#define RT_MEMHEAP_MINIALLOC RT_ALIGN(12, RT_ALIGN_SIZE)
#define RT_MEMHEAP_SIZE RT_ALIGN(sizeof(struct rt_memheap_item), RT_ALIGN_SIZE)
#define MEMITEM_SIZE(item) ((rt_ubase_t)item->next - (rt_ubase_t)item - RT_MEMHEAP_SIZE)
#define MEMITEM(ptr) (struct rt_memheap_item*)((rt_uint8_t*)ptr - RT_MEMHEAP_SIZE)
static void _remove_next_ptr(volatile struct rt_memheap_item *next_ptr)
{
/* Fix the crash problem after opening Oz optimization on ac6 */
/* Fix IAR compiler warning */
next_ptr->next_free->prev_free = next_ptr->prev_free;
next_ptr->prev_free->next_free = next_ptr->next_free;
next_ptr->next->prev = next_ptr->prev;
next_ptr->prev->next = next_ptr->next;
}
/**
* @brief This function initializes a piece of memory called memheap.
*
* @note The initialized memory pool will be:
* +-----------------------------------+--------------------------+
* | whole freed memory block | Used Memory Block Tailer |
* +-----------------------------------+--------------------------+
*
* block_list --> whole freed memory block
*
* The length of Used Memory Block Tailer is 0,
* which is prevents block merging across list
*
* @param memheap is a pointer of the memheap object.
*
* @param name is the name of the memheap.
*
* @param start_addr is the start address of the memheap.
*
* @param size is the size of the memheap.
*
* @return RT_EOK
*/
rt_err_t rt_memheap_init(struct rt_memheap *memheap,
const char *name,
void *start_addr,
rt_size_t size)
{
struct rt_memheap_item *item;
RT_ASSERT(memheap != RT_NULL);
/* initialize pool object */
rt_object_init(&(memheap->parent), RT_Object_Class_MemHeap, name);
memheap->start_addr = start_addr;
memheap->pool_size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
memheap->available_size = memheap->pool_size - (2 * RT_MEMHEAP_SIZE);
memheap->max_used_size = memheap->pool_size - memheap->available_size;
/* initialize the free list header */
item = &(memheap->free_header);
item->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED);
item->pool_ptr = memheap;
item->next = RT_NULL;
item->prev = RT_NULL;
item->next_free = item;
item->prev_free = item;
/* set the free list to free list header */
memheap->free_list = item;
/* initialize the first big memory block */
item = (struct rt_memheap_item *)start_addr;
item->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED);
item->pool_ptr = memheap;
item->next = RT_NULL;
item->prev = RT_NULL;
item->next_free = item;
item->prev_free = item;
#ifdef RT_USING_MEMTRACE
rt_memset(item->owner_thread_name, ' ', sizeof(item->owner_thread_name));
#endif /* RT_USING_MEMTRACE */
item->next = (struct rt_memheap_item *)
((rt_uint8_t *)item + memheap->available_size + RT_MEMHEAP_SIZE);
item->prev = item->next;
/* block list header */
memheap->block_list = item;
/* place the big memory block to free list */
item->next_free = memheap->free_list->next_free;
item->prev_free = memheap->free_list;
memheap->free_list->next_free->prev_free = item;
memheap->free_list->next_free = item;
/* move to the end of memory pool to build a small tailer block,
* which prevents block merging
*/
item = item->next;
/* it's a used memory block */
item->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED);
item->pool_ptr = memheap;
item->next = (struct rt_memheap_item *)start_addr;
item->prev = (struct rt_memheap_item *)start_addr;
/* not in free list */
item->next_free = item->prev_free = RT_NULL;
/* initialize semaphore lock */
rt_sem_init(&(memheap->lock), name, 1, RT_IPC_FLAG_PRIO);
memheap->locked = RT_FALSE;
LOG_D("memory heap: start addr 0x%08x, size %d, free list header 0x%08x",
start_addr, size, &(memheap->free_header));
return RT_EOK;
}
RTM_EXPORT(rt_memheap_init);
/**
* @brief This function will remove a memheap from the system.
*
* @param heap is a pointer of memheap object.
*
* @return RT_EOK
*/
rt_err_t rt_memheap_detach(struct rt_memheap *heap)
{
RT_ASSERT(heap);
RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap);
RT_ASSERT(rt_object_is_systemobject(&heap->parent));
rt_sem_detach(&heap->lock);
rt_object_detach(&(heap->parent));
/* Return a successful completion. */
return RT_EOK;
}
RTM_EXPORT(rt_memheap_detach);
/**
* @brief Allocate a block of memory with a minimum of 'size' bytes on memheap.
*
* @param heap is a pointer for memheap object.
*
* @param size is the minimum size of the requested block in bytes.
*
* @return the pointer to allocated memory or NULL if no free memory was found.
*/
void *rt_memheap_alloc(struct rt_memheap *heap, rt_size_t size)
{
rt_err_t result;
rt_size_t free_size;
struct rt_memheap_item *header_ptr;
RT_ASSERT(heap != RT_NULL);
RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap);
/* align allocated size */
size = RT_ALIGN(size, RT_ALIGN_SIZE);
if (size < RT_MEMHEAP_MINIALLOC)
size = RT_MEMHEAP_MINIALLOC;
LOG_D("allocate %d on heap:%8.*s",
size, RT_NAME_MAX, heap->parent.name);
if (size < heap->available_size)
{
/* search on free list */
free_size = 0;
/* lock memheap */
if (heap->locked == RT_FALSE)
{
result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(result);
return RT_NULL;
}
}
/* get the first free memory block */
header_ptr = heap->free_list->next_free;
while (header_ptr != heap->free_list && free_size < size)
{
/* get current freed memory block size */
free_size = MEMITEM_SIZE(header_ptr);
if (free_size < size)
{
/* move to next free memory block */
header_ptr = header_ptr->next_free;
}
}
/* determine if the memory is available. */
if (free_size >= size)
{
/* a block that satisfies the request has been found. */
/* determine if the block needs to be split. */
if (free_size >= (size + RT_MEMHEAP_SIZE + RT_MEMHEAP_MINIALLOC))
{
struct rt_memheap_item *new_ptr;
/* split the block. */
new_ptr = (struct rt_memheap_item *)
(((rt_uint8_t *)header_ptr) + size + RT_MEMHEAP_SIZE);
LOG_D("split: block[0x%08x] nextm[0x%08x] prevm[0x%08x] to new[0x%08x]",
header_ptr,
header_ptr->next,
header_ptr->prev,
new_ptr);
/* mark the new block as a memory block and freed. */
new_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED);
/* put the pool pointer into the new block. */
new_ptr->pool_ptr = heap;
#ifdef RT_USING_MEMTRACE
rt_memset(new_ptr->owner_thread_name, ' ', sizeof(new_ptr->owner_thread_name));
#endif /* RT_USING_MEMTRACE */
/* break down the block list */
new_ptr->prev = header_ptr;
new_ptr->next = header_ptr->next;
header_ptr->next->prev = new_ptr;
header_ptr->next = new_ptr;
/* remove header ptr from free list */
header_ptr->next_free->prev_free = header_ptr->prev_free;
header_ptr->prev_free->next_free = header_ptr->next_free;
header_ptr->next_free = RT_NULL;
header_ptr->prev_free = RT_NULL;
/* insert new_ptr to free list */
new_ptr->next_free = heap->free_list->next_free;
new_ptr->prev_free = heap->free_list;
heap->free_list->next_free->prev_free = new_ptr;
heap->free_list->next_free = new_ptr;
LOG_D("new ptr: next_free 0x%08x, prev_free 0x%08x",
new_ptr->next_free,
new_ptr->prev_free);
/* decrement the available byte count. */
heap->available_size = heap->available_size -
size -
RT_MEMHEAP_SIZE;
if (heap->pool_size - heap->available_size > heap->max_used_size)
heap->max_used_size = heap->pool_size - heap->available_size;
}
else
{
/* decrement the entire free size from the available bytes count. */
heap->available_size = heap->available_size - free_size;
if (heap->pool_size - heap->available_size > heap->max_used_size)
heap->max_used_size = heap->pool_size - heap->available_size;
/* remove header_ptr from free list */
LOG_D("one block: block[0x%08x], next_free 0x%08x, prev_free 0x%08x",
header_ptr,
header_ptr->next_free,
header_ptr->prev_free);
header_ptr->next_free->prev_free = header_ptr->prev_free;
header_ptr->prev_free->next_free = header_ptr->next_free;
header_ptr->next_free = RT_NULL;
header_ptr->prev_free = RT_NULL;
}
/* Mark the allocated block as not available. */
header_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED);
#ifdef RT_USING_MEMTRACE
if (rt_thread_self())
rt_memcpy(header_ptr->owner_thread_name, rt_thread_self()->parent.name, sizeof(header_ptr->owner_thread_name));
else
rt_memcpy(header_ptr->owner_thread_name, "NONE", sizeof(header_ptr->owner_thread_name));
#endif /* RT_USING_MEMTRACE */
if (heap->locked == RT_FALSE)
{
/* release lock */
rt_sem_release(&(heap->lock));
}
/* Return a memory address to the caller. */
LOG_D("alloc mem: memory[0x%08x], heap[0x%08x], size: %d",
(void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE),
header_ptr,
size);
return (void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE);
}
if (heap->locked == RT_FALSE)
{
/* release lock */
rt_sem_release(&(heap->lock));
}
}
LOG_D("allocate memory: failed");
/* Return the completion status. */
return RT_NULL;
}
RTM_EXPORT(rt_memheap_alloc);
/**
* @brief This function will change the size of previously allocated memory block.
*
* @param heap is a pointer to the memheap object, which will reallocate
* memory from the block
*
* @param ptr is a pointer to start address of memory.
*
* @param newsize is the required new size.
*
* @return the changed memory block address.
*/
void *rt_memheap_realloc(struct rt_memheap *heap, void *ptr, rt_size_t newsize)
{
rt_err_t result;
rt_size_t oldsize;
struct rt_memheap_item *header_ptr;
struct rt_memheap_item *new_ptr;
RT_ASSERT(heap);
RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap);
if (newsize == 0)
{
rt_memheap_free(ptr);
return RT_NULL;
}
/* align allocated size */
newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE);
if (newsize < RT_MEMHEAP_MINIALLOC)
newsize = RT_MEMHEAP_MINIALLOC;
if (ptr == RT_NULL)
{
return rt_memheap_alloc(heap, newsize);
}
/* get memory block header and get the size of memory block */
header_ptr = (struct rt_memheap_item *)
((rt_uint8_t *)ptr - RT_MEMHEAP_SIZE);
oldsize = MEMITEM_SIZE(header_ptr);
/* re-allocate memory */
if (newsize > oldsize)
{
void *new_ptr;
volatile struct rt_memheap_item *next_ptr;
if (heap->locked == RT_FALSE)
{
/* lock memheap */
result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(result);
return RT_NULL;
}
}
next_ptr = header_ptr->next;
/* header_ptr should not be the tail */
RT_ASSERT(next_ptr > header_ptr);
/* check whether the following free space is enough to expand */
if (!RT_MEMHEAP_IS_USED(next_ptr))
{
rt_int32_t nextsize;
nextsize = MEMITEM_SIZE(next_ptr);
RT_ASSERT(next_ptr > 0);
/* Here is the ASCII art of the situation that we can make use of
* the next free node without alloc/memcpy, |*| is the control
* block:
*
* oldsize free node
* |*|-----------|*|----------------------|*|
* newsize >= minialloc
* |*|----------------|*|-----------------|*|
*/
if (nextsize + oldsize > newsize + RT_MEMHEAP_MINIALLOC)
{
/* decrement the entire free size from the available bytes count. */
heap->available_size = heap->available_size - (newsize - oldsize);
if (heap->pool_size - heap->available_size > heap->max_used_size)
heap->max_used_size = heap->pool_size - heap->available_size;
/* remove next_ptr from free list */
LOG_D("remove block: block[0x%08x], next_free 0x%08x, prev_free 0x%08x",
next_ptr,
next_ptr->next_free,
next_ptr->prev_free);
_remove_next_ptr(next_ptr);
/* build a new one on the right place */
next_ptr = (struct rt_memheap_item *)((char *)ptr + newsize);
LOG_D("new free block: block[0x%08x] nextm[0x%08x] prevm[0x%08x]",
next_ptr,
next_ptr->next,
next_ptr->prev);
/* mark the new block as a memory block and freed. */
next_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED);
/* put the pool pointer into the new block. */
next_ptr->pool_ptr = heap;
#ifdef RT_USING_MEMTRACE
rt_memset((void *)next_ptr->owner_thread_name, ' ', sizeof(next_ptr->owner_thread_name));
#endif /* RT_USING_MEMTRACE */
next_ptr->prev = header_ptr;
next_ptr->next = header_ptr->next;
header_ptr->next->prev = (struct rt_memheap_item *)next_ptr;
header_ptr->next = (struct rt_memheap_item *)next_ptr;
/* insert next_ptr to free list */
next_ptr->next_free = heap->free_list->next_free;
next_ptr->prev_free = heap->free_list;
heap->free_list->next_free->prev_free = (struct rt_memheap_item *)next_ptr;
heap->free_list->next_free = (struct rt_memheap_item *)next_ptr;
LOG_D("new ptr: next_free 0x%08x, prev_free 0x%08x",
next_ptr->next_free,
next_ptr->prev_free);
if (heap->locked == RT_FALSE)
{
/* release lock */
rt_sem_release(&(heap->lock));
}
return ptr;
}
}
if (heap->locked == RT_FALSE)
{
/* release lock */
rt_sem_release(&(heap->lock));
}
/* re-allocate a memory block */
new_ptr = (void *)rt_memheap_alloc(heap, newsize);
if (new_ptr != RT_NULL)
{
rt_memcpy(new_ptr, ptr, oldsize < newsize ? oldsize : newsize);
rt_memheap_free(ptr);
}
return new_ptr;
}
/* don't split when there is less than one node space left */
if (newsize + RT_MEMHEAP_SIZE + RT_MEMHEAP_MINIALLOC >= oldsize)
return ptr;
if (heap->locked == RT_FALSE)
{
/* lock memheap */
result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(result);
return RT_NULL;
}
}
/* split the block. */
new_ptr = (struct rt_memheap_item *)
(((rt_uint8_t *)header_ptr) + newsize + RT_MEMHEAP_SIZE);
LOG_D("split: block[0x%08x] nextm[0x%08x] prevm[0x%08x] to new[0x%08x]",
header_ptr,
header_ptr->next,
header_ptr->prev,
new_ptr);
/* mark the new block as a memory block and freed. */
new_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED);
/* put the pool pointer into the new block. */
new_ptr->pool_ptr = heap;
#ifdef RT_USING_MEMTRACE
rt_memset(new_ptr->owner_thread_name, ' ', sizeof(new_ptr->owner_thread_name));
#endif /* RT_USING_MEMTRACE */
/* break down the block list */
new_ptr->prev = header_ptr;
new_ptr->next = header_ptr->next;
header_ptr->next->prev = new_ptr;
header_ptr->next = new_ptr;
/* determine if the block can be merged with the next neighbor. */
if (!RT_MEMHEAP_IS_USED(new_ptr->next))
{
struct rt_memheap_item *free_ptr;
/* merge block with next neighbor. */
free_ptr = new_ptr->next;
heap->available_size = heap->available_size - MEMITEM_SIZE(free_ptr);
LOG_D("merge: right node 0x%08x, next_free 0x%08x, prev_free 0x%08x",
header_ptr, header_ptr->next_free, header_ptr->prev_free);
free_ptr->next->prev = new_ptr;
new_ptr->next = free_ptr->next;
/* remove free ptr from free list */
free_ptr->next_free->prev_free = free_ptr->prev_free;
free_ptr->prev_free->next_free = free_ptr->next_free;
}
/* insert the split block to free list */
new_ptr->next_free = heap->free_list->next_free;
new_ptr->prev_free = heap->free_list;
heap->free_list->next_free->prev_free = new_ptr;
heap->free_list->next_free = new_ptr;
LOG_D("new free ptr: next_free 0x%08x, prev_free 0x%08x",
new_ptr->next_free,
new_ptr->prev_free);
/* increment the available byte count. */
heap->available_size = heap->available_size + MEMITEM_SIZE(new_ptr);
if (heap->locked == RT_FALSE)
{
/* release lock */
rt_sem_release(&(heap->lock));
}
/* return the old memory block */
return ptr;
}
RTM_EXPORT(rt_memheap_realloc);
/**
* @brief This function will release the allocated memory block by
* rt_malloc. The released memory block is taken back to system heap.
*
* @param ptr the address of memory which will be released.
*/
void rt_memheap_free(void *ptr)
{
rt_err_t result;
struct rt_memheap *heap;
struct rt_memheap_item *header_ptr, *new_ptr;
rt_bool_t insert_header;
/* NULL check */
if (ptr == RT_NULL) return;
/* set initial status as OK */
insert_header = RT_TRUE;
new_ptr = RT_NULL;
header_ptr = (struct rt_memheap_item *)
((rt_uint8_t *)ptr - RT_MEMHEAP_SIZE);
LOG_D("free memory: memory[0x%08x], block[0x%08x]",
ptr, header_ptr);
/* check magic */
if (header_ptr->magic != (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED) ||
(header_ptr->next->magic & RT_MEMHEAP_MASK) != RT_MEMHEAP_MAGIC)
{
LOG_D("bad magic:0x%08x @ memheap",
header_ptr->magic);
RT_ASSERT(header_ptr->magic == (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED));
/* check whether this block of memory has been over-written. */
RT_ASSERT((header_ptr->next->magic & RT_MEMHEAP_MASK) == RT_MEMHEAP_MAGIC);
}
/* get pool ptr */
heap = header_ptr->pool_ptr;
RT_ASSERT(heap);
RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap);
if (heap->locked == RT_FALSE)
{
/* lock memheap */
result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(result);
return ;
}
}
/* Mark the memory as available. */
header_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED);
/* Adjust the available number of bytes. */
heap->available_size += MEMITEM_SIZE(header_ptr);
/* Determine if the block can be merged with the previous neighbor. */
if (!RT_MEMHEAP_IS_USED(header_ptr->prev))
{
LOG_D("merge: left node 0x%08x",
header_ptr->prev);
/* adjust the available number of bytes. */
heap->available_size += RT_MEMHEAP_SIZE;
/* yes, merge block with previous neighbor. */
(header_ptr->prev)->next = header_ptr->next;
(header_ptr->next)->prev = header_ptr->prev;
/* move header pointer to previous. */
header_ptr = header_ptr->prev;
/* don't insert header to free list */
insert_header = RT_FALSE;
}
/* determine if the block can be merged with the next neighbor. */
if (!RT_MEMHEAP_IS_USED(header_ptr->next))
{
/* adjust the available number of bytes. */
heap->available_size += RT_MEMHEAP_SIZE;
/* merge block with next neighbor. */
new_ptr = header_ptr->next;
LOG_D("merge: right node 0x%08x, next_free 0x%08x, prev_free 0x%08x",
new_ptr, new_ptr->next_free, new_ptr->prev_free);
new_ptr->next->prev = header_ptr;
header_ptr->next = new_ptr->next;
/* remove new ptr from free list */
new_ptr->next_free->prev_free = new_ptr->prev_free;
new_ptr->prev_free->next_free = new_ptr->next_free;
}
if (insert_header)
{
struct rt_memheap_item *n = heap->free_list->next_free;;
#if defined(RT_MEMHEAP_BEST_MODE)
rt_size_t blk_size = MEMITEM_SIZE(header_ptr);
for (;n != heap->free_list; n = n->next_free)
{
rt_size_t m = MEMITEM_SIZE(n);
if (blk_size <= m)
{
break;
}
}
#endif
/* no left merge, insert to free list */
header_ptr->next_free = n;
header_ptr->prev_free = n->prev_free;
n->prev_free->next_free = header_ptr;
n->prev_free = header_ptr;
LOG_D("insert to free list: next_free 0x%08x, prev_free 0x%08x",
header_ptr->next_free, header_ptr->prev_free);
}
#ifdef RT_USING_MEMTRACE
rt_memset(header_ptr->owner_thread_name, ' ', sizeof(header_ptr->owner_thread_name));
#endif /* RT_USING_MEMTRACE */
if (heap->locked == RT_FALSE)
{
/* release lock */
rt_sem_release(&(heap->lock));
}
}
RTM_EXPORT(rt_memheap_free);
/**
* @brief This function will caculate the total memory, the used memory, and
* the max used memory.
*
* @param heap is a pointer to the memheap object, which will reallocate
* memory from the block
*
* @param total is a pointer to get the total size of the memory.
*
* @param used is a pointer to get the size of memory used.
*
* @param max_used is a pointer to get the maximum memory used.
*/
void rt_memheap_info(struct rt_memheap *heap,
rt_size_t *total,
rt_size_t *used,
rt_size_t *max_used)
{
rt_err_t result;
if (heap->locked == RT_FALSE)
{
/* lock memheap */
result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER);
if (result != RT_EOK)
{
rt_set_errno(result);
return;
}
}
if (total != RT_NULL)
*total = heap->pool_size;
if (used != RT_NULL)
*used = heap->pool_size - heap->available_size;
if (max_used != RT_NULL)
*max_used = heap->max_used_size;
if (heap->locked == RT_FALSE)
{
/* release lock */
rt_sem_release(&(heap->lock));
}
}
#ifdef RT_USING_MEMHEAP_AS_HEAP
/*
* rt_malloc port function
*/
void *_memheap_alloc(struct rt_memheap *heap, rt_size_t size)
{
void *ptr;
/* try to allocate in system heap */
ptr = rt_memheap_alloc(heap, size);
#ifdef RT_USING_MEMHEAP_AUTO_BINDING
if (ptr == RT_NULL)
{
struct rt_object *object;
struct rt_list_node *node;
struct rt_memheap *_heap;
struct rt_object_information *information;
/* try to allocate on other memory heap */
information = rt_object_get_information(RT_Object_Class_MemHeap);
RT_ASSERT(information != RT_NULL);
for (node = information->object_list.next;
node != &(information->object_list);
node = node->next)
{
object = rt_list_entry(node, struct rt_object, list);
_heap = (struct rt_memheap *)object;
/* not allocate in the default system heap */
if (heap == _heap)
continue;
ptr = rt_memheap_alloc(_heap, size);
if (ptr != RT_NULL)
break;
}
}
#endif /* RT_USING_MEMHEAP_AUTO_BINDING */
return ptr;
}
/*
* rt_free port function
*/
void _memheap_free(void *rmem)
{
rt_memheap_free(rmem);
}
/*
* rt_realloc port function
*/
void *_memheap_realloc(struct rt_memheap *heap, void *rmem, rt_size_t newsize)
{
void *new_ptr;
struct rt_memheap_item *header_ptr;
if (rmem == RT_NULL)
return _memheap_alloc(heap, newsize);
if (newsize == 0)
{
_memheap_free(rmem);
return RT_NULL;
}
/* get old memory item */
header_ptr = (struct rt_memheap_item *)
((rt_uint8_t *)rmem - RT_MEMHEAP_SIZE);
new_ptr = rt_memheap_realloc(header_ptr->pool_ptr, rmem, newsize);
if (new_ptr == RT_NULL && newsize != 0)
{
/* allocate memory block from other memheap */
new_ptr = _memheap_alloc(heap, newsize);
if (new_ptr != RT_NULL && rmem != RT_NULL)
{
rt_size_t oldsize;
/* get the size of old memory block */
oldsize = MEMITEM_SIZE(header_ptr);
if (newsize > oldsize)
rt_memcpy(new_ptr, rmem, oldsize);
else
rt_memcpy(new_ptr, rmem, newsize);
_memheap_free(rmem);
}
}
return new_ptr;
}
#endif
#ifdef RT_USING_MEMTRACE
static int memheapcheck(int argc, char *argv[])
{
struct rt_object_information *info;
struct rt_list_node *list;
struct rt_memheap *heap;
struct rt_list_node *node;
struct rt_memheap_item *item;
rt_bool_t has_bad = RT_FALSE;
rt_base_t level;
char *name;
name = argc > 1 ? argv[1] : RT_NULL;
level = rt_hw_interrupt_disable();
info = rt_object_get_information(RT_Object_Class_MemHeap);
list = &info->object_list;
for (node = list->next; node != list; node = node->next)
{
heap = (struct rt_memheap *)rt_list_entry(node, struct rt_object, list);
/* find the specified object */
if (name != RT_NULL && rt_strncmp(name, heap->parent.name, RT_NAME_MAX) != 0)
continue;
/* check memheap */
for (item = heap->block_list; item->next != heap->block_list; item = item->next)
{
/* check magic */
if (!((item->magic & (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED)) == (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED) ||
(item->magic & (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED)) == (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED)))
{
has_bad = RT_TRUE;
break;
}
/* check pool_ptr */
if (heap != item->pool_ptr)
{
has_bad = RT_TRUE;
break;
}
/* check next and prev */
if (!((rt_ubase_t)item->next <= (rt_ubase_t)((rt_ubase_t)heap->start_addr + heap->pool_size) &&
(rt_ubase_t)item->prev >= (rt_ubase_t)heap->start_addr) &&
(rt_ubase_t)item->next == RT_ALIGN((rt_ubase_t)item->next, RT_ALIGN_SIZE) &&
(rt_ubase_t)item->prev == RT_ALIGN((rt_ubase_t)item->prev, RT_ALIGN_SIZE))
{
has_bad = RT_TRUE;
break;
}
/* check item */
if (item->next == item->next->prev)
{
has_bad = RT_TRUE;
break;
}
}
}
rt_hw_interrupt_enable(level);
if (has_bad)
{
rt_kprintf("Memory block wrong:\n");
rt_kprintf("name: %s\n", heap->parent.name);
rt_kprintf("item: 0x%p\n", item);
}
return 0;
}
MSH_CMD_EXPORT(memheapcheck, check memory for memheap);
static int memheaptrace(int argc, char *argv[])
{
struct rt_object_information *info;
struct rt_list_node *list;
struct rt_memheap *mh;
struct rt_list_node *node;
char *name;
name = argc > 1 ? argv[1] : RT_NULL;
info = rt_object_get_information(RT_Object_Class_MemHeap);
list = &info->object_list;
for (node = list->next; node != list; node = node->next)
{
struct rt_memheap_item *header_ptr;
long block_size;
mh = (struct rt_memheap *)rt_list_entry(node, struct rt_object, list);
/* find the specified object */
if (name != RT_NULL && rt_strncmp(name, mh->parent.name, RT_NAME_MAX) != 0)
continue;
/* memheap dump */
rt_kprintf("\nmemory heap address:\n");
rt_kprintf("name : %s\n", mh->parent.name);
rt_kprintf("heap_ptr: 0x%p\n", mh->start_addr);
rt_kprintf("free : 0x%08x\n", mh->available_size);
rt_kprintf("max_used: 0x%08x\n", mh->max_used_size);
rt_kprintf("size : 0x%08x\n", mh->pool_size);
rt_kprintf("\n--memory used information --\n");
/* memheap item */
for (header_ptr = mh->block_list;
header_ptr->next != mh->block_list;
header_ptr = header_ptr->next)
{
if ((header_ptr->magic & RT_MEMHEAP_MASK) != RT_MEMHEAP_MAGIC)
{
rt_kprintf("[0x%p - incorrect magic: 0x%08x\n",
header_ptr, header_ptr->magic);
break;
}
/* get current memory block size */
block_size = MEMITEM_SIZE(header_ptr);
if (block_size < 0)
break;
rt_kprintf("[0x%p - ", header_ptr);
if (block_size < 1024)
rt_kprintf("%5d", block_size);
else if (block_size < 1024 * 1024)
rt_kprintf("%4dK", block_size / 1024);
else if (block_size < 1024 * 1024 * 100)
rt_kprintf("%2d.%dM", block_size / (1024 * 1024), (block_size % (1024 * 1024) * 10) / (1024 * 1024));
else
rt_kprintf("%4dM", block_size / (1024 * 1024));
/* dump thread name */
rt_kprintf("] %c%c%c%c\n",
header_ptr->owner_thread_name[0],
header_ptr->owner_thread_name[1],
header_ptr->owner_thread_name[2],
header_ptr->owner_thread_name[3]);
}
}
return 0;
}
#ifdef RT_USING_FINSH
#include <finsh.h>
MSH_CMD_EXPORT(memheaptrace, dump memory trace for memheap);
#endif /* RT_USING_FINSH */
#endif /* RT_USING_MEMTRACE */
#endif /* RT_USING_MEMHEAP */
+411
View File
@@ -0,0 +1,411 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2006-05-27 Bernard implement memory pool
* 2006-06-03 Bernard fix the thread timer init bug
* 2006-06-30 Bernard fix the allocate/free block bug
* 2006-08-04 Bernard add hook support
* 2006-08-10 Bernard fix interrupt bug in rt_mp_alloc
* 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca
* 2010-10-26 yi.qiu add module support in rt_mp_delete
* 2011-01-24 Bernard add object allocation check.
* 2012-03-22 Bernard fix align issue in rt_mp_init and rt_mp_create.
* 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to mempool.c
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2023-12-10 xqyjlj fix spinlock assert
*/
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_MEMPOOL
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
static void (*rt_mp_alloc_hook)(struct rt_mempool *mp, void *block);
static void (*rt_mp_free_hook)(struct rt_mempool *mp, void *block);
/**
* @addtogroup Hook
*/
/**@{*/
/**
* @brief This function will set a hook function, which will be invoked when a memory
* block is allocated from the memory pool.
*
* @param hook the hook function
*/
void rt_mp_alloc_sethook(void (*hook)(struct rt_mempool *mp, void *block))
{
rt_mp_alloc_hook = hook;
}
/**
* @brief This function will set a hook function, which will be invoked when a memory
* block is released to the memory pool.
*
* @param hook the hook function
*/
void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block))
{
rt_mp_free_hook = hook;
}
/**@}*/
#endif /* RT_USING_HOOK */
/**
* @addtogroup MM
*/
/**@{*/
/**
* @brief This function will initialize a memory pool object, normally which is used
* for static object.
*
* @param mp is the memory pool object.
*
* @param name is the name of the memory pool.
*
* @param start is the start address of the memory pool.
*
* @param size is the total size of the memory pool.
*
* @param block_size is the size for each block..
*
* @return RT_EOK
*/
rt_err_t rt_mp_init(struct rt_mempool *mp,
const char *name,
void *start,
rt_size_t size,
rt_size_t block_size)
{
rt_uint8_t *block_ptr;
rt_size_t offset;
/* parameter check */
RT_ASSERT(mp != RT_NULL);
RT_ASSERT(name != RT_NULL);
RT_ASSERT(start != RT_NULL);
RT_ASSERT(size > 0 && block_size > 0);
/* initialize object */
rt_object_init(&(mp->parent), RT_Object_Class_MemPool, name);
/* initialize memory pool */
mp->start_address = start;
mp->size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE);
/* align the block size */
block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
mp->block_size = block_size;
/* align to align size byte */
mp->block_total_count = mp->size / (mp->block_size + sizeof(rt_uint8_t *));
mp->block_free_count = mp->block_total_count;
/* initialize suspended thread list */
rt_list_init(&(mp->suspend_thread));
/* initialize free block list */
block_ptr = (rt_uint8_t *)mp->start_address;
for (offset = 0; offset < mp->block_total_count; offset ++)
{
*(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *))) =
(rt_uint8_t *)(block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *)));
}
*(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *))) =
RT_NULL;
mp->block_list = block_ptr;
rt_spin_lock_init(&(mp->spinlock));
return RT_EOK;
}
RTM_EXPORT(rt_mp_init);
/**
* @brief This function will detach a memory pool from system object management.
*
* @param mp is the memory pool object.
*
* @return RT_EOK
*/
rt_err_t rt_mp_detach(struct rt_mempool *mp)
{
rt_base_t level;
/* parameter check */
RT_ASSERT(mp != RT_NULL);
RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool);
RT_ASSERT(rt_object_is_systemobject(&mp->parent));
level = rt_spin_lock_irqsave(&(mp->spinlock));
/* wake up all suspended threads */
rt_susp_list_resume_all(&mp->suspend_thread, RT_ERROR);
/* detach object */
rt_object_detach(&(mp->parent));
rt_spin_unlock_irqrestore(&(mp->spinlock), level);
return RT_EOK;
}
RTM_EXPORT(rt_mp_detach);
#ifdef RT_USING_HEAP
/**
* @brief This function will create a mempool object and allocate the memory pool from
* heap.
*
* @param name is the name of memory pool.
*
* @param block_count is the count of blocks in memory pool.
*
* @param block_size is the size for each block.
*
* @return the created mempool object
*/
rt_mp_t rt_mp_create(const char *name,
rt_size_t block_count,
rt_size_t block_size)
{
rt_uint8_t *block_ptr;
struct rt_mempool *mp;
rt_size_t offset;
RT_DEBUG_NOT_IN_INTERRUPT;
/* parameter check */
RT_ASSERT(name != RT_NULL);
RT_ASSERT(block_count > 0 && block_size > 0);
/* allocate object */
mp = (struct rt_mempool *)rt_object_allocate(RT_Object_Class_MemPool, name);
/* allocate object failed */
if (mp == RT_NULL)
return RT_NULL;
/* initialize memory pool */
block_size = RT_ALIGN(block_size, RT_ALIGN_SIZE);
mp->block_size = block_size;
mp->size = (block_size + sizeof(rt_uint8_t *)) * block_count;
/* allocate memory */
mp->start_address = rt_malloc((block_size + sizeof(rt_uint8_t *)) *
block_count);
if (mp->start_address == RT_NULL)
{
/* no memory, delete memory pool object */
rt_object_delete(&(mp->parent));
return RT_NULL;
}
mp->block_total_count = block_count;
mp->block_free_count = mp->block_total_count;
/* initialize suspended thread list */
rt_list_init(&(mp->suspend_thread));
/* initialize free block list */
block_ptr = (rt_uint8_t *)mp->start_address;
for (offset = 0; offset < mp->block_total_count; offset ++)
{
*(rt_uint8_t **)(block_ptr + offset * (block_size + sizeof(rt_uint8_t *)))
= block_ptr + (offset + 1) * (block_size + sizeof(rt_uint8_t *));
}
*(rt_uint8_t **)(block_ptr + (offset - 1) * (block_size + sizeof(rt_uint8_t *)))
= RT_NULL;
mp->block_list = block_ptr;
rt_spin_lock_init(&(mp->spinlock));
return mp;
}
RTM_EXPORT(rt_mp_create);
/**
* @brief This function will delete a memory pool and release the object memory.
*
* @param mp is the memory pool object.
*
* @return RT_EOK
*/
rt_err_t rt_mp_delete(rt_mp_t mp)
{
rt_base_t level;
RT_DEBUG_NOT_IN_INTERRUPT;
/* parameter check */
RT_ASSERT(mp != RT_NULL);
RT_ASSERT(rt_object_get_type(&mp->parent) == RT_Object_Class_MemPool);
RT_ASSERT(rt_object_is_systemobject(&mp->parent) == RT_FALSE);
level = rt_spin_lock_irqsave(&(mp->spinlock));
/* wake up all suspended threads */
rt_susp_list_resume_all(&mp->suspend_thread, RT_ERROR);
rt_spin_unlock_irqrestore(&(mp->spinlock), level);
/* release allocated room */
rt_free(mp->start_address);
/* detach object */
rt_object_delete(&(mp->parent));
return RT_EOK;
}
RTM_EXPORT(rt_mp_delete);
#endif /* RT_USING_HEAP */
/**
* @brief This function will allocate a block from memory pool.
*
* @param mp is the memory pool object.
*
* @param time is the maximum waiting time for allocating memory.
* - 0 for not waiting, allocating memory immediately.
*
* @return the allocated memory block or RT_NULL on allocated failed.
*/
void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time)
{
rt_uint8_t *block_ptr;
rt_base_t level;
struct rt_thread *thread;
rt_uint32_t before_sleep = 0;
/* parameter check */
RT_ASSERT(mp != RT_NULL);
/* get current thread */
thread = rt_thread_self();
level = rt_spin_lock_irqsave(&(mp->spinlock));
while (mp->block_free_count == 0)
{
/* memory block is unavailable. */
if (time == 0)
{
rt_spin_unlock_irqrestore(&(mp->spinlock), level);
rt_set_errno(-RT_ETIMEOUT);
return RT_NULL;
}
RT_DEBUG_NOT_IN_INTERRUPT;
thread->error = RT_EOK;
/* need suspend thread */
rt_thread_suspend_to_list(thread, &mp->suspend_thread, RT_IPC_FLAG_FIFO, RT_UNINTERRUPTIBLE);
if (time > 0)
{
/* get the start tick of timer */
before_sleep = rt_tick_get();
/* init thread timer and start it */
rt_timer_control(&(thread->thread_timer),
RT_TIMER_CTRL_SET_TIME,
&time);
rt_timer_start(&(thread->thread_timer));
}
/* enable interrupt */
rt_spin_unlock_irqrestore(&(mp->spinlock), level);
/* do a schedule */
rt_schedule();
if (thread->error != RT_EOK)
return RT_NULL;
if (time > 0)
{
time -= rt_tick_get() - before_sleep;
if (time < 0)
time = 0;
}
level = rt_spin_lock_irqsave(&(mp->spinlock));
}
/* memory block is available. decrease the free block counter */
mp->block_free_count--;
/* get block from block list */
block_ptr = mp->block_list;
RT_ASSERT(block_ptr != RT_NULL);
/* Setup the next free node. */
mp->block_list = *(rt_uint8_t **)block_ptr;
/* point to memory pool */
*(rt_uint8_t **)block_ptr = (rt_uint8_t *)mp;
rt_spin_unlock_irqrestore(&(mp->spinlock), level);
RT_OBJECT_HOOK_CALL(rt_mp_alloc_hook,
(mp, (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *))));
return (rt_uint8_t *)(block_ptr + sizeof(rt_uint8_t *));
}
RTM_EXPORT(rt_mp_alloc);
/**
* @brief This function will release a memory block.
*
* @param block the address of memory block to be released.
*/
void rt_mp_free(void *block)
{
rt_uint8_t **block_ptr;
struct rt_mempool *mp;
rt_base_t level;
/* parameter check */
if (block == RT_NULL) return;
/* get the control block of pool which the block belongs to */
block_ptr = (rt_uint8_t **)((rt_uint8_t *)block - sizeof(rt_uint8_t *));
mp = (struct rt_mempool *)*block_ptr;
RT_OBJECT_HOOK_CALL(rt_mp_free_hook, (mp, block));
level = rt_spin_lock_irqsave(&(mp->spinlock));
/* increase the free block count */
mp->block_free_count ++;
/* link the block into the block list */
*block_ptr = mp->block_list;
mp->block_list = (rt_uint8_t *)block_ptr;
if (rt_susp_list_dequeue(&mp->suspend_thread, RT_EOK))
{
rt_spin_unlock_irqrestore(&(mp->spinlock), level);
/* do a schedule */
rt_schedule();
return;
}
rt_spin_unlock_irqrestore(&(mp->spinlock), level);
}
RTM_EXPORT(rt_mp_free);
/**@}*/
#endif /* RT_USING_MEMPOOL */
+704
View File
@@ -0,0 +1,704 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2006-03-14 Bernard the first version
* 2006-04-21 Bernard change the scheduler lock to interrupt lock
* 2006-05-18 Bernard fix the object init bug
* 2006-08-03 Bernard add hook support
* 2007-01-28 Bernard rename RT_OBJECT_Class_Static to RT_Object_Class_Static
* 2010-10-26 yi.qiu add module support in rt_object_allocate and rt_object_free
* 2017-12-10 Bernard Add object_info enum.
* 2018-01-25 Bernard Fix the object find issue when enable MODULE.
* 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to object.c
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2023-11-17 xqyjlj add process group and session support
*/
#include <rtthread.h>
#include <rthw.h>
#ifdef RT_USING_MODULE
#include <dlmodule.h>
#endif /* RT_USING_MODULE */
#ifdef RT_USING_SMART
#include <lwp.h>
#endif
struct rt_custom_object
{
struct rt_object parent;
rt_err_t (*destroy)(void *);
void *data;
};
/*
* define object_info for the number of _object_container items.
*/
enum rt_object_info_type
{
RT_Object_Info_Thread = 0, /**< The object is a thread. */
#ifdef RT_USING_SEMAPHORE
RT_Object_Info_Semaphore, /**< The object is a semaphore. */
#endif
#ifdef RT_USING_MUTEX
RT_Object_Info_Mutex, /**< The object is a mutex. */
#endif
#ifdef RT_USING_EVENT
RT_Object_Info_Event, /**< The object is a event. */
#endif
#ifdef RT_USING_MAILBOX
RT_Object_Info_MailBox, /**< The object is a mail box. */
#endif
#ifdef RT_USING_MESSAGEQUEUE
RT_Object_Info_MessageQueue, /**< The object is a message queue. */
#endif
#ifdef RT_USING_MEMHEAP
RT_Object_Info_MemHeap, /**< The object is a memory heap */
#endif
#ifdef RT_USING_MEMPOOL
RT_Object_Info_MemPool, /**< The object is a memory pool. */
#endif
#ifdef RT_USING_DEVICE
RT_Object_Info_Device, /**< The object is a device */
#endif
RT_Object_Info_Timer, /**< The object is a timer. */
#ifdef RT_USING_MODULE
RT_Object_Info_Module, /**< The object is a module. */
#endif
#ifdef RT_USING_HEAP
RT_Object_Info_Memory, /**< The object is a memory. */
#endif
#ifdef RT_USING_SMART
RT_Object_Info_Channel, /**< The object is a IPC channel */
RT_Object_Info_ProcessGroup, /**< The object is a process group */
RT_Object_Info_Session, /**< The object is a session */
#endif
#ifdef RT_USING_HEAP
RT_Object_Info_Custom, /**< The object is a custom object */
#endif
RT_Object_Info_Unknown, /**< The object is unknown. */
};
#define _OBJ_CONTAINER_LIST_INIT(c) \
{&(_object_container[c].object_list), &(_object_container[c].object_list)}
static struct rt_object_information _object_container[RT_Object_Info_Unknown] =
{
/* initialize object container - thread */
{RT_Object_Class_Thread, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread), sizeof(struct rt_thread), RT_SPINLOCK_INIT},
#ifdef RT_USING_SEMAPHORE
/* initialize object container - semaphore */
{RT_Object_Class_Semaphore, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Semaphore), sizeof(struct rt_semaphore), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_MUTEX
/* initialize object container - mutex */
{RT_Object_Class_Mutex, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Mutex), sizeof(struct rt_mutex), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_EVENT
/* initialize object container - event */
{RT_Object_Class_Event, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Event), sizeof(struct rt_event), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_MAILBOX
/* initialize object container - mailbox */
{RT_Object_Class_MailBox, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MailBox), sizeof(struct rt_mailbox), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_MESSAGEQUEUE
/* initialize object container - message queue */
{RT_Object_Class_MessageQueue, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MessageQueue), sizeof(struct rt_messagequeue), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_MEMHEAP
/* initialize object container - memory heap */
{RT_Object_Class_MemHeap, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemHeap), sizeof(struct rt_memheap), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_MEMPOOL
/* initialize object container - memory pool */
{RT_Object_Class_MemPool, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemPool), sizeof(struct rt_mempool), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_DEVICE
/* initialize object container - device */
{RT_Object_Class_Device, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Device), sizeof(struct rt_device), RT_SPINLOCK_INIT},
#endif
/* initialize object container - timer */
{RT_Object_Class_Timer, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Timer), sizeof(struct rt_timer), RT_SPINLOCK_INIT},
#ifdef RT_USING_MODULE
/* initialize object container - module */
{RT_Object_Class_Module, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Module), sizeof(struct rt_dlmodule), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_HEAP
/* initialize object container - small memory */
{RT_Object_Class_Memory, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Memory), sizeof(struct rt_memory), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_SMART
/* initialize object container - module */
{RT_Object_Class_Channel, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Channel), sizeof(struct rt_channel), RT_SPINLOCK_INIT},
{RT_Object_Class_ProcessGroup, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_ProcessGroup), sizeof(struct rt_processgroup), RT_SPINLOCK_INIT},
{RT_Object_Class_Session, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Session), sizeof(struct rt_session), RT_SPINLOCK_INIT},
#endif
#ifdef RT_USING_HEAP
{RT_Object_Class_Custom, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Custom), sizeof(struct rt_custom_object), RT_SPINLOCK_INIT},
#endif
};
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
static void (*rt_object_attach_hook)(struct rt_object *object);
static void (*rt_object_detach_hook)(struct rt_object *object);
void (*rt_object_trytake_hook)(struct rt_object *object);
void (*rt_object_take_hook)(struct rt_object *object);
void (*rt_object_put_hook)(struct rt_object *object);
/**
* @addtogroup Hook
*/
/**@{*/
/**
* @brief This function will set a hook function, which will be invoked when object
* attaches to kernel object system.
*
* @param hook is the hook function.
*/
void rt_object_attach_sethook(void (*hook)(struct rt_object *object))
{
rt_object_attach_hook = hook;
}
/**
* @brief This function will set a hook function, which will be invoked when object
* detaches from kernel object system.
*
* @param hook is the hook function
*/
void rt_object_detach_sethook(void (*hook)(struct rt_object *object))
{
rt_object_detach_hook = hook;
}
/**
* @brief This function will set a hook function, which will be invoked when object
* is taken from kernel object system.
*
* The object is taken means:
* semaphore - semaphore is taken by thread
* mutex - mutex is taken by thread
* event - event is received by thread
* mailbox - mail is received by thread
* message queue - message is received by thread
*
* @param hook is the hook function.
*/
void rt_object_trytake_sethook(void (*hook)(struct rt_object *object))
{
rt_object_trytake_hook = hook;
}
/**
* @brief This function will set a hook function, which will be invoked when object
* have been taken from kernel object system.
*
* The object have been taken means:
* semaphore - semaphore have been taken by thread
* mutex - mutex have been taken by thread
* event - event have been received by thread
* mailbox - mail have been received by thread
* message queue - message have been received by thread
* timer - timer is started
*
* @param hook the hook function.
*/
void rt_object_take_sethook(void (*hook)(struct rt_object *object))
{
rt_object_take_hook = hook;
}
/**
* @brief This function will set a hook function, which will be invoked when object
* is put to kernel object system.
*
* @param hook is the hook function
*/
void rt_object_put_sethook(void (*hook)(struct rt_object *object))
{
rt_object_put_hook = hook;
}
/**@}*/
#endif /* RT_USING_HOOK */
/**
* @addtogroup KernelObject
*/
/**@{*/
/**
* @brief This function will return the specified type of object information.
*
* @param type is the type of object, which can be
* RT_Object_Class_Thread/Semaphore/Mutex... etc
*
* @return the object type information or RT_NULL
*/
struct rt_object_information *
rt_object_get_information(enum rt_object_class_type type)
{
int index;
type = (enum rt_object_class_type)(type & ~RT_Object_Class_Static);
for (index = 0; index < RT_Object_Info_Unknown; index ++)
if (_object_container[index].type == type) return &_object_container[index];
return RT_NULL;
}
RTM_EXPORT(rt_object_get_information);
/**
* @brief This function will return the length of object list in object container.
*
* @param type is the type of object, which can be
* RT_Object_Class_Thread/Semaphore/Mutex... etc
*
* @return the length of object list
*/
int rt_object_get_length(enum rt_object_class_type type)
{
int count = 0;
rt_base_t level;
struct rt_list_node *node = RT_NULL;
struct rt_object_information *information = RT_NULL;
information = rt_object_get_information((enum rt_object_class_type)type);
if (information == RT_NULL) return 0;
level = rt_spin_lock_irqsave(&(information->spinlock));
rt_list_for_each(node, &(information->object_list))
{
count ++;
}
rt_spin_unlock_irqrestore(&(information->spinlock), level);
return count;
}
RTM_EXPORT(rt_object_get_length);
/**
* @brief This function will copy the object pointer of the specified type,
* with the maximum size specified by maxlen.
*
* @param type is the type of object, which can be
* RT_Object_Class_Thread/Semaphore/Mutex... etc
*
* @param pointers is the pointer will be saved to.
*
* @param maxlen is the maximum number of pointers can be saved.
*
* @return the copied number of object pointers.
*/
int rt_object_get_pointers(enum rt_object_class_type type, rt_object_t *pointers, int maxlen)
{
int index = 0;
rt_base_t level;
struct rt_object *object;
struct rt_list_node *node = RT_NULL;
struct rt_object_information *information = RT_NULL;
if (maxlen <= 0) return 0;
information = rt_object_get_information(type);
if (information == RT_NULL) return 0;
level = rt_spin_lock_irqsave(&(information->spinlock));
/* retrieve pointer of object */
rt_list_for_each(node, &(information->object_list))
{
object = rt_list_entry(node, struct rt_object, list);
pointers[index] = object;
index ++;
if (index >= maxlen) break;
}
rt_spin_unlock_irqrestore(&(information->spinlock), level);
return index;
}
RTM_EXPORT(rt_object_get_pointers);
/**
* @brief This function will initialize an object and add it to object system
* management.
*
* @param object is the specified object to be initialized.
*
* @param type is the object type.
*
* @param name is the object name. In system, the object's name must be unique.
*/
void rt_object_init(struct rt_object *object,
enum rt_object_class_type type,
const char *name)
{
rt_base_t level;
#ifdef RT_USING_DEBUG
struct rt_list_node *node = RT_NULL;
#endif
struct rt_object_information *information;
#ifdef RT_USING_MODULE
struct rt_dlmodule *module = dlmodule_self();
#endif /* RT_USING_MODULE */
/* get object information */
information = rt_object_get_information(type);
RT_ASSERT(information != RT_NULL);
#ifdef RT_USING_DEBUG
/* check object type to avoid re-initialization */
/* enter critical */
level = rt_spin_lock_irqsave(&(information->spinlock));
/* try to find object */
for (node = information->object_list.next;
node != &(information->object_list);
node = node->next)
{
struct rt_object *obj;
obj = rt_list_entry(node, struct rt_object, list);
RT_ASSERT(obj != object);
}
/* leave critical */
rt_spin_unlock_irqrestore(&(information->spinlock), level);
#endif
/* initialize object's parameters */
/* set object type to static */
object->type = type | RT_Object_Class_Static;
#if RT_NAME_MAX > 0
rt_strncpy(object->name, name, RT_NAME_MAX); /* copy name */
#else
object->name = name;
#endif /* RT_NAME_MAX > 0 */
RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object));
level = rt_spin_lock_irqsave(&(information->spinlock));
#ifdef RT_USING_MODULE
if (module)
{
rt_list_insert_after(&(module->object_list), &(object->list));
object->module_id = (void *)module;
}
else
#endif /* RT_USING_MODULE */
{
/* insert object into information object list */
rt_list_insert_after(&(information->object_list), &(object->list));
}
rt_spin_unlock_irqrestore(&(information->spinlock), level);
}
/**
* @brief This function will detach a static object from object system,
* and the memory of static object is not freed.
*
* @param object the specified object to be detached.
*/
void rt_object_detach(rt_object_t object)
{
rt_base_t level;
struct rt_object_information *information;
/* object check */
RT_ASSERT(object != RT_NULL);
RT_OBJECT_HOOK_CALL(rt_object_detach_hook, (object));
information = rt_object_get_information((enum rt_object_class_type)object->type);
RT_ASSERT(information != RT_NULL);
level = rt_spin_lock_irqsave(&(information->spinlock));
/* remove from old list */
rt_list_remove(&(object->list));
rt_spin_unlock_irqrestore(&(information->spinlock), level);
object->type = 0;
}
#ifdef RT_USING_HEAP
/**
* @brief This function will allocate an object from object system.
*
* @param type is the type of object.
*
* @param name is the object name. In system, the object's name must be unique.
*
* @return object
*/
rt_object_t rt_object_allocate(enum rt_object_class_type type, const char *name)
{
struct rt_object *object;
rt_base_t level;
struct rt_object_information *information;
#ifdef RT_USING_MODULE
struct rt_dlmodule *module = dlmodule_self();
#endif /* RT_USING_MODULE */
RT_DEBUG_NOT_IN_INTERRUPT;
/* get object information */
information = rt_object_get_information(type);
RT_ASSERT(information != RT_NULL);
object = (struct rt_object *)RT_KERNEL_MALLOC(information->object_size);
if (object == RT_NULL)
{
/* no memory can be allocated */
return RT_NULL;
}
/* clean memory data of object */
rt_memset(object, 0x0, information->object_size);
/* initialize object's parameters */
/* set object type */
object->type = type;
/* set object flag */
object->flag = 0;
#if RT_NAME_MAX > 0
rt_strncpy(object->name, name, RT_NAME_MAX - 1); /* copy name */
#else
object->name = name;
#endif /* RT_NAME_MAX > 0 */
RT_OBJECT_HOOK_CALL(rt_object_attach_hook, (object));
level = rt_spin_lock_irqsave(&(information->spinlock));
#ifdef RT_USING_MODULE
if (module)
{
rt_list_insert_after(&(module->object_list), &(object->list));
object->module_id = (void *)module;
}
else
#endif /* RT_USING_MODULE */
{
/* insert object into information object list */
rt_list_insert_after(&(information->object_list), &(object->list));
}
rt_spin_unlock_irqrestore(&(information->spinlock), level);
return object;
}
/**
* @brief This function will delete an object and release object memory.
*
* @param object is the specified object to be deleted.
*/
void rt_object_delete(rt_object_t object)
{
rt_base_t level;
struct rt_object_information *information;
/* object check */
RT_ASSERT(object != RT_NULL);
RT_ASSERT(!(object->type & RT_Object_Class_Static));
RT_OBJECT_HOOK_CALL(rt_object_detach_hook, (object));
information = rt_object_get_information((enum rt_object_class_type)object->type);
RT_ASSERT(information != RT_NULL);
level = rt_spin_lock_irqsave(&(information->spinlock));
/* remove from old list */
rt_list_remove(&(object->list));
rt_spin_unlock_irqrestore(&(information->spinlock), level);
/* reset object type */
object->type = RT_Object_Class_Null;
/* free the memory of object */
RT_KERNEL_FREE(object);
}
#endif /* RT_USING_HEAP */
/**
* @brief This function will judge the object is system object or not.
*
* @note Normally, the system object is a static object and the type
* of object set to RT_Object_Class_Static.
*
* @param object is the specified object to be judged.
*
* @return RT_TRUE if a system object, RT_FALSE for others.
*/
rt_bool_t rt_object_is_systemobject(rt_object_t object)
{
/* object check */
RT_ASSERT(object != RT_NULL);
if (object->type & RT_Object_Class_Static)
return RT_TRUE;
return RT_FALSE;
}
/**
* @brief This function will return the type of object without
* RT_Object_Class_Static flag.
*
* @param object is the specified object to be get type.
*
* @return the type of object.
*/
rt_uint8_t rt_object_get_type(rt_object_t object)
{
/* object check */
RT_ASSERT(object != RT_NULL);
return object->type & ~RT_Object_Class_Static;
}
/**
* @brief This function will find specified name object from object
* container.
*
* @param name is the specified name of object.
*
* @param type is the type of object
*
* @return the found object or RT_NULL if there is no this object
* in object container.
*
* @note this function shall not be invoked in interrupt status.
*/
rt_object_t rt_object_find(const char *name, rt_uint8_t type)
{
struct rt_object *object = RT_NULL;
struct rt_list_node *node = RT_NULL;
struct rt_object_information *information = RT_NULL;
rt_base_t level;
information = rt_object_get_information((enum rt_object_class_type)type);
/* parameter check */
if ((name == RT_NULL) || (information == RT_NULL)) return RT_NULL;
/* which is invoke in interrupt status */
RT_DEBUG_NOT_IN_INTERRUPT;
/* enter critical */
level = rt_spin_lock_irqsave(&(information->spinlock));
/* try to find object */
rt_list_for_each(node, &(information->object_list))
{
object = rt_list_entry(node, struct rt_object, list);
if (rt_strncmp(object->name, name, RT_NAME_MAX) == 0)
{
rt_spin_unlock_irqrestore(&(information->spinlock), level);
return object;
}
}
rt_spin_unlock_irqrestore(&(information->spinlock), level);
return RT_NULL;
}
/**
* @brief This function will return the name of the specified object container
*
* @param object the specified object to be get name
* @param name buffer to store the object name string
* @param name_size maximum size of the buffer to store object name
*
* @return -RT_EINVAL if any parameter is invalid or RT_EOK if the operation is successfully executed
*
* @note this function shall not be invoked in interrupt status
*/
rt_err_t rt_object_get_name(rt_object_t object, char *name, rt_uint8_t name_size)
{
rt_err_t result = -RT_EINVAL;
if ((object != RT_NULL) && (name != RT_NULL) && (name_size != 0U))
{
const char *obj_name = object->name;
(void) rt_strncpy(name, obj_name, (rt_size_t)name_size);
result = RT_EOK;
}
return result;
}
#ifdef RT_USING_HEAP
/**
* This function will create a custom object
* container.
*
* @param name the specified name of object.
* @param data the custom data
* @param data_destroy the custom object destroy callback
*
* @return the found object or RT_NULL if there is no this object
* in object container.
*
* @note this function shall not be invoked in interrupt status.
*/
rt_object_t rt_custom_object_create(const char *name, void *data, rt_err_t (*data_destroy)(void *))
{
struct rt_custom_object *cobj = RT_NULL;
cobj = (struct rt_custom_object *)rt_object_allocate(RT_Object_Class_Custom, name);
if (!cobj)
{
return RT_NULL;
}
cobj->destroy = data_destroy;
cobj->data = data;
return (struct rt_object *)cobj;
}
/**
* This function will destroy a custom object
* container.
*
* @param obj the specified name of object.
*
* @note this function shall not be invoked in interrupt status.
*/
rt_err_t rt_custom_object_destroy(rt_object_t obj)
{
rt_err_t ret = -1;
struct rt_custom_object *cobj = (struct rt_custom_object *)obj;
if (obj && obj->type == RT_Object_Class_Custom)
{
if (cobj->destroy)
{
ret = cobj->destroy(cobj->data);
}
rt_object_delete(obj);
}
return ret;
}
#endif
/**@}*/
+277
View File
@@ -0,0 +1,277 @@
/*
* Copyright (c) 2006-2024, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* (scheduler_comm.c) Common API of scheduling routines.
*
* Change Logs:
* Date Author Notes
* 2024-01-18 Shell Separate scheduling related codes from thread.c, scheduler_.*
*/
#define DBG_TAG "kernel.sched"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
#include <rtthread.h>
void rt_sched_thread_init_ctx(struct rt_thread *thread, rt_uint32_t tick, rt_uint8_t priority)
{
/* setup thread status */
RT_SCHED_CTX(thread).stat = RT_THREAD_INIT;
#ifdef RT_USING_SMP
/* not bind on any cpu */
RT_SCHED_CTX(thread).bind_cpu = RT_CPUS_NR;
RT_SCHED_CTX(thread).oncpu = RT_CPU_DETACHED;
#endif /* RT_USING_SMP */
rt_sched_thread_init_priv(thread, tick, priority);
}
rt_err_t rt_sched_thread_timer_start(struct rt_thread *thread)
{
RT_SCHED_DEBUG_IS_LOCKED;
RT_SCHED_CTX(thread).sched_flag_ttmr_set = 1;
return RT_EOK;
}
rt_err_t rt_sched_thread_timer_stop(struct rt_thread *thread)
{
rt_err_t error;
RT_SCHED_DEBUG_IS_LOCKED;
if (RT_SCHED_CTX(thread).sched_flag_ttmr_set)
{
error = rt_timer_stop(&thread->thread_timer);
/* mask out timer flag no matter stop success or not */
RT_SCHED_CTX(thread).sched_flag_ttmr_set = 0;
}
else
{
error = RT_EOK;
}
return error;
}
rt_uint8_t rt_sched_thread_get_stat(struct rt_thread *thread)
{
RT_SCHED_DEBUG_IS_LOCKED;
return RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK;
}
rt_uint8_t rt_sched_thread_get_curr_prio(struct rt_thread *thread)
{
RT_SCHED_DEBUG_IS_LOCKED;
return RT_SCHED_PRIV(thread).current_priority;
}
rt_uint8_t rt_sched_thread_get_init_prio(struct rt_thread *thread)
{
/* read only fields, so lock is unecessary */
return RT_SCHED_PRIV(thread).init_priority;
}
/**
* @note Caller must hold the scheduler lock
*/
rt_uint8_t rt_sched_thread_is_suspended(struct rt_thread *thread)
{
RT_SCHED_DEBUG_IS_LOCKED;
return (RT_SCHED_CTX(thread).stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK;
}
rt_err_t rt_sched_thread_close(struct rt_thread *thread)
{
RT_SCHED_DEBUG_IS_LOCKED;
RT_SCHED_CTX(thread).stat = RT_THREAD_CLOSE;
return RT_EOK;
}
rt_err_t rt_sched_thread_yield(struct rt_thread *thread)
{
RT_SCHED_DEBUG_IS_LOCKED;
RT_SCHED_PRIV(thread).remaining_tick = RT_SCHED_PRIV(thread).init_tick;
RT_SCHED_CTX(thread).stat |= RT_THREAD_STAT_YIELD;
return RT_EOK;
}
rt_err_t rt_sched_thread_ready(struct rt_thread *thread)
{
rt_err_t error;
RT_SCHED_DEBUG_IS_LOCKED;
if (!rt_sched_thread_is_suspended(thread))
{
/* failed to proceed, and that's possibly due to a racing condition */
error = -RT_EINVAL;
}
else
{
if (RT_SCHED_CTX(thread).sched_flag_ttmr_set)
{
/**
* Quiet timeout timer first if set. and don't continue if we
* failed, because it probably means that a timeout ISR racing to
* resume thread before us.
*/
error = rt_sched_thread_timer_stop(thread);
}
else
{
error = RT_EOK;
}
if (!error)
{
/* remove from suspend list */
rt_list_remove(&RT_THREAD_LIST_NODE(thread));
#ifdef RT_USING_SMART
thread->wakeup_handle.func = RT_NULL;
#endif
/* insert to schedule ready list and remove from susp list */
rt_sched_insert_thread(thread);
}
}
return error;
}
rt_err_t rt_sched_tick_increase(void)
{
struct rt_thread *thread;
rt_sched_lock_level_t slvl;
thread = rt_thread_self();
rt_sched_lock(&slvl);
RT_SCHED_PRIV(thread).remaining_tick--;
if (RT_SCHED_PRIV(thread).remaining_tick)
{
rt_sched_unlock(slvl);
}
else
{
rt_sched_thread_yield(thread);
/* request a rescheduling even though we are probably in an ISR */
rt_sched_unlock_n_resched(slvl);
}
return RT_EOK;
}
/**
* @brief Update priority of the target thread
*/
rt_err_t rt_sched_thread_change_priority(struct rt_thread *thread, rt_uint8_t priority)
{
RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
RT_SCHED_DEBUG_IS_LOCKED;
/* for ready thread, change queue; otherwise simply update the priority */
if ((RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY)
{
/* remove thread from schedule queue first */
rt_sched_remove_thread(thread);
/* change thread priority */
RT_SCHED_PRIV(thread).current_priority = priority;
/* recalculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32
RT_SCHED_PRIV(thread).number = RT_SCHED_PRIV(thread).current_priority >> 3; /* 5bit */
RT_SCHED_PRIV(thread).number_mask = 1 << RT_SCHED_PRIV(thread).number;
RT_SCHED_PRIV(thread).high_mask = 1 << (RT_SCHED_PRIV(thread).current_priority & 0x07); /* 3bit */
#else
RT_SCHED_PRIV(thread).number_mask = 1 << RT_SCHED_PRIV(thread).current_priority;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
RT_SCHED_CTX(thread).stat = RT_THREAD_INIT;
/* insert thread to schedule queue again */
rt_sched_insert_thread(thread);
}
else
{
RT_SCHED_PRIV(thread).current_priority = priority;
/* recalculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32
RT_SCHED_PRIV(thread).number = RT_SCHED_PRIV(thread).current_priority >> 3; /* 5bit */
RT_SCHED_PRIV(thread).number_mask = 1 << RT_SCHED_PRIV(thread).number;
RT_SCHED_PRIV(thread).high_mask = 1 << (RT_SCHED_PRIV(thread).current_priority & 0x07); /* 3bit */
#else
RT_SCHED_PRIV(thread).number_mask = 1 << RT_SCHED_PRIV(thread).current_priority;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
}
return RT_EOK;
}
#ifdef RT_USING_OVERFLOW_CHECK
void rt_scheduler_stack_check(struct rt_thread *thread)
{
RT_ASSERT(thread != RT_NULL);
#ifdef RT_USING_SMART
#ifndef ARCH_MM_MMU
struct rt_lwp *lwp = thread ? (struct rt_lwp *)thread->lwp : 0;
/* if stack pointer locate in user data section skip stack check. */
if (lwp && ((rt_uint32_t)thread->sp > (rt_uint32_t)lwp->data_entry &&
(rt_uint32_t)thread->sp <= (rt_uint32_t)lwp->data_entry + (rt_uint32_t)lwp->data_size))
{
return;
}
#endif /* not defined ARCH_MM_MMU */
#endif /* RT_USING_SMART */
#ifndef RT_USING_HW_STACK_GUARD
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
if (*((rt_uint8_t *)((rt_ubase_t)thread->stack_addr + thread->stack_size - 1)) != '#' ||
#else
if (*((rt_uint8_t *)thread->stack_addr) != '#' ||
#endif /* ARCH_CPU_STACK_GROWS_UPWARD */
(rt_ubase_t)thread->sp <= (rt_ubase_t)thread->stack_addr ||
(rt_ubase_t)thread->sp >
(rt_ubase_t)thread->stack_addr + (rt_ubase_t)thread->stack_size)
{
rt_base_t dummy = 1;
LOG_E("thread:%s stack overflow\n", thread->parent.name);
while (dummy);
}
#endif /* RT_USING_HW_STACK_GUARD */
#ifdef ARCH_CPU_STACK_GROWS_UPWARD
#ifndef RT_USING_HW_STACK_GUARD
else if ((rt_ubase_t)thread->sp > ((rt_ubase_t)thread->stack_addr + thread->stack_size))
#else
if ((rt_ubase_t)thread->sp > ((rt_ubase_t)thread->stack_addr + thread->stack_size))
#endif
{
LOG_W("warning: %s stack is close to the top of stack address.\n",
thread->parent.name);
}
#else
#ifndef RT_USING_HW_STACK_GUARD
else if ((rt_ubase_t)thread->sp <= ((rt_ubase_t)thread->stack_addr + 32))
#else
if ((rt_ubase_t)thread->sp <= ((rt_ubase_t)thread->stack_addr + 32))
#endif
{
LOG_W("warning: %s stack is close to end of stack address.\n",
thread->parent.name);
}
#endif /* ARCH_CPU_STACK_GROWS_UPWARD */
}
#endif /* RT_USING_OVERFLOW_CHECK */
File diff suppressed because it is too large. Load diff
+568
View File
@@ -0,0 +1,568 @@
/*
* Copyright (c) 2006-2023, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2006-03-17 Bernard the first version
* 2006-04-28 Bernard fix the scheduler algorthm
* 2006-04-30 Bernard add SCHEDULER_DEBUG
* 2006-05-27 Bernard fix the scheduler algorthm for same priority
* thread schedule
* 2006-06-04 Bernard rewrite the scheduler algorithm
* 2006-08-03 Bernard add hook support
* 2006-09-05 Bernard add 32 priority level support
* 2006-09-24 Bernard add rt_system_scheduler_start function
* 2009-09-16 Bernard fix _rt_scheduler_stack_check
* 2010-04-11 yi.qiu add module feature
* 2010-07-13 Bernard fix the maximal number of rt_scheduler_lock_nest
* issue found by kuronca
* 2010-12-13 Bernard add defunct list initialization even if not use heap.
* 2011-05-10 Bernard clean scheduler debug log.
* 2013-12-21 Grissiom add rt_critical_level
* 2018-11-22 Jesven remove the current task from ready queue
* add per cpu ready queue
* add _scheduler_get_highest_priority_thread to find highest priority task
* rt_schedule_insert_thread won't insert current task to ready queue
* in smp version, rt_hw_context_switch_interrupt maybe switch to
* new task directly
* 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to scheduler.c
* 2023-03-27 rose_man Split into scheduler upc and scheduler_mp.c
* 2023-10-17 ChuShicheng Modify the timing of clearing RT_THREAD_STAT_YIELD flag bits
*/
#include <rtthread.h>
#include <rthw.h>
#define DBG_TAG "kernel.scheduler"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
rt_uint32_t rt_thread_ready_priority_group;
#if RT_THREAD_PRIORITY_MAX > 32
/* Maximum priority level, 256 */
rt_uint8_t rt_thread_ready_table[32];
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
extern volatile rt_uint8_t rt_interrupt_nest;
static rt_int16_t rt_scheduler_lock_nest;
struct rt_thread *rt_current_thread = RT_NULL;
rt_uint8_t rt_current_priority;
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
static void (*rt_scheduler_hook)(struct rt_thread *from, struct rt_thread *to);
static void (*rt_scheduler_switch_hook)(struct rt_thread *tid);
/**
* @addtogroup Hook
*/
/**@{*/
/**
* @brief This function will set a hook function, which will be invoked when thread
* switch happens.
*
* @param hook is the hook function.
*/
void rt_scheduler_sethook(void (*hook)(struct rt_thread *from, struct rt_thread *to))
{
rt_scheduler_hook = hook;
}
/**
* @brief This function will set a hook function, which will be invoked when context
* switch happens.
*
* @param hook is the hook function.
*/
void rt_scheduler_switch_sethook(void (*hook)(struct rt_thread *tid))
{
rt_scheduler_switch_hook = hook;
}
/**@}*/
#endif /* RT_USING_HOOK */
static struct rt_thread* _scheduler_get_highest_priority_thread(rt_ubase_t *highest_prio)
{
struct rt_thread *highest_priority_thread;
rt_ubase_t highest_ready_priority;
#if RT_THREAD_PRIORITY_MAX > 32
rt_ubase_t number;
number = __rt_ffs(rt_thread_ready_priority_group) - 1;
highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1;
#else
highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
/* get highest ready priority thread */
highest_priority_thread = RT_THREAD_LIST_NODE_ENTRY(rt_thread_priority_table[highest_ready_priority].next);
*highest_prio = highest_ready_priority;
return highest_priority_thread;
}
rt_err_t rt_sched_lock(rt_sched_lock_level_t *plvl)
{
rt_base_t level;
if (!plvl)
return -RT_EINVAL;
level = rt_hw_interrupt_disable();
*plvl = level;
return RT_EOK;
}
rt_err_t rt_sched_unlock(rt_sched_lock_level_t level)
{
rt_hw_interrupt_enable(level);
return RT_EOK;
}
rt_err_t rt_sched_unlock_n_resched(rt_sched_lock_level_t level)
{
if (rt_thread_self())
{
/* if scheduler is available */
rt_schedule();
}
rt_hw_interrupt_enable(level);
return RT_EOK;
}
/**
* @brief This function will initialize the system scheduler.
*/
void rt_system_scheduler_init(void)
{
rt_base_t offset;
rt_scheduler_lock_nest = 0;
LOG_D("start scheduler: max priority 0x%02x",
RT_THREAD_PRIORITY_MAX);
for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++)
{
rt_list_init(&rt_thread_priority_table[offset]);
}
/* initialize ready priority group */
rt_thread_ready_priority_group = 0;
#if RT_THREAD_PRIORITY_MAX > 32
/* initialize ready table */
rt_memset(rt_thread_ready_table, 0, sizeof(rt_thread_ready_table));
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
}
/**
* @brief This function will startup the scheduler. It will select one thread
* with the highest priority level, then switch to it.
*/
void rt_system_scheduler_start(void)
{
struct rt_thread *to_thread;
rt_ubase_t highest_ready_priority;
to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority);
rt_current_thread = to_thread;
rt_sched_remove_thread(to_thread);
RT_SCHED_CTX(to_thread).stat = RT_THREAD_RUNNING;
/* switch to new thread */
rt_hw_context_switch_to((rt_ubase_t)&to_thread->sp);
/* never come back */
}
/**
* @addtogroup Thread
* @cond
*/
/**@{*/
/**
* @brief This function will perform scheduling once. It will select one thread
* with the highest priority, and switch to it immediately.
*/
void rt_schedule(void)
{
rt_base_t level;
struct rt_thread *to_thread;
struct rt_thread *from_thread;
/* disable interrupt */
level = rt_hw_interrupt_disable();
/* check the scheduler is enabled or not */
if (rt_scheduler_lock_nest == 0)
{
rt_ubase_t highest_ready_priority;
if (rt_thread_ready_priority_group != 0)
{
/* need_insert_from_thread: need to insert from_thread to ready queue */
int need_insert_from_thread = 0;
to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority);
if ((RT_SCHED_CTX(rt_current_thread).stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING)
{
if (RT_SCHED_PRIV(rt_current_thread).current_priority < highest_ready_priority)
{
to_thread = rt_current_thread;
}
else if (RT_SCHED_PRIV(rt_current_thread).current_priority == highest_ready_priority && (RT_SCHED_CTX(rt_current_thread).stat & RT_THREAD_STAT_YIELD_MASK) == 0)
{
to_thread = rt_current_thread;
}
else
{
need_insert_from_thread = 1;
}
}
if (to_thread != rt_current_thread)
{
/* if the destination thread is not the same as current thread */
rt_current_priority = (rt_uint8_t)highest_ready_priority;
from_thread = rt_current_thread;
rt_current_thread = to_thread;
RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (from_thread, to_thread));
if (need_insert_from_thread)
{
rt_sched_insert_thread(from_thread);
}
if ((RT_SCHED_CTX(from_thread).stat & RT_THREAD_STAT_YIELD_MASK) != 0)
{
RT_SCHED_CTX(from_thread).stat &= ~RT_THREAD_STAT_YIELD_MASK;
}
rt_sched_remove_thread(to_thread);
RT_SCHED_CTX(to_thread).stat = RT_THREAD_RUNNING | (RT_SCHED_CTX(to_thread).stat & ~RT_THREAD_STAT_MASK);
/* switch to new thread */
LOG_D("[%d]switch to priority#%d "
"thread:%.*s(sp:0x%08x), "
"from thread:%.*s(sp: 0x%08x)",
rt_interrupt_nest, highest_ready_priority,
RT_NAME_MAX, to_thread->parent.name, to_thread->sp,
RT_NAME_MAX, from_thread->parent.name, from_thread->sp);
RT_SCHEDULER_STACK_CHECK(to_thread);
if (rt_interrupt_nest == 0)
{
extern void rt_thread_handle_sig(rt_bool_t clean_state);
RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (from_thread));
rt_hw_context_switch((rt_ubase_t)&from_thread->sp,
(rt_ubase_t)&to_thread->sp);
/* enable interrupt */
rt_hw_interrupt_enable(level);
#ifdef RT_USING_SIGNALS
/* check stat of thread for signal */
level = rt_hw_interrupt_disable();
if (RT_SCHED_CTX(rt_current_thread).stat & RT_THREAD_STAT_SIGNAL_PENDING)
{
extern void rt_thread_handle_sig(rt_bool_t clean_state);
RT_SCHED_CTX(rt_current_thread).stat &= ~RT_THREAD_STAT_SIGNAL_PENDING;
rt_hw_interrupt_enable(level);
/* check signal status */
rt_thread_handle_sig(RT_TRUE);
}
else
{
rt_hw_interrupt_enable(level);
}
#endif /* RT_USING_SIGNALS */
goto __exit;
}
else
{
LOG_D("switch in interrupt");
rt_hw_context_switch_interrupt((rt_ubase_t)&from_thread->sp,
(rt_ubase_t)&to_thread->sp, from_thread, to_thread);
}
}
else
{
rt_sched_remove_thread(rt_current_thread);
RT_SCHED_CTX(rt_current_thread).stat = RT_THREAD_RUNNING | (RT_SCHED_CTX(rt_current_thread).stat & ~RT_THREAD_STAT_MASK);
}
}
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
__exit:
return;
}
/* Normally, there isn't anyone racing with us so this operation is lockless */
void rt_sched_thread_startup(struct rt_thread *thread)
{
#if RT_THREAD_PRIORITY_MAX > 32
RT_SCHED_PRIV(thread).number = RT_SCHED_PRIV(thread).current_priority >> 3; /* 5bit */
RT_SCHED_PRIV(thread).number_mask = 1L << RT_SCHED_PRIV(thread).number;
RT_SCHED_PRIV(thread).high_mask = 1L << (RT_SCHED_PRIV(thread).current_priority & 0x07); /* 3bit */
#else
RT_SCHED_PRIV(thread).number_mask = 1L << RT_SCHED_PRIV(thread).current_priority;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
/* change thread stat, so we can resume it */
RT_SCHED_CTX(thread).stat = RT_THREAD_SUSPEND;
}
void rt_sched_thread_init_priv(struct rt_thread *thread, rt_uint32_t tick, rt_uint8_t priority)
{
rt_list_init(&RT_THREAD_LIST_NODE(thread));
/* priority init */
RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
RT_SCHED_PRIV(thread).init_priority = priority;
RT_SCHED_PRIV(thread).current_priority = priority;
/* don't add to scheduler queue as init thread */
RT_SCHED_PRIV(thread).number_mask = 0;
#if RT_THREAD_PRIORITY_MAX > 32
RT_SCHED_PRIV(thread).number = 0;
RT_SCHED_PRIV(thread).high_mask = 0;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
/* tick init */
RT_SCHED_PRIV(thread).init_tick = tick;
RT_SCHED_PRIV(thread).remaining_tick = tick;
}
/**
* @brief This function will insert a thread to the system ready queue. The state of
* thread will be set as READY and the thread will be removed from suspend queue.
*
* @param thread is the thread to be inserted.
*
* @note Please do not invoke this function in user application.
*/
void rt_sched_insert_thread(struct rt_thread *thread)
{
rt_base_t level;
RT_ASSERT(thread != RT_NULL);
/* disable interrupt */
level = rt_hw_interrupt_disable();
/* it's current thread, it should be RUNNING thread */
if (thread == rt_current_thread)
{
RT_SCHED_CTX(thread).stat = RT_THREAD_RUNNING | (RT_SCHED_CTX(thread).stat & ~RT_THREAD_STAT_MASK);
goto __exit;
}
/* READY thread, insert to ready queue */
RT_SCHED_CTX(thread).stat = RT_THREAD_READY | (RT_SCHED_CTX(thread).stat & ~RT_THREAD_STAT_MASK);
/* there is no time slices left(YIELD), inserting thread before ready list*/
if((RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_YIELD_MASK) != 0)
{
rt_list_insert_before(&(rt_thread_priority_table[RT_SCHED_PRIV(thread).current_priority]),
&RT_THREAD_LIST_NODE(thread));
}
/* there are some time slices left, inserting thread after ready list to schedule it firstly at next time*/
else
{
rt_list_insert_after(&(rt_thread_priority_table[RT_SCHED_PRIV(thread).current_priority]),
&RT_THREAD_LIST_NODE(thread));
}
LOG_D("insert thread[%.*s], the priority: %d",
RT_NAME_MAX, thread->parent.name, RT_SCHED_PRIV(rt_current_thread).current_priority);
/* set priority mask */
#if RT_THREAD_PRIORITY_MAX > 32
rt_thread_ready_table[RT_SCHED_PRIV(thread).number] |= RT_SCHED_PRIV(thread).high_mask;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
rt_thread_ready_priority_group |= RT_SCHED_PRIV(thread).number_mask;
__exit:
/* enable interrupt */
rt_hw_interrupt_enable(level);
}
/**
* @brief This function will remove a thread from system ready queue.
*
* @param thread is the thread to be removed.
*
* @note Please do not invoke this function in user application.
*/
void rt_sched_remove_thread(struct rt_thread *thread)
{
rt_base_t level;
RT_ASSERT(thread != RT_NULL);
/* disable interrupt */
level = rt_hw_interrupt_disable();
LOG_D("remove thread[%.*s], the priority: %d",
RT_NAME_MAX, thread->parent.name,
RT_SCHED_PRIV(rt_current_thread).current_priority);
/* remove thread from ready list */
rt_list_remove(&RT_THREAD_LIST_NODE(thread));
if (rt_list_isempty(&(rt_thread_priority_table[RT_SCHED_PRIV(thread).current_priority])))
{
#if RT_THREAD_PRIORITY_MAX > 32
rt_thread_ready_table[RT_SCHED_PRIV(thread).number] &= ~RT_SCHED_PRIV(thread).high_mask;
if (rt_thread_ready_table[RT_SCHED_PRIV(thread).number] == 0)
{
rt_thread_ready_priority_group &= ~RT_SCHED_PRIV(thread).number_mask;
}
#else
rt_thread_ready_priority_group &= ~RT_SCHED_PRIV(thread).number_mask;
#endif /* RT_THREAD_PRIORITY_MAX > 32 */
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
}
#ifdef RT_USING_DEBUG
static volatile int _critical_error_occurred = 0;
void rt_exit_critical_safe(rt_base_t critical_level)
{
rt_base_t level;
/* disable interrupt */
level = rt_hw_interrupt_disable();
if (!_critical_error_occurred)
{
if (critical_level != rt_scheduler_lock_nest)
{
int dummy = 1;
_critical_error_occurred = 1;
rt_kprintf("%s: un-compatible critical level\n" \
"\tCurrent %d\n\tCaller %d\n",
__func__, rt_scheduler_lock_nest,
critical_level);
rt_backtrace();
while (dummy) ;
}
}
rt_hw_interrupt_enable(level);
rt_exit_critical();
}
#else
void rt_exit_critical_safe(rt_base_t critical_level)
{
rt_exit_critical();
}
#endif
RTM_EXPORT(rt_exit_critical_safe);
/**
* @brief This function will lock the thread scheduler.
*/
rt_base_t rt_enter_critical(void)
{
rt_base_t level;
rt_base_t critical_level;
/* disable interrupt */
level = rt_hw_interrupt_disable();
/*
* the maximal number of nest is RT_UINT16_MAX, which is big
* enough and does not check here
*/
rt_scheduler_lock_nest ++;
critical_level = rt_scheduler_lock_nest;
/* enable interrupt */
rt_hw_interrupt_enable(level);
return critical_level;
}
RTM_EXPORT(rt_enter_critical);
/**
* @brief This function will unlock the thread scheduler.
*/
void rt_exit_critical(void)
{
rt_base_t level;
/* disable interrupt */
level = rt_hw_interrupt_disable();
rt_scheduler_lock_nest --;
if (rt_scheduler_lock_nest <= 0)
{
rt_scheduler_lock_nest = 0;
/* enable interrupt */
rt_hw_interrupt_enable(level);
if (rt_current_thread)
{
/* if scheduler is started, do a schedule */
rt_schedule();
}
}
else
{
/* enable interrupt */
rt_hw_interrupt_enable(level);
}
}
RTM_EXPORT(rt_exit_critical);
/**
* @brief Get the scheduler lock level.
*
* @return the level of the scheduler lock. 0 means unlocked.
*/
rt_uint16_t rt_critical_level(void)
{
return rt_scheduler_lock_nest;
}
RTM_EXPORT(rt_critical_level);
rt_err_t rt_sched_thread_bind_cpu(struct rt_thread *thread, int cpu)
{
return -RT_EINVAL;
}
/**@}*/
/**@endcond*/
+675
View File
@@ -0,0 +1,675 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2017/10/5 Bernard the first version
* 2018/09/17 Jesven fix: in _signal_deliver RT_THREAD_STAT_MASK to RT_THREAD_STAT_SIGNAL_MASK
* 2018/11/22 Jesven in smp version rt_hw_context_switch_to add a param
*/
#include <stdint.h>
#include <string.h>
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_SIGNALS
#ifndef RT_SIG_INFO_MAX
#ifdef ARCH_CPU_64BIT
#define RT_SIG_INFO_MAX 64
#else
#define RT_SIG_INFO_MAX 32
#endif /* ARCH_CPU_64BIT */
#endif /* RT_SIG_INFO_MAX */
#define DBG_TAG "SIGN"
#define DBG_LVL DBG_WARNING
#include <rtdbg.h>
#define sig_mask(sig_no) (1u << sig_no)
#define sig_valid(sig_no) (sig_no >= 0 && sig_no < RT_SIG_MAX)
static struct rt_spinlock _thread_signal_lock = RT_SPINLOCK_INIT;
struct siginfo_node
{
siginfo_t si;
struct rt_slist_node list;
};
static struct rt_mempool *_siginfo_pool;
static void _signal_deliver(rt_thread_t tid);
void rt_thread_handle_sig(rt_bool_t clean_state);
static void _signal_default_handler(int signo)
{
RT_UNUSED(signo);
LOG_I("handled signo[%d] with default action.", signo);
return ;
}
static void _signal_entry(void *parameter)
{
RT_UNUSED(parameter);
rt_thread_t tid = rt_thread_self();
/* handle signal */
rt_thread_handle_sig(RT_FALSE);
#ifdef RT_USING_SMP
#else
/* return to thread */
tid->sp = tid->sig_ret;
tid->sig_ret = RT_NULL;
#endif /* RT_USING_SMP */
LOG_D("switch back to: 0x%08x\n", tid->sp);
RT_SCHED_CTX(tid).stat &= ~RT_THREAD_STAT_SIGNAL;
#ifdef RT_USING_SMP
rt_hw_context_switch_to((rt_base_t)&parameter, tid);
#else
rt_hw_context_switch_to((rt_ubase_t)&(tid->sp));
#endif /* RT_USING_SMP */
}
/*
* To deliver a signal to thread, there are cases:
* 1. When thread is suspended, function resumes thread and
* set signal stat;
* 2. When thread is ready:
* - If function delivers a signal to self thread, just handle
* it.
* - If function delivers a signal to another ready thread, OS
* should build a slice context to handle it.
*/
static void _signal_deliver(rt_thread_t tid)
{
rt_base_t level;
level = rt_spin_lock_irqsave(&_thread_signal_lock);
/* thread is not interested in pended signals */
if (!(tid->sig_pending & tid->sig_mask))
{
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
return;
}
if ((RT_SCHED_CTX(tid).stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK)
{
/* resume thread to handle signal */
#ifdef RT_USING_SMART
rt_thread_wakeup(tid);
#else
rt_thread_resume(tid);
#endif
/* add signal state */
RT_SCHED_CTX(tid).stat |= (RT_THREAD_STAT_SIGNAL | RT_THREAD_STAT_SIGNAL_PENDING);
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
/* re-schedule */
rt_schedule();
}
else
{
if (tid == rt_thread_self())
{
/* add signal state */
RT_SCHED_CTX(tid).stat |= RT_THREAD_STAT_SIGNAL;
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
/* do signal action in self thread context */
if (rt_interrupt_get_nest() == 0)
{
rt_thread_handle_sig(RT_TRUE);
}
}
else if (!((RT_SCHED_CTX(tid).stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL))
{
/* add signal state */
RT_SCHED_CTX(tid).stat |= (RT_THREAD_STAT_SIGNAL | RT_THREAD_STAT_SIGNAL_PENDING);
#ifdef RT_USING_SMP
{
int cpu_id;
cpu_id = RT_SCHED_CTX(tid).oncpu;
if ((cpu_id != RT_CPU_DETACHED) && (cpu_id != rt_hw_cpu_id()))
{
rt_uint32_t cpu_mask;
cpu_mask = RT_CPU_MASK ^ (1 << cpu_id);
rt_hw_ipi_send(RT_SCHEDULE_IPI, cpu_mask);
}
}
#else
/* point to the signal handle entry */
RT_SCHED_CTX(tid).stat &= ~RT_THREAD_STAT_SIGNAL_PENDING;
tid->sig_ret = tid->sp;
tid->sp = rt_hw_stack_init((void *)_signal_entry, RT_NULL,
(void *)((char *)tid->sig_ret - 32), RT_NULL);
#endif /* RT_USING_SMP */
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
LOG_D("signal stack pointer @ 0x%08x", tid->sp);
/* re-schedule */
rt_schedule();
}
else
{
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
}
}
}
#ifdef RT_USING_SMP
void *rt_signal_check(void* context)
{
rt_sched_lock_level_t level;
int cpu_id;
struct rt_cpu* pcpu;
struct rt_thread *current_thread;
level = rt_spin_lock_irqsave(&_thread_signal_lock);
cpu_id = rt_hw_cpu_id();
pcpu = rt_cpu_index(cpu_id);
current_thread = pcpu->current_thread;
if (pcpu->irq_nest)
{
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
return context;
}
if (current_thread->cpus_lock_nest == 1)
{
if (RT_SCHED_CTX(current_thread).stat & RT_THREAD_STAT_SIGNAL_PENDING)
{
void *sig_context;
RT_SCHED_CTX(current_thread).stat &= ~RT_THREAD_STAT_SIGNAL_PENDING;
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
sig_context = rt_hw_stack_init((void *)_signal_entry, context,
(void*)((char*)context - 32), RT_NULL);
return sig_context;
}
}
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
return context;
}
#endif /* RT_USING_SMP */
/**
* @brief This function will install a processing function to a specific
* signal and return the old processing function of this signal.
*
* @note This function needs to be used in conjunction with the
* rt_signal_unmask() function to make the signal effective.
*
* @see rt_signal_unmask()
*
* @param signo is a specific signal value (range: 0 ~ RT_SIG_MAX).
*
* @param handler is sets the processing of signal value.
*
* @return Return the old processing function of this signal. ONLY When the
* return value is SIG_ERR, the operation is failed.
*/
rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler)
{
rt_base_t level;
rt_sighandler_t old = RT_NULL;
rt_thread_t tid = rt_thread_self();
if (!sig_valid(signo)) return SIG_ERR;
level = rt_spin_lock_irqsave(&_thread_signal_lock);
if (tid->sig_vectors == RT_NULL)
{
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
rt_thread_alloc_sig(tid);
level = rt_spin_lock_irqsave(&_thread_signal_lock);
}
if (tid->sig_vectors)
{
old = tid->sig_vectors[signo];
if (handler == SIG_IGN) tid->sig_vectors[signo] = RT_NULL;
else if (handler == SIG_DFL) tid->sig_vectors[signo] = _signal_default_handler;
else tid->sig_vectors[signo] = handler;
}
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
return old;
}
/**
* @brief This function will block the specified signal.
*
* @note This function will block the specified signal, even if the
* rt_thread_kill() function is called to send this signal to
* the current thread, it will no longer take effect.
*
* @see rt_thread_kill()
*
* @param signo is a specific signal value (range: 0 ~ RT_SIG_MAX).
*/
void rt_signal_mask(int signo)
{
rt_base_t level;
rt_thread_t tid = rt_thread_self();
level = rt_spin_lock_irqsave(&_thread_signal_lock);
tid->sig_mask &= ~sig_mask(signo);
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
}
/**
* @brief This function will unblock the specified signal.
*
* @note This function will unblock the specified signal. After calling
* the rt_thread_kill() function to send this signal to the current
* thread, it will take effect.
*
* @see rt_thread_kill()
*
* @param signo is a specific signal value (range: 0 ~ RT_SIG_MAX).
*/
void rt_signal_unmask(int signo)
{
rt_base_t level;
rt_thread_t tid = rt_thread_self();
level = rt_spin_lock_irqsave(&_thread_signal_lock);
tid->sig_mask |= sig_mask(signo);
/* let thread handle pended signals */
if (tid->sig_mask & tid->sig_pending)
{
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
_signal_deliver(tid);
}
else
{
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
}
}
/**
* @brief This function will wait for the arrival of the set signal. If it does not wait for this signal, the thread will be
* suspended until it waits for this signal or the waiting time exceeds the specified timeout: timeout.
*
* @param set is the set of signal values to be waited for. Use the function
* sigaddset() to add the signal.
*
* @param si is a pointer to the received signal info. If you don't care about this value, you can use RT_NULL to set.
*
* @param timeout is a timeout period (unit: an OS tick).
*
* @return Return the operation status. When the return value is RT_EOK, the operation is successful.
* If the return value is any other values, it means that the signal wait failed.
*/
int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t *si, rt_int32_t timeout)
{
int ret = RT_EOK;
rt_base_t level;
rt_thread_t tid = rt_thread_self();
struct siginfo_node *si_node = RT_NULL, *si_prev = RT_NULL;
/* current context checking */
RT_DEBUG_IN_THREAD_CONTEXT;
/* parameters check */
if (set == NULL || *set == 0 || si == NULL )
{
ret = -RT_EINVAL;
goto __done_return;
}
/* clear siginfo to avoid unknown value */
memset(si, 0x0, sizeof(rt_siginfo_t));
level = rt_spin_lock_irqsave(&_thread_signal_lock);
/* already pending */
if (tid->sig_pending & *set) goto __done;
if (timeout == 0)
{
ret = -RT_ETIMEOUT;
goto __done_int;
}
/* suspend self thread */
rt_thread_suspend_with_flag(tid, RT_UNINTERRUPTIBLE);
/* set thread stat as waiting for signal */
RT_SCHED_CTX(tid).stat |= RT_THREAD_STAT_SIGNAL_WAIT;
/* start timeout timer */
if (timeout != RT_WAITING_FOREVER)
{
/* reset the timeout of thread timer and start it */
rt_timer_control(&(tid->thread_timer),
RT_TIMER_CTRL_SET_TIME,
&timeout);
rt_timer_start(&(tid->thread_timer));
}
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
/* do thread scheduling */
rt_schedule();
level = rt_spin_lock_irqsave(&_thread_signal_lock);
/* remove signal waiting flag */
RT_SCHED_CTX(tid).stat &= ~RT_THREAD_STAT_SIGNAL_WAIT;
/* check errno of thread */
if (tid->error == -RT_ETIMEOUT)
{
tid->error = RT_EOK;
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
/* timer timeout */
ret = -RT_ETIMEOUT;
goto __done_return;
}
__done:
/* to get the first matched pending signals */
si_node = (struct siginfo_node *)tid->si_list;
while (si_node)
{
int signo;
signo = si_node->si.si_signo;
if (sig_mask(signo) & *set)
{
*si = si_node->si;
LOG_D("sigwait: %d sig raised!", signo);
if (si_prev) si_prev->list.next = si_node->list.next;
else
{
struct siginfo_node *node_next;
if (si_node->list.next)
{
node_next = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list);
tid->si_list = node_next;
}
else
{
tid->si_list = RT_NULL;
}
}
/* clear pending */
tid->sig_pending &= ~sig_mask(signo);
rt_mp_free(si_node);
break;
}
si_prev = si_node;
if (si_node->list.next)
{
si_node = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list);
}
else
{
si_node = RT_NULL;
}
}
__done_int:
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
__done_return:
return ret;
}
void rt_thread_handle_sig(rt_bool_t clean_state)
{
rt_base_t level;
rt_thread_t tid = rt_thread_self();
struct siginfo_node *si_node;
level = rt_spin_lock_irqsave(&_thread_signal_lock);
if (tid->sig_pending & tid->sig_mask)
{
/* if thread is not waiting for signal */
if (!(RT_SCHED_CTX(tid).stat & RT_THREAD_STAT_SIGNAL_WAIT))
{
while (tid->sig_pending & tid->sig_mask)
{
int signo, error;
rt_sighandler_t handler;
si_node = (struct siginfo_node *)tid->si_list;
if (!si_node) break;
/* remove this sig info node from list */
if (si_node->list.next == RT_NULL)
tid->si_list = RT_NULL;
else
tid->si_list = (void *)rt_slist_entry(si_node->list.next, struct siginfo_node, list);
signo = si_node->si.si_signo;
handler = tid->sig_vectors[signo];
tid->sig_pending &= ~sig_mask(signo);
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
LOG_D("handle signal: %d, handler 0x%08x", signo, handler);
if (handler) handler(signo);
level = rt_spin_lock_irqsave(&_thread_signal_lock);
error = -RT_EINTR;
rt_mp_free(si_node); /* release this siginfo node */
/* set errno in thread tcb */
tid->error = error;
}
/* whether clean signal status */
if (clean_state == RT_TRUE)
{
RT_SCHED_CTX(tid).stat &= ~RT_THREAD_STAT_SIGNAL;
}
else
{
return;
}
}
}
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
}
void rt_thread_alloc_sig(rt_thread_t tid)
{
int index;
rt_bool_t need_free = RT_FALSE;
rt_base_t level;
rt_sighandler_t *vectors;
vectors = (rt_sighandler_t *)RT_KERNEL_MALLOC(sizeof(rt_sighandler_t) * RT_SIG_MAX);
RT_ASSERT(vectors != RT_NULL);
for (index = 0; index < RT_SIG_MAX; index ++)
{
vectors[index] = _signal_default_handler;
}
level = rt_spin_lock_irqsave(&_thread_signal_lock);
if (tid->sig_vectors == RT_NULL)
{
tid->sig_vectors = vectors;
}
else
{
need_free = RT_TRUE;
}
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
if (need_free)
{
rt_free(vectors);
}
}
void rt_thread_free_sig(rt_thread_t tid)
{
rt_base_t level;
struct siginfo_node *si_node;
rt_sighandler_t *sig_vectors;
level = rt_spin_lock_irqsave(&_thread_signal_lock);
si_node = (struct siginfo_node *)tid->si_list;
tid->si_list = RT_NULL;
sig_vectors = tid->sig_vectors;
tid->sig_vectors = RT_NULL;
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
if (si_node)
{
struct rt_slist_node *node;
struct rt_slist_node *node_to_free;
LOG_D("free signal info list");
node = &(si_node->list);
do
{
node_to_free = node;
node = node->next;
si_node = rt_slist_entry(node_to_free, struct siginfo_node, list);
rt_mp_free(si_node);
} while (node);
}
if (sig_vectors)
{
RT_KERNEL_FREE(sig_vectors);
}
}
/**
* @brief This function can be used to send any signal to any thread.
*
* @param tid is a pointer to the thread that receives the signal.
*
* @param sig is a specific signal value (range: 0 ~ RT_SIG_MAX).
*
* @return Return the operation status. When the return value is RT_EOK, the operation is successful.
* If the return value is any other values, it means that the signal send failed.
*/
int rt_thread_kill(rt_thread_t tid, int sig)
{
siginfo_t si;
rt_base_t level;
struct siginfo_node *si_node;
RT_ASSERT(tid != RT_NULL);
if (!sig_valid(sig)) return -RT_EINVAL;
LOG_I("send signal: %d", sig);
si.si_signo = sig;
si.si_code = SI_USER;
si.si_value.sival_ptr = RT_NULL;
level = rt_spin_lock_irqsave(&_thread_signal_lock);
if (tid->sig_pending & sig_mask(sig))
{
/* whether already emits this signal? */
struct rt_slist_node *node;
struct siginfo_node *entry;
si_node = (struct siginfo_node *)tid->si_list;
if (si_node)
node = (struct rt_slist_node *)&si_node->list;
else
node = RT_NULL;
/* update sig info */
for (; (node) != RT_NULL; node = node->next)
{
entry = rt_slist_entry(node, struct siginfo_node, list);
if (entry->si.si_signo == sig)
{
memcpy(&(entry->si), &si, sizeof(siginfo_t));
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
return 0;
}
}
}
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
si_node = (struct siginfo_node *) rt_mp_alloc(_siginfo_pool, 0);
if (si_node)
{
rt_slist_init(&(si_node->list));
memcpy(&(si_node->si), &si, sizeof(siginfo_t));
level = rt_spin_lock_irqsave(&_thread_signal_lock);
if (tid->si_list)
{
struct siginfo_node *si_list;
si_list = (struct siginfo_node *)tid->si_list;
rt_slist_append(&(si_list->list), &(si_node->list));
}
else
{
tid->si_list = si_node;
}
/* a new signal */
tid->sig_pending |= sig_mask(sig);
rt_spin_unlock_irqrestore(&_thread_signal_lock, level);
}
else
{
LOG_E("The allocation of signal info node failed.");
return -RT_EEMPTY;
}
/* deliver signal to this thread */
_signal_deliver(tid);
return RT_EOK;
}
int rt_system_signal_init(void)
{
_siginfo_pool = rt_mp_create("signal", RT_SIG_INFO_MAX, sizeof(struct siginfo_node));
if (_siginfo_pool == RT_NULL)
{
LOG_E("create memory pool for signal info failed.");
RT_ASSERT(0);
}
return 0;
}
#endif /* RT_USING_SIGNALS */
+856
View File
@@ -0,0 +1,856 @@
/*
* Copyright (c) 2006-2021, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* File : slab.c
*
* Change Logs:
* Date Author Notes
* 2008-07-12 Bernard the first version
* 2010-07-13 Bernard fix RT_ALIGN issue found by kuronca
* 2010-10-23 yi.qiu add module memory allocator
* 2010-12-18 yi.qiu fix zone release bug
*/
/*
* KERN_SLABALLOC.C - Kernel SLAB memory allocator
*
* Copyright (c) 2003,2004 The DragonFly Project. All rights reserved.
*
* This code is derived from software contributed to The DragonFly Project
* by Matthew Dillon <dillon@backplane.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name of The DragonFly Project nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific, prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#include <rthw.h>
#include <rtthread.h>
#ifdef RT_USING_SLAB
#define DBG_TAG "kernel.slab"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
/*
* slab allocator implementation
*
* A slab allocator reserves a ZONE for each chunk size, then lays the
* chunks out in an array within the zone. Allocation and deallocation
* is nearly instantanious, and fragmentation/overhead losses are limited
* to a fixed worst-case amount.
*
* The downside of this slab implementation is in the chunk size
* multiplied by the number of zones. ~80 zones * 128K = 10MB of VM per cpu.
* In a kernel implementation all this memory will be physical so
* the zone size is adjusted downward on machines with less physical
* memory. The upside is that overhead is bounded... this is the *worst*
* case overhead.
*
* Slab management is done on a per-cpu basis and no locking or mutexes
* are required, only a critical section. When one cpu frees memory
* belonging to another cpu's slab manager an asynchronous IPI message
* will be queued to execute the operation. In addition, both the
* high level slab allocator and the low level zone allocator optimize
* M_ZERO requests, and the slab allocator does not have to pre initialize
* the linked list of chunks.
*
* XXX Balancing is needed between cpus. Balance will be handled through
* asynchronous IPIs primarily by reassigning the z_Cpu ownership of chunks.
*
* XXX If we have to allocate a new zone and M_USE_RESERVE is set, use of
* the new zone should be restricted to M_USE_RESERVE requests only.
*
* Alloc Size Chunking Number of zones
* 0-127 8 16
* 128-255 16 8
* 256-511 32 8
* 512-1023 64 8
* 1024-2047 128 8
* 2048-4095 256 8
* 4096-8191 512 8
* 8192-16383 1024 8
* 16384-32767 2048 8
* (if RT_MM_PAGE_SIZE is 4K the maximum zone allocation is 16383)
*
* Allocations >= zone_limit go directly to kmem.
*
* API REQUIREMENTS AND SIDE EFFECTS
*
* To operate as a drop-in replacement to the FreeBSD-4.x malloc() we
* have remained compatible with the following API requirements:
*
* + small power-of-2 sized allocations are power-of-2 aligned (kern_tty)
* + all power-of-2 sized allocations are power-of-2 aligned (twe)
* + malloc(0) is allowed and returns non-RT_NULL (ahc driver)
* + ability to allocate arbitrarily large chunks of memory
*/
#define ZALLOC_SLAB_MAGIC 0x51ab51ab
#define ZALLOC_ZONE_LIMIT (16 * 1024) /* max slab-managed alloc */
#define ZALLOC_MIN_ZONE_SIZE (32 * 1024) /* minimum zone size */
#define ZALLOC_MAX_ZONE_SIZE (128 * 1024) /* maximum zone size */
#define ZONE_RELEASE_THRESH 2 /* threshold number of zones */
/*
* Misc constants. Note that allocations that are exact multiples of
* RT_MM_PAGE_SIZE, or exceed the zone limit, fall through to the kmem module.
*/
#define MIN_CHUNK_SIZE 8 /* in bytes */
#define MIN_CHUNK_MASK (MIN_CHUNK_SIZE - 1)
/*
* Array of descriptors that describe the contents of each page
*/
#define PAGE_TYPE_FREE 0x00
#define PAGE_TYPE_SMALL 0x01
#define PAGE_TYPE_LARGE 0x02
#define btokup(addr) \
(&slab->memusage[((rt_ubase_t)(addr) - slab->heap_start) >> RT_MM_PAGE_BITS])
/**
* Base structure of slab memory object
*/
/*
* The IN-BAND zone header is placed at the beginning of each zone.
*/
struct rt_slab_zone
{
rt_uint32_t z_magic; /**< magic number for sanity check */
rt_uint32_t z_nfree; /**< total free chunks / ualloc space in zone */
rt_uint32_t z_nmax; /**< maximum free chunks */
struct rt_slab_zone *z_next; /**< zoneary[] link if z_nfree non-zero */
rt_uint8_t *z_baseptr; /**< pointer to start of chunk array */
rt_uint32_t z_uindex; /**< current initial allocation index */
rt_uint32_t z_chunksize; /**< chunk size for validation */
rt_uint32_t z_zoneindex; /**< zone index */
struct rt_slab_chunk *z_freechunk; /**< free chunk list */
};
/*
* Chunk structure for free elements
*/
struct rt_slab_chunk
{
struct rt_slab_chunk *c_next;
};
struct rt_slab_memusage
{
rt_uint32_t type: 2 ; /**< page type */
rt_uint32_t size: 30; /**< pages allocated or offset from zone */
};
/*
* slab page allocator
*/
struct rt_slab_page
{
struct rt_slab_page *next; /**< next valid page */
rt_size_t page; /**< number of page */
/* dummy */
char dummy[RT_MM_PAGE_SIZE - (sizeof(struct rt_slab_page *) + sizeof(rt_size_t))];
};
#define RT_SLAB_NZONES 72 /* number of zones */
/*
* slab object
*/
struct rt_slab
{
struct rt_memory parent; /**< inherit from rt_memory */
rt_ubase_t heap_start; /**< memory start address */
rt_ubase_t heap_end; /**< memory end address */
struct rt_slab_memusage *memusage;
struct rt_slab_zone *zone_array[RT_SLAB_NZONES]; /* linked list of zones NFree > 0 */
struct rt_slab_zone *zone_free; /* whole zones that have become free */
rt_uint32_t zone_free_cnt;
rt_uint32_t zone_size;
rt_uint32_t zone_limit;
rt_uint32_t zone_page_cnt;
struct rt_slab_page *page_list;
};
/**
* @brief Alloc memory size by page.
*
* @param m the slab memory management object.
*
* @param npages the number of pages.
*/
void *rt_slab_page_alloc(rt_slab_t m, rt_size_t npages)
{
struct rt_slab_page *b, *n;
struct rt_slab_page **prev;
struct rt_slab *slab = (struct rt_slab *)m;
if (npages == 0)
return RT_NULL;
for (prev = &slab->page_list; (b = *prev) != RT_NULL; prev = &(b->next))
{
if (b->page > npages)
{
/* splite pages */
n = b + npages;
n->next = b->next;
n->page = b->page - npages;
*prev = n;
break;
}
if (b->page == npages)
{
/* this node fit, remove this node */
*prev = b->next;
break;
}
}
return b;
}
/**
* @brief Free memory by page.
*
* @param m the slab memory management object.
*
* @param addr is the head address of first page.
*
* @param npages is the number of pages.
*/
void rt_slab_page_free(rt_slab_t m, void *addr, rt_size_t npages)
{
struct rt_slab_page *b, *n;
struct rt_slab_page **prev;
struct rt_slab *slab = (struct rt_slab *)m;
RT_ASSERT(addr != RT_NULL);
RT_ASSERT((rt_ubase_t)addr % RT_MM_PAGE_SIZE == 0);
RT_ASSERT(npages != 0);
n = (struct rt_slab_page *)addr;
for (prev = &slab->page_list; (b = *prev) != RT_NULL; prev = &(b->next))
{
RT_ASSERT(b->page > 0);
RT_ASSERT(b > n || b + b->page <= n);
if (b + b->page == n)
{
if (b + (b->page += npages) == b->next)
{
b->page += b->next->page;
b->next = b->next->next;
}
return;
}
if (b == n + npages)
{
n->page = b->page + npages;
n->next = b->next;
*prev = n;
return;
}
if (b > n + npages)
break;
}
n->page = npages;
n->next = b;
*prev = n;
}
/*
* Initialize the page allocator
*/
static void rt_slab_page_init(struct rt_slab *slab, void *addr, rt_size_t npages)
{
RT_ASSERT(addr != RT_NULL);
RT_ASSERT(npages != 0);
slab->page_list = RT_NULL;
rt_slab_page_free((rt_slab_t)(&slab->parent), addr, npages);
}
/**
* @brief This function will init slab memory management algorithm
*
* @param name is the name of the slab memory management object.
*
* @param begin_addr the beginning address of system page.
*
* @param size is the size of the memory.
*
* @return Return a pointer to the slab memory object.
*/
rt_slab_t rt_slab_init(const char *name, void *begin_addr, rt_size_t size)
{
rt_uint32_t limsize, npages;
rt_ubase_t start_addr, begin_align, end_align;
struct rt_slab *slab;
slab = (struct rt_slab *)RT_ALIGN((rt_ubase_t)begin_addr, RT_ALIGN_SIZE);
start_addr = (rt_ubase_t)slab + sizeof(*slab);
/* align begin and end addr to page */
begin_align = RT_ALIGN((rt_ubase_t)start_addr, RT_MM_PAGE_SIZE);
end_align = RT_ALIGN_DOWN((rt_ubase_t)begin_addr + size, RT_MM_PAGE_SIZE);
if (begin_align >= end_align)
{
rt_kprintf("slab init errr. wrong address[0x%x - 0x%x]\n",
(rt_ubase_t)begin_addr, (rt_ubase_t)begin_addr + size);
return RT_NULL;
}
limsize = end_align - begin_align;
npages = limsize / RT_MM_PAGE_SIZE;
LOG_D("heap[0x%x - 0x%x], size 0x%x, 0x%x pages",
begin_align, end_align, limsize, npages);
rt_memset(slab, 0, sizeof(*slab));
/* initialize slab memory object */
rt_object_init(&(slab->parent.parent), RT_Object_Class_Memory, name);
slab->parent.algorithm = "slab";
slab->parent.address = begin_align;
slab->parent.total = limsize;
slab->parent.used = 0;
slab->parent.max = 0;
slab->heap_start = begin_align;
slab->heap_end = end_align;
/* init pages */
rt_slab_page_init(slab, (void *)slab->heap_start, npages);
/* calculate zone size */
slab->zone_size = ZALLOC_MIN_ZONE_SIZE;
while (slab->zone_size < ZALLOC_MAX_ZONE_SIZE && (slab->zone_size << 1) < (limsize / 1024))
slab->zone_size <<= 1;
slab->zone_limit = slab->zone_size / 4;
if (slab->zone_limit > ZALLOC_ZONE_LIMIT)
slab->zone_limit = ZALLOC_ZONE_LIMIT;
slab->zone_page_cnt = slab->zone_size / RT_MM_PAGE_SIZE;
LOG_D("zone size 0x%x, zone page count 0x%x",
slab->zone_size, slab->zone_page_cnt);
/* allocate slab->memusage array */
limsize = npages * sizeof(struct rt_slab_memusage);
limsize = RT_ALIGN(limsize, RT_MM_PAGE_SIZE);
slab->memusage = rt_slab_page_alloc((rt_slab_t)(&slab->parent), limsize / RT_MM_PAGE_SIZE);
LOG_D("slab->memusage 0x%x, size 0x%x",
(rt_ubase_t)slab->memusage, limsize);
return &slab->parent;
}
RTM_EXPORT(rt_slab_init);
/**
* @brief This function will remove a slab object from the system.
*
* @param m the slab memory management object.
*
* @return RT_EOK
*/
rt_err_t rt_slab_detach(rt_slab_t m)
{
struct rt_slab *slab = (struct rt_slab *)m;
RT_ASSERT(slab != RT_NULL);
RT_ASSERT(rt_object_get_type(&slab->parent.parent) == RT_Object_Class_Memory);
RT_ASSERT(rt_object_is_systemobject(&slab->parent.parent));
rt_object_detach(&(slab->parent.parent));
return RT_EOK;
}
RTM_EXPORT(rt_slab_detach);
/*
* Calculate the zone index for the allocation request size and set the
* allocation request size to that particular zone's chunk size.
*/
rt_inline int zoneindex(rt_size_t *bytes)
{
/* unsigned for shift opt */
rt_ubase_t n = (rt_ubase_t)(*bytes);
if (n < 128)
{
*bytes = n = (n + 7) & ~7;
/* 8 byte chunks, 16 zones */
return (n / 8 - 1);
}
if (n < 256)
{
*bytes = n = (n + 15) & ~15;
return (n / 16 + 7);
}
if (n < 8192)
{
if (n < 512)
{
*bytes = n = (n + 31) & ~31;
return (n / 32 + 15);
}
if (n < 1024)
{
*bytes = n = (n + 63) & ~63;
return (n / 64 + 23);
}
if (n < 2048)
{
*bytes = n = (n + 127) & ~127;
return (n / 128 + 31);
}
if (n < 4096)
{
*bytes = n = (n + 255) & ~255;
return (n / 256 + 39);
}
*bytes = n = (n + 511) & ~511;
return (n / 512 + 47);
}
if (n < 16384)
{
*bytes = n = (n + 1023) & ~1023;
return (n / 1024 + 55);
}
rt_kprintf("Unexpected byte count %d", n);
return 0;
}
/**
* @addtogroup MM
*/
/**@{*/
/**
* @brief This function will allocate a block from slab object.
*
* @note the RT_NULL is returned if
* - the nbytes is less than zero.
* - there is no nbytes sized memory valid in system.
*
* @param m the slab memory management object.
*
* @param size is the size of memory to be allocated.
*
* @return the allocated memory.
*/
void *rt_slab_alloc(rt_slab_t m, rt_size_t size)
{
struct rt_slab_zone *z;
rt_int32_t zi;
struct rt_slab_chunk *chunk;
struct rt_slab_memusage *kup;
struct rt_slab *slab = (struct rt_slab *)m;
/* zero size, return RT_NULL */
if (size == 0)
return RT_NULL;
/*
* Handle large allocations directly. There should not be very many of
* these so performance is not a big issue.
*/
if (size >= slab->zone_limit)
{
size = RT_ALIGN(size, RT_MM_PAGE_SIZE);
chunk = rt_slab_page_alloc(m, size >> RT_MM_PAGE_BITS);
if (chunk == RT_NULL)
return RT_NULL;
/* set kup */
kup = btokup(chunk);
kup->type = PAGE_TYPE_LARGE;
kup->size = size >> RT_MM_PAGE_BITS;
LOG_D("alloc a large memory 0x%x, page cnt %d, kup %d",
size,
size >> RT_MM_PAGE_BITS,
((rt_ubase_t)chunk - slab->heap_start) >> RT_MM_PAGE_BITS);
/* mem stat */
slab->parent.used += size;
if (slab->parent.used > slab->parent.max)
slab->parent.max = slab->parent.used;
return chunk;
}
/*
* Attempt to allocate out of an existing zone. First try the free list,
* then allocate out of unallocated space. If we find a good zone move
* it to the head of the list so later allocations find it quickly
* (we might have thousands of zones in the list).
*
* Note: zoneindex() will panic of size is too large.
*/
zi = zoneindex(&size);
RT_ASSERT(zi < RT_SLAB_NZONES);
LOG_D("try to alloc 0x%x on zone: %d", size, zi);
if ((z = slab->zone_array[zi]) != RT_NULL)
{
RT_ASSERT(z->z_nfree > 0);
/* Remove us from the zone_array[] when we become full */
if (--z->z_nfree == 0)
{
slab->zone_array[zi] = z->z_next;
z->z_next = RT_NULL;
}
/*
* No chunks are available but nfree said we had some memory, so
* it must be available in the never-before-used-memory area
* governed by uindex. The consequences are very serious if our zone
* got corrupted so we use an explicit rt_kprintf rather then a KASSERT.
*/
if (z->z_uindex + 1 != z->z_nmax)
{
z->z_uindex = z->z_uindex + 1;
chunk = (struct rt_slab_chunk *)(z->z_baseptr + z->z_uindex * size);
}
else
{
/* find on free chunk list */
chunk = z->z_freechunk;
/* remove this chunk from list */
z->z_freechunk = z->z_freechunk->c_next;
}
/* mem stats */
slab->parent.used += z->z_chunksize;
if (slab->parent.used > slab->parent.max)
slab->parent.max = slab->parent.used;
return chunk;
}
/*
* If all zones are exhausted we need to allocate a new zone for this
* index.
*
* At least one subsystem, the tty code (see CROUND) expects power-of-2
* allocations to be power-of-2 aligned. We maintain compatibility by
* adjusting the base offset below.
*/
{
rt_uint32_t off;
if ((z = slab->zone_free) != RT_NULL)
{
/* remove zone from free zone list */
slab->zone_free = z->z_next;
-- slab->zone_free_cnt;
}
else
{
/* allocate a zone from page */
z = rt_slab_page_alloc(m, slab->zone_size / RT_MM_PAGE_SIZE);
if (z == RT_NULL)
{
return RT_NULL;
}
LOG_D("alloc a new zone: 0x%x",
(rt_ubase_t)z);
/* set message usage */
for (off = 0, kup = btokup(z); off < slab->zone_page_cnt; off ++)
{
kup->type = PAGE_TYPE_SMALL;
kup->size = off;
kup ++;
}
}
/* clear to zero */
rt_memset(z, 0, sizeof(struct rt_slab_zone));
/* offset of slab zone struct in zone */
off = sizeof(struct rt_slab_zone);
/*
* Guarentee power-of-2 alignment for power-of-2-sized chunks.
* Otherwise just 8-byte align the data.
*/
if ((size | (size - 1)) + 1 == (size << 1))
off = (off + size - 1) & ~(size - 1);
else
off = (off + MIN_CHUNK_MASK) & ~MIN_CHUNK_MASK;
z->z_magic = ZALLOC_SLAB_MAGIC;
z->z_zoneindex = zi;
z->z_nmax = (slab->zone_size - off) / size;
z->z_nfree = z->z_nmax - 1;
z->z_baseptr = (rt_uint8_t *)z + off;
z->z_uindex = 0;
z->z_chunksize = size;
chunk = (struct rt_slab_chunk *)(z->z_baseptr + z->z_uindex * size);
/* link to zone array */
z->z_next = slab->zone_array[zi];
slab->zone_array[zi] = z;
/* mem stats */
slab->parent.used += z->z_chunksize;
if (slab->parent.used > slab->parent.max)
slab->parent.max = slab->parent.used;
}
return chunk;
}
RTM_EXPORT(rt_slab_alloc);
/**
* @brief This function will change the size of previously allocated memory block.
*
* @param m the slab memory management object.
*
* @param ptr is the previously allocated memory block.
*
* @param size is the new size of memory block.
*
* @return the allocated memory.
*/
void *rt_slab_realloc(rt_slab_t m, void *ptr, rt_size_t size)
{
void *nptr;
struct rt_slab_zone *z;
struct rt_slab_memusage *kup;
struct rt_slab *slab = (struct rt_slab *)m;
if (ptr == RT_NULL)
return rt_slab_alloc(m, size);
if (size == 0)
{
rt_slab_free(m, ptr);
return RT_NULL;
}
/*
* Get the original allocation's zone. If the new request winds up
* using the same chunk size we do not have to do anything.
*/
kup = btokup((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK);
if (kup->type == PAGE_TYPE_LARGE)
{
rt_size_t osize;
osize = kup->size << RT_MM_PAGE_BITS;
if ((nptr = rt_slab_alloc(m, size)) == RT_NULL)
return RT_NULL;
rt_memcpy(nptr, ptr, size > osize ? osize : size);
rt_slab_free(m, ptr);
return nptr;
}
else if (kup->type == PAGE_TYPE_SMALL)
{
z = (struct rt_slab_zone *)(((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK) -
kup->size * RT_MM_PAGE_SIZE);
RT_ASSERT(z->z_magic == ZALLOC_SLAB_MAGIC);
zoneindex(&size);
if (z->z_chunksize == size)
return (ptr); /* same chunk */
/*
* Allocate memory for the new request size. Note that zoneindex has
* already adjusted the request size to the appropriate chunk size, which
* should optimize our bcopy(). Then copy and return the new pointer.
*/
if ((nptr = rt_slab_alloc(m, size)) == RT_NULL)
return RT_NULL;
rt_memcpy(nptr, ptr, size > z->z_chunksize ? z->z_chunksize : size);
rt_slab_free(m, ptr);
return nptr;
}
return RT_NULL;
}
RTM_EXPORT(rt_slab_realloc);
/**
* @brief This function will release the previous allocated memory block by rt_slab_alloc.
*
* @note The released memory block is taken back to system heap.
*
* @param m the slab memory management object.
* @param ptr is the address of memory which will be released
*/
void rt_slab_free(rt_slab_t m, void *ptr)
{
struct rt_slab_zone *z;
struct rt_slab_chunk *chunk;
struct rt_slab_memusage *kup;
struct rt_slab *slab = (struct rt_slab *)m;
/* free a RT_NULL pointer */
if (ptr == RT_NULL)
return ;
/* get memory usage */
#if (DBG_LVL == DBG_LOG)
{
rt_ubase_t addr = ((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK);
LOG_D("free a memory 0x%x and align to 0x%x, kup index %d",
(rt_ubase_t)ptr,
(rt_ubase_t)addr,
((rt_ubase_t)(addr) - slab->heap_start) >> RT_MM_PAGE_BITS);
}
#endif /* DBG_LVL == DBG_LOG */
kup = btokup((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK);
/* release large allocation */
if (kup->type == PAGE_TYPE_LARGE)
{
rt_ubase_t size;
/* clear page counter */
size = kup->size;
kup->size = 0;
/* mem stats */
slab->parent.used -= size * RT_MM_PAGE_SIZE;
LOG_D("free large memory block 0x%x, page count %d",
(rt_ubase_t)ptr, size);
/* free this page */
rt_slab_page_free(m, ptr, size);
return;
}
/* zone case. get out zone. */
z = (struct rt_slab_zone *)(((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK) -
kup->size * RT_MM_PAGE_SIZE);
RT_ASSERT(z->z_magic == ZALLOC_SLAB_MAGIC);
chunk = (struct rt_slab_chunk *)ptr;
chunk->c_next = z->z_freechunk;
z->z_freechunk = chunk;
/* mem stats */
slab->parent.used -= z->z_chunksize;
/*
* Bump the number of free chunks. If it becomes non-zero the zone
* must be added back onto the appropriate list.
*/
if (z->z_nfree++ == 0)
{
z->z_next = slab->zone_array[z->z_zoneindex];
slab->zone_array[z->z_zoneindex] = z;
}
/*
* If the zone becomes totally free, and there are other zones we
* can allocate from, move this zone to the FreeZones list. Since
* this code can be called from an IPI callback, do *NOT* try to mess
* with kernel_map here. Hysteresis will be performed at malloc() time.
*/
if (z->z_nfree == z->z_nmax &&
(z->z_next || slab->zone_array[z->z_zoneindex] != z))
{
struct rt_slab_zone **pz;
LOG_D("free zone 0x%x",
(rt_ubase_t)z, z->z_zoneindex);
/* remove zone from zone array list */
for (pz = &slab->zone_array[z->z_zoneindex]; z != *pz; pz = &(*pz)->z_next)
;
*pz = z->z_next;
/* reset zone */
z->z_magic = RT_UINT32_MAX;
/* insert to free zone list */
z->z_next = slab->zone_free;
slab->zone_free = z;
++ slab->zone_free_cnt;
/* release zone to page allocator */
if (slab->zone_free_cnt > ZONE_RELEASE_THRESH)
{
register rt_uint32_t i;
z = slab->zone_free;
slab->zone_free = z->z_next;
-- slab->zone_free_cnt;
/* set message usage */
for (i = 0, kup = btokup(z); i < slab->zone_page_cnt; i ++)
{
kup->type = PAGE_TYPE_FREE;
kup->size = 0;
kup ++;
}
/* release pages */
rt_slab_page_free(m, z, slab->zone_size / RT_MM_PAGE_SIZE);
return;
}
}
}
RTM_EXPORT(rt_slab_free);
#endif /* RT_USING_SLAB */
File diff suppressed because it is too large. Load diff
+941
View File
@@ -0,0 +1,941 @@
/*
* Copyright (c) 2006-2022, RT-Thread Development Team
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2006-03-12 Bernard first version
* 2006-04-29 Bernard implement thread timer
* 2006-06-04 Bernard implement rt_timer_control
* 2006-08-10 Bernard fix the periodic timer bug
* 2006-09-03 Bernard implement rt_timer_detach
* 2009-11-11 LiJin add soft timer
* 2010-05-12 Bernard fix the timer check bug.
* 2010-11-02 Charlie re-implement tick overflow issue
* 2012-12-15 Bernard fix the next timeout issue in soft timer
* 2014-07-12 Bernard does not lock scheduler when invoking soft-timer
* timeout function.
* 2021-08-15 supperthomas add the comment
* 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to timer.c
* 2022-04-19 Stanley Correct descriptions
* 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable
* 2024-01-25 Shell add RT_TIMER_FLAG_THREAD_TIMER for timer to sync with sched
*/
#include <rtthread.h>
#include <rthw.h>
#define DBG_TAG "kernel.timer"
#define DBG_LVL DBG_INFO
#include <rtdbg.h>
/* hard timer list */
static rt_list_t _timer_list[RT_TIMER_SKIP_LIST_LEVEL];
static struct rt_spinlock _htimer_lock;
#ifdef RT_USING_TIMER_SOFT
#ifndef RT_TIMER_THREAD_STACK_SIZE
#define RT_TIMER_THREAD_STACK_SIZE 512
#endif /* RT_TIMER_THREAD_STACK_SIZE */
#ifndef RT_TIMER_THREAD_PRIO
#define RT_TIMER_THREAD_PRIO 0
#endif /* RT_TIMER_THREAD_PRIO */
/* soft timer list */
static rt_list_t _soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
static struct rt_spinlock _stimer_lock;
static struct rt_thread _timer_thread;
static struct rt_semaphore _soft_timer_sem;
rt_align(RT_ALIGN_SIZE)
static rt_uint8_t _timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE];
#endif /* RT_USING_TIMER_SOFT */
#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR)
extern void (*rt_object_take_hook)(struct rt_object *object);
extern void (*rt_object_put_hook)(struct rt_object *object);
static void (*rt_timer_enter_hook)(struct rt_timer *timer);
static void (*rt_timer_exit_hook)(struct rt_timer *timer);
/**
* @addtogroup Hook
*/
/**@{*/
/**
* @brief This function will set a hook function on timer,
* which will be invoked when enter timer timeout callback function.
*
* @param hook is the function point of timer
*/
void rt_timer_enter_sethook(void (*hook)(struct rt_timer *timer))
{
rt_timer_enter_hook = hook;
}
/**
* @brief This function will set a hook function, which will be
* invoked when exit timer timeout callback function.
*
* @param hook is the function point of timer
*/
void rt_timer_exit_sethook(void (*hook)(struct rt_timer *timer))
{
rt_timer_exit_hook = hook;
}
/**@}*/
#endif /* RT_USING_HOOK */
rt_inline struct rt_spinlock* _timerlock_idx(struct rt_timer *timer)
{
#ifdef RT_USING_TIMER_SOFT
if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
{
return &_stimer_lock;
}
else
#endif /* RT_USING_TIMER_SOFT */
{
return &_htimer_lock;
}
}
/**
* @brief [internal] The init funtion of timer
*
* The internal called function of rt_timer_init
*
* @see rt_timer_init
*
* @param timer is timer object
*
* @param timeout is the timeout function
*
* @param parameter is the parameter of timeout function
*
* @param time is the tick of timer
*
* @param flag the flag of timer
*/
static void _timer_init(rt_timer_t timer,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
{
int i;
/* set flag */
timer->parent.flag = flag;
/* set deactivated */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
timer->timeout_func = timeout;
timer->parameter = parameter;
timer->timeout_tick = 0;
timer->init_tick = time;
/* initialize timer list */
for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
rt_list_init(&(timer->row[i]));
}
}
/**
* @brief Find the next emtpy timer ticks
*
* @param timer_list is the array of time list
*
* @param timeout_tick is the next timer's ticks
*
* @return Return the operation status. If the return value is RT_EOK, the function is successfully executed.
* If the return value is any other values, it means this operation failed.
*/
static rt_err_t _timer_list_next_timeout(rt_list_t timer_list[], rt_tick_t *timeout_tick)
{
struct rt_timer *timer;
if (!rt_list_isempty(&timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
{
timer = rt_list_entry(timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
*timeout_tick = timer->timeout_tick;
return RT_EOK;
}
return -RT_ERROR;
}
/**
* @brief Remove the timer
*
* @param timer the point of the timer
*/
rt_inline void _timer_remove(rt_timer_t timer)
{
int i;
for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
rt_list_remove(&timer->row[i]);
}
}
#if (DBG_LVL == DBG_LOG)
/**
* @brief The number of timer
*
* @param timer the head of timer
*
* @return count of timer
*/
static int _timer_count_height(struct rt_timer *timer)
{
int i, cnt = 0;
for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
if (!rt_list_isempty(&timer->row[i]))
cnt++;
}
return cnt;
}
/**
* @brief dump the all timer information
*
* @param timer_heads the head of timer
*/
void rt_timer_dump(rt_list_t timer_heads[])
{
rt_list_t *list;
for (list = timer_heads[RT_TIMER_SKIP_LIST_LEVEL - 1].next;
list != &timer_heads[RT_TIMER_SKIP_LIST_LEVEL - 1];
list = list->next)
{
struct rt_timer *timer = rt_list_entry(list,
struct rt_timer,
row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
rt_kprintf("%d", _timer_count_height(timer));
}
rt_kprintf("\n");
}
#endif /* (DBG_LVL == DBG_LOG) */
/**
* @addtogroup Clock
*/
/**@{*/
/**
* @brief This function will initialize a timer
* normally this function is used to initialize a static timer object.
*
* @param timer is the point of timer
*
* @param name is a pointer to the name of the timer
*
* @param timeout is the callback of timer
*
* @param parameter is the param of the callback
*
* @param time is timeout ticks of timer
*
* NOTE: The max timeout tick should be no more than (RT_TICK_MAX/2 - 1).
*
* @param flag is the flag of timer
*
*/
void rt_timer_init(rt_timer_t timer,
const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
{
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(timeout != RT_NULL);
RT_ASSERT(time < RT_TICK_MAX / 2);
/* timer object initialization */
rt_object_init(&(timer->parent), RT_Object_Class_Timer, name);
_timer_init(timer, timeout, parameter, time, flag);
}
RTM_EXPORT(rt_timer_init);
/**
* @brief This function will detach a timer from timer management.
*
* @param timer is the timer to be detached
*
* @return the status of detach
*/
rt_err_t rt_timer_detach(rt_timer_t timer)
{
rt_base_t level;
struct rt_spinlock *spinlock;
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
RT_ASSERT(rt_object_is_systemobject(&timer->parent));
spinlock = _timerlock_idx(timer);
level = rt_spin_lock_irqsave(spinlock);
_timer_remove(timer);
/* stop timer */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_spin_unlock_irqrestore(spinlock, level);
rt_object_detach(&(timer->parent));
return RT_EOK;
}
RTM_EXPORT(rt_timer_detach);
#ifdef RT_USING_HEAP
/**
* @brief This function will create a timer
*
* @param name is the name of timer
*
* @param timeout is the timeout function
*
* @param parameter is the parameter of timeout function
*
* @param time is timeout ticks of the timer
*
* NOTE: The max timeout tick should be no more than (RT_TICK_MAX/2 - 1).
*
* @param flag is the flag of timer. Timer will invoke the timeout function according to the selected values of flag, if one or more of the following flags is set.
*
* RT_TIMER_FLAG_ONE_SHOT One shot timing
* RT_TIMER_FLAG_PERIODIC Periodic timing
*
* RT_TIMER_FLAG_HARD_TIMER Hardware timer
* RT_TIMER_FLAG_SOFT_TIMER Software timer
* RT_TIMER_FLAG_THREAD_TIMER Thread timer
*
* NOTE:
* You can use multiple values with "|" logical operator. By default, system will use the RT_TIME_FLAG_HARD_TIMER.
*
* @return the created timer object
*/
rt_timer_t rt_timer_create(const char *name,
void (*timeout)(void *parameter),
void *parameter,
rt_tick_t time,
rt_uint8_t flag)
{
struct rt_timer *timer;
/* parameter check */
RT_ASSERT(timeout != RT_NULL);
RT_ASSERT(time < RT_TICK_MAX / 2);
/* allocate a object */
timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name);
if (timer == RT_NULL)
{
return RT_NULL;
}
_timer_init(timer, timeout, parameter, time, flag);
return timer;
}
RTM_EXPORT(rt_timer_create);
/**
* @brief This function will delete a timer and release timer memory
*
* @param timer the timer to be deleted
*
* @return the operation status, RT_EOK on OK; -RT_ERROR on error
*/
rt_err_t rt_timer_delete(rt_timer_t timer)
{
rt_base_t level;
struct rt_spinlock *spinlock;
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
RT_ASSERT(rt_object_is_systemobject(&timer->parent) == RT_FALSE);
spinlock = _timerlock_idx(timer);
level = rt_spin_lock_irqsave(spinlock);
_timer_remove(timer);
/* stop timer */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_spin_unlock_irqrestore(spinlock, level);
rt_object_delete(&(timer->parent));
return RT_EOK;
}
RTM_EXPORT(rt_timer_delete);
#endif /* RT_USING_HEAP */
/**
* @brief This function will start the timer
*
* @param timer the timer to be started
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
static rt_err_t _timer_start(rt_list_t *timer_list, rt_timer_t timer)
{
unsigned int row_lvl;
rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL];
unsigned int tst_nr;
static unsigned int random_nr;
if (timer->parent.flag & RT_TIMER_FLAG_PROCESSING)
{
return -RT_ERROR;
}
/* remove timer from list */
_timer_remove(timer);
/* change status of timer */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent)));
timer->timeout_tick = rt_tick_get() + timer->init_tick;
row_head[0] = &timer_list[0];
for (row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
for (; row_head[row_lvl] != timer_list[row_lvl].prev;
row_head[row_lvl] = row_head[row_lvl]->next)
{
struct rt_timer *t;
rt_list_t *p = row_head[row_lvl]->next;
/* fix up the entry pointer */
t = rt_list_entry(p, struct rt_timer, row[row_lvl]);
/* If we have two timers that timeout at the same time, it's
* preferred that the timer inserted early get called early.
* So insert the new timer to the end the the some-timeout timer
* list.
*/
if ((t->timeout_tick - timer->timeout_tick) == 0)
{
continue;
}
else if ((t->timeout_tick - timer->timeout_tick) < RT_TICK_MAX / 2)
{
break;
}
}
if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1)
row_head[row_lvl + 1] = row_head[row_lvl] + 1;
}
/* Interestingly, this super simple timer insert counter works very very
* well on distributing the list height uniformly. By means of "very very
* well", I mean it beats the randomness of timer->timeout_tick very easily
* (actually, the timeout_tick is not random and easy to be attacked). */
random_nr++;
tst_nr = random_nr;
rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - 1],
&(timer->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
for (row_lvl = 2; row_lvl <= RT_TIMER_SKIP_LIST_LEVEL; row_lvl++)
{
if (!(tst_nr & RT_TIMER_SKIP_LIST_MASK))
rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - row_lvl],
&(timer->row[RT_TIMER_SKIP_LIST_LEVEL - row_lvl]));
else
break;
/* Shift over the bits we have tested. Works well with 1 bit and 2
* bits. */
tst_nr >>= (RT_TIMER_SKIP_LIST_MASK + 1) >> 1;
}
timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED;
return RT_EOK;
}
/**
* @brief This function will start the timer
*
* @param timer the timer to be started
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_timer_start(rt_timer_t timer)
{
rt_sched_lock_level_t slvl;
int is_thread_timer = 0;
struct rt_spinlock *spinlock;
rt_list_t *timer_list;
rt_base_t level;
rt_err_t err;
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
#ifdef RT_USING_TIMER_SOFT
if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)
{
timer_list = _soft_timer_list;
spinlock = &_stimer_lock;
}
else
#endif /* RT_USING_TIMER_SOFT */
{
timer_list = _timer_list;
spinlock = &_htimer_lock;
}
if (timer->parent.flag & RT_TIMER_FLAG_THREAD_TIMER)
{
rt_thread_t thread;
is_thread_timer = 1;
rt_sched_lock(&slvl);
thread = rt_container_of(timer, struct rt_thread, thread_timer);
RT_ASSERT(rt_object_get_type(&thread->parent) == RT_Object_Class_Thread);
rt_sched_thread_timer_start(thread);
}
level = rt_spin_lock_irqsave(spinlock);
err = _timer_start(timer_list, timer);
#ifdef RT_USING_TIMER_SOFT
if (err == RT_EOK && (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER))
{
rt_sem_release(&_soft_timer_sem);
}
#endif /* RT_USING_TIMER_SOFT */
rt_spin_unlock_irqrestore(spinlock, level);
if (is_thread_timer)
{
rt_sched_unlock(slvl);
}
return err;
}
RTM_EXPORT(rt_timer_start);
/**
* @brief This function will stop the timer
*
* @param timer the timer to be stopped
*
* @return the operation status, RT_EOK on OK, -RT_ERROR on error
*/
rt_err_t rt_timer_stop(rt_timer_t timer)
{
rt_base_t level;
struct rt_spinlock *spinlock;
/* timer check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
spinlock = _timerlock_idx(timer);
level = rt_spin_lock_irqsave(spinlock);
if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
rt_spin_unlock_irqrestore(spinlock, level);
return -RT_ERROR;
}
RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(timer->parent)));
_timer_remove(timer);
/* change status */
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_spin_unlock_irqrestore(spinlock, level);
return RT_EOK;
}
RTM_EXPORT(rt_timer_stop);
/**
* @brief This function will get or set some options of the timer
*
* @param timer the timer to be get or set
* @param cmd the control command
* @param arg the argument
*
* @return the statu of control
*/
rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg)
{
struct rt_spinlock *spinlock;
rt_base_t level;
/* parameter check */
RT_ASSERT(timer != RT_NULL);
RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer);
spinlock = _timerlock_idx(timer);
level = rt_spin_lock_irqsave(spinlock);
switch (cmd)
{
case RT_TIMER_CTRL_GET_TIME:
*(rt_tick_t *)arg = timer->init_tick;
break;
case RT_TIMER_CTRL_SET_TIME:
RT_ASSERT((*(rt_tick_t *)arg) < RT_TICK_MAX / 2);
if (timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
{
_timer_remove(timer);
timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
timer->init_tick = *(rt_tick_t *)arg;
break;
case RT_TIMER_CTRL_SET_ONESHOT:
timer->parent.flag &= ~RT_TIMER_FLAG_PERIODIC;
break;
case RT_TIMER_CTRL_SET_PERIODIC:
timer->parent.flag |= RT_TIMER_FLAG_PERIODIC;
break;
case RT_TIMER_CTRL_GET_STATE:
if(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)
{
/*timer is start and run*/
*(rt_uint32_t *)arg = RT_TIMER_FLAG_ACTIVATED;
}
else
{
/*timer is stop*/
*(rt_uint32_t *)arg = RT_TIMER_FLAG_DEACTIVATED;
}
break;
case RT_TIMER_CTRL_GET_REMAIN_TIME:
*(rt_tick_t *)arg = timer->timeout_tick;
break;
case RT_TIMER_CTRL_GET_FUNC:
*(void **)arg = (void *)timer->timeout_func;
break;
case RT_TIMER_CTRL_SET_FUNC:
timer->timeout_func = (void (*)(void*))arg;
break;
case RT_TIMER_CTRL_GET_PARM:
*(void **)arg = timer->parameter;
break;
case RT_TIMER_CTRL_SET_PARM:
timer->parameter = arg;
break;
default:
break;
}
rt_spin_unlock_irqrestore(spinlock, level);
return RT_EOK;
}
RTM_EXPORT(rt_timer_control);
/**
* @brief This function will check timer list, if a timeout event happens,
* the corresponding timeout function will be invoked.
*
* @note This function shall be invoked in operating system timer interrupt.
*/
void rt_timer_check(void)
{
struct rt_timer *t;
rt_tick_t current_tick;
rt_base_t level;
rt_list_t list;
RT_ASSERT(rt_interrupt_get_nest() > 0);
LOG_D("timer check enter");
level = rt_spin_lock_irqsave(&_htimer_lock);
current_tick = rt_tick_get();
#ifdef RT_USING_SMP
/* Running on core 0 only */
if (rt_hw_cpu_id() != 0)
{
rt_spin_unlock_irqrestore(&_htimer_lock, level);
return;
}
#endif
rt_list_init(&list);
while (!rt_list_isempty(&_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
{
t = rt_list_entry(_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
/*
* It supposes that the new tick shall less than the half duration of
* tick max.
*/
if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
{
RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t));
/* remove timer from timer list firstly */
_timer_remove(t);
if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC))
{
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
t->parent.flag |= RT_TIMER_FLAG_PROCESSING;
/* add timer to temporary list */
rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
rt_spin_unlock_irqrestore(&_htimer_lock, level);
/* call timeout function */
t->timeout_func(t->parameter);
/* re-get tick */
current_tick = rt_tick_get();
RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t));
LOG_D("current tick: %d", current_tick);
level = rt_spin_lock_irqsave(&_htimer_lock);
t->parent.flag &= ~RT_TIMER_FLAG_PROCESSING;
/* Check whether the timer object is detached or started again */
if (rt_list_isempty(&list))
{
continue;
}
rt_list_remove(&(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
/* start it */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
_timer_start(_timer_list, t);
}
}
else break;
}
rt_spin_unlock_irqrestore(&_htimer_lock, level);
LOG_D("timer check leave");
}
/**
* @brief This function will return the next timeout tick in the system.
*
* @return the next timeout tick in the system
*/
rt_tick_t rt_timer_next_timeout_tick(void)
{
rt_base_t level;
rt_tick_t next_timeout = RT_TICK_MAX;
level = rt_spin_lock_irqsave(&_htimer_lock);
_timer_list_next_timeout(_timer_list, &next_timeout);
rt_spin_unlock_irqrestore(&_htimer_lock, level);
return next_timeout;
}
#ifdef RT_USING_TIMER_SOFT
/**
* @brief This function will check software-timer list, if a timeout event happens, the
* corresponding timeout function will be invoked.
*/
static void _soft_timer_check(void)
{
rt_tick_t current_tick;
struct rt_timer *t;
rt_base_t level;
rt_list_t list;
rt_list_init(&list);
LOG_D("software timer check enter");
level = rt_spin_lock_irqsave(&_stimer_lock);
while (!rt_list_isempty(&_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
{
t = rt_list_entry(_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
current_tick = rt_tick_get();
/*
* It supposes that the new tick shall less than the half duration of
* tick max.
*/
if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
{
RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t));
/* remove timer from timer list firstly */
_timer_remove(t);
if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC))
{
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
t->parent.flag |= RT_TIMER_FLAG_PROCESSING;
/* add timer to temporary list */
rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
rt_spin_unlock_irqrestore(&_stimer_lock, level);
/* call timeout function */
t->timeout_func(t->parameter);
RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t));
LOG_D("current tick: %d", current_tick);
level = rt_spin_lock_irqsave(&_stimer_lock);
t->parent.flag &= ~RT_TIMER_FLAG_PROCESSING;
/* Check whether the timer object is detached or started again */
if (rt_list_isempty(&list))
{
continue;
}
rt_list_remove(&(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1]));
if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
/* start it */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
_timer_start(_soft_timer_list, t);
}
}
else break; /* not check anymore */
}
rt_spin_unlock_irqrestore(&_stimer_lock, level);
LOG_D("software timer check leave");
}
/**
* @brief System timer thread entry
*
* @param parameter is the arg of the thread
*/
static void _timer_thread_entry(void *parameter)
{
rt_err_t ret = RT_ERROR;
rt_tick_t next_timeout;
rt_base_t level;
RT_UNUSED(parameter);
rt_sem_control(&_soft_timer_sem, RT_IPC_CMD_SET_VLIMIT, (void*)1);
while (1)
{
/* get the next timeout tick */
level = rt_spin_lock_irqsave(&_stimer_lock);
ret = _timer_list_next_timeout(_soft_timer_list, &next_timeout);
rt_spin_unlock_irqrestore(&_stimer_lock, level);
if (ret != RT_EOK)
{
rt_sem_take(&_soft_timer_sem, RT_WAITING_FOREVER);
}
else
{
rt_tick_t current_tick;
/* get current tick */
current_tick = rt_tick_get();
if ((next_timeout - current_tick) < RT_TICK_MAX / 2)
{
/* get the delta timeout tick */
next_timeout = next_timeout - current_tick;
rt_sem_take(&_soft_timer_sem, next_timeout);
}
}
/* check software timer */
_soft_timer_check();
}
}
#endif /* RT_USING_TIMER_SOFT */
/**
* @ingroup SystemInit
*
* @brief This function will initialize system timer
*/
void rt_system_timer_init(void)
{
rt_size_t i;
for (i = 0; i < sizeof(_timer_list) / sizeof(_timer_list[0]); i++)
{
rt_list_init(_timer_list + i);
}
rt_spin_lock_init(&_htimer_lock);
}
/**
* @ingroup SystemInit
*
* @brief This function will initialize system timer thread
*/
void rt_system_timer_thread_init(void)
{
#ifdef RT_USING_TIMER_SOFT
int i;
for (i = 0;
i < sizeof(_soft_timer_list) / sizeof(_soft_timer_list[0]);
i++)
{
rt_list_init(_soft_timer_list + i);
}
rt_spin_lock_init(&_stimer_lock);
rt_sem_init(&_soft_timer_sem, "stimer", 0, RT_IPC_FLAG_PRIO);
/* start software timer thread */
rt_thread_init(&_timer_thread,
"timer",
_timer_thread_entry,
RT_NULL,
&_timer_thread_stack[0],
sizeof(_timer_thread_stack),
RT_TIMER_THREAD_PRIO,
10);
/* startup */
rt_thread_startup(&_timer_thread);
#endif /* RT_USING_TIMER_SOFT */
}
/**@}*/