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