Swap - Swap領域の登録
1. Swap領域の登録
1.1 swapon
sys_swapon()により、システムにスワップ領域を登録する。sys_swapon()はswaponシステムコールのエントリポイント。
sys_swapon()はスワップ領域の管理テーブルswap_infoから空きエントリを探して、見つけた空きエントリに、登録するスワップ領域のstruct swap_info_structを構築していく。
/* swap_infoから空き領域を探す */ p = swap_info; for (type = 0 ; type < nr_swapfiles ; type++,p++) if (!(p->flags & SWP_USED)) break; /* 以下で空きエントリ(ポインタpが指している場所)に * struct swap_info_structを構築していく */ INIT_LIST_HEAD(&p->extent_list); p->flags = SWP_USED; p->swap_file = NULL; : : /* Swap領域のパーティション/ファイルをopen */ name = getname(specialfile); swap_file = filp_open(name, O_RDWR|O_LARGEFILE, 0); : /* Swapヘッダを読み出して内容チェック(*1) */ page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage, swap_file); wait_on_page_locked(page); /* 読み込み完了待ち */ /* スワップヘッダのシグネチャチェック */ /* バージョンに応じた処理 */ switch (swap_header_version) { case 1: /* Ver.1はもう未サポート */ case 2: /* Ver.2 の初期化処理 */ /* 1つのスワップ領域で使用できる最大サイズ(ページ数)を算出する */ maxpages = swp_offset(pte_to_swp_entry(swp_entry_to_pte(swp_entry(0,~0UL)))) - 1; /* */ if (maxpages > swap_header->info.last_page) maxpages = swap_header->info.last_page; /* スワップ領域内の各ページの参照カウンタテーブルを確保 */ p->swap_map = vmalloc(maxpages * sizeof(short)); /* ディスク上に使用不可なページがあれば、 * 参照カウンタテーブルの該当箇所にSWAP_MAP_BADをセット */ for (i = 0; i < swap_header->info.nr_badpages; i++) { int page_nr = swap_header->info.badpages[i]; if (page_nr <= 0 || page_nr >= swap_header->info.last_page) error = -EINVAL; else p->swap_map[page_nr] = SWAP_MAP_BAD; } /* 使用可能なページ数 */ nr_good_pages = swap_header->info.last_page - swap_header->info.nr_badpages - 1 /* header page */; } : if (nr_good_pages) { /* スワップ領域のディスクブロックへのマップを作成する * (swap_info_structのextent_listを構築する) */ nr_extents = setup_swap_extents(p, &span); }
1.2 Swap領域のディスクブロックへのマッピング
Swap領域の空間がディスクブロックにどのようにマッピングされるかはstruct swap_info_structのextent_listによって決められる。extent_listはstruct swap_extentをチェーンしている。1つのstruct swap_extentはSwap領域の連続ページ領域がディスク上のどの連続ブロックに対応しているかを表す。詳細は「Swap関連データ構造」参照。
extent_listの構築はswapon時にsetup_swap_extents()で行われる。extent_listの構築はスワップ領域がパーティションであるかファイルであるかで処理が大きくわかれる。
スワップ領域がディスクパーティション(スワップパーティション)であれば、スワップ空間はディスクパーティションにストレートにマッピングされるため、スワップ空間全体を表すswap_extentが1つ作成されるだけとなる(Swap関連データ構造 図3)。
一方、スワップ領域がファイルであった場合は、スワップファイルがディスク上で連続ブロックとなっている部分を探しながらswap_extentを作成していくので処理が若干複雑になる(図1およびSwap関連データ構造
図4)。またこの場合、スワップファイルがディスク上のページ境界内で非連続となっているブロック群はswap_extentが作られずスワップ領域とし ては使われない(図1の濃い灰色のブロック)。これは、Linuxではスワップ処理はページ単位で行われることから、その処理を簡単にするため。
図1 スワップファイルに対するswap_extent作成処理
inode = sis->swap_file->f_mapping->host; /* スワップ領域がディスクパーティション(スワップパーティション)の場合 * (大抵はこっち) */ if (S_ISBLK(inode->i_mode)) { /* 全スワップ領域をスワップパーティションへストレートにマッピング * (swap_extentを1つだけ作って終了) */ ret = add_swap_extent(sis, 0, sis->max, 0); *span = sis->pages; goto done; } /* 以下はファイルをスワップ領域にしている場合の処理 */ probe_block = 0; /* スワップファイル内のブロック番号 */ page_no = 0; /* スワップ空間内のページ番号 */ last_block = i_size_read(inode) >> blkbits; /* スワップファイルの最終ブロック番号 */ /* スワップファイルを先頭からサーチして、連続ブロックとなっている * 領域を検出してswap_extentを作成する。 */ while ((probe_block + blocks_per_page) <= last_block && page_no < sis->max) { /* スワップファイルのブロック番号から * ディスク上のブロック番号を取得 */ first_block = bmap(inode, probe_block); /* ページ境界先頭のブロックでなければスキップ * (このブロックはマッピングされない。 * つまりスワップ領域としては使用しない) */ if (first_block & (blocks_per_page - 1)) { probe_block++; goto reprobe; } /* first_blockがページ境界先頭のブロックだった */ /* ページ境界内のその他のブロックが連続しているかをチェック */ for (block_in_page = 1; block_in_page < blocks_per_page; block_in_page++) { block = bmap(inode, probe_block + block_in_page); if (block != first_block + block_in_page) { /* Discontiguity */ probe_block++; goto reprobe; } } : /* page_no 1ページ分のマッピング(swap_extent)を * extent_listに登録(*1) */ ret = add_swap_extent(sis, page_no, 1, first_block); nr_extents += ret; page_no++; probe_block += blocks_per_page; reprobe: }