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/iomap.h>
7 #include <linux/swap.h>
8
9 /* Swapfile activation */
10
11 struct iomap_swapfile_info {
12 struct iomap iomap; /* accumulated iomap */
13 struct swap_info_struct *sis;
14 uint64_t lowest_ppage; /* lowest physical addr seen (pages) */
15 uint64_t highest_ppage; /* highest physical addr seen (pages) */
16 unsigned long nr_pages; /* number of pages collected */
17 int nr_extents; /* extent count */
18 struct file *file;
19 };
20
21 /*
22 * Collect physical extents for this swap file. Physical extents reported to
23 * the swap code must be trimmed to align to a page boundary. The logical
24 * offset within the file is irrelevant since the swapfile code maps logical
25 * page numbers of the swap device to the physical page-aligned extents.
26 */
iomap_swapfile_add_extent(struct iomap_swapfile_info * isi)27 static int iomap_swapfile_add_extent(struct iomap_swapfile_info *isi)
28 {
29 struct iomap *iomap = &isi->iomap;
30 unsigned long nr_pages;
31 unsigned long max_pages;
32 uint64_t first_ppage;
33 uint64_t first_ppage_reported;
34 uint64_t next_ppage;
35 int error;
36
37 if (unlikely(isi->nr_pages >= isi->sis->max))
38 return 0;
39 max_pages = isi->sis->max - isi->nr_pages;
40
41 /*
42 * Round the start up and the end down so that the physical
43 * extent aligns to a page boundary.
44 */
45 first_ppage = ALIGN(iomap->addr, PAGE_SIZE) >> PAGE_SHIFT;
46 next_ppage = ALIGN_DOWN(iomap->addr + iomap->length, PAGE_SIZE) >>
47 PAGE_SHIFT;
48
49 /* Skip too-short physical extents. */
50 if (first_ppage >= next_ppage)
51 return 0;
52 nr_pages = next_ppage - first_ppage;
53 nr_pages = min(nr_pages, max_pages);
54
55 /*
56 * Calculate how much swap space we're adding; the first page contains
57 * the swap header and doesn't count. The mm still wants that first
58 * page fed to add_swap_extent, however.
59 */
60 first_ppage_reported = first_ppage;
61 if (iomap->offset == 0)
62 first_ppage_reported++;
63 if (isi->lowest_ppage > first_ppage_reported)
64 isi->lowest_ppage = first_ppage_reported;
65 if (isi->highest_ppage < (next_ppage - 1))
66 isi->highest_ppage = next_ppage - 1;
67
68 /* Add extent, set up for the next call. */
69 error = add_swap_extent(isi->sis, isi->nr_pages, nr_pages, first_ppage);
70 if (error < 0)
71 return error;
72 isi->nr_extents += error;
73 isi->nr_pages += nr_pages;
74 return 0;
75 }
76
iomap_swapfile_fail(struct iomap_swapfile_info * isi,const char * str)77 static int iomap_swapfile_fail(struct iomap_swapfile_info *isi, const char *str)
78 {
79 char *buf, *p = ERR_PTR(-ENOMEM);
80
81 buf = kmalloc(PATH_MAX, GFP_KERNEL);
82 if (buf)
83 p = file_path(isi->file, buf, PATH_MAX);
84 pr_err("swapon: file %s %s\n", IS_ERR(p) ? "<unknown>" : p, str);
85 kfree(buf);
86 return -EINVAL;
87 }
88
89 /*
90 * Accumulate iomaps for this swap file. We have to accumulate iomaps because
91 * swap only cares about contiguous page-aligned physical extents and makes no
92 * distinction between written and unwritten extents.
93 */
iomap_swapfile_iter(struct iomap_iter * iter,struct iomap * iomap,struct iomap_swapfile_info * isi)94 static int iomap_swapfile_iter(struct iomap_iter *iter,
95 struct iomap *iomap, struct iomap_swapfile_info *isi)
96 {
97 switch (iomap->type) {
98 case IOMAP_MAPPED:
99 case IOMAP_UNWRITTEN:
100 /* Only real or unwritten extents. */
101 break;
102 case IOMAP_INLINE:
103 /* No inline data. */
104 return iomap_swapfile_fail(isi, "is inline");
105 default:
106 return iomap_swapfile_fail(isi, "has unallocated extents");
107 }
108
109 /* No uncommitted metadata or shared blocks. */
110 if (iomap->flags & IOMAP_F_DIRTY)
111 return iomap_swapfile_fail(isi, "is not committed");
112 if (iomap->flags & IOMAP_F_SHARED)
113 return iomap_swapfile_fail(isi, "has shared extents");
114
115 /* Only one bdev per swap file. */
116 if (iomap->bdev != isi->sis->bdev)
117 return iomap_swapfile_fail(isi, "outside the main device");
118
119 if (isi->iomap.length == 0) {
120 /* No accumulated extent, so just store it. */
121 memcpy(&isi->iomap, iomap, sizeof(isi->iomap));
122 } else if (isi->iomap.addr + isi->iomap.length == iomap->addr) {
123 /* Append this to the accumulated extent. */
124 isi->iomap.length += iomap->length;
125 } else {
126 /* Otherwise, add the retained iomap and store this one. */
127 int error = iomap_swapfile_add_extent(isi);
128 if (error)
129 return error;
130 memcpy(&isi->iomap, iomap, sizeof(isi->iomap));
131 }
132
133 return iomap_iter_advance_full(iter);
134 }
135
136 /*
137 * Iterate a swap file's iomaps to construct physical extents that can be
138 * passed to the swapfile subsystem.
139 */
iomap_swapfile_activate(struct swap_info_struct * sis,struct file * swap_file,sector_t * pagespan,const struct iomap_ops * ops)140 int iomap_swapfile_activate(struct swap_info_struct *sis,
141 struct file *swap_file, sector_t *pagespan,
142 const struct iomap_ops *ops)
143 {
144 struct inode *inode = swap_file->f_mapping->host;
145 struct iomap_iter iter = {
146 .inode = inode,
147 .pos = 0,
148 .len = ALIGN_DOWN(i_size_read(inode), PAGE_SIZE),
149 .flags = IOMAP_REPORT,
150 };
151 struct iomap_swapfile_info isi = {
152 .sis = sis,
153 .lowest_ppage = (sector_t)-1ULL,
154 .file = swap_file,
155 };
156 int ret;
157
158 /*
159 * Persist all file mapping metadata so that we won't have any
160 * IOMAP_F_DIRTY iomaps.
161 */
162 ret = vfs_fsync(swap_file, 1);
163 if (ret)
164 return ret;
165
166 while ((ret = iomap_iter(&iter, ops)) > 0)
167 iter.status = iomap_swapfile_iter(&iter, &iter.iomap, &isi);
168 if (ret < 0)
169 return ret;
170
171 if (isi.iomap.length) {
172 ret = iomap_swapfile_add_extent(&isi);
173 if (ret)
174 return ret;
175 }
176
177 /*
178 * If this swapfile doesn't contain even a single page-aligned
179 * contiguous range of blocks, reject this useless swapfile to
180 * prevent confusion later on.
181 */
182 if (isi.nr_pages == 0) {
183 pr_warn("swapon: Cannot find a single usable page in file.\n");
184 return -EINVAL;
185 }
186
187 *pagespan = 1 + isi.highest_ppage - isi.lowest_ppage;
188 sis->max = isi.nr_pages;
189 sis->pages = isi.nr_pages - 1;
190 return isi.nr_extents;
191 }
192 EXPORT_SYMBOL_GPL(iomap_swapfile_activate);
193