Received: by 2002:a25:c593:0:0:0:0:0 with SMTP id v141csp4156046ybe; Mon, 9 Sep 2019 05:15:22 -0700 (PDT) X-Google-Smtp-Source: APXvYqxWrOXwaaWOUJ78hRpeyEhmlBR9H3X545Hc3CfcxU/h2AEDZlskpj9xw0kscNDtbtddBGJg X-Received: by 2002:aa7:d488:: with SMTP id b8mr18732826edr.90.1568031322158; Mon, 09 Sep 2019 05:15:22 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1568031322; cv=none; d=google.com; s=arc-20160816; b=08UO9zSxKY3BSbY8jLwO9ly8d7zI7IgDMmaN9tdDYYxYlpX/6tWg8pHrcB2+LZ2gq1 PqmglepYIAIPlITGnjkk3UBXHfEdd/2h6oqyyP7ekCouvSbHBHYfg3T7jfVl/282yQFk qWvFP33c28ktIIM+Pys9TEDGv6WZudVrCWBcIbsDhzUbCiRYDOk8NVb+rk9fw7kiWPWJ oySQ4Azq0fswUMAOMagsPdri2k8hi3Qs8SJBQWKhLGQPaQoKWbsq6fGHbkYFs4965FYb 51ZlX5dyGfTpVmoO4NXj9FH2QMTCJWZWF2cgIikJPpwUWbznScGl/po+04tHiaClp5NY Yv9Q== 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 :user-agent:references:in-reply-to:message-id:date:subject:cc:to :from:dkim-signature; bh=kbEcv9CC1+ZEqamNH5DopvQU5vXVSMxCLZmADUjy5gs=; b=ZggPlWOsD1FmMJ7SQE8phiCnzqU+XudISI+xP1t4LmGWVC7ItaVvn54MXolJ0CWTec eyYWYAlfmIbGhxxAoF+alB+fSXSdXm6Mf6r9GAJWfZJgyCoMvnkSiD2pLDjFdiX6mu+6 0gv3K5ceVg215ZFKMsM7zhL0JViaPVxwv9y1kescdQEX/PZsCUCkLXI2DR5QRrIyKlJm G3SuVZQ6jFZcKfBl3VWc8CaueM+bAgoXP/5J4N9SMKRGcvJyOWCYwNRxzcVuQukBmNy5 JvysRtTtTe+eG5Wla6ee19uLN7FKZK9Pc+5JIQE8ClEQ0gz0K1pVqLqbKJpDl9mS0wvW MH9Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=1cu7xGIq; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id d11si6819626edh.407.2019.09.09.05.14.57; Mon, 09 Sep 2019 05:15:22 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=1cu7xGIq; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2387544AbfIHMxP (ORCPT + 99 others); Sun, 8 Sep 2019 08:53:15 -0400 Received: from mail.kernel.org ([198.145.29.99]:46066 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2387515AbfIHMxO (ORCPT ); Sun, 8 Sep 2019 08:53:14 -0400 Received: from localhost (unknown [62.28.240.114]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 1AA7821924; Sun, 8 Sep 2019 12:53:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1567947193; bh=li+mlqNCT+zPV19GtP8i7kOq2Zs5YM2+qdvcWNoWoNI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=1cu7xGIq1tmcm56Y3/EEHRMwhFS29ff/v7Vqix3IM6w7XP8DPy2230DhRUVK9j+0Y cB29BykmdMhk/ZC9ADV4mjjO2o+6b6C8QS9uppEds0VSD2/7FMC8JuCHJFPtl7O8Db ynscghZAk30i9kYK7agQ4gfgYThYNUnbG/HvJka0= From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, "Darrick J. Wong" , Bill ODonnell , "Matthew Wilcox (Oracle)" , Sasha Levin Subject: [PATCH 5.2 56/94] vfs: fix page locking deadlocks when deduping files Date: Sun, 8 Sep 2019 13:41:52 +0100 Message-Id: <20190908121152.039054428@linuxfoundation.org> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20190908121150.420989666@linuxfoundation.org> References: <20190908121150.420989666@linuxfoundation.org> User-Agent: quilt/0.66 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org [ Upstream commit edc58dd0123b552453a74369bd0c8d890b497b4b ] When dedupe wants to use the page cache to compare parts of two files for dedupe, we must be very careful to handle locking correctly. The current code doesn't do this. It must lock and unlock the page only once if the two pages are the same, since the overlapping range check doesn't catch this when blocksize < pagesize. If the pages are distinct but from the same file, we must observe page locking order and lock them in order of increasing offset to avoid clashing with writeback locking. Fixes: 876bec6f9bbfcb3 ("vfs: refactor clone/dedupe_file_range common functions") Signed-off-by: Darrick J. Wong Reviewed-by: Bill O'Donnell Reviewed-by: Matthew Wilcox (Oracle) Signed-off-by: Sasha Levin --- fs/read_write.c | 49 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/fs/read_write.c b/fs/read_write.c index c543d965e2880..e8b0f1192a3a4 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -1776,10 +1776,7 @@ static int generic_remap_check_len(struct inode *inode_in, return (remap_flags & REMAP_FILE_DEDUP) ? -EBADE : -EINVAL; } -/* - * Read a page's worth of file data into the page cache. Return the page - * locked. - */ +/* Read a page's worth of file data into the page cache. */ static struct page *vfs_dedupe_get_page(struct inode *inode, loff_t offset) { struct page *page; @@ -1791,10 +1788,32 @@ static struct page *vfs_dedupe_get_page(struct inode *inode, loff_t offset) put_page(page); return ERR_PTR(-EIO); } - lock_page(page); return page; } +/* + * Lock two pages, ensuring that we lock in offset order if the pages are from + * the same file. + */ +static void vfs_lock_two_pages(struct page *page1, struct page *page2) +{ + /* Always lock in order of increasing index. */ + if (page1->index > page2->index) + swap(page1, page2); + + lock_page(page1); + if (page1 != page2) + lock_page(page2); +} + +/* Unlock two pages, being careful not to unlock the same page twice. */ +static void vfs_unlock_two_pages(struct page *page1, struct page *page2) +{ + unlock_page(page1); + if (page1 != page2) + unlock_page(page2); +} + /* * Compare extents of two files to see if they are the same. * Caller must have locked both inodes to prevent write races. @@ -1832,10 +1851,24 @@ static int vfs_dedupe_file_range_compare(struct inode *src, loff_t srcoff, dest_page = vfs_dedupe_get_page(dest, destoff); if (IS_ERR(dest_page)) { error = PTR_ERR(dest_page); - unlock_page(src_page); put_page(src_page); goto out_error; } + + vfs_lock_two_pages(src_page, dest_page); + + /* + * Now that we've locked both pages, make sure they're still + * mapped to the file data we're interested in. If not, + * someone is invalidating pages on us and we lose. + */ + if (!PageUptodate(src_page) || !PageUptodate(dest_page) || + src_page->mapping != src->i_mapping || + dest_page->mapping != dest->i_mapping) { + same = false; + goto unlock; + } + src_addr = kmap_atomic(src_page); dest_addr = kmap_atomic(dest_page); @@ -1847,8 +1880,8 @@ static int vfs_dedupe_file_range_compare(struct inode *src, loff_t srcoff, kunmap_atomic(dest_addr); kunmap_atomic(src_addr); - unlock_page(dest_page); - unlock_page(src_page); +unlock: + vfs_unlock_two_pages(src_page, dest_page); put_page(dest_page); put_page(src_page); -- 2.20.1