ページスロットの取得
ページスロットの取得は、まずget_swap_page()でページ領域を、そしてそこからscan_swap_map()でページスロットを取得します。ページ領域の取得は、優先順位に従って決定され、もし同じ優先順位ならラウンドロビンで取得されます。そして、そのスワップ領域のswap_map[]からページスロットを取得する事になります。
ページスロット取得において、まずSWAPFILE_CLUSTERという256個の物理的に連続するページスロットを確保し、そこから順に取得し、その領域を使い切ると再度SWAPFILE_CLUSTERを取得しなおすという具合です。もし、SWAPFILE_CLUSTERで取得した領域が無くなって、新規のSWAPFILE_CLUSTERが取得できない時、空きページスロットの先頭から走査する事で取得しています。
から
SWP_ACTIVE = (SWP_USED | SWP_WRITEOK),
スワップ領域が決定すると、このスワップファイルのswap_map[]を走査して空きページスロットを取得します。ページスロットは物理的に連続するブロックが理想です。そのため検索において、まずSWAPFILE_CLUSTER個のブロックの取得を試み、そこから順次ページスロットを取得します。もしSWAPFILE_CLUSTER個のブロック個のブロックが取得できなかった場合、空きブロックの先頭から走査する事でページスロットを取得します。
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することで、ページスロットのオフセットのマスク値となるわけです。
ページスロット取得において、まず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;
}






