__iomem
被用于存储ioremap()
的返回类型,但我在ARM架构中使用了u32
也运行良好。
那么__iomem
在这里有什么区别?在哪些情况下应该使用它呢?__iomem
被用于存储ioremap()
的返回类型,但我在ARM架构中使用了u32
也运行良好。
那么__iomem
在这里有什么区别?在哪些情况下应该使用它呢?许多类型转换都会“很好地”工作。然而,这并不是非常严格的。没有任何东西阻止你将强制转换为*并对其进行解引用,但这不遵循内核API并容易出错。
__iomem
是Sparse使用的一种cookie,Sparse是用于在内核中查找可能的编码错误的工具。如果您没有启用Sparse编译内核代码,则无论如何都将忽略__iomem
。
首先安装Sparse,然后在您的make
调用中添加C=1
来使用Sparse。例如,在构建模块时,请使用:
make -C $KPATH M=$PWD C=1 modules
__iomem
的定义如下:
# define __iomem __attribute__((noderef, address_space(2)))
添加(并要求)像__iomem
的cookie来处理所有I/O访问是一种更严格、避免编程错误的方法。您不想使用绝对地址从/向I/O内存区域读写,因为通常使用的是虚拟内存。因此,
void __iomem *ioremap(phys_addr_t offset, unsigned long size);
通常用于获取指定长度为size
字节的I/O物理地址offset
的虚拟地址。 ioremap()
返回一个带有__iomem
cookie的指针,因此现在可以与像readl()
/writel()
这样的内联函数一起使用(尽管现在更建议使用更明确的宏,例如ioread32()
/iowrite32()
),这些函数接受__iomem
地址。
此外,noderef
属性由Sparse用于确保您不会解引用__iomem
指针。在某些架构中,访问I/O确实是通过内存映射的,但其他架构使用特殊指令访问I/O,在这种情况下,解引用将无法工作。
让我们看一个例子:
void *io = ioremap(42, 4);
Sparse不开心:
warning: incorrect type in initializer (different address spaces)
expected void *io
got void [noderef] <asn:2>*
或者:u32 __iomem* io = ioremap(42, 4);
pr_info("%x\n", *io);
稀疏也不开心:
warning: dereference of noderef expression
在最后一个例子中,第一行是正确的,因为ioremap()
将其值返回给一个__iomem
变量。但是然后我们对它进行了解引用,这是不应该的。这使得Sparse感到满意:
void __iomem* io = ioremap(42, 4);
pr_info("%x\n", ioread32(io));
底线:在需要时始终使用__iomem
(作为返回类型或参数类型),并使用Sparse确保您已这样做。另外:不要解引用__iomem
指针。
编辑:这里有一篇关于__iomem
及其相关函数诞生的精彩LWN文章。
简单、直接和简短(S3)的解释。 有一篇文章https://lwn.net/Articles/653585/提供更多细节。