ページスロットの取得
Rev.1を表示中。最新版はこちら。
ページスロットの取得は、まっずページ領域を、そしてそこからページスロットを取得することにああります。ページ領域の取得は、優先順位に従って決定され、もし同じ優先順位ならラウンドロビンで取得されます。そして、そのスワップ領域のswap_map[]からページスロットを取得する事になるわけです。ページスロット取得において、まずSWAPFILE_CLUSTERという256個の物理的に連続するページスロットを確保し、そこから順に取得し、その領域を使い切ると再度SWAPFILE_CLUSTERを取得しなおすという具合です。もし、SWAPFILE_CLUSTERで取得した領域が無くなって、新規のSWAPFILE_CLUSTERが取得できない時、空きページスロットの先頭から走査する事で取得しています。
から
swp_entry_t get_swap_page(void) { struct swap_info_struct *si; pgoff_t offset; int type, next; int wrapped = 0; spin_lock(&swap_lock); if (nr_swap_pages <= 0) goto noswap; nr_swap_pages--;空きページスロットが無ければスキップです。あればnr_swap_pages--とし、スワップ領域を検索します。
for (type = swap_list.next; type >= 0 && wrapped < 2; type = next) { si = swap_info + type; next = si->next; if (next < 0 || (!wrapped && si->prio != swap_info[next].prio)) { next = swap_list.head; wrapped++; }上記の処理を行うのがこのforループ条件で、プログラム的にはなかなか凡人にできる実装ではないなと。ポイントはwrapped変数に使い方にあります。swap_list.nextをトップとし、そのリストを順番に走査していきます。type >= 0は最後まで走査した。ということです。wrapped < 2も同じような物ですが、処理において次のスワップ領域の優先順位が同じでないなら、再度優先順位のトップ(swap_list.head)から検索します。、この場合、インクリメントすることで再度同じ処理を避ける事にあります。優先順位が同じならそれが検索される事になります。(ラウンドロビン)
if (!si->highest_bit) continue; if (!(si->flags & SWP_WRITEOK)) continue;si->highest_bitは空きページスロットの終端です。言い換えればそれより前に空きページスロットがあります。0なら空きスロットは無いと言うことです。SWP_WRITEOKはswaponする時si->flagsにはSWP_ACTIVEが設定されています。SWP_ACTIVEは以下のマクロで、swapoff時の処理においてSWP_WRITEOKがレセットされます。
SWP_ACTIVE = (SWP_USED | SWP_WRITEOK),
swap_list.next = next; offset = scan_swap_map(si); if (offset) { spin_unlock(&swap_lock); return swp_entry(type, offset); } next = swap_list.next;検索するスワップ領域のswap_map[]から空きページスロットをscan_swap_map()検索します。取得できればそれを返り値で復帰します。無ければ次にスワップ領域で再度同じ処理を行うことになります。再ループ処理で、swap_list.next = next/next = swap_list.nextと冗長的な処理となっていますが、scan_swap_map()でたプロセスの切り替えが発生する場合があります。そしてそのケースでswapon等でのswap_list変更を伴う処理が行われた場合、最新のスワップリストで処理を行うためだと思います。
} nr_swap_pages++;ページスロットが取得できませんでした。先にインクした空きページスロット数をデクリメントします。
noswap: spin_unlock(&swap_lock); return (swp_entry_t) {0};取得できなかったため0を返します。
}
スワップ領域が決定すると、このスワップファイルのswap_map[]を走査して空きページスロットを取得します。ページスロットは物理的に連続するブロックが理想です。そのため検索において、まずSWAPFILE_CLUSTER個のブロックの取得を試み、そこから順次ページスロットを取得します。もしSWAPFILE_CLUSTER個のブロック個のブロックが取得できなかった場合、空きブロックの先頭から走査する事でページスロットを取得します。
#define SWAPFILE_CLUSTER 256 #define LATENCY_LIMIT 256 static inline unsigned long scan_swap_map(struct swap_info_struct *si) { unsigned long offset, last_in_cluster; int latency_ration = LATENCY_LIMIT; si->flags += SWP_SCANNING; if (unlikely(!si->cluster_nr)) {si->cluster_nrは前に取得した取得SWAPFILE_CLUSTER個の内の残ページスロットです。無ければSWAPFILE_CLUSTERを取得します。
si->cluster_nr = SWAPFILE_CLUSTER - 1; if (si->pages - si->inuse_pages < SWAPFILE_CLUSTER) goto lowest; spin_unlock(&swap_lock);全ページスロット-使用済ページスロット(残空きページスロット)がSWAPFILE_CLUSTERより少ないと、SWAPFILE_CLUSTERは取得できません。空きページスロットの先頭から取得することになります。
offset = si->lowest_bit; last_in_cluster = offset + SWAPFILE_CLUSTER - 1;SWAPFILE_CLUSTERを取得走査する開始位置(offset)と終了位置(last_in_cluster)です。
for (; last_in_cluster <= si->highest_bit; offset++) { if (si->swap_map[offset]) last_in_cluster = offset + SWAPFILE_CLUSTER;このページスロットは使用済みです。この位置を開始位置として、終了位置を更新します。
else if (offset == last_in_cluster) { spin_lock(&swap_lock); si->cluster_next = offset-SWAPFILE_CLUSTER+1; goto cluster;無事終了位置まで未使用ページスロットが有りました。ページスロット取得位置si->cluster_nextを、検索した開始位置に更新します。
} if (unlikely(--latency_ration < 0)) { cond_resched(); latency_ration = LATENCY_LIMIT; }LATENCY_LIMIT回ループしたら、一旦スケジューラをコールします。
} spin_lock(&swap_lock); goto lowest;SWAPFILE_CLUSTERが取得できませんでした。空きページスロットの先頭から取得します。
} si->cluster_nr--;1ページスロット使用するためデクリメントします。
cluster: offset = si->cluster_next; if (offset > si->highest_bit) lowest: offset = si->lowest_bit;クラスタがまだ有しているなら、si->cluster_nextから、そうで無いならsi->lowest_bitから取得します。
checks: if (!(si->flags & SWP_WRITEOK)) goto no_page;上記処理でスケジューラを呼び出しているため、このスワップファイルがswapoffとなっているかもしれません。また同様に以降のチェックも上でチェックしても、swap_infoの各種情報が更新されているかもしれません。
if (!si->highest_bit) goto no_page;si->highest_bitが0と言う事は、それ以前に空きページスロットが無いと言う事です。
if (!si->swap_map[offset]) {空きページスロットが有りました。
if (offset == si->lowest_bit) si->lowest_bit++;取得位置が検索開始位置(si->lowest_bit)なら、si->lowest_bitをインクリメントします。
if (offset == si->highest_bit) si->highest_bit--;取得位置が検索終了始位置(si->highest_bit)なら、si->highest_bitをデクリメントします。
si->inuse_pages++;使用済みページスロット数をインクリメントします。
if (si->inuse_pages == si->pages) { si->lowest_bit = si->max; si->highest_bit = 0;使用済みページスロット数と全ページスロット数なら、もうページスロットは無い旨の設定を施します。
} si->swap_map[offset] = 1; si->cluster_next = offset + 1; si->flags -= SWP_SCANNING; return offset;取得したページスロットに取得した旨の情報si->swap_map[]=を設定し、si->cluster_nextを次回検索のため更新し、そのページスロット位置を返します。
}スケジューラにより再度スワップ処理が動作して、上段処理で取得できなかった場合(想像)、次のページスロット位置をチェックし、取得可能なら再度上段の処理を実行します。
spin_unlock(&swap_lock); while (++offset <= si->highest_bit) { if (!si->swap_map[offset]) { spin_lock(&swap_lock); goto checks; } if (unlikely(--latency_ration < 0)) { cond_resched(); latency_ration = LATENCY_LIMIT; } } spin_lock(&swap_lock); goto lowest; no_page: si->flags -= SWP_SCANNING; return 0; }
swp_entry()はスワップ領域識別子とそのページスロット位置をunsigned long valにパックしたものです。システムに関係なく、スワップ領域数は32(ないし30)です。ただしページスロットはアーキテクチャ依存です。
sizeof(e.val) * 8はこのアーキテクチャのunsigned long valのビット数になります。それをべき乗で表したスワップ領域数で引くことで、SWP_TYPE_SHIFTはunsigned long valの先頭ビットからMAX_SWAPFILES_SHIFTでアサインされる事になり、SWP_OFFSET_MASはそれから-1することで、ページスロットのオフセットのマスク値となるわけです。
#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) typedef struct { unsigned long val; } swp_entry_t; 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; }