Line data Source code
1 : /**
2 : * Copyright (c) 2025 Huawei Technologies Co., Ltd.
3 : * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4 : * CANN Open Software License Agreement Version 2.0 (the "License").
5 : * Please refer to the License for details. You may not use this file except in compliance with the License.
6 : * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 : * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8 : * See LICENSE in the root of the software repository for the full text of the License.
9 : */
10 :
11 : #include "bind_cpu_utils.h"
12 : #include <sstream>
13 : #include <fcntl.h>
14 : #include <semaphore.h>
15 : #include <cstring>
16 : #include <signal.h>
17 : #include <stdlib.h>
18 : #include "qs_interface_process.h"
19 : #include "driver/ascend_hal.h"
20 : #include "common/bqs_log.h"
21 : #include "bqs_proc_mgr_sys_operator_agent.h"
22 : #include "common/bqs_feature_ctrl.h"
23 : #include "queue_schedule_feature_ctrl.h"
24 :
25 : namespace bqs {
26 : namespace {
27 : sem_t g_sem;
28 : bool g_semInited = false;
29 : }
30 :
31 13 : static drvError_t GetCpuInfo(const uint32_t deviceId, CpuInfo &cpuInfo)
32 : {
33 13 : drvError_t ret = halGetDeviceInfo(deviceId, static_cast<int32_t>(MODULE_TYPE_CCPU),
34 : static_cast<int32_t>(INFO_TYPE_CORE_NUM), &(cpuInfo.ccpuNum));
35 13 : if (ret != DRV_ERROR_NONE) {
36 1 : BQS_LOG_ERROR("get device[%u] cpu info failed, ret = %d.", deviceId, static_cast<int32_t>(ret));
37 1 : return ret;
38 : }
39 :
40 12 : ret = halGetDeviceInfo(deviceId, static_cast<int32_t>(MODULE_TYPE_DCPU),
41 : static_cast<int32_t>(INFO_TYPE_CORE_NUM), &(cpuInfo.dcpuNum));
42 12 : if (ret != DRV_ERROR_NONE) {
43 1 : BQS_LOG_ERROR("get device[%u] cpu info failed, ret = %d.", deviceId, static_cast<int32_t>(ret));
44 1 : return ret;
45 : }
46 :
47 11 : ret = halGetDeviceInfo(deviceId, static_cast<int32_t>(MODULE_TYPE_AICPU),
48 : static_cast<int32_t>(INFO_TYPE_CORE_NUM), &(cpuInfo.aicpuNum));
49 11 : if (ret != DRV_ERROR_NONE) {
50 1 : BQS_LOG_ERROR("get device[%u] cpu info failed, ret = %d.", deviceId, static_cast<int32_t>(ret));
51 1 : return ret;
52 : }
53 :
54 10 : ret = halGetDeviceInfo(deviceId, static_cast<int32_t>(MODULE_TYPE_TSCPU),
55 : static_cast<int32_t>(INFO_TYPE_CORE_NUM), &(cpuInfo.tscpuNum));
56 10 : if (ret != DRV_ERROR_NONE) {
57 1 : BQS_LOG_ERROR("get device[%u] cpu info failed, ret = %d.", deviceId, static_cast<int32_t>(ret));
58 1 : return ret;
59 : }
60 :
61 9 : ret = halGetDeviceInfo(deviceId, static_cast<int32_t>(MODULE_TYPE_CCPU),
62 : static_cast<int32_t>(INFO_TYPE_OS_SCHED), &(cpuInfo.ccpuOsSched));
63 9 : if (ret != DRV_ERROR_NONE) {
64 1 : BQS_LOG_ERROR("get device[%u] cpu info failed, ret = %d.", deviceId, static_cast<int32_t>(ret));
65 1 : return ret;
66 : }
67 :
68 8 : ret = halGetDeviceInfo(deviceId, static_cast<int32_t>(MODULE_TYPE_DCPU),
69 : static_cast<int32_t>(INFO_TYPE_OS_SCHED), &(cpuInfo.dcpuOsSched));
70 8 : if (ret != DRV_ERROR_NONE) {
71 1 : BQS_LOG_ERROR("get device[%u] cpu info failed, ret = %d.", deviceId, static_cast<int32_t>(ret));
72 1 : return ret;
73 : }
74 :
75 7 : ret = halGetDeviceInfo(deviceId, static_cast<int32_t>(MODULE_TYPE_AICPU),
76 : static_cast<int32_t>(INFO_TYPE_OS_SCHED), &(cpuInfo.aicpuOsSched));
77 7 : if (ret != DRV_ERROR_NONE) {
78 1 : BQS_LOG_ERROR("get device[%u] cpu info failed, ret = %d.", deviceId, static_cast<int32_t>(ret));
79 1 : return ret;
80 : }
81 :
82 6 : ret = halGetDeviceInfo(deviceId, static_cast<int32_t>(MODULE_TYPE_TSCPU),
83 : static_cast<int32_t>(INFO_TYPE_OS_SCHED), &(cpuInfo.tscpuOsSched));
84 6 : if (ret != DRV_ERROR_NONE) {
85 1 : BQS_LOG_ERROR("get device[%u] cpu info failed, ret = %d.", deviceId, static_cast<int32_t>(ret));
86 1 : return ret;
87 : }
88 :
89 5 : return ret;
90 : }
91 :
92 2 : BqsStatus BindCpuUtils::InitSem()
93 : {
94 2 : if (!g_semInited) {
95 2 : const int32_t semInitRet = sem_init(&g_sem, 0, 0U);
96 2 : if (semInitRet == -1) {
97 1 : BQS_LOG_ERROR("sem init failed, %s", strerror(errno));
98 1 : return BQS_STATUS_INNER_ERROR;
99 : }
100 1 : g_semInited = true;
101 : }
102 1 : return BQS_STATUS_OK;
103 : }
104 :
105 2 : BqsStatus BindCpuUtils::WaitSem()
106 : {
107 2 : if (g_semInited) {
108 2 : const int32_t semWaitRet = sem_wait(&g_sem);
109 2 : if (semWaitRet == -1) {
110 1 : BQS_LOG_ERROR("sem wait failed, %s", strerror(errno));
111 1 : return BQS_STATUS_INNER_ERROR;
112 : }
113 1 : return BQS_STATUS_OK;
114 : }
115 0 : BQS_LOG_ERROR("sem wait failed, sem not init");
116 0 : return BQS_STATUS_INNER_ERROR;
117 : }
118 :
119 2 : BqsStatus BindCpuUtils::PostSem()
120 : {
121 2 : if (g_semInited) {
122 2 : const int32_t semPostRet = sem_post(&g_sem);
123 2 : if (semPostRet == -1) {
124 1 : BQS_LOG_ERROR("sem post failed, %s", strerror(errno));
125 1 : return BQS_STATUS_INNER_ERROR;
126 : }
127 1 : return BQS_STATUS_OK;
128 : }
129 0 : BQS_LOG_ERROR("sem post failed, sem not init");
130 0 : return BQS_STATUS_INNER_ERROR;
131 : }
132 :
133 1 : void BindCpuUtils::DestroySem()
134 : {
135 1 : if (g_semInited) {
136 1 : (void)sem_destroy(&g_sem);
137 1 : g_semInited = false;
138 : }
139 1 : }
140 :
141 4 : BqsStatus BindCpuUtils::WriteTidToCpuSet()
142 : {
143 4 : const pid_t tid = static_cast<pid_t>(GetTid());
144 4 : std::string command = "cd /var/ && sudo ./add_aicpu_tid_to_tasks.sh";
145 4 : command = command + " " + std::to_string(static_cast<uint64_t>(tid));
146 : // system() may fail due to "No child processes".
147 : // if SIGCHLD is set to SIG_IGN, waitpid() may report ECHILD error because it cannot find the child process.
148 : // The reason is that the system() relies on a feature of the system, that is,
149 : // when the kernel initializes the process, the processing mode of SIGCHLD signal is SIG_IGN.
150 4 : const int32_t ret = system(command.c_str());
151 4 : if (ret != 0) {
152 1 : BQS_LOG_WARN("write tid[%d] to /sys/fs/cgroup/cpuset/AICPU/tasks failed, ret[%d], strerror[%s]",
153 : tid, ret, strerror(errno));
154 1 : return BQS_STATUS_INNER_ERROR;
155 : }
156 :
157 3 : return BQS_STATUS_OK;
158 4 : }
159 :
160 4 : BqsStatus BindCpuUtils::BindAicpuBySelf(uint32_t bindCpuIndex)
161 : {
162 : // On device, it must write tid to aicpu set file, when you need bind aicpu.
163 4 : const BqsStatus writeRet = WriteTidToCpuSet();
164 4 : if (writeRet != BQS_STATUS_OK) {
165 1 : BQS_LOG_WARN(
166 : "Write tid to cpuset file failed, maybe cause bind cpu failed, ret=%d.", static_cast<int32_t>(writeRet));
167 : }
168 :
169 : cpu_set_t mask;
170 4 : CPU_ZERO(&mask);
171 4 : CPU_SET(bindCpuIndex, &mask);
172 4 : int32_t ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &mask);
173 4 : if (ret != 0) {
174 1 : BQS_LOG_ERROR("Set affinity with cpu[%u] failed, ret=%d.", bindCpuIndex, ret);
175 1 : return BQS_STATUS_INNER_ERROR;
176 : }
177 :
178 3 : CPU_ZERO(&mask);
179 : // check set result
180 3 : ret = pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &mask);
181 3 : if (ret != 0) {
182 1 : BQS_LOG_ERROR("Getaffinity failed, ret=%d", ret);
183 1 : return BQS_STATUS_INNER_ERROR;
184 : }
185 : // check bind result
186 2 : if (CPU_ISSET(bindCpuIndex, &mask) == 0U) {
187 1 : BQS_LOG_INFO("Check CPU_ISSET with cpu[%u] not success", bindCpuIndex);
188 1 : return BQS_STATUS_INNER_ERROR;
189 : }
190 1 : BQS_LOG_INFO("Set affinity with cpu[%u] successful", bindCpuIndex);
191 1 : return BQS_STATUS_OK;
192 : }
193 :
194 4 : BqsStatus BindCpuUtils::SetThreadAffinity(const pthread_t &threadId, const std::vector<uint32_t> &cpuIds)
195 : {
196 : cpu_set_t cpuset;
197 4 : CPU_ZERO(&cpuset);
198 7 : for (const auto cpuId: cpuIds) {
199 3 : CPU_SET(cpuId, &cpuset);
200 3 : BQS_LOG_INFO("prepare bind threadId=%lu to cpuId=%u", threadId, static_cast<uint32_t>(cpuId));
201 : }
202 4 : BQS_LOG_RUN_INFO("begin call pthread_setaffinity_np threadId=%lu", threadId);
203 : // 设置线程亲和性
204 4 : int32_t ret = pthread_setaffinity_np(threadId, sizeof(cpu_set_t), &cpuset);
205 4 : if (ret != 0) {
206 2 : BQS_LOG_ERROR("BindCpu threadId=%lu setaffinity failed, ret=%d", threadId, ret);
207 2 : return BQS_STATUS_INNER_ERROR;
208 : }
209 : // 检查线程实际亲和性
210 2 : ret = pthread_getaffinity_np(threadId, sizeof(cpu_set_t), &cpuset);
211 2 : if (ret != 0) {
212 1 : BQS_LOG_ERROR("BindCpu threadId=%lu getaffinity failed, ret=%d", threadId, ret);
213 1 : return BQS_STATUS_INNER_ERROR;
214 : }
215 1025 : for (int32_t cpuId = 0; cpuId < CPU_SETSIZE; cpuId++) {
216 1024 : if (CPU_ISSET(cpuId, &cpuset) != 0) {
217 1 : BQS_LOG_INFO("BindCpu threadId=%lu to cpuId=%d success", threadId, static_cast<int32_t>(cpuId));
218 : }
219 : }
220 1 : return BQS_STATUS_OK;
221 : }
222 :
223 2 : BqsStatus BindCpuUtils::BindAicpuByPm(const uint32_t bindCpuIndex)
224 : {
225 2 : const pid_t tid = static_cast<pid_t>(GetTid());
226 :
227 2 : std::vector<uint32_t> coreAffinity;
228 2 : coreAffinity.push_back(bindCpuIndex);
229 2 : const auto ret = ProcMgrBindThread(tid, coreAffinity);
230 2 : if (ret != 0U) {
231 1 : BQS_LOG_ERROR("set affinity failed ret[%d], aicpu bindCpuIndex[%u], tid[%u]",
232 : ret, bindCpuIndex, tid);
233 1 : return BQS_STATUS_INNER_ERROR;
234 : }
235 1 : return BQS_STATUS_OK;
236 2 : }
237 :
238 6 : BqsStatus BindCpuUtils::BindAicpu(const uint32_t bindCpuIndex)
239 : {
240 6 : std::string cpuSetFlag;
241 6 : const char * const envValue = std::getenv("PROCMGR_AICPU_CPUSET");
242 6 : if (envValue != nullptr) {
243 8 : cpuSetFlag = std::string(envValue);
244 : }
245 :
246 6 : BqsStatus res = BQS_STATUS_OK;
247 6 : if (cpuSetFlag == "1") {
248 2 : res = BindAicpuByPm(bindCpuIndex);
249 2 : BQS_LOG_RUN_INFO("Qs bind tid by pm, cpuSetFlag:[%s], index[%u], res[%d].",
250 : cpuSetFlag.c_str(), bindCpuIndex, static_cast<int32_t>(res));
251 : } else {
252 4 : res = BindAicpuBySelf(bindCpuIndex);
253 4 : BQS_LOG_RUN_INFO("Qs bind tid by self, cpuSetFlag:[%s] index[%u], res[%d].",
254 : cpuSetFlag.c_str(), bindCpuIndex, static_cast<int32_t>(res));
255 : }
256 6 : return res;
257 6 : }
258 :
259 7 : void BindCpuUtils::SetThreadFIFO(const uint32_t deviceId)
260 : {
261 7 : if (QSFeatureCtrl::ShouldSetThreadFIFO(deviceId)) {
262 : struct sched_param param;
263 0 : param.sched_priority = sched_get_priority_max(SCHED_FIFO);
264 0 : if (param.sched_priority == -1) {
265 0 : BQS_LOG_WARN("QueueSchedule sched_get_priority_max failed, errno[%d].", errno);
266 0 : } else if (sched_setscheduler(0, SCHED_FIFO, ¶m) == -1) {
267 0 : BQS_LOG_WARN("QueueSchedule sched_setscheduler failed, errno[%d].", errno);
268 : } else {
269 0 : BQS_LOG_INFO("QueueSchedule sched_setscheduler and sched_get_priority_max success.");
270 : }
271 : }
272 7 : }
273 :
274 14 : BqsStatus BindCpuUtils::GetDevCpuInfo(const uint32_t deviceId, std::vector<uint32_t> &aiCpuIds,
275 : std::vector<uint32_t> &ctrlCpuIds, uint32_t &coreNumPerDev, uint32_t &aicpuNum,
276 : uint32_t &aicpuBaseId)
277 : {
278 14 : CpuInfo cpuInfo = {};
279 14 : uint32_t deviceIdTmp = deviceId;
280 : uint32_t ret;
281 14 : if (FeatureCtrl::IsVfModeDie1(deviceId)) {
282 2 : int64_t pfDeviceId = 0;
283 2 : ret = halGetDeviceInfo(deviceId, MODULE_TYPE_SYSTEM, INFO_TYPE_PHY_DIE_ID, &pfDeviceId);
284 2 : if (ret != DRV_ERROR_NONE) {
285 1 : BQS_LOG_ERROR("halGetDeviceInfo get pfDeviceId fail in device[%d], ret[%d]", deviceId, ret);
286 1 : return BQS_STATUS_DRIVER_ERROR;
287 : }
288 1 : deviceIdTmp = static_cast<uint32_t>(pfDeviceId);
289 : }
290 13 : ret = GetCpuInfo(deviceIdTmp, cpuInfo);
291 13 : if (ret != DRV_ERROR_NONE) {
292 8 : BQS_LOG_ERROR("get device[%u] cpu info failed, ret = %d.", deviceIdTmp, ret);
293 8 : return BQS_STATUS_DRIVER_ERROR;
294 : }
295 5 : coreNumPerDev = static_cast<uint32_t>(cpuInfo.ccpuNum) * static_cast<uint32_t>(cpuInfo.ccpuOsSched) +
296 5 : static_cast<uint32_t>(cpuInfo.dcpuNum) * static_cast<uint32_t>(cpuInfo.dcpuOsSched) +
297 5 : static_cast<uint32_t>(cpuInfo.aicpuNum) * static_cast<uint32_t>(cpuInfo.aicpuOsSched) +
298 5 : static_cast<uint32_t>(cpuInfo.tscpuNum) * static_cast<uint32_t>(cpuInfo.tscpuOsSched);
299 5 : BQS_LOG_INFO("get device[%u] cpu num [%u] success", deviceIdTmp, coreNumPerDev);
300 5 : if (FeatureCtrl::IsAosCore()) {
301 1 : aicpuNum = static_cast<uint32_t>(cpuInfo.aicpuNum);
302 1 : aicpuBaseId = 0U;
303 1 : if (cpuInfo.ccpuOsSched != 0U) {
304 1 : aicpuBaseId += cpuInfo.ccpuNum;
305 : }
306 1 : if (cpuInfo.dcpuOsSched != 0U) {
307 1 : aicpuBaseId += cpuInfo.dcpuNum;
308 : }
309 1 : BQS_LOG_INFO("get device[%u] aicpu cpu info success, aicpuBaseId[%u], aicpuNum[%u]",
310 : deviceIdTmp, aicpuBaseId, aicpuNum);
311 : } else {
312 4 : int64_t aicpuNumTmp = 0;
313 4 : int64_t aicpuBitMap = 0;
314 4 : if (FeatureCtrl::IsVfModeCheckedByDeviceId(deviceId)) {
315 2 : ret = halGetDeviceInfo(deviceId, MODULE_TYPE_AICPU, INFO_TYPE_PF_CORE_NUM, &aicpuNumTmp) +
316 2 : halGetDeviceInfo(deviceId, MODULE_TYPE_AICPU, INFO_TYPE_PF_OCCUPY, &aicpuBitMap);
317 : } else {
318 2 : ret = halGetDeviceInfo(deviceId, MODULE_TYPE_AICPU, INFO_TYPE_CORE_NUM, &aicpuNumTmp) +
319 2 : halGetDeviceInfo(deviceId, MODULE_TYPE_AICPU, INFO_TYPE_OCCUPY, &aicpuBitMap);
320 : }
321 4 : BQS_LOG_INFO("get device[%u] aicpu cpu info success, aicpuBitMap[%ld], aicpuNum[%ld]",
322 : deviceId, aicpuBitMap, aicpuNumTmp);
323 4 : aicpuNum = aicpuNumTmp;
324 4 : aiCpuIds.clear();
325 4 : uint32_t id = 0;
326 8 : while (aicpuBitMap != 0) {
327 4 : if ((static_cast<uint64_t>(aicpuBitMap) & static_cast<uint64_t>(0x1)) != 0) {
328 4 : aiCpuIds.push_back(id);
329 : }
330 4 : aicpuBitMap = static_cast<uint64_t>(aicpuBitMap) >> 1;
331 4 : id++;
332 : }
333 : }
334 :
335 5 : BQS_LOG_INFO("get device[%u] ctrl cpu info success, ccpuNum[%ld], ccpuOsSched[%ld]",
336 : deviceId, cpuInfo.ccpuNum, cpuInfo.ccpuOsSched);
337 5 : if (cpuInfo.ccpuOsSched > 0L && !FeatureCtrl::IsVfModeDie1(deviceId)) {
338 8 : for (auto i = 0L; i < cpuInfo.ccpuNum; i++) {
339 4 : ctrlCpuIds.emplace_back(coreNumPerDev * deviceId + static_cast<uint32_t>(i));
340 : }
341 : }
342 5 : return BQS_STATUS_OK;
343 : }
344 :
345 7 : bool BindCpuUtils::AddToCgroup(const uint32_t deviceId, const uint32_t vfId)
346 : {
347 7 : if (!QSFeatureCtrl::ShouldAddToCGroup(deviceId)) {
348 0 : return true;
349 : }
350 :
351 7 : BQS_LOG_RUN_INFO("AddToCgroup, deviceId = %u, vfId = %u", deviceId, vfId);
352 7 : const int32_t pid = drvDeviceGetBareTgid();
353 14 : std::string command = "cd /var/ && sudo ./tsdaemon_add_to_usermemory.sh";
354 7 : const std::string pathStr = "/var/tsdaemon_add_to_usermemory.sh";
355 7 : if (access(pathStr.c_str(), F_OK) != 0) {
356 7 : command = "cd /usr/local/Ascend/driver/tools/ && sudo ./tsdaemon_add_to_usermemory.sh";
357 : }
358 7 : (void)command.append(" memory ")
359 7 : .append(std::to_string(pid))
360 7 : .append(" ")
361 14 : .append(std::to_string(deviceId))
362 7 : .append(" ")
363 7 : .append(std::to_string(vfId));
364 7 : const sighandler_t oldHandler = signal(SIGCHLD, SIG_DFL);
365 :
366 : // system() may fail due to "No child processes".
367 : // if SIGCHLD is set to SIG_IGN, waitpid() may report ECHILD error because it cannot find the child process.
368 : // The reason is that the system() relies on a feature of the system, that is,
369 : // when the kernel initializes the process, the processing mode of SIGCHLD signal is SIG_IGN.
370 7 : const int32_t ret = system(command.c_str());
371 7 : (void)signal(SIGCHLD, oldHandler);
372 7 : if (ret != 0) {
373 0 : BQS_LOG_ERROR("Add to cgroup failed, ret:[%d], cmd:[%s]", ret, command.c_str());
374 0 : return false;
375 : }
376 7 : BQS_LOG_RUN_INFO("Add to cgroup successfully, cmd:[%s].", command.c_str());
377 7 : return true;
378 7 : }
379 : } // namespace bqs
|