スワップ処理のつづき
Rev.1を表示中。最新版はこちら。
スワップ処理はページ回収処理の一環として行われ、回収ページを引数にadd_to_swap関数がコールされる。ここでget_swap_page関数でスワップ領域を特定し、そこからscan_swap_mapでスワップスロットを取得し、この2つの情報をswp_entry_tとして、get_swap_page関数の返り値としてadd_to_swap関数に返される。 ここでのswp_entry_tはアーキテクチャに非依存である。スワップアウトは回収ページとswp_entry_t情報が特定されれば回収ページ内容をスワップ領域に書き出すことが可能となる。しかし、直接書き出しをおこなっていなし。add_to_swap_cacheがコールされ、このページをいったんスワップキャッシュという状態におく。これは共有するページにおいて、複数のプロセスでスワップアウトしたり、1つのプロセスがスワップアウトし、もう一つのプロセスがスワップインをしようとする競合を防ぐためである。
スワップアウト、スワップインするときはこのスワップキャッシュを確認して実際の実際のスワップ処理をおこうことで、この競合を防いでいる。実際のスワップ領域への書き込みはこのあと行う。
スワップキャッシュに置くというのは、ページ参照カウンタをインクリメントし(スワップのページスロットが参照するため)、page->privateをswp_entry_tで更新する。スワップキャッシュ用のラヂックスツリに登録するこtで実現している。
そしてこのページを引数にtry_to_unmap関数がコールされ、ここでそのページに対応するページエントリテーブルPTEをこのpage->privateに設定したswp_entry_tで更新するようになっているのだが、ここでの更新はアーキテクチャー依存で、swp_entry_tはアーキテクチャに応じたフォーマットに変換されての更新となる。
86アーキテクチャでは下位0ビットを0で、下1ビットから7ビットをスワップ領域、8ビットから31ビットをページスロットとしている。下位0ビットが0というのはそのページが存在しないことを意味する。もしこのPTEでアクセスしたら例外が発生するわけだが、その例外処理でスワップインの処理をおこなうことになる。ただページフレームがまだ割り当てられてないことでも同じようなに例外が発生する。そこは、スワップ領域のページスロットの0番目はスワップ領域のヘッダ情報が収められて、ページスロットは必ず1からだということを利用して、その例外のPTEがNULLなら、まだページフレームが割り当てられていな例外、そうでないならスワップアウトによる例外だということで処理を行っている。
swp_entry関数はスワップ領域typeとページスロットoffsetを引数とし、この2つをパックした値を返す。実装ではその識別子valの型のサイズに依存しないかたちで、その上位5ビットをtypeに、以下その下位にoffsetをパックした構造である。add_to_swap_cache関数ではpgae->privateにはこの値が設定される。
typedef struct { unsigned long val; } swp_entry_t; #define MAX_SWAPFILES_SHIFT 5 #define SWP_TYPE_SHIFT(e) (sizeof(e.val) * 8 - MAX_SWAPFILES_SHIFT) #define SWP_OFFSET_MASK(e) ((1UL << SWP_TYPE_SHIFT(e)) - 1) static inline swp_entry_t swp_entry(unsigned long type, pgoff_t offset) { swp_entry_t ret; ret.val = (type << SWP_TYPE_SHIFT(ret)) | (offset & SWP_OFFSET_MASK(ret)); return ret; }add_to_swap_cache関数でページをスワップキャッシュに変更する。page_cache_get関数でページ参照カウンターをインクリメントする。これはスワップ領域のページスロットが参照しているからである。これでこのページはスワップアウトしようとするプロセスとスワップ領域が参照していることになる。
SetPageSwapCache関数は、page->flagをPG_cashで更新し、set_page_private関数でpage->privateにswp_entry_tをセットしている。あとはwapper_space.page_treeをヘッドとするラヂィックスツリに本ページ挿入する。なおこの時点ではまだプロセスから見たページはスワップアウトされていない。プロセスとスワップ領域がともにこのページを参照している状態と言える。実際のプロセスからの切り離しはtry_to_unmap関数で行う。
int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask) { int error; BUG_ON(!PageLocked(page)); BUG_ON(PageSwapCache(page)); BUG_ON(PagePrivate(page)); error = radix_tree_preload(gfp_mask); if (!error) { page_cache_get(page); SetPageSwapCache(page); set_page_private(page, entry.val); spin_lock_irq(&swapper_space.tree_lock); error = radix_tree_insert(&swapper_space.page_tree, entry.val, page); if (likely(!error)) { total_swapcache_pages++; __inc_zone_page_state(page, NR_FILE_PAGES); INC_CACHE_INFO(add_total); } spin_unlock_irq(&swapper_space.tree_lock); radix_tree_preload_end(); if (unlikely(error)) { set_page_private(page, 0UL); ClearPageSwapCache(page); page_cache_release(page); } } return error; }プロセスからの切り離しはtry_to_unmap関数で、pteテーブルをpage->private(内容はswp_entry_tである。)で更新することになる。それにはpageからpteを検出する処理が必要となるのだが、それはpage識別子に紐づくvm_area_structからmm_structを取得して行う。逆マッピングというらしい。まあそういうことで取得したpteにpage->privateで更新するわけだが、ここはアーキテクチャ依存となる。X-86についてはスワップ領域を下位1ビットから7ビットへ、ページスロットを8ビット以降にシフトしたものをpteにセットしている。この0ビットは0となり、このpteにはページが無いことを意味することになる。
#define __swp_entry(type, offset) \ ((swp_entry_t) { ((type) << 1) | ((offset) << 8) }) static inline pte_t swp_entry_to_pte(swp_entry_t entry) { swp_entry_t arch_entry; arch_entry = __swp_entry(swp_type(entry), swp_offset(entry)); BUG_ON(pte_file(__swp_entry_to_pte(arch_entry))); return __swp_entry_to_pte(arch_entry); }スワップアウトされたページがアクセスされると例外が発生する。そしてhandle_pte_fault関数へと処理が移り、pte_present関数でページフォルト例外処理の場合で、そのpteエントリーがNULLでないならdo_swap_page関数とスワップインの処理が実行される。
static inline int handle_pte_fault(struct mm_struct *mm, struct vm_area_struct *vma, unsigned long address, pte_t *pte, pmd_t *pmd, int write_access) { pte_t entry; spinlock_t *ptl; entry = *pte; if (!pte_present(entry)) { if (pte_none(entry)) { if (vma->vm_ops) { if (likely(vma->vm_ops->fault)) return do_linear_fault(mm, vma, address, pte, pmd, write_access, entry); } return do_anonymous_page(mm, vma, address, pte, pmd, write_access); } if (pte_file(entry)) return do_nonlinear_fault(mm, vma, address, pte, pmd, write_access, entry); return do_swap_page(mm, vma, address, pte, pmd, write_access, entry); }