diff options
author | Christoph Hellwig <hch@lst.de> | 2016-09-18 20:12:45 -0400 |
---|---|---|
committer | Dave Chinner <david@fromorbit.com> | 2016-09-18 20:12:45 -0400 |
commit | 5f4e5752a8a3a72c79514def2ad9fc7cd410ce2e (patch) | |
tree | 943edac57e87caabeaece086af13c8a0c6cef60f | |
parent | ea78d80866ce375defb2fdd1c8a3aafec95e0f85 (diff) |
fs: add iomap_file_dirty
Originally-From: Christoph Hellwig <hch@lst.de>
This function uses the iomap infrastructure to re-write all pages
in a given range. This is useful for doing a copy-up of COW ranges,
and might be useful for scrubbing in the future.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
-rw-r--r-- | fs/iomap.c | 82 | ||||
-rw-r--r-- | include/linux/iomap.h | 2 |
2 files changed, 84 insertions, 0 deletions
diff --git a/fs/iomap.c b/fs/iomap.c index 706270f21b35..e9b3f991c2d8 100644 --- a/fs/iomap.c +++ b/fs/iomap.c | |||
@@ -252,6 +252,88 @@ iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *iter, | |||
252 | } | 252 | } |
253 | EXPORT_SYMBOL_GPL(iomap_file_buffered_write); | 253 | EXPORT_SYMBOL_GPL(iomap_file_buffered_write); |
254 | 254 | ||
255 | static struct page * | ||
256 | __iomap_read_page(struct inode *inode, loff_t offset) | ||
257 | { | ||
258 | struct address_space *mapping = inode->i_mapping; | ||
259 | struct page *page; | ||
260 | |||
261 | page = read_mapping_page(mapping, offset >> PAGE_SHIFT, NULL); | ||
262 | if (IS_ERR(page)) | ||
263 | return page; | ||
264 | if (!PageUptodate(page)) { | ||
265 | put_page(page); | ||
266 | return ERR_PTR(-EIO); | ||
267 | } | ||
268 | return page; | ||
269 | } | ||
270 | |||
271 | static loff_t | ||
272 | iomap_dirty_actor(struct inode *inode, loff_t pos, loff_t length, void *data, | ||
273 | struct iomap *iomap) | ||
274 | { | ||
275 | long status = 0; | ||
276 | ssize_t written = 0; | ||
277 | |||
278 | do { | ||
279 | struct page *page, *rpage; | ||
280 | unsigned long offset; /* Offset into pagecache page */ | ||
281 | unsigned long bytes; /* Bytes to write to page */ | ||
282 | |||
283 | offset = (pos & (PAGE_SIZE - 1)); | ||
284 | bytes = min_t(unsigned long, PAGE_SIZE - offset, length); | ||
285 | |||
286 | rpage = __iomap_read_page(inode, pos); | ||
287 | if (IS_ERR(rpage)) | ||
288 | return PTR_ERR(rpage); | ||
289 | |||
290 | status = iomap_write_begin(inode, pos, bytes, | ||
291 | AOP_FLAG_NOFS | AOP_FLAG_UNINTERRUPTIBLE, | ||
292 | &page, iomap); | ||
293 | put_page(rpage); | ||
294 | if (unlikely(status)) | ||
295 | return status; | ||
296 | |||
297 | WARN_ON_ONCE(!PageUptodate(page)); | ||
298 | |||
299 | status = iomap_write_end(inode, pos, bytes, bytes, page); | ||
300 | if (unlikely(status <= 0)) { | ||
301 | if (WARN_ON_ONCE(status == 0)) | ||
302 | return -EIO; | ||
303 | return status; | ||
304 | } | ||
305 | |||
306 | cond_resched(); | ||
307 | |||
308 | pos += status; | ||
309 | written += status; | ||
310 | length -= status; | ||
311 | |||
312 | balance_dirty_pages_ratelimited(inode->i_mapping); | ||
313 | } while (length); | ||
314 | |||
315 | return written; | ||
316 | } | ||
317 | |||
318 | int | ||
319 | iomap_file_dirty(struct inode *inode, loff_t pos, loff_t len, | ||
320 | struct iomap_ops *ops) | ||
321 | { | ||
322 | loff_t ret; | ||
323 | |||
324 | while (len) { | ||
325 | ret = iomap_apply(inode, pos, len, IOMAP_WRITE, ops, NULL, | ||
326 | iomap_dirty_actor); | ||
327 | if (ret <= 0) | ||
328 | return ret; | ||
329 | pos += ret; | ||
330 | len -= ret; | ||
331 | } | ||
332 | |||
333 | return 0; | ||
334 | } | ||
335 | EXPORT_SYMBOL_GPL(iomap_file_dirty); | ||
336 | |||
255 | static int iomap_zero(struct inode *inode, loff_t pos, unsigned offset, | 337 | static int iomap_zero(struct inode *inode, loff_t pos, unsigned offset, |
256 | unsigned bytes, struct iomap *iomap) | 338 | unsigned bytes, struct iomap *iomap) |
257 | { | 339 | { |
diff --git a/include/linux/iomap.h b/include/linux/iomap.h index 3d70ece10313..3a56212a0a8d 100644 --- a/include/linux/iomap.h +++ b/include/linux/iomap.h | |||
@@ -64,6 +64,8 @@ struct iomap_ops { | |||
64 | 64 | ||
65 | ssize_t iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *from, | 65 | ssize_t iomap_file_buffered_write(struct kiocb *iocb, struct iov_iter *from, |
66 | struct iomap_ops *ops); | 66 | struct iomap_ops *ops); |
67 | int iomap_file_dirty(struct inode *inode, loff_t pos, loff_t len, | ||
68 | struct iomap_ops *ops); | ||
67 | int iomap_zero_range(struct inode *inode, loff_t pos, loff_t len, | 69 | int iomap_zero_range(struct inode *inode, loff_t pos, loff_t len, |
68 | bool *did_zero, struct iomap_ops *ops); | 70 | bool *did_zero, struct iomap_ops *ops); |
69 | int iomap_truncate_page(struct inode *inode, loff_t pos, bool *did_zero, | 71 | int iomap_truncate_page(struct inode *inode, loff_t pos, bool *did_zero, |