ハイメモリ(その2)


ハイメモリは永続的カーネルマッピングで、そのページフレームを使用するプロセスの遅延を許すものでしたが、遅延を許されない使用方として、一時的カーネルマッピングと言うのがあります。これはまさにバンク切り替えで、バンク切り替えをページエントリテーブルを書き換える事で実現しています。なお、一時カーネルマッピングして、速やかにそのページフレームにデータを書き込むなり、読み込むなりして、アンマップする必要(マップしている状態で、他のプロセスがマップしにいくと、そのまま何も考えずに、新しいアドレスでマップしに行くみたいです...)があります。

一時カーネルマッピングのページエントリテーブルとして、固定マップ用リニアアドレスとして、enum fixed_addressesが定義されており、そのFIX_KMAP_BEGINからFIX_KMAP_ENDの間になります。__fix_to_virtマクロは固定マップ用リニアアドレスに変換します。引数のxはfixed_addressesのメンバーです。そのマクロはFIXADDR_TOP - ((x) << PAGE_SHIFT)となっています(+でなく-)。すなわち固定マップ用リニアアドレスアドレスは、FIXADDR_TOP=0xfffff000から低位に向かって配置されていきます。

FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1となっています。KM_TYPE_NRは一時カーネルマッピングのエントリの数で、NR_CPUSはCPUの数です。すなわちそのエントリはCPU毎に独自に有していると言うことです。
#define __fix_to_virt(x)        (FIXADDR_TOP - ((x) << PAGE_SHIFT))

enum fixed_addresses {
       FIX_HOLE,
       FIX_VDSO,
       FIX_DBGP_BASE,
       FIX_EARLYCON_MEM_BASE,
  :
       FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */
       FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
  :
       __end_of_fixed_addresses
};
enum km_typeがFIX_KMAP_BEGINからのエントリとなります。KM_BOUNCE_READならそのエントリはFIX_KMAP_BEGIN+KM_BOUNCE_READ,KM_SKB_SUNRPC_DATAならFIX_KMAP_BEGIN+KM_SKB_SUNRPC_DATAという具合です。エントリはその使用方法でカテゴリ化されており、できるだけ異なる処理でエントリの競合が起きないよう配慮されています。ただこのカテゴリによる処理の違いはなさそうです。基本的にはどれを使ってもよさそうみたいです。
enum km_type {
D(0)    KM_BOUNCE_READ,
D(1)    KM_SKB_SUNRPC_DATA,
D(2)    KM_SKB_DATA_SOFTIRQ,
D(3)    KM_USER0,
D(4)    KM_USER1,
D(5)    KM_BIO_SRC_IRQ,
D(6)    KM_BIO_DST_IRQ,
D(7)    KM_PTE0,
D(8)    KM_PTE1,
D(9)    KM_IRQ0,
D(10)   KM_IRQ1,
D(11)   KM_SOFTIRQ0,
D(12)   KM_SOFTIRQ1,
D(13)   KM_TYPE_NR
};
マップ処理はkmap_atomic関数から、kmap_atomic_prot関数をコールすることで行われます。kmap_protは__PAGE_KERNELと定義されており、従って_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | |
_PAGE_ACCESSED _PAGE_GLOBAL
_PAGE_PRESENTページが存在している
_PAGE_RW読み書き可能
_PAGE_DIRTYページが汚れている
_PAGE_ACCESSEDページにアクセスあり
_PAGE_GLOBAL?
_PAGE_NX実行不可
一時カーネルマッピングの特性故(すぐに読み書きして解放する。)、ということでしょうか?
#define kmap_prot _PAGE_KERNEL
#define __PAGE_KERNEL           (__PAGE_KERNEL_EXEC | _PAGE_NX)
#define __PAGE_KERNEL_EXEC                                              \
       (_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED | _PAGE_GLOBAL)

void *kmap_atomic(struct page *page, enum km_type type)
{
       return kmap_atomic_prot(page, type, kmap_prot);
}
kmap_atomic_prot関数でマップします。まず、pagefault_disable関数はプリエンプションカウンターをインクリメントし、プリエンプションを不可とします(他のカーネルパスによりマップされないため)。

pageがハイメモリでないなら、page_addressでリニアアドレスを取得してお終いです。ハイメモリならenum km_type typeから、idx = type + KM_TYPE_NR*smp_processor_id()でそのエントリを求めます。そのエントリをset_pte関数で、pteに書き込んで、arch_flush_lazy_mmu_mode関数でTLBを無効にしてお終いです。
void *kmap_atomic_prot(struct page *page, enum km_type type, pgprot_t prot)
{
       enum fixed_addresses idx;
       unsigned long vaddr;

       pagefault_disable();

       if (!PageHighMem(page))
               return page_address(page);

       debug_kmap_atomic_prot(type);

       idx = type + KM_TYPE_NR*smp_processor_id();
       vaddr = __fix_to_virt(FIX_KMAP_BEGIN + idx);
       BUG_ON(!pte_none(*(kmap_pte-idx)));
       set_pte(kmap_pte-idx, mk_pte(page, prot));
       arch_flush_lazy_mmu_mode();

       return (void *)vaddr;
}
実装そのものは単純故、負荷の軽い処理としての利用という事のようです。


最終更新 2010/12/11 12:15:14 - north
(2010/12/11 12:14:48 作成)


検索

アクセス数
3689392
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。