From: Andreas Gruenbacher <agruenba@redhat.com> To: linux-fsdevel@vger.kernel.org Cc: Andreas Gruenbacher <agruenba@redhat.com>, linux-xfs@vger.kernel.org, linux-ext4@vger.kernel.org Subject: [PATCH v3 3/5] xfs: Switch to page_cache_seek_hole_data Date: Mon, 26 Jun 2017 16:25:16 +0200 [thread overview] Message-ID: <1498487118-6564-4-git-send-email-agruenba@redhat.com> (raw) In-Reply-To: <1498487118-6564-1-git-send-email-agruenba@redhat.com> Fix SEEK_HOLE / SEEK_DATA when a page fits more than two filesystem blocks (see the commit that introduces page_cache_seek_hole_data). Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> --- fs/xfs/xfs_file.c | 197 +++--------------------------------------------------- 1 file changed, 9 insertions(+), 188 deletions(-) diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 86701a0..962dafd 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -981,191 +981,6 @@ xfs_file_readdir( } /* - * This type is designed to indicate the type of offset we would like - * to search from page cache for xfs_seek_hole_data(). - */ -enum { - HOLE_OFF = 0, - DATA_OFF, -}; - -/* - * Lookup the desired type of offset from the given page. - * - * On success, return true and the offset argument will point to the - * start of the region that was found. Otherwise this function will - * return false and keep the offset argument unchanged. - */ -STATIC bool -xfs_lookup_buffer_offset( - struct page *page, - loff_t *offset, - unsigned int type) -{ - loff_t lastoff = page_offset(page); - bool found = false; - struct buffer_head *bh, *head; - - bh = head = page_buffers(page); - do { - /* - * Unwritten extents that have data in the page - * cache covering them can be identified by the - * BH_Unwritten state flag. Pages with multiple - * buffers might have a mix of holes, data and - * unwritten extents - any buffer with valid - * data in it should have BH_Uptodate flag set - * on it. - */ - if (buffer_unwritten(bh) || - buffer_uptodate(bh)) { - if (type == DATA_OFF) - found = true; - } else { - if (type == HOLE_OFF) - found = true; - } - - if (found) { - *offset = lastoff; - break; - } - lastoff += bh->b_size; - } while ((bh = bh->b_this_page) != head); - - return found; -} - -/* - * This routine is called to find out and return a data or hole offset - * from the page cache for unwritten extents according to the desired - * type for xfs_seek_hole_data(). - * - * The argument offset is used to tell where we start to search from the - * page cache. Map is used to figure out the end points of the range to - * lookup pages. - * - * Return true if the desired type of offset was found, and the argument - * offset is filled with that address. Otherwise, return false and keep - * offset unchanged. - */ -STATIC bool -xfs_find_get_desired_pgoff( - struct inode *inode, - struct xfs_bmbt_irec *map, - unsigned int type, - loff_t *offset) -{ - struct xfs_inode *ip = XFS_I(inode); - struct xfs_mount *mp = ip->i_mount; - struct pagevec pvec; - pgoff_t index; - pgoff_t end; - loff_t endoff; - loff_t startoff = *offset; - loff_t lastoff = startoff; - bool found = false; - - pagevec_init(&pvec, 0); - - index = startoff >> PAGE_SHIFT; - endoff = XFS_FSB_TO_B(mp, map->br_startoff + map->br_blockcount); - end = (endoff - 1) >> PAGE_SHIFT; - do { - int want; - unsigned nr_pages; - unsigned int i; - - want = min_t(pgoff_t, end - index, PAGEVEC_SIZE - 1) + 1; - nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index, - want); - if (nr_pages == 0) - break; - - for (i = 0; i < nr_pages; i++) { - struct page *page = pvec.pages[i]; - loff_t b_offset; - - /* - * At this point, the page may be truncated or - * invalidated (changing page->mapping to NULL), - * or even swizzled back from swapper_space to tmpfs - * file mapping. However, page->index will not change - * because we have a reference on the page. - * - * If current page offset is beyond where we've ended, - * we've found a hole. - */ - if (type == HOLE_OFF && lastoff < endoff && - lastoff < page_offset(pvec.pages[i])) { - found = true; - *offset = lastoff; - goto out; - } - /* Searching done if the page index is out of range. */ - if (page->index > end) - goto out; - - lock_page(page); - /* - * Page truncated or invalidated(page->mapping == NULL). - * We can freely skip it and proceed to check the next - * page. - */ - if (unlikely(page->mapping != inode->i_mapping)) { - unlock_page(page); - continue; - } - - if (!page_has_buffers(page)) { - unlock_page(page); - continue; - } - - found = xfs_lookup_buffer_offset(page, &b_offset, type); - if (found) { - /* - * The found offset may be less than the start - * point to search if this is the first time to - * come here. - */ - *offset = max_t(loff_t, startoff, b_offset); - unlock_page(page); - goto out; - } - - /* - * We either searching data but nothing was found, or - * searching hole but found a data buffer. In either - * case, probably the next page contains the desired - * things, update the last offset to it so. - */ - lastoff = page_offset(page) + PAGE_SIZE; - unlock_page(page); - } - - /* - * The number of returned pages less than our desired, search - * done. - */ - if (nr_pages < want) - break; - - index = pvec.pages[i - 1]->index + 1; - pagevec_release(&pvec); - } while (index <= end); - - /* No page at lastoff and we are not done - we found a hole. */ - if (type == HOLE_OFF && lastoff < endoff) { - *offset = lastoff; - found = true; - } -out: - pagevec_release(&pvec); - return found; -} - -/* * caller must lock inode with xfs_ilock_data_map_shared, * can we craft an appropriate ASSERT? * @@ -1236,10 +1051,16 @@ __xfs_seek_hole_data( * for hole or data from page cache. */ if (map[i].br_state == XFS_EXT_UNWRITTEN) { - if (xfs_find_get_desired_pgoff(inode, &map[i], - whence == SEEK_HOLE ? HOLE_OFF : DATA_OFF, - &offset)) + xfs_fileoff_t endoff = map[i].br_startoff + + map[i].br_blockcount; + loff_t length = XFS_FSB_TO_B(mp, endoff) - offset; + loff_t ret; + + ret = page_cache_seek_hole_data(inode, offset, length, whence); + if (ret >= 0) { + offset = ret; goto out; + } } } -- 2.7.5
WARNING: multiple messages have this Message-ID (diff)
From: Andreas Gruenbacher <agruenba@redhat.com> To: linux-fsdevel@vger.kernel.org Cc: Andreas Gruenbacher <agruenba@redhat.com>, linux-xfs@vger.kernel.org, linux-ext4@vger.kernel.org Subject: [PATCH v3 3/5] xfs: Switch to page_cache_seek_hole_data Date: Mon, 26 Jun 2017 16:25:16 +0200 [thread overview] Message-ID: <1498487118-6564-4-git-send-email-agruenba@redhat.com> (raw) In-Reply-To: <1498487118-6564-1-git-send-email-agruenba@redhat.com> Fix SEEK_HOLE / SEEK_DATA when a page fits more than two filesystem blocks (see the commit that introduces page_cache_seek_hole_data). Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> --- fs/xfs/xfs_file.c | 197 +++--------------------------------------------------- 1 file changed, 9 insertions(+), 188 deletions(-) diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 86701a0..962dafd 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -981,191 +981,6 @@ xfs_file_readdir( } /* - * This type is designed to indicate the type of offset we would like - * to search from page cache for xfs_seek_hole_data(). - */ -enum { - HOLE_OFF = 0, - DATA_OFF, -}; - -/* - * Lookup the desired type of offset from the given page. - * - * On success, return true and the offset argument will point to the - * start of the region that was found. Otherwise this function will - * return false and keep the offset argument unchanged. - */ -STATIC bool -xfs_lookup_buffer_offset( - struct page *page, - loff_t *offset, - unsigned int type) -{ - loff_t lastoff = page_offset(page); - bool found = false; - struct buffer_head *bh, *head; - - bh = head = page_buffers(page); - do { - /* - * Unwritten extents that have data in the page - * cache covering them can be identified by the - * BH_Unwritten state flag. Pages with multiple - * buffers might have a mix of holes, data and - * unwritten extents - any buffer with valid - * data in it should have BH_Uptodate flag set - * on it. - */ - if (buffer_unwritten(bh) || - buffer_uptodate(bh)) { - if (type == DATA_OFF) - found = true; - } else { - if (type == HOLE_OFF) - found = true; - } - - if (found) { - *offset = lastoff; - break; - } - lastoff += bh->b_size; - } while ((bh = bh->b_this_page) != head); - - return found; -} - -/* - * This routine is called to find out and return a data or hole offset - * from the page cache for unwritten extents according to the desired - * type for xfs_seek_hole_data(). - * - * The argument offset is used to tell where we start to search from the - * page cache. Map is used to figure out the end points of the range to - * lookup pages. - * - * Return true if the desired type of offset was found, and the argument - * offset is filled with that address. Otherwise, return false and keep - * offset unchanged. - */ -STATIC bool -xfs_find_get_desired_pgoff( - struct inode *inode, - struct xfs_bmbt_irec *map, - unsigned int type, - loff_t *offset) -{ - struct xfs_inode *ip = XFS_I(inode); - struct xfs_mount *mp = ip->i_mount; - struct pagevec pvec; - pgoff_t index; - pgoff_t end; - loff_t endoff; - loff_t startoff = *offset; - loff_t lastoff = startoff; - bool found = false; - - pagevec_init(&pvec, 0); - - index = startoff >> PAGE_SHIFT; - endoff = XFS_FSB_TO_B(mp, map->br_startoff + map->br_blockcount); - end = (endoff - 1) >> PAGE_SHIFT; - do { - int want; - unsigned nr_pages; - unsigned int i; - - want = min_t(pgoff_t, end - index, PAGEVEC_SIZE - 1) + 1; - nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index, - want); - if (nr_pages == 0) - break; - - for (i = 0; i < nr_pages; i++) { - struct page *page = pvec.pages[i]; - loff_t b_offset; - - /* - * At this point, the page may be truncated or - * invalidated (changing page->mapping to NULL), - * or even swizzled back from swapper_space to tmpfs - * file mapping. However, page->index will not change - * because we have a reference on the page. - * - * If current page offset is beyond where we've ended, - * we've found a hole. - */ - if (type == HOLE_OFF && lastoff < endoff && - lastoff < page_offset(pvec.pages[i])) { - found = true; - *offset = lastoff; - goto out; - } - /* Searching done if the page index is out of range. */ - if (page->index > end) - goto out; - - lock_page(page); - /* - * Page truncated or invalidated(page->mapping == NULL). - * We can freely skip it and proceed to check the next - * page. - */ - if (unlikely(page->mapping != inode->i_mapping)) { - unlock_page(page); - continue; - } - - if (!page_has_buffers(page)) { - unlock_page(page); - continue; - } - - found = xfs_lookup_buffer_offset(page, &b_offset, type); - if (found) { - /* - * The found offset may be less than the start - * point to search if this is the first time to - * come here. - */ - *offset = max_t(loff_t, startoff, b_offset); - unlock_page(page); - goto out; - } - - /* - * We either searching data but nothing was found, or - * searching hole but found a data buffer. In either - * case, probably the next page contains the desired - * things, update the last offset to it so. - */ - lastoff = page_offset(page) + PAGE_SIZE; - unlock_page(page); - } - - /* - * The number of returned pages less than our desired, search - * done. - */ - if (nr_pages < want) - break; - - index = pvec.pages[i - 1]->index + 1; - pagevec_release(&pvec); - } while (index <= end); - - /* No page at lastoff and we are not done - we found a hole. */ - if (type == HOLE_OFF && lastoff < endoff) { - *offset = lastoff; - found = true; - } -out: - pagevec_release(&pvec); - return found; -}
next prev parent reply other threads:[~2017-06-26 14:25 UTC|newest] Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top 2017-06-26 14:25 [PATCH v3 0/5] lseek SEEK_HOLE / SEEK_DATA fix and switch to iomap Andreas Gruenbacher 2017-06-26 14:25 ` [PATCH v3 1/5] vfs: Add page_cache_seek_hole_data helper Andreas Gruenbacher 2017-06-26 14:25 ` [PATCH v3 2/5] ext4: Switch to page_cache_seek_hole_data Andreas Gruenbacher 2017-06-26 14:25 ` Andreas Gruenbacher [this message] 2017-06-26 14:25 ` [PATCH v3 3/5] xfs: " Andreas Gruenbacher 2017-06-26 14:25 ` [PATCH v3 4/5] vfs: Add iomap_seek_hole_data helper Andreas Gruenbacher 2017-06-26 14:25 ` [PATCH v3 5/5] xfs: Switch to iomap for SEEK_HOLE / SEEK_DATA Andreas Gruenbacher 2017-06-26 14:25 ` Andreas Gruenbacher 2017-06-27 0:34 ` Darrick J. Wong 2017-06-27 12:56 ` Dave Chinner
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=1498487118-6564-4-git-send-email-agruenba@redhat.com \ --to=agruenba@redhat.com \ --cc=linux-ext4@vger.kernel.org \ --cc=linux-fsdevel@vger.kernel.org \ --cc=linux-xfs@vger.kernel.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.