Skip to content

Commit 6748212

Browse files
committed
iomap: add a swapfile activation function
Add a new iomap_swapfile_activate function so that filesystems can activate swap files without having to use the obsolete and slow bmap function. This enables XFS to support fallocate'd swap files and swap files on realtime devices. Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Jan Kara <[email protected]>
1 parent d6b636e commit 6748212

File tree

3 files changed

+185
-0
lines changed

3 files changed

+185
-0
lines changed

fs/iomap.c

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <linux/task_io_accounting_ops.h>
2828
#include <linux/dax.h>
2929
#include <linux/sched/signal.h>
30+
#include <linux/swap.h>
3031

3132
#include "internal.h"
3233

@@ -1139,3 +1140,164 @@ iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
11391140
return ret;
11401141
}
11411142
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 */

fs/xfs/xfs_aops.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1475,6 +1475,16 @@ xfs_vm_set_page_dirty(
14751475
return newly_dirty;
14761476
}
14771477

1478+
static int
1479+
xfs_iomap_swapfile_activate(
1480+
struct swap_info_struct *sis,
1481+
struct file *swap_file,
1482+
sector_t *span)
1483+
{
1484+
sis->bdev = xfs_find_bdev_for_inode(file_inode(swap_file));
1485+
return iomap_swapfile_activate(sis, swap_file, span, &xfs_iomap_ops);
1486+
}
1487+
14781488
const struct address_space_operations xfs_address_space_operations = {
14791489
.readpage = xfs_vm_readpage,
14801490
.readpages = xfs_vm_readpages,
@@ -1488,11 +1498,13 @@ const struct address_space_operations xfs_address_space_operations = {
14881498
.migratepage = buffer_migrate_page,
14891499
.is_partially_uptodate = block_is_partially_uptodate,
14901500
.error_remove_page = generic_error_remove_page,
1501+
.swap_activate = xfs_iomap_swapfile_activate,
14911502
};
14921503

14931504
const struct address_space_operations xfs_dax_aops = {
14941505
.writepages = xfs_dax_writepages,
14951506
.direct_IO = noop_direct_IO,
14961507
.set_page_dirty = noop_set_page_dirty,
14971508
.invalidatepage = noop_invalidatepage,
1509+
.swap_activate = xfs_iomap_swapfile_activate,
14981510
};

include/linux/iomap.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,15 @@ typedef int (iomap_dio_end_io_t)(struct kiocb *iocb, ssize_t ret,
106106
ssize_t iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
107107
const struct iomap_ops *ops, iomap_dio_end_io_t end_io);
108108

109+
#ifdef CONFIG_SWAP
110+
struct file;
111+
struct swap_info_struct;
112+
113+
int iomap_swapfile_activate(struct swap_info_struct *sis,
114+
struct file *swap_file, sector_t *pagespan,
115+
const struct iomap_ops *ops);
116+
#else
117+
# define iomap_swapfile_activate(sis, swapfile, pagespan, ops) (-EIO)
118+
#endif /* CONFIG_SWAP */
119+
109120
#endif /* LINUX_IOMAP_H */

0 commit comments

Comments
 (0)