swapon
swapデバイスは32ケまで動的に追加する事ができます。(ただしCONFIG_MIGRATIONの時は-2、CONFIG_MEMORY_FAILUREの時は-1となります。)
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にリストすることで、このデバイスがスワップとして有効になるわけです。
以下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に設定することになります。言葉で説明するより実装を見てもらったほうがいいかと思います。プライオリティ順にリストしているに過ぎませんから。なお、同じプライオリティの場合、新規に追加した物が優先されることになります。
#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); }