【NPU】Ascend Device Plugin — Part 1 入口层 + 工厂层 超深度源码分析之二
·
五、devicefactory/customname/public_name_config.go — 设备自定义命名
5.1 模块定位
| 维度 | 说明 |
|---|---|
| 业务职责 | 管理设备的自定义公共名称映射。允许用户通过 JSON 配置文件自定义设备在 Kubernetes 中暴露的资源名称、设备名称前缀和 Pod 注解名称 |
| 功能定位 | Name Mapping / Adapter:在内部设备类型名称与外部公开名称之间建立映射关系,支持新旧两套命名体系 |
| 在系统中的位置 | 被 entry.go 在工厂初始化最早期调用,被 server 层在设备注册、节点标注、Pod注解处理时调用 |
5.2 模块整体结构
5.2.1 常量与正则定义
const nameConfigFilePath = "/usr/local/deviceNameCustomization.json" // 配置文件路径
// 设备类型正则:字母开头,允许字母数字./,长度10-32,字母数字结尾
var deviceTypePattern = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9./]{8,30}[a-zA-Z0-9]$`)
// 设备名称前缀正则:字母开头,允许字母数字-,长度2-16
var deviceNamePattern = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9-]{1,15}$`)
// Pod配置名称正则:字母开头,允许字母数字./-,长度10-63,字母数字结尾
var podConfigurationPattern = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9./-]{8,61}[a-zA-Z0-9]$`)
5.2.2 全局变量
var devNameMap = map[string]DevName{} // 核心映射表:ResourceType → DevName
// 旧设备类型集合 — 判断是否使用旧命名体系
var oldDeviceTypes = map[string]bool{
api.Ascend310: true, // "Ascend310"
api.Ascend310P: true, // "Ascend310P"
api.Ascend310B: true, // "Ascend310B"
api.Ascend910A: true, // "Ascend910" (910A)
api.Ascend910B: true, // "Ascend910B"
api.Ascend910A3: true, // "Ascend910A3"
}
oldDeviceTypes 的意义:
- 旧设备类型使用
huawei.com/Ascend910、huawei.com/Ascend310等资源名- 新设备类型(如 A5)统一使用
huawei.com/npu资源名- 这个映射表决定了名称替换的分支逻辑
5.2.3 DevName 结构体
5.2.4 核心方法清单
| 方法 | 签名 | 作用 |
|---|---|---|
InitPublicNameConfig |
func() |
从 JSON 文件加载配置,校验后填充 devNameMap |
setDefaultName |
func(DevName) DevName |
为旧设备类型填充 Old* 字段 |
IsOldDeviceType |
func(string) bool |
判断设备类型是否属于旧设备类型 |
getResourceName |
func(string) string |
根据设备类型返回资源名(旧名 or huawei.com/npu) |
getResourceNamePrefix |
func(string) string |
根据设备类型返回资源名前缀 |
getResourceNamePrefixInner |
func(string, DevName) string |
内部使用的资源名前缀替换 |
getPodAnnotationWithPublicName |
func(map, DevName) map |
获取带公共名称的Pod注解 |
ReplaceDevicePublicName |
func(string, string) string |
替换设备公共名称前缀 |
ReplaceDeviceInnerName |
func(string, []string) []string |
替换设备内部名称 |
ReplaceDevicePublicType |
func(string, string) string |
替换设备公共类型 |
ReplacePodAnnotation |
func(string, map) map |
替换Pod注解的key和value |
ReplaceDeviceInfoPublicName |
func(string, map, string, string) (map, string, string) |
批量替换设备信息中的名称 |
checkName |
func([]DevName) bool |
校验自定义命名配置的合法性 |
loadFaultCodeFromFile |
func() ([]DevName, error) |
从文件加载JSON配置 |
5.3 核心业务逻辑深度解析
5.3.1 InitPublicNameConfig() 完整流程
func InitPublicNameConfig() {
// 从文件加载自定义命名配置
devNames, err := loadFaultCodeFromFile()
if err != nil {
// 配置文件不存在或解析失败不报错,使用默认命名
hwlog.RunLog.Infof("do not use the custom device name, because %v", err)
return
}
// 校验配置内容合法性
if !checkName(devNames) {
hwlog.RunLog.Warn("the custom name configuration is invalid")
return
}
// 逐个处理并写入全局映射表
for _, devName := range devNames {
devName = setDefaultName(devName) // 填充 Old* 字段
devNameMap[devName.ResourceType] = devName // 以 ResourceType 为 key
}
hwlog.RunLog.Infof("the custom name takes effect: %v", devNameMap)
}
5.3.2 setDefaultName() — 旧名称默认值填充
func setDefaultName(devName DevName) DevName {
// 根据 ResourceType 设置对应的旧名称体系
// 这些旧名称是硬编码的,对应 Kubernetes 资源注册时的历史命名
switch devName.ResourceType {
case api.Ascend910: // "Ascend910"
devName.OldDevicePublicType = api.HuaweiAscend910 // "huawei.com/Ascend910"
devName.OldDevicePublicNamePre = api.Ascend910MinuxPrefix // "Ascend910-"
devName.OldPodConfigurationName = api.Pod910DeviceAnno // "ascend.kubectl.kubernetes.io/ascend-910-configuration"
case api.Ascend310: // "Ascend310"
devName.OldDevicePublicType = api.HuaweiAscend310 // "huawei.com/Ascend310"
devName.OldDevicePublicNamePre = api.Ascend310MinuxPrefix // "Ascend310-"
// 注意:310 没有 PodConfigurationName
case api.Ascend310P: // "Ascend310P"
devName.OldDevicePublicType = api.HuaweiAscend310P // "huawei.com/Ascend310P"
devName.OldDevicePublicNamePre = api.Ascend310PMinuxPrefix // "Ascend310P-"
default:
hwlog.RunLog.Errorf("devicePublicType(%s) is invalid ", devName.DevicePublicType)
}
return devName
}
命名映射关系图:
5.3.3 checkName() — 配置校验
func checkName(devNames []DevName) bool {
for _, devName := range devNames {
// 必填字段检查
if len(devName.DevicePublicNamePre) == 0 || len(devName.ResourceType) == 0 ||
len(devName.DevicePublicType) == 0 {
hwlog.RunLog.Error("all name should not be null")
return false
}
// ResourceType 枚举校验:仅支持 310, 310P, 910
if devName.ResourceType != api.Ascend310 &&
devName.ResourceType != api.Ascend310P &&
devName.ResourceType != api.Ascend910 {
hwlog.RunLog.Errorf("resourceType only support %s, %s, %s",
api.Ascend310, api.Ascend310P, api.Ascend910)
return false
}
// DevicePublicType 正则校验:字母开头,允许字母数字./,长度10-32
if !deviceTypePattern.MatchString(devName.DevicePublicType) {
hwlog.RunLog.Errorf("devicePublicType(%s) is invalid", devName.DevicePublicType)
return false
}
// DevicePublicNamePre 正则校验:字母开头,允许字母数字-,长度2-16
if !deviceNamePattern.MatchString(devName.DevicePublicNamePre) {
hwlog.RunLog.Errorf("devicePublicNamePre(%s) is invalid", devName.DevicePublicNamePre)
return false
}
// PodConfigurationName 可选,非空时校验
if len(devName.PodConfigurationName) != 0 &&
!podConfigurationPattern.MatchString(devName.PodConfigurationName) {
hwlog.RunLog.Errorf("podConfigurationName(%s) is invalid", devName.PodConfigurationName)
return false
}
}
return true
}
5.3.4 Replace 系列函数 — 名称替换核心逻辑
ReplaceDevicePublicName():
func ReplaceDevicePublicName(resourceType string, oldName string) string {
// 从全局映射表获取该设备类型的命名配置
devName := devNameMap[resourceType]
// 如果没有自定义名称前缀,使用默认逻辑
if len(devName.DevicePublicNamePre) == 0 {
return getResourceNamePrefix(oldName)
}
// 有自定义名称前缀:将旧前缀替换为自定义前缀
// 例如: "Ascend910-0" → "custom910-0"
return strings.ReplaceAll(oldName, devName.OldDevicePublicNamePre, devName.DevicePublicNamePre)
}
ReplaceDevicePublicType():
func ReplaceDevicePublicType(resourceType string, oldName string) string {
devName := devNameMap[resourceType]
if len(devName.DevicePublicType) == 0 {
// 无自定义:旧设备类型用旧名,新设备类型用 huawei.com/npu
return getResourceName(oldName)
}
// 有自定义:将旧公开类型替换为自定义类型
// 例如: "huawei.com/Ascend910" → "huawei.com/custom910"
return strings.ReplaceAll(oldName, devName.OldDevicePublicType, devName.DevicePublicType)
}
ReplacePodAnnotation():
ReplaceDeviceInfoPublicName():
func ReplaceDeviceInfoPublicName(resourceType string, deviceList map[string]string,
deviceName string, reasonCm string) (map[string]string, string, string) {
devName := devNameMap[resourceType]
if len(devName.DevicePublicNamePre) == 0 {
// 无自定义名称前缀的分支
if !oldDeviceTypes[common.ParamOption.RealCardType] {
// 新设备类型:将所有 "huawei.com/Ascend910" 替换为 "huawei.com/npu"
// 并将所有名称前缀统一为 "npu-"
newDeviceList := make(map[string]string, len(deviceList))
for key, value := range deviceList {
newKey := strings.ReplaceAll(key, api.HuaweiAscend910, api.HuaweiNPU)
newValue := getResourceNamePrefix(value)
newDeviceList[newKey] = newValue
}
newDeviceName := getResourceNamePrefix(deviceName)
newReasonCm := getResourceNamePrefix(reasonCm)
return newDeviceList, newDeviceName, newReasonCm
}
// 旧设备类型:原样返回
return deviceList, deviceName, reasonCm
}
// 有自定义名称前缀的分支:批量替换 key 和 value 中的前缀
newDeviceList := make(map[string]string, len(deviceList))
for key, value := range deviceList {
newKey := strings.ReplaceAll(key, devName.OldDevicePublicType, devName.DevicePublicType)
newValue := strings.ReplaceAll(value, devName.OldDevicePublicNamePre, devName.DevicePublicNamePre)
newDeviceList[newKey] = newValue
}
newDeviceName := strings.ReplaceAll(deviceName, devName.OldDevicePublicNamePre, devName.DevicePublicNamePre)
newReasonCm := strings.ReplaceAll(reasonCm, devName.OldDevicePublicNamePre, devName.DevicePublicNamePre)
return newDeviceList, newDeviceName, newReasonCm
}
Replace 系列设计模式:
- 双分支策略:每个 Replace 函数都有"有自定义配置"和"无自定义配置"两个分支
- 无自定义配置分支:再分"旧设备类型"和"新设备类型"两个子分支
- 新设备类型:统一使用
huawei.com/npu和npu-前缀,实现命名归一化- 有自定义配置分支:按用户配置进行前缀/类型名替换
5.3.5 loadFaultCodeFromFile() — 配置文件加载
func loadFaultCodeFromFile() ([]DevName, error) {
// 使用 utils.LoadFile 读取配置文件(最大限制由 utils 内部控制)
faultCodeBytes, err := utils.LoadFile(nameConfigFilePath)
if err != nil {
return nil, fmt.Errorf("load name config json failed: %v", err)
}
// JSON 反序列化为 DevName 切片
var devNames []DevName
if err := json.Unmarshal(faultCodeBytes, &devNames); err != nil {
return nil, fmt.Errorf("unmarshal fault code byte failed: %v", err)
}
return devNames, nil
}
函数名遗留问题:
loadFaultCodeFromFile命名带有 “FaultCode” 是历史遗留,实际加载的是设备名称配置。配置文件路径常量nameConfigFilePath是正确的。
5.3.6 命名体系完整决策树
六、topology/topo.go — 拓扑文件读写
6.1 模块定位
| 维度 | 说明 |
|---|---|
| 业务职责 | 提供拓扑文件的读取、SHA256 哈希比较和写入功能。确保拓扑文件内容变更时才写入磁盘,避免无意义的 I/O |
| 功能定位 | File I/O Utility:纯文件操作,无业务逻辑,被 rack_topology.go 调用 |
| 在系统中的位置 | 拓扑模块的底层工具层,被 RasTopoWriteTask 调用 |
6.2 模块整体结构
6.2.1 常量定义
const (
Sha256HashLength = 32 // SHA256 哈希字节数
maxRackNumPerSuperPod = 256 // 每个SuperPod最大Rack数
maxSuperPodNum = 64 // 最大SuperPod数
defaultPerm = 0644 // 默认文件权限
rackDirPerm = 0755 // Rack目录权限
publishCmNamePrefix = "super-pod" // ConfigMap名称前缀
size50M = 50 * 1024 * 1024 // 文件最大50MB
)
6.2.2 结构体
6.2.3 核心方法清单
| 方法 | 签名 | 作用 |
|---|---|---|
topoFileToStr |
func(string) (string, error) |
读取拓扑文件并返回字符串,校验JSON合法性 |
getFileHash |
func(string) ([32]byte, error) |
计算文件内容的 SHA256 哈希 |
ToFile |
func(filePath, orgFile string) error |
将源文件内容写入目标文件(带哈希比较,内容相同不写入) |
6.3 核心业务逻辑深度解析
6.3.1 topoFileToStr() — 读取并校验拓扑文件
func topoFileToStr(orgFile string) (string, error) {
// 读取源文件,限制最大50MB防止内存溢出
topoData, err := utils.ReadLimitBytes(orgFile, size50M)
if err != nil {
return "", fmt.Errorf("read topo file failed, path:<%v>; err:<%v>", orgFile, err)
}
// 校验文件内容是否为合法JSON
if !json.Valid(topoData) {
return "", fmt.Errorf("topo file is not json, path:<%v>", orgFile)
}
return string(topoData), nil
}
安全设计:
ReadLimitBytes限制50MB,防止超大文件导致OOM- JSON合法性校验,防止写入无效拓扑数据
6.3.2 getFileHash() — 计算文件哈希
func getFileHash(filePath string) ([Sha256HashLength]byte, error) {
// 读取文件全部内容
fileContent, err := os.ReadFile(filePath)
if err != nil {
return [Sha256HashLength]byte{}, err
}
// 返回 SHA256 哈希值(32字节)
return sha256.Sum256(fileContent), nil
}
6.3.3 ToFile() — 带哈希比较的文件写入
func ToFile(filePath string, orgFile string) error {
// 步骤1: 读取源文件内容并校验JSON
newTopoStr, err := topoFileToStr(orgFile)
if err != nil {
return err
}
// 步骤2: 计算新内容的SHA256哈希
newHash := sha256.Sum256([]byte(newTopoStr))
var originalHash [Sha256HashLength]byte
// 步骤3: 检查目标文件是否存在
if _, err := os.Stat(filePath); err != nil {
if !os.IsNotExist(err) {
return err // 非文件不存在错误,返回
}
// 文件不存在:originalHash 保持零值,必然与 newHash 不同
} else {
// 文件存在:读取原文件哈希
originalHash, err = getFileHash(filePath)
if err != nil {
return err
}
}
// 步骤4: 比较哈希,内容相同则跳过写入
if newHash == originalHash {
hwlog.RunLog.Infof("The file %s is up to date, no need to write", filePath)
return nil
}
// 步骤5: 内容不同(或文件不存在),写入新内容
f, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, defaultPerm)
if err != nil {
return err
}
defer f.Close()
if _, err = f.WriteString(newTopoStr); err != nil {
return err
}
// 步骤6: 确保文件权限正确
if err = os.Chmod(filePath, defaultPerm); err != nil {
return err
}
return nil
}
设计亮点:
- 哈希比较避免无效写入:周期性调用时(30秒一次),如果拓扑未变化则零I/O
- 原子性保证:
O_TRUNC先清空再写入,虽然非原子操作,但对于拓扑配置可接受- 权限显式设置:
Chmod确保 umask 不会导致权限异常- JSON校验前置:写入前校验,防止写入损坏数据
七、topology/rack_topology.go — Rack拓扑写入任务
7.1 模块定位
| 维度 | 说明 |
|---|---|
| 业务职责 | 作为后台 goroutine 周期性地将 Rack 拓扑信息写入 RAS(Reliability Availability Serviceability)文件路径,供慢网(slownet)模块使用 |
| 功能定位 | Background Task:长周期循环任务,每30秒检查并更新拓扑文件 |
| 在系统中的位置 | 被 main.go 以 goroutine 启动,仅对 A5 芯片生效 |
7.2 模块整体结构
7.2.1 全局变量
var topoFilePathMap = map[int8]string{
common.ProductTypeServer: common.Server8PTopoPath, // 0 → /usr/local/Ascend/driver/topo/950/atlas_850_1.json
common.ProductType1D: common.Pod1DTopoPath, // 1 → /usr/local/Ascend/driver/topo/950/atlas_950_1.json
common.ProductType2D: common.Pod2DTopoPath, // 2 → /usr/local/Ascend/driver/topo/950/atlas_950_2.json
common.ProductType16PServer: common.Server16PTopoPath, // 3 → /usr/local/Ascend/driver/topo/950/atlas_850_2.json
common.ProductType32PServer: common.Server32PTopoPath, // 4 → /usr/local/Ascend/driver/topo/950/atlas_850_3.json
}
var (
superPodId int32 // SuperPod ID(从 HwDevManager 获取)
superPodType int8 // SuperPod 类型(决定源拓扑文件路径)
rackId int32 // Rack ID(决定目标文件路径)
serverIndex int32 // Server索引(决定目标文件路径)
)
SuperPod类型与源拓扑文件映射:
7.2.2 核心方法清单
| 方法 | 签名 | 作用 |
|---|---|---|
GetTopoFileAndWrite |
func(topoJsonFile string) |
根据superPodType查找源拓扑路径,调用ToFile写入 |
RasTopoWriteTask |
func(ctx, *HwDevManager) |
后台goroutine主循环:初始化→每30秒检查并写入拓扑 |
checkConfigReady |
func(superPodIdStr string) (string, bool) |
检查RAS环境是否就绪,返回目标文件路径 |
7.3 核心业务逻辑深度解析
7.3.1 RasTopoWriteTask() — 后台任务主循环
func RasTopoWriteTask(ctx context.Context, hdm *server.HwDevManager) {
// ★ 前置检查1: 仅A5芯片需要此任务
if common.ParamOption.RealCardType != common.Ascend910A5 {
hwlog.RunLog.Infof("current is not %s, no need start RasTopoWriteTask", api.HuaweiNPU)
return
}
// ★ 前置检查2: hdm 不能为nil
if hdm == nil {
hwlog.RunLog.Error("illegal input, hdm is nil")
return
}
// ★ 前置检查3: RAS网络根路径必须存在
_, err := slownet.GetRasNetRootPath()
if err != nil {
hwlog.RunLog.Errorf("get ras net root path failed, err: %v", err)
return
}
// ★ 初始化阶段:从 HwDevManager 读取拓扑信息(加锁保护)
hdm.ManagerLock.Lock()
superPodId = hdm.GetSuperPodID() // SuperPod ID
superPodType = hdm.GetSuperPodType() // SuperPod 类型(决定源拓扑文件)
rackId = hdm.GetRackID() // Rack ID
serverIndex = hdm.GetDevManager().GetServerIndex() // Server 索引
hdm.ManagerLock.Unlock()
// ★ 主循环:每30秒检查并写入拓扑文件
for {
select {
case _, ok := <-ctx.Done():
if !ok {
hwlog.RunLog.Info("catch stop signal channel closed")
}
hwlog.RunLog.Info("RasTopoWriteTask stop")
return
default:
// 检查配置是否就绪,就绪则写入拓扑文件
if filePath, ok := checkConfigReady(fmt.Sprintf("%d", superPodId)); ok {
hwlog.RunLog.Infof("ready to wirte topology file of rack to %s", filePath)
GetTopoFileAndWrite(filePath)
}
// 30秒后重试
time.Sleep(common.TopologyRefreshTime * time.Second) // 30秒
}
}
}
设计要点:
- A5专属功能:仅 Ascend910A5 芯片启动此任务,其他芯片直接返回
- 初始化加锁:读取 HwDevManager 状态时使用
ManagerLock,防止并发修改- 全局变量赋值:
superPodId、superPodType、rackId、serverIndex是包级变量,初始化后只读- ctx 优雅退出:通过
select { case <-ctx.Done(): }感知上下文取消- default 分支:ctx 未取消时执行
checkConfigReady,然后 sleep 30秒
7.3.2 checkConfigReady() — 前置条件检查
func checkConfigReady(superPodIdStr string) (string, bool) {
// 步骤1: 获取 super-pod-x.json 的文件路径
// slownet 模块根据 superPodId 和 ConfigMap 前缀构建路径
filePath, err := slownet.GetSuperPodInfoFilePath(superPodIdStr, publishCmNamePrefix)
if err != nil {
hwlog.RunLog.Errorf("get superpod topo file path err: %v", err)
return "", false
}
// 步骤2: 检查 super-pod-x 父目录是否存在
fileParentDir := filepath.Dir(filePath)
if !utils.IsLexist(fileParentDir) {
hwlog.RunLog.Infof("superpod topo file parent dir %s is not exist", fileParentDir)
return "", false // 父目录不存在说明super-pod配置尚未就绪
}
hwlog.RunLog.Infof("the super-pod-x dir is exist in %s", fileParentDir)
// 步骤3: 获取 Rack 拓扑文件的目标路径
// 路径包含 superPodId、rackId、serverIndex 信息
topoFile, err := slownet.GetRackTopologyFilePath(superPodId, rackId, serverIndex)
if err != nil {
hwlog.RunLog.Errorf("get rack topo file path err: %v", err)
return "", false
}
// 步骤4: 确保 Rack 目录存在
rackDir := filepath.Dir(topoFile)
if !utils.IsLexist(rackDir) {
hwlog.RunLog.Infof("%s topo path is not exist and will create it", rackDir)
if mkErr := os.MkdirAll(rackDir, rackDirPerm); mkErr != nil {
hwlog.RunLog.Errorf("create dir failed rack dir %s, err: %v", rackDir, mkErr)
return "", false
}
hwlog.RunLog.Infof("create rack dir %s success", rackDir)
}
// 步骤5: 确保 Rack 目录权限正确
if chErr := os.Chmod(rackDir, rackDirPerm); chErr != nil {
hwlog.RunLog.Errorf("change mod failed rack dir %s, err: %v", rackDir, chErr)
return "", false
}
return topoFile, true
}
设计意图:
- 两阶段就绪检查:先检查 super-pod 配置就绪,再检查 rack 目录就绪
- 自动创建目录:rack 目录不存在时自动创建,适应首次运行场景
- 权限显式设置:
Chmod确保 0755 权限,即使MkdirAll受 umask 影响- 幂等性:每次循环都检查,目录已存在则跳过创建
7.3.3 GetTopoFileAndWrite() — 写入拓扑文件
func GetTopoFileAndWrite(topoJsonFile string) {
// 根据superPodType查找对应的源拓扑文件路径
path, exist := topoFilePathMap[superPodType]
if !exist {
hwlog.RunLog.Errorf("super pod type:<%d> topo path not exist", superPodType)
return
}
// 调用 topo.go 的 ToFile 函数:从源路径读取,写入目标路径
if err := ToFile(topoJsonFile, path); err != nil {
hwlog.RunLog.Errorf("write topology info of RackID=%d SuperPodID=%d failed, err is %s",
rackId, superPodId, err.Error())
}
hwlog.RunLog.Infof("write rack %d topology info to file %s success", rackId, topoJsonFile)
}
注意:此处
path是源文件(驱动生成的拓扑文件),topoJsonFile是目标文件(RAS路径)。ToFile(目标, 源)的参数顺序需要注意。
7.3.4 完整数据流
八、模块间调用关系总览
8.1 完整调用图
8.2 数据流总览
8.3 错误处理策略总览
8.4 并发模型
并发设计要点:
- 3个后台 goroutine 共享同一个
ctx,通过cancel()统一通知退出CheckDuplicateDevices是同步调用,在SignCatch之前完成SignCatch是阻塞调用,主 goroutine 在此等待信号- 收到信号 →
cancel()→ ctx.Done() → 各 goroutine 退出 → 主 goroutine 返回
附录:文件索引
| 文件 | 行数 | 职责 |
|---|---|---|
main.go |
343 | 程序入口、参数解析、参数校验、全局配置注入、生命周期编排 |
pkg/next/devicefactory/entry.go |
47 | 工厂入口函数,协调命名初始化、驱动初始化、管理器创建 |
pkg/next/devicefactory/driver_init.go |
43 | 底层驱动自动初始化,交换设备条件初始化 |
pkg/next/devicefactory/customname/public_name_config.go |
274 | 设备自定义命名映射,Replace系列名称替换函数 |
pkg/topology/topo.go |
116 | 拓扑文件读取、SHA256哈希比较、文件写入 |
pkg/topology/rack_topology.go |
138 | A5芯片Rack拓扑后台写入任务,30秒循环 |
文档生成时间:2026-07-31
分析基于:mind-cluster-v26.0.1 / ascend-device-plugin
鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。
更多推荐

所有评论(0)