在人工智能计算领域,硬件加速器的高效利用一直是核心挑战。CANN(Compute Architecture for Neural Networks)作为华为推出的神经网络计算架构,其驱动层设计了一套精密的设备资源管理机制。这套机制需要同时解决三个关键问题:多进程间的资源隔离、硬件资源的公平分配,以及异常情况下的自动回收。
CANN驱动采用典型的分层设计架构,从下到上分为三个主要层次:
这种分层设计使得上层应用无需关心具体硬件实现,同时为多进程共享提供了基础架构支持。
TRS作为驱动中的核心调度引擎,其设计围绕几个关键数据结构展开:
c复制struct trs_task {
uint64_t task_id; // 全局唯一任务标识符
pid_t submit_pid; // 提交进程的PID
struct file *submit_file; // 关联的文件描述符
void *sq_vaddr; // 提交队列虚拟地址
uint32_t sq_size; // 提交队列大小
enum trs_task_state state; // 任务状态机
struct list_head list; // 链表节点
// 其他元数据字段...
};
这个结构体中的submit_pid和submit_file字段特别重要,它们建立了任务与提交进程之间的绑定关系。当进程异常退出时,驱动可以通过这些信息准确识别并回收相关资源。
TRS实现了一个完整的状态机来管理任务生命周期:
状态转换通过以下核心函数实现:
c复制static int trs_task_transition(struct trs_task *task,
enum trs_task_state new_state)
{
// 验证状态转换合法性
if (!is_valid_transition(task->state, new_state)) {
return -EINVAL;
}
// 执行状态转换
task->state = new_state;
// 触发相关处理
switch (new_state) {
case RUNNING:
start_hardware_counter(task);
break;
case COMPLETED:
notify_user_space(task);
cleanup_hardware_resources(task);
break;
// 其他状态处理...
}
return 0;
}
TRS采用混合调度策略来平衡公平性和效率:
调度器的核心逻辑大致如下:
c复制void trs_scheduler_thread(void *data)
{
while (!kthread_should_stop()) {
// 检查硬件资源可用性
if (!hardware_resources_available()) {
schedule_timeout();
continue;
}
// 从队列中选择下一个任务
struct trs_task *task = select_next_task();
// 执行状态转换
trs_task_transition(task, RUNNING);
// 提交到硬件队列
submit_to_hardware(task);
}
}
每个物理设备在/dev目录下表现为一个字符设备文件(如/dev/davinci0)。当进程打开这个文件时,驱动会创建一个client上下文:
c复制static int davinci_open(struct inode *inode, struct file *file)
{
struct ascend_client *client = kzalloc(sizeof(*client), GFP_KERNEL);
// 初始化client结构
client->pid = get_pid(task_tgid(current));
client->tgid = task_tgid_nr(current);
INIT_LIST_HEAD(&client->task_list);
// 关联到file结构
file->private_data = client;
return 0;
}
这种设计使得每个文件描述符都精确对应一个client上下文,为后续的资源追踪奠定了基础。
驱动通过两个关键机制实现资源自动回收:
release回调的实现示例如下:
c复制static int davinci_release(struct inode *inode, struct file *file)
{
struct ascend_client *client = file->private_data;
// 清理所有未完成任务
list_for_each_entry_safe(task, tmp, &client->task_list, list) {
cancel_task(task);
}
// 释放所有分配的内存
svm_client_cleanup(client);
// 释放client结构
kfree(client);
return 0;
}
SVM允许CPU和NPU直接共享地址空间,其管理结构如下:
c复制struct svm_area {
struct vm_area_struct *vma;
struct list_head list;
atomic_t refcount;
struct pid *owner;
// 其他元数据...
};
内存分配时会将区域与进程绑定:
c复制int svm_mmap(struct file *file, struct vm_area_struct *vma)
{
struct svm_area *area = kzalloc(sizeof(*area), GFP_KERNEL);
area->vma = vma;
area->owner = get_pid(task_tgid(current));
atomic_set(&area->refcount, 1);
// 设置MMU notifier
mmu_notifier_register(&area->notifier, vma->vm_mm);
// 添加到全局列表
list_add(&area->list, &svm_global_list);
return 0;
}
为支持容器环境,驱动扩展了传统的进程模型:
c复制bool is_container_process(struct pid *pid)
{
struct task_struct *task = get_pid_task(pid, PIDTYPE_PID);
char *cgroup_path = get_task_cgroup(task);
// 检查cgroup路径是否包含容器特征
bool ret = strstr(cgroup_path, "docker") ||
strstr(cgroup_path, "kubepods");
kfree(cgroup_path);
put_task_struct(task);
return ret;
}
驱动提供多种进程间通信方式:
Event的实现核心:
c复制struct npu_event {
atomic_t signaled;
wait_queue_head_t waitq;
struct list_head list;
};
// 用户态等待事件
long npu_wait_event(struct npu_event *event, long timeout)
{
return wait_event_interruptible_timeout(
event->waitq,
atomic_read(&event->signaled),
timeout);
}
// 驱动触发事件
void npu_signal_event(struct npu_event *event)
{
atomic_set(&event->signaled, 1);
wake_up_interruptible(&event->waitq);
}
通过合并小任务减少调度开销:
c复制int trs_batch_submit(struct list_head *tasks)
{
struct trs_task *first = list_first_entry(tasks, struct trs_task, list);
// 验证所有任务属于同一上下文
if (!validate_task_ownership(tasks)) {
return -EPERM;
}
// 合并任务描述符
struct batch_descriptor *batch = build_batch_descriptor(tasks);
// 单次硬件提交
submit_batch_to_hardware(batch);
return 0;
}
实现零拷贝数据传输:
c复制int npu_map_user_buffer(unsigned long uaddr, size_t size)
{
// 获取用户空间页框
struct page **pages = get_user_pages(uaddr, size);
// 建立设备映射
create_device_mapping(pages, size);
// 设置MMU notifier监控变化
setup_mmu_notifier(current->mm, uaddr, size);
return 0;
}
c复制void trs_timeout_worker(struct work_struct *work)
{
struct trs_task *task = container_of(work, struct trs_task, timeout_work);
if (task->state == RUNNING) {
// 尝试取消硬件执行
if (cancel_hardware_task(task) == 0) {
trs_task_transition(task, ERROR);
} else {
// 无法取消,可能需要重置硬件
schedule_hardware_reset();
}
}
}
c复制void dump_task_info(struct trs_task *task)
{
pr_info("Task %llu (state=%d):\n", task->task_id, task->state);
pr_info(" Submitted by PID=%d\n", task->submit_pid);
pr_info(" SQ va=%px size=%u\n", task->sq_vaddr, task->sq_size);
if (task->state == ERROR) {
pr_info(" Error code: %d\n", task->error_code);
dump_hardware_error_registers();
}
}
在实际部署中,我们发现正确处理异常情况对系统稳定性至关重要。特别是在多进程共享环境下,一个进程的错误不应该影响其他进程的正常运行。通过完善的资源隔离和回收机制,CANN驱动能够实现这一目标。