在编写Linux设备驱动程序时,__iomem的用途是什么?

30
我看到__iomem被用于存储ioremap()的返回类型,但我在ARM架构中使用了u32也运行良好。 那么__iomem在这里有什么区别?在哪些情况下应该使用它呢?
2个回答

64

许多类型转换都会“很好地”工作。然而,这并不是非常严格的。没有任何东西阻止你将强制转换为*并对其进行解引用,但这不遵循内核API并容易出错。

__iomemSparse使用的一种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文章


漂亮的解释。言简意赅,附有一些好的参考资料。 - Adit Ya
这正是我在寻找的。讲解得非常清楚明白。 - BZKN

0

虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。如果链接页面更改,仅有链接的答案可能会失效。-【来自审查】 - Antoine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接