diff options
author | Christoph Hellwig <hch@lst.de> | 2018-06-01 12:05:15 -0400 |
---|---|---|
committer | Darrick J. Wong <darrick.wong@oracle.com> | 2018-06-01 21:37:33 -0400 |
commit | afd9d6a1df75807684fa40dab77c52e104e5c74b (patch) | |
tree | 6411e69f4f2c8c64aceb79f17448c93368de4d68 | |
parent | bd56b3e14410b8cf6a4c13c94f7065341b179517 (diff) |
fs: use ->is_partially_uptodate in page_cache_seek_hole_data
This way the implementation doesn't depend on buffer_head internals.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Andreas Gruenbacher <agruenba@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-rw-r--r-- | fs/iomap.c | 83 |
1 files changed, 40 insertions, 43 deletions
diff --git a/fs/iomap.c b/fs/iomap.c index 2a20d50f0c1d..206539d369a8 100644 --- a/fs/iomap.c +++ b/fs/iomap.c | |||
@@ -595,34 +595,53 @@ EXPORT_SYMBOL_GPL(iomap_fiemap); | |||
595 | 595 | ||
596 | /* | 596 | /* |
597 | * Seek for SEEK_DATA / SEEK_HOLE within @page, starting at @lastoff. | 597 | * Seek for SEEK_DATA / SEEK_HOLE within @page, starting at @lastoff. |
598 | * | 598 | * Returns true if found and updates @lastoff to the offset in file. |
599 | * Returns the offset within the file on success, and -ENOENT otherwise. | ||
600 | */ | 599 | */ |
601 | static loff_t | 600 | static bool |
602 | page_seek_hole_data(struct page *page, loff_t lastoff, int whence) | 601 | page_seek_hole_data(struct inode *inode, struct page *page, loff_t *lastoff, |
602 | int whence) | ||
603 | { | 603 | { |
604 | loff_t offset = page_offset(page); | 604 | const struct address_space_operations *ops = inode->i_mapping->a_ops; |
605 | struct buffer_head *bh, *head; | 605 | unsigned int bsize = i_blocksize(inode), off; |
606 | bool seek_data = whence == SEEK_DATA; | 606 | bool seek_data = whence == SEEK_DATA; |
607 | loff_t poff = page_offset(page); | ||
607 | 608 | ||
608 | if (lastoff < offset) | 609 | if (WARN_ON_ONCE(*lastoff >= poff + PAGE_SIZE)) |
609 | lastoff = offset; | 610 | return false; |
610 | |||
611 | bh = head = page_buffers(page); | ||
612 | do { | ||
613 | offset += bh->b_size; | ||
614 | if (lastoff >= offset) | ||
615 | continue; | ||
616 | 611 | ||
612 | if (*lastoff < poff) { | ||
617 | /* | 613 | /* |
618 | * Any buffer with valid data in it should have BH_Uptodate set. | 614 | * Last offset smaller than the start of the page means we found |
615 | * a hole: | ||
619 | */ | 616 | */ |
620 | if (buffer_uptodate(bh) == seek_data) | 617 | if (whence == SEEK_HOLE) |
621 | return lastoff; | 618 | return true; |
619 | *lastoff = poff; | ||
620 | } | ||
622 | 621 | ||
623 | lastoff = offset; | 622 | /* |
624 | } while ((bh = bh->b_this_page) != head); | 623 | * Just check the page unless we can and should check block ranges: |
625 | return -ENOENT; | 624 | */ |
625 | if (bsize == PAGE_SIZE || !ops->is_partially_uptodate) | ||
626 | return PageUptodate(page) == seek_data; | ||
627 | |||
628 | lock_page(page); | ||
629 | if (unlikely(page->mapping != inode->i_mapping)) | ||
630 | goto out_unlock_not_found; | ||
631 | |||
632 | for (off = 0; off < PAGE_SIZE; off += bsize) { | ||
633 | if ((*lastoff & ~PAGE_MASK) >= off + bsize) | ||
634 | continue; | ||
635 | if (ops->is_partially_uptodate(page, off, bsize) == seek_data) { | ||
636 | unlock_page(page); | ||
637 | return true; | ||
638 | } | ||
639 | *lastoff = poff + off + bsize; | ||
640 | } | ||
641 | |||
642 | out_unlock_not_found: | ||
643 | unlock_page(page); | ||
644 | return false; | ||
626 | } | 645 | } |
627 | 646 | ||
628 | /* | 647 | /* |
@@ -659,30 +678,8 @@ page_cache_seek_hole_data(struct inode *inode, loff_t offset, loff_t length, | |||
659 | for (i = 0; i < nr_pages; i++) { | 678 | for (i = 0; i < nr_pages; i++) { |
660 | struct page *page = pvec.pages[i]; | 679 | struct page *page = pvec.pages[i]; |
661 | 680 | ||
662 | /* | 681 | if (page_seek_hole_data(inode, page, &lastoff, whence)) |
663 | * At this point, the page may be truncated or | ||
664 | * invalidated (changing page->mapping to NULL), or | ||
665 | * even swizzled back from swapper_space to tmpfs file | ||
666 | * mapping. However, page->index will not change | ||
667 | * because we have a reference on the page. | ||
668 | * | ||
669 | * If current page offset is beyond where we've ended, | ||
670 | * we've found a hole. | ||
671 | */ | ||
672 | if (whence == SEEK_HOLE && | ||
673 | lastoff < page_offset(page)) | ||
674 | goto check_range; | 682 | goto check_range; |
675 | |||
676 | lock_page(page); | ||
677 | if (likely(page->mapping == inode->i_mapping) && | ||
678 | page_has_buffers(page)) { | ||
679 | lastoff = page_seek_hole_data(page, lastoff, whence); | ||
680 | if (lastoff >= 0) { | ||
681 | unlock_page(page); | ||
682 | goto check_range; | ||
683 | } | ||
684 | } | ||
685 | unlock_page(page); | ||
686 | lastoff = page_offset(page) + PAGE_SIZE; | 683 | lastoff = page_offset(page) + PAGE_SIZE; |
687 | } | 684 | } |
688 | pagevec_release(&pvec); | 685 | pagevec_release(&pvec); |