diff options
author | Darrick J. Wong <darrick.wong@oracle.com> | 2019-07-15 11:50:57 -0400 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2019-07-17 10:14:10 -0400 |
commit | a45c0eccc564649541305c71539350c7a890226e (patch) | |
tree | 058be82a29cf3217273f622cbc33dbe51269621f | |
parent | 1c230208f53de479352cdb7fa2f851005df7cdc9 (diff) |
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 <darrick.wong@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
-rw-r--r-- | fs/iomap.c | 170 | ||||
-rw-r--r-- | fs/iomap/Makefile | 3 | ||||
-rw-r--r-- | fs/iomap/swapfile.c | 180 |
3 files changed, 183 insertions, 170 deletions
diff --git a/fs/iomap.c b/fs/iomap.c index 217c3e5a13d6..521e90825dbe 100644 --- a/fs/iomap.c +++ b/fs/iomap.c | |||
@@ -2000,176 +2000,6 @@ out_free_dio: | |||
2000 | } | 2000 | } |
2001 | EXPORT_SYMBOL_GPL(iomap_dio_rw); | 2001 | EXPORT_SYMBOL_GPL(iomap_dio_rw); |
2002 | 2002 | ||
2003 | /* Swapfile activation */ | ||
2004 | |||
2005 | #ifdef CONFIG_SWAP | ||
2006 | struct iomap_swapfile_info { | ||
2007 | struct iomap iomap; /* accumulated iomap */ | ||
2008 | struct swap_info_struct *sis; | ||
2009 | uint64_t lowest_ppage; /* lowest physical addr seen (pages) */ | ||
2010 | uint64_t highest_ppage; /* highest physical addr seen (pages) */ | ||
2011 | unsigned long nr_pages; /* number of pages collected */ | ||
2012 | int nr_extents; /* extent count */ | ||
2013 | }; | ||
2014 | |||
2015 | /* | ||
2016 | * Collect physical extents for this swap file. Physical extents reported to | ||
2017 | * the swap code must be trimmed to align to a page boundary. The logical | ||
2018 | * offset within the file is irrelevant since the swapfile code maps logical | ||
2019 | * page numbers of the swap device to the physical page-aligned extents. | ||
2020 | */ | ||
2021 | static int iomap_swapfile_add_extent(struct iomap_swapfile_info *isi) | ||
2022 | { | ||
2023 | struct iomap *iomap = &isi->iomap; | ||
2024 | unsigned long nr_pages; | ||
2025 | uint64_t first_ppage; | ||
2026 | uint64_t first_ppage_reported; | ||
2027 | uint64_t next_ppage; | ||
2028 | int error; | ||
2029 | |||
2030 | /* | ||
2031 | * Round the start up and the end down so that the physical | ||
2032 | * extent aligns to a page boundary. | ||
2033 | */ | ||
2034 | first_ppage = ALIGN(iomap->addr, PAGE_SIZE) >> PAGE_SHIFT; | ||
2035 | next_ppage = ALIGN_DOWN(iomap->addr + iomap->length, PAGE_SIZE) >> | ||
2036 | PAGE_SHIFT; | ||
2037 | |||
2038 | /* Skip too-short physical extents. */ | ||
2039 | if (first_ppage >= next_ppage) | ||
2040 | return 0; | ||
2041 | nr_pages = next_ppage - first_ppage; | ||
2042 | |||
2043 | /* | ||
2044 | * Calculate how much swap space we're adding; the first page contains | ||
2045 | * the swap header and doesn't count. The mm still wants that first | ||
2046 | * page fed to add_swap_extent, however. | ||
2047 | */ | ||
2048 | first_ppage_reported = first_ppage; | ||
2049 | if (iomap->offset == 0) | ||
2050 | first_ppage_reported++; | ||
2051 | if (isi->lowest_ppage > first_ppage_reported) | ||
2052 | isi->lowest_ppage = first_ppage_reported; | ||
2053 | if (isi->highest_ppage < (next_ppage - 1)) | ||
2054 | isi->highest_ppage = next_ppage - 1; | ||
2055 | |||
2056 | /* Add extent, set up for the next call. */ | ||
2057 | error = add_swap_extent(isi->sis, isi->nr_pages, nr_pages, first_ppage); | ||
2058 | if (error < 0) | ||
2059 | return error; | ||
2060 | isi->nr_extents += error; | ||
2061 | isi->nr_pages += nr_pages; | ||
2062 | return 0; | ||
2063 | } | ||
2064 | |||
2065 | /* | ||
2066 | * Accumulate iomaps for this swap file. We have to accumulate iomaps because | ||
2067 | * swap only cares about contiguous page-aligned physical extents and makes no | ||
2068 | * distinction between written and unwritten extents. | ||
2069 | */ | ||
2070 | static loff_t iomap_swapfile_activate_actor(struct inode *inode, loff_t pos, | ||
2071 | loff_t count, void *data, struct iomap *iomap) | ||
2072 | { | ||
2073 | struct iomap_swapfile_info *isi = data; | ||
2074 | int error; | ||
2075 | |||
2076 | switch (iomap->type) { | ||
2077 | case IOMAP_MAPPED: | ||
2078 | case IOMAP_UNWRITTEN: | ||
2079 | /* Only real or unwritten extents. */ | ||
2080 | break; | ||
2081 | case IOMAP_INLINE: | ||
2082 | /* No inline data. */ | ||
2083 | pr_err("swapon: file is inline\n"); | ||
2084 | return -EINVAL; | ||
2085 | default: | ||
2086 | pr_err("swapon: file has unallocated extents\n"); | ||
2087 | return -EINVAL; | ||
2088 | } | ||
2089 | |||
2090 | /* No uncommitted metadata or shared blocks. */ | ||
2091 | if (iomap->flags & IOMAP_F_DIRTY) { | ||
2092 | pr_err("swapon: file is not committed\n"); | ||
2093 | return -EINVAL; | ||
2094 | } | ||
2095 | if (iomap->flags & IOMAP_F_SHARED) { | ||
2096 | pr_err("swapon: file has shared extents\n"); | ||
2097 | return -EINVAL; | ||
2098 | } | ||
2099 | |||
2100 | /* Only one bdev per swap file. */ | ||
2101 | if (iomap->bdev != isi->sis->bdev) { | ||
2102 | pr_err("swapon: file is on multiple devices\n"); | ||
2103 | return -EINVAL; | ||
2104 | } | ||
2105 | |||
2106 | if (isi->iomap.length == 0) { | ||
2107 | /* No accumulated extent, so just store it. */ | ||
2108 | memcpy(&isi->iomap, iomap, sizeof(isi->iomap)); | ||
2109 | } else if (isi->iomap.addr + isi->iomap.length == iomap->addr) { | ||
2110 | /* Append this to the accumulated extent. */ | ||
2111 | isi->iomap.length += iomap->length; | ||
2112 | } else { | ||
2113 | /* Otherwise, add the retained iomap and store this one. */ | ||
2114 | error = iomap_swapfile_add_extent(isi); | ||
2115 | if (error) | ||
2116 | return error; | ||
2117 | memcpy(&isi->iomap, iomap, sizeof(isi->iomap)); | ||
2118 | } | ||
2119 | return count; | ||
2120 | } | ||
2121 | |||
2122 | /* | ||
2123 | * Iterate a swap file's iomaps to construct physical extents that can be | ||
2124 | * passed to the swapfile subsystem. | ||
2125 | */ | ||
2126 | int iomap_swapfile_activate(struct swap_info_struct *sis, | ||
2127 | struct file *swap_file, sector_t *pagespan, | ||
2128 | const struct iomap_ops *ops) | ||
2129 | { | ||
2130 | struct iomap_swapfile_info isi = { | ||
2131 | .sis = sis, | ||
2132 | .lowest_ppage = (sector_t)-1ULL, | ||
2133 | }; | ||
2134 | struct address_space *mapping = swap_file->f_mapping; | ||
2135 | struct inode *inode = mapping->host; | ||
2136 | loff_t pos = 0; | ||
2137 | loff_t len = ALIGN_DOWN(i_size_read(inode), PAGE_SIZE); | ||
2138 | loff_t ret; | ||
2139 | |||
2140 | /* | ||
2141 | * Persist all file mapping metadata so that we won't have any | ||
2142 | * IOMAP_F_DIRTY iomaps. | ||
2143 | */ | ||
2144 | ret = vfs_fsync(swap_file, 1); | ||
2145 | if (ret) | ||
2146 | return ret; | ||
2147 | |||
2148 | while (len > 0) { | ||
2149 | ret = iomap_apply(inode, pos, len, IOMAP_REPORT, | ||
2150 | ops, &isi, iomap_swapfile_activate_actor); | ||
2151 | if (ret <= 0) | ||
2152 | return ret; | ||
2153 | |||
2154 | pos += ret; | ||
2155 | len -= ret; | ||
2156 | } | ||
2157 | |||
2158 | if (isi.iomap.length) { | ||
2159 | ret = iomap_swapfile_add_extent(&isi); | ||
2160 | if (ret) | ||
2161 | return ret; | ||
2162 | } | ||
2163 | |||
2164 | *pagespan = 1 + isi.highest_ppage - isi.lowest_ppage; | ||
2165 | sis->max = isi.nr_pages; | ||
2166 | sis->pages = isi.nr_pages - 1; | ||
2167 | sis->highest_bit = isi.nr_pages - 1; | ||
2168 | return isi.nr_extents; | ||
2169 | } | ||
2170 | EXPORT_SYMBOL_GPL(iomap_swapfile_activate); | ||
2171 | #endif /* CONFIG_SWAP */ | ||
2172 | |||
2173 | static loff_t | 2003 | static loff_t |
2174 | iomap_bmap_actor(struct inode *inode, loff_t pos, loff_t length, | 2004 | iomap_bmap_actor(struct inode *inode, loff_t pos, loff_t length, |
2175 | void *data, struct iomap *iomap) | 2005 | void *data, struct iomap *iomap) |
diff --git a/fs/iomap/Makefile b/fs/iomap/Makefile index de5a1f914b2c..f88eca22ae80 100644 --- a/fs/iomap/Makefile +++ b/fs/iomap/Makefile | |||
@@ -3,3 +3,6 @@ | |||
3 | # Copyright (c) 2019 Oracle. | 3 | # Copyright (c) 2019 Oracle. |
4 | # All Rights Reserved. | 4 | # All Rights Reserved. |
5 | # | 5 | # |
6 | obj-$(CONFIG_FS_IOMAP) += iomap.o | ||
7 | |||
8 | iomap-$(CONFIG_SWAP) += swapfile.o | ||
diff --git a/fs/iomap/swapfile.c b/fs/iomap/swapfile.c new file mode 100644 index 000000000000..b79c33631263 --- /dev/null +++ b/fs/iomap/swapfile.c | |||
@@ -0,0 +1,180 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (C) 2018 Oracle. All Rights Reserved. | ||
4 | * Author: Darrick J. Wong <darrick.wong@oracle.com> | ||
5 | */ | ||
6 | #include <linux/module.h> | ||
7 | #include <linux/compiler.h> | ||
8 | #include <linux/fs.h> | ||
9 | #include <linux/iomap.h> | ||
10 | #include <linux/swap.h> | ||
11 | |||
12 | #include "../internal.h" | ||
13 | |||
14 | /* Swapfile activation */ | ||
15 | |||
16 | struct iomap_swapfile_info { | ||
17 | struct iomap iomap; /* accumulated iomap */ | ||
18 | struct swap_info_struct *sis; | ||
19 | uint64_t lowest_ppage; /* lowest physical addr seen (pages) */ | ||
20 | uint64_t highest_ppage; /* highest physical addr seen (pages) */ | ||
21 | unsigned long nr_pages; /* number of pages collected */ | ||
22 | int nr_extents; /* extent count */ | ||
23 | }; | ||
24 | |||
25 | /* | ||
26 | * Collect physical extents for this swap file. Physical extents reported to | ||
27 | * the swap code must be trimmed to align to a page boundary. The logical | ||
28 | * offset within the file is irrelevant since the swapfile code maps logical | ||
29 | * page numbers of the swap device to the physical page-aligned extents. | ||
30 | */ | ||
31 | static int iomap_swapfile_add_extent(struct iomap_swapfile_info *isi) | ||
32 | { | ||
33 | struct iomap *iomap = &isi->iomap; | ||
34 | unsigned long nr_pages; | ||
35 | uint64_t first_ppage; | ||
36 | uint64_t first_ppage_reported; | ||
37 | uint64_t next_ppage; | ||
38 | int error; | ||
39 | |||
40 | /* | ||
41 | * Round the start up and the end down so that the physical | ||
42 | * extent aligns to a page boundary. | ||
43 | */ | ||
44 | first_ppage = ALIGN(iomap->addr, PAGE_SIZE) >> PAGE_SHIFT; | ||
45 | next_ppage = ALIGN_DOWN(iomap->addr + iomap->length, PAGE_SIZE) >> | ||
46 | PAGE_SHIFT; | ||
47 | |||
48 | /* Skip too-short physical extents. */ | ||
49 | if (first_ppage >= next_ppage) | ||
50 | return 0; | ||
51 | nr_pages = next_ppage - first_ppage; | ||
52 | |||
53 | /* | ||
54 | * Calculate how much swap space we're adding; the first page contains | ||
55 | * the swap header and doesn't count. The mm still wants that first | ||
56 | * page fed to add_swap_extent, however. | ||
57 | */ | ||
58 | first_ppage_reported = first_ppage; | ||
59 | if (iomap->offset == 0) | ||
60 | first_ppage_reported++; | ||
61 | if (isi->lowest_ppage > first_ppage_reported) | ||
62 | isi->lowest_ppage = first_ppage_reported; | ||
63 | if (isi->highest_ppage < (next_ppage - 1)) | ||
64 | isi->highest_ppage = next_ppage - 1; | ||
65 | |||
66 | /* Add extent, set up for the next call. */ | ||
67 | error = add_swap_extent(isi->sis, isi->nr_pages, nr_pages, first_ppage); | ||
68 | if (error < 0) | ||
69 | return error; | ||
70 | isi->nr_extents += error; | ||
71 | isi->nr_pages += nr_pages; | ||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | /* | ||
76 | * Accumulate iomaps for this swap file. We have to accumulate iomaps because | ||
77 | * swap only cares about contiguous page-aligned physical extents and makes no | ||
78 | * distinction between written and unwritten extents. | ||
79 | */ | ||
80 | static loff_t iomap_swapfile_activate_actor(struct inode *inode, loff_t pos, | ||
81 | loff_t count, void *data, struct iomap *iomap) | ||
82 | { | ||
83 | struct iomap_swapfile_info *isi = data; | ||
84 | int error; | ||
85 | |||
86 | switch (iomap->type) { | ||
87 | case IOMAP_MAPPED: | ||
88 | case IOMAP_UNWRITTEN: | ||
89 | /* Only real or unwritten extents. */ | ||
90 | break; | ||
91 | case IOMAP_INLINE: | ||
92 | /* No inline data. */ | ||
93 | pr_err("swapon: file is inline\n"); | ||
94 | return -EINVAL; | ||
95 | default: | ||
96 | pr_err("swapon: file has unallocated extents\n"); | ||
97 | return -EINVAL; | ||
98 | } | ||
99 | |||
100 | /* No uncommitted metadata or shared blocks. */ | ||
101 | if (iomap->flags & IOMAP_F_DIRTY) { | ||
102 | pr_err("swapon: file is not committed\n"); | ||
103 | return -EINVAL; | ||
104 | } | ||
105 | if (iomap->flags & IOMAP_F_SHARED) { | ||
106 | pr_err("swapon: file has shared extents\n"); | ||
107 | return -EINVAL; | ||
108 | } | ||
109 | |||
110 | /* Only one bdev per swap file. */ | ||
111 | if (iomap->bdev != isi->sis->bdev) { | ||
112 | pr_err("swapon: file is on multiple devices\n"); | ||
113 | return -EINVAL; | ||
114 | } | ||
115 | |||
116 | if (isi->iomap.length == 0) { | ||
117 | /* No accumulated extent, so just store it. */ | ||
118 | memcpy(&isi->iomap, iomap, sizeof(isi->iomap)); | ||
119 | } else if (isi->iomap.addr + isi->iomap.length == iomap->addr) { | ||
120 | /* Append this to the accumulated extent. */ | ||
121 | isi->iomap.length += iomap->length; | ||
122 | } else { | ||
123 | /* Otherwise, add the retained iomap and store this one. */ | ||
124 | error = iomap_swapfile_add_extent(isi); | ||
125 | if (error) | ||
126 | return error; | ||
127 | memcpy(&isi->iomap, iomap, sizeof(isi->iomap)); | ||
128 | } | ||
129 | return count; | ||
130 | } | ||
131 | |||
132 | /* | ||
133 | * Iterate a swap file's iomaps to construct physical extents that can be | ||
134 | * passed to the swapfile subsystem. | ||
135 | */ | ||
136 | int iomap_swapfile_activate(struct swap_info_struct *sis, | ||
137 | struct file *swap_file, sector_t *pagespan, | ||
138 | const struct iomap_ops *ops) | ||
139 | { | ||
140 | struct iomap_swapfile_info isi = { | ||
141 | .sis = sis, | ||
142 | .lowest_ppage = (sector_t)-1ULL, | ||
143 | }; | ||
144 | struct address_space *mapping = swap_file->f_mapping; | ||
145 | struct inode *inode = mapping->host; | ||
146 | loff_t pos = 0; | ||
147 | loff_t len = ALIGN_DOWN(i_size_read(inode), PAGE_SIZE); | ||
148 | loff_t ret; | ||
149 | |||
150 | /* | ||
151 | * Persist all file mapping metadata so that we won't have any | ||
152 | * IOMAP_F_DIRTY iomaps. | ||
153 | */ | ||
154 | ret = vfs_fsync(swap_file, 1); | ||
155 | if (ret) | ||
156 | return ret; | ||
157 | |||
158 | while (len > 0) { | ||
159 | ret = iomap_apply(inode, pos, len, IOMAP_REPORT, | ||
160 | ops, &isi, iomap_swapfile_activate_actor); | ||
161 | if (ret <= 0) | ||
162 | return ret; | ||
163 | |||
164 | pos += ret; | ||
165 | len -= ret; | ||
166 | } | ||
167 | |||
168 | if (isi.iomap.length) { | ||
169 | ret = iomap_swapfile_add_extent(&isi); | ||
170 | if (ret) | ||
171 | return ret; | ||
172 | } | ||
173 | |||
174 | *pagespan = 1 + isi.highest_ppage - isi.lowest_ppage; | ||
175 | sis->max = isi.nr_pages; | ||
176 | sis->pages = isi.nr_pages - 1; | ||
177 | sis->highest_bit = isi.nr_pages - 1; | ||
178 | return isi.nr_extents; | ||
179 | } | ||
180 | EXPORT_SYMBOL_GPL(iomap_swapfile_activate); | ||