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);
}






