public inbox for stable@vger.kernel.org
 help / color / mirror / Atom feed
From: Sasha Levin <sashal@kernel.org>
To: linux-kernel@vger.kernel.org, stable@vger.kernel.org
Cc: Qu Wenruo <wqu@suse.com>, Martin Doucha <martin.doucha@suse.com>,
	Filipe Manana <fdmanana@suse.com>,
	Anand Jain <anand.jain@oracle.com>,
	David Sterba <dsterba@suse.com>, Sasha Levin <sashal@kernel.org>,
	linux-btrfs@vger.kernel.org
Subject: [PATCH AUTOSEL 5.7 54/60] btrfs: allow btrfs_truncate_block() to fallback to nocow for data space reservation
Date: Mon, 10 Aug 2020 15:10:22 -0400	[thread overview]
Message-ID: <20200810191028.3793884-54-sashal@kernel.org> (raw)
In-Reply-To: <20200810191028.3793884-1-sashal@kernel.org>

From: Qu Wenruo <wqu@suse.com>

[ Upstream commit 6d4572a9d71d5fc2affee0258d8582d39859188c ]

[BUG]
When the data space is exhausted, even if the inode has NOCOW attribute,
we will still refuse to truncate unaligned range due to ENOSPC.

The following script can reproduce it pretty easily:
  #!/bin/bash

  dev=/dev/test/test
  mnt=/mnt/btrfs

  umount $dev &> /dev/null
  umount $mnt &> /dev/null

  mkfs.btrfs -f $dev -b 1G
  mount -o nospace_cache $dev $mnt
  touch $mnt/foobar
  chattr +C $mnt/foobar

  xfs_io -f -c "pwrite -b 4k 0 4k" $mnt/foobar > /dev/null
  xfs_io -f -c "pwrite -b 4k 0 1G" $mnt/padding &> /dev/null
  sync

  xfs_io -c "fpunch 0 2k" $mnt/foobar
  umount $mnt

Currently this will fail at the fpunch part.

[CAUSE]
Because btrfs_truncate_block() always reserves space without checking
the NOCOW attribute.

Since the writeback path follows NOCOW bit, we only need to bother the
space reservation code in btrfs_truncate_block().

[FIX]
Make btrfs_truncate_block() follow btrfs_buffered_write() to try to
reserve data space first, and fall back to NOCOW check only when we
don't have enough space.

Such always-try-reserve is an optimization introduced in
btrfs_buffered_write(), to avoid expensive btrfs_check_can_nocow() call.

This patch will export check_can_nocow() as btrfs_check_can_nocow(), and
use it in btrfs_truncate_block() to fix the problem.

Reported-by: Martin Doucha <martin.doucha@suse.com>
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: Anand Jain <anand.jain@oracle.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
 fs/btrfs/ctree.h |  2 ++
 fs/btrfs/file.c  | 12 ++++++------
 fs/btrfs/inode.c | 44 +++++++++++++++++++++++++++++++++++++-------
 3 files changed, 45 insertions(+), 13 deletions(-)

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 09e6dff8a8f85..68bd89e3d4f09 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -2982,6 +2982,8 @@ int btrfs_dirty_pages(struct inode *inode, struct page **pages,
 		      size_t num_pages, loff_t pos, size_t write_bytes,
 		      struct extent_state **cached);
 int btrfs_fdatawrite_range(struct inode *inode, loff_t start, loff_t end);
+int btrfs_check_can_nocow(struct btrfs_inode *inode, loff_t pos,
+			  size_t *write_bytes, bool nowait);
 
 /* tree-defrag.c */
 int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index 93244934d4f92..1e1af0ce70771 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -1540,8 +1540,8 @@ lock_and_cleanup_extent_if_need(struct btrfs_inode *inode, struct page **pages,
 	return ret;
 }
 
-static noinline int check_can_nocow(struct btrfs_inode *inode, loff_t pos,
-				    size_t *write_bytes, bool nowait)
+int btrfs_check_can_nocow(struct btrfs_inode *inode, loff_t pos,
+			  size_t *write_bytes, bool nowait)
 {
 	struct btrfs_fs_info *fs_info = inode->root->fs_info;
 	struct btrfs_root *root = inode->root;
@@ -1656,8 +1656,8 @@ static noinline ssize_t btrfs_buffered_write(struct kiocb *iocb,
 		if (ret < 0) {
 			if ((BTRFS_I(inode)->flags & (BTRFS_INODE_NODATACOW |
 						      BTRFS_INODE_PREALLOC)) &&
-			    check_can_nocow(BTRFS_I(inode), pos,
-					    &write_bytes, false) > 0) {
+			    btrfs_check_can_nocow(BTRFS_I(inode), pos,
+						  &write_bytes, false) > 0) {
 				/*
 				 * For nodata cow case, no need to reserve
 				 * data space.
@@ -1936,8 +1936,8 @@ static ssize_t btrfs_file_write_iter(struct kiocb *iocb,
 		 */
 		if (!(BTRFS_I(inode)->flags & (BTRFS_INODE_NODATACOW |
 					      BTRFS_INODE_PREALLOC)) ||
-		    check_can_nocow(BTRFS_I(inode), pos, &nocow_bytes,
-				    true) <= 0) {
+		    btrfs_check_can_nocow(BTRFS_I(inode), pos, &nocow_bytes,
+					  true) <= 0) {
 			inode_unlock(inode);
 			return -EAGAIN;
 		}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index e7bdda3ed069b..6cb3dc2748974 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4520,11 +4520,13 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len,
 	struct extent_state *cached_state = NULL;
 	struct extent_changeset *data_reserved = NULL;
 	char *kaddr;
+	bool only_release_metadata = false;
 	u32 blocksize = fs_info->sectorsize;
 	pgoff_t index = from >> PAGE_SHIFT;
 	unsigned offset = from & (blocksize - 1);
 	struct page *page;
 	gfp_t mask = btrfs_alloc_write_mask(mapping);
+	size_t write_bytes = blocksize;
 	int ret = 0;
 	u64 block_start;
 	u64 block_end;
@@ -4536,11 +4538,27 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len,
 	block_start = round_down(from, blocksize);
 	block_end = block_start + blocksize - 1;
 
-	ret = btrfs_delalloc_reserve_space(inode, &data_reserved,
-					   block_start, blocksize);
-	if (ret)
-		goto out;
 
+	ret = btrfs_check_data_free_space(inode, &data_reserved, block_start,
+					  blocksize);
+	if (ret < 0) {
+		if ((BTRFS_I(inode)->flags & (BTRFS_INODE_NODATACOW |
+					      BTRFS_INODE_PREALLOC)) &&
+		    btrfs_check_can_nocow(BTRFS_I(inode), block_start,
+					  &write_bytes, false) > 0) {
+			/* For nocow case, no need to reserve data space */
+			only_release_metadata = true;
+		} else {
+			goto out;
+		}
+	}
+	ret = btrfs_delalloc_reserve_metadata(BTRFS_I(inode), blocksize);
+	if (ret < 0) {
+		if (!only_release_metadata)
+			btrfs_free_reserved_data_space(inode, data_reserved,
+					block_start, blocksize);
+		goto out;
+	}
 again:
 	page = find_or_create_page(mapping, index, mask);
 	if (!page) {
@@ -4609,14 +4627,26 @@ int btrfs_truncate_block(struct inode *inode, loff_t from, loff_t len,
 	set_page_dirty(page);
 	unlock_extent_cached(io_tree, block_start, block_end, &cached_state);
 
+	if (only_release_metadata)
+		set_extent_bit(&BTRFS_I(inode)->io_tree, block_start,
+				block_end, EXTENT_NORESERVE, NULL, NULL,
+				GFP_NOFS);
+
 out_unlock:
-	if (ret)
-		btrfs_delalloc_release_space(inode, data_reserved, block_start,
-					     blocksize, true);
+	if (ret) {
+		if (only_release_metadata)
+			btrfs_delalloc_release_metadata(BTRFS_I(inode),
+					blocksize, true);
+		else
+			btrfs_delalloc_release_space(inode, data_reserved,
+					block_start, blocksize, true);
+	}
 	btrfs_delalloc_release_extents(BTRFS_I(inode), blocksize);
 	unlock_page(page);
 	put_page(page);
 out:
+	if (only_release_metadata)
+		btrfs_drew_write_unlock(&BTRFS_I(inode)->root->snapshot_lock);
 	extent_changeset_free(data_reserved);
 	return ret;
 }
-- 
2.25.1


  parent reply	other threads:[~2020-08-10 19:26 UTC|newest]

Thread overview: 64+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-08-10 19:09 [PATCH AUTOSEL 5.7 01/60] drm/tilcdc: fix leak & null ref in panel_connector_get_modes Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 02/60] soc: qcom: rpmh-rsc: Set suppress_bind_attrs flag Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 03/60] net: mscc: ocelot: fix encoding destination ports into multicast IPv4 address Sasha Levin
2020-08-10 21:01   ` Vladimir Oltean
2020-08-11 16:42     ` Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 04/60] ARM: exynos: clear L310_AUX_CTRL_FULL_LINE_ZERO in default l2c_aux_val Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 05/60] Bluetooth: add a mutex lock to avoid UAF in do_enale_set Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 06/60] loop: be paranoid on exit and prevent new additions / removals Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 07/60] io_uring: fix req->work corruption Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 08/60] fs/btrfs: Add cond_resched() for try_release_extent_mapping() stalls Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 09/60] drm/amdgpu: avoid dereferencing a NULL pointer Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 10/60] drm/radeon: Fix reference count leaks caused by pm_runtime_get_sync Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 11/60] crypto: aesni - Fix build with LLVM_IAS=1 Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 12/60] video: fbdev: savage: fix memory leak on error handling path in probe Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 13/60] video: fbdev: neofb: fix memory leak in neo_scan_monitor() Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 14/60] bus: ti-sysc: Add missing quirk flags for usb_host_hs Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 15/60] md-cluster: fix wild pointer of unlock_all_bitmaps() Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 16/60] drm/nouveau/kms/nv50-: Fix disabling dithering Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 17/60] arm64: dts: hisilicon: hikey: fixes to comply with adi, adv7533 DT binding Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 18/60] drm/etnaviv: fix ref count leak via pm_runtime_get_sync Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 19/60] memory: samsung: exynos5422-dmc: Do not ignore return code of regmap_read() Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 20/60] drm/nouveau: fix reference count leak in nouveau_debugfs_strap_peek Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 21/60] drm/nouveau: fix multiple instances of reference count leaks Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 22/60] mmc: sdhci-cadence: do not use hardware tuning for SD mode Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 23/60] btrfs: fix lockdep splat from btrfs_dump_space_info Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 24/60] usb: mtu3: clear dual mode of u3port when disable device Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 25/60] drm: msm: a6xx: fix gpu failure after system resume Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 26/60] drm/msm: Fix a null pointer access in msm_gem_shrinker_count() Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 27/60] drm/debugfs: fix plain echo to connector "force" attribute Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 28/60] drm/radeon: disable AGP by default Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 29/60] net: phy: mscc: restore the base page in vsc8514/8584_config_init Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 30/60] irqchip/irq-mtk-sysirq: Replace spinlock with raw_spinlock Sasha Levin
2020-08-10 19:09 ` [PATCH AUTOSEL 5.7 31/60] mm/mmap.c: Add cond_resched() for exit_mmap() CPU stalls Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 32/60] drm/amd/display: Improve DisplayPort monitor interop Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 33/60] drm/amdgpu/display bail early in dm_pp_get_static_clocks Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 34/60] drm/amdgpu/display: properly guard the calls to swSMU functions Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 35/60] drm/amd/powerplay: fix compile error with ARCH=arc Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 36/60] bpf: Fix fds_example SIGSEGV error Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 37/60] Bluetooth: hci_qca: Bug fixes for SSR Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 38/60] brcmfmac: keep SDIO watchdog running when console_interval is non-zero Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 39/60] brcmfmac: To fix Bss Info flag definition Bug Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 40/60] brcmfmac: set state of hanger slot to FREE when flushing PSQ Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 41/60] platform/x86: asus-nb-wmi: add support for ASUS ROG Zephyrus G14 and G15 Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 42/60] iwlegacy: Check the return value of pcie_capability_read_*() Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 43/60] gpu: host1x: debug: Fix multiple channels emitting messages simultaneously Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 44/60] drm/amd/powerplay: suppress compile error around BUG_ON Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 45/60] ionic: update eid test for overflow Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 46/60] x86/uaccess: Make __get_user_size() Clang compliant on 32-bit Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 47/60] mmc: sdhci-pci-o2micro: Bug fix for O2 host controller Seabird1 Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 48/60] usb: gadget: net2280: fix memory leak on probe error handling paths Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 49/60] bdc: Fix bug causing crash after multiple disconnects Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 50/60] usb: bdc: Halt controller on suspend Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 51/60] dyndbg: fix a BUG_ON in ddebug_describe_flags Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 52/60] dyndbg: prefer declarative init in caller, to memset in callee Sasha Levin
2020-08-11  5:13   ` Greg Kroah-Hartman
2020-08-15 23:07     ` Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 53/60] bcache: fix super block seq numbers comparision in register_cache_set() Sasha Levin
2020-08-10 19:10 ` Sasha Levin [this message]
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 55/60] btrfs: qgroup: free per-trans reserved space when a subvolume gets dropped Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 56/60] ACPICA: Do not increment operation_region reference counts for field units Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 57/60] io_uring: fix racy overflow count reporting Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 58/60] io_uring: fix stalled deferred requests Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 59/60] crypto: caam - silence .setkey in case of bad key length Sasha Levin
2020-08-10 19:10 ` [PATCH AUTOSEL 5.7 60/60] drm/msm: ratelimit crtc event overflow error Sasha Levin

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=20200810191028.3793884-54-sashal@kernel.org \
    --to=sashal@kernel.org \
    --cc=anand.jain@oracle.com \
    --cc=dsterba@suse.com \
    --cc=fdmanana@suse.com \
    --cc=linux-btrfs@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=martin.doucha@suse.com \
    --cc=stable@vger.kernel.org \
    --cc=wqu@suse.com \
    /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: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox