Nouveau源码分析(一):从module_init开始

本文详细解读了Nouveau驱动的初始化过程,包括模块加载、设备匹配、驱动加载等关键步骤,深入理解Nouveau如何实现对Nvidia设备的管理。

Nouveau源码分析(一)


  1. // /drivers/gpu/drm/nouveau/nouveau_drm.c  
  2. 1089 module_init(nouveau_drm_init);  
  3. 1090 module_exit(nouveau_drm_exit);  

相信只要看过linux源码的都会知道这意味着什么,Nouveau被加载后第一个执行的函数和Nouveau被卸载前最后一个执行的函数.

显然,我们需要查看nouveau_drm_init

  1. // /drivers/gpu/drm/nouveau/nouveau_drm.c  
  2. 1062 static int __init  
  3. 1063 nouveau_drm_init(void)  
  4. 1064 {  
  5. 1065         if (nouveau_modeset == -1) {  
  6. 1066 #ifdef CONFIG_VGA_CONSOLE  
  7. 1067                 if (vgacon_text_force())  
  8. 1068                         nouveau_modeset = 0;  
  9. 1069 #endif  
  10. 1070         }  
  11. 1071   
  12. 1072         if (!nouveau_modeset)  
  13. 1073                 return 0;  
  14. 1074   
  15. 1075         nouveau_register_dsm_handler();  
  16. 1076         return drm_pci_init(&driver, &nouveau_drm_pci_driver);  
  17. 1077 }  

1065行,nouveau_modset,这是什么东西? 虽然我们完全可以跳过这段代码不看,但还是多了解一下吧.

  1. // /drivers/gpu/drm/nouveau/nouveau_drm.c  
  2.  67 MODULE_PARM_DESC(modeset, "enable driver (default: auto, "  
  3.  68                           "0 = disabled, 1 = enabled, 2 = headless)");  
  4.  69 int nouveau_modeset = -1;  
  5.  70 module_param_named(modeset, nouveau_modeset, int, 0400);  
嗯,一个指示Nouveau禁用或启用的变量,如果被设置成0[disabled]就会直接在1073行返回

1075行,nouveau_register_dsm_handler()

这个函数貌似是关于电源管理以及双显卡切换的,暂且忽略,以后再来看

1076行,最关键的一行代码出现了,通过drm_pci_init接口注册Nouveau驱动,所有的东西全都是由传入的两个结构体中的函数指针引发的

先看nouveau_drm_pci_driver吧:

  1. // /drivers/gpu/drm/nouveau/nouveau_drm.c  
  2. 1017 static struct pci_driver  
  3. 1018 nouveau_drm_pci_driver = {  
  4. 1019         .name = "nouveau",  
  5. 1020         .id_table = nouveau_drm_pci_table,  
  6. 1021         .probe = nouveau_drm_probe,  
  7. 1022         .remove = nouveau_drm_remove,  
  8. 1023         .driver.pm = &nouveau_pm_ops,  
  9. 1024 };  

这个的主要功能就是匹配pci中的Nvidia设备,先匹配id_table,符合id_table中的规则的调用probe,成功则Nouveau正式管理了这个Nvidia设备.

当然这里还有移除时调用的remove函数,和电源管理的pm部分.

  1. // /drivers/gpu/drm/nouveau/nouveau_drm.c  
  2. 893 static struct pci_device_id  
  3. 894 nouveau_drm_pci_table[] = {  
  4. 895         {  
  5. 896                 PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),  
  6. 897                 .class = PCI_BASE_CLASS_DISPLAY << 16,  
  7. 898                 .class_mask  = 0xff << 16,  
  8. 899         },  
  9. 900         {  
  10. 901                 PCI_DEVICE(PCI_VENDOR_ID_NVIDIA_SGS, PCI_ANY_ID),  
  11. 902                 .class = PCI_BASE_CLASS_DISPLAY << 16,  
  12. 903                 .class_mask  = 0xff << 16,  
  13. 904         },  
  14. 905         {}  
  15. 906 };  
pci设备的匹配规则,相信大部分人都能看懂.

然后我们来看driver:

  1. // /drivers/gpu/drm/nouveau/nouveau_drm.c  
  2. 833 static struct drm_driver  
  3. 834 driver = {  
  4. 835         .driver_features =  
  5. 836                 DRIVER_USE_AGP |  
  6. 837                 DRIVER_GEM | DRIVER_MODESET | DRIVER_PRIME | DRIVER_RENDER,  
  7. 838   
  8. 839         .load = nouveau_drm_load,  
  9. 840         .unload = nouveau_drm_unload,  
  10. 841         .open = nouveau_drm_open,  
  11. 842         .preclose = nouveau_drm_preclose,  
  12. 843         .postclose = nouveau_drm_postclose,  
  13. 844         .lastclose = nouveau_vga_lastclose,  
  14. 845   
  15. 846 #if defined(CONFIG_DEBUG_FS)  
  16. 847         .debugfs_init = nouveau_debugfs_init,  
  17. 848         .debugfs_cleanup = nouveau_debugfs_takedown,  
  18. 849 #endif  
  19. 850   
  20. 851         .get_vblank_counter = drm_vblank_count,  
  21. 852         .enable_vblank = nouveau_display_vblank_enable,  
  22. 853         .disable_vblank = nouveau_display_vblank_disable,  
  23. 854         .get_scanout_position = nouveau_display_scanoutpos,  
  24. 855         .get_vblank_timestamp = nouveau_display_vblstamp,  
  25. 856   
  26. 857         .ioctls = nouveau_ioctls,  
  27. 858         .num_ioctls = ARRAY_SIZE(nouveau_ioctls),  
  28. 859         .fops = &nouveau_driver_fops,  
  29. 860   
  30. 861         .prime_handle_to_fd = drm_gem_prime_handle_to_fd,  
  31. 862         .prime_fd_to_handle = drm_gem_prime_fd_to_handle,  
  32. 863         .gem_prime_export = drm_gem_prime_export,  
  33. 864         .gem_prime_import = drm_gem_prime_import,  
  34. 865         .gem_prime_pin = nouveau_gem_prime_pin,  
  35. 866         .gem_prime_res_obj = nouveau_gem_prime_res_obj,  
  36. 867         .gem_prime_unpin = nouveau_gem_prime_unpin,  
  37. 868         .gem_prime_get_sg_table = nouveau_gem_prime_get_sg_table,  
  38. 869         .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table,  
  39. 870         .gem_prime_vmap = nouveau_gem_prime_vmap,  
  40. 871         .gem_prime_vunmap = nouveau_gem_prime_vunmap,  
  41. 872   
  42. 873         .gem_free_object = nouveau_gem_object_del,  
  43. 874         .gem_open_object = nouveau_gem_object_open,  
  44. 875         .gem_close_object = nouveau_gem_object_close,  
  45. 876   
  46. 877         .dumb_create = nouveau_display_dumb_create,  
  47. 878         .dumb_map_offset = nouveau_display_dumb_map_offset,  
  48. 879         .dumb_destroy = drm_gem_dumb_destroy,  
  49. 880   
  50. 881         .name = DRIVER_NAME,  
  51. 882         .desc = DRIVER_DESC,  
  52. 883 #ifdef GIT_REVISION  
  53. 884         .date = GIT_REVISION,  
  54. 885 #else  
  55. 886         .date = DRIVER_DATE,  
  56. 887 #endif  
  57. 888         .major = DRIVER_MAJOR,  
  58. 889         .minor = DRIVER_MINOR,  
  59. 890         .patchlevel = DRIVER_PATCHLEVEL,  
  60. 891 };  
看起来相当复杂,这就是Nouveau和外界的接口,注意一下初始化部分: ".load = nouveau_drm_load,"


至此nouveau_drm_init完成了它的任务------注册Nouveau驱动,下面就进入到了Nouveau针对具体Nvidia设备的初始化阶段------nouveau_drm_probe和nouveau_drm_load.

<think>嗯,用户遇到了个问题,执行了`sudo modprobe -r drm_kms_helper`,结果提示模块是内置的,无法移除。这时候我需要解释为什么会出现这个情况,以及用户可能需要采取的其他步骤。 首先,我需要确认`drm_kms_helper`模块的作用。它是DRM内核模式设置的部分,负责处理显示输出的核心功能,比如热插拔检测和连接器轮询。这个模块通常是内置的,而不是可加载的内核模块,所以用户无法用modprobe移除它。 接下来,用户为什么会想移除这个模块呢?可能他们遇到了显示问题,比如分辨率不正确、热插拔检测有问题,或者想重新加载显卡驱动。但因为这个模块是内置的,直接移除不可行,需要其他方法。 我应该告诉用户这个模块的重要性,移除它可能导致显示问题。然后,建议替代方案:重新加载整个DRM模块或显卡驱动。比如,使用`sudo rmmod`命令卸载相关的模块,但需要注意依赖关系,可能要先卸载依赖它的模块,如`i915`(Intel显卡)、`amdgpu`(AMD显卡)或`nvidia`(NVIDIA显卡)。 然后,用户可能需要重新加载这些模块。例如,对于Intel显卡,可以执行`sudo modprobe i915`。同时,提醒用户这个过程可能导致短暂的显示中断,甚至需要重启,特别是在SSH连接中操作需谨慎。 另外,用户可能需要检查内核启动参数,如果问题与显示相关,可能需要添加参数如`nomodeset`,但这会影响图形性能。或者建议用户更新内核或显卡驱动,确保兼容性。 还需要提醒用户,强制移除内核模块有风险,可能导致系统不稳定,尤其是在生产环境中需谨慎。如果用户只是想重置显示配置,可以尝试重启或重新加载Xorg/显示管理器。 总结步骤:解释模块作用,说明无法移除的原因,提供替代方法(卸载相关驱动模块并重新加载),注意事项和风险,其他可能的解决方案。确保用户理解操作的风险,并在安全的环境下进行测试。</think>### 问题分析 您尝试执行 `sudo modprobe -r drm_kms_helper` 时出现 **"Module is builtin"** 错误,说明该模块已直接编译进内核,而非以可加载模块(.ko文件)形式存在。以下是具体解释和解决方案: --- ### 、模块作用与错误原因 #### 1. `drm_kms_helper` 的核心功能 - **显示核心管理**:负责显示器连接状态检测(热插拔)、分辨率协商、连接器轮询等 - **依赖关系**:是 `drm` 框架的核心组件,通常与显卡驱动(如 `i915`、`amdgpu`)深度绑定 #### 2. 错误原因 ```bash modprobe: FATAL: Module drm_kms_helper is builtin. ``` - **内核编译模式**:该模块在您系统的内核配置中标记为 `=y`(内置),而非 `=m`(可加载模块) - **无法动态卸载**:内置模块会随内核启动永久驻留内存,无法通过 `modprobe -r` 移除 --- ### 二、替代解决方案 #### ▶ 场景:需要重置显示相关功能(如修复分辨率异常/热插拔失效) ```bash # 1. 强制重置整个 DRM 子系统(可能引起屏幕闪烁) sudo rmmod <显卡驱动模块> # 例如:sudo rmmod i915(Intel)/ amdgpu(AMD)/ nouveau(NVIDIA 开源) sudo modprobe <显卡驱动模块> # 重新加载驱动 # 2. 通过 sysfs 触发连接器重探测(推荐优先尝试) echo detect > /sys/class/drm/card0-HDMI-A-1/status # 将路径替换为实际接口(通过 ls /sys/class/drm 查看) ``` #### ▶ 场景:调试 EDID 或显示时序问题 ```bash # 1. 覆盖 EDID 无需卸载模块(示例) sudo cp /path/to/custom.edid /lib/firmware/edid.bin echo edid.bin > /sys/class/drm/card0-HDMI-A-1/edid_load # 通过 sysfs 注入新 EDID # 2. 通过内核参数临时禁用功能(重启生效) # 在 GRUB 配置中添加: nomodeset # 完全禁用内核模式设置(极端情况) drm_kms_helper.poll=0 # 禁用连接器轮询(解决某些 HDMI 检测问题) ``` --- ### 三、高级操作:重建内核模块(需重新编译内核) 仅建议熟悉内核编译的用户尝试: 1. **修改内核配置**: ```bash # 在 kernel source 目录执行: make menuconfig # 导航到 Graphics support → DRM → <显卡品牌> → 将 "DRM KMS helper" 设为 =m ``` 2. **编译并安装新内核** ```bash make -j$(nproc) && sudo make modules_install && sudo make install ``` 3. **重启后验证** ```bash lsmod | grep drm_kms_helper # 此时应显示为可加载模块 ``` --- ### 四、风险与注意事项 | 操作类型 | 潜在风险 | 应急恢复方法 | |-----------------------|--------------------------------------------------------------------------|----------------------------------| | 强制卸载显卡驱动模块 | 可能导致屏幕冻结、SSH 连接中断 | 物理重启设备 | | 修改内核配置 | 错误编译会导致系统无法启动 | 保留旧内核启动项作为备份 | | 写入错误 EDID | 显示器黑屏或分辨率异常 | 通过 TTY 删除错误 EDID 文件 | --- ### 五、推荐替代调试工具 1. **DRM 调试信息输出**: ```bash sudo sysctl -w drm.debug=0x0e # 启用完整 DRM 日志(dmesg 查看) ``` 2. **EDID 实时读取**: ```bash sudo cat /sys/class/drm/card0-HDMI-A-1/edid | edid-decode # 验证当前 EDID ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值