web-dev-qa-db-ja.com

Linuxカーネルの任意のアドレスから構造体ページを取得する方法

struct page *のリストを取得し、デバイスとメモリを共有するための記述子テーブルを作成する既存のコードがあります。そのコードの上位層は現在、vmallocまたはユーザースペースから割り当てられたバッファーを想定しており、 vmalloc_to_page を使用して対応するstruct page *を取得します。

ここで、上位層は、vmallocを介して取得されたメモリだけでなく、すべての種類のメモリに対処する必要があります。これは、kmallocで取得されたバッファー、カーネルスレッドのスタック内のポインター、または私が気付いていない他の場合である可能性があります。私が持っている唯一の保証は、この上位層の呼び出し元が、問題のメモリバッファがその時点でカーネル空間にマップされていることを確認する必要があることです(つまり、この時点ですべてのbuffer[i]0<=i<sizeにアクセスすることが有効です)。 任意のポインタに対応するstruct page*を取得するにはどうすればよいですか?

それを擬似コードに入れると、私はこれを持っています:

lower_layer(struct page*);
upper_layer(void *buffer, size_t size) {
    for (addr = buffer & PAGE_MASK; addr <= buffer + size; addr += PAGE_SIZE) {
        struct page *pg = vmalloc_to_page(addr);
        lower_layer(pg);
    }
}

そして、有効なバッファに対処するためにupper_layerを変更する必要があります(lower_layerを変更せずに)。

virt_to_page を見つけました。これは、 Linuxデバイスドライバ が「論理アドレスで動作し、vmallocのメモリではない」ことを示しています。またはハイメモリ」。さらに、 is_vmalloc_addr アドレスがvmallocからのものかどうかをテストし、 virt_addr_valid アドレスが有効な仮想アドレスであるかどうかをテストします(virt_to_pageの飼料。これにはkmalloc(GFP_KERNEL)が含まれます。およびカーネルスタック)。他の場合はどうですか:グローバルバッファ、高メモリ(今は無視できますが、いつか来るでしょう)、おそらく私が気付いていない他の種類ですか?だから私は私の質問を次のように再定式化することができます:

  1. カーネル内のall種類のメモリゾーンとは何ですか?
  2. どうすればそれらを区別できますか?
  3. それぞれのページマッピング情報を取得するにはどうすればよいですか?

重要な場合、コードはARM(MMUを使用)で実行されており、カーネルバージョンは少なくとも2.6.26です。

必要なのは、次のようなページテーブルウォークだと思います(警告、実際のコードではない、ロックがないなど):

struct mm_struct *mm = current->mm;
pgd = pgd_offset(mm, address);
pmd = pmd_offset(pgd, address);  
pte = *pte_offset_map(pmd, address);  
page = pte_page(pte);

しかし、これには非常に注意する必要があります。たとえば、取得したkmallocアドレスはページ揃えされていない可能性があります。これは私には非常に危険なAPIのように聞こえます。

13
gby

アドレスを構造体ページにマッピングする

Linuxには、仮想アドレスを物理アドレスにマッピングする高速な方法と、構造体ページを物理アドレスにマッピングするための高速な方法が必要です。 Linuxは、仮想メモリと物理メモリの両方で、グローバルmem_map配列が、システム内の物理メモリを表すすべての構造体ページへのポインタを持っているため、どこにあるかを知ることによってこれを実現します。すべてのアーキテクチャは非常に類似したメカニズムでこれを実現しますが、説明のために、x86のみを注意深く調べます。

物理カーネルアドレスから仮想カーネルアドレスへのマッピング

pAGE_OFFSETを減算するだけで、任意の仮想アドレスを物理アドレスに変換できます。これは、基本的に、マクロ__pa()を使用した関数virt_to_phys()が行うことです。

/* from <asm-i386/page.h> */
132 #define __pa(x)        ((unsigned long)(x)-PAGE_OFFSET)

/* from <asm-i386/io.h> */
 76 static inline unsigned long virt_to_phys(volatile void * address)
 77 {
 78         return __pa(address);
 79 }

明らかに、逆の操作では、PAGE_OFFSETを追加するだけで済みます。これは、関数phys_to_virt()とマクロ__va()によって実行されます。次に、これが構造体ページの物理アドレスへのマッピングにどのように役立つかを確認します。

virt_to_phys()を使用して仮想アドレスを物理アドレスに変換できない例外が1つあります。具体的には、PPC =およびARMアーキテクチャ、virt_to_phys()を使用して、関数consistent_alloc()によって返されたアドレスを変換することはできません。consistent_alloc()は=で使用されますPPCおよびARM DMAで使用するためにキャッシュされていないものからメモリを返すアーキテクチャ。

カーネル内のすべての種類のメモリゾーンは何ですか? <---ここを参照してください

7
Lars

64ビットアーキテクチャの場合、gbyの答えは次のように適合させる必要があります。

 pgd_t * pgd;
 pmd_t * pmd;
 pte_t * pte;
 struct page *page = NULL;
 pud_t * pud;
 void * kernel_address;

 pgd = pgd_offset(mm, address);
 pud = pud_offset(pgd, address);
 pmd = pmd_offset(pud, address);
 pte = pte_offset_map(pmd, address);
 page = pte_page(*pte);

 // mapping in kernel memory:
 kernel_address = kmap(page);

 // work with kernel_address....

 kunmap(page);
1
JFL

あなたは試すことができます virt_to_page 。それがあなたが望むものかどうかはわかりませんが、少なくともそれはどこかで探し始めるところです。

1
CesarB

ユーザースペースに割り当てられたメモリの場合、get_user_pagesを使用します。これにより、mallocされたメモリに関連付けられたページのリストが表示され、参照カウンターもインクリメントされます(ページが完了したら、各ページでpage_cache_releaseを呼び出す必要があります)。 。)

Vmallocされたページの場合、vmalloc_to_pageはあなたの友達であり、何もする必要はないと思います。

1
Hervé