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