From patchwork Fri Jun 23 00:19:32 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Gruenbacher X-Patchwork-Id: 779743 Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3wtzcN5TsTz9s9Y for ; Fri, 23 Jun 2017 10:20:20 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754023AbdFWATj (ORCPT ); Thu, 22 Jun 2017 20:19:39 -0400 Received: from mx1.redhat.com ([209.132.183.28]:54470 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753913AbdFWATh (ORCPT ); Thu, 22 Jun 2017 20:19:37 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 5E48D85A05; Fri, 23 Jun 2017 00:19:37 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 5E48D85A05 Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com Authentication-Results: ext-mx02.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=agruenba@redhat.com DKIM-Filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 5E48D85A05 Received: from nux.redhat.com (ovpn-116-35.ams2.redhat.com [10.36.116.35]) by smtp.corp.redhat.com (Postfix) with ESMTP id EAC8F7E5DF; Fri, 23 Jun 2017 00:19:35 +0000 (UTC) From: Andreas Gruenbacher To: linux-fsdevel@vger.kernel.org Cc: Andreas Gruenbacher , linux-ext4@vger.kernel.org, linux-xfs@vger.kernel.org, Jan Kara Subject: [PATCH 2/3] ext4: Switch to page_cache_seek_hole_data Date: Fri, 23 Jun 2017 02:19:32 +0200 Message-Id: <1498177173-30798-2-git-send-email-agruenba@redhat.com> In-Reply-To: <1498177173-30798-1-git-send-email-agruenba@redhat.com> References: <1498177173-30798-1-git-send-email-agruenba@redhat.com> X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Fri, 23 Jun 2017 00:19:37 +0000 (UTC) Sender: linux-ext4-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-ext4@vger.kernel.org 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 --- fs/ext4/file.c | 144 ++++++--------------------------------------------------- 1 file changed, 13 insertions(+), 131 deletions(-) diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 02ce7e7..d0b0862 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -439,129 +439,6 @@ static int ext4_file_open(struct inode * inode, struct file * filp) } /* - * Here we use ext4_map_blocks() to get a block mapping for a extent-based - * file rather than ext4_ext_walk_space() because we can introduce - * SEEK_DATA/SEEK_HOLE for block-mapped and extent-mapped file at the same - * function. When extent status tree has been fully implemented, it will - * track all extent status for a file and we can directly use it to - * retrieve the offset for SEEK_DATA/SEEK_HOLE. - */ - -/* - * When we retrieve the offset for SEEK_DATA/SEEK_HOLE, we would need to - * lookup page cache to check whether or not there has some data between - * [startoff, endoff] because, if this range contains an unwritten extent, - * we determine this extent as a data or a hole according to whether the - * page cache has data or not. - */ -static int ext4_find_unwritten_pgoff(struct inode *inode, - int whence, - ext4_lblk_t end_blk, - loff_t *offset) -{ - struct pagevec pvec; - unsigned int blkbits; - pgoff_t index; - pgoff_t end; - loff_t endoff; - loff_t startoff; - loff_t lastoff; - int found = 0; - - blkbits = inode->i_sb->s_blocksize_bits; - startoff = *offset; - lastoff = startoff; - endoff = (loff_t)end_blk << blkbits; - - index = startoff >> PAGE_SHIFT; - end = (endoff - 1) >> PAGE_SHIFT; - - pagevec_init(&pvec, 0); - do { - int i, num; - unsigned long nr_pages; - - num = min_t(pgoff_t, end - index, PAGEVEC_SIZE - 1) + 1; - nr_pages = pagevec_lookup(&pvec, inode->i_mapping, index, - (pgoff_t)num); - if (nr_pages == 0) - break; - - for (i = 0; i < nr_pages; i++) { - struct page *page = pvec.pages[i]; - struct buffer_head *bh, *head; - - /* - * If current offset is smaller than the page offset, - * there is a hole at this offset. - */ - if (whence == SEEK_HOLE && lastoff < endoff && - lastoff < page_offset(pvec.pages[i])) { - found = 1; - *offset = lastoff; - goto out; - } - - if (page->index > end) - goto out; - - lock_page(page); - - if (unlikely(page->mapping != inode->i_mapping)) { - unlock_page(page); - continue; - } - - if (!page_has_buffers(page)) { - unlock_page(page); - continue; - } - - if (page_has_buffers(page)) { - lastoff = page_offset(page); - bh = head = page_buffers(page); - do { - if (buffer_uptodate(bh) || - buffer_unwritten(bh)) { - if (whence == SEEK_DATA) - found = 1; - } else { - if (whence == SEEK_HOLE) - found = 1; - } - if (found) { - *offset = max_t(loff_t, - startoff, lastoff); - unlock_page(page); - goto out; - } - lastoff += bh->b_size; - bh = bh->b_this_page; - } while (bh != head); - } - - lastoff = page_offset(page) + PAGE_SIZE; - unlock_page(page); - } - - /* The no. of pages is less than our desired, we are done. */ - if (nr_pages < num) - break; - - index = pvec.pages[i - 1]->index + 1; - pagevec_release(&pvec); - } while (index <= end); - - if (whence == SEEK_HOLE && lastoff < endoff) { - found = 1; - *offset = lastoff; - } -out: - pagevec_release(&pvec); - return found; -} - -/* * ext4_seek_data() retrieves the offset for SEEK_DATA. */ static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize) @@ -569,7 +446,7 @@ static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize) struct inode *inode = file->f_mapping->host; struct extent_status es; ext4_lblk_t start, last, end; - loff_t dataoff, isize; + loff_t dataoff, length, isize; int blkbits; int ret; @@ -608,8 +485,10 @@ static loff_t ext4_seek_data(struct file *file, loff_t offset, loff_t maxsize) * it will be as a data or a hole according to page * cache that has data or not. */ - if (ext4_find_unwritten_pgoff(inode, SEEK_DATA, - es.es_lblk + es.es_len, &dataoff)) + length = ((loff_t)(last + es.es_len) << blkbits) - dataoff; + dataoff = page_cache_seek_hole_data(inode, dataoff, + length, SEEK_DATA); + if (dataoff >= 0) break; last += es.es_len; dataoff = (loff_t)last << blkbits; @@ -632,7 +511,7 @@ static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize) struct inode *inode = file->f_mapping->host; struct extent_status es; ext4_lblk_t start, last, end; - loff_t holeoff, isize; + loff_t holeoff, length, isize; int blkbits; int ret; @@ -667,10 +546,13 @@ static loff_t ext4_seek_hole(struct file *file, loff_t offset, loff_t maxsize) * it will be as a data or a hole according to page * cache that has data or not. */ - if (ext4_es_is_unwritten(&es) && - ext4_find_unwritten_pgoff(inode, SEEK_HOLE, - last + es.es_len, &holeoff)) - break; + if (ext4_es_is_unwritten(&es)) { + length = ((loff_t)(last + es.es_len) << blkbits) - holeoff; + holeoff = page_cache_seek_hole_data(inode, holeoff, + length, SEEK_HOLE); + if (holeoff >= 0) + break; + } last += es.es_len; holeoff = (loff_t)last << blkbits;