swapon


swapデバイスは32ケまで動的に追加する事ができます。(ただしCONFIG_MIGRATIONの時は-2、CONFIG_MEMORY_FAILUREの時は-1となります。)
#ifdef CONFIG_MIGRATION
#define SWP_MIGRATION_NUM 2
#else
 #define SWP_MIGRATION_NUM 0
#endif

#ifdef CONFIG_MEMORY_FAILURE
#define SWP_HWPOISON_NUM 1
#else
#define SWP_HWPOISON_NUM 0
#endif

#define MAX_SWAPFILES_SHIFT     5
#define MAX_SWAPFILES \
       ((1 << MAX_SWAPFILES_SHIFT) - SWP_MIGRATION_NUM - SWP_HWPOISON_NUM)
スワップ領域は、先頭ブロックにunion swap_headerを有するデバイスで、mkswapコマンドで作成されます。
union swap_header {
       struct {
               char reserved[PAGE_SIZE - 10];
               char magic[10];                 /* SWAP-SPACE or SWAPSPACE2 */
       } magic;
       struct {
               char            bootbits[1024]; /* Space for disklabel etc. */
               __u32           version;
               __u32           last_page;
               __u32           nr_badpages;
               unsigned char   sws_uuid[16];
               unsigned char   sws_volume[16];
               __u32           padding[117];
               __u32           badpages[1];
       } info;
};
swap領域は、swapデバイスのswap_headerから、swap_info_structを設定し、それをswap_info[]の空インデックスに設定し、これをプライオリティ順にswap_listをヘッドとするリストに登録することです。なお、プライオリティオプションなしの場合、直前に追加されたプライオリティの1つ低いプライオリティが設定されます。
static struct swap_list_t swap_list = {-1, -1};
static struct swap_info_struct *swap_info[MAX_SWAPFILES];

struct swap_info_struct {
       unsigned long   flags;          /* SWP_USED etc: see above */
       signed short    prio;           /* swap priority of this type */
       signed char     type;           /* strange name for an index */
       signed char     next;           /* next type on the swap list */
       unsigned int    max;            /* extent of the swap_map */
       unsigned char *swap_map;        /* vmalloc'ed array of usage counts */
       unsigned int lowest_bit;        /* index of first free in swap_map */
       unsigned int highest_bit;       /* index of last free in swap_map */
       unsigned int pages;             /* total of usable pages of swap */
       unsigned int inuse_pages;       /* number of those currently in use */
       unsigned int cluster_next;      /* likely index for next allocation */
       unsigned int cluster_nr;        /* countdown to next cluster search */
       unsigned int lowest_alloc;      /* while preparing discard cluster */
       unsigned int highest_alloc;     /* while preparing discard cluster */
       struct swap_extent *curr_swap_extent;
       struct swap_extent first_swap_extent;
       struct block_device *bdev;      /* swap device or bdev of swap file */
       struct file *swap_file;         /* seldom referenced */
       unsigned int old_block_size;    /* seldom referenced */
};
alloc_swap_info()でstruct swap_info_structを取得し、同時に空いているswap_info[]に設定、nr_swapfiles++し、swap_info[]の設定したインデックスをswap_info_struc->typeに設定されます。

forループは登録しようとするswapデバイスは、すでに登録されているかチェックします。

read_mapping_page()でswapデバイスの先頭ブロックを読み込み、read_swap_header()で swap_info_structを設定します。

unsigned char *swap_map = vzalloc(maxpages);はswapデバイスのmaxpagesの配列を確保し、その1つ1つがスロットの1つ1つの参照カウンターとなります。この配列はswap_info_struct->swap_mapに設定されます。設定はsetup_swap_map_and_extents()で行われます。なおswap_headerのbadpages[]に相当する処はSWAP_MAP_BADが設定され(swap_map[0]は、スワップヘッダ故SWAP_MAP_BADとなります。)利用できないようにします。また同時に、物理的に連続するブロック単位で、swap_info_structのfirst_swap_extentをヘッドとするリストに、その情報のstruct swap_extentをリストします。スワップ取得時、シークロスをなくすため、まずこのリストからスロットを取得することになります。

最後に、enable_swap_info()でswap_info_structをswap_listにリストすることで、このデバイスがスワップとして有効になるわけです。
SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
{
       struct swap_info_struct *p;
       char *name;
       struct file *swap_file = NULL;
       struct address_space *mapping;
       int i;
       int prio;
       int error;
       union swap_header *swap_header;
       int nr_extents;
       sector_t span;
       unsigned long maxpages;
       unsigned char *swap_map = NULL;
       struct page *page = NULL;
       struct inode *inode = NULL;

       if (!capable(CAP_SYS_ADMIN))
               return -EPERM;

       p = alloc_swap_info();
       if (IS_ERR(p))
               return PTR_ERR(p);

       name = getname(specialfile);
       if (IS_ERR(name)) {
               error = PTR_ERR(name);
               name = NULL;
               goto bad_swap;
       }
       swap_file = filp_open(name, O_RDWR|O_LARGEFILE, 0);
       if (IS_ERR(swap_file)) {
               error = PTR_ERR(swap_file);
               swap_file = NULL;
               goto bad_swap;
       }

       p->swap_file = swap_file;
       mapping = swap_file->f_mapping;

       for (i = 0; i < nr_swapfiles; i++) {
               struct swap_info_struct *q = swap_info[i];

               if (q == p || !q->swap_file)
                       continue;
               if (mapping == q->swap_file->f_mapping) {
                       error = -EBUSY;
                       goto bad_swap;
               }
       }

       inode = mapping->host;
       error = claim_swapfile(p, inode);
       if (unlikely(error))
               goto bad_swap;


       if (!mapping->a_ops->readpage) {
               error = -EINVAL;
               goto bad_swap;
       }
       page = read_mapping_page(mapping, 0, swap_file);
       if (IS_ERR(page)) {
               error = PTR_ERR(page);
               goto bad_swap;
       }
       swap_header = kmap(page);

       maxpages = read_swap_header(p, swap_header, inode);
       if (unlikely(!maxpages)) {
               error = -EINVAL;
               goto bad_swap;
       }

       swap_map = vzalloc(maxpages);
       if (!swap_map) {
               error = -ENOMEM;
               goto bad_swap;
       }

       error = swap_cgroup_swapon(p->type, maxpages);
       if (error)
               goto bad_swap;

       nr_extents = setup_swap_map_and_extents(p, swap_header, swap_map,
               maxpages, &span);
       if (unlikely(nr_extents < 0)) {
               error = nr_extents;
               goto bad_swap;
       }

       if (p->bdev) {
               if (blk_queue_nonrot(bdev_get_queue(p->bdev))) {
                       p->flags |= SWP_SOLIDSTATE;
                       p->cluster_next = 1 + (random32() % p->highest_bit);
               }
               if ((swap_flags & SWAP_FLAG_DISCARD) && discard_swap(p) == 0)
                       p->flags |= SWP_DISCARDABLE;
       }

       mutex_lock(&swapon_mutex);
       prio = -1;
       if (swap_flags & SWAP_FLAG_PREFER)
               prio =
                 (swap_flags & SWAP_FLAG_PRIO_MASK) >> SWAP_FLAG_PRIO_SHIFT;
       enable_swap_info(p, prio, swap_map);

       printk(KERN_INFO "Adding %uk swap on %s.  "
                       "Priority:%d extents:%d across:%lluk %s%s\n",
               p->pages<<(PAGE_SHIFT-10), name, p->prio,
               nr_extents, (unsigned long long)span<<(PAGE_SHIFT-10),
               (p->flags & SWP_SOLIDSTATE) ? "SS" : "",
               (p->flags & SWP_DISCARDABLE) ? "D" : "");

       mutex_unlock(&swapon_mutex);
       atomic_inc(&proc_poll_event);
       wake_up_interruptible(&proc_poll_wait);

       if (S_ISREG(inode->i_mode))
               inode->i_flags |= S_SWAPFILE;
       error = 0;
       goto out;
bad_swap:
       if (inode && S_ISBLK(inode->i_mode) && p->bdev) {
               set_blocksize(p->bdev, p->old_block_size);
               blkdev_put(p->bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL);
       }
       destroy_swap_extents(p);
       swap_cgroup_swapoff(p->type);
       spin_lock(&swap_lock);
       p->swap_file = NULL;
       p->flags = 0;
       spin_unlock(&swap_lock);
       vfree(swap_map);
       if (swap_file) {
               if (inode && S_ISREG(inode->i_mode)) {
                       mutex_unlock(&inode->i_mutex);
                       inode = NULL;
               }
               filp_close(swap_file, NULL);
       }
out:
       if (page && !IS_ERR(page)) {
               kunmap(page);
               page_cache_release(page);
       }
       if (name)
               putname(name);
       if (inode && S_ISREG(inode->i_mode))
               mutex_unlock(&inode->i_mutex);
       return error;
}
swap_info[]の空の最後のインデックスに、swap_info_structを設定します。もし、先に割り当てたスワップで使われなくなったのがあると、それと差し替えることで実現しています。
static struct swap_info_struct *alloc_swap_info(void)
{
       struct swap_info_struct *p;
       unsigned int type;

       p = kzalloc(sizeof(*p), GFP_KERNEL);
       if (!p)
               return ERR_PTR(-ENOMEM);

       spin_lock(&swap_lock);
       for (type = 0; type < nr_swapfiles; type++) {
               if (!(swap_info[type]->flags & SWP_USED))
                       break;
       }
       if (type >= MAX_SWAPFILES) {
               spin_unlock(&swap_lock);
               kfree(p);
               return ERR_PTR(-EPERM);
       }
       if (type >= nr_swapfiles) {
               p->type = type;
               swap_info[type] = p;
               smp_wmb();
               nr_swapfiles++;
       } else {
               kfree(p);
               p = swap_info[type];
       }
       INIT_LIST_HEAD(&p->first_swap_extent.list);
       p->flags = SWP_USED;
       p->next = -1;
       spin_unlock(&swap_lock);

       return p;
}
if (prio >= 0)なら引数のprioが設定され、マイナスなら--least_priority(前回のprio)が設定されます。swaponでprioが未設定の場合、prio=-1で、従って通常は、追加毎に、順次prioは小さくなっていきます。nr_swap_pagesはまだ使われていないスワップページ数、total_swap_pagesはトータルのスワップページ数で、新規追加分を加算します。

以下p->prioに応じたswap_list.head以下のリストに追加しているだけですが・・・。簡単なロジックにもかかわらず、でうんんって感じです。ポイントは、swap_list.head/swap_list.next/p->typeはswap_map[]のインデックスで、すなわちスロットそのものでと言うことです。そして最後となるp->nextは-1という事です。

for (i = swap_list.head; i >= 0; i = swap_info[i]->next)のループで、swap_list.headから順にswap_info[i]->nextを走査し、if (prev < 0)なら、swap_list.headよりプライオリティが高いということで、swap_listに設定します。そうでないなら、直前のインデックスとなるprevのnextに設定することになります。言葉で説明するより実装を見てもらったほうがいいかと思います。プライオリティ順にリストしているに過ぎませんから。なお、同じプライオリティの場合、新規に追加した物が優先されることになります。
static void enable_swap_info(struct swap_info_struct *p, int prio,
                               unsigned char *swap_map)
{
       int i, prev;

       spin_lock(&swap_lock);
       if (prio >= 0)
               p->prio = prio;
       else
               p->prio = --least_priority;
       p->swap_map = swap_map;
       p->flags |= SWP_WRITEOK;
       nr_swap_pages += p->pages;
       total_swap_pages += p->pages;

       prev = -1;
       for (i = swap_list.head; i >= 0; i = swap_info[i]->next) {
               if (p->prio >= swap_info[i]->prio)
                       break;
               prev = i;
       }
       p->next = i;
       if (prev < 0)
               swap_list.head = swap_list.next = p->type;
       else
               swap_info[prev]->next = p->type;
       spin_unlock(&swap_lock);
}

補足

スワップ取得は、swap_listのデバイスから順に空きスロットを取得しますが、プライオリティが同じ場合、取得したデバイスは同プライオリティの最後にリストし直されます。

最終更新 2014/02/06 01:17:23 - north
(2014/02/06 01:17:23 作成)


検索

アクセス数
3589794
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。