1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "eu/rtg_ioctl.h"
17 
18 #include <sstream>
19 #include <iomanip>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <cstring>
23 #include <climits>
24 #include <sys/syscall.h>
25 #include <sys/ioctl.h>
26 
27 #include "ffrt_trace.h"
28 #include "dfx/log/ffrt_log_api.h"
29 
30 constexpr int RTG_FRAME_START = 1;
31 constexpr int RTG_FRAME_END = 2;
32 
33 constexpr int RTG_IPC_MAGIC = 0XBC;
34 constexpr int RTG_IPC_CMDID = 0xCD;
35 #define RTG_IPC_CMD _IOWR(RTG_IPC_MAGIC, RTG_IPC_CMDID, class RTGMsg)
36 
37 namespace ffrt {
38 enum RTGCtrlCmd {
39     CMD_CREATE_RTG,
40     CMD_RELEASE_RTG,
41     CMD_ADD_RTG_THREAD,
42     CMD_DEL_RTG_THREAD,
43     CMD_SET_GROUP_UTIL,
44     CMD_SET_GROUP_FREQ,
45     CMD_GET_THREAD_LOAD,
46     CMD_GET_GROUP_LOAD,
47     CMD_SET_GROUP_WINDOW_SIZE,
48     CMD_SET_GROUP_WINDOW_ROLLOVER,
49     CMD_SET_PREFERRED_CLUSTER,
50     CMD_SET_INVALID_INTERVAL,
51     CMD_ID_MAX,
52 };
53 
FromatRTGCtrlCmd(uint32_t cmd)54 static const char* FromatRTGCtrlCmd(uint32_t cmd)
55 {
56     static const char* str[] = {
57         "CMD_CREATE_RTG",
58         "CMD_RELEASE_RTG",
59         "CMD_ADD_RTG_THREAD",
60         "CMD_DEL_RTG_THREAD",
61         "CMD_SET_GROUP_UTIL",
62         "CMD_SET_GROUP_FREQ",
63         "CMD_GET_THREAD_LOAD",
64         "CMD_GET_GROUP_LOAD",
65         "CMD_SET_GROUP_WINDOW_SIZE",
66         "CMD_SET_GROUP_WINDOW_ROLLOVER",
67         "CMD_SET_PREFERRED_CLUSTER",
68         "CMD_SET_INVALID_INTERVAL",
69     };
70 
71     if (cmd >= CMD_ID_MAX) {
72         return "Unknown";
73     }
74 
75     return str[cmd];
76 }
77 
78 class RTGCtrl::RTGMsg {
79 public:
Build(uint32_t cmd=0,int32_t tgid=0,int64_t data=0)80     static RTGMsg Build(uint32_t cmd = 0, int32_t tgid = 0, int64_t data = 0)
81     {
82         return RTGMsg(cmd, tgid, data);
83     }
84 
Cmd(uint32_t var)85     RTGMsg& Cmd(uint32_t var)
86     {
87         this->cmd = var;
88         return *this;
89     }
90 
TGid(int32_t var)91     RTGMsg& TGid(int32_t var)
92     {
93         this->tgid = var;
94         return *this;
95     }
96 
Data(int64_t var)97     RTGMsg& Data(int64_t var)
98     {
99         this->data = var;
100         return *this;
101     }
102 
InSize(uint32_t var)103     RTGMsg& InSize(uint32_t var)
104     {
105         this->in_size = var;
106         return *this;
107     }
108 
OutSize(uint32_t var)109     RTGMsg& OutSize(uint32_t var)
110     {
111         this->out_size = var;
112         return *this;
113     }
114 
In(void * var)115     RTGMsg& In(void* var)
116     {
117         this->in = var;
118         return *this;
119     }
120 
Out(void * var)121     RTGMsg& Out(void* var)
122     {
123         this->out = var;
124         return *this;
125     }
126 
Format() const127     std::string Format() const
128     {
129         std::stringstream ss;
130 
131         auto formatBuf = [&](const char* head, const char* buf, uint32_t size) {
132             if (!buf || size == 0) {
133                 return;
134             }
135 
136             ss << head;
137             for (uint32_t i = 0; i < size; ++i) {
138                 ss << static_cast<int>(buf[i]) << " ";
139             }
140         };
141 
142         ss << "cmd: " << FromatRTGCtrlCmd(cmd);
143         ss << " tgid: " << tgid;
144         ss << " data: " << data;
145 
146         ss << std::hex << std::uppercase << std::setfill('0') << std::setw(2);
147 
148         formatBuf(" in data: ", static_cast<const char*>(in), in_size);
149         formatBuf(" out data: ", static_cast<const char*>(out), out_size);
150 
151         return ss.str();
152     }
153 
154 private:
RTGMsg(uint32_t cmd,int32_t tgid,int64_t data)155     RTGMsg(uint32_t cmd, int32_t tgid, int64_t data)
156         : cmd(cmd), tgid(tgid), data(data), in_size(0), out_size(0), in(nullptr), out(nullptr)
157     {
158     }
159 
160     uint32_t cmd;
161     int32_t tgid;
162     int64_t data;
163     uint32_t in_size;
164     uint32_t out_size;
165     void* in;
166     void* out;
167 };
168 
RTGCtrl()169 RTGCtrl::RTGCtrl()
170 {
171     char filePath[PATH_MAX];
172 
173     std::string fileName = "/proc/self/ffrt";
174     if (realpath(fileName.c_str(), filePath) == nullptr) {
175         FFRT_LOGE("Invalid file Path %s", fileName.c_str());
176         return;
177     }
178 
179     fd = open(filePath, O_RDWR);
180     if (fd < 0) {
181         FFRT_LOGE("Failed to open RTG, Ret %d", fd);
182     }
183 }
184 
~RTGCtrl()185 RTGCtrl::~RTGCtrl()
186 {
187     if (fd < 0) {
188         return;
189     }
190 
191     close(fd);
192 }
193 
GetThreadGroup()194 int RTGCtrl::GetThreadGroup()
195 {
196     FFRT_TRACE_SCOPE(TRACE_LEVEL1, CREATE_RTG);
197 
198     int tgid = -1;
199     RTGMsg msg = RTGMsg::Build(CMD_CREATE_RTG).Out(&tgid).OutSize(sizeof(tgid));
200 
201     return RTGIOCtrl(msg) ? tgid : -1;
202 }
203 
PutThreadGroup(int tgid)204 bool RTGCtrl::PutThreadGroup(int tgid)
205 {
206     FFRT_TRACE_SCOPE(TRACE_LEVEL1, RELEASE_RTG);
207 
208     RTGMsg msg = RTGMsg::Build(CMD_RELEASE_RTG, tgid);
209     return RTGIOCtrl(msg);
210 }
211 
JoinThread(int tgid,pid_t tid)212 bool RTGCtrl::JoinThread(int tgid, pid_t tid)
213 {
214     FFRT_TRACE_SCOPE(TRACE_LEVEL1, ADD_RTG_THREAD);
215 
216     RTGMsg msg = RTGMsg::Build(CMD_ADD_RTG_THREAD, tgid, tid);
217     return RTGIOCtrl(msg);
218 }
219 
RemoveThread(int tgid,pid_t tid)220 bool RTGCtrl::RemoveThread(int tgid, pid_t tid)
221 {
222     FFRT_TRACE_SCOPE(TRACE_LEVEL1, DEL_RTG_THREAD);
223 
224     RTGMsg msg = RTGMsg::Build(CMD_DEL_RTG_THREAD, tgid, tid);
225     return RTGIOCtrl(msg);
226 }
227 
UpdatePerfUtil(int tgid,int util)228 bool RTGCtrl::UpdatePerfUtil(int tgid, int util)
229 {
230     FFRT_TRACE_SCOPE(TRACE_LEVEL1, SET_GROUP_UTIL);
231 
232     RTGMsg msg = RTGMsg::Build(CMD_SET_GROUP_UTIL, tgid, util);
233     return RTGIOCtrl(msg);
234 }
235 
UpdatePerfFreq(int tgid,int64_t freq)236 bool RTGCtrl::UpdatePerfFreq(int tgid, int64_t freq)
237 {
238     FFRT_TRACE_SCOPE(TRACE_LEVEL1, SET_GROUP_FREQ);
239 
240     RTGMsg msg = RTGMsg::Build(CMD_SET_GROUP_FREQ, tgid, freq);
241     return RTGIOCtrl(msg);
242 }
243 
UpdateAndGetLoad(int tgid,pid_t tid)244 RTGLoadInfo RTGCtrl::UpdateAndGetLoad(int tgid, pid_t tid)
245 {
246     FFRT_TRACE_SCOPE(TRACE_LEVEL1, GET_THREAD_LOAD);
247 
248     RTGLoadInfo info {0};
249     RTGMsg msg = RTGMsg::Build(CMD_GET_THREAD_LOAD, tgid, tid).Out(&info).OutSize(sizeof(info));
250 
251     RTGIOCtrl(msg);
252 
253     FFRT_LOGI("Get Thread Load %llu Runtime %llu", info.load, info.runtime);
254 
255     return info;
256 }
257 
UpdateAndGetLoad(int tgid)258 RTGLoadInfo RTGCtrl::UpdateAndGetLoad(int tgid)
259 {
260     FFRT_TRACE_SCOPE(TRACE_LEVEL1, GET_SERIAL_LOAD);
261 
262     RTGLoadInfo info {0};
263     RTGMsg msg = RTGMsg::Build(CMD_GET_GROUP_LOAD, tgid).Out(&info).OutSize(sizeof(info));
264 
265     RTGIOCtrl(msg);
266 
267     FFRT_LOGI("Get Serial Load %llu Runtime %llu", info.load, info.runtime);
268 
269     return info;
270 }
271 
SetGroupWindowSize(int tgid,uint64_t size)272 bool RTGCtrl::SetGroupWindowSize(int tgid, uint64_t size)
273 {
274     FFRT_TRACE_SCOPE(TRACE_LEVEL1, SET_GROUP_WINDOW_SIZE);
275 
276     RTGMsg msg = RTGMsg::Build(CMD_SET_GROUP_WINDOW_SIZE, tgid, size);
277     return RTGIOCtrl(msg);
278 }
279 
SetInvalidInterval(int tgid,uint64_t interval)280 bool RTGCtrl::SetInvalidInterval(int tgid, uint64_t interval)
281 {
282     FFRT_TRACE_SCOPE(TRACE_LEVEL1, SET_INVALID_INTERVAL);
283 
284     RTGMsg msg = RTGMsg::Build(CMD_SET_INVALID_INTERVAL, tgid, interval);
285     return RTGIOCtrl(msg);
286 }
287 
Begin(int tgid)288 bool RTGCtrl::Begin(int tgid)
289 {
290     FFRT_TRACE_SCOPE(TRACE_LEVEL1, SET_GROUP_WINDOW_ROLLOVER_BEGIN);
291 
292     RTGMsg msg = RTGMsg::Build(CMD_SET_GROUP_WINDOW_ROLLOVER, tgid, RTG_FRAME_START);
293     return RTGIOCtrl(msg);
294 }
295 
End(int tgid)296 bool RTGCtrl::End(int tgid)
297 {
298     FFRT_TRACE_SCOPE(TRACE_LEVEL1, SET_GROUP_WINDOW_ROLLOVER_END);
299 
300     RTGMsg msg = RTGMsg::Build(CMD_SET_GROUP_WINDOW_ROLLOVER, tgid, RTG_FRAME_END);
301     return RTGIOCtrl(msg);
302 }
303 
SetPreferredCluster(int tgid,int clusterId)304 bool RTGCtrl::SetPreferredCluster(int tgid, int clusterId)
305 {
306     FFRT_TRACE_SCOPE(TRACE_LEVEL1, SET_PREFERRED_CLUSTER);
307 
308     RTGMsg msg = RTGMsg::Build(CMD_SET_PREFERRED_CLUSTER, tgid, clusterId);
309     return RTGIOCtrl(msg);
310 }
311 
GetTID()312 pid_t RTGCtrl::GetTID()
313 {
314     return syscall(SYS_gettid);
315 }
316 
RTGIOCtrl(RTGMsg & msg)317 bool RTGCtrl::RTGIOCtrl(RTGMsg& msg)
318 {
319     int ret = ioctl(fd, RTG_IPC_CMD, &msg);
320     if (ret < 0) {
321         FFRT_LOGE("RTG IOCtrl Failed Ret:%d, %s\n", ret, msg.Format().c_str());
322         return false;
323     }
324 
325     FFRT_LOGD("RTG IOCtrl Success %s\n", msg.Format().c_str());
326 
327     return true;
328 }
329 }; // namespace ffrt