___iomem
_がioremap()
の戻り値の型を格納するために使用されていることを確認しましたが、ARMアーキテクチャで_u32
_を使用しました。うまく機能します。
では、ここで___iomem
_はどのような違いをもたらしますか?そして、どのような状況でそれを正確に使用する必要がありますか?
多くの型キャストは「うまく機能する」だけです。ただし、これはそれほど厳密ではありません。 _u32
_を_u32 *
_にキャストして逆参照することを妨げるものは何もありませんが、これはカーネルAPIに準拠しておらず、エラーが発生しやすくなっています。
___iomem
_は、カーネルで発生する可能性のあるコーディング障害を見つけるために使用されるツールである Sparse によって使用されるCookieです。スパースを有効にしてカーネルコードをコンパイルしない場合、___iomem
_はとにかく無視されます。
Sparseを使用するには、最初にインストールしてから、make
呼び出しに_C=1
_を追加します。たとえば、モジュールを構築するときは、次を使用します。
_make -C $KPATH M=$PWD C=1 modules
_
___iomem
_は次のように定義されます。
_# define __iomem __attribute__((noderef, address_space(2)))
_
すべてのI/Oアクセスに___iomem
_のようなCookieを追加(および要求)することは、より厳密にプログラミングエラーを回避する方法です。通常は仮想メモリを使用しているため、絶対アドレスを使用してI/Oメモリ領域との間で読み取り/書き込みを行う必要はありません。したがって、
_void __iomem *ioremap(phys_addr_t offset, unsigned long size);
_
通常、I/O物理アドレスoffset
の仮想アドレスを、バイト単位で指定された長さsize
で取得するために呼び出されます。 ioremap()
は___iomem
_ cookieを含むポインターを返すため、これはmay nowreadl()
/writel()
などのインライン関数で使用されます(ただし___iomem
_アドレスを受け入れるより明示的なマクロioread32()
/iowrite32()
などを使用することが望ましいようになりました。
また、noderef
属性は、___iomem
_ポインターを逆参照しないようにするためにSparseによって使用されます。逆参照は、I/Oが実際にメモリマップされている一部のアーキテクチャで機能するはずですが、他のアーキテクチャはI/Oにアクセスするために特別な命令を使用し、この場合、逆参照は機能しません。
例を見てみましょう:
_void *io = ioremap(42, 4);
_
スパースは幸せではありません:
_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
_変数に返すため、最初の行は正しいです。しかし、その後、私たちはそれを尊重します、そして私たちはそうするべきではありません。
これはスパースを幸せにします:
_void __iomem* io = ioremap(42, 4);
pr_info("%x\n", ioread32(io));
_
結論:必要な場合は常に___iomem
_を(戻り値の型またはパラメーター型として)使用し、スパースを使用して確実に使用してください。また、___iomem
_ポインターを逆参照しないでください。
編集:___iomem
_の開始とそれを使用する関数についてのすばらしい LWN記事 です。