|  | 
| 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