首页 > 解决方案 > 如何正确禁用 PCIe 设备?

问题描述

我正在 linux 中为 Xilinx UltrascaleMPSoC FPGA 部件上实现的 PCIe 端点编写设备驱动程序。我已经正确实现了删除功能。我使用适配器将我的设备连接到我的电脑,打开设备,启用其端点,然后打开电脑,一切正常。但是,当我尝试使用 rmmod 命令卸载驱动程序模块时,进程挂起。

我浏览了 Linux 文档和 pci_disable_device()文档,它说

请注意,直到 pci_enable_device() 的所有调用者都调用了 pci_disable_device(),我们才真正禁用设备。

这是否意味着,我的驱动程序必须等到 linux 中的所有其他 pcie 驱动程序调用 pci_disable_device() ,然后设备才会被禁用?我真的很怀疑这一点:(

我尝试使用 modprobe -r 并使用“lsmod”列出了模块使用计数。lsmod 将使用计数显示为“0”。但是我仍然无法卸载模块:(我还添加了打印语句。

void remove(struct pci_dev pdev)
{
    pci_unmap_single(pdev, privdata->dma_mem,
    PAGE_SIZE * (1 << memorder),
    PCI_DMA_FROMDEVICE);
    printk(KERN_INFO"unmap_single() complete\n");
    free_pages ((unsigned long) privdata->mem, memorder);
    printk(KERN_INFO"free_pages() complete\n");
    free_irq(pdev->irq, privdata);
    printk(KERN_INFO"free_irq() complete\n");
    pci_disable_msi(pdev);
    printk(KERN_INFO"MSI disable complete\n");
    pci_clear_master(pdev); /* Nobody seems to do this */
    printk(KERN_INFO"clear_master() complete\n");
    pci_iounmap(pdev, privdata->registers);
    printk(KERN_INFO"iounmap() complete\n");
    pci_disable_device(pdev);
    printk(KERN_INFO"disable_device() complete\n");
    pci_release_regions(pdev);
    printk(KERN_INFO"release_regions() complete\n");
}

预期:设备必须被禁用。我无法得出结论。当我在另一个终端中执行 dmesg 时,我得到打印,直到disable_device() 完成”并且终端挂起。此外,我检查了文件:/proc/iomem 和 /proc/interrupts-> 当我卸载模块时,相应的条目我的两个文件中都删除了我的设备!我只完成了 pci_disable_device() 的执行,进程挂起。

注意:只有 rmmod 进程挂起,我不能使用当前终端,但我可以打开另一个终端,做所有事情。

最后,当重新启动时,系统再次挂起,每次我都通过按住电源按钮 10-15 秒来强制重启。检查这个reboot_hang

为什么 pci_release_regions() 挂起我的系统,即使 lsmod 显示我的模块使用计数为“0”。

在此先感谢:)另外,我应该在上面的代码中交换pci_disable_device()和方法吗?pci_release_regions()-> 我也试过了,但是直到 release_regions() 完成后我才打印出来,但 disable_device complete 没有打印出来。并且进程挂起:(

请帮忙

标签: clinux-kernellinux-device-driverpci-e

解决方案


如果没有更多关于您在驱动程序上真正使用的信息,很难回答。

但是,我会尽力给你一个答案。

这是否意味着,我的驱动程序必须等到 linux 中的所有其他 pcie 驱动程序调用 pci_disable_device() ,然后设备才会被禁用?

不,这会变得不切实际并且不是很模块化。

看来您正在释放 DMA 页面。你是如何分配它们的?您是否使用一致分配?看看这里,看看如何正确使用 DMA。

对于其余部分,您可以尝试在 clear_master 之前移动发布区域吗?例子 :

pci_disable_msi(pdev);
ioummap(ADDRESS);
pci_release_regions(pdev);
pci_clear_master(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);

有关如何编写 PCI 驱动程序的更多信息,请查看此处的文档。它提供了很多信息。


推荐阅读