~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/iomap/seek.c

Version: ~ [ linux-5.19-rc3 ] ~ [ linux-5.18.5 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.48 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.123 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.199 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.248 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.284 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.319 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.302 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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, struct iomap *srcmap)
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, struct iomap *srcmap)
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);
213 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp