]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 5.4
authorSasha Levin <sashal@kernel.org>
Fri, 2 Oct 2020 23:18:36 +0000 (19:18 -0400)
committerSasha Levin <sashal@kernel.org>
Fri, 2 Oct 2020 23:18:36 +0000 (19:18 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
queue-5.4/net-virtio_vsock-enhance-connection-semantics.patch [new file with mode: 0644]
queue-5.4/series [new file with mode: 0644]
queue-5.4/vsock-virtio-add-transport-parameter-to-the-virtio_t.patch [new file with mode: 0644]
queue-5.4/xfs-trim-io-to-found-cow-extent-limit.patch [new file with mode: 0644]

diff --git a/queue-5.4/net-virtio_vsock-enhance-connection-semantics.patch b/queue-5.4/net-virtio_vsock-enhance-connection-semantics.patch
new file mode 100644 (file)
index 0000000..7d3325c
--- /dev/null
@@ -0,0 +1,60 @@
+From 7eccc8744f41ee8239d1110a4aef691cb2077ff6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 14 Feb 2020 12:48:01 +0100
+Subject: net: virtio_vsock: Enhance connection semantics
+
+From: Sebastien Boeuf <sebastien.boeuf@intel.com>
+
+[ Upstream commit df12eb6d6cd920ab2f0e0a43cd6e1c23a05cea91 ]
+
+Whenever the vsock backend on the host sends a packet through the RX
+queue, it expects an answer on the TX queue. Unfortunately, there is one
+case where the host side will hang waiting for the answer and might
+effectively never recover if no timeout mechanism was implemented.
+
+This issue happens when the guest side starts binding to the socket,
+which insert a new bound socket into the list of already bound sockets.
+At this time, we expect the guest to also start listening, which will
+trigger the sk_state to move from TCP_CLOSE to TCP_LISTEN. The problem
+occurs if the host side queued a RX packet and triggered an interrupt
+right between the end of the binding process and the beginning of the
+listening process. In this specific case, the function processing the
+packet virtio_transport_recv_pkt() will find a bound socket, which means
+it will hit the switch statement checking for the sk_state, but the
+state won't be changed into TCP_LISTEN yet, which leads the code to pick
+the default statement. This default statement will only free the buffer,
+while it should also respond to the host side, by sending a packet on
+its TX queue.
+
+In order to simply fix this unfortunate chain of events, it is important
+that in case the default statement is entered, and because at this stage
+we know the host side is waiting for an answer, we must send back a
+packet containing the operation VIRTIO_VSOCK_OP_RST.
+
+One could say that a proper timeout mechanism on the host side will be
+enough to avoid the backend to hang. But the point of this patch is to
+ensure the normal use case will be provided with proper responsiveness
+when it comes to establishing the connection.
+
+Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/vmw_vsock/virtio_transport_common.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
+index f0b8ad2656f5e..efbb521bff135 100644
+--- a/net/vmw_vsock/virtio_transport_common.c
++++ b/net/vmw_vsock/virtio_transport_common.c
+@@ -1127,6 +1127,7 @@ void virtio_transport_recv_pkt(struct virtio_transport *t,
+               virtio_transport_free_pkt(pkt);
+               break;
+       default:
++              (void)virtio_transport_reset_no_sock(t, pkt);
+               virtio_transport_free_pkt(pkt);
+               break;
+       }
+-- 
+2.25.1
+
diff --git a/queue-5.4/series b/queue-5.4/series
new file mode 100644 (file)
index 0000000..fa71c18
--- /dev/null
@@ -0,0 +1,3 @@
+vsock-virtio-add-transport-parameter-to-the-virtio_t.patch
+net-virtio_vsock-enhance-connection-semantics.patch
+xfs-trim-io-to-found-cow-extent-limit.patch
diff --git a/queue-5.4/vsock-virtio-add-transport-parameter-to-the-virtio_t.patch b/queue-5.4/vsock-virtio-add-transport-parameter-to-the-virtio_t.patch
new file mode 100644 (file)
index 0000000..d459864
--- /dev/null
@@ -0,0 +1,409 @@
+From 639b747d60e2fb4d49d164249d49eaa191518879 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 Nov 2019 10:57:40 +0100
+Subject: vsock/virtio: add transport parameter to the
+ virtio_transport_reset_no_sock()
+
+From: Stefano Garzarella <sgarzare@redhat.com>
+
+[ Upstream commit 4c7246dc45e2706770d5233f7ce1597a07e069ba ]
+
+We are going to add 'struct vsock_sock *' parameter to
+virtio_transport_get_ops().
+
+In some cases, like in the virtio_transport_reset_no_sock(),
+we don't have any socket assigned to the packet received,
+so we can't use the virtio_transport_get_ops().
+
+In order to allow virtio_transport_reset_no_sock() to use the
+'.send_pkt' callback from the 'vhost_transport' or 'virtio_transport',
+we add the 'struct virtio_transport *' to it and to its caller:
+virtio_transport_recv_pkt().
+
+We moved the 'vhost_transport' and 'virtio_transport' definition,
+to pass their address to the virtio_transport_recv_pkt().
+
+Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
+Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/vhost/vsock.c                   |  94 +++++++-------
+ include/linux/virtio_vsock.h            |   3 +-
+ net/vmw_vsock/virtio_transport.c        | 160 ++++++++++++------------
+ net/vmw_vsock/virtio_transport_common.c |  12 +-
+ 4 files changed, 135 insertions(+), 134 deletions(-)
+
+diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
+index ca68a27b98edd..f21f5bfbb78dc 100644
+--- a/drivers/vhost/vsock.c
++++ b/drivers/vhost/vsock.c
+@@ -384,6 +384,52 @@ static bool vhost_vsock_more_replies(struct vhost_vsock *vsock)
+       return val < vq->num;
+ }
++static struct virtio_transport vhost_transport = {
++      .transport = {
++              .get_local_cid            = vhost_transport_get_local_cid,
++
++              .init                     = virtio_transport_do_socket_init,
++              .destruct                 = virtio_transport_destruct,
++              .release                  = virtio_transport_release,
++              .connect                  = virtio_transport_connect,
++              .shutdown                 = virtio_transport_shutdown,
++              .cancel_pkt               = vhost_transport_cancel_pkt,
++
++              .dgram_enqueue            = virtio_transport_dgram_enqueue,
++              .dgram_dequeue            = virtio_transport_dgram_dequeue,
++              .dgram_bind               = virtio_transport_dgram_bind,
++              .dgram_allow              = virtio_transport_dgram_allow,
++
++              .stream_enqueue           = virtio_transport_stream_enqueue,
++              .stream_dequeue           = virtio_transport_stream_dequeue,
++              .stream_has_data          = virtio_transport_stream_has_data,
++              .stream_has_space         = virtio_transport_stream_has_space,
++              .stream_rcvhiwat          = virtio_transport_stream_rcvhiwat,
++              .stream_is_active         = virtio_transport_stream_is_active,
++              .stream_allow             = virtio_transport_stream_allow,
++
++              .notify_poll_in           = virtio_transport_notify_poll_in,
++              .notify_poll_out          = virtio_transport_notify_poll_out,
++              .notify_recv_init         = virtio_transport_notify_recv_init,
++              .notify_recv_pre_block    = virtio_transport_notify_recv_pre_block,
++              .notify_recv_pre_dequeue  = virtio_transport_notify_recv_pre_dequeue,
++              .notify_recv_post_dequeue = virtio_transport_notify_recv_post_dequeue,
++              .notify_send_init         = virtio_transport_notify_send_init,
++              .notify_send_pre_block    = virtio_transport_notify_send_pre_block,
++              .notify_send_pre_enqueue  = virtio_transport_notify_send_pre_enqueue,
++              .notify_send_post_enqueue = virtio_transport_notify_send_post_enqueue,
++
++              .set_buffer_size          = virtio_transport_set_buffer_size,
++              .set_min_buffer_size      = virtio_transport_set_min_buffer_size,
++              .set_max_buffer_size      = virtio_transport_set_max_buffer_size,
++              .get_buffer_size          = virtio_transport_get_buffer_size,
++              .get_min_buffer_size      = virtio_transport_get_min_buffer_size,
++              .get_max_buffer_size      = virtio_transport_get_max_buffer_size,
++      },
++
++      .send_pkt = vhost_transport_send_pkt,
++};
++
+ static void vhost_vsock_handle_tx_kick(struct vhost_work *work)
+ {
+       struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
+@@ -440,7 +486,7 @@ static void vhost_vsock_handle_tx_kick(struct vhost_work *work)
+               if (le64_to_cpu(pkt->hdr.src_cid) == vsock->guest_cid &&
+                   le64_to_cpu(pkt->hdr.dst_cid) ==
+                   vhost_transport_get_local_cid())
+-                      virtio_transport_recv_pkt(pkt);
++                      virtio_transport_recv_pkt(&vhost_transport, pkt);
+               else
+                       virtio_transport_free_pkt(pkt);
+@@ -793,52 +839,6 @@ static struct miscdevice vhost_vsock_misc = {
+       .fops = &vhost_vsock_fops,
+ };
+-static struct virtio_transport vhost_transport = {
+-      .transport = {
+-              .get_local_cid            = vhost_transport_get_local_cid,
+-
+-              .init                     = virtio_transport_do_socket_init,
+-              .destruct                 = virtio_transport_destruct,
+-              .release                  = virtio_transport_release,
+-              .connect                  = virtio_transport_connect,
+-              .shutdown                 = virtio_transport_shutdown,
+-              .cancel_pkt               = vhost_transport_cancel_pkt,
+-
+-              .dgram_enqueue            = virtio_transport_dgram_enqueue,
+-              .dgram_dequeue            = virtio_transport_dgram_dequeue,
+-              .dgram_bind               = virtio_transport_dgram_bind,
+-              .dgram_allow              = virtio_transport_dgram_allow,
+-
+-              .stream_enqueue           = virtio_transport_stream_enqueue,
+-              .stream_dequeue           = virtio_transport_stream_dequeue,
+-              .stream_has_data          = virtio_transport_stream_has_data,
+-              .stream_has_space         = virtio_transport_stream_has_space,
+-              .stream_rcvhiwat          = virtio_transport_stream_rcvhiwat,
+-              .stream_is_active         = virtio_transport_stream_is_active,
+-              .stream_allow             = virtio_transport_stream_allow,
+-
+-              .notify_poll_in           = virtio_transport_notify_poll_in,
+-              .notify_poll_out          = virtio_transport_notify_poll_out,
+-              .notify_recv_init         = virtio_transport_notify_recv_init,
+-              .notify_recv_pre_block    = virtio_transport_notify_recv_pre_block,
+-              .notify_recv_pre_dequeue  = virtio_transport_notify_recv_pre_dequeue,
+-              .notify_recv_post_dequeue = virtio_transport_notify_recv_post_dequeue,
+-              .notify_send_init         = virtio_transport_notify_send_init,
+-              .notify_send_pre_block    = virtio_transport_notify_send_pre_block,
+-              .notify_send_pre_enqueue  = virtio_transport_notify_send_pre_enqueue,
+-              .notify_send_post_enqueue = virtio_transport_notify_send_post_enqueue,
+-
+-              .set_buffer_size          = virtio_transport_set_buffer_size,
+-              .set_min_buffer_size      = virtio_transport_set_min_buffer_size,
+-              .set_max_buffer_size      = virtio_transport_set_max_buffer_size,
+-              .get_buffer_size          = virtio_transport_get_buffer_size,
+-              .get_min_buffer_size      = virtio_transport_get_min_buffer_size,
+-              .get_max_buffer_size      = virtio_transport_get_max_buffer_size,
+-      },
+-
+-      .send_pkt = vhost_transport_send_pkt,
+-};
+-
+ static int __init vhost_vsock_init(void)
+ {
+       int ret;
+diff --git a/include/linux/virtio_vsock.h b/include/linux/virtio_vsock.h
+index 07875ccc7bb50..b139f76060a65 100644
+--- a/include/linux/virtio_vsock.h
++++ b/include/linux/virtio_vsock.h
+@@ -150,7 +150,8 @@ virtio_transport_dgram_enqueue(struct vsock_sock *vsk,
+ void virtio_transport_destruct(struct vsock_sock *vsk);
+-void virtio_transport_recv_pkt(struct virtio_vsock_pkt *pkt);
++void virtio_transport_recv_pkt(struct virtio_transport *t,
++                             struct virtio_vsock_pkt *pkt);
+ void virtio_transport_free_pkt(struct virtio_vsock_pkt *pkt);
+ void virtio_transport_inc_tx_pkt(struct virtio_vsock_sock *vvs, struct virtio_vsock_pkt *pkt);
+ u32 virtio_transport_get_credit(struct virtio_vsock_sock *vvs, u32 wanted);
+diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c
+index 861ec9a671f9d..5905f0cddc895 100644
+--- a/net/vmw_vsock/virtio_transport.c
++++ b/net/vmw_vsock/virtio_transport.c
+@@ -86,33 +86,6 @@ static u32 virtio_transport_get_local_cid(void)
+       return ret;
+ }
+-static void virtio_transport_loopback_work(struct work_struct *work)
+-{
+-      struct virtio_vsock *vsock =
+-              container_of(work, struct virtio_vsock, loopback_work);
+-      LIST_HEAD(pkts);
+-
+-      spin_lock_bh(&vsock->loopback_list_lock);
+-      list_splice_init(&vsock->loopback_list, &pkts);
+-      spin_unlock_bh(&vsock->loopback_list_lock);
+-
+-      mutex_lock(&vsock->rx_lock);
+-
+-      if (!vsock->rx_run)
+-              goto out;
+-
+-      while (!list_empty(&pkts)) {
+-              struct virtio_vsock_pkt *pkt;
+-
+-              pkt = list_first_entry(&pkts, struct virtio_vsock_pkt, list);
+-              list_del_init(&pkt->list);
+-
+-              virtio_transport_recv_pkt(pkt);
+-      }
+-out:
+-      mutex_unlock(&vsock->rx_lock);
+-}
+-
+ static int virtio_transport_send_pkt_loopback(struct virtio_vsock *vsock,
+                                             struct virtio_vsock_pkt *pkt)
+ {
+@@ -370,59 +343,6 @@ static bool virtio_transport_more_replies(struct virtio_vsock *vsock)
+       return val < virtqueue_get_vring_size(vq);
+ }
+-static void virtio_transport_rx_work(struct work_struct *work)
+-{
+-      struct virtio_vsock *vsock =
+-              container_of(work, struct virtio_vsock, rx_work);
+-      struct virtqueue *vq;
+-
+-      vq = vsock->vqs[VSOCK_VQ_RX];
+-
+-      mutex_lock(&vsock->rx_lock);
+-
+-      if (!vsock->rx_run)
+-              goto out;
+-
+-      do {
+-              virtqueue_disable_cb(vq);
+-              for (;;) {
+-                      struct virtio_vsock_pkt *pkt;
+-                      unsigned int len;
+-
+-                      if (!virtio_transport_more_replies(vsock)) {
+-                              /* Stop rx until the device processes already
+-                               * pending replies.  Leave rx virtqueue
+-                               * callbacks disabled.
+-                               */
+-                              goto out;
+-                      }
+-
+-                      pkt = virtqueue_get_buf(vq, &len);
+-                      if (!pkt) {
+-                              break;
+-                      }
+-
+-                      vsock->rx_buf_nr--;
+-
+-                      /* Drop short/long packets */
+-                      if (unlikely(len < sizeof(pkt->hdr) ||
+-                                   len > sizeof(pkt->hdr) + pkt->len)) {
+-                              virtio_transport_free_pkt(pkt);
+-                              continue;
+-                      }
+-
+-                      pkt->len = len - sizeof(pkt->hdr);
+-                      virtio_transport_deliver_tap_pkt(pkt);
+-                      virtio_transport_recv_pkt(pkt);
+-              }
+-      } while (!virtqueue_enable_cb(vq));
+-
+-out:
+-      if (vsock->rx_buf_nr < vsock->rx_buf_max_nr / 2)
+-              virtio_vsock_rx_fill(vsock);
+-      mutex_unlock(&vsock->rx_lock);
+-}
+-
+ /* event_lock must be held */
+ static int virtio_vsock_event_fill_one(struct virtio_vsock *vsock,
+                                      struct virtio_vsock_event *event)
+@@ -586,6 +506,86 @@ static struct virtio_transport virtio_transport = {
+       .send_pkt = virtio_transport_send_pkt,
+ };
++static void virtio_transport_loopback_work(struct work_struct *work)
++{
++      struct virtio_vsock *vsock =
++              container_of(work, struct virtio_vsock, loopback_work);
++      LIST_HEAD(pkts);
++
++      spin_lock_bh(&vsock->loopback_list_lock);
++      list_splice_init(&vsock->loopback_list, &pkts);
++      spin_unlock_bh(&vsock->loopback_list_lock);
++
++      mutex_lock(&vsock->rx_lock);
++
++      if (!vsock->rx_run)
++              goto out;
++
++      while (!list_empty(&pkts)) {
++              struct virtio_vsock_pkt *pkt;
++
++              pkt = list_first_entry(&pkts, struct virtio_vsock_pkt, list);
++              list_del_init(&pkt->list);
++
++              virtio_transport_recv_pkt(&virtio_transport, pkt);
++      }
++out:
++      mutex_unlock(&vsock->rx_lock);
++}
++
++static void virtio_transport_rx_work(struct work_struct *work)
++{
++      struct virtio_vsock *vsock =
++              container_of(work, struct virtio_vsock, rx_work);
++      struct virtqueue *vq;
++
++      vq = vsock->vqs[VSOCK_VQ_RX];
++
++      mutex_lock(&vsock->rx_lock);
++
++      if (!vsock->rx_run)
++              goto out;
++
++      do {
++              virtqueue_disable_cb(vq);
++              for (;;) {
++                      struct virtio_vsock_pkt *pkt;
++                      unsigned int len;
++
++                      if (!virtio_transport_more_replies(vsock)) {
++                              /* Stop rx until the device processes already
++                               * pending replies.  Leave rx virtqueue
++                               * callbacks disabled.
++                               */
++                              goto out;
++                      }
++
++                      pkt = virtqueue_get_buf(vq, &len);
++                      if (!pkt) {
++                              break;
++                      }
++
++                      vsock->rx_buf_nr--;
++
++                      /* Drop short/long packets */
++                      if (unlikely(len < sizeof(pkt->hdr) ||
++                                   len > sizeof(pkt->hdr) + pkt->len)) {
++                              virtio_transport_free_pkt(pkt);
++                              continue;
++                      }
++
++                      pkt->len = len - sizeof(pkt->hdr);
++                      virtio_transport_deliver_tap_pkt(pkt);
++                      virtio_transport_recv_pkt(&virtio_transport, pkt);
++              }
++      } while (!virtqueue_enable_cb(vq));
++
++out:
++      if (vsock->rx_buf_nr < vsock->rx_buf_max_nr / 2)
++              virtio_vsock_rx_fill(vsock);
++      mutex_unlock(&vsock->rx_lock);
++}
++
+ static int virtio_vsock_probe(struct virtio_device *vdev)
+ {
+       vq_callback_t *callbacks[] = {
+diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
+index fb2060dffb0af..f0b8ad2656f5e 100644
+--- a/net/vmw_vsock/virtio_transport_common.c
++++ b/net/vmw_vsock/virtio_transport_common.c
+@@ -696,9 +696,9 @@ static int virtio_transport_reset(struct vsock_sock *vsk,
+ /* Normally packets are associated with a socket.  There may be no socket if an
+  * attempt was made to connect to a socket that does not exist.
+  */
+-static int virtio_transport_reset_no_sock(struct virtio_vsock_pkt *pkt)
++static int virtio_transport_reset_no_sock(const struct virtio_transport *t,
++                                        struct virtio_vsock_pkt *pkt)
+ {
+-      const struct virtio_transport *t;
+       struct virtio_vsock_pkt *reply;
+       struct virtio_vsock_pkt_info info = {
+               .op = VIRTIO_VSOCK_OP_RST,
+@@ -718,7 +718,6 @@ static int virtio_transport_reset_no_sock(struct virtio_vsock_pkt *pkt)
+       if (!reply)
+               return -ENOMEM;
+-      t = virtio_transport_get_ops();
+       if (!t) {
+               virtio_transport_free_pkt(reply);
+               return -ENOTCONN;
+@@ -1060,7 +1059,8 @@ static bool virtio_transport_space_update(struct sock *sk,
+ /* We are under the virtio-vsock's vsock->rx_lock or vhost-vsock's vq->mutex
+  * lock.
+  */
+-void virtio_transport_recv_pkt(struct virtio_vsock_pkt *pkt)
++void virtio_transport_recv_pkt(struct virtio_transport *t,
++                             struct virtio_vsock_pkt *pkt)
+ {
+       struct sockaddr_vm src, dst;
+       struct vsock_sock *vsk;
+@@ -1082,7 +1082,7 @@ void virtio_transport_recv_pkt(struct virtio_vsock_pkt *pkt)
+                                       le32_to_cpu(pkt->hdr.fwd_cnt));
+       if (le16_to_cpu(pkt->hdr.type) != VIRTIO_VSOCK_TYPE_STREAM) {
+-              (void)virtio_transport_reset_no_sock(pkt);
++              (void)virtio_transport_reset_no_sock(t, pkt);
+               goto free_pkt;
+       }
+@@ -1093,7 +1093,7 @@ void virtio_transport_recv_pkt(struct virtio_vsock_pkt *pkt)
+       if (!sk) {
+               sk = vsock_find_bound_socket(&dst);
+               if (!sk) {
+-                      (void)virtio_transport_reset_no_sock(pkt);
++                      (void)virtio_transport_reset_no_sock(t, pkt);
+                       goto free_pkt;
+               }
+       }
+-- 
+2.25.1
+
diff --git a/queue-5.4/xfs-trim-io-to-found-cow-extent-limit.patch b/queue-5.4/xfs-trim-io-to-found-cow-extent-limit.patch
new file mode 100644 (file)
index 0000000..c1316ae
--- /dev/null
@@ -0,0 +1,58 @@
+From a4205a5effe6634f291e18d8ee3b9bd203998547 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 1 Oct 2020 08:34:48 -0500
+Subject: xfs: trim IO to found COW extent limit
+
+From: Eric Sandeen <sandeen@sandeen.net>
+
+A bug existed in the XFS reflink code between v5.1 and v5.5 in which
+the mapping for a COW IO was not trimmed to the mapping of the COW
+extent that was found.  This resulted in a too-short copy, and
+corruption of other files which shared the original extent.
+
+(This happened only when extent size hints were set, which bypasses
+delalloc and led to this code path.)
+
+This was (inadvertently) fixed upstream with
+
+36adcbace24e "xfs: fill out the srcmap in iomap_begin"
+
+and related patches which moved lots of this functionality to
+the iomap subsystem.
+
+Hence, this is a -stable only patch, targeted to fix this
+corruption vector without other major code changes.
+
+Fixes: 78f0cc9d55cb ("xfs: don't use delalloc extents for COW on files with extsize hints")
+Cc: <stable@vger.kernel.org> # 5.4.x
+Signed-off-by: Eric Sandeen <sandeen@redhat.com>
+Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/xfs/xfs_iomap.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c
+index f780e223b1185..239c9548b1568 100644
+--- a/fs/xfs/xfs_iomap.c
++++ b/fs/xfs/xfs_iomap.c
+@@ -1002,9 +1002,15 @@ xfs_file_iomap_begin(
+                * I/O, which must be block aligned, we need to report the
+                * newly allocated address.  If the data fork has a hole, copy
+                * the COW fork mapping to avoid allocating to the data fork.
++               *
++               * Otherwise, ensure that the imap range does not extend past
++               * the range allocated/found in cmap.
+                */
+               if (directio || imap.br_startblock == HOLESTARTBLOCK)
+                       imap = cmap;
++              else
++                      xfs_trim_extent(&imap, cmap.br_startoff,
++                                      cmap.br_blockcount);
+               end_fsb = imap.br_startoff + imap.br_blockcount;
+               length = XFS_FSB_TO_B(mp, end_fsb) - offset;
+-- 
+2.25.1
+