1 /*
2 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this list of
9 * conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 * of conditions and the following disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16 * to endorse or promote products derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "perf_pmu_pri.h"
33
34 #define US_PER_SECOND 1000000
35 #define HRTIMER_DEFAULT_PERIOD_US 1000
36
37 STATIC SwPmu g_perfTimed;
38
OsPerfTimedPeriodValid(UINT32 period)39 STATIC BOOL OsPerfTimedPeriodValid(UINT32 period)
40 {
41 return period >= TIMER_PERIOD_LOWER_BOUND_US;
42 }
43
OsPerfTimedStart(VOID)44 STATIC UINT32 OsPerfTimedStart(VOID)
45 {
46 UINT32 i;
47 UINT32 cpuid = ArchCurrCpuid();
48 PerfEvent *events = &g_perfTimed.pmu.events;
49 UINT32 eventNum = events->nr;
50
51 for (i = 0; i < eventNum; i++) {
52 Event *event = &(events->per[i]);
53 event->count[cpuid] = 0;
54 }
55
56 if (cpuid != 0) { /* only need start on one core */
57 return LOS_OK;
58 }
59
60 if (hrtimer_start(&g_perfTimed.hrtimer, g_perfTimed.time, HRTIMER_MODE_REL) != 0) {
61 PRINT_ERR("Hrtimer start failed\n");
62 return LOS_NOK;
63 }
64
65 if (hrtimer_forward(&g_perfTimed.hrtimer, g_perfTimed.cfgTime) == 0) {
66 PRINT_ERR("Hrtimer forward failed\n");
67 return LOS_NOK;
68 }
69
70 g_perfTimed.time = g_perfTimed.cfgTime;
71 return LOS_OK;
72 }
73
OsPerfTimedConfig(VOID)74 STATIC UINT32 OsPerfTimedConfig(VOID)
75 {
76 UINT32 i;
77 PerfEvent *events = &g_perfTimed.pmu.events;
78 UINT32 eventNum = events->nr;
79
80 for (i = 0; i < eventNum; i++) {
81 Event *event = &(events->per[i]);
82 UINT32 period = event->period;
83 if (event->eventId == PERF_COUNT_CPU_CLOCK) {
84 if (!OsPerfTimedPeriodValid(period)) {
85 period = TIMER_PERIOD_LOWER_BOUND_US;
86 PRINT_ERR("config period invalid, should be >= 100, use default period:%u us\n", period);
87 }
88
89 g_perfTimed.cfgTime = (union ktime) {
90 .tv.sec = period / US_PER_SECOND,
91 .tv.usec = period % US_PER_SECOND
92 };
93 PRINT_INFO("hrtimer config period - sec:%d, usec:%d\n", g_perfTimed.cfgTime.tv.sec,
94 g_perfTimed.cfgTime.tv.usec);
95 return LOS_OK;
96 }
97 }
98 return LOS_NOK;
99 }
100
OsPerfTimedStop(VOID)101 STATIC UINT32 OsPerfTimedStop(VOID)
102 {
103 UINT32 ret;
104
105 if (ArchCurrCpuid() != 0) { /* only need stop on one core */
106 return LOS_OK;
107 }
108
109 ret = hrtimer_cancel(&g_perfTimed.hrtimer);
110 if (ret != 1) {
111 PRINT_ERR("Hrtimer stop failed!, 0x%x\n", ret);
112 return LOS_NOK;
113 }
114 return LOS_OK;
115 }
116
OsPerfTimedHandle(VOID)117 STATIC VOID OsPerfTimedHandle(VOID)
118 {
119 UINT32 index;
120 PerfRegs regs;
121
122 PerfEvent *events = &g_perfTimed.pmu.events;
123 UINT32 eventNum = events->nr;
124
125 (VOID)memset_s(®s, sizeof(PerfRegs), 0, sizeof(PerfRegs));
126 OsPerfFetchIrqRegs(®s);
127
128 for (index = 0; index < eventNum; index++) {
129 Event *event = &(events->per[index]);
130 OsPerfUpdateEventCount(event, 1); /* eventCount += 1 every once */
131 OsPerfHandleOverFlow(event, ®s);
132 }
133 }
134
OsPerfHrtimer(struct hrtimer * hrtimer)135 STATIC enum hrtimer_restart OsPerfHrtimer(struct hrtimer *hrtimer)
136 {
137 SMP_CALL_PERF_FUNC(OsPerfTimedHandle); /* send to all cpu to collect data */
138 return HRTIMER_RESTART;
139 }
140
OsPerfGetEventName(Event * event)141 STATIC CHAR *OsPerfGetEventName(Event *event)
142 {
143 if (event->eventId == PERF_COUNT_CPU_CLOCK) {
144 return "timed";
145 } else {
146 return "unknown";
147 }
148 }
149
OsTimedPmuInit(VOID)150 UINT32 OsTimedPmuInit(VOID)
151 {
152 UINT32 ret;
153
154 g_perfTimed.time = (union ktime) {
155 .tv.sec = 0,
156 .tv.usec = HRTIMER_DEFAULT_PERIOD_US,
157 };
158
159 hrtimer_init(&g_perfTimed.hrtimer, 1, HRTIMER_MODE_REL);
160
161 ret = hrtimer_create(&g_perfTimed.hrtimer, g_perfTimed.time, OsPerfHrtimer);
162 if (ret != LOS_OK) {
163 return ret;
164 }
165
166 g_perfTimed.pmu = (Pmu) {
167 .type = PERF_EVENT_TYPE_TIMED,
168 .config = OsPerfTimedConfig,
169 .start = OsPerfTimedStart,
170 .stop = OsPerfTimedStop,
171 .getName = OsPerfGetEventName,
172 };
173
174 (VOID)memset_s(&g_perfTimed.pmu.events, sizeof(PerfEvent), 0, sizeof(PerfEvent));
175 ret = OsPerfPmuRegister(&g_perfTimed.pmu);
176 return ret;
177 }
178