|
27 | 27 | #include <linux/task_io_accounting_ops.h>
|
28 | 28 | #include <linux/dax.h>
|
29 | 29 | #include <linux/sched/signal.h>
|
| 30 | +#include <linux/swap.h> |
30 | 31 |
|
31 | 32 | #include "internal.h"
|
32 | 33 |
|
@@ -1139,3 +1140,164 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
|
1139 | 1140 | return ret;
|
1140 | 1141 | }
|
1141 | 1142 | EXPORT_SYMBOL_GPL(iomap_dio_rw);
|
| 1143 | + |
| 1144 | +/* Swapfile activation */ |
| 1145 | + |
| 1146 | +#ifdef CONFIG_SWAP |
| 1147 | +struct iomap_swapfile_info { |
| 1148 | + struct iomap iomap; /* accumulated iomap */ |
| 1149 | + struct swap_info_struct *sis; |
| 1150 | + uint64_t lowest_ppage; /* lowest physical addr seen (pages) */ |
| 1151 | + uint64_t highest_ppage; /* highest physical addr seen (pages) */ |
| 1152 | + unsigned long nr_pages; /* number of pages collected */ |
| 1153 | + int nr_extents; /* extent count */ |
| 1154 | +}; |
| 1155 | + |
| 1156 | +/* |
| 1157 | + * Collect physical extents for this swap file. Physical extents reported to |
| 1158 | + * the swap code must be trimmed to align to a page boundary. The logical |
| 1159 | + * offset within the file is irrelevant since the swapfile code maps logical |
| 1160 | + * page numbers of the swap device to the physical page-aligned extents. |
| 1161 | + */ |
| 1162 | +static int iomap_swapfile_add_extent(struct iomap_swapfile_info *isi) |
| 1163 | +{ |
| 1164 | + struct iomap *iomap = &isi->iomap; |
| 1165 | + unsigned long nr_pages; |
| 1166 | + uint64_t first_ppage; |
| 1167 | + uint64_t first_ppage_reported; |
| 1168 | + uint64_t next_ppage; |
| 1169 | + int error; |
| 1170 | + |
| 1171 | + /* |
| 1172 | + * Round the start up and the end down so that the physical |
| 1173 | + * extent aligns to a page boundary. |
| 1174 | + */ |
| 1175 | + first_ppage = ALIGN(iomap->addr, PAGE_SIZE) >> PAGE_SHIFT; |
| 1176 | + next_ppage = ALIGN_DOWN(iomap->addr + iomap->length, PAGE_SIZE) >> |
| 1177 | + PAGE_SHIFT; |
| 1178 | + |
| 1179 | + /* Skip too-short physical extents. */ |
| 1180 | + if (first_ppage >= next_ppage) |
| 1181 | + return 0; |
| 1182 | + nr_pages = next_ppage - first_ppage; |
| 1183 | + |
| 1184 | + /* |
| 1185 | + * Calculate how much swap space we're adding; the first page contains |
| 1186 | + * the swap header and doesn't count. The mm still wants that first |
| 1187 | + * page fed to add_swap_extent, however. |
| 1188 | + */ |
| 1189 | + first_ppage_reported = first_ppage; |
| 1190 | + if (iomap->offset == 0) |
| 1191 | + first_ppage_reported++; |
| 1192 | + if (isi->lowest_ppage > first_ppage_reported) |
| 1193 | + isi->lowest_ppage = first_ppage_reported; |
| 1194 | + if (isi->highest_ppage < (next_ppage - 1)) |
| 1195 | + isi->highest_ppage = next_ppage - 1; |
| 1196 | + |
| 1197 | + /* Add extent, set up for the next call. */ |
| 1198 | + error = add_swap_extent(isi->sis, isi->nr_pages, nr_pages, first_ppage); |
| 1199 | + if (error < 0) |
| 1200 | + return error; |
| 1201 | + isi->nr_extents += error; |
| 1202 | + isi->nr_pages += nr_pages; |
| 1203 | + return 0; |
| 1204 | +} |
| 1205 | + |
| 1206 | +/* |
| 1207 | + * Accumulate iomaps for this swap file. We have to accumulate iomaps because |
| 1208 | + * swap only cares about contiguous page-aligned physical extents and makes no |
| 1209 | + * distinction between written and unwritten extents. |
| 1210 | + */ |
| 1211 | +static loff_t iomap_swapfile_activate_actor(struct inode *inode, loff_t pos, |
| 1212 | + loff_t count, void *data, struct iomap *iomap) |
| 1213 | +{ |
| 1214 | + struct iomap_swapfile_info *isi = data; |
| 1215 | + int error; |
| 1216 | + |
| 1217 | + /* Skip holes. */ |
| 1218 | + if (iomap->type == IOMAP_HOLE) |
| 1219 | + goto out; |
| 1220 | + |
| 1221 | + /* Only one bdev per swap file. */ |
| 1222 | + if (iomap->bdev != isi->sis->bdev) |
| 1223 | + goto err; |
| 1224 | + |
| 1225 | + /* Only real or unwritten extents. */ |
| 1226 | + if (iomap->type != IOMAP_MAPPED && iomap->type != IOMAP_UNWRITTEN) |
| 1227 | + goto err; |
| 1228 | + |
| 1229 | + /* No uncommitted metadata or shared blocks or inline data. */ |
| 1230 | + if (iomap->flags & (IOMAP_F_DIRTY | IOMAP_F_SHARED | |
| 1231 | + IOMAP_F_DATA_INLINE)) |
| 1232 | + goto err; |
| 1233 | + |
| 1234 | + /* No null physical addresses. */ |
| 1235 | + if (iomap->addr == IOMAP_NULL_ADDR) |
| 1236 | + goto err; |
| 1237 | + |
| 1238 | + if (isi->iomap.length == 0) { |
| 1239 | + /* No accumulated extent, so just store it. */ |
| 1240 | + memcpy(&isi->iomap, iomap, sizeof(isi->iomap)); |
| 1241 | + } else if (isi->iomap.addr + isi->iomap.length == iomap->addr) { |
| 1242 | + /* Append this to the accumulated extent. */ |
| 1243 | + isi->iomap.length += iomap->length; |
| 1244 | + } else { |
| 1245 | + /* Otherwise, add the retained iomap and store this one. */ |
| 1246 | + error = iomap_swapfile_add_extent(isi); |
| 1247 | + if (error) |
| 1248 | + return error; |
| 1249 | + memcpy(&isi->iomap, iomap, sizeof(isi->iomap)); |
| 1250 | + } |
| 1251 | +out: |
| 1252 | + return count; |
| 1253 | +err: |
| 1254 | + pr_err("swapon: file cannot be used for swap\n"); |
| 1255 | + return -EINVAL; |
| 1256 | +} |
| 1257 | + |
| 1258 | +/* |
| 1259 | + * Iterate a swap file's iomaps to construct physical extents that can be |
| 1260 | + * passed to the swapfile subsystem. |
| 1261 | + */ |
| 1262 | +int iomap_swapfile_activate(struct swap_info_struct *sis, |
| 1263 | + struct file *swap_file, sector_t *pagespan, |
| 1264 | + const struct iomap_ops *ops) |
| 1265 | +{ |
| 1266 | + struct iomap_swapfile_info isi = { |
| 1267 | + .sis = sis, |
| 1268 | + .lowest_ppage = (sector_t)-1ULL, |
| 1269 | + }; |
| 1270 | + struct address_space *mapping = swap_file->f_mapping; |
| 1271 | + struct inode *inode = mapping->host; |
| 1272 | + loff_t pos = 0; |
| 1273 | + loff_t len = ALIGN_DOWN(i_size_read(inode), PAGE_SIZE); |
| 1274 | + loff_t ret; |
| 1275 | + |
| 1276 | + ret = filemap_write_and_wait(inode->i_mapping); |
| 1277 | + if (ret) |
| 1278 | + return ret; |
| 1279 | + |
| 1280 | + while (len > 0) { |
| 1281 | + ret = iomap_apply(inode, pos, len, IOMAP_REPORT, |
| 1282 | + ops, &isi, iomap_swapfile_activate_actor); |
| 1283 | + if (ret <= 0) |
| 1284 | + return ret; |
| 1285 | + |
| 1286 | + pos += ret; |
| 1287 | + len -= ret; |
| 1288 | + } |
| 1289 | + |
| 1290 | + if (isi.iomap.length) { |
| 1291 | + ret = iomap_swapfile_add_extent(&isi); |
| 1292 | + if (ret) |
| 1293 | + return ret; |
| 1294 | + } |
| 1295 | + |
| 1296 | + *pagespan = 1 + isi.highest_ppage - isi.lowest_ppage; |
| 1297 | + sis->max = isi.nr_pages; |
| 1298 | + sis->pages = isi.nr_pages - 1; |
| 1299 | + sis->highest_bit = isi.nr_pages - 1; |
| 1300 | + return isi.nr_extents; |
| 1301 | +} |
| 1302 | +EXPORT_SYMBOL_GPL(iomap_swapfile_activate); |
| 1303 | +#endif /* CONFIG_SWAP */ |
0 commit comments