無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

ページスロットの取得


ページスロットの取得は、まずget_swap_page()でページ領域を、そしてそこからscan_swap_map()でページスロットを取得します。ページ領域の取得は、優先順位に従って決定され、もし同じ優先順位ならラウンドロビンで取得されます。そして、そのスワップ領域の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;
}


最終更新 2011/10/02 17:26:42 - north
(2011/10/02 17:23:50 作成)