7

2GBのメモリを使用するセットアップがあり、1GB(またはそれ以上)の物理メモリをユーザースペースの仮想アドレスにマッピングしたいと思います。32ビットのセットアップでは、ユーザーの土地アプリで3GBの仮想アドレスを使用できるため、理論的には可能です。

カーネルのコマンドラインを次のパラメーターで更新しました。カーネルmem=1G memmap=1G$1G に1GBのRAMを認識させ、最後の1GBを予約します。

ユーザースペースのmmap()呼び出しを処理し、関数を使用して物理アドレス0x40000000(1G)をユーザースペースアドレスにマップするカスタムドライバーがありますremap_pfn_range()

ただし、この関数はでカーネルBUG()をトリガーしますremap_pte_range()。同じ呼び出しが、1GBではなく300MBのリマップで機能していました。

私は通常、ioremap()物理アドレスをカーネル仮想アドレスにマップするためにドライバーを呼び出していました。この場合、1G / 3G仮想アドレスが分割されているためできません(カーネルの場合は1G、アプリの場合は3G)。だから私は、カーネルでこれらの物理アドレスをマッピングせずに、物理アドレスをユーザースペースの仮想アドレスにマッピングすることが可能かどうか疑問に思いましたか?

これは32ビットのx86カーネル、つまり「i386」アーキテクチャです。

4

1 に答える 1

5

remap_pfn_range 呼び出しがカーネル BUG() をトリガーするのはなぜですか

ここBUG_ONでのマクロremap_pfn_rangeの呼び出し

2277 BUG_ON(addr >= end);

remap_pfn_range を呼び出すremap_pud_rangeを呼び出す を呼び出すをremap_pmd_range呼び出すremap_pte_range

BUG_ONここへの、またはここVM_BUG_ONからの後続の呼び出しremap_pmd_range

2191 VM_BUG_ON(pmd_trans_huge(*pmd));

そしてremap_pte_range ここから

2171 BUG_ON(!pte_none(*pte));

BUG_ONマクロはここで定義されます

なので

#define BUG_ON(condition) do { if (unlikely(condition)) BUG(); } while(0)

その上にBUGマクロが定義されており、メッセージとパニックを出力します。

unlikelyマクロはここで定義されます

として# define unlikely(x) (__builtin_expect(!!(x), 0))

したがって、開始するターゲット ユーザー アドレスがとして定義されているaddr以上の場合、BUG_ON は 1 を返し、BUG を呼び出します。endend = addr + PAGE_ALIGN(size);

または、ここでpmd_trans_huge定義されている場合

153 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
154 static inline int pmd_trans_splitting(pmd_t pmd)
155 {
156         return pmd_val(pmd) & _PAGE_SPLITTING;
157 }
158 
159 static inline int pmd_trans_huge(pmd_t pmd)
160 {
161         return pmd_val(pmd) & _PAGE_PSE;
162 }
163 
164 static inline int has_transparent_hugepage(void)
165 {
166         return cpu_has_pse;
167 }

0 を返します。これは、CONFIG_TRANSPARENT_HUGEPAGE がカーネルで構成されていない場合、またはpmd(Page Metadate) 値または& _PAGE_PSE

または whenpte_noneは、対応するエントリが存在しない場合は 1 を返し、存在する場合は 0 を返します。

したがって!pte_none、 に渡される条件として、対応するページ テーブル エントリが存在しない場合は 0 を返し、それ以外の場合は 1 を返しますBUG_ON

BUGページ テーブル エントリが既に存在する場合は、マクロの呼び出しが発生します。

!GB よりも少ない量のメモリを 300MB より大きく、たとえば 500MB や 800MB と指定するとどうなりますか?

したがって、開始アドレスが終了アドレスよりも大きいかCONFIG_TRANSPARENT_HUGEPAGE、カーネルで構成されていないか、ページ メタデータが存在しないか、ページ テーブル エントリが既に存在することを参照しています。

コメントから明確にするために、あなたの呼び出しはremap_pfn_rangePage Table Entry ポインタを参照するか*pte、すでにページテーブルエントリまたはを指していますpte

これはset_pte_at(mm, addr, pte, pte_mkspecial(pfn_pte(pfn, prot)));、pte ポインターが既にページ テーブル エントリを指しているため、それを に設定できないため、失敗することを意味しpteますpte_mkspecial(pfn_pte(pfn, prot))

1G/3G 仮想アドレス分割のバイパス

次の記事を参照してくださいLinux カーネルの高メモリ

最小 1 GB の RAM を搭載した HIGHMEM に関する追加情報について説明している次のメーリング リストの投稿を参照してください。

カーネルお​​よび非カーネル仮想アドレス空間のユーザーランドへのマッピングに関する情報

カーネル仮想アドレスと非カーネル (vmalloc() によって返される) 仮想アドレスをユーザー空間にマップする 1 つの方法は、remap_pfn_range. 詳細については、 Linux メモリ マッピングを参照してください。

古いカーネルでの nopage ハンドラーの使用を置き換える別の方法は、次のvm_insert_page関数です。

追加のリソースは次のとおりです。

于 2012-03-11T00:53:55.783 に答える