-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
iomap: move the swapfile code into a separate file
Move the swapfile activation code into a separate file so that we can group related functions in a single file instead of having a single enormous source file. Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]>
- Loading branch information
Showing
3 changed files
with
183 additions
and
170 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* | ||
* Copyright (C) 2018 Oracle. All Rights Reserved. | ||
* Author: Darrick J. Wong <[email protected]> | ||
*/ | ||
#include <linux/module.h> | ||
#include <linux/compiler.h> | ||
#include <linux/fs.h> | ||
#include <linux/iomap.h> | ||
#include <linux/swap.h> | ||
|
||
#include "../internal.h" | ||
|
||
/* Swapfile activation */ | ||
|
||
struct iomap_swapfile_info { | ||
struct iomap iomap; /* accumulated iomap */ | ||
struct swap_info_struct *sis; | ||
uint64_t lowest_ppage; /* lowest physical addr seen (pages) */ | ||
uint64_t highest_ppage; /* highest physical addr seen (pages) */ | ||
unsigned long nr_pages; /* number of pages collected */ | ||
int nr_extents; /* extent count */ | ||
}; | ||
|
||
/* | ||
* Collect physical extents for this swap file. Physical extents reported to | ||
* the swap code must be trimmed to align to a page boundary. The logical | ||
* offset within the file is irrelevant since the swapfile code maps logical | ||
* page numbers of the swap device to the physical page-aligned extents. | ||
*/ | ||
static int iomap_swapfile_add_extent(struct iomap_swapfile_info *isi) | ||
{ | ||
struct iomap *iomap = &isi->iomap; | ||
unsigned long nr_pages; | ||
uint64_t first_ppage; | ||
uint64_t first_ppage_reported; | ||
uint64_t next_ppage; | ||
int error; | ||
|
||
/* | ||
* Round the start up and the end down so that the physical | ||
* extent aligns to a page boundary. | ||
*/ | ||
first_ppage = ALIGN(iomap->addr, PAGE_SIZE) >> PAGE_SHIFT; | ||
next_ppage = ALIGN_DOWN(iomap->addr + iomap->length, PAGE_SIZE) >> | ||
PAGE_SHIFT; | ||
|
||
/* Skip too-short physical extents. */ | ||
if (first_ppage >= next_ppage) | ||
return 0; | ||
nr_pages = next_ppage - first_ppage; | ||
|
||
/* | ||
* Calculate how much swap space we're adding; the first page contains | ||
* the swap header and doesn't count. The mm still wants that first | ||
* page fed to add_swap_extent, however. | ||
*/ | ||
first_ppage_reported = first_ppage; | ||
if (iomap->offset == 0) | ||
first_ppage_reported++; | ||
if (isi->lowest_ppage > first_ppage_reported) | ||
isi->lowest_ppage = first_ppage_reported; | ||
if (isi->highest_ppage < (next_ppage - 1)) | ||
isi->highest_ppage = next_ppage - 1; | ||
|
||
/* Add extent, set up for the next call. */ | ||
error = add_swap_extent(isi->sis, isi->nr_pages, nr_pages, first_ppage); | ||
if (error < 0) | ||
return error; | ||
isi->nr_extents += error; | ||
isi->nr_pages += nr_pages; | ||
return 0; | ||
} | ||
|
||
/* | ||
* Accumulate iomaps for this swap file. We have to accumulate iomaps because | ||
* swap only cares about contiguous page-aligned physical extents and makes no | ||
* distinction between written and unwritten extents. | ||
*/ | ||
static loff_t iomap_swapfile_activate_actor(struct inode *inode, loff_t pos, | ||
loff_t count, void *data, struct iomap *iomap) | ||
{ | ||
struct iomap_swapfile_info *isi = data; | ||
int error; | ||
|
||
switch (iomap->type) { | ||
case IOMAP_MAPPED: | ||
case IOMAP_UNWRITTEN: | ||
/* Only real or unwritten extents. */ | ||
break; | ||
case IOMAP_INLINE: | ||
/* No inline data. */ | ||
pr_err("swapon: file is inline\n"); | ||
return -EINVAL; | ||
default: | ||
pr_err("swapon: file has unallocated extents\n"); | ||
return -EINVAL; | ||
} | ||
|
||
/* No uncommitted metadata or shared blocks. */ | ||
if (iomap->flags & IOMAP_F_DIRTY) { | ||
pr_err("swapon: file is not committed\n"); | ||
return -EINVAL; | ||
} | ||
if (iomap->flags & IOMAP_F_SHARED) { | ||
pr_err("swapon: file has shared extents\n"); | ||
return -EINVAL; | ||
} | ||
|
||
/* Only one bdev per swap file. */ | ||
if (iomap->bdev != isi->sis->bdev) { | ||
pr_err("swapon: file is on multiple devices\n"); | ||
return -EINVAL; | ||
} | ||
|
||
if (isi->iomap.length == 0) { | ||
/* No accumulated extent, so just store it. */ | ||
memcpy(&isi->iomap, iomap, sizeof(isi->iomap)); | ||
} else if (isi->iomap.addr + isi->iomap.length == iomap->addr) { | ||
/* Append this to the accumulated extent. */ | ||
isi->iomap.length += iomap->length; | ||
} else { | ||
/* Otherwise, add the retained iomap and store this one. */ | ||
error = iomap_swapfile_add_extent(isi); | ||
if (error) | ||
return error; | ||
memcpy(&isi->iomap, iomap, sizeof(isi->iomap)); | ||
} | ||
return count; | ||
} | ||
|
||
/* | ||
* Iterate a swap file's iomaps to construct physical extents that can be | ||
* passed to the swapfile subsystem. | ||
*/ | ||
int iomap_swapfile_activate(struct swap_info_struct *sis, | ||
struct file *swap_file, sector_t *pagespan, | ||
const struct iomap_ops *ops) | ||
{ | ||
struct iomap_swapfile_info isi = { | ||
.sis = sis, | ||
.lowest_ppage = (sector_t)-1ULL, | ||
}; | ||
struct address_space *mapping = swap_file->f_mapping; | ||
struct inode *inode = mapping->host; | ||
loff_t pos = 0; | ||
loff_t len = ALIGN_DOWN(i_size_read(inode), PAGE_SIZE); | ||
loff_t ret; | ||
|
||
/* | ||
* Persist all file mapping metadata so that we won't have any | ||
* IOMAP_F_DIRTY iomaps. | ||
*/ | ||
ret = vfs_fsync(swap_file, 1); | ||
if (ret) | ||
return ret; | ||
|
||
while (len > 0) { | ||
ret = iomap_apply(inode, pos, len, IOMAP_REPORT, | ||
ops, &isi, iomap_swapfile_activate_actor); | ||
if (ret <= 0) | ||
return ret; | ||
|
||
pos += ret; | ||
len -= ret; | ||
} | ||
|
||
if (isi.iomap.length) { | ||
ret = iomap_swapfile_add_extent(&isi); | ||
if (ret) | ||
return ret; | ||
} | ||
|
||
*pagespan = 1 + isi.highest_ppage - isi.lowest_ppage; | ||
sis->max = isi.nr_pages; | ||
sis->pages = isi.nr_pages - 1; | ||
sis->highest_bit = isi.nr_pages - 1; | ||
return isi.nr_extents; | ||
} | ||
EXPORT_SYMBOL_GPL(iomap_swapfile_activate); |