このページを編集する際は、編集に関する方針に従ってください。

概要

  • linux2.6/mm/rmap.cにて定義
  • 引数で渡されたページに対応する物理ページとのマッピングを解除する
    • マッピングの解除に失敗した場合はエラーコードを返す

引数

実装

/*

* Subfunctions of try_to_unmap: try_to_unmap_one called
* repeatedly from either try_to_unmap_anon or try_to_unmap_file.
*/

static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma) {

	struct mm_struct *mm = vma->vm_mm;
	unsigned long address;
  • アンマップするページの仮想アドレス
	pgd_t *pgd;
  • ページグローバルディレクトリ
	pmd_t *pmd;
  • ページミドルディレクトリ
	pte_t *pte;
	pte_t pteval;
  • ページテーブルエントリ
	int ret = SWAP_AGAIN;
	if (!mm->rss)
		goto out;
  • mm->rssはmmが使用している実ページ数
    • mmが実ページを使用していない場合はoutへジャンプ
	address = vma_address(page, vma);
  • メモリリージョンvma内にあるページpageのアドレスを返す
    • メモリリージョン内にページが存在しない場合はエラーコードを返す
    • 詳細はvma_address()/linux2.6を参照
	if (address == -EFAULT)
		goto out;
  • アドレスが適切な値でない場合はoutへジャンプ
	/*
	 * We need the page_table_lock to protect us from page faults,
	 * munmap, fork, etc...
	 */
	spin_lock(&mm->page_table_lock);
  • スピンロックmm->page_table_lockの取得を試みる
    • ロックの取得に失敗した場合は待ち状態に入る
    • 詳細はspin_lock()/linux2.6を参照
	pgd = pgd_offset(mm, address);
  • addressに対応するページグローバルディレクトリを返す
	if (!pgd_present(*pgd))
		goto out_unlock;
	pmd = pmd_offset(pgd, address);
  • 引数で渡された情報に基づきページミドルディレクトリの特定エントリのアドレスを返す
	if (!pmd_present(*pmd))
		goto out_unlock;
  • ページミドルディレクトリpmdがメモリ上に存在する場合は1を返す。そうでない場合は0を返す
	pte = pte_offset_map(pmd, address);
	if (!pte_present(*pte))
		goto out_unmap;
  • pteに対応する物理ページが存在していない場合はout_unmapへジャンプ
  • ページテーブルエントリpteに対応する物理ページが存在するか判定する
	if (page_to_pfn(page) != pte_pfn(*pte))
		goto out_unmap;
  • pageとpteの対応するページフレーム番号が異なる場合はout_unmapへジャンプ
  • ページpageからページフレーム番号を計算し返す
  • ページテーブルエントリpteに対応するページフレーム番号を返す
	/*
	 * If the page is mlock()d, we cannot swap it out.
	 * If it's recently referenced (perhaps page_referenced
	 * skipped over this mm) then we should reactivate it.
	 */
	if ((vma->vm_flags & (VM_LOCKED|VM_RESERVED)) ||
			ptep_clear_flush_young(vma, address, pte)) {
  • ページテーブルエントリpteのアクセスフラグをクリアし、参照している(自分を含まない)CPUに対して仮想アドレスaddressに対応するTLBの無効要求を送信する
		ret = SWAP_FAIL;
		goto out_unmap;
	}
	/*
	 * Don't pull an anonymous page out from under get_user_pages.
	 * GUP carefully breaks COW and raises page count (while holding
	 * page_table_lock, as we have here) to make sure that the page
	 * cannot be freed.  If we unmap that page here, a user write
	 * access to the virtual address will bring back the page, but
	 * its raised count will (ironically) be taken to mean it's not
	 * an exclusive swap page, do_wp_page will replace it by a copy
	 * page, and the user never get to see the data GUP was holding
	 * the original page for.
	 *
	 * This test is also useful for when swapoff (unuse_process) has
	 * to drop page lock: its reference to the page stops existing
	 * ptes from being unmapped, so swapoff can make progress.
	 */
	if (PageSwapCache(page) &&
	    page_count(page) != page_mapcount(page) + 2) {
  • ページpageがスワップキャッシュとして使用されているか調べる
    • スワップキャッシュとして使用されている場合は1を、そうでない場合は0を返す
    • 詳細はPageSwapCache()/linux2.6を参照
  • ページpageの参照数を1カウントアップする
  • ページpageのアドレス空間にマップされている数を返す
		ret = SWAP_FAIL;
		goto out_unmap;
	}
	/* Nuke the page table entry. */
	flush_cache_page(vma, address);
	pteval = ptep_clear_flush(vma, address, pte);
  • ページテーブルエントリpteをクリアし、クリアされる前のページテーブルエントリを返す
	/* Move the dirty bit to the physical page now the pte is gone. */
	if (pte_dirty(pteval))
		set_page_dirty(page);
  • ページテーブルエントリptevalが更新されているか調べる
    • 更新されている場合は1を、そうでない場合は0を返す
    • 詳細はpte_dirty()/linux2.6を参照
  • ページpageの更新フラグを設定する
	if (PageAnon(page)) {
  • ページpageがユーザ仮想メモリにマッピングされている場合、1を返して終了
		swp_entry_t entry = { .val = page->private };
		/*
		 * Store the swap location in the pte.
		 * See handle_pte_fault() ...
		 */
		BUG_ON(!PageSwapCache(page));
  • ページpageがスワップキャッシュとして使用されているか調べる
    • スワップキャッシュとして使用されている場合は1を、そうでない場合は0を返す
    • 詳細はPageSwapCache()/linux2.6を参照
		swap_duplicate(entry);
  • スワップエントリentryが該当するスワップ領域内に収まっているか調べる
		if (list_empty(&mm->mmlist)) {
  • リストmm->mmlistが空であるか調べる
			spin_lock(&mmlist_lock);
  • スピンロックmmlist_lockの取得を試みる
    • ロックの取得に失敗した場合は待ち状態に入る
    • 詳細はspin_lock()/linux2.6を参照
			list_add(&mm->mmlist, &init_mm.mmlist);
  • リストinit_mm.mmlistにエントリmm->mmlistを追加する
			spin_unlock(&mmlist_lock);
  • スピンロックmmlist_lockの開放を行う。プリエンプション機能を有効にし、可能であれば自ら積極的にプリエンプション(実行権の移譲を行うこと)する
		}
		set_pte(pte, swp_entry_to_pte(entry));
  • スワップエントリentryからページテーブルエントリを作る
  • 設定元のページテーブルエントリswp_entry_to_pte(entry)の情報を設定先のページテーブルエントリpteへコピーする
		BUG_ON(pte_file(*pte));
  • ページテーブルエントリpteがページキャッシュにあるか、スワップ領域上にあるか調べる
    • 1の場合はページキャッシュにあり、0の場合はスワップ領域上にある
    • 詳細はpte_file()/linux2.6を参照
		mm->anon_rss--;
	}
	mm->rss--;
	page_remove_rmap(page);
	page_cache_release(page);

out_unmap:

	pte_unmap(pte);
  • CONFIG_HIGHPTEが無効である場合、処理を行わない

out_unlock:

	spin_unlock(&mm->page_table_lock);
  • スピンロックmm->page_table_lockの開放を行う。プリエンプション機能を有効にし、可能であれば自ら積極的にプリエンプション(実行権の移譲を行うこと)する

out:

	return ret;

}

呼出元


履歴

  • 作者:ひら
  • 日付:2005/11/29
  • 対象:2.6.10
    更新日更新者更新内容

コメント



トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2009-11-24 (火) 07:19:57 (2861d)