/*---------------------------------------------------------------------------- * Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved. * Description: LiteOS Perf Timed Pmu Implementation * Author: Huawei LiteOS Team * Create: 2020-07-29 * 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 "perf_pmu_pri.h" #ifdef __cplusplus #if __cplusplus extern "C" { #endif /* __cplusplus */ #endif /* __cplusplus */ #define US_PER_SECOND 1000000 #define HRTIMER_DEFAULT_PERIOD_US 1000 STATIC SwPmu g_perfTimed; STATIC BOOL OsPerfTimedPeriodValid(UINT32 period)//验证定时器中的时间周期是否合法 { return period >= TIMER_PERIOD_LOWER_BOUND_US; } STATIC UINT32 OsPerfTimedStart(VOID)//启动定时器事件 { UINT32 i; UINT32 cpuid = ArchCurrCpuid(); PerfEvent *events = &g_perfTimed.pmu.events; UINT32 eventNum = events->nr; for (i = 0; i < eventNum; i++) { Event *event = &(events->per[i]); event->count[cpuid] = 0; } if (cpuid != 0) { /* only need start on one core */ return LOS_OK; } if (hrtimer_start(&g_perfTimed.hrtimer, g_perfTimed.time, HRTIMER_MODE_REL) != 0) { PRINT_ERR("Hrtimer start failed\n"); return LOS_NOK; } if (hrtimer_forward(&g_perfTimed.hrtimer, g_perfTimed.cfgTime) == 0) { PRINT_ERR("Hrtimer forward failed\n"); return LOS_NOK; } g_perfTimed.time = g_perfTimed.cfgTime; return LOS_OK; } STATIC UINT32 OsPerfTimedConfig(VOID)//配置定时器,检验是否合法 { UINT32 i; PerfEvent *events = &g_perfTimed.pmu.events; UINT32 eventNum = events->nr; for (i = 0; i < eventNum; i++) { Event *event = &(events->per[i]); UINT32 period = event->period; if (event->eventId == PERF_COUNT_CPU_CLOCK) { if (!OsPerfTimedPeriodValid(period)) { period = TIMER_PERIOD_LOWER_BOUND_US; PRINT_ERR("config period invalid, should be >= 100, use default period:%u us\n", period); } g_perfTimed.cfgTime = (union ktime) { .tv.sec = period / US_PER_SECOND, .tv.usec = period % US_PER_SECOND }; PRINT_INFO("hrtimer config period - sec:%d, usec:%d\n", g_perfTimed.cfgTime.tv.sec, g_perfTimed.cfgTime.tv.usec); return LOS_OK; } } return LOS_NOK; } STATIC UINT32 OsPerfTimedStop(VOID)//关闭定时器设置 { UINT32 ret; if (ArchCurrCpuid() != 0) { /* only need stop on one core */ return LOS_OK; } ret = hrtimer_cancel(&g_perfTimed.hrtimer); if (ret != 1) { PRINT_ERR("Hrtimer stop failed!, 0x%x\n", ret); return LOS_NOK; } return LOS_OK; } STATIC VOID OsPerfTimedHandle(VOID)//处理定时器事件 { UINT32 index; PerfRegs regs; PerfEvent *events = &g_perfTimed.pmu.events; UINT32 eventNum = events->nr; (VOID)memset_s(®s, sizeof(PerfRegs), 0, sizeof(PerfRegs)); OsPerfFetchIrqRegs(®s);//获取当前寄存器状态 for (index = 0; index < eventNum; index++) { Event *event = &(events->per[index]); OsPerfUpdateEventCount(event, 1); /* eventCount += 1 every once */ OsPerfHandleOverFlow(event, ®s); } } STATIC enum hrtimer_restart OsPerfHrtimer(struct hrtimer *hrtimer)//高精度定时器(hrtimer)的回调函数 { SMP_CALL_PERF_FUNC(OsPerfTimedHandle); /* send to all cpu to collect data */ //将定时器事件的处理发送给所有CPU进行数据收集 return HRTIMER_RESTART; } STATIC CHAR *OsPerfGetEventName(Event *event)//获取事件名称 { if (event->eventId == PERF_COUNT_CPU_CLOCK) {//读取事件ID是否与计数器中的事件记录相同 return "timed"; } else { return "unknown"; } } UINT32 OsTimedPmuInit(VOID)//初始化定时器 { UINT32 ret; g_perfTimed.time = (union ktime) {//保存定时器信息 .tv.sec = 0, .tv.usec = HRTIMER_DEFAULT_PERIOD_US, }; hrtimer_init(&g_perfTimed.hrtimer, 1, HRTIMER_MODE_REL);//第一个参数代表定时器对象,第二个参数为时间源类型,代表的是相对时间,最后一个参数代表定时器模式,这里使用的是相对时间模式 ret = hrtimer_create(&g_perfTimed.hrtimer, g_perfTimed.time, OsPerfHrtimer);//创建定时器 if (ret != LOS_OK) { return ret; } g_perfTimed.pmu = (Pmu) { .type = PERF_EVENT_TYPE_TIMED,//此性能计数器是定时器类型 .config = OsPerfTimedConfig,//性能计数器的事件配置 .start = OsPerfTimedStart,//启动 .stop = OsPerfTimedStop,//停止 .getName = OsPerfGetEventName,//获取事件名称 }; (VOID)memset_s(&g_perfTimed.pmu.events, sizeof(PerfEvent), 0, sizeof(PerfEvent)); //执行完之后清零事件结构体,并在下面进行状态码的注册读取 ret = OsPerfPmuRegister(&g_perfTimed.pmu); return ret;//返回值为初始化状态码 } #ifdef __cplusplus #if __cplusplus } #endif /* __cplusplus */ #endif /* __cplusplus */