Received: by 2002:a05:6a10:22f:0:0:0:0 with SMTP id 15csp2153495pxk; Mon, 14 Sep 2020 06:13:23 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxC5776W2yyvAwDSswda4uY6Tu0gMaNMnMnaAKc8u9SSrg+LuXoPWZYiBq+3HZeEGQKjaXx X-Received: by 2002:a17:906:4a19:: with SMTP id w25mr14094563eju.199.1600089203092; Mon, 14 Sep 2020 06:13:23 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1600089203; cv=none; d=google.com; s=arc-20160816; b=uKHDNYCaCZUhLGPma7PtphToa+vY2AFFhh2wE7O1HoVz+wtbk3FZeHWFivGYSzkgXR WcUZqaQyCR130n8jNBGEO8tGya++K7tuRV2oX4W+99Oe5gjgAEht2CYYk4tJx6nBqPU+ 7qzIKVvIBJRueK149xfjwHBChBL+BYNK0mYXaIjHy8xR+yTsDCBEiTp9I5YtZRizxGhm wMWDM/zJ/NrnQFpYV+PGkRZ09n2s9/5SUaT+BowtReUo1AcUMg9CI9VrVE3RfSb23Fwf zUT738xSFMaor2K6psidqJNhOAd4pWdZl6an+HMCVGRvH7m0kLijfr8vQN+K+zOAFzBH DiZw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=TKHzlSAHsh+0tFbmwr75UlNxN+0hvM8uw8TZwi68h9M=; b=dsVoeU01vtwwW1DSe84D4Iss3C1j2xk/k3wOYbH9grixupIZjzGwxECfycjE/PNTut rU5ahQi+8pOwSKIEqvSWXJcPwikZEzVTUH+zDA+3hMlRr1NoKyyoEJzvAKab5VazlfXR 17S1kBXiicl6HfffNTtlP0ieGUgU3nTSQ6Q3p4O6YiVWQ14e8id91RtHZo2n5KkN7WpV /uiwcBaE55aPTTsVOwSy54p9mnsbVmQ0EtDClWq1mEWFpvVK6TZjx9B8dp9uTZn3Qcw7 +5Lic/6p3cgR/qYegDu1kw6ERyxnpULRP3jaLbrewXiNdfUk89quKi6chR2W+C8nUfB/ EBlg== ARC-Authentication-Results: i=1; mx.google.com; dkim=fail header.i=@infradead.org header.s=casper.20170209 header.b=YX+XddNl; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id b12si7218108edv.217.2020.09.14.06.13.00; Mon, 14 Sep 2020 06:13:23 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=fail header.i=@infradead.org header.s=casper.20170209 header.b=YX+XddNl; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726669AbgINNJ2 (ORCPT + 99 others); Mon, 14 Sep 2020 09:09:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:52518 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726540AbgINNBm (ORCPT ); Mon, 14 Sep 2020 09:01:42 -0400 Received: from casper.infradead.org (casper.infradead.org [IPv6:2001:8b0:10b:1236::1]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 228BAC061356 for ; Mon, 14 Sep 2020 06:01:06 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=casper.20170209; h=Content-Transfer-Encoding:MIME-Version: References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To: Content-Type:Content-ID:Content-Description; bh=TKHzlSAHsh+0tFbmwr75UlNxN+0hvM8uw8TZwi68h9M=; b=YX+XddNl2Lo5jKdpeBESzmmj/4 2HImZeBIF5e4ANG4GIiaeUO7SDfoVe4WZz7EVPEbZJavi12rAxpa+RsxqvnMj4/T+l787yFwf0soe mX0v/+mvEs7xmAnodpRrTYodecfgeWN1fWqKIsSYEY3vUOvS1BMULdFs0ZuHUpqxX/2vtWs7WA8tW gPxWOnY0fJVRs67fAgPCv0HpuJ9kTsyxzSe5sT7QZ4CNrlXxHvorkUwZuVuzabgVfQhNXDeOo5OuQ Cf76UyZAG/h1ikS+9OeEZntb4Q9Yj4cbYVAISSJzeyoz9sKvDTwTRyGjVMSisCqCVFk+tgNvvqPJc 0taPB2BQ==; Received: from willy by casper.infradead.org with local (Exim 4.92.3 #3 (Red Hat Linux)) id 1kHo69-0002zy-3C; Mon, 14 Sep 2020 13:00:45 +0000 From: "Matthew Wilcox (Oracle)" To: linux-mm@kvack.org Cc: "Matthew Wilcox (Oracle)" , Andrew Morton , Hugh Dickins , William Kucharski , Johannes Weiner , Jan Kara , Yang Shi , Dave Chinner , linux-kernel@vger.kernel.org Subject: [PATCH v2 04/12] mm/filemap: Add mapping_seek_hole_data Date: Mon, 14 Sep 2020 14:00:34 +0100 Message-Id: <20200914130042.11442-5-willy@infradead.org> X-Mailer: git-send-email 2.21.3 In-Reply-To: <20200914130042.11442-1-willy@infradead.org> References: <20200914130042.11442-1-willy@infradead.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Rewrite shmem_seek_hole_data() and move it to filemap.c. Signed-off-by: Matthew Wilcox (Oracle) --- include/linux/pagemap.h | 2 ++ mm/filemap.c | 68 ++++++++++++++++++++++++++++++++++++++ mm/shmem.c | 72 +++-------------------------------------- 3 files changed, 74 insertions(+), 68 deletions(-) diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h index 4e52a3ff92fb..869dc371b800 100644 --- a/include/linux/pagemap.h +++ b/include/linux/pagemap.h @@ -724,6 +724,8 @@ extern void __delete_from_page_cache(struct page *page, void *shadow); int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask); void delete_from_page_cache_batch(struct address_space *mapping, struct pagevec *pvec); +loff_t mapping_seek_hole_data(struct address_space *, loff_t start, loff_t end, + int whence); /* * Like add_to_page_cache_locked, but used to add newly allocated pages: diff --git a/mm/filemap.c b/mm/filemap.c index d8f5ff07eb9c..5662f932b85b 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -2416,6 +2416,74 @@ generic_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) } EXPORT_SYMBOL(generic_file_read_iter); +static inline +unsigned int seek_page_size(struct xa_state *xas, struct page *page) +{ + if (xa_is_value(page)) + return PAGE_SIZE << xa_get_order(xas->xa, xas->xa_index); + return thp_size(page); +} + +/** + * mapping_seek_hole_data - Seek for SEEK_DATA / SEEK_HOLE in the page cache. + * @mapping: Address space to search. + * @start: First byte to consider. + * @end: Limit of search (exclusive). + * @whence: Either SEEK_HOLE or SEEK_DATA. + * + * If the page cache knows which blocks contain holes and which blocks + * contain data, your filesystem can use this function to implement + * SEEK_HOLE and SEEK_DATA. This is useful for filesystems which are + * entirely memory-based such as tmpfs, and filesystems which support + * unwritten extents. + * + * Return: The requested offset on successs, or -ENXIO if @whence specifies + * SEEK_DATA and there is no data after @start. There is an implicit hole + * after @end - 1, so SEEK_HOLE returns @end if all the bytes between @start + * and @end contain data. + */ +loff_t mapping_seek_hole_data(struct address_space *mapping, loff_t start, + loff_t end, int whence) +{ + XA_STATE(xas, &mapping->i_pages, start >> PAGE_SHIFT); + pgoff_t max = (end - 1) / PAGE_SIZE; + bool seek_data = (whence == SEEK_DATA); + struct page *page; + + if (end <= start) + return -ENXIO; + + rcu_read_lock(); + while ((page = xas_find_get_entry(&xas, max, XA_PRESENT))) { + loff_t pos = xas.xa_index * PAGE_SIZE; + + if (start < pos) { + if (!seek_data) + goto unlock; + start = pos; + } + + if (seek_data) + goto unlock; + + start = pos + seek_page_size(&xas, page); + } + rcu_read_unlock(); + + if (seek_data) + return -ENXIO; + goto out; + +unlock: + rcu_read_unlock(); + if (!xa_is_value(page)) + put_page(page); +out: + if (start > end) + return end; + return start; +} + #ifdef CONFIG_MMU #define MMAP_LOTSAMISS (100) /* diff --git a/mm/shmem.c b/mm/shmem.c index 108931a6cc43..b65263d9bb67 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -2659,85 +2659,21 @@ static ssize_t shmem_file_read_iter(struct kiocb *iocb, struct iov_iter *to) return retval ? retval : error; } -/* - * llseek SEEK_DATA or SEEK_HOLE through the page cache. - */ -static pgoff_t shmem_seek_hole_data(struct address_space *mapping, - pgoff_t index, pgoff_t end, int whence) -{ - struct page *page; - struct pagevec pvec; - pgoff_t indices[PAGEVEC_SIZE]; - bool done = false; - int i; - - pagevec_init(&pvec); - pvec.nr = 1; /* start small: we may be there already */ - while (!done) { - pvec.nr = find_get_entries(mapping, index, - pvec.nr, pvec.pages, indices); - if (!pvec.nr) { - if (whence == SEEK_DATA) - index = end; - break; - } - for (i = 0; i < pvec.nr; i++, index++) { - if (index < indices[i]) { - if (whence == SEEK_HOLE) { - done = true; - break; - } - index = indices[i]; - } - page = pvec.pages[i]; - if (page && !xa_is_value(page)) { - if (!PageUptodate(page)) - page = NULL; - } - if (index >= end || - (page && whence == SEEK_DATA) || - (!page && whence == SEEK_HOLE)) { - done = true; - break; - } - } - pagevec_remove_exceptionals(&pvec); - pagevec_release(&pvec); - pvec.nr = PAGEVEC_SIZE; - cond_resched(); - } - return index; -} - static loff_t shmem_file_llseek(struct file *file, loff_t offset, int whence) { struct address_space *mapping = file->f_mapping; struct inode *inode = mapping->host; - pgoff_t start, end; - loff_t new_offset; if (whence != SEEK_DATA && whence != SEEK_HOLE) return generic_file_llseek_size(file, offset, whence, MAX_LFS_FILESIZE, i_size_read(inode)); + if (offset < 0) + return -ENXIO; + inode_lock(inode); /* We're holding i_mutex so we can access i_size directly */ - if (offset < 0 || offset >= inode->i_size) - offset = -ENXIO; - else { - start = offset >> PAGE_SHIFT; - end = (inode->i_size + PAGE_SIZE - 1) >> PAGE_SHIFT; - new_offset = shmem_seek_hole_data(mapping, start, end, whence); - new_offset <<= PAGE_SHIFT; - if (new_offset > offset) { - if (new_offset < inode->i_size) - offset = new_offset; - else if (whence == SEEK_DATA) - offset = -ENXIO; - else - offset = inode->i_size; - } - } + offset = mapping_seek_hole_data(mapping, offset, inode->i_size, whence); if (offset >= 0) offset = vfs_setpos(file, offset, MAX_LFS_FILESIZE); -- 2.28.0