Part 1.1 — 用户态入口:设备文件、ioctl 分发与 fd 类型

发布时间:2026/11/13 6:28:11

Part 1.1 — 用户态入口:设备文件、ioctl 分发与 fd 类型 UVM Part 1 用户态接口首篇。适用代码kernel-open/nvidia-uvm/关联源码uvm.c、uvm_fd_type.h、uvm_fd_type.c、uvm_ioctl.h、uvm_api.h1. 本篇要回答的问题前两篇讲的是内核里有哪些结构。本篇回到最上游——用户态请求是怎么进内核的CUDA 打开/dev/nvidia-uvm后一个struct file承载什么状态UVM_*ioctl 是如何被分发到具体uvm_api_*处理函数的为什么一个 fd 要区分UVM_FD_VA_SPACE/UVM_FD_MM等类型初始化为什么要做原子 CAS2. 代码地图关注点符号位置设备文件操作表uvm_fopsuvm.cstatic const struct file_operations打开设备uvm_open()uvm.cioctl 分发uvm_ioctl()uvm.c分发宏UVM_ROUTE_CMD_*uvm_api.h初始化uvm_api_initialize()uvm.cfd 类型uvm_fd_type_t,uvm_fd_type()uvm_fd_type.h/.cioctl 号 / 参数UVM_*_PARAMSuvm_ioctl.h3. 设备文件操作表uvm_fopsUVM 注册一个字符设备nvidia-uvm其file_operations极简staticconststructfile_operationsuvm_fops{.openuvm_open_entry,.releaseuvm_release_entry,.mmapuvm_mmap_entry,.unlocked_ioctluvm_unlocked_ioctl_entry,#ifNVCPU_IS_X86_64.compat_ioctluvm_unlocked_ioctl_entry,#endif.ownerTHIS_MODULE,};四个入口的分工回调作用后续篇open每个struct file建立独立address_space本篇第 4 节mmap把托管 / 信号量 / P2P 内存映射进用户 VAPart 1.2 / Part 3unlocked_ioctl所有UVM_*控制命令的总入口本篇第 6 节release关闭 fd销毁 VA space可能延迟到 kthreadPart 1.2每个*_entry包装函数用UVM_ENTRY_RET宏包裹真正实现统一做线程上下文 / 栈检查等是 UVM 的通用模式。4.uvm_open()为什么每个 fd 要独立 address_spaceuvm_open()做了一件不显眼但关键的事——给每个打开的文件分配独立的struct address_spacemappinguvm_kvmalloc(sizeof(*mapping));address_space_init_once(mapping);mapping-hostinode;mapping-a_opsinode-i_mapping-a_ops;filp-private_dataNULL;// 初始为 UVM_FD_UNINITIALIZEDfilp-f_mappingmapping;原因源码注释已说明默认同一 inode 上所有进程的struct file共享inode 的address_space那么unmap_mapping_range会跨进程一起解映射。而UVM 把 mapping offset 当作该进程文件的 VA因此必须让每个进程的映射相互隔离。此时filp-private_data NULL即UVM_FD_UNINITIALIZED尚未绑定任何 VA space。5. fd 类型系统一个 fd 可以是什么filp-private_data不是简单地存一个指针而是用低位打包类型标签uvm_fd_type.htypedefenum{UVM_FD_UNINITIALIZED,// 刚 open未初始化UVM_FD_INITIALIZING,// 正在初始化CAS 中间态UVM_FD_VA_SPACE,// 已绑定 uvm_va_space_tUVM_FD_MM,// 绑定 mm用于 va_space_mm 机制UVM_FD_TEST,// 测试用UVM_FD_COUNT}uvm_fd_type_t;#defineUVM_FD_TYPE_BITS3#defineUVM_FD_TYPE_MASK((1ULUVM_FD_TYPE_BITS)-1)因为指针天然按 8 字节对齐低 3 位恒为 0UVM 就把类型塞进低 3 位、高位存实际指针。uvm_fd_type(filp, ptr)解出{类型, 指针}uvm_fd_get_type(filp, TYPE)仅当类型匹配才返回指针。uvm_open()private_data NULLuvm_fd_type_init_cas()原子 CAS 抢占UVM_INITIALIZE 成功uvm_fd_type_set(VA_SPACE, va_space)创建失败回退允许重试UVM_MM_INITIALIZE测试路径uvm_release()UNINITINITIALIZINGVA_SPACEMMTEST6. ioctl 分发uvm_ioctl()的路由表所有控制命令进入uvm_ioctl()本质是一张大switch用一组UVM_ROUTE_CMD_*宏把 ioctl 号映射到uvm_api_*函数staticlonguvm_ioctl(structfile*filp,unsignedintcmd,unsignedlongarg){switch(cmd){caseUVM_DEINITIALIZE:return0;UVM_ROUTE_CMD_STACK_NO_INIT_CHECK(UVM_INITIALIZE,uvm_api_initialize);UVM_ROUTE_CMD_STACK_NO_INIT_CHECK(UVM_MM_INITIALIZE,uvm_api_mm_initialize);UVM_ROUTE_CMD_STACK_INIT_CHECK(UVM_REGISTER_GPU,uvm_api_register_gpu);UVM_ROUTE_CMD_STACK_INIT_CHECK(UVM_SET_PREFERRED_LOCATION,uvm_api_set_preferred_location);UVM_ROUTE_CMD_STACK_INIT_CHECK(UVM_MIGRATE,uvm_api_migrate);UVM_ROUTE_CMD_ALLOC_INIT_CHECK(UVM_ALLOC_SEMAPHORE_POOL,uvm_api_alloc_semaphore_pool);/* ...数十条... */}// 都没匹配 → 交给测试 ioctlreturnuvm_test_ioctl(filp,cmd,arg);}6.1 三种路由宏的区别宏名的差异编码了两个正交维度参数存放位置、是否需要已初始化的 VA space。宏参数缓冲VA space 检查典型命令..._STACK_NO_INIT_CHECK栈上不要求已初始化UVM_INITIALIZE、UVM_MM_INITIALIZE..._STACK_INIT_CHECK栈上要求UVM_FD_VA_SPACE绝大多数命令..._ALLOC_INIT_CHECK堆分配参数较大要求已初始化UVM_MAP_EXTERNAL_ALLOCATION、UVM_ALLOC_SEMAPHORE_POOL宏内部统一负责从用户空间copy_from_user参数、视宏而定校验 fd 已是 VA space、调用处理函数、再copy_to_user回写结果。因此每个uvm_api_*函数签名统一为NV_STATUS uvm_api_xxx(UVM_XXX_PARAMS *params, struct file *filp)。6.2 命令分类速览路由表里的命令大致可分为类别代表命令对应结构操作生命周期UVM_INITIALIZE/UVM_DEINITIALIZE创建 / 销毁uvm_va_space_tGPU 管理UVM_REGISTER_GPU/UVM_UNREGISTER_GPU改registered_gpus掩码PeerUVM_ENABLE_PEER_ACCESS改enabled_peers策略UVM_SET_PREFERRED_LOCATION/UVM_SET_ACCESSED_BY/UVM_ENABLE_READ_DUPLICATION改 rangepolicyPart 1.2迁移UVM_MIGRATE/UVM_POPULATE_PAGEABLE触发 make_residentPart 3.4外部内存UVM_MAP_EXTERNAL_ALLOCATION/UVM_IMPORT_DMA_BUFexternal / dma-buf range信号量 / P2PUVM_ALLOC_SEMAPHORE_POOL/UVM_ALLOC_DEVICE_P2P专用 range 类型工具UVM_TOOLS_*/UVM_QUERY_RESIDENCY观测Part 5.27.UVM_INITIALIZE并发安全的 VA space 创建UVM_INITIALIZE是把一个裸 fd 变成 VA space 的那一步也是并发处理的典范。多个线程可能同时对同一 fd 发UVM_INITIALIZE代码用原子 CAS保证只有一个线程真正创建old_fd_typeuvm_fd_type_init_cas(filp);// 期望 UNINIT → 换成 INITIALIZINGswitch(old_fd_type){caseUVM_FD_UNINITIALIZED:// 只有抢到的线程走这里statusuvm_va_space_create(filp-f_mapping,va_space,params-flags);if(status!NV_OK){uvm_fd_type_set(filp,UVM_FD_UNINITIALIZED,NULL);// 失败回退允许重试returnstatus;}uvm_fd_type_set(filp,UVM_FD_VA_SPACE,va_space);break;caseUVM_FD_VA_SPACE:// 已初始化仅当 flags 一致才算成功va_spaceuvm_va_space_get(filp);status(params-flags!va_space-initialization_flags)?NV_ERR_INVALID_ARGUMENT:NV_OK;break;caseUVM_FD_INITIALIZING:// 别人正在建 → 让用户重试statusNV_ERR_BUSY_RETRY;break;/* ... */}要点CAS 抢占uvm_fd_type_init_cas()把UNINIT原子换成INITIALIZING只有观察到UNINIT的线程负责创建 VA space。失败可重试创建失败时把状态复位为UNINIT其他线程只可能看到过INITIALIZING不会看到半成品VA_SPACE因此安全。幂等语义已是VA_SPACE时重复 initialize只要 flags 一致就返回成功。NV_ERR_BUSY_RETRY撞上中间态时返回让用户态重试而非阻塞等待。这一步成功后filp-private_data就指向 Part 0.2 讲的uvm_va_space_t后续所有UVM_ROUTE_CMD_STACK_INIT_CHECK命令才能通过 fd 类型检查。8. 完整入口时序uvm_api_*uvm_ioctl 路由uvm_fopsVFS用户态 (CUDA)uvm_api_*uvm_ioctl 路由uvm_fopsVFS用户态 (CUDA)open(/dev/nvidia-uvm)uvm_open()分配独立 address_spaceprivate_data UNINITioctl(fd, UVM_INITIALIZE, params)uvm_unlocked_ioctl_entryuvm_ioctl(filp, cmd, arg)uvm_api_initialize()CAS 建 va_spaceNV_OKfd 现为 VA_SPACEioctl(fd, UVM_REGISTER_GPU, ...)uvm_ioctl → 校验 fd 类型uvm_api_register_gpu()结果 copy_to_user9. 边界与坑点fd 类型必须先对UVM_ROUTE_CMD_STACK_INIT_CHECK会拒绝未初始化 fd工具类命令若在 initialize 前发出会失败。PM 锁uvm_unlocked_ioctl外层持g_uvm_global.pm.lock电源管理release若 trylock 失败会把销毁延迟到 kthreaduvm_release_deferred这在挂起 / 恢复期间尤其重要。测试 ioctl 兜底路由表未命中会落到uvm_test_ioctl仅在开启测试构建时有意义。别把UVM_FD_MM当 VA spaceUVM_MM_INITIALIZE走另一条 fd 类型用于va_space_mm机制Part 4 相关与主 VA space fd 不是一回事。10. 小结与下一篇uvm_fops只有 open / mmap / ioctl / release 四个入口open 为每个 fd 建独立address_space以隔离进程映射。fd 用低 3 位打包uvm_fd_type_tUVM_INITIALIZE通过原子 CAS 并发安全地绑定uvm_va_space_t。uvm_ioctl()用UVM_ROUTE_CMD_*宏把命令路由到统一签名的uvm_api_*宏名编码了参数放栈/堆 是否需已初始化。下一篇Part 1.211-vaspace-lifecycle深入uvm_va_space_create()/ GPU 注册 / range 创建与销毁把这些 ioctl 落到第 0.2 篇那棵结构树的增删改上。交叉引用fd 绑定的uvm_va_space_t字段见 01-four-layer-model策略类命令的语义见 Part 1.212-policy-apiUVM_MIGRATE的内核流程见 Part 3.433-make-resident-migrate。
延伸阅读

更多相关文章

2026/11/13 6:28:10

HTML5生鲜商城源码实战:从语义化骨架到响应式购物车优化

简介:这是一份面向前端初学者与中小型电商建站的HTML5水果蔬菜在线商城网站源码,适用于快速搭建网上蔬菜店、水果店或生鲜展示页面。资源打包为zip,共112个文件,压缩包仅4.53MB,以15个HTML页面、13个CSS样式文件、15个…

2026/11/8 17:20:33

CentOS7离线部署Python3.9+PyTorch深度学习环境实战

简介:本资源是一套面向人工智能初学者与进阶学习者的系统性自学资料包,覆盖机器学习基础、深度学习实践及环境配置等核心环节,适用于高校学生、转行入门者及自学备考人员。压缩包共105个文件,包含27个Jupyter Notebook&#xff08…

2026/11/13 6:27:24

工地太忙根本没时间复习考试?泗水电工证考试报名良心建议来了

工地太忙根本没时间复习考试?泗水电工证考试报名良心建议来了 工地太忙根本没时间复习考试,这是很多一线电工的真实写照。泗水电工证考试报名流程复杂,考试内容又偏理论,很多人在忙完一天的活儿后,连翻书的时间都没有。但你可能不知道,…

2026/11/13 6:27:24

双鸭山电工证真证怎么报名 避坑指南来了

双鸭山电工证真证怎么报名 避坑指南来了 证书过期了不知道怎么复审延期,其实很多人都遇到过这个问题。特别是从事电力相关工作的朋友,电工证不仅关系到你的职业发展,还直接关系到你能不能合法上岗作业。很多人因为忽视复审时间,导致证书失效,一旦被查到…

2026/11/13 6:27:24

临沂应急管理局电工证怎么选?上岗必备这些避坑干货你必须知道

临沂应急管理局电工证怎么选?上岗必备这些避坑干货你必须知道 你是不是也遇到过这样的情况,想考个电工证上岗,结果一打听,报名机构五花八门,价格参差不齐,担心被中介坑?临沂应急管理局电工证虽然是上岗必备的资质,但很多人在报名时却不知从何下手。本…

2026/11/13 6:27:24

电工证现在不发卡吗官方回应来了,应届生别慌

电工证现在不发卡吗官方回应来了,应届生别慌 你是不是正盯着手机屏幕,满脑子想着“电工证现在不发卡吗”?手上拿着电工证报名表,却听说现在不发卡了,心里慌得不行?别急,官方回应来了,今天咱们就来唠一唠这个事儿,让你心里有数,顺利拿到证上岗。…

2026/11/12 20:35:13

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/13 0:01:06

办上海高压电工证良心建议:别被中介坑了,自己看这篇就够了

办上海高压电工证良心建议:别被中介坑了,自己看这篇就够了 你是不是也像我一样,第一次听说“高压电工证”这个名词,就感觉一头雾水?其实很多人都跟我一样,不知道去哪报名、怕被中介坑,结果花了冤枉钱还考不上。今天我就从一个在商丘培训过的学员角度,…

2026/11/13 0:01:06

黎里考电工证在哪里值不值得考

黎里考电工证在哪里值不值得考 之前考的证在外省能不能转过来?这是很多在外地工作、准备回老家发展的朋友最关心的问题。特别是在黎里地区,很多从事电力相关工作的朋友,都面临这样一个难题: 外省的电工证能否在本地使用?值不值得考?…

2026/11/13 0:01:06

毕节哪里考电工证 怎么报名学历不高也能上手

毕节哪里考电工证 怎么报名学历不高也能上手 你是不是也和我一样,学历不高,想着考个电工证,但又担心自己条件不够,怕报不上名?其实不然,只要符合条件,学历不高也能考电工证。今天就来聊聊【毕节哪里考电工证】这个问题,还有怎么报名,让你一步到位不…

2026/11/12 22:10:13

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/12 9:51:42

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑