Nouveau源码分析(五):NVIDIA设备初始化之nouveau_drm_load (2)

本文详细分析了Nouveau驱动中nouveau_drm_load的实现,涉及NVIF对象初始化、ioctl调用流程、nouveau_object及nouveau_handle的创建过程。内容涵盖版本校验、对象层级构建、subdev的增减操作,以及初始化过程中关键函数的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Nouveau源码分析(五)

接着上一篇来,先把nouveau_drm_load再贴出一遍来吧:

// /drivers/gpu/drm/nouveau/nouveau_drm.c
364 static int
365 nouveau_drm_load(struct drm_device *dev, unsigned long flags)
366 {
367         struct pci_dev *pdev = dev->pdev;
368         struct nouveau_drm *drm;
369         int ret;
370 
371         ret = nouveau_cli_create(nouveau_name(dev), "DRM", sizeof(*drm),
372                                  (void **)&drm);
373         if (ret)
374                 return ret;
375 
376         dev->dev_private = drm;
377         drm->dev = dev;
378         nvkm_client(&drm->client.base)->debug =
379                 nouveau_dbgopt(nouveau_debug, "DRM");
380 
381         INIT_LIST_HEAD(&drm->clients);
382         spin_lock_init(&drm->tile.lock);
383 
384         nouveau_get_hdmi_dev(drm);
385 
386         /* make sure AGP controller is in a consistent state before we
387          * (possibly) execute vbios init tables (see nouveau_agp.h)
388          */
389         if (pdev && drm_pci_device_is_agp(dev) && dev->agp) {
390                 const u64 enables = NV_DEVICE_V0_DISABLE_IDENTIFY |
391                                     NV_DEVICE_V0_DISABLE_MMIO;
392                 /* dummy device object, doesn't init anything, but allows
393                  * agp code access to registers
394                  */
395                 ret = nvif_device_init(&drm->client.base.base, NULL,
396                                        NVDRM_DEVICE, NV_DEVICE,
397                                        &(struct nv_device_v0) {
398                                                 .device = ~0,
399                                                 .disable = ~enables,
400                                                 .debug0 = ~0,
401                                        }, sizeof(struct nv_device_v0),
402                                        &drm->device);
403                 if (ret)
404                         goto fail_device;
405 
406                 nouveau_agp_reset(drm);
407                 nvif_device_fini(&drm->device);
408         }
409 
410         ret = nvif_device_init(&drm->client.base.base, NULL, NVDRM_DEVICE,
411                                NV_DEVICE,
412                                &(struct nv_device_v0) {
413                                         .device = ~0,
414                                         .disable = 0,
415                                         .debug0 = 0,
416                                }, sizeof(struct nv_device_v0),
417                                &drm->device);
418         if (ret)
419                 goto fail_device;
420 
421         dev->irq_enabled = true;
422 
423         /* workaround an odd issue on nvc1 by disabling the device's
424          * nosnoop capability.  hopefully won't cause issues until a
425          * better fix is found - assuming there is one...
426          */
427         if (drm->device.info.chipset == 0xc1)
428                 nvif_mask(&drm->device, 0x00088080, 0x00000800, 0x00000000);
429 
430         nouveau_vga_init(drm);
431         nouveau_agp_init(drm);
432 
433         if (drm->device.info.family >= NV_DEVICE_INFO_V0_TESLA) {
434                 ret = nouveau_vm_new(nvkm_device(&drm->device), 0, (1ULL << 40),
435                                      0x1000, &drm->client.vm);
436                 if (ret)
437                         goto fail_device;
438 
439                 nvkm_client(&drm->client.base)->vm = drm->client.vm;
440         }
441 
442         ret = nouveau_ttm_init(drm);
443         if (ret)
444                 goto fail_ttm;
445 
446         ret = nouveau_bios_init(dev);
447         if (ret)
448                 goto fail_bios;
449 
450         ret = nouveau_display_create(dev);
451         if (ret)
452                 goto fail_dispctor;
453 
454         if (dev->mode_config.num_crtc) {
455                 ret = nouveau_display_init(dev);
456                 if (ret)
457                         goto fail_dispinit;
458         }
459 
460         nouveau_sysfs_init(dev);
461         nouveau_hwmon_init(dev);
462         nouveau_accel_init(drm);
463         nouveau_fbcon_init(dev);
464 
465         if (nouveau_runtime_pm != 0) {
466                 pm_runtime_use_autosuspend(dev->dev);
467                 pm_runtime_set_autosuspend_delay(dev->dev, 5000);
468                 pm_runtime_set_active(dev->dev);
469                 pm_runtime_allow(dev->dev);
470                 pm_runtime_mark_last_busy(dev->dev);
471                 pm_runtime_put(dev->dev);
472         }
473         return 0;
474 
475 fail_dispinit:
476         nouveau_display_destroy(dev);
477 fail_dispctor:
478         nouveau_bios_takedown(dev);
479 fail_bios:
480         nouveau_ttm_fini(drm);
481 fail_ttm:
482         nouveau_agp_fini(drm);
483         nouveau_vga_fini(drm);
484 fail_device:
485         nvif_device_fini(&drm->device);
486         nouveau_cli_destroy(&drm->client);
487         return ret;
488 }

第386行到第408行,注释说的很清楚,重置AGP. 好了来看nvif_device_init:

// /drivers/gpu/drm/nouveau/nvif/device.c
 33 int
 34 nvif_device_init(struct nvif_object *parent, void (*dtor)(struct nvif_device *),
 35                  u32 handle, u32 oclass, void *data, u32 size,
 36                  struct nvif_device *device)
 37 {
 38         int ret = nvif_object_init(parent, (void *)dtor, handle, oclass,
 39                                    data, size, &device->base);
 40         if (ret == 0) {
 41                 device->object = &device->base;
 42                 device->info.version = 0;
 43                 ret = nvif_object_mthd(&device->base, NV_DEVICE_V0_INFO,
 4
<think>嗯,用户遇到了Linux启动时的报错信息“drm:nv_drm_load [nvidia_drm] failed to allocate nvkmskapidevice”。首先,我需要确定这个错误的大致原因。通常,涉及NVIDIADRM模块的错误可能与显卡驱动有关。可能的情况包括驱动未正确安装、版本冲突,或者内核模块加载的问题。 接下来,我应该考虑用户可能的环境。用户可能是在安装或更新NVIDIA驱动后遇到这个问题,或者在系统更新(比如内核升级)之后出现的。此外,用户可能使用的是Ubuntu、Fedora等常见发行版,或者Arch Linux等滚动更新的系统,不同发行版的解决方法可能略有不同。 然后,我需要分解错误信息本身。错误提到“nv_drm_load”失败,无法分配“nvkmskapidevice”。这通常指向NVIDIA的内核模块(如nvidia-drm)在加载时遇到了问题。可能的原因包括驱动与当前运行的内核版本不兼容,或者多个驱动版本冲突,比如同时存在开源驱动nouveau和闭源NVIDIA驱动。 接下来,思考可能的解决步骤。首先建议用户检查NVIDIA驱动是否正确安装,使用命令如`nvidia-smi`来验证驱动是否正常。如果驱动未安装,可能需要安装合适的版本。如果已经安装,可能需要重新配置或更新驱动。 另外,考虑内核模块的问题。用户可能需要重建initramfs,特别是在内核升级后,确保模块正确集成。或者,检查是否有多个驱动版本存在,导致冲突,需要禁用nouveau驱动,并确保NVIDIA驱动正确加载。 还可能涉及到GRUB配置,比如在引导参数中添加`nomodeset`来暂时禁用图形模式,或者修改模块黑名单。此外,检查Xorg的日志(/var/log/Xorg.0.log)可能会有更详细的错误信息,帮助诊断问题。 还要考虑用户可能尝试过的操作,比如自行安装驱动时是否使用了正确的仓库或方法,或者是否在安装后运行了必要的配置步骤。比如,使用`ubuntu-drivers`自动安装推荐驱动,或者手动安装后运行`nvidia-xconfig`生成Xorg配置。 另外,如果用户使用的是Secure Boot,可能需要签名NVIDIA内核模块,否则驱动无法加载。这种情况下,用户需要禁用Secure Boot或进行模块签名。 最后,可能需要建议用户在恢复模式下操作,比如进入恢复模式后卸载并重新安装驱动,或者使用低图形模式启动系统进行调整。 总结可能的解决步骤:1. 检查驱动状态;2. 禁用nouveau;3. 重新安装或更新驱动;4. 重建initramfs;5. 检查Secure Boot设置;6. 调整GRUB参数;7. 检查日志获取更多信息。需要逐步引导用户排查,确保每个步骤清晰,避免遗漏关键点。</think>### Linux启动报错 `drm:nv_drm_load [nvidia_drm] failed to allocate nvkmskapidevice` 分析与解决 该报错通常与 **NVIDIA显卡驱动** 的加载问题相关,可能由驱动未正确安装、版本冲突、内核模块加载失败等原因导致。以下是逐步排查和解决方法: --- #### 1. **检查NVIDIA驱动状态** - **验证驱动是否安装**: 终端输入以下命令,检查驱动是否正常加载: ```bash nvidia-smi ``` - 若输出显卡信息,说明驱动已加载。 - 若提示`command not found`,说明驱动未安装或未正确加载。 - **查看已安装的驱动版本**: ```bash dkms status # 查看内核模块状态 apt list --installed | grep nvidia # Ubuntu/Debian rpm -qa | grep nvidia # Fedora/CentOS ``` --- #### 2. **禁用开源驱动 `nouveau`** NVIDIA闭源驱动可能与开源的`nouveau`驱动冲突,需禁用后者: - **编辑黑名单文件**: ```bash sudo nano /etc/modprobe.d/blacklist-nouveau.conf ``` 添加以下内容: ```conf blacklist nouveau options nouveau modeset=0 ``` - **更新initramfs**: ```bash sudo update-initramfs -u ``` - **重启系统**。 --- #### 3. **重新安装NVIDIA驱动** - **卸载旧驱动**: ```bash sudo apt purge nvidia-* # Ubuntu/Debian sudo dnf remove nvidia-* # Fedora ``` - **安装推荐版本**(以Ubuntu为例): ```bash sudo ubuntu-drivers autoinstall ``` 或手动指定版本: ```bash sudo apt install nvidia-driver-535 # 替换为推荐版本号 ``` - **重启并验证**: ```bash nvidia-smi ``` --- #### 4. **修复内核模块问题** - **重建内核模块**: ```bash sudo dkms remove -m nvidia -v <驱动版本> --all sudo dkms install -m nvidia -v <驱动版本> sudo update-initramfs -u ``` - **检查Secure Boot**: 若系统启用Secure Boot,需禁用或为NVIDIA模块签名(需密钥)。 --- #### 5. **调整GRUB引导参数** - **临时禁用图形模式**: 在GRUB启动菜单中按`e`键,在`linux`行末尾添加: ```text nomodeset ``` - **永久生效**(编辑`/etc/default/grub`): ```text GRUB_CMDLINE_LINUX_DEFAULT="quiet splash nomodeset" ``` ```bash sudo update-grub ``` --- #### 6. **检查日志定位问题** - **查看启动日志**: ```bash journalctl -b | grep nvidia dmesg | grep nvidia ``` - **Xorg日志**: ```bash cat /var/log/Xorg.0.log | grep EE ``` --- ### 常见原因总结 | 可能原因 | 解决方法 | |-------------------------|-----------------------------| | 驱动未安装/版本不兼容 | 重新安装适配当前内核的驱动 | | `nouveau`驱动冲突 | 彻底禁用`nouveau` | | Secure Boot阻止加载模块 | 禁用Secure Boot或签名模块 | | 内核更新后未重建模块 | 通过`dkms`重新生成模块 | 如果问题仍未解决,请提供以下信息: 1. 系统发行版及版本 2. 内核版本(`uname -r`) 3. NVIDIA驱动版本 4. 相关日志片段(`dmesg`或`Xorg.0.log`)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值