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 <fstream>
12 : #include <climits>
13 : #include <cstdlib>
14 : #include <cerrno>
15 : #include <securec.h>
16 : #include "ascend_hal_define.h"
17 : #include "aicpusd_util.h"
18 : #include "aicpusd_drv_manager.h"
19 : #include "aicpu_context.h"
20 : #include "aicpusd_meminfo_process.h"
21 :
22 : namespace AicpuSchedule {
23 : namespace {
24 : // Field names expected in each memzone cfg entry of the json file.
25 : const char_t* const CFG_FIELD_NAMES[] = {"cfg_id", "total_size", "blk_size", "max_buf_size", "page_type"};
26 : const size_t CFG_FIELD_COUNT = sizeof(CFG_FIELD_NAMES) / sizeof(CFG_FIELD_NAMES[0]);
27 :
28 : // Forward declarations of internal helpers (definitions below).
29 : size_t SkipWhiteSpace(const std::string& s, size_t pos);
30 : bool ReadJsonString(const std::string& s, size_t& pos, std::string& out);
31 : size_t FindMatchingBrace(const std::string& s, size_t openPos);
32 : size_t FindMatchingBracket(const std::string& s, size_t openPos);
33 : bool SkipJsonValue(const std::string& s, size_t& pos);
34 :
35 : // Skip whitespace starting at pos. Return new pos (end if none found).
36 2507 : size_t SkipWhiteSpace(const std::string& s, size_t pos)
37 : {
38 2841 : while (pos < s.size()) {
39 2840 : const char_t c = s[pos];
40 2840 : if ((c != ' ') && (c != '\t') && (c != '\n') && (c != '\r')) {
41 2506 : break;
42 : }
43 334 : ++pos;
44 : }
45 2507 : return pos;
46 : }
47 :
48 : // Read a quoted string starting at pos (s[pos] must be '"'). On success, return true and
49 : // set out to the decoded string content; advance pos past the closing quote.
50 1351 : bool ReadJsonString(const std::string& s, size_t& pos, std::string& out)
51 : {
52 1351 : if ((pos >= s.size()) || (s[pos] != '"')) {
53 2 : return false;
54 : }
55 1349 : ++pos;
56 1349 : out.clear();
57 12318 : while (pos < s.size()) {
58 12318 : const char_t c = s[pos];
59 12318 : if (c == '"') {
60 1348 : ++pos;
61 1348 : return true;
62 : }
63 10970 : if (c == '\\') {
64 25 : ++pos;
65 25 : if (pos >= s.size()) {
66 0 : return false;
67 : }
68 25 : const char_t esc = s[pos];
69 25 : switch (esc) {
70 3 : case '"':
71 3 : out.push_back('"');
72 3 : break;
73 3 : case '\\':
74 3 : out.push_back('\\');
75 3 : break;
76 3 : case '/':
77 3 : out.push_back('/');
78 3 : break;
79 3 : case 'b':
80 3 : out.push_back('\b');
81 3 : break;
82 3 : case 'f':
83 3 : out.push_back('\f');
84 3 : break;
85 3 : case 'n':
86 3 : out.push_back('\n');
87 3 : break;
88 3 : case 'r':
89 3 : out.push_back('\r');
90 3 : break;
91 3 : case 't':
92 3 : out.push_back('\t');
93 3 : break;
94 1 : default:
95 : // unknown escape, reject to keep parser strict-but-simple
96 1 : return false;
97 : }
98 24 : ++pos;
99 : } else {
100 10945 : out.push_back(c);
101 10945 : ++pos;
102 : }
103 : }
104 0 : return false; // unterminated string
105 : }
106 :
107 : // Read an unsigned/signed integer literal starting at pos. On success, return true and
108 : // store the value in outVal (unsigned long long). Caller validates range as needed.
109 359 : bool ReadJsonInteger(const std::string& s, size_t& pos, unsigned long long& outVal)
110 : {
111 359 : size_t p = SkipWhiteSpace(s, pos);
112 359 : if (p >= s.size()) {
113 0 : return false;
114 : }
115 359 : bool negative = false;
116 359 : if (s[p] == '-') {
117 2 : negative = true;
118 2 : ++p;
119 2 : if (p >= s.size()) {
120 0 : return false;
121 : }
122 : }
123 359 : if (s[p] == '+') { // tolerate leading '+'
124 1 : ++p;
125 1 : if (p >= s.size()) {
126 0 : return false;
127 : }
128 : }
129 359 : if ((s[p] < '0') || (s[p] > '9')) {
130 2 : return false;
131 : }
132 357 : errno = 0;
133 357 : char_t* endPtr = nullptr;
134 357 : const unsigned long long v = strtoull(s.c_str() + p, &endPtr, 10);
135 357 : if (errno != 0) {
136 1 : return false;
137 : }
138 356 : const size_t consumed = static_cast<size_t>(endPtr - (s.c_str() + p));
139 356 : if (consumed == 0U) {
140 0 : return false;
141 : }
142 : // strtoull silently accepts trailing garbage; we require the number to be followed
143 : // by whitespace, comma, } or end-of-buffer — anything else is a syntax error.
144 356 : const size_t after = p + consumed;
145 356 : if (after < s.size()) {
146 356 : const char_t t = s[after];
147 356 : if ((t != ' ') && (t != '\t') && (t != '\n') && (t != '\r') && (t != ',') && (t != '}')) {
148 1 : return false;
149 : }
150 : }
151 355 : pos = after;
152 355 : outVal = negative ? 0ULL : v; // negative values are not meaningful for cfg; clamp to 0
153 355 : return true;
154 : }
155 :
156 : // Parse a single memzone entry object substring [objStart, objEnd) and fill cfg.
157 : // objStart points at the opening '{', objEnd at the matching '}'.
158 78 : bool ParseOneEntry(const std::string& s, size_t objStart, size_t objEnd, memZoneCfg& cfg)
159 : {
160 78 : cfg = {};
161 78 : bool found[CFG_FIELD_COUNT] = {false, false, false, false, false};
162 78 : size_t pos = objStart + 1U;
163 435 : while (pos < objEnd) {
164 366 : pos = SkipWhiteSpace(s, pos);
165 366 : if (pos >= objEnd) {
166 0 : break;
167 : }
168 366 : if (s[pos] == '}') {
169 0 : break;
170 : }
171 366 : std::string key;
172 366 : if (!ReadJsonString(s, pos, key)) {
173 1 : return false;
174 : }
175 365 : pos = SkipWhiteSpace(s, pos);
176 365 : if ((pos >= objEnd) || (s[pos] != ':')) {
177 1 : return false;
178 : }
179 364 : ++pos;
180 364 : pos = SkipWhiteSpace(s, pos);
181 : // Match against known field names
182 364 : size_t fieldIdx = CFG_FIELD_COUNT;
183 1095 : for (size_t i = 0U; i < CFG_FIELD_COUNT; ++i) {
184 1090 : if (key == CFG_FIELD_NAMES[i]) {
185 359 : fieldIdx = i;
186 359 : break;
187 : }
188 : }
189 364 : if (fieldIdx >= CFG_FIELD_COUNT) {
190 : // Unknown field: silently skip its value to align with the original
191 : // nlohmann-based behaviour, which only fetched the 5 known fields and
192 : // ignored any others present in the entry object.
193 5 : if (!SkipJsonValue(s, pos)) {
194 1 : return false;
195 : }
196 : } else {
197 359 : unsigned long long v = 0ULL;
198 359 : if (!ReadJsonInteger(s, pos, v)) {
199 4 : return false;
200 : }
201 355 : switch (fieldIdx) {
202 72 : case 0U:
203 72 : cfg.cfg_id = static_cast<unsigned int>(v);
204 72 : break;
205 71 : case 1U:
206 71 : cfg.total_size = v;
207 71 : break;
208 71 : case 2U:
209 71 : cfg.blk_size = static_cast<unsigned int>(v);
210 71 : break;
211 71 : case 3U:
212 71 : cfg.max_buf_size = v;
213 71 : break;
214 70 : case 4U:
215 70 : cfg.page_type = static_cast<unsigned int>(v);
216 70 : break;
217 0 : default:
218 0 : return false;
219 : }
220 355 : found[fieldIdx] = true;
221 : }
222 359 : pos = SkipWhiteSpace(s, pos);
223 359 : if (pos < objEnd) {
224 290 : if (s[pos] == ',') {
225 289 : ++pos;
226 : // RFC 8259 disallows trailing comma: a key must follow the comma.
227 289 : pos = SkipWhiteSpace(s, pos);
228 289 : if ((pos >= objEnd) || (s[pos] != '"')) {
229 1 : return false;
230 : }
231 1 : } else if (s[pos] == '}') {
232 0 : break;
233 : } else {
234 1 : return false;
235 : }
236 : }
237 366 : }
238 413 : for (size_t i = 0U; i < CFG_FIELD_COUNT; ++i) {
239 345 : if (!found[i]) {
240 1 : return false;
241 : }
242 : }
243 68 : return true;
244 : }
245 :
246 : // Find the matching closing '}' for an opening '{' at openPos, skipping nested strings.
247 : // Returns the index of the matching '}' or std::string::npos if unbalanced.
248 100 : size_t FindMatchingBrace(const std::string& s, size_t openPos)
249 : {
250 100 : if ((openPos >= s.size()) || (s[openPos] != '{')) {
251 0 : return std::string::npos;
252 : }
253 100 : size_t pos = openPos + 1U;
254 100 : int depth = 1;
255 5412 : while (pos < s.size()) {
256 5411 : const char_t c = s[pos];
257 5411 : if (c == '"') {
258 901 : std::string ignored;
259 901 : size_t p = pos;
260 901 : if (!ReadJsonString(s, p, ignored)) {
261 1 : return std::string::npos;
262 : }
263 900 : pos = p;
264 900 : continue;
265 901 : }
266 4510 : if (c == '{') {
267 89 : ++depth;
268 4421 : } else if (c == '}') {
269 186 : --depth;
270 186 : if (depth == 0) {
271 98 : return pos;
272 : }
273 : }
274 4412 : ++pos;
275 : }
276 1 : return std::string::npos;
277 : }
278 :
279 : // Find the matching closing ']' for an opening '[' at openPos, skipping nested strings.
280 : // Returns the index of the matching ']' or std::string::npos if unbalanced.
281 2 : size_t FindMatchingBracket(const std::string& s, size_t openPos)
282 : {
283 2 : if ((openPos >= s.size()) || (s[openPos] != '[')) {
284 0 : return std::string::npos;
285 : }
286 2 : size_t pos = openPos + 1U;
287 2 : int depth = 1;
288 16 : while (pos < s.size()) {
289 15 : const char_t c = s[pos];
290 15 : if (c == '"') {
291 1 : std::string ignored;
292 1 : size_t p = pos;
293 1 : if (!ReadJsonString(s, p, ignored)) {
294 0 : return std::string::npos;
295 : }
296 1 : pos = p;
297 1 : continue;
298 1 : }
299 14 : if (c == '[') {
300 1 : ++depth;
301 13 : } else if (c == ']') {
302 2 : --depth;
303 2 : if (depth == 0) {
304 1 : return pos;
305 : }
306 : }
307 13 : ++pos;
308 : }
309 1 : return std::string::npos;
310 : }
311 :
312 : // Skip an arbitrary JSON value starting at pos (after leading whitespace). Used to ignore
313 : // unknown fields in a memzone entry. Returns true on success and advances pos past the
314 : // value; returns false if the value is malformed.
315 5 : bool SkipJsonValue(const std::string& s, size_t& pos)
316 : {
317 5 : pos = SkipWhiteSpace(s, pos);
318 5 : if (pos >= s.size()) {
319 0 : return false;
320 : }
321 5 : const char_t c = s[pos];
322 5 : if (c == '"') {
323 1 : std::string ignored;
324 1 : return ReadJsonString(s, pos, ignored);
325 1 : }
326 4 : if (c == '{') {
327 1 : const size_t end = FindMatchingBrace(s, pos);
328 1 : if (end == std::string::npos) {
329 0 : return false;
330 : }
331 1 : pos = end + 1U;
332 1 : return true;
333 : }
334 3 : if (c == '[') {
335 2 : const size_t end = FindMatchingBracket(s, pos);
336 2 : if (end == std::string::npos) {
337 1 : return false;
338 : }
339 1 : pos = end + 1U;
340 1 : return true;
341 : }
342 : // primitive: number, true, false, null — scan until value terminator.
343 5 : while (pos < s.size()) {
344 5 : const char_t t = s[pos];
345 5 : if ((t == ',') || (t == '}') || (t == ']') || (t == ' ') || (t == '\t') || (t == '\n') || (t == '\r')) {
346 : break;
347 : }
348 4 : ++pos;
349 : }
350 1 : return true;
351 : }
352 : } // namespace
353 :
354 17 : StatusCode AicpuMemInfoProcess::GetMemZoneInfo(BuffCfg& buffCfg)
355 : {
356 17 : aicpusd_info("Start get memzone info!");
357 17 : buffCfg = {}; // default
358 17 : auto ret = CheckRunMode();
359 17 : if (ret != AICPU_SCHEDULE_OK) {
360 1 : return ret;
361 : }
362 :
363 16 : std::string blockModePath = "";
364 16 : const bool envRet = AicpuUtil::GetEnvVal(ENV_NAME_BLOCK_CFG_PATH, blockModePath);
365 16 : if (!envRet) {
366 13 : aicpusd_run_info("The pointer of BLOCK_CFG_PATH is nullptr");
367 13 : return AICPU_SCHEDULE_OK;
368 : }
369 :
370 3 : if ((blockModePath.size() > 0U) && (blockModePath[blockModePath.size() - 1U] != '/')) {
371 2 : (void)blockModePath.append("/");
372 : }
373 :
374 3 : const std::string procName = AicpuDrvManager::GetInstance().GetHostProcName();
375 3 : const std::string memBuffCfgFile = blockModePath + "aifmk/" + procName + ".json";
376 3 : ret = CheckPathValid(memBuffCfgFile);
377 3 : if (ret != AICPU_SCHEDULE_OK) {
378 1 : aicpusd_run_info("The memBufCfgFile path is invalid: [%s]!", memBuffCfgFile.c_str());
379 1 : return AICPU_SCHEDULE_ERROR_GET_PATH_FAILED;
380 : }
381 :
382 2 : ret = LoadMemCfgFromFile(memBuffCfgFile, buffCfg);
383 2 : if (ret != AICPU_SCHEDULE_OK) {
384 1 : aicpusd_run_info("Execute LoadMemCfgFromFile returned [%d].", ret);
385 1 : return AICPU_SCHEDULE_ERROR_READ_JSON_FAILED;
386 : }
387 1 : return AICPU_SCHEDULE_OK;
388 16 : }
389 :
390 24 : StatusCode AicpuMemInfoProcess::LoadMemCfgFromFile(const std::string& filePath, BuffCfg& output)
391 : {
392 24 : aicpusd_info("Read [%s] file", filePath.c_str());
393 24 : std::ifstream ifs(filePath);
394 24 : if (!ifs.is_open()) {
395 1 : aicpusd_run_info("Cant not open [%s], please check!", filePath.c_str());
396 1 : return AICPU_SCHEDULE_ERROR_READ_JSON_FAILED;
397 : }
398 23 : std::string content((std::istreambuf_iterator<char_t>(ifs)), std::istreambuf_iterator<char_t>());
399 23 : ifs.close();
400 :
401 23 : aicpusd_info("Read [%s] file successfully, size is [%zu].", filePath.c_str(), content.size());
402 :
403 : // Locate the outermost '{'
404 23 : size_t pos = SkipWhiteSpace(content, 0U);
405 23 : if ((pos >= content.size()) || (content[pos] != '{')) {
406 2 : aicpusd_run_info("Invalid json: top-level object expected in [%s].", filePath.c_str());
407 2 : return AICPU_SCHEDULE_ERROR_READ_JSON_FAILED;
408 : }
409 21 : size_t topEnd = FindMatchingBrace(content, pos);
410 21 : if (topEnd == std::string::npos) {
411 2 : aicpusd_run_info("Unbalanced braces in json [%s].", filePath.c_str());
412 2 : return AICPU_SCHEDULE_ERROR_READ_JSON_FAILED;
413 : }
414 :
415 19 : size_t entryCount = 0U;
416 19 : pos = pos + 1U; // step past outer '{'
417 85 : while (pos < topEnd) {
418 84 : pos = SkipWhiteSpace(content, pos);
419 84 : if (pos >= topEnd) {
420 2 : break;
421 : }
422 83 : if (content[pos] == '}') {
423 0 : break;
424 : }
425 83 : if (entryCount >= static_cast<size_t>(BUFF_MAX_CFG_NUM)) {
426 1 : aicpusd_run_info("Json entry count exceeds BUFF_MAX_CFG_NUM[%d], truncating.", BUFF_MAX_CFG_NUM);
427 1 : break;
428 : }
429 : // Read top-level key (must be a quoted string, expected numeric like "0","1",...)
430 82 : std::string key;
431 82 : if (!ReadJsonString(content, pos, key)) {
432 1 : aicpusd_run_info("Invalid top-level key at pos [%zu] in [%s].", pos, filePath.c_str());
433 1 : return AICPU_SCHEDULE_ERROR_READ_JSON_FAILED;
434 : }
435 : // Legacy semantics: top-level keys must be contiguous decimal integers starting at
436 : // "0" (the original parsing logic iterated i=0..N and required the key std::to_string(i)).
437 81 : const std::string expectedKey = std::to_string(entryCount);
438 81 : if (key != expectedKey) {
439 1 : aicpusd_run_info(
440 : "Top-level key [%s] does not match expected [%s] at index [%zu] in [%s].", key.c_str(),
441 : expectedKey.c_str(), entryCount, filePath.c_str());
442 1 : return AICPU_SCHEDULE_ERROR_READ_JSON_FAILED;
443 : }
444 80 : pos = SkipWhiteSpace(content, pos);
445 80 : if ((pos >= topEnd) || (content[pos] != ':')) {
446 1 : aicpusd_run_info("Expected ':' after key [%s] in [%s].", key.c_str(), filePath.c_str());
447 1 : return AICPU_SCHEDULE_ERROR_READ_JSON_FAILED;
448 : }
449 79 : ++pos;
450 79 : pos = SkipWhiteSpace(content, pos);
451 79 : if ((pos >= topEnd) || (content[pos] != '{')) {
452 1 : aicpusd_run_info("Expected object value for key [%s] in [%s].", key.c_str(), filePath.c_str());
453 1 : return AICPU_SCHEDULE_ERROR_READ_JSON_FAILED;
454 : }
455 78 : size_t entryEnd = FindMatchingBrace(content, pos);
456 78 : if (entryEnd == std::string::npos) {
457 0 : aicpusd_run_info("Unbalanced entry object for key [%s] in [%s].", key.c_str(), filePath.c_str());
458 0 : return AICPU_SCHEDULE_ERROR_READ_JSON_FAILED;
459 : }
460 78 : if (!ParseOneEntry(content, pos, entryEnd, output.cfg[entryCount])) {
461 10 : aicpusd_run_info("Failed to parse entry for key [%s] in [%s].", key.c_str(), filePath.c_str());
462 10 : return AICPU_SCHEDULE_ERROR_READ_JSON_FAILED;
463 : }
464 68 : ++entryCount;
465 68 : pos = entryEnd + 1U;
466 68 : pos = SkipWhiteSpace(content, pos);
467 68 : if (pos < topEnd) {
468 67 : if (content[pos] == ',') {
469 66 : ++pos;
470 : // RFC 8259 disallows trailing comma: a key must follow the comma.
471 66 : pos = SkipWhiteSpace(content, pos);
472 66 : if ((pos >= topEnd) || (content[pos] != '"')) {
473 1 : aicpusd_run_info("Trailing comma or missing key after entry in [%s].", filePath.c_str());
474 1 : return AICPU_SCHEDULE_ERROR_READ_JSON_FAILED;
475 : }
476 1 : } else if (content[pos] == '}') {
477 0 : break;
478 : } else {
479 1 : aicpusd_run_info("Unexpected char [%c] after entry in [%s].", content[pos], filePath.c_str());
480 1 : return AICPU_SCHEDULE_ERROR_READ_JSON_FAILED;
481 : }
482 : }
483 97 : }
484 3 : aicpusd_info("Parsed [%zu] memzone cfg entries from [%s].", entryCount, filePath.c_str());
485 3 : return AICPU_SCHEDULE_OK;
486 24 : }
487 :
488 5 : StatusCode AicpuMemInfoProcess::CheckPathValid(const std::string& cfgFullPath)
489 : {
490 5 : if (cfgFullPath.length() >= static_cast<size_t>(PATH_MAX)) {
491 1 : aicpusd_run_info("cfgFullPath file length[%zu] must less than PATH_MAX[%u]", cfgFullPath.length(), PATH_MAX);
492 1 : return AICPU_SCHEDULE_ERROR_GET_PATH_FAILED;
493 : }
494 :
495 4 : std::unique_ptr<char_t[]> path(new (std::nothrow) char_t[PATH_MAX]);
496 4 : if (path == nullptr) {
497 0 : aicpusd_run_info("Alloc memory for path failed.");
498 0 : return AICPU_SCHEDULE_ERROR_GET_PATH_FAILED;
499 : }
500 :
501 4 : const auto eRet = memset_s(path.get(), PATH_MAX, 0, PATH_MAX);
502 4 : if (eRet != EOK) {
503 1 : aicpusd_run_info("Mem set was not successful, ret=%d", eRet);
504 1 : return AICPU_SCHEDULE_ERROR_GET_PATH_FAILED;
505 : }
506 :
507 3 : if (realpath(cfgFullPath.data(), path.get()) == nullptr) {
508 1 : aicpusd_run_info("Check cfg file full path:[%s], path:[%s]", cfgFullPath.c_str(), path.get());
509 1 : return AICPU_SCHEDULE_ERROR_GET_PATH_FAILED;
510 : }
511 2 : const std::string normalPath(path.get());
512 2 : if (normalPath != cfgFullPath) {
513 1 : aicpusd_run_info("Invalid mem cfg file:[%s], should be [%s]", cfgFullPath.c_str(), normalPath.c_str());
514 1 : return AICPU_SCHEDULE_ERROR_GET_PATH_FAILED;
515 : }
516 1 : aicpusd_info("Check mem cfg file [%s] success.", cfgFullPath.c_str());
517 1 : return AICPU_SCHEDULE_OK;
518 4 : }
519 :
520 15 : StatusCode AicpuMemInfoProcess::CheckRunMode()
521 : {
522 : uint32_t runMode;
523 15 : const aicpu::status_t status = aicpu::GetAicpuRunMode(runMode);
524 15 : if (status != aicpu::AICPU_ERROR_NONE) {
525 1 : aicpusd_err("GetAicpuRunMode returned [%u]", status);
526 1 : return AICPU_SCHEDULE_ERROR_GET_RUN_MODE_FAILED;
527 : }
528 14 : if (runMode != static_cast<uint32_t>(aicpu::AicpuRunMode::PROCESS_SOCKET_MODE)) {
529 8 : aicpusd_run_info("Current aicpu mode is not MDC, please check!");
530 8 : return AICPU_SCHEDULE_OK;
531 : }
532 6 : return AICPU_SCHEDULE_OK;
533 : }
534 : } // namespace AicpuSchedule
|