You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

251 lines
14 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
* Copyright (c) 2020-2021 Huawei Device Co., Ltd. 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. Neither the name of the copyright holder 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 HOLDER 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 "los_sortlink_pri.h"
#include "los_memory.h"
#include "los_exc.h"
#include "los_percpu_pri.h"
#include "los_sched_pri.h"
#include "los_mp.h"
UINT32 OsSortLinkInit(SortLinkAttribute *sortLinkHeader)
{
LOS_ListInit(&sortLinkHeader->sortLink); // 初始化排序链表头
sortLinkHeader->nodeNum = 0; // 初始化节点数量为0
return LOS_OK;
}
STATIC INLINE VOID OsAddNode2SortLink(SortLinkAttribute *sortLinkHeader, SortLinkList *sortList)
{
LOS_DL_LIST *head = (LOS_DL_LIST *)&sortLinkHeader->sortLink; // 获取排序链表头指针
if (LOS_ListEmpty(head)) { // 如果链表为空
LOS_ListHeadInsert(head, &sortList->sortLinkNode); // 将节点插入链表头
sortLinkHeader->nodeNum++; // 节点数量加1
return;
}
SortLinkList *listSorted = LOS_DL_LIST_ENTRY(head->pstNext, SortLinkList, sortLinkNode); // 获取链表中的第一个节点
if (listSorted->responseTime > sortList->responseTime) { // 如果插入节点的响应时间小于第一个节点的响应时间
LOS_ListAdd(head, &sortList->sortLinkNode); // 将节点插入链表头之前
sortLinkHeader->nodeNum++; // 节点数量加1
return;
} else if (listSorted->responseTime == sortList->responseTime) { // 如果插入节点的响应时间等于第一个节点的响应时间
LOS_ListAdd(head->pstNext, &sortList->sortLinkNode); // 将节点插入第一个节点之后
sortLinkHeader->nodeNum++; // 节点数量加1
return;
}
LOS_DL_LIST *prevNode = head->pstPrev; // 获取链表中的最后一个节点
do {
listSorted = LOS_DL_LIST_ENTRY(prevNode, SortLinkList, sortLinkNode); // 获取当前节点
if (listSorted->responseTime <= sortList->responseTime) { // 如果插入节点的响应时间小于等于当前节点的响应时间
LOS_ListAdd(prevNode, &sortList->sortLinkNode); // 将节点插入当前节点之前
sortLinkHeader->nodeNum++; // 节点数量加1
break;
}
prevNode = prevNode->pstPrev; // 继续向前遍历
} while (1);
}
/*OsSortLinkInit函数用于初始化排序链表头。在函数中它调用LOS_ListInit函数初始化排序链表头并将节点数量设置为0然后返回LOS_OK。
OsAddNode2SortLink函数用于将节点插入排序链表中。在函数中首先获取排序链表头指针。然后如果链表为空将节点插入链表头并增加节点数量
然后返回。如果链表不为空,获取链表中的第一个节点,并比较插入节点的响应时间与第一个节点的响应时间。如果插入节点的响应时间小于第一个节点的
响应时间,将节点插入链表头之前,并增加节点数量,然后返回。如果插入节点的响应时间等于第一个节点的响应时间,将节点插入第一个节点之后,
并增加节点数量,然后返回。如果插入节点的响应时间大于第一个节点的响应时间,获取链表中的最后一个节点,并从链表尾部向前遍历。在遍历过程中,
如果插入节点的响应时间小于等于当前节点的响应时间,将节点插入当前节点之前,并增加节点数量,然后退出循环。最后,返回。*/
VOID OsDeleteNodeSortLink(SortLinkAttribute *sortLinkHeader, SortLinkList *sortList)
{
LOS_ListDelete(&sortList->sortLinkNode); // 从排序链表中删除节点
SET_SORTLIST_VALUE(sortList, OS_SORT_LINK_INVALID_TIME); // 将节点的响应时间设置为无效值
sortLinkHeader->nodeNum--; // 节点数量减1
}
STATIC INLINE UINT64 OsGetSortLinkNextExpireTime(SortLinkAttribute *sortHeader, UINT64 startTime)
{
LOS_DL_LIST *head = &sortHeader->sortLink; // 获取排序链表头指针
LOS_DL_LIST *list = head->pstNext; // 获取链表中的第一个节点
if (LOS_ListEmpty(head)) { // 如果链表为空
return OS_SCHED_MAX_RESPONSE_TIME - OS_TICK_RESPONSE_PRECISION; // 返回最大响应时间减去精度
}
SortLinkList *listSorted = LOS_DL_LIST_ENTRY(list, SortLinkList, sortLinkNode); // 获取第一个节点
if (listSorted->responseTime <= (startTime + OS_TICK_RESPONSE_PRECISION)) { // 如果第一个节点的响应时间小于等于(开始时间加上精度)
return startTime + OS_TICK_RESPONSE_PRECISION; // 返回开始时间加上精度
}
return listSorted->responseTime; // 返回第一个节点的响应时间
}
STATIC Percpu *OsFindIdleCpu(UINT16 *idleCpuID)
{
Percpu *idleCpu = OsPercpuGetByID(0); // 获取CPU 0 的PerCPU结构体指针
*idleCpuID = 0; // 将空闲CPU的ID设置为0
/*OsDeleteNodeSortLink函数用于从排序链表中删除节点。在函数中它调用LOS_ListDelete函数删除节点并将节点的响应时间设置为无效值然后将节点数量减1。
OsGetSortLinkNextExpireTime函数用于获取排序链表中下一个即将到期的节点的响应时间。在函数中首先获取排序链表头指针和链表中的第一个节点。
然后,如果链表为空,返回最大响应时间减去精度。如果第一个节点的响应时间小于等于(开始时间加上精度),返回开始时间加上精度。否则,返回第一个节点的响应时间。
OsFindIdleCpu函数用于查找空闲的CPU。在函数中它通过调用OsPercpuGetByID函数获取CPU 0 的PerCPU结构体指针并将空闲CPU的ID设置为0。*/
#ifdef LOSCFG_KERNEL_SMP
UINT16 cpuID = 1; // 初始化CPU ID为1
UINT32 nodeNum = idleCpu->taskSortLink.nodeNum + idleCpu->swtmrSortLink.nodeNum; // 获取当前CPU的任务排序链表和软件定时器排序链表的节点数量之和
do {
Percpu *cpu = OsPercpuGetByID(cpuID); // 获取指定CPU的PerCPU结构体指针
UINT32 temp = cpu->taskSortLink.nodeNum + cpu->swtmrSortLink.nodeNum; // 获取指定CPU的任务排序链表和软件定时器排序链表的节点数量之和
if (nodeNum > temp) { // 如果当前CPU的节点数量之和大于指定CPU的节点数量之和
idleCpu = cpu; // 更新空闲CPU指针
*idleCpuID = cpuID; // 更新空闲CPU的ID
}
cpuID++; // 继续遍历下一个CPU
} while (cpuID < LOSCFG_KERNEL_CORE_NUM); // 遍历完所有的CPU
#endif
return idleCpu; // 返回空闲CPU的指针
}
VOID OsAdd2SortLink(SortLinkList *node, UINT64 startTime, UINT32 waitTicks, SortLinkType type)
{
UINT32 intSave;
Percpu *cpu = NULL;
SortLinkAttribute *sortLinkHeader = NULL;
SPIN_LOCK_S *spinLock = NULL;
UINT16 idleCpu;
if (OS_SCHEDULER_ACTIVE) { // 如果调度器处于活动状态
cpu = OsFindIdleCpu(&idleCpu); // 查找空闲的CPU
} else {
idleCpu = ArchCurrCpuid(); // 获取当前CPU的ID
cpu = OsPercpuGet(); // 获取当前CPU的PerCPU结构体指针
}
if (type == OS_SORT_LINK_TASK) {
sortLinkHeader = &cpu->taskSortLink; // 获取任务排序链表头指针
spinLock = &cpu->taskSortLinkSpin; // 获取任务排序链表自旋锁
} else if (type == OS_SORT_LINK_SWTMR) {
sortLinkHeader = &cpu->swtmrSortLink; // 获取软件定时器排序链表头指针
spinLock = &cpu->swtmrSortLinkSpin; // 获取软件定时器排序链表自旋锁
} else {
LOS_Panic("Sort link type error : %u\n", type); // 报错,排序链表类型错误
}
LOS_SpinLockSave(spinLock, &intSave); // 保存自旋锁状态并禁止中断
SET_SORTLIST_VALUE(node, startTime + (UINT64)waitTicks * OS_CYCLE_PER_TICK); // 设置节点的响应时间
OsAddNode2SortLink(sortLinkHeader, node); // 将节点插入排序链表中
#ifdef LOSCFG_KERNEL_SMP
node->cpuid = idleCpu; // 设置节点的CPU ID为空闲CPU的ID
if (idleCpu != ArchCurrCpuid()) { // 如果空闲CPU的ID不等于当前CPU的ID
LOS_MpSchedule(CPUID_TO_AFFI_MASK(idleCpu)); // 调度空闲CPU执行任务
}
#endif
LOS_SpinUnlockRestore(spinLock, intSave); // 恢复自旋锁状态并允许中断
}
VOID OsDeleteSortLink(SortLinkList *node, SortLinkType type)
{
UINT32 intSave;
#ifdef LOSCFG_KERNEL_SMP
Percpu *cpu = OsPercpuGetByID(node->cpuid); // 获取节点所在CPU的PerCPU结构体指针
#else
Percpu *cpu = OsPercpuGetByID(0); // 获取CPU 0 的PerCPU结构体指针
#endif
/*OsAdd2SortLink函数用于将节点插入排序链表中。在函数中首先根据调度器的活动状态选择空闲的CPU。
如果调度器处于活动状态调用OsFindIdleCpu函数查找空闲的CPU否则获取当前CPU的ID
并通过OsPercpuGet函数获取当前CPU的PerCPU结构体指针。然后根据排序链表类型选择相应的排序链表头指针和自旋锁。
接下来保存自旋锁状态并禁止中断设置节点的响应时间将节点插入排序链表中根据SMP配置设置节点的CPU ID
并如果空闲CPU的ID不等于当前CPU的ID调度空闲CPU执行任务。最后恢复自旋锁状态并允许中断。
OsDeleteSortLink函数用于从排序链表中删除节点。在函数中首先保存自旋锁状态并禁止中断。如果SMP配置开启
根据节点的CPU ID获取节点所在CPU的PerCPU结构体指针否则获取CPU 0 的PerCPU结构体指针。*/
SPIN_LOCK_S *spinLock = NULL;
SortLinkAttribute *sortLinkHeader = NULL;
if (type == OS_SORT_LINK_TASK) {
sortLinkHeader = &cpu->taskSortLink; // 获取任务排序链表头指针
spinLock = &cpu->taskSortLinkSpin; // 获取任务排序链表自旋锁
} else if (type == OS_SORT_LINK_SWTMR) {
sortLinkHeader = &cpu->swtmrSortLink; // 获取软件定时器排序链表头指针
spinLock = &cpu->swtmrSortLinkSpin; // 获取软件定时器排序链表自旋锁
} else {
LOS_Panic("Sort link type error : %u\n", type); // 报错,排序链表类型错误
}
LOS_SpinLockSave(spinLock, &intSave); // 保存自旋锁状态并禁止中断
if (node->responseTime != OS_SORT_LINK_INVALID_TIME) { // 如果节点的响应时间不为无效时间
OsDeleteNodeSortLink(sortLinkHeader, node); // 从排序链表中删除节点
}
LOS_SpinUnlockRestore(spinLock, intSave); // 恢复自旋锁状态并允许中断
}
UINT64 OsGetNextExpireTime(UINT64 startTime)
{
UINT32 intSave;
Percpu *cpu = OsPercpuGet(); // 获取当前CPU的PerCPU结构体指针
SortLinkAttribute *taskHeader = &cpu->taskSortLink; // 获取任务排序链表头指针
SortLinkAttribute *swtmrHeader = &cpu->swtmrSortLink; // 获取软件定时器排序链表头指针
LOS_SpinLockSave(&cpu->taskSortLinkSpin, &intSave); // 保存任务排序链表自旋锁状态并禁止中断
UINT64 taskExpirTime = OsGetSortLinkNextExpireTime(taskHeader, startTime); // 获取任务排序链表中下一个过期时间
LOS_SpinUnlockRestore(&cpu->taskSortLinkSpin, intSave); // 恢复任务排序链表自旋锁状态并允许中断
LOS_SpinLockSave(&cpu->swtmrSortLinkSpin, &intSave); // 保存软件定时器排序链表自旋锁状态并禁止中断
UINT64 swtmrExpirTime = OsGetSortLinkNextExpireTime(swtmrHeader, startTime); // 获取软件定时器排序链表中下一个过期时间
LOS_SpinUnlockRestore(&cpu->swtmrSortLinkSpin, intSave); // 恢复软件定时器排序链表自旋锁状态并允许中断
return (taskExpirTime < swtmrExpirTime) ? taskExpirTime : swtmrExpirTime; // 返回较小的过期时间
}
UINT32 OsSortLinkGetTargetExpireTime(const SortLinkList *targetSortList)
{
UINT64 currTimes = OsGetCurrSchedTimeCycle(); // 获取当前调度时间
if (currTimes >= targetSortList->responseTime) { // 如果当前时间大于等于目标排序链表节点的响应时间
return 0; // 返回0表示已经过期
}
return (UINT32)(targetSortList->responseTime - currTimes) / OS_CYCLE_PER_TICK; // 计算剩余的滴答数
}
UINT32 OsSortLinkGetNextExpireTime(const SortLinkAttribute *sortLinkHeader)
{
LOS_DL_LIST *head = (LOS_DL_LIST *)&sortLinkHeader->sortLink; // 获取排序链表头节点
if (LOS_ListEmpty(head)) { // 如果排序链表为空
return 0; // 返回0表示没有过期时间
}
SortLinkList *listSorted = LOS_DL_LIST_ENTRY(head->pstNext, SortLinkList, sortLinkNode); // 获取排序链表中的第一个节点
return OsSortLinkGetTargetExpireTime(listSorted); // 获取目标排序链表节点的过期时间
}