diff for duplicates of <20260917151607.082567058@linuxfoundation.org> diff --git a/a/1.txt b/N1/1.txt index f9a6c40..40d41a5 100644 --- a/a/1.txt +++ b/N1/1.txt @@ -1,47 +1,154 @@ -6.18-stable review patch. If anyone has any objections, please let me know. +6.12-stable review patch. If anyone has any objections, please let me know. ------------------ -From: Jia Jia <physicalmtea@gmail.com> +From: Qu Wenruo <wqu@suse.com> -[ Upstream commit 22598f55a4c2b510b3df5e69e563387a963222ae ] +[ Upstream commit 1e5773e0bab761c853eaf7286394905a544ef02d ] -vhost-scsi translates guest response descriptors into userspace iovecs -when commands are submitted. Target-core completes those commands -asynchronously, so VHOST_SET_MEM_TABLE can replace the memory table while -an in-flight command still retains response iovecs translated through the -old table. +The function btrfs_punch_hole_lock_range() needs to make sure there is +no other folio in the range, thus it goes with filemap_range_has_page(), +which works pretty fine. -If the old mapping is reused after VHOST_SET_MEM_TABLE returns, command -completion can write the response to an unrelated userspace object. +But if we have large folios, under the following case +filemap_range_has_page() will always return true, forcing +btrfs_punch_hole_lock_range() to do a very time consuming busy loop: -Flush the vhost-scsi backend after vhost_dev_ioctl() handles a device -ioctl. This waits for in-flight commands that can still use the old -response iovecs before the ioctl returns. + start end + | | + |//|//|//|//| | | | | | | | |//|//| + \ / \ / + Folio A Folio B -Signed-off-by: Jia Jia <physicalmtea@gmail.com> -Signed-off-by: Michael S. Tsirkin <mst@redhat.com> -Message-ID: <20260724060919.1569170-1-physicalmtea@gmail.com> +In the above case, folio A and B contain our start/end indexes, and there +are no other folios in the range. Thus we do not need to retry inside +btrfs_punch_hole_lock_range(). + +To prepare for large data folios, introduce a helper, +check_range_has_page(), which will: + +- Shrink the search range towards page boundaries + If the rounded down end (exclusive, otherwise it can underflow when @end + is inside the folio at file offset 0) is no larger than the rounded up + start, it means the range contains no other pages other than the ones + covering @start and @end. + + Can return false directly in that case. + +- Grab all the folios inside the range + +- Skip any large folios that cover the start and end indexes + +- If any other folios are found return true + +- Otherwise return false + +This new helper is going to handle both large folios and regular ones. + +Reviewed-by: Filipe Manana <fdmanana@suse.com> +Signed-off-by: Qu Wenruo <wqu@suse.com> +Signed-off-by: David Sterba <dsterba@suse.com> +Stable-dep-of: 3f950867c307 ("btrfs: fix extent map leak in NOCOW direct I/O write") Signed-off-by: Sasha Levin <sashal@kernel.org> +Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> --- - drivers/vhost/scsi.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c -index 29716ce714554..deb4c8bfd3d57 100644 ---- a/drivers/vhost/scsi.c -+++ b/drivers/vhost/scsi.c -@@ -2448,9 +2448,10 @@ vhost_scsi_ioctl(struct file *f, - default: - mutex_lock(&vs->dev.mutex); - r = vhost_dev_ioctl(&vs->dev, ioctl, argp); -- /* TODO: flush backend after dev ioctl. */ - if (r == -ENOIOCTLCMD) - r = vhost_vring_ioctl(&vs->dev, ioctl, argp); -+ else -+ vhost_scsi_flush(vs); - mutex_unlock(&vs->dev.mutex); - return r; - } --- -2.53.0 + fs/btrfs/file.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 56 insertions(+), 11 deletions(-) + +--- a/fs/btrfs/file.c ++++ b/fs/btrfs/file.c +@@ -2204,11 +2204,29 @@ static int find_first_non_hole(struct bt + return ret; + } + +-static void btrfs_punch_hole_lock_range(struct inode *inode, +- const u64 lockstart, +- const u64 lockend, +- struct extent_state **cached_state) ++/* ++ * Check if there is no folio in the range. ++ * ++ * We cannot utilize filemap_range_has_page() in a filemap with large folios ++ * as we can hit the following false positive: ++ * ++ * start end ++ * | | ++ * |//|//|//|//| | | | | | | | |//|//| ++ * \ / \ / ++ * Folio A Folio B ++ * ++ * That large folio A and B cover the start and end indexes. ++ * In that case filemap_range_has_page() will always return true, but the above ++ * case is fine for btrfs_punch_hole_lock_range() usage. ++ * ++ * So here we only ensure that no other folios is in the range, excluding the ++ * head/tail large folio. ++ */ ++static bool check_range_has_page(struct inode *inode, u64 start, u64 end) + { ++ struct folio_batch fbatch; ++ bool ret = false; + /* + * For subpage case, if the range is not at page boundary, we could + * have pages at the leading/tailing part of the range. +@@ -2219,17 +2237,45 @@ static void btrfs_punch_hole_lock_range( + * + * And do not decrease page_lockend right now, as it can be 0. + */ +- const u64 page_lockstart = round_up(lockstart, PAGE_SIZE); +- const u64 page_lockend = round_down(lockend + 1, PAGE_SIZE); ++ const u64 page_lockstart = round_up(start, PAGE_SIZE); ++ const u64 page_lockend = round_down(end + 1, PAGE_SIZE); ++ const pgoff_t start_index = page_lockstart >> PAGE_SHIFT; ++ const pgoff_t end_index = (page_lockend - 1) >> PAGE_SHIFT; ++ pgoff_t tmp = start_index; ++ int found_folios; ++ ++ /* The same page or adjacent pages. */ ++ if (page_lockend <= page_lockstart) ++ return false; + ++ folio_batch_init(&fbatch); ++ found_folios = filemap_get_folios(inode->i_mapping, &tmp, end_index, &fbatch); ++ for (int i = 0; i < found_folios; i++) { ++ struct folio *folio = fbatch.folios[i]; ++ ++ /* A large folio begins before the start. Not a target. */ ++ if (folio->index < start_index) ++ continue; ++ /* A large folio extends beyond the end. Not a target. */ ++ if (folio->index + folio_nr_pages(folio) > end_index) ++ continue; ++ /* A folio doesn't cover the head/tail index. Found a target. */ ++ ret = true; ++ break; ++ } ++ folio_batch_release(&fbatch); ++ return ret; ++} ++ ++static void btrfs_punch_hole_lock_range(struct inode *inode, ++ const u64 lockstart, const u64 lockend, ++ struct extent_state **cached_state) ++{ + while (1) { + truncate_pagecache_range(inode, lockstart, lockend); + + lock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend, + cached_state); +- /* The same page or adjacent pages. */ +- if (page_lockend <= page_lockstart) +- break; + /* + * We can't have ordered extents in the range, nor dirty/writeback + * pages, because we have locked the inode's VFS lock in exclusive +@@ -2240,8 +2286,7 @@ static void btrfs_punch_hole_lock_range( + * locking the range check if we have pages in the range, and if + * we do, unlock the range and retry. + */ +- if (!filemap_range_has_page(inode->i_mapping, page_lockstart, +- page_lockend - 1)) ++ if (!check_range_has_page(inode, lockstart, lockend)) + break; + + unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend, diff --git a/a/content_digest b/N1/content_digest index 1fc73d4..e5589ab 100644 --- a/a/content_digest +++ b/N1/content_digest @@ -1,61 +1,169 @@ - "ref\020260917151551.901433442@linuxfoundation.org\0" + "ref\020260917151539.408551884@linuxfoundation.org\0" "From\0Greg Kroah-Hartman <gregkh@linuxfoundation.org>\0" - "Subject\0[PATCH 6.18 0559/1250] vhost-scsi: flush backend after device ioctls\0" - "Date\0Thu, 17 Sep 2026 16:05:54 +0100\0" + "Subject\0[PATCH 6.12 1047/1102] btrfs: prepare btrfs_punch_hole_lock_range() for large data folios\0" + "Date\0Thu, 17 Sep 2026 16:16:30 +0100\0" "To\0stable@vger.kernel.org\0" "Cc\0Greg Kroah-Hartman <gregkh@linuxfoundation.org>" patches@lists.linux.dev - Jia Jia <physicalmtea@gmail.com> - Michael S. Tsirkin <mst@redhat.com> + Filipe Manana <fdmanana@suse.com> + Qu Wenruo <wqu@suse.com> + David Sterba <dsterba@suse.com> " Sasha Levin <sashal@kernel.org>\0" "\00:1\0" "b\0" - "6.18-stable review patch. If anyone has any objections, please let me know.\n" + "6.12-stable review patch. If anyone has any objections, please let me know.\n" "\n" "------------------\n" "\n" - "From: Jia Jia <physicalmtea@gmail.com>\n" + "From: Qu Wenruo <wqu@suse.com>\n" "\n" - "[ Upstream commit 22598f55a4c2b510b3df5e69e563387a963222ae ]\n" + "[ Upstream commit 1e5773e0bab761c853eaf7286394905a544ef02d ]\n" "\n" - "vhost-scsi translates guest response descriptors into userspace iovecs\n" - "when commands are submitted. Target-core completes those commands\n" - "asynchronously, so VHOST_SET_MEM_TABLE can replace the memory table while\n" - "an in-flight command still retains response iovecs translated through the\n" - "old table.\n" + "The function btrfs_punch_hole_lock_range() needs to make sure there is\n" + "no other folio in the range, thus it goes with filemap_range_has_page(),\n" + "which works pretty fine.\n" "\n" - "If the old mapping is reused after VHOST_SET_MEM_TABLE returns, command\n" - "completion can write the response to an unrelated userspace object.\n" + "But if we have large folios, under the following case\n" + "filemap_range_has_page() will always return true, forcing\n" + "btrfs_punch_hole_lock_range() to do a very time consuming busy loop:\n" "\n" - "Flush the vhost-scsi backend after vhost_dev_ioctl() handles a device\n" - "ioctl. This waits for in-flight commands that can still use the old\n" - "response iovecs before the ioctl returns.\n" + " start end\n" + " | |\n" + " |//|//|//|//| | | | | | | | |//|//|\n" + " \\ / \\ /\n" + " Folio A Folio B\n" "\n" - "Signed-off-by: Jia Jia <physicalmtea@gmail.com>\n" - "Signed-off-by: Michael S. Tsirkin <mst@redhat.com>\n" - "Message-ID: <20260724060919.1569170-1-physicalmtea@gmail.com>\n" + "In the above case, folio A and B contain our start/end indexes, and there\n" + "are no other folios in the range. Thus we do not need to retry inside\n" + "btrfs_punch_hole_lock_range().\n" + "\n" + "To prepare for large data folios, introduce a helper,\n" + "check_range_has_page(), which will:\n" + "\n" + "- Shrink the search range towards page boundaries\n" + " If the rounded down end (exclusive, otherwise it can underflow when @end\n" + " is inside the folio at file offset 0) is no larger than the rounded up\n" + " start, it means the range contains no other pages other than the ones\n" + " covering @start and @end.\n" + "\n" + " Can return false directly in that case.\n" + "\n" + "- Grab all the folios inside the range\n" + "\n" + "- Skip any large folios that cover the start and end indexes\n" + "\n" + "- If any other folios are found return true\n" + "\n" + "- Otherwise return false\n" + "\n" + "This new helper is going to handle both large folios and regular ones.\n" + "\n" + "Reviewed-by: Filipe Manana <fdmanana@suse.com>\n" + "Signed-off-by: Qu Wenruo <wqu@suse.com>\n" + "Signed-off-by: David Sterba <dsterba@suse.com>\n" + "Stable-dep-of: 3f950867c307 (\"btrfs: fix extent map leak in NOCOW direct I/O write\")\n" "Signed-off-by: Sasha Levin <sashal@kernel.org>\n" + "Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>\n" "---\n" - " drivers/vhost/scsi.c | 3 ++-\n" - " 1 file changed, 2 insertions(+), 1 deletion(-)\n" - "\n" - "diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c\n" - "index 29716ce714554..deb4c8bfd3d57 100644\n" - "--- a/drivers/vhost/scsi.c\n" - "+++ b/drivers/vhost/scsi.c\n" - "@@ -2448,9 +2448,10 @@ vhost_scsi_ioctl(struct file *f,\n" - " \tdefault:\n" - " \t\tmutex_lock(&vs->dev.mutex);\n" - " \t\tr = vhost_dev_ioctl(&vs->dev, ioctl, argp);\n" - "-\t\t/* TODO: flush backend after dev ioctl. */\n" - " \t\tif (r == -ENOIOCTLCMD)\n" - " \t\t\tr = vhost_vring_ioctl(&vs->dev, ioctl, argp);\n" - "+\t\telse\n" - "+\t\t\tvhost_scsi_flush(vs);\n" - " \t\tmutex_unlock(&vs->dev.mutex);\n" - " \t\treturn r;\n" - " \t}\n" - "-- \n" - 2.53.0 + " fs/btrfs/file.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++----------\n" + " 1 file changed, 56 insertions(+), 11 deletions(-)\n" + "\n" + "--- a/fs/btrfs/file.c\n" + "+++ b/fs/btrfs/file.c\n" + "@@ -2204,11 +2204,29 @@ static int find_first_non_hole(struct bt\n" + " \treturn ret;\n" + " }\n" + " \n" + "-static void btrfs_punch_hole_lock_range(struct inode *inode,\n" + "-\t\t\t\t\tconst u64 lockstart,\n" + "-\t\t\t\t\tconst u64 lockend,\n" + "-\t\t\t\t\tstruct extent_state **cached_state)\n" + "+/*\n" + "+ * Check if there is no folio in the range.\n" + "+ *\n" + "+ * We cannot utilize filemap_range_has_page() in a filemap with large folios\n" + "+ * as we can hit the following false positive:\n" + "+ *\n" + "+ * start end\n" + "+ * | |\n" + "+ * |//|//|//|//| | | | | | | | |//|//|\n" + "+ * \\ / \\ /\n" + "+ * Folio A Folio B\n" + "+ *\n" + "+ * That large folio A and B cover the start and end indexes.\n" + "+ * In that case filemap_range_has_page() will always return true, but the above\n" + "+ * case is fine for btrfs_punch_hole_lock_range() usage.\n" + "+ *\n" + "+ * So here we only ensure that no other folios is in the range, excluding the\n" + "+ * head/tail large folio.\n" + "+ */\n" + "+static bool check_range_has_page(struct inode *inode, u64 start, u64 end)\n" + " {\n" + "+\tstruct folio_batch fbatch;\n" + "+\tbool ret = false;\n" + " \t/*\n" + " \t * For subpage case, if the range is not at page boundary, we could\n" + " \t * have pages at the leading/tailing part of the range.\n" + "@@ -2219,17 +2237,45 @@ static void btrfs_punch_hole_lock_range(\n" + " \t *\n" + " \t * And do not decrease page_lockend right now, as it can be 0.\n" + " \t */\n" + "-\tconst u64 page_lockstart = round_up(lockstart, PAGE_SIZE);\n" + "-\tconst u64 page_lockend = round_down(lockend + 1, PAGE_SIZE);\n" + "+\tconst u64 page_lockstart = round_up(start, PAGE_SIZE);\n" + "+\tconst u64 page_lockend = round_down(end + 1, PAGE_SIZE);\n" + "+\tconst pgoff_t start_index = page_lockstart >> PAGE_SHIFT;\n" + "+\tconst pgoff_t end_index = (page_lockend - 1) >> PAGE_SHIFT;\n" + "+\tpgoff_t tmp = start_index;\n" + "+\tint found_folios;\n" + "+\n" + "+\t/* The same page or adjacent pages. */\n" + "+\tif (page_lockend <= page_lockstart)\n" + "+\t\treturn false;\n" + " \n" + "+\tfolio_batch_init(&fbatch);\n" + "+\tfound_folios = filemap_get_folios(inode->i_mapping, &tmp, end_index, &fbatch);\n" + "+\tfor (int i = 0; i < found_folios; i++) {\n" + "+\t\tstruct folio *folio = fbatch.folios[i];\n" + "+\n" + "+\t\t/* A large folio begins before the start. Not a target. */\n" + "+\t\tif (folio->index < start_index)\n" + "+\t\t\tcontinue;\n" + "+\t\t/* A large folio extends beyond the end. Not a target. */\n" + "+\t\tif (folio->index + folio_nr_pages(folio) > end_index)\n" + "+\t\t\tcontinue;\n" + "+\t\t/* A folio doesn't cover the head/tail index. Found a target. */\n" + "+\t\tret = true;\n" + "+\t\tbreak;\n" + "+\t}\n" + "+\tfolio_batch_release(&fbatch);\n" + "+\treturn ret;\n" + "+}\n" + "+\n" + "+static void btrfs_punch_hole_lock_range(struct inode *inode,\n" + "+\t\t\t\t\tconst u64 lockstart, const u64 lockend,\n" + "+\t\t\t\t\tstruct extent_state **cached_state)\n" + "+{\n" + " \twhile (1) {\n" + " \t\ttruncate_pagecache_range(inode, lockstart, lockend);\n" + " \n" + " \t\tlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend,\n" + " \t\t\t cached_state);\n" + "-\t\t/* The same page or adjacent pages. */\n" + "-\t\tif (page_lockend <= page_lockstart)\n" + "-\t\t\tbreak;\n" + " \t\t/*\n" + " \t\t * We can't have ordered extents in the range, nor dirty/writeback\n" + " \t\t * pages, because we have locked the inode's VFS lock in exclusive\n" + "@@ -2240,8 +2286,7 @@ static void btrfs_punch_hole_lock_range(\n" + " \t\t * locking the range check if we have pages in the range, and if\n" + " \t\t * we do, unlock the range and retry.\n" + " \t\t */\n" + "-\t\tif (!filemap_range_has_page(inode->i_mapping, page_lockstart,\n" + "-\t\t\t\t\t page_lockend - 1))\n" + "+\t\tif (!check_range_has_page(inode, lockstart, lockend))\n" + " \t\t\tbreak;\n" + " \n" + " \t\tunlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend," -543fddc699d70728d9ac6661fa82f811af9d4bf5ecbae3590e0e2fb91320f641 +b0572c518267b9b745bf2b278a88ddafd7b3293a249ce135a5c529fff00b65c7
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox