root/fs/iomap/seek.c

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

DEFINITIONS

This source file includes following definitions.
  1. page_seek_hole_data
  2. page_cache_seek_hole_data
  3. iomap_seek_hole_actor
  4. iomap_seek_hole
  5. iomap_seek_data_actor
  6. iomap_seek_data

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Copyright (C) 2017 Red Hat, Inc.
   4  * Copyright (c) 2018 Christoph Hellwig.
   5  */
   6 #include <linux/module.h>
   7 #include <linux/compiler.h>
   8 #include <linux/fs.h>
   9 #include <linux/iomap.h>
  10 #include <linux/pagemap.h>
  11 #include <linux/pagevec.h>
  12 
  13 /*
  14  * Seek for SEEK_DATA / SEEK_HOLE within @page, starting at @lastoff.
  15  * Returns true if found and updates @lastoff to the offset in file.
  16  */
  17 static bool
  18 page_seek_hole_data(struct inode *inode, struct page *page, loff_t *lastoff,
  19                 int whence)
  20 {
  21         const struct address_space_operations *ops = inode->i_mapping->a_ops;
  22         unsigned int bsize = i_blocksize(inode), off;
  23         bool seek_data = whence == SEEK_DATA;
  24         loff_t poff = page_offset(page);
  25 
  26         if (WARN_ON_ONCE(*lastoff >= poff + PAGE_SIZE))
  27                 return false;
  28 
  29         if (*lastoff < poff) {
  30                 /*
  31                  * Last offset smaller than the start of the page means we found
  32                  * a hole:
  33                  */
  34                 if (whence == SEEK_HOLE)
  35                         return true;
  36                 *lastoff = poff;
  37         }
  38 
  39         /*
  40          * Just check the page unless we can and should check block ranges:
  41          */
  42         if (bsize == PAGE_SIZE || !ops->is_partially_uptodate)
  43                 return PageUptodate(page) == seek_data;
  44 
  45         lock_page(page);
  46         if (unlikely(page->mapping != inode->i_mapping))
  47                 goto out_unlock_not_found;
  48 
  49         for (off = 0; off < PAGE_SIZE; off += bsize) {
  50                 if (offset_in_page(*lastoff) >= off + bsize)
  51                         continue;
  52                 if (ops->is_partially_uptodate(page, off, bsize) == seek_data) {
  53                         unlock_page(page);
  54                         return true;
  55                 }
  56                 *lastoff = poff + off + bsize;
  57         }
  58 
  59 out_unlock_not_found:
  60         unlock_page(page);
  61         return false;
  62 }
  63 
  64 /*
  65  * Seek for SEEK_DATA / SEEK_HOLE in the page cache.
  66  *
  67  * Within unwritten extents, the page cache determines which parts are holes
  68  * and which are data: uptodate buffer heads count as data; everything else
  69  * counts as a hole.
  70  *
  71  * Returns the resulting offset on successs, and -ENOENT otherwise.
  72  */
  73 static loff_t
  74 page_cache_seek_hole_data(struct inode *inode, loff_t offset, loff_t length,
  75                 int whence)
  76 {
  77         pgoff_t index = offset >> PAGE_SHIFT;
  78         pgoff_t end = DIV_ROUND_UP(offset + length, PAGE_SIZE);
  79         loff_t lastoff = offset;
  80         struct pagevec pvec;
  81 
  82         if (length <= 0)
  83                 return -ENOENT;
  84 
  85         pagevec_init(&pvec);
  86 
  87         do {
  88                 unsigned nr_pages, i;
  89 
  90                 nr_pages = pagevec_lookup_range(&pvec, inode->i_mapping, &index,
  91                                                 end - 1);
  92                 if (nr_pages == 0)
  93                         break;
  94 
  95                 for (i = 0; i < nr_pages; i++) {
  96                         struct page *page = pvec.pages[i];
  97 
  98                         if (page_seek_hole_data(inode, page, &lastoff, whence))
  99                                 goto check_range;
 100                         lastoff = page_offset(page) + PAGE_SIZE;
 101                 }
 102                 pagevec_release(&pvec);
 103         } while (index < end);
 104 
 105         /* When no page at lastoff and we are not done, we found a hole. */
 106         if (whence != SEEK_HOLE)
 107                 goto not_found;
 108 
 109 check_range:
 110         if (lastoff < offset + length)
 111                 goto out;
 112 not_found:
 113         lastoff = -ENOENT;
 114 out:
 115         pagevec_release(&pvec);
 116         return lastoff;
 117 }
 118 
 119 
 120 static loff_t
 121 iomap_seek_hole_actor(struct inode *inode, loff_t offset, loff_t length,
 122                       void *data, struct iomap *iomap)
 123 {
 124         switch (iomap->type) {
 125         case IOMAP_UNWRITTEN:
 126                 offset = page_cache_seek_hole_data(inode, offset, length,
 127                                                    SEEK_HOLE);
 128                 if (offset < 0)
 129                         return length;
 130                 /* fall through */
 131         case IOMAP_HOLE:
 132                 *(loff_t *)data = offset;
 133                 return 0;
 134         default:
 135                 return length;
 136         }
 137 }
 138 
 139 loff_t
 140 iomap_seek_hole(struct inode *inode, loff_t offset, const struct iomap_ops *ops)
 141 {
 142         loff_t size = i_size_read(inode);
 143         loff_t length = size - offset;
 144         loff_t ret;
 145 
 146         /* Nothing to be found before or beyond the end of the file. */
 147         if (offset < 0 || offset >= size)
 148                 return -ENXIO;
 149 
 150         while (length > 0) {
 151                 ret = iomap_apply(inode, offset, length, IOMAP_REPORT, ops,
 152                                   &offset, iomap_seek_hole_actor);
 153                 if (ret < 0)
 154                         return ret;
 155                 if (ret == 0)
 156                         break;
 157 
 158                 offset += ret;
 159                 length -= ret;
 160         }
 161 
 162         return offset;
 163 }
 164 EXPORT_SYMBOL_GPL(iomap_seek_hole);
 165 
 166 static loff_t
 167 iomap_seek_data_actor(struct inode *inode, loff_t offset, loff_t length,
 168                       void *data, struct iomap *iomap)
 169 {
 170         switch (iomap->type) {
 171         case IOMAP_HOLE:
 172                 return length;
 173         case IOMAP_UNWRITTEN:
 174                 offset = page_cache_seek_hole_data(inode, offset, length,
 175                                                    SEEK_DATA);
 176                 if (offset < 0)
 177                         return length;
 178                 /*FALLTHRU*/
 179         default:
 180                 *(loff_t *)data = offset;
 181                 return 0;
 182         }
 183 }
 184 
 185 loff_t
 186 iomap_seek_data(struct inode *inode, loff_t offset, const struct iomap_ops *ops)
 187 {
 188         loff_t size = i_size_read(inode);
 189         loff_t length = size - offset;
 190         loff_t ret;
 191 
 192         /* Nothing to be found before or beyond the end of the file. */
 193         if (offset < 0 || offset >= size)
 194                 return -ENXIO;
 195 
 196         while (length > 0) {
 197                 ret = iomap_apply(inode, offset, length, IOMAP_REPORT, ops,
 198                                   &offset, iomap_seek_data_actor);
 199                 if (ret < 0)
 200                         return ret;
 201                 if (ret == 0)
 202                         break;
 203 
 204                 offset += ret;
 205                 length -= ret;
 206         }
 207 
 208         if (length <= 0)
 209                 return -ENXIO;
 210         return offset;
 211 }
 212 EXPORT_SYMBOL_GPL(iomap_seek_data);

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