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 : #include "aicpusd_cust_so_manager.h"
11 :
12 : #include <fstream>
13 : #include <cstring>
14 : #include <dirent.h>
15 : #include <errno.h>
16 : #include <unistd.h>
17 : #include <securec.h>
18 : #include <sys/stat.h>
19 : #include <sys/file.h>
20 : #include <sys/types.h>
21 : #include "aicpu_engine.h"
22 : #include "aicpusd_status.h"
23 : #include "aicpusd_info.h"
24 : #include "aicpu_event_struct.h"
25 : #include "common/aicpusd_util.h"
26 : #include "core/aicpusd_drv_manager.h"
27 : #include "aicpusd_feature_ctrl.h"
28 :
29 : namespace {
30 : // cust so root path: CustAiCpuUser
31 : const std::string CP_CUSTOM_SO_ROOT_PATH = "/home/CustAiCpuUser";
32 : const std::string CP_CUSTOM_SO_LIB_NAME = "lib/";
33 : const std::string CP_CUSTOM_SO_LOCK_NAME = "lib_locker";
34 : // prefix of cust so dir name
35 : constexpr const char* CP_CUSTOM_SO_DIR_NAME_PREFIX = "cust_aicpu_";
36 : // pattern for custom so name
37 : constexpr const char* CP_PATTERN_FOR_SO_NAME("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890-_.");
38 : constexpr const uint32_t DIV_NUM = 2U;
39 : // physical machine VFID == 0 物理机场景
40 : constexpr uint32_t CP_DEFAULT_VF_ID = 0U;
41 : // permissions
42 : constexpr uint32_t READ_OWNER = 256U;
43 : constexpr uint32_t WRITE_OWNER = 128U;
44 : constexpr uint32_t EXEC_OWNER = 64U;
45 : constexpr uint32_t READ_GROUP = 32U;
46 : constexpr uint32_t EXEC_GROUP = 8U;
47 : constexpr uint64_t MAX_FILE_SIZE = 0xc800000; // 200MB
48 :
49 : // permissions to 640
50 : constexpr mode_t MODE_640 =
51 : static_cast<mode_t>(READ_OWNER) | static_cast<mode_t>(WRITE_OWNER) | static_cast<mode_t>(READ_GROUP);
52 : // permissions to 750
53 : constexpr mode_t MODE_750 = static_cast<mode_t>(READ_OWNER) | static_cast<mode_t>(WRITE_OWNER) |
54 : static_cast<mode_t>(EXEC_OWNER) | static_cast<mode_t>(READ_GROUP) |
55 : static_cast<mode_t>(EXEC_GROUP);
56 : } // namespace
57 :
58 : namespace AicpuSchedule {
59 123 : AicpuCustSoManager& AicpuCustSoManager::GetInstance()
60 : {
61 123 : static AicpuCustSoManager instance;
62 123 : return instance;
63 : }
64 :
65 37 : int32_t AicpuCustSoManager::InitAicpuCustSoManager(const aicpu::AicpuRunMode mode, const AicpuSchedMode schedMode)
66 : {
67 37 : runMode_ = mode;
68 37 : schedMode_ = schedMode;
69 37 : (void)GetDirForCustAicpuSo(custSoRootPath_);
70 : // init cust so soft link path
71 37 : custSoDirName_ = GetPathForCustAicpuSoftLink();
72 37 : if (runMode_ == aicpu::AicpuRunMode::PROCESS_PCIE_MODE) {
73 3 : hashCalculator_.SetSoRootPath(custSoRootPath_);
74 3 : fileLocker_.InitFileLocker(custSoRootPath_ + CP_CUSTOM_SO_LOCK_NAME);
75 : }
76 :
77 37 : aicpusd_run_info(
78 : "Cust so manager init successfully, dir=%s, runMode=%u, schedMode=%u.", custSoDirName_.c_str(),
79 : static_cast<uint32_t>(runMode_), static_cast<uint32_t>(schedMode));
80 37 : return AICPU_SCHEDULE_OK;
81 : }
82 :
83 31 : bool AicpuCustSoManager::IsSupportCustAicpu() const
84 : {
85 : // msgq mode does not support cust aicpu
86 31 : if (schedMode_ == SCHED_MODE_MSGQ) {
87 3 : return false;
88 : }
89 28 : return true;
90 : }
91 :
92 28 : int32_t AicpuCustSoManager::CheckAndCreateSoFile(const LoadOpFromBufArgs* const args, std::string& soName)
93 : {
94 : // check init result
95 28 : if (!CheckCustSoPath()) {
96 1 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
97 : }
98 :
99 27 : if (args == nullptr) {
100 1 : aicpusd_err("cust so ptr is null.");
101 1 : return AICPU_SCHEDULE_ERROR_PARAMETER_NOT_VALID;
102 : }
103 : // check so buf
104 26 : char_t* const soBuf = PtrToPtr<void, char_t>(ValueToPtr(args->kernelSoBuf));
105 26 : if (soBuf == nullptr) {
106 1 : aicpusd_err("BatchLoadOpFromBuf kernel so buf ptr is null.");
107 1 : return AICPU_SCHEDULE_ERROR_PARAMETER_NOT_VALID;
108 : }
109 : // check so buf len
110 25 : const uint32_t soBufLen = static_cast<uint32_t>(args->kernelSoBufLen);
111 25 : if (soBufLen == 0U) {
112 3 : aicpusd_err("BatchLoadOpFromBuf kernel input param soBufLen must > 0");
113 3 : return AICPU_SCHEDULE_ERROR_PARAMETER_NOT_VALID;
114 : }
115 : // check so name
116 22 : const char_t* const soNamePtr = static_cast<const char_t*>(ValueToPtr(args->kernelSoName));
117 22 : if (soNamePtr == nullptr) {
118 1 : aicpusd_err("BatchLoadOpFromBuf kernel so name ptr is null.");
119 1 : return AICPU_SCHEDULE_ERROR_PARAMETER_NOT_VALID;
120 : }
121 : // check so name len
122 21 : const uint32_t soNameLen = static_cast<uint32_t>(args->kernelSoNameLen);
123 21 : if ((soNameLen == 0U) || (soNameLen > MAX_CUST_SO_NAME_LEN)) {
124 1 : aicpusd_err(
125 : "BatchLoadOpFromBuf kernel input param soNameLen=%u not in (0, %u].", soNameLen, MAX_CUST_SO_NAME_LEN);
126 1 : return AICPU_SCHEDULE_ERROR_PARAMETER_NOT_VALID;
127 : }
128 :
129 : // crate so file
130 20 : const std::string realSoName(soNamePtr, static_cast<size_t>(soNameLen));
131 20 : const FileInfo fileInfo = {.data = soBuf, .size = soBufLen, .name = realSoName};
132 20 : const int32_t ret = CreateSoFile(fileInfo);
133 20 : if (ret != AICPU_SCHEDULE_OK) {
134 3 : aicpusd_err("Process BatchLoadOpFromBuf failed as parse so file failed, so name[%s].", realSoName.c_str());
135 3 : return ret;
136 : }
137 17 : soName = realSoName;
138 17 : return AICPU_SCHEDULE_OK;
139 20 : }
140 :
141 18 : int32_t AicpuCustSoManager::CheckAndDeleteSoFile(const LoadOpFromBufArgs* const args)
142 : {
143 : // check init result
144 18 : if (!CheckCustSoPath()) {
145 1 : aicpusd_err("Check cust so path fail");
146 1 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
147 : }
148 :
149 17 : if (args == nullptr) {
150 1 : aicpusd_err("cust so ptr is null.");
151 1 : return AICPU_SCHEDULE_ERROR_PARAMETER_NOT_VALID;
152 : }
153 : // check so name len
154 16 : const uint32_t soNameLen = static_cast<uint32_t>(args->kernelSoNameLen);
155 16 : if ((soNameLen == 0U) || (soNameLen > MAX_CUST_SO_NAME_LEN)) {
156 2 : aicpusd_err("LoadOpFromBuf kernel input param soNameLen=%u not in (0, %u].", soNameLen, MAX_CUST_SO_NAME_LEN);
157 2 : return AICPU_SCHEDULE_ERROR_PARAMETER_NOT_VALID;
158 : }
159 : // check so name
160 14 : if (args->kernelSoName == 0U) {
161 1 : aicpusd_err("so name ptr is null.");
162 1 : return AICPU_SCHEDULE_ERROR_PARAMETER_NOT_VALID;
163 : }
164 : const std::string realSoName(
165 13 : static_cast<const char_t*>(ValueToPtr(args->kernelSoName)), static_cast<size_t>(soNameLen));
166 13 : if (realSoName.find_first_not_of(CP_PATTERN_FOR_SO_NAME) != std::string::npos) {
167 1 : aicpusd_err("Cust so name %s is not invalid. Please check!", realSoName.c_str());
168 1 : return AICPU_SCHEDULE_ERROR_PARAMETER_NOT_VALID;
169 : }
170 :
171 12 : std::string fileName;
172 12 : (void)fileName.append(custSoDirName_).append(std::string(realSoName));
173 12 : if (access(fileName.c_str(), F_OK) != 0) {
174 9 : aicpusd_run_warn("Check deleteCustOp's so full path:%s failed, error:%s", fileName.c_str(), strerror(errno));
175 9 : return AICPU_SCHEDULE_OK;
176 : }
177 : // check so file
178 3 : const int32_t ret = CheckSoFullPathValid(fileName);
179 3 : if (ret != AICPU_SCHEDULE_OK) {
180 1 : return ret;
181 : }
182 :
183 : // delete so
184 2 : return DeleteSoFile(custSoDirName_, fileName);
185 13 : }
186 :
187 7 : int32_t AicpuCustSoManager::CheckAndDeleteCustSoDir()
188 : {
189 : // check init result
190 7 : if (!CheckCustSoPath()) {
191 1 : return AICPU_SCHEDULE_OK;
192 : }
193 :
194 6 : const std::string dirName(custSoDirName_);
195 : // check whether dir is empty. If empty, delete dir
196 : {
197 6 : const std::lock_guard<std::mutex> soFileLock(soFileMutex_);
198 6 : if (CheckDirEmpty(dirName)) {
199 2 : if (remove(dirName.c_str()) != 0) {
200 1 : aicpusd_run_info("Delete dir:%s failed, error is %s.", dirName.c_str(), strerror(errno));
201 : } else {
202 1 : aicpusd_info("Delete dir:%s success.", dirName.c_str());
203 : }
204 : }
205 6 : }
206 6 : return AICPU_SCHEDULE_OK;
207 6 : }
208 :
209 14 : int32_t AicpuCustSoManager::CreateSoFile(const FileInfo& fileInfo)
210 : {
211 14 : aicpusd_info("begin to create so file. soName is %s", fileInfo.name.c_str());
212 : // check so name
213 14 : const std::string kernelSo(fileInfo.name);
214 14 : if (kernelSo.find_first_not_of(CP_PATTERN_FOR_SO_NAME) != std::string::npos) {
215 1 : aicpusd_err("Cust so name %s is not invalid. Please check!", kernelSo.c_str());
216 1 : return AICPU_SCHEDULE_ERROR_PARAMETER_NOT_VALID;
217 : }
218 :
219 13 : const int32_t ret = DoCreateSoFile(fileInfo);
220 13 : if (ret != AICPU_SCHEDULE_OK) {
221 4 : aicpusd_err("Create so failed, path=%s", fileInfo.name.c_str());
222 4 : return ret;
223 : }
224 :
225 : // load so if current aicpu mode is thread mode(minirc/ctrlcpu/lhisi)
226 9 : if (runMode_ == aicpu::AicpuRunMode::THREAD_MODE) {
227 7 : const uint32_t loadSoNum = 1U;
228 7 : const char* soNames[loadSoNum] = {fileInfo.name.c_str()};
229 7 : (void)aeBatchLoadKernelSo(aicpu::KERNEL_TYPE_AICPU_CUSTOM, loadSoNum, &(soNames[0]));
230 : }
231 9 : aicpusd_info("Create so file %s success", fileInfo.name.c_str());
232 9 : return AICPU_SCHEDULE_OK;
233 14 : }
234 :
235 13 : int32_t AicpuCustSoManager::DoCreateSoFile(const FileInfo& fileInfo)
236 : {
237 13 : const std::lock_guard<std::mutex> soFileLock(soFileMutex_);
238 13 : const std::string softLinkPath = custSoDirName_ + fileInfo.name;
239 13 : if (runMode_ != aicpu::AicpuRunMode::PROCESS_PCIE_MODE) {
240 8 : aicpusd_info("Aicpu is not process mode, only write file. path=%s", softLinkPath.c_str());
241 8 : const FileInfo newFileInfo = {.data = fileInfo.data, .size = fileInfo.size, .name = softLinkPath};
242 8 : return WriteBufToSoFile(newFileInfo, GetPathForCustAicpuSoftLink());
243 8 : }
244 :
245 5 : int32_t ret = AICPU_SCHEDULE_OK;
246 5 : FileHashInfo existedFileInfo = {};
247 5 : const std::string soFullPath = GenerateSoFullPath(fileInfo.name); // The real so name, not is soft link
248 5 : const FileInfo newFileInfo = {.data = fileInfo.data, .size = fileInfo.size, .name = soFullPath};
249 5 : fileLocker_.LockFileLocker();
250 5 : if (hashCalculator_.GetSameFileInfo(newFileInfo, existedFileInfo)) {
251 4 : ret = CreateSoftLinkToSoFile(softLinkPath, existedFileInfo.filePath);
252 : } else {
253 1 : ret = WriteBufToSoFile(newFileInfo, GetPathForCustAicpuRealSo());
254 1 : if (ret == AICPU_SCHEDULE_OK) {
255 0 : ret = CreateSoftLinkToSoFile(softLinkPath, soFullPath);
256 : }
257 : }
258 5 : fileLocker_.UnlockFileLocker();
259 5 : if (ret != AICPU_SCHEDULE_OK) {
260 3 : aicpusd_err("Create so file failed, so=%s", softLinkPath.c_str());
261 3 : return ret;
262 : }
263 :
264 2 : return ret;
265 13 : }
266 :
267 13 : int32_t AicpuCustSoManager::WriteBufToSoFile(const FileInfo& fileInfo, const std::string& dirPath) const
268 : {
269 13 : const std::string soName = fileInfo.name;
270 : // check and make dir
271 13 : int32_t ret = CheckOrMakeDirectory(dirPath);
272 13 : if (ret != 0) {
273 0 : aicpusd_err("Check or create directory:%s failed.", dirPath.c_str());
274 0 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
275 : }
276 : // serious security problem, must check realpath
277 13 : ret = CheckSoFullPathValid(soName);
278 13 : if (ret != AICPU_SCHEDULE_OK) {
279 3 : return ret;
280 : }
281 : // check whether so exist
282 10 : if (access(soName.c_str(), F_OK) == 0) {
283 1 : aicpusd_info("%s already exists in the current directory, no need to write again.", soName.c_str());
284 1 : return AICPU_SCHEDULE_OK;
285 : }
286 : try {
287 9 : std::ofstream soFile(soName, std::ios::out | std::ios::binary);
288 9 : if (!soFile) {
289 1 : aicpusd_err("Fail to open file:%s. reason=%s", soName.c_str(), strerror(errno));
290 1 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
291 : }
292 16 : const ScopeGuard fileGuard([&soFile]() { soFile.close(); });
293 8 : (void)soFile.write(fileInfo.data, static_cast<std::streamsize>(fileInfo.size));
294 8 : if (!soFile.good()) {
295 0 : aicpusd_err(
296 : "Fail to write file:%s, length:%u, eof=%d, bad=%d, fail=%d, reason=%s. please check host so size",
297 : soName.c_str(), fileInfo.size, soFile.eof(), soFile.bad(), soFile.fail(), strerror(errno));
298 0 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
299 : }
300 8 : soFile.flush();
301 8 : if (!soFile.good()) {
302 1 : aicpusd_err(
303 : "Fail to flush file:%s, length:%u, eof=%d, bad=%d, fail=%d, reason=%s. please check host so size",
304 : soName.c_str(), fileInfo.size, soFile.eof(), soFile.bad(), soFile.fail(), strerror(errno));
305 1 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
306 : }
307 10 : } catch (const std::ofstream::failure& err) {
308 0 : aicpusd_err("Fail to write file:%s, err=%s, reason=%s.", soName.c_str(), err.what(), strerror(errno));
309 0 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
310 0 : }
311 : // change so file permissions to 640
312 7 : if (chmod(soName.c_str(), MODE_640) != 0) {
313 0 : aicpusd_err("Change file:%s mode failed.", soName.c_str());
314 0 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
315 : }
316 7 : const uint64_t hashValue = HashCalculator::GetQuickHash(fileInfo.data, fileInfo.size);
317 7 : aicpusd_run_info(
318 : "Write buffer to so success, path=%s, hash=%llu, fileSize=%zu", soName.c_str(), hashValue, fileInfo.size);
319 7 : return AICPU_SCHEDULE_OK;
320 13 : }
321 :
322 4 : int32_t AicpuCustSoManager::CreateSoftLinkToSoFile(const std::string& softLinkPath, const std::string& existedSoPath)
323 : {
324 4 : if (access(existedSoPath.c_str(), F_OK) != 0) {
325 1 : aicpusd_err(
326 : "The target path is not existed in device, cannot create. linkPath=%s, targetPath=%s", softLinkPath.c_str(),
327 : existedSoPath.c_str());
328 1 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
329 : }
330 :
331 3 : if (access(softLinkPath.c_str(), F_OK) == 0) {
332 1 : aicpusd_info(
333 : "The soft link has existed in device, cannot create again. linkPath=%s, targetPath=%s",
334 : softLinkPath.c_str(), existedSoPath.c_str());
335 1 : return AICPU_SCHEDULE_OK;
336 : }
337 :
338 2 : int32_t ret = CheckOrMakeDirectory(custSoDirName_);
339 2 : if (ret != AICPU_SCHEDULE_OK) {
340 1 : aicpusd_err("The dir to create so soft link is not exist, dir=%s", custSoDirName_.c_str());
341 1 : return ret;
342 : }
343 :
344 1 : ret = CheckSoFullPathValid(softLinkPath);
345 1 : if (ret != AICPU_SCHEDULE_OK) {
346 0 : aicpusd_err("Invalid soft link path, linkPath=%s, targetPath=%s", softLinkPath.c_str(), existedSoPath.c_str());
347 0 : return ret;
348 : }
349 :
350 1 : ret = symlink(existedSoPath.c_str(), softLinkPath.c_str());
351 1 : if (ret != 0) {
352 0 : aicpusd_err(
353 : "Create soft links failed, ret=%d, linkPath=%s, targetPath=%s, reason=%s", ret, softLinkPath.c_str(),
354 : existedSoPath.c_str(), strerror(errno));
355 0 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
356 : }
357 :
358 1 : aicpusd_run_info(
359 : "Create soft link so success, linkPath=%s, targetPath=%s", softLinkPath.c_str(), existedSoPath.c_str());
360 1 : return AICPU_SCHEDULE_OK;
361 : }
362 :
363 40 : int32_t AicpuCustSoManager::GetDirForCustAicpuSo(std::string& dirName) const
364 : {
365 40 : const uint32_t uniqueVfId = AicpuDrvManager::GetInstance().GetUniqueVfId();
366 40 : if (runMode_ == aicpu::AicpuRunMode::PROCESS_PCIE_MODE) {
367 4 : dirName = GetCustAicpuUserPath(uniqueVfId);
368 : } else {
369 : // get cust aicpu so path
370 36 : bool envRet = false;
371 36 : std::string custDirName = "";
372 36 : if (runMode_ == aicpu::AicpuRunMode::THREAD_MODE) {
373 31 : envRet = AicpuUtil::GetEnvVal(ENV_NAME_CUST_SO_PATH, custDirName);
374 : }
375 36 : if (!envRet) {
376 35 : envRet = AicpuUtil::GetEnvVal(ENV_NAME_HOME, custDirName);
377 35 : if (!envRet) {
378 1 : aicpusd_info("Getting current home directory was not successful.");
379 1 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
380 : }
381 : }
382 :
383 35 : dirName = custDirName;
384 35 : const size_t len = dirName.size();
385 35 : if ((len == 0U) || (len >= static_cast<size_t>(PATH_MAX))) {
386 1 : aicpusd_err("The length of custDirName is %zu which is invalid.", len);
387 1 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
388 : }
389 34 : if (dirName[dirName.size() - 1U] != '/') {
390 34 : (void)dirName.append("/");
391 : }
392 36 : }
393 :
394 : // check directory ${customUser} or ${currentUser}
395 38 : const int ret = access(dirName.c_str(), F_OK);
396 38 : if (ret != 0) {
397 4 : aicpusd_err("Check directory:%s failed, error is %s.", dirName.c_str(), strerror(errno));
398 4 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
399 : }
400 :
401 34 : return AICPU_SCHEDULE_OK;
402 : }
403 :
404 4 : std::string AicpuCustSoManager::GetCustAicpuUserPath(const uint32_t uniqueVfId) const
405 : {
406 : // dc: ${CustAicpuUser${vfId}}/cust_aicpu_vf${vfId}/cust_aicpu_pid${hostpid}/
407 : // vfId is equal to 0, user is CustAicpuUser, other are CustAicpuUser${vfId}
408 4 : std::string dirName = CP_CUSTOM_SO_ROOT_PATH;
409 4 : if (uniqueVfId == CP_DEFAULT_VF_ID) {
410 1 : (void)dirName.append("/");
411 3 : } else if (FeatureCtrl::IsVfModeCheckedByDeviceId(uniqueVfId)) {
412 2 : const uint32_t custNum = (uniqueVfId - VDEVICE_MIN_CPU_NUM) / DIV_NUM + 1;
413 2 : (void)dirName.append(std::to_string(custNum)).append("/");
414 : } else {
415 1 : (void)dirName.append(std::to_string(uniqueVfId)).append("/");
416 : }
417 :
418 4 : return dirName;
419 0 : }
420 :
421 6 : std::string AicpuCustSoManager::GetPathForCustAicpuRealSo() const { return custSoRootPath_ + CP_CUSTOM_SO_LIB_NAME; }
422 :
423 45 : std::string AicpuCustSoManager::GetPathForCustAicpuSoftLink() const
424 : {
425 45 : const uint32_t uniqueVfId = AicpuDrvManager::GetInstance().GetUniqueVfId();
426 45 : std::string dirName = custSoRootPath_;
427 45 : const uint32_t deviceId = AicpuDrvManager::GetInstance().GetDeviceId();
428 45 : const pid_t hostPid = AicpuDrvManager::GetInstance().GetHostPid();
429 45 : (void)dirName.append(CP_CUSTOM_SO_DIR_NAME_PREFIX)
430 45 : .append(std::to_string(deviceId))
431 45 : .append("_")
432 90 : .append(std::to_string(uniqueVfId))
433 45 : .append("_")
434 90 : .append(std::to_string(static_cast<int32_t>(hostPid)))
435 45 : .append("/");
436 45 : return dirName;
437 0 : }
438 :
439 9 : int32_t AicpuCustSoManager::CheckSoFullPathValid(const std::string& soFullPath) const
440 : {
441 9 : if (soFullPath.length() >= static_cast<size_t>(PATH_MAX)) {
442 1 : aicpusd_err("soFullPath file length[%zu] must less than PATH_MAX[%u]", soFullPath.length(), PATH_MAX);
443 1 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
444 : }
445 :
446 8 : const std::string::size_type pos = soFullPath.rfind('/');
447 8 : if (pos == std::string::npos) {
448 4 : aicpusd_err("The path of current so file %s does not contain /.", soFullPath.c_str());
449 4 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
450 : }
451 : // include last '/'
452 4 : const std::string realFilePath = soFullPath.substr(0U, pos + 1U);
453 :
454 4 : std::unique_ptr<char_t[]> path(new (std::nothrow) char_t[PATH_MAX]);
455 4 : if (path == nullptr) {
456 0 : aicpusd_run_info("Alloc memory for path failed.");
457 0 : return AICPU_SCHEDULE_ERROR_PARAMETER_NOT_VALID;
458 : }
459 :
460 4 : const auto eRet = memset_s(path.get(), PATH_MAX, 0, PATH_MAX);
461 4 : if (eRet != EOK) {
462 1 : aicpusd_run_info("Mem set was not successful, ret=%d", eRet);
463 1 : return AICPU_SCHEDULE_ERROR_PARAMETER_NOT_VALID;
464 : }
465 :
466 3 : if (realpath(realFilePath.data(), path.get()) == nullptr) {
467 1 : aicpusd_err("Format to realpath failed:%s, path:%s", realFilePath.c_str(), path.get());
468 1 : return AICPU_SCHEDULE_ERROR_PARAMETER_NOT_VALID;
469 : }
470 : // path after realpath
471 2 : std::string normalizedPath(path.get());
472 2 : if (normalizedPath[normalizedPath.length() - 1U] != '/') {
473 2 : (void)normalizedPath.append("/");
474 : }
475 :
476 2 : if (normalizedPath != realFilePath) {
477 0 : aicpusd_err("Invalid so file:%s, should be %s", realFilePath.c_str(), normalizedPath.c_str());
478 0 : return AICPU_SCHEDULE_ERROR_PARAMETER_NOT_VALID;
479 : }
480 2 : aicpusd_info("check so file %s success.", soFullPath.c_str());
481 2 : return AICPU_SCHEDULE_OK;
482 4 : }
483 :
484 12 : int32_t AicpuCustSoManager::CheckOrMakeDirectory(const std::string& dirName) const
485 : {
486 : // input dirName is checked to be not nullptr
487 12 : int32_t ret = access(dirName.c_str(), F_OK);
488 12 : if (ret == -1) {
489 : // create directory and set permissions to 750
490 10 : ret = mkdir(dirName.c_str(), MODE_750);
491 10 : if (ret == -1) {
492 1 : const std::string errMsg = "Create directory:" + dirName + " failed, error:" + strerror(errno);
493 1 : aicpusd_err("%s", errMsg.c_str());
494 1 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
495 1 : }
496 9 : ret = chmod(dirName.c_str(), MODE_750);
497 9 : if (ret != 0) {
498 1 : aicpusd_err("Change directory:%s mode failed, errno:%s.", dirName.c_str(), strerror(errno));
499 1 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
500 : }
501 : }
502 10 : return AICPU_SCHEDULE_OK;
503 : }
504 :
505 7 : bool AicpuCustSoManager::CheckDirEmpty(const std::string& dirName) const
506 : {
507 : // open dir
508 7 : DIR* const dirHandle = opendir(dirName.c_str());
509 7 : if (dirHandle == nullptr) {
510 5 : aicpusd_err("Open dir:%s failed, error:%s.", dirName.c_str(), strerror(errno));
511 5 : return false;
512 : }
513 : // search dir
514 2 : dirent* ent = nullptr;
515 5 : while ((ent = readdir(dirHandle)) != nullptr) {
516 : // check whether . or ..
517 3 : if ((strcmp(".", ent->d_name) == 0) || (strcmp("..", ent->d_name) == 0)) {
518 2 : continue;
519 : }
520 : // if exist dir or file, return false
521 1 : const int32_t dType = static_cast<int32_t>(ent->d_type);
522 1 : if ((dType == DT_DIR) || (dType == DT_REG)) {
523 : // filter hidden file
524 : // If so is dlopen but not dlclose, a hidden file will be created when delete the so
525 1 : if (ent->d_name[0] != '.') {
526 0 : aicpusd_info("Dir:%s is not empty, file name is %s.", dirName.c_str(), ent->d_name);
527 0 : (void)closedir(dirHandle);
528 0 : return false;
529 : } else {
530 1 : aicpusd_run_info("Exist hidden file %s in dir:%s.", ent->d_name, dirName.c_str());
531 : }
532 : }
533 : }
534 2 : (void)closedir(dirHandle);
535 2 : aicpusd_info("Dir:%s is empty", dirName.c_str());
536 2 : return true;
537 : }
538 :
539 4 : int32_t AicpuCustSoManager::DeleteCustSoDir() const
540 : {
541 4 : if (custSoDirName_.empty()) {
542 1 : return AICPU_SCHEDULE_OK;
543 : }
544 :
545 3 : if (access(custSoDirName_.c_str(), F_OK) != 0) {
546 1 : aicpusd_run_info(
547 : "Accessing cust so dir %s was not successful, reason is %s.", custSoDirName_.c_str(), strerror(errno));
548 1 : return AICPU_SCHEDULE_OK;
549 : }
550 2 : const std::string command = "rm -rf " + custSoDirName_;
551 :
552 2 : const int32_t ret = AicpuUtil::ExecuteCmd(command.c_str());
553 2 : if (ret != 0) {
554 0 : aicpusd_err("Delete cust so dir %s failed, error is %s.", custSoDirName_.c_str(), strerror(errno));
555 0 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
556 : }
557 2 : return AICPU_SCHEDULE_OK;
558 2 : }
559 :
560 4 : int32_t AicpuCustSoManager::DeleteSoFile(const std::string& dirName, const std::string& soFullPath)
561 : {
562 4 : const std::lock_guard<std::mutex> soFileLock(soFileMutex_);
563 4 : const int32_t ret = RemoveSoFile(dirName, soFullPath);
564 4 : if (ret != AICPU_SCHEDULE_OK) {
565 1 : return ret;
566 : }
567 : // in delete so process, cannot dlclose so, because current cust so may be use some third party library,
568 : // the third party library may use pthread_key_create to register destruct call back for current thread when
569 : // dlopen, if we dlclose the so, the destruct will get invalid pointer and cause coredump
570 3 : return AICPU_SCHEDULE_OK;
571 4 : }
572 :
573 5 : int32_t AicpuCustSoManager::RemoveSoFile(const std::string& dirName, const std::string& soFullPath) const
574 : {
575 5 : if (access(soFullPath.c_str(), F_OK) != 0) {
576 1 : aicpusd_info("%s not exists in the current directory, reason:%s.", soFullPath.c_str(), strerror(errno));
577 1 : return AICPU_SCHEDULE_OK;
578 : }
579 4 : if (chmod(dirName.c_str(), MODE_750) != 0) {
580 1 : aicpusd_err("Change deleteCustOp's directory:%s mode failed, error:%s.", dirName.c_str(), strerror(errno));
581 1 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
582 : }
583 :
584 3 : if (remove(soFullPath.c_str()) != 0) {
585 1 : aicpusd_err("Delete so of custom op:%s failed, error:%s.", soFullPath.c_str(), strerror(errno));
586 1 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
587 : }
588 2 : return AICPU_SCHEDULE_OK;
589 : }
590 :
591 51 : bool AicpuCustSoManager::CheckCustSoPath() const
592 : {
593 51 : if (custSoDirName_.empty()) {
594 3 : aicpusd_err("custSoDirName_ is empty. Please check log of init method.");
595 3 : return false;
596 : }
597 48 : return true;
598 : }
599 :
600 5 : std::string AicpuCustSoManager::GenerateSoFullPath(const std::string& soName) const
601 : {
602 : // Use pid to avoid same so name
603 5 : const pid_t hostPid = AicpuDrvManager::GetInstance().GetHostPid();
604 5 : const uint32_t uniqueVfId = AicpuDrvManager::GetInstance().GetUniqueVfId();
605 5 : const uint32_t deviceId = AicpuDrvManager::GetInstance().GetDeviceId();
606 10 : return GetPathForCustAicpuRealSo() + soName + "." + std::to_string(deviceId) + "." + std::to_string(uniqueVfId) +
607 15 : "." + std::to_string(hostPid);
608 : }
609 :
610 5 : bool HashCalculator::GetSameFileInfo(const FileInfo& fileInfo, FileHashInfo& existedFileHashInfo)
611 : {
612 5 : UpdateCache();
613 :
614 5 : const uint64_t hashValue = GetQuickHash(fileInfo.data, fileInfo.size);
615 5 : if (GetSameHashFileFromCache(hashValue, existedFileHashInfo)) {
616 4 : aicpusd_info(
617 : "The file has same existed file after hash compare, so=%s, hash=%lu", fileInfo.name.c_str(), hashValue);
618 4 : return true;
619 : }
620 :
621 1 : FileHashInfo hashInfo = {};
622 1 : hashInfo.filePath = fileInfo.name;
623 1 : hashInfo.fileSize = fileInfo.size;
624 1 : hashInfo.hashValue = hashValue;
625 1 : AddHashInfoToCache(hashInfo);
626 :
627 1 : return false;
628 1 : }
629 :
630 7 : void HashCalculator::UpdateCache()
631 : {
632 7 : std::vector<std::string> file_names = {};
633 7 : int32_t ret = GetFileNameInDir(file_names);
634 7 : if (ret != AICPU_SCHEDULE_OK) {
635 0 : aicpusd_warn("Get file name in dir failed");
636 0 : return;
637 : }
638 :
639 9 : for (const auto& file_name : file_names) {
640 2 : const std::string filePath = soRootPath_ + CP_CUSTOM_SO_LIB_NAME + file_name;
641 2 : if (IsFileInCache(filePath)) {
642 1 : continue;
643 : }
644 :
645 1 : aicpusd_info("Start to read so %s", filePath.c_str());
646 1 : FileHashInfo hashInfo = {};
647 1 : ret = GenerateFileHashInfo(filePath, hashInfo);
648 1 : if (ret != AICPU_SCHEDULE_OK) {
649 0 : aicpusd_err("Get file hash info failed, filePath=%s", filePath.c_str());
650 : } else {
651 1 : AddHashInfoToCache(hashInfo);
652 : }
653 2 : }
654 7 : }
655 :
656 5 : bool HashCalculator::GetSameHashFileFromCache(const uint64_t hashValue, FileHashInfo& existedFileHashInfo) const
657 : {
658 5 : for (const auto& hashInfo : cache_) {
659 4 : if (hashValue == hashInfo.hashValue) {
660 4 : existedFileHashInfo = hashInfo;
661 4 : return true;
662 : }
663 : }
664 :
665 1 : return false;
666 : }
667 :
668 4 : int32_t HashCalculator::GenerateFileHashInfo(const std::string& filePath, FileHashInfo& hashInfo) const
669 : {
670 4 : if (!IsFileExist(filePath) || !IsRegularFile(filePath)) {
671 1 : aicpusd_err(
672 : "The file is not a regular file or not exist, filePath=%s, exist=%d", filePath.c_str(),
673 : static_cast<int32_t>(IsFileExist(filePath)));
674 1 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
675 : }
676 :
677 3 : std::ifstream file(filePath, std::ios::binary | std::ios::ate);
678 3 : if (!file.is_open()) {
679 1 : aicpusd_err("Open file failed, filePath=%s, reason=%s", filePath.c_str(), strerror(errno));
680 1 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
681 : }
682 4 : const ScopeGuard fileGuard([&file]() { (void)file.close(); });
683 2 : std::streamsize size = file.tellg();
684 2 : if (static_cast<uint64_t>(size) > MAX_FILE_SIZE) {
685 0 : aicpusd_err("The file size is large than max file size(200MB), size=%luBytes", static_cast<uint64_t>(size));
686 0 : return AICPU_SCHEDULE_ERROR_INNER_ERROR;
687 : }
688 :
689 2 : file.seekg(0, std::ios::beg);
690 2 : std::vector<char_t> buffer = {};
691 2 : buffer.resize(size);
692 2 : file.read(&buffer[0], size);
693 :
694 2 : hashInfo.hashValue = GetQuickHash(buffer.data(), size);
695 2 : hashInfo.fileSize = size;
696 2 : hashInfo.filePath = filePath;
697 :
698 2 : return AICPU_SCHEDULE_OK;
699 3 : }
700 :
701 2 : void HashCalculator::AddHashInfoToCache(const FileHashInfo& hashInfo)
702 : {
703 2 : cache_.emplace_back(hashInfo);
704 2 : aicpusd_info(
705 : "Add file %s to cache, size=%lu, hash=%lu", hashInfo.filePath.c_str(), hashInfo.fileSize, hashInfo.hashValue);
706 2 : }
707 :
708 5 : bool HashCalculator::IsFileInCache(const std::string& filePath) const
709 : {
710 6 : for (const auto& hashInfo : cache_) {
711 4 : if (filePath == hashInfo.filePath) {
712 3 : return true;
713 : }
714 : }
715 :
716 2 : return false;
717 : }
718 :
719 19 : uint64_t HashCalculator::GetQuickHash(const void* data, const size_t size)
720 : {
721 : /*
722 : * Using the FNV-1a algorithm for hash computation offers faster speed.
723 : * Here, it is solely used to record whether files are consistent and does not involve security scenarios.
724 : * In the event of a hash collision, the worst consequence would merely be saving an additional copy of a file.
725 : */
726 19 : const uint8_t* bytes = static_cast<const uint8_t*>(data);
727 19 : const uint64_t prime = 1099511628211UL;
728 19 : uint64_t hash = 14695981039346656037UL;
729 :
730 19 : constexpr size_t parallelNum = 8UL;
731 19 : size_t i = 0UL;
732 46 : for (; i + parallelNum <= size; i += parallelNum) {
733 27 : hash ^= bytes[i];
734 27 : hash *= prime;
735 27 : hash ^= bytes[i + 1UL];
736 27 : hash *= prime;
737 27 : hash ^= bytes[i + 2UL];
738 27 : hash *= prime;
739 27 : hash ^= bytes[i + 3UL];
740 27 : hash *= prime;
741 27 : hash ^= bytes[i + 4UL];
742 27 : hash *= prime;
743 27 : hash ^= bytes[i + 5UL];
744 27 : hash *= prime;
745 27 : hash ^= bytes[i + 6UL];
746 27 : hash *= prime;
747 27 : hash ^= bytes[i + 7UL];
748 27 : hash *= prime;
749 : }
750 :
751 62 : for (; i < size; i++) {
752 43 : hash ^= bytes[i];
753 43 : hash *= prime;
754 : }
755 :
756 19 : return hash;
757 : }
758 :
759 8 : int32_t HashCalculator::GetFileNameInDir(std::vector<std::string>& file_names) const
760 : {
761 8 : const std::string dirPath = soRootPath_ + CP_CUSTOM_SO_LIB_NAME;
762 8 : DIR* dir = opendir(dirPath.c_str());
763 8 : if (dir == nullptr) {
764 5 : aicpusd_run_warn("Open dir failed, path=%s, reason=%s", dirPath.c_str(), strerror(errno));
765 5 : return AICPU_SCHEDULE_OK;
766 : }
767 :
768 3 : dirent* entry = nullptr;
769 15 : while ((entry = readdir(dir)) != nullptr) {
770 9 : if (entry->d_type == DT_REG) {
771 3 : file_names.emplace_back(entry->d_name);
772 : }
773 : }
774 :
775 3 : const int32_t ret = closedir(dir);
776 3 : if (ret != 0) {
777 0 : aicpusd_warn("Close dir failed, ret=%d, path=%s, reason=%s", ret, dirPath.c_str(), strerror(errno));
778 : }
779 :
780 3 : return AICPU_SCHEDULE_OK;
781 8 : }
782 :
783 5 : bool HashCalculator::IsFileExist(const std::string& filePath) const
784 : {
785 5 : struct stat st = {};
786 5 : return (stat(filePath.c_str(), &st) == 0);
787 : }
788 :
789 3 : bool HashCalculator::IsRegularFile(const std::string& filePath) const
790 : {
791 3 : struct stat st = {};
792 3 : if (stat(filePath.c_str(), &st) != 0) {
793 0 : return false;
794 : }
795 :
796 3 : return S_ISREG(st.st_mode);
797 : }
798 :
799 3 : void CustSoFileLock::InitFileLocker(const std::string& filePath)
800 : {
801 3 : locker_ = open(filePath.c_str(), O_RDONLY);
802 3 : if (locker_ < 0) {
803 3 : aicpusd_run_warn("Open file locker may not exist, path=%s, reason=%s", filePath.c_str(), strerror(errno));
804 : }
805 :
806 3 : aicpusd_info("Init cust so file locker success, path=%s, fd=%d", filePath.c_str(), locker_);
807 3 : }
808 :
809 7 : void CustSoFileLock::LockFileLocker() const
810 : {
811 7 : if (locker_ < 0) {
812 5 : return;
813 : }
814 :
815 2 : const int32_t ret = flock(locker_, LOCK_EX);
816 2 : if (ret != 0) {
817 1 : aicpusd_err("Lock write file lock failed, reason=%s", strerror(errno));
818 : }
819 : }
820 :
821 7 : void CustSoFileLock::UnlockFileLocker() const
822 : {
823 7 : if (locker_ < 0) {
824 5 : return;
825 : }
826 :
827 2 : const int32_t ret = flock(locker_, LOCK_UN);
828 2 : if (ret != 0) {
829 1 : aicpusd_err("Unlock file lock failed, reason=%s", strerror(errno));
830 : }
831 : }
832 : } // namespace AicpuSchedule
|