【NPU】Ascend Device Plugin — Part 1 入口层 + 工厂层 超深度源码分析之一
Ascend Device Plugin — 入口层 + 工厂层 超深度源码分析
分析范围:
main.go、pkg/next/devicefactory/entry.go、pkg/next/devicefactory/driver_init.go、pkg/next/devicefactory/customname/public_name_config.go、pkg/topology/topo.go、pkg/topology/rack_topology.go
目录
- 一、全局架构总览
- 二、main.go — 程序入口与参数校验
- 三、devicefactory/entry.go — 设备工厂入口
- 四、devicefactory/driver_init.go — 驱动初始化
- 五、devicefactory/customname/public_name_config.go — 设备自定义命名
- 六、topology/topo.go — 拓扑文件读写
- 七、topology/rack_topology.go — Rack拓扑写入任务
- 八、模块间调用关系总览
一、全局架构总览
本组文件构成了 Ascend Device Plugin 的启动入口 → 工厂初始化 → 设备命名定制 → 拓扑写入 四层链路。
启动时序图
二、main.go — 程序入口与参数校验
2.1 模块定位
| 维度 | 说明 |
|---|---|
| 业务职责 | Ascend Device Plugin 的唯一入口点(package main)。负责命令行参数解析、日志初始化、全局参数校验、全局配置注入、驱动工厂调用、并发生命周期管理(设备监听 / DPU监听 / 拓扑写入 / 重复检测 / 信号捕获) |
| 功能定位 | Bootstrap + Orchestrator:不包含具体设备管理逻辑,仅做"编排"——组装各组件并启动后台 goroutine |
| 在系统中的位置 | Kubernetes Device Plugin 进程入口。运行在节点上,向 kubelet 注册设备资源 |
2.2 模块整体结构
2.2.1 常量定义
const (
defaultLogPath = "/var/log/mindx-dl/devicePlugin/devicePlugin.log" // 默认日志路径(中心场景)
defaultListWatchPeriod = 5 // 默认设备状态监听周期(秒)
maxListWatchPeriod = 1800 // 最大监听周期
minListWatchPeriod = 3 // 最小监听周期
maxLogLineLength = 1024 // 单行日志最大长度
defaultLinkdownTimeout = 30 // 默认链路断开超时(秒)
maxLinkdownTimeout = 30 // 最大链路断开超时
minLinkdownTimeout = 1 // 最小链路断开超时
)
2.2.2 命令行 Flag 定义
2.2.3 全局变量与构建期注入
var (
// 通过 -ldflags 在构建时注入
BuildName string // 应用名称
BuildVersion string // 应用版本
BuildScene string // 启动场景: "center" 或 "edge"
)
设计目的:构建时通过
-ldflags "-X main.BuildVersion=xxx"注入版本信息,运行时通过--version输出。BuildScene决定日志路径和边缘/中心场景行为。
2.2.4 核心方法清单
| 方法 | 签名 | 作用 |
|---|---|---|
initLogModule |
func(ctx) error |
根据 fdFlag 选择日志路径,初始化 hwlog 运行时日志 |
checkParam |
func() bool |
编排13项参数校验,任一失败即终止 |
checkListWatchPeriod |
func() bool |
校验监听周期范围 [3, 1800] |
checkPresetAndVolcanoRelation |
func() bool |
preset=false 时 volcano 必须为 true |
checkUse310PMixedInsertWithVolcano |
func() bool |
310P混合插卡与Volcano互斥 |
checkUse310PMixedInsertWithShareDevCount |
func() bool |
310P混合插卡时 shareDevCount 必须为1 |
checkPresetWithShareDevCount |
func() bool |
preset=false 时 shareDevCount 必须为1 |
checkSoftShareDevConfigDir |
func() bool |
软共享配置目录校验(绝对路径、shareDevCount=100、目录存在) |
checkHotResetMode |
func() bool |
热复位模式枚举值校验 |
checkBuildScene |
func() bool |
构建场景必须为 edge 或 center |
checkLinkdownTimeout |
func() bool |
链路断开超时范围校验 [1, 30] |
checkThirdPartyScanDelay |
func() bool |
三方扫描延迟非负校验 |
checkDeviceResetTimeout |
func() bool |
设备复位超时范围校验 [10, 600] |
checkShareDevCount |
func() bool |
共享设备数范围校验 [1, 100] |
main |
func() |
程序入口:解析→日志→校验→配置→工厂→启动 |
setParameters |
func() |
将所有 Flag 值写入 common.ParamOption 全局结构体 |
setUseAscendDocker |
func() |
根据环境变量和运行模式调整 useAscendDocker |
2.3 核心业务逻辑深度解析
2.3.1 main() 函数完整执行流程
2.3.2 initLogModule() 逐行解析
func initLogModule(ctx context.Context) error {
var loggerPath string
loggerPath = *logFile // 默认使用中心场景日志路径
if *fdFlag {
loggerPath = *edgeLogFile // FD系统模式下使用边缘场景日志路径
}
// 检查日志文件用户与进程用户是否一致(安全检查)
if !common.CheckFileUserSameWithProcess(loggerPath) {
return fmt.Errorf("check log file failed")
}
// 构建hwlog配置结构体
hwLogConfig := hwlog.LogConfig{
LogFileName: loggerPath, // 日志文件名
LogLevel: *logLevel, // 日志级别: -1(debug)~3(critical)
MaxBackups: *logMaxBackups, // 最大备份文件数
MaxAge: *logMaxAge, // 日志保留天数
MaxLineLength: maxLogLineLength, // 单行最大1024字符
}
// 初始化运行时日志器
if err := hwlog.InitRunLogger(&hwLogConfig, ctx); err != nil {
fmt.Printf("log init failed, error is %v\n", err) // 日志未就绪,用fmt输出
return err
}
return nil
}
设计意图:
- 双场景日志路径切换:中心场景
/var/log/mindx-dl/devicePlugin/devicePlugin.log,边缘场景/var/alog/AtlasEdge_log/devicePlugin.log- 文件用户一致性检查防止权限泄漏
ctx传入日志模块,支持随进程退出优雅关闭日志
2.3.3 checkParam() 参数校验链
逐个校验函数详解:
func checkListWatchPeriod() bool {
// listWatchPeriod 控制 ListAndWatch gRPC 流的轮询间隔
// 过小浪费CPU,过大延迟感知设备状态变化
if *listWatchPeriod < minListWatchPeriod || *listWatchPeriod > maxListWatchPeriod {
hwlog.RunLog.Errorf("list and watch period %d out of range", *listWatchPeriod)
return false
}
return true
}
func checkPresetAndVolcanoRelation() bool {
// presetVirtualDevice=false 表示关闭算力切分预置
// 此时必须使用 volcano 调度器来处理虚拟设备调度
if !(*presetVirtualDevice) && !(*volcanoType) {
hwlog.RunLog.Error("presetVirtualDevice is false, volcanoType should be true")
return false
}
return true
}
func checkUse310PMixedInsertWithVolcano() bool {
// 310P混合插卡模式 与 volcano调度 互斥
// 混合插卡需要特殊的设备分配逻辑,volcano的调度策略不兼容
if *use310PMixedInsert && *volcanoType {
hwlog.RunLog.Errorf("%s is true, volcanoType should be false", api.Use310PMixedInsert)
return false
}
return true
}
func checkSoftShareDevConfigDir() bool {
if *softShareDevConfigDir == "" {
return true // 未配置软共享目录,跳过检查
}
// 软共享必须配合最大共享数100
if *shareDevCount != common.MaxShareDevCount {
hwlog.RunLog.Errorf("shareDevCount should be %d when softShareDevConfigDir is set",
common.MaxShareDevCount)
return false
}
// 必须是绝对路径(防止相对路径导致配置文件定位错误)
if !filepath.IsAbs(*softShareDevConfigDir) {
hwlog.RunLog.Errorf("softShareDevConfigDir: %s is not absolute path", *softShareDevConfigDir)
return false
}
// 父目录必须存在且可读
_, err := utils.RealDirChecker(filepath.Dir(*softShareDevConfigDir), true, false)
if err != nil {
hwlog.RunLog.Errorf("check softShareDevConfigDir: %s failed, error is %v", *softShareDevConfigDir, err)
return false
}
return true
}
func checkHotResetMode() bool {
// 热复位模式枚举:
// -1: 关闭热复位
// 0: 推理芯片热复位
// 1: 训练在线热复位
// 2: 训练离线热复位
switch *hotReset {
case common.HotResetClose, common.HotResetInfer,
common.HotResetTrainOnLine, common.HotResetTrainOffLine:
return true
default:
hwlog.RunLog.Error("hot reset mode param invalid")
return false
}
}
2.3.4 setParameters() 全局配置注入
func setParameters() {
// 将所有命令行 Flag 值一次性写入 common.ParamOption 全局结构体
// 后续所有模块通过 common.ParamOption 读取配置,无需逐个传参
common.ParamOption = common.Option{
GetFdFlag: *fdFlag, // FD系统管理模式
UseAscendDocker: *useAscendDocker, // NPU Docker运行时
UseVolcanoType: *volcanoType, // Volcano调度
AutoStowingDevs: *autoStowing, // 自动收纳
ListAndWatchPeriod: *listWatchPeriod, // 监听周期
PresetVDevice: *presetVirtualDevice, // 算力切分
Use310PMixedInsert: *use310PMixedInsert, // 310P混合插卡
HotReset: *hotReset, // 热复位模式
BuildScene: BuildScene, // 构建场景
ShareCount: *shareDevCount, // 共享数
LinkdownTimeout: *linkdownTimeout, // 链路断开超时
DealWatchHandler: *dealWatchHandler, // watch错误处理
CheckCachedPods: *checkCachedPods, // Pod缓存检查
EnableSlowNode: *enableSlowNode, // 慢节点通知
ThirdPartyScanDelay: *thirdPartyScanDelay, // 三方扫描延迟
DeviceResetTimeout: *deviceResetTimeout, // 设备复位超时
SoftShareDevConfigDir: *softShareDevConfigDir, // 软共享配置目录
UseSingleDieMode: *useSingleDieMode, // A3单Die模式
}
}
设计模式:全局 Option 模式(单例配置)。简化参数传递,但所有模块共享同一配置结构体,非线程安全(仅在启动时写入一次)。
2.3.5 setUseAscendDocker() Docker运行时调整
func setUseAscendDocker() {
*useAscendDocker = true // 默认值:启用Ascend Docker运行时
// 检查环境变量 ASCEND_DOCKER_RUNTIME 是否为 "True"
ascendDocker := os.Getenv(api.AscendDockerRuntimeEnv)
if ascendDocker != "True" {
*useAscendDocker = false
hwlog.RunLog.Debugf("get docker runtime from env is: %#v", ascendDocker)
}
// 310P混合插卡模式下不使用 NPU Docker(使用原生设备映射)
if common.ParamOption.Use310PMixedInsert {
*useAscendDocker = false
hwlog.RunLog.Debugf("mixed insert mode do not use npu docker")
}
// Atlas 200I SoC 是单芯片SoC产品,不需要Docker运行时
if len(common.ParamOption.ProductTypes) == 1 &&
common.ParamOption.ProductTypes[0] == common.Atlas200ISoc {
*useAscendDocker = false
}
// 回写全局配置(注意:setParameters已写入一次,这里可能覆盖)
common.ParamOption.UseAscendDocker = *useAscendDocker
hwlog.RunLog.Infof("device-plugin set npu docker as: %v", *useAscendDocker)
}
关键设计点:
setUseAscendDocker()在InitFunction()之后调用,因为需要读取common.ParamOption.ProductTypes(由NewHwDevManager→setAscendManager→GetAllProductType填充)。这说明配置注入分两个阶段:Flag→Option(setParameters)→ 实际初始化 → Option修正(setUseAscendDocker)。
2.3.6 main() 函数后半段逐行解析
func main() {
flag.Parse() // 解析命令行参数到各 flag 变量
if *version { // --version flag: 输出版本信息后退出
fmt.Printf("%s version: %s\n", BuildName, BuildVersion)
return
}
// 创建带取消的上下文,cancel 用于优雅退出
ctx, cancel := context.WithCancel(context.Background())
if err := initLogModule(ctx); err != nil { // 初始化日志系统
return // 日志初始化失败,无法继续
}
if !checkParam() { // 13项参数校验
return // 任一校验失败即退出
}
hwlog.RunLog.Infof("device plugin starting and the version is %s", BuildVersion)
hwlog.RunLog.Infof("device plugin starting scene is %s", BuildScene)
setParameters() // 将 Flag 值写入 common.ParamOption
hdm, err := devicefactory.InitFunction() // ★ 工厂初始化(核心调用)
if err != nil {
return // 工厂初始化失败,无法继续
}
hdm.DoSetMultiDiePolicyForA3() // 设置 A3 芯片多 Die 策略
setUseAscendDocker() // 根据实际环境修正 Docker 运行时配置
// ★ 启动4个并发任务
go hdm.ListenDevice(ctx) // goroutine 1: 设备状态监听 + ListAndWatch
go hdm.ListenDpu(ctx) // goroutine 2: DPU(数据处理单元)监听
go topology.RasTopoWriteTask(ctx, hdm) // goroutine 3: RAS拓扑文件写入(仅A5)
duplicatedetector.CheckDuplicateDevices(ctx, &types.DetectorConfig{ // 同步调用:重复设备检测
CriEndpoint: "", // 使用默认 CRI endpoint
RuntimeType: hdm.ContainerRuntime, // 从 HwDevManager 获取容器运行时
})
hwlog.RunLog.Infof("device plugin started.")
hdm.SignCatch(cancel) // ★ 信号捕获(阻塞):等待 SIGINT/SIGTERM
// SignCatch 收到信号后调用 cancel(),取消 ctx,
// 各 goroutine 通过 ctx.Done() 感知并退出
}
生命周期管理设计:
ctx+cancel模式:主 goroutine 在SignCatch阻塞等待信号- 收到信号后调用
cancel(),ctx 被取消- 各后台 goroutine 通过
select { case <-ctx.Done(): }感知并优雅退出duplicatedetector.CheckDuplicateDevices是同步调用,在SignCatch之前完成
三、devicefactory/entry.go — 设备工厂入口
3.1 模块定位
| 维度 | 说明 |
|---|---|
| 业务职责 | 设备工厂的入口函数,协调自定义命名初始化、底层驱动管理器初始化、交换设备管理器初始化,最终组装出 HwDevManager |
| 功能定位 | Factory Facade:对外暴露唯一的 InitFunction(),内部编排三个子初始化 |
| 在系统中的位置 | 被 main.go 直接调用,是 main 与 server 层之间的工厂层 |
3.2 模块整体结构
3.2.1 类/结构体与依赖关系
3.2.2 核心方法清单
| 方法 | 签名 | 作用 |
|---|---|---|
InitFunction |
func() (*server.HwDevManager, error) |
工厂入口:初始化命名→初始化驱动→创建管理器→配置交换设备 |
3.3 核心业务逻辑深度解析
3.3.1 InitFunction() 逐行解析
// InitFunction init function — 工厂入口函数
func InitFunction() (*server.HwDevManager, error) {
// 步骤1: 初始化设备自定义命名配置
// 从 /usr/local/deviceNameCustomization.json 加载自定义设备名称映射
// 失败时不报错,仅记录 info 日志,使用默认命名
customname.InitPublicNameConfig()
// 步骤2: 初始化底层驱动管理器和交换设备管理器
// devM: 通过 devmanager.AutoInit 自动探测设备类型并初始化
// switchDevM: 交换设备管理器(仅非A5芯片时初始化)
devM, switchDevM, err := initDevManager()
if err != nil {
hwlog.RunLog.Errorf("init dev manager failed, err: %v", err)
return nil, err
}
// 步骤3: 创建上层设备管理器 HwDevManager
// NewHwDevManager 内部会: 设置设备类型→设置所有设备和类型→检查产品类型→
// 设置SuperPod信息→更新节点标签→初始化插件服务器→获取容器运行时
hdm := server.NewHwDevManager(devM)
if hdm == nil {
hwlog.RunLog.Error("init device manager failed")
return nil, fmt.Errorf("init device manager failed")
}
hwlog.RunLog.Info("init device manager success")
// 步骤4: 配置交换设备管理器
// 默认关闭交换设备故障检测
common.ParamOption.EnableSwitchFault = false
if switchDevM != nil {
// 有交换设备管理器时挂载到 hdm 并启用故障检测
hdm.SwitchDevManager = switchDevM
common.ParamOption.EnableSwitchFault = true
}
return hdm, nil
}
设计意图:
- 命名初始化优先:自定义命名必须在设备管理器创建之前完成,因为
NewHwDevManager内部会使用命名映射来设置资源名称- 交换设备可选:A5芯片不支持交换设备,
switchDevM为 nil 时不报错,仅不启用相关功能- EnableSwitchFault 全局开关:通过
common.ParamOption全局传递,其他模块据此决定是否处理交换设备故障
四、devicefactory/driver_init.go — 驱动初始化
4.1 模块定位
| 维度 | 说明 |
|---|---|
| 业务职责 | 初始化底层设备驱动管理器(devmanager.DeviceInterface)和交换设备管理器(SwitchDevManager),处理 A5 芯片的特殊逻辑 |
| 功能定位 | Driver Bootstrap:自动探测 NPU 设备类型,根据设备类型决定是否初始化交换设备 |
| 在系统中的位置 | 被 entry.go 的 InitFunction() 调用,连接底层 devmanager 和上层 server |
4.2 模块整体结构
4.2.1 核心方法清单
| 方法 | 签名 | 作用 |
|---|---|---|
initDevManager |
func() (devmanager.DeviceInterface, *deviceswitch.SwitchDevManager, error) |
自动初始化底层驱动,按设备类型决定是否初始化交换设备管理器 |
4.2.2 依赖关系
4.3 核心业务逻辑深度解析
4.3.1 initDevManager() 逐行解析
func initDevManager() (devmanager.DeviceInterface, *deviceswitch.SwitchDevManager, error) {
// 步骤1: 自动初始化底层驱动管理器
// 传入空字符串 "" 表示自动探测设备路径
// common.ParamOption.DeviceResetTimeout 是等待驱动上报所有芯片的最大超时时间
devM, err := devmanager.AutoInit("", common.ParamOption.DeviceResetTimeout)
if err != nil {
hwlog.RunLog.Errorf("init devmanager failed, err: %v", err)
return devM, nil, err // 注意:返回 devM(可能为nil)便于上层调试
}
// 步骤2: A5芯片特殊处理 — 不支持交换设备
if devM.GetDevType() == api.Ascend910A5 {
hwlog.RunLog.Infof("current devType is %s, switch device manager not supported.",
api.HuaweiNPU)
return devM, nil, nil // A5直接返回,switchDevM为nil
}
// 步骤3: 非A5芯片,创建并初始化交换设备管理器
switchDevMgr := deviceswitch.NewSwitchDevManager()
if err := switchDevMgr.InitSwitchDev(); err != nil {
// 交换设备初始化失败不致命,仅警告
// 系统仍可正常运行,只是不处理交换设备故障
hwlog.RunLog.Warnf("failed to init switch device manager, "+
"will not deal with switch fault, err: %s", err.Error())
return devM, nil, nil // 交换设备为nil,但驱动管理器有效
}
return devM, switchDevMgr, nil
}
执行流程图:
关键设计决策:
- A5芯片不初始化交换设备:A5(Ascend910A5)是新一代训练芯片,架构上不需要交换设备管理
- 交换设备初始化失败不影响主流程:
InitSwitchDev()失败仅 Warn 不 Error,系统降级运行(不处理交换设备故障)- 降级模式安全:
switchDevM为 nil 时,entry.go中EnableSwitchFault保持 false,其他模块不会访问交换设备
鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。
更多推荐

所有评论(0)