Received: by 2002:a25:8b12:0:0:0:0:0 with SMTP id i18csp2672393ybl; Thu, 29 Aug 2019 11:16:20 -0700 (PDT) X-Google-Smtp-Source: APXvYqwE/KybxLezqQgIBAfXVrcKwjxpRCMx3Xww3/j3GGF6uwbI2S/R7qAmFGFVXnURd/cesag0 X-Received: by 2002:a63:9e56:: with SMTP id r22mr9541307pgo.221.1567102579941; Thu, 29 Aug 2019 11:16:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1567102579; cv=none; d=google.com; s=arc-20160816; b=TXFSG5FHJWry2T/FUsJXxzvKQi6VUZa59Sy14MuzVDuxzr6xKfOsY51daLQXX9dwpK XSxSEqA/sa6mrA5RI6UVnPsAOqsouIqGSTPHfDE0pid4OiFQqvoqgI58L3eRn/VhM+AO OiHGvIPSZU8aSZBvHgEMocmuZOix5DquLZX15MUQ6ya0F4DG1Fl78q1Vapmb46y61szH gWeMoYdLhtCYgpaHtnqCWocuQcYRbM5YtpFY0oJStk80kVnhLmoEa3sST9wKVhCCXe8Y M1L3vUbg0gMGbUO/ismtRbeHV5aX7Fvo28gfZLgJz1abEfgBjCK1X1iF+U6XvWuhFiR7 I1zg== 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=f/mugSFcGhppq8kPsP4WkEHuND8vdQfMAp6YuV94eQc=; b=xQJ70lfs7nLTaKKNiv20MRi2vWnDI42IdJtRo8p7IZ61U/TcnVvUa3MAfkOZsWpYhX C1ilpStgDizLY2kadjOn+Uhtky1wjGqTml5wGLMmizhD20IgdAs91wLA7p6guOjZho7d bWSRuien1aA3s4m9d7iRpRc+Wl92WNj8do60AWX7JFQ4e9Ykz8/gk1ijmseBDNsf05Cs rhz0S2tHowpKIwRJfIvYSny/ijjnDZGFPl0YGU4MDrhHc67QFA9Us5oX1NPLheNEH6yC 25CcN1cawaar+aDP3ohqf0bODasoogwrFaE1YVWiMJdcHVExWPTNX4bzTlqGrKuYAZs9 baTg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=default header.b=l92DR7+F; 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; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id cm7si2850784pjb.15.2019.08.29.11.16.04; Thu, 29 Aug 2019 11:16:19 -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=l92DR7+F; 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; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727992AbfH2SOl (ORCPT + 99 others); Thu, 29 Aug 2019 14:14:41 -0400 Received: from mail.kernel.org ([198.145.29.99]:56236 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728919AbfH2SOi (ORCPT ); Thu, 29 Aug 2019 14:14:38 -0400 Received: from sasha-vm.mshome.net (c-73-47-72-35.hsd1.nh.comcast.net [73.47.72.35]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id F05DB233FF; Thu, 29 Aug 2019 18:14:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1567102476; bh=FL8bHyiyQZxqHEAAaSx3IsT3DTGpqyV0KzVXHvH5kpo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=l92DR7+FdrrN+6t51yCPr5YDJXVh9e5cGEcKr4oDWn8KNOm3cVY2OZq1Lp7p+V4qe r1Ugsirr7Fq6l7E8xtIWsQjpiUNekhfKfcN8ftkahN4jibWfFdGQmdLq6QJP2YWP2h Ioig2zmKcWw/6Zu5yyGDP1DJ7ZvzZUYSDFOBvcfM= From: Sasha Levin To: linux-kernel@vger.kernel.org, stable@vger.kernel.org Cc: "Darrick J. Wong" , Bill O'Donnell , Matthew Wilcox , Sasha Levin , linux-fsdevel@vger.kernel.org Subject: [PATCH AUTOSEL 5.2 40/76] vfs: fix page locking deadlocks when deduping files Date: Thu, 29 Aug 2019 14:12:35 -0400 Message-Id: <20190829181311.7562-40-sashal@kernel.org> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20190829181311.7562-1-sashal@kernel.org> References: <20190829181311.7562-1-sashal@kernel.org> MIME-Version: 1.0 X-stable: review X-Patchwork-Hint: Ignore Content-Transfer-Encoding: 8bit Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: "Darrick J. Wong" [ 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