1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2017 Red Hat, Inc.
4 * Copyright (c) 2018 Christoph Hellwig.
6 #include <linux/module.h>
7 #include <linux/compiler.h>
9 #include <linux/iomap.h>
10 #include <linux/pagemap.h>
11 #include <linux/pagevec.h>
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.
18 page_seek_hole_data(struct inode
*inode
, struct page
*page
, loff_t
*lastoff
,
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
);
26 if (WARN_ON_ONCE(*lastoff
>= poff
+ PAGE_SIZE
))
29 if (*lastoff
< poff
) {
31 * Last offset smaller than the start of the page means we found
34 if (whence
== SEEK_HOLE
)
40 * Just check the page unless we can and should check block ranges:
42 if (bsize
== PAGE_SIZE
|| !ops
->is_partially_uptodate
)
43 return PageUptodate(page
) == seek_data
;
46 if (unlikely(page
->mapping
!= inode
->i_mapping
))
47 goto out_unlock_not_found
;
49 for (off
= 0; off
< PAGE_SIZE
; off
+= bsize
) {
50 if (offset_in_page(*lastoff
) >= off
+ bsize
)
52 if (ops
->is_partially_uptodate(page
, off
, bsize
) == seek_data
) {
56 *lastoff
= poff
+ off
+ bsize
;
65 * Seek for SEEK_DATA / SEEK_HOLE in the page cache.
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
71 * Returns the resulting offset on successs, and -ENOENT otherwise.
74 page_cache_seek_hole_data(struct inode
*inode
, loff_t offset
, loff_t length
,
77 pgoff_t index
= offset
>> PAGE_SHIFT
;
78 pgoff_t end
= DIV_ROUND_UP(offset
+ length
, PAGE_SIZE
);
79 loff_t lastoff
= offset
;
90 nr_pages
= pagevec_lookup_range(&pvec
, inode
->i_mapping
, &index
,
95 for (i
= 0; i
< nr_pages
; i
++) {
96 struct page
*page
= pvec
.pages
[i
];
98 if (page_seek_hole_data(inode
, page
, &lastoff
, whence
))
100 lastoff
= page_offset(page
) + PAGE_SIZE
;
102 pagevec_release(&pvec
);
103 } while (index
< end
);
105 /* When no page at lastoff and we are not done, we found a hole. */
106 if (whence
!= SEEK_HOLE
)
110 if (lastoff
< offset
+ length
)
115 pagevec_release(&pvec
);
121 iomap_seek_hole_actor(struct inode
*inode
, loff_t offset
, loff_t length
,
122 void *data
, struct iomap
*iomap
, struct iomap
*srcmap
)
124 switch (iomap
->type
) {
125 case IOMAP_UNWRITTEN
:
126 offset
= page_cache_seek_hole_data(inode
, offset
, length
,
132 *(loff_t
*)data
= offset
;
140 iomap_seek_hole(struct inode
*inode
, loff_t offset
, const struct iomap_ops
*ops
)
142 loff_t size
= i_size_read(inode
);
143 loff_t length
= size
- offset
;
146 /* Nothing to be found before or beyond the end of the file. */
147 if (offset
< 0 || offset
>= size
)
151 ret
= iomap_apply(inode
, offset
, length
, IOMAP_REPORT
, ops
,
152 &offset
, iomap_seek_hole_actor
);
164 EXPORT_SYMBOL_GPL(iomap_seek_hole
);
167 iomap_seek_data_actor(struct inode
*inode
, loff_t offset
, loff_t length
,
168 void *data
, struct iomap
*iomap
, struct iomap
*srcmap
)
170 switch (iomap
->type
) {
173 case IOMAP_UNWRITTEN
:
174 offset
= page_cache_seek_hole_data(inode
, offset
, length
,
180 *(loff_t
*)data
= offset
;
186 iomap_seek_data(struct inode
*inode
, loff_t offset
, const struct iomap_ops
*ops
)
188 loff_t size
= i_size_read(inode
);
189 loff_t length
= size
- offset
;
192 /* Nothing to be found before or beyond the end of the file. */
193 if (offset
< 0 || offset
>= size
)
197 ret
= iomap_apply(inode
, offset
, length
, IOMAP_REPORT
, ops
,
198 &offset
, iomap_seek_data_actor
);
212 EXPORT_SYMBOL_GPL(iomap_seek_data
);