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