添加 request_mem_region 后,我的驱动程序每次第一次访问都会失败,并显示“忙碌”消息

Posted

技术标签:

【中文标题】添加 request_mem_region 后,我的驱动程序每次第一次访问都会失败,并显示“忙碌”消息【英文标题】:After adding request_mem_region my driver fails every first access with "busy" message 【发布时间】:2012-09-13 01:18:03 【问题描述】:

好吧,这对我来说真的很奇怪。我有一个模拟的 CAN 总线驱动程序,它是一个 Linux 内核模块。然后我有一个在用户空间运行的测试应用程序,它通过打开文件描述符并发送ioctl() 消息来访问驱动程序。

现在 CAN 总线驱动程序只是我一直采用的在 x86 平台上运行的东西(它在我们的嵌入式 Coldfire 系统上运行)。在嵌入式系统上,它必须使用 request_mem_region()/ioremap() 来访问内存 I/O 区域,我不需要需要这样做,但我想保留尽可能多的代码尽可能通用。

这里有一些有用的定义:

#define MCF_MBAR    0x10000000

extern unsigned int Base[];
extern unsigned int can_range[];

  //This is the CAN registers on coldfire, just unused on my x86 desktop
Base[minor] = (MCF_MBAR + 0x1c0000); 
can_range[minor] = 0x180;

然后在初始化期间我们这样做:

if(NULL == request_mem_region(Base[minor], can_range[minor], "CAN-IO")) 
    return -EBUSY;


can_base[minor] = ioremap(Base[minor], can_range[minor]);

现在,如果我理解正确......我们在这里所做的只是请求保留一系列未分配的内存地址,如果我们成功,则让我们可以访问它们。

我通过cat /proc/iomem检查了当前映射的地址:

00000000-0000ffff : reserved
00010000-0009fbff : System RAM
0009fc00-0009ffff : reserved
000a0000-000bffff : Video RAM area
000c0000-000c8fff : Video ROM
000e2000-000e6fff : Adapter ROM
000f0000-000fffff : reserved
000f0000-000fffff : System ROM
00100000-1ffeffff : System RAM
00200000-0071038b : Kernel code
0071038c-00ad8a7f : Kernel data
00b58000-00c52fff : Kernel bss
                          <--  101C0000-101C0180 : This is where I'd be mapping memory
1fff0000-1fffffff : ACPI Tables
e0000000-e0ffffff : 0000:00:02.0
e0000000-e0bfffff : vesafb
f0000000-f001ffff : 0000:00:03.0
f0000000-f001ffff : e1000
f0400000-f07fffff : 0000:00:04.0
f0400000-f07fffff : vboxguest
f0800000-f0803fff : 0000:00:04.0
f0804000-f0804fff : 0000:00:06.0
f0804000-f0804fff : ohci_hcd
f0806000-f0807fff : 0000:00:0d.0
f0806000-f0807fff : ahci
fee00000-fee00fff : Local APIC
fffc0000-ffffffff : reserved

看起来没有什么东西在使用那个内存,所以我想我在这里还可以。所以我成功地加载了我的内核模块,去运行我的测试程序并且它失败了,再次运行它并且它可以工作。 每次新加载后运行它,它都会失败......第二次,第三次,第n次,它会工作:

mike@linux-4puc:~> ./a.out 
  Starting driver test
  Error 'Device or resource busy' opening CAN device
mike@linux-4puc:~> ./a.out 
  Starting driver test
  We opened successfully

这是我非常简单的用户空间程序的一部分:

int fd;
char* dev = "/dev/can0";

printf("Starting driver test\n");

if ((fd = open(dev, O_RDWR)) < 0) 
    printf("Error '%s' opening CAN device", strerror(errno));
    close(fd);
    return -1;

关于为什么会发生这种情况的任何想法?如果我从驱动程序中删除 request_mem_region() 代码,一切正常,所以我认为我只是在做一些愚蠢的事情......但为什么它会以这种方式失败?

【问题讨论】:

【参考方案1】:

我有点惊讶这对你有用。 request_mem_region() 被传递了一个值0x101C0000,它在系统 RAM 的物理地址范围内,0x00100000:0x1ffeffff。我猜想(初始)错误返回表明内核已经将此物理内存区域安装到其内存池中。当驱动程序错误退出时(或模块卸载时),它是否会尝试进行适当的清理并调用release_mem_region(),这可能会在下一次复飞时启用成功的request_mem_region()

通常您会为request_mem_region() 提供一个 正在使用的地址范围(即当前对内核来说是未知的),因为驱动程序正在处理使该物理地址范围“可用”(即声明用于映射到虚拟地址空间的物理地址范围)。

如果你使用类似的东西会发生什么

#define MCF_MBAR    0x90000000

假设那个物理地址空间真的没有被使用?

顺便说一句,如果您的驱动程序在第一次使用后调用release_mem_region(),那么驱动程序就有错误。驱动程序应该只释放它实际获得的资源。如果request_mem_region() 返回错误,则永远不会获取内存资源。因此没有理由调用release_mem_region()(并且驱动程序在_init() 期间总是会收到分配错误)。如果您检查一些运行正常的 Linux 设备驱动程序,您可能会发现精心设计的错误退出方案(使用 goto 语句)以在 _init() 例程中展开分配的资源,并在 _exit() 代码中在释放之前检查有效性。

【讨论】:

是的,地址空间未被使用并成功完成了这项工作。我想我在想“加载到 RAM”,所以将它安装在 RAM 的区域,但这导致了问题。您是否有关于查找内存区域是否空闲而不只是循环 check_mem_region() 的建议? “内存区域是空闲的” - 注意措辞,即。地址空间还是RAM? phys addr 空间中的分配通常列在 HW 文档中。对于 32 位嵌入式系统,phys addr 空间通常是稀疏的。分配通常是硬连线的而不是动态的,但是硬件的安装可以是可选的。因此,通常在启动 MMU 之前的引导期间(例如通过 U-Boot)进行探测可以找到已安装的内容。向内核提供了一个可用 RAM 的“内存列表”,因此如果您想劫持一些 RAM,您应该在内核启动之前编辑该列表。

以上是关于添加 request_mem_region 后,我的驱动程序每次第一次访问都会失败,并显示“忙碌”消息的主要内容,如果未能解决你的问题,请参考以下文章

request_mem_region,ioremap 和phys_to_virt()

内核request_mem_region 和 ioremap的理解

内核request_mem_region 和 ioremap的理解

Rails:添加列后添加索引

生成文档后添加元素

在 Blender 中添加后如何编辑网格属性