root/fs/iomap/swapfile.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. iomap_swapfile_add_extent
  2. iomap_swapfile_activate_actor
  3. iomap_swapfile_activate

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

/* [<][>][^][v][top][bottom][index][help] */