* [PATCH net-next] virtio-net: support xsk wake up
From: Menglong Dong @ 2026-06-10 8:16 UTC (permalink / raw)
To: xuanzhuo
Cc: mst, jasowang, eperezma, andrew+netdev, davem, edumazet, kuba,
pabeni, netdev, virtualization, linux-kernel
For now, XDP_RING_NEED_WAKEUP is not supported properly by the virtio-net.
Take the tx path for example, we set xsk_set_tx_need_wakeup() in
virtnet_xsk_xmit(), but we didn't call xsk_clear_tx_need_wakeup()
anywhere, which means the user will call send() for every packet.
For the tx path, we will call xsk_set_tx_need_wakeup() after
virtnet_xsk_xmit_batch() if sq->vq is empty, as we can't be wakeup by the
skb_xmit_done() in this case. Otherwise, we will clear the wakeup flag.
For the rx path, we will call xsk_set_rx_need_wakeup() if we have free
buffers in rq->vq.
Race condition is considered for both rx and tx path.
Signed-off-by: Menglong Dong <dongml2@chinatelecom.cn>
---
drivers/net/virtio_net.c | 52 ++++++++++++++++++++++++++++++++++------
1 file changed, 45 insertions(+), 7 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index 9b3da9f9786c..25e895b849a6 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -1323,16 +1323,27 @@ static int virtnet_add_recvbuf_xsk(struct virtnet_info *vi, struct receive_queue
struct xsk_buff_pool *pool, gfp_t gfp)
{
struct xdp_buff **xsk_buffs;
+ bool need_wakeup;
dma_addr_t addr;
int err = 0;
u32 len, i;
int num;
+ need_wakeup = xsk_uses_need_wakeup(pool);
xsk_buffs = rq->xsk_buffs;
+ /* If both rq->vq and fill ring are empty, and then the user submit
+ * all the chunks to the fill ring and check the wake up flag
+ * after xsk_buff_alloc_batch() and before xsk_set_rx_need_wakeup(),
+ * we will lose the chance to wake up the rx napi, so we have to
+ * set the need_wakeup flag here.
+ */
+ if (need_wakeup && virtqueue_get_vring_size(rq->vq) == rq->vq->num_free)
+ xsk_set_rx_need_wakeup(pool);
+
num = xsk_buff_alloc_batch(pool, xsk_buffs, rq->vq->num_free);
if (!num) {
- if (xsk_uses_need_wakeup(pool)) {
+ if (need_wakeup) {
xsk_set_rx_need_wakeup(pool);
/* Return 0 instead of -ENOMEM so that NAPI is
* descheduled.
@@ -1341,8 +1352,6 @@ static int virtnet_add_recvbuf_xsk(struct virtnet_info *vi, struct receive_queue
}
return -ENOMEM;
- } else {
- xsk_clear_rx_need_wakeup(pool);
}
len = xsk_pool_get_rx_frame_size(pool) + vi->hdr_len;
@@ -1363,6 +1372,16 @@ static int virtnet_add_recvbuf_xsk(struct virtnet_info *vi, struct receive_queue
goto err;
}
+ if (need_wakeup) {
+ if (rq->vq->num_free)
+ /* We have free buffers, so we'd better wake up the
+ * rx napi as soon as possible.
+ */
+ xsk_set_rx_need_wakeup(pool);
+ else
+ xsk_clear_rx_need_wakeup(pool);
+ }
+
return num;
err:
@@ -1440,8 +1459,9 @@ static bool virtnet_xsk_xmit(struct send_queue *sq, struct xsk_buff_pool *pool,
struct virtnet_info *vi = sq->vq->vdev->priv;
struct virtnet_sq_free_stats stats = {};
struct net_device *dev = vi->dev;
+ int sent, vring_size;
+ bool need_wakeup;
u64 kicks = 0;
- int sent;
/* Avoid to wakeup napi meanless, so call __free_old_xmit instead of
* free_old_xmit().
@@ -1451,8 +1471,29 @@ static bool virtnet_xsk_xmit(struct send_queue *sq, struct xsk_buff_pool *pool,
if (stats.xsk)
xsk_tx_completed(sq->xsk_pool, stats.xsk);
+ vring_size = virtqueue_get_vring_size(sq->vq);
+ need_wakeup = xsk_uses_need_wakeup(pool);
+ /* If the sq->vq is empty, and the tx ring is empty, and the user
+ * submit an entry to the tx ring after virtnet_xsk_xmit_batch() and
+ * before xsk_set_tx_need_wakeup(), we will lose the chance to wake
+ * up the tx napi, so we have to set the need_wakeup flag here.
+ */
+ if (need_wakeup && vring_size == sq->vq->num_free)
+ xsk_set_tx_need_wakeup(pool);
+
sent = virtnet_xsk_xmit_batch(sq, pool, budget, &kicks);
+ if (need_wakeup) {
+ if (vring_size == sq->vq->num_free)
+ /* we can't wake up by ourself, and it should be done
+ * by the user.
+ */
+ xsk_set_tx_need_wakeup(pool);
+ else
+ /* we can wake up from skb_xmit_done() */
+ xsk_clear_tx_need_wakeup(pool);
+ }
+
if (!is_xdp_raw_buffer_queue(vi, sq - vi->sq))
check_sq_full_and_disable(vi, vi->dev, sq);
@@ -1470,9 +1511,6 @@ static bool virtnet_xsk_xmit(struct send_queue *sq, struct xsk_buff_pool *pool,
u64_stats_add(&sq->stats.xdp_tx, sent);
u64_stats_update_end(&sq->stats.syncp);
- if (xsk_uses_need_wakeup(pool))
- xsk_set_tx_need_wakeup(pool);
-
return sent;
}
--
2.54.0
^ permalink raw reply related
* Re: [PATCH net-next] virtio-net: support xsk wake up
From: Eugenio Perez Martin @ 2026-06-10 8:27 UTC (permalink / raw)
To: Menglong Dong
Cc: xuanzhuo, mst, jasowang, andrew+netdev, davem, edumazet, kuba,
pabeni, netdev, virtualization, linux-kernel
In-Reply-To: <20260610081648.2205711-1-dongml2@chinatelecom.cn>
On Wed, Jun 10, 2026 at 10:17 AM Menglong Dong <menglong8.dong@gmail.com> wrote:
>
> For now, XDP_RING_NEED_WAKEUP is not supported properly by the virtio-net.
> Take the tx path for example, we set xsk_set_tx_need_wakeup() in
> virtnet_xsk_xmit(), but we didn't call xsk_clear_tx_need_wakeup()
> anywhere, which means the user will call send() for every packet.
>
> For the tx path, we will call xsk_set_tx_need_wakeup() after
> virtnet_xsk_xmit_batch() if sq->vq is empty, as we can't be wakeup by the
> skb_xmit_done() in this case. Otherwise, we will clear the wakeup flag.
>
> For the rx path, we will call xsk_set_rx_need_wakeup() if we have free
> buffers in rq->vq.
>
> Race condition is considered for both rx and tx path.
>
I didn't review the code itself, but the patch message lacks a Fixes:
tag. And the From and Signed-off-by emails don't match, which I'm not
sure is valid.
> Signed-off-by: Menglong Dong <dongml2@chinatelecom.cn>
> ---
> drivers/net/virtio_net.c | 52 ++++++++++++++++++++++++++++++++++------
> 1 file changed, 45 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index 9b3da9f9786c..25e895b849a6 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -1323,16 +1323,27 @@ static int virtnet_add_recvbuf_xsk(struct virtnet_info *vi, struct receive_queue
> struct xsk_buff_pool *pool, gfp_t gfp)
> {
> struct xdp_buff **xsk_buffs;
> + bool need_wakeup;
> dma_addr_t addr;
> int err = 0;
> u32 len, i;
> int num;
>
> + need_wakeup = xsk_uses_need_wakeup(pool);
> xsk_buffs = rq->xsk_buffs;
>
> + /* If both rq->vq and fill ring are empty, and then the user submit
> + * all the chunks to the fill ring and check the wake up flag
> + * after xsk_buff_alloc_batch() and before xsk_set_rx_need_wakeup(),
> + * we will lose the chance to wake up the rx napi, so we have to
> + * set the need_wakeup flag here.
> + */
> + if (need_wakeup && virtqueue_get_vring_size(rq->vq) == rq->vq->num_free)
> + xsk_set_rx_need_wakeup(pool);
> +
> num = xsk_buff_alloc_batch(pool, xsk_buffs, rq->vq->num_free);
> if (!num) {
> - if (xsk_uses_need_wakeup(pool)) {
> + if (need_wakeup) {
> xsk_set_rx_need_wakeup(pool);
> /* Return 0 instead of -ENOMEM so that NAPI is
> * descheduled.
> @@ -1341,8 +1352,6 @@ static int virtnet_add_recvbuf_xsk(struct virtnet_info *vi, struct receive_queue
> }
>
> return -ENOMEM;
> - } else {
> - xsk_clear_rx_need_wakeup(pool);
> }
>
> len = xsk_pool_get_rx_frame_size(pool) + vi->hdr_len;
> @@ -1363,6 +1372,16 @@ static int virtnet_add_recvbuf_xsk(struct virtnet_info *vi, struct receive_queue
> goto err;
> }
>
> + if (need_wakeup) {
> + if (rq->vq->num_free)
> + /* We have free buffers, so we'd better wake up the
> + * rx napi as soon as possible.
> + */
> + xsk_set_rx_need_wakeup(pool);
> + else
> + xsk_clear_rx_need_wakeup(pool);
> + }
> +
> return num;
>
> err:
> @@ -1440,8 +1459,9 @@ static bool virtnet_xsk_xmit(struct send_queue *sq, struct xsk_buff_pool *pool,
> struct virtnet_info *vi = sq->vq->vdev->priv;
> struct virtnet_sq_free_stats stats = {};
> struct net_device *dev = vi->dev;
> + int sent, vring_size;
> + bool need_wakeup;
> u64 kicks = 0;
> - int sent;
>
> /* Avoid to wakeup napi meanless, so call __free_old_xmit instead of
> * free_old_xmit().
> @@ -1451,8 +1471,29 @@ static bool virtnet_xsk_xmit(struct send_queue *sq, struct xsk_buff_pool *pool,
> if (stats.xsk)
> xsk_tx_completed(sq->xsk_pool, stats.xsk);
>
> + vring_size = virtqueue_get_vring_size(sq->vq);
> + need_wakeup = xsk_uses_need_wakeup(pool);
> + /* If the sq->vq is empty, and the tx ring is empty, and the user
> + * submit an entry to the tx ring after virtnet_xsk_xmit_batch() and
> + * before xsk_set_tx_need_wakeup(), we will lose the chance to wake
> + * up the tx napi, so we have to set the need_wakeup flag here.
> + */
> + if (need_wakeup && vring_size == sq->vq->num_free)
> + xsk_set_tx_need_wakeup(pool);
> +
> sent = virtnet_xsk_xmit_batch(sq, pool, budget, &kicks);
>
> + if (need_wakeup) {
> + if (vring_size == sq->vq->num_free)
> + /* we can't wake up by ourself, and it should be done
> + * by the user.
> + */
> + xsk_set_tx_need_wakeup(pool);
> + else
> + /* we can wake up from skb_xmit_done() */
> + xsk_clear_tx_need_wakeup(pool);
> + }
> +
> if (!is_xdp_raw_buffer_queue(vi, sq - vi->sq))
> check_sq_full_and_disable(vi, vi->dev, sq);
>
> @@ -1470,9 +1511,6 @@ static bool virtnet_xsk_xmit(struct send_queue *sq, struct xsk_buff_pool *pool,
> u64_stats_add(&sq->stats.xdp_tx, sent);
> u64_stats_update_end(&sq->stats.syncp);
>
> - if (xsk_uses_need_wakeup(pool))
> - xsk_set_tx_need_wakeup(pool);
> -
> return sent;
> }
>
> --
> 2.54.0
>
^ permalink raw reply
* [PATCH v3] vduse: Add suspend
From: Eugenio Pérez @ 2026-06-10 8:34 UTC (permalink / raw)
To: Michael S . Tsirkin
Cc: virtualization, Jason Wang, Cindy Lu, Xuan Zhuo,
Stefano Garzarella, linux-kernel, Laurent Vivier, Yongji Xie,
Eugenio Pérez, Maxime Coquelin
Implement suspend operation for vduse devices, so vhost-vdpa will offer
that backend feature and userspace can effectively suspend the device.
This is a must before get virtqueue indexes (base) for live migration,
since the device could modify them after userland gets them.
This patch does not implement resume, so VMM resets the whole device
to recover from a live migration failure. Resume optimization can be
implemented on top of these patches, as other vDPA devices have done in
the past.
Signed-off-by: Eugenio Pérez <eperezma@redhat.com>
---
This patch depends on
https://lore.kernel.org/lkml/20260310190759.1097506-1-eperezma@redhat.com
v3:
* Expand the patch message with information about resume operation.
v2:
* Take the rwsem only before the actual kick, not in vduse_vdpa_kick_vq.
This assures that we're not in a critical section.
---
drivers/vdpa/vdpa_user/vduse_dev.c | 86 +++++++++++++++++++++++++++++-
include/uapi/linux/vduse.h | 4 ++
2 files changed, 88 insertions(+), 2 deletions(-)
diff --git a/drivers/vdpa/vdpa_user/vduse_dev.c b/drivers/vdpa/vdpa_user/vduse_dev.c
index 5b9248d58938..92f8bb4bb282 100644
--- a/drivers/vdpa/vdpa_user/vduse_dev.c
+++ b/drivers/vdpa/vdpa_user/vduse_dev.c
@@ -54,7 +54,8 @@
#define IRQ_UNBOUND -1
/* Supported VDUSE features */
-static const uint64_t vduse_features = BIT_U64(VDUSE_F_QUEUE_READY);
+static const uint64_t vduse_features = BIT_U64(VDUSE_F_QUEUE_READY) |
+ BIT_U64(VDUSE_F_SUSPEND);
/*
* VDUSE instance have not asked the vduse API version, so assume 0.
@@ -85,6 +86,7 @@ struct vduse_virtqueue {
int irq_effective_cpu;
struct cpumask irq_affinity;
struct kobject kobj;
+ struct vduse_dev *dev;
};
struct vduse_dev;
@@ -134,6 +136,7 @@ struct vduse_dev {
int minor;
bool broken;
bool connected;
+ bool suspended;
u64 api_version;
u64 device_features;
u64 driver_features;
@@ -503,6 +506,7 @@ static void vduse_dev_reset(struct vduse_dev *dev)
down_write(&dev->rwsem);
+ dev->suspended = false;
dev->status = 0;
dev->driver_features = 0;
dev->generation++;
@@ -561,6 +565,10 @@ static void vduse_vq_kick(struct vduse_virtqueue *vq)
if (!vq->ready)
goto unlock;
+ guard(rwsem_read)(&vq->dev->rwsem);
+ if (vq->dev->suspended)
+ return;
+
if (vq->kickfd)
eventfd_signal(vq->kickfd);
else
@@ -919,6 +927,27 @@ static int vduse_vdpa_set_map(struct vdpa_device *vdpa,
return 0;
}
+static int vduse_vdpa_suspend(struct vdpa_device *vdpa)
+{
+ struct vduse_dev *dev = vdpa_to_vduse(vdpa);
+ struct vduse_dev_msg msg = { 0 };
+ int ret;
+
+ msg.req.type = VDUSE_SUSPEND;
+
+ ret = vduse_dev_msg_sync(dev, &msg);
+ if (ret == 0) {
+ scoped_guard(rwsem_write, &dev->rwsem)
+ dev->suspended = true;
+
+ cancel_work_sync(&dev->inject);
+ for (u32 i = 0; i < dev->vq_num; i++)
+ cancel_work_sync(&dev->vqs[i]->inject);
+ }
+
+ return ret;
+}
+
static void vduse_vdpa_free(struct vdpa_device *vdpa)
{
struct vduse_dev *dev = vdpa_to_vduse(vdpa);
@@ -960,6 +989,41 @@ static const struct vdpa_config_ops vduse_vdpa_config_ops = {
.free = vduse_vdpa_free,
};
+static const struct vdpa_config_ops vduse_vdpa_config_ops_with_suspend = {
+ .set_vq_address = vduse_vdpa_set_vq_address,
+ .kick_vq = vduse_vdpa_kick_vq,
+ .set_vq_cb = vduse_vdpa_set_vq_cb,
+ .set_vq_num = vduse_vdpa_set_vq_num,
+ .get_vq_size = vduse_vdpa_get_vq_size,
+ .get_vq_group = vduse_get_vq_group,
+ .set_vq_ready = vduse_vdpa_set_vq_ready,
+ .get_vq_ready = vduse_vdpa_get_vq_ready,
+ .set_vq_state = vduse_vdpa_set_vq_state,
+ .get_vq_state = vduse_vdpa_get_vq_state,
+ .get_vq_align = vduse_vdpa_get_vq_align,
+ .get_device_features = vduse_vdpa_get_device_features,
+ .set_driver_features = vduse_vdpa_set_driver_features,
+ .get_driver_features = vduse_vdpa_get_driver_features,
+ .set_config_cb = vduse_vdpa_set_config_cb,
+ .get_vq_num_max = vduse_vdpa_get_vq_num_max,
+ .get_device_id = vduse_vdpa_get_device_id,
+ .get_vendor_id = vduse_vdpa_get_vendor_id,
+ .get_status = vduse_vdpa_get_status,
+ .set_status = vduse_vdpa_set_status,
+ .get_config_size = vduse_vdpa_get_config_size,
+ .get_config = vduse_vdpa_get_config,
+ .set_config = vduse_vdpa_set_config,
+ .get_generation = vduse_vdpa_get_generation,
+ .set_vq_affinity = vduse_vdpa_set_vq_affinity,
+ .get_vq_affinity = vduse_vdpa_get_vq_affinity,
+ .reset = vduse_vdpa_reset,
+ .set_map = vduse_vdpa_set_map,
+ .set_group_asid = vduse_set_group_asid,
+ .get_vq_map = vduse_get_vq_map,
+ .suspend = vduse_vdpa_suspend,
+ .free = vduse_vdpa_free,
+};
+
static void vduse_dev_sync_single_for_device(union virtio_map token,
dma_addr_t dma_addr, size_t size,
enum dma_data_direction dir)
@@ -1171,6 +1235,10 @@ static void vduse_dev_irq_inject(struct work_struct *work)
{
struct vduse_dev *dev = container_of(work, struct vduse_dev, inject);
+ guard(rwsem_read)(&dev->rwsem);
+ if (dev->suspended)
+ return;
+
spin_lock_bh(&dev->irq_lock);
if (dev->config_cb.callback)
dev->config_cb.callback(dev->config_cb.private);
@@ -1182,6 +1250,10 @@ static void vduse_vq_irq_inject(struct work_struct *work)
struct vduse_virtqueue *vq = container_of(work,
struct vduse_virtqueue, inject);
+ guard(rwsem_read)(&vq->dev->rwsem);
+ if (vq->dev->suspended)
+ return;
+
spin_lock_bh(&vq->irq_lock);
if (vq->ready && vq->cb.callback)
vq->cb.callback(vq->cb.private);
@@ -1212,6 +1284,9 @@ static int vduse_dev_queue_irq_work(struct vduse_dev *dev,
int ret = -EINVAL;
down_read(&dev->rwsem);
+ if (dev->suspended)
+ return ret;
+
if (!(dev->status & VIRTIO_CONFIG_S_DRIVER_OK))
goto unlock;
@@ -1976,6 +2051,7 @@ static int vduse_dev_init_vqs(struct vduse_dev *dev, u32 vq_align, u32 vq_num)
}
dev->vqs[i]->index = i;
+ dev->vqs[i]->dev = dev;
dev->vqs[i]->irq_effective_cpu = IRQ_UNBOUND;
INIT_WORK(&dev->vqs[i]->inject, vduse_vq_irq_inject);
INIT_WORK(&dev->vqs[i]->kick, vduse_vq_kick_work);
@@ -2426,12 +2502,18 @@ static struct vduse_mgmt_dev *vduse_mgmt;
static int vduse_dev_init_vdpa(struct vduse_dev *dev, const char *name)
{
struct vduse_vdpa *vdev;
+ const struct vdpa_config_ops *ops;
if (dev->vdev)
return -EEXIST;
+ if (dev->vduse_features & BIT_U64(VDUSE_F_SUSPEND))
+ ops = &vduse_vdpa_config_ops_with_suspend;
+ else
+ ops = &vduse_vdpa_config_ops;
+
vdev = vdpa_alloc_device(struct vduse_vdpa, vdpa, dev->dev,
- &vduse_vdpa_config_ops, &vduse_map_ops,
+ ops, &vduse_map_ops,
dev->ngroups, dev->nas, name, true);
if (IS_ERR(vdev))
return PTR_ERR(vdev);
diff --git a/include/uapi/linux/vduse.h b/include/uapi/linux/vduse.h
index 7324faea5df4..8c616895c511 100644
--- a/include/uapi/linux/vduse.h
+++ b/include/uapi/linux/vduse.h
@@ -17,6 +17,9 @@
/* The VDUSE instance expects a request for vq ready */
#define VDUSE_F_QUEUE_READY 0
+/* The VDUSE instance expects a request for suspend */
+#define VDUSE_F_SUSPEND 1
+
/*
* Get the version of VDUSE API that kernel supported (VDUSE_API_VERSION).
* This is used for future extension.
@@ -334,6 +337,7 @@ enum vduse_req_type {
VDUSE_UPDATE_IOTLB,
VDUSE_SET_VQ_GROUP_ASID,
VDUSE_SET_VQ_READY,
+ VDUSE_SUSPEND,
};
/**
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 0/2] vsock: fold acceptq accounting into core helpers
From: Raf Dickson @ 2026-06-10 9:11 UTC (permalink / raw)
To: netdev, virtualization
Cc: pabeni, sgarzare, stefanha, bryan-bt.tan, vishnu.dasa,
bcm-kernel-feedback-list, Raf Dickson
These two patches follow up on commit c05fa14db43e
("vsock/vmci: fix sk_ack_backlog leak on failed handshake")
by folding sk_acceptq_added() and sk_acceptq_removed() into
vsock_enqueue_accept() and vsock_remove_pending() respectively,
as suggested by Paolo Abeni and Stefano Garzarella.
This makes the accounting automatic and prevents future callers
from forgetting it.
Raf Dickson (2):
vsock: fold sk_acceptq_added() into vsock_enqueue_accept()
vsock: fold sk_acceptq_removed() into vsock_remove_pending()
net/vmw_vsock/af_vsock.c | 4 ++--
net/vmw_vsock/hyperv_transport.c | 1 -
net/vmw_vsock/virtio_transport_common.c | 1 -
net/vmw_vsock/vmci_transport.c | 6 +-----
4 files changed, 3 insertions(+), 9 deletions(-)
--
2.54.0
^ permalink raw reply
* [PATCH net-next 1/2] vsock: fold sk_acceptq_added() into vsock_enqueue_accept()
From: Raf Dickson @ 2026-06-10 9:11 UTC (permalink / raw)
To: netdev, virtualization
Cc: pabeni, sgarzare, stefanha, bryan-bt.tan, vishnu.dasa,
bcm-kernel-feedback-list, Raf Dickson
In-Reply-To: <20260610091121.213324-1-rafdog35@gmail.com>
All three transports (vmci, virtio, hyperv) call sk_acceptq_added()
immediately before vsock_enqueue_accept(). Move the call into
vsock_enqueue_accept() itself so callers cannot forget it and the
accounting is always consistent.
Suggested-by: Paolo Abeni <pabeni@redhat.com>
Suggested-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Raf Dickson <rafdog35@gmail.com>
---
net/vmw_vsock/af_vsock.c | 1 +
net/vmw_vsock/hyperv_transport.c | 1 -
net/vmw_vsock/virtio_transport_common.c | 1 -
net/vmw_vsock/vmci_transport.c | 1 -
4 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index 2ce1063d4a..73e6416ee9 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -507,6 +507,7 @@ void vsock_enqueue_accept(struct sock *listener, struct sock *connected)
sock_hold(connected);
sock_hold(listener);
list_add_tail(&vconnected->accept_queue, &vlistener->accept_queue);
+ sk_acceptq_added(listener);
}
EXPORT_SYMBOL_GPL(vsock_enqueue_accept);
diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c
index b3394946b2..0de8148877 100644
--- a/net/vmw_vsock/hyperv_transport.c
+++ b/net/vmw_vsock/hyperv_transport.c
@@ -410,7 +410,6 @@ static void hvs_open_connection(struct vmbus_channel *chan)
if (conn_from_host) {
new->sk_state = TCP_ESTABLISHED;
- sk_acceptq_added(sk);
hvs_new->vm_srv_id = *if_type;
hvs_new->host_srv_id = *if_instance;
diff --git a/net/vmw_vsock/virtio_transport_common.c b/net/vmw_vsock/virtio_transport_common.c
index b10666937c..4a39d48db9 100644
--- a/net/vmw_vsock/virtio_transport_common.c
+++ b/net/vmw_vsock/virtio_transport_common.c
@@ -1582,7 +1582,6 @@ virtio_transport_recv_listen(struct sock *sk, struct sk_buff *skb,
return ret;
}
- sk_acceptq_added(sk);
if (virtio_transport_space_update(child, skb))
child->sk_write_space(child);
diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c
index 91516488a7..4ce6660c11 100644
--- a/net/vmw_vsock/vmci_transport.c
+++ b/net/vmw_vsock/vmci_transport.c
@@ -1109,7 +1109,6 @@ static int vmci_transport_recv_listen(struct sock *sk,
}
vsock_add_pending(sk, pending);
- sk_acceptq_added(sk);
pending->sk_state = TCP_SYN_SENT;
vmci_trans(vpending)->produce_size =
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 2/2] vsock: fold sk_acceptq_removed() into vsock_remove_pending()
From: Raf Dickson @ 2026-06-10 9:11 UTC (permalink / raw)
To: netdev, virtualization
Cc: pabeni, sgarzare, stefanha, bryan-bt.tan, vishnu.dasa,
bcm-kernel-feedback-list, Raf Dickson
In-Reply-To: <20260610091121.213324-1-rafdog35@gmail.com>
Callers of vsock_remove_pending() must also call sk_acceptq_removed()
to keep sk_ack_backlog consistent. Move the call into
vsock_remove_pending() itself to make it automatic and prevent future
callers from forgetting it.
Suggested-by: Stefano Garzarella <sgarzare@redhat.com>
Signed-off-by: Raf Dickson <rafdog35@gmail.com>
---
net/vmw_vsock/af_vsock.c | 3 +--
net/vmw_vsock/vmci_transport.c | 5 +----
2 files changed, 2 insertions(+), 6 deletions(-)
diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
index 73e6416ee9..b9772a0205 100644
--- a/net/vmw_vsock/af_vsock.c
+++ b/net/vmw_vsock/af_vsock.c
@@ -493,6 +493,7 @@ void vsock_remove_pending(struct sock *listener, struct sock *pending)
list_del_init(&vpending->pending_links);
sock_put(listener);
sock_put(pending);
+ sk_acceptq_removed(listener);
}
EXPORT_SYMBOL_GPL(vsock_remove_pending);
@@ -761,8 +762,6 @@ static void vsock_pending_work(struct work_struct *work)
if (vsock_is_pending(sk)) {
vsock_remove_pending(listener, sk);
-
- sk_acceptq_removed(listener);
} else if (!vsk->rejected) {
/* We are not on the pending list and accept() did not reject
* us, so we must have been accepted by our user process. We
diff --git a/net/vmw_vsock/vmci_transport.c b/net/vmw_vsock/vmci_transport.c
index 4ce6660c11..356bebca7c 100644
--- a/net/vmw_vsock/vmci_transport.c
+++ b/net/vmw_vsock/vmci_transport.c
@@ -980,11 +980,8 @@ static int vmci_transport_recv_listen(struct sock *sk,
err = -EINVAL;
}
- if (err < 0) {
+ if (err < 0)
vsock_remove_pending(sk, pending);
- sk_acceptq_removed(sk);
- }
-
release_sock(pending);
vmci_transport_release_pending(pending);
--
2.54.0
^ permalink raw reply related
* Re: [PATCH net-next] virtio-net: support xsk wake up
From: Menglong Dong @ 2026-06-10 9:15 UTC (permalink / raw)
To: Eugenio Perez Martin
Cc: xuanzhuo, mst, jasowang, andrew+netdev, davem, edumazet, kuba,
pabeni, netdev, virtualization, linux-kernel
In-Reply-To: <CAJaqyWeYiruNosJsMTh2jQ=XCEcPg7956aqeRRpDSyynfpjNZA@mail.gmail.com>
On Wed, Jun 10, 2026 at 4:28 PM Eugenio Perez Martin
<eperezma@redhat.com> wrote:
>
> On Wed, Jun 10, 2026 at 10:17 AM Menglong Dong <menglong8.dong@gmail.com> wrote:
> >
> > For now, XDP_RING_NEED_WAKEUP is not supported properly by the virtio-net.
> > Take the tx path for example, we set xsk_set_tx_need_wakeup() in
> > virtnet_xsk_xmit(), but we didn't call xsk_clear_tx_need_wakeup()
> > anywhere, which means the user will call send() for every packet.
> >
> > For the tx path, we will call xsk_set_tx_need_wakeup() after
> > virtnet_xsk_xmit_batch() if sq->vq is empty, as we can't be wakeup by the
> > skb_xmit_done() in this case. Otherwise, we will clear the wakeup flag.
> >
> > For the rx path, we will call xsk_set_rx_need_wakeup() if we have free
> > buffers in rq->vq.
> >
> > Race condition is considered for both rx and tx path.
> >
>
> I didn't review the code itself, but the patch message lacks a Fixes:
Yeah, you are right, it's better to use the Fix tag here, and I'll
add it in the next version.
> tag. And the From and Signed-off-by emails don't match, which I'm not
> sure is valid.
Sorry about that. I use my company email in the SOB. I used
my company email for the From too. However, my company
email system can trigger "Delivery failure" messages sometimes,
and Alexei said it is annoying. So I didn't use it for the From
and removed it from the CC since then :/
Thanks!
Menglong Dong
>
> > Signed-off-by: Menglong Dong <dongml2@chinatelecom.cn>
> > ---
> > drivers/net/virtio_net.c | 52 ++++++++++++++++++++++++++++++++++------
> > 1 file changed, 45 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> > index 9b3da9f9786c..25e895b849a6 100644
> > --- a/drivers/net/virtio_net.c
> > +++ b/drivers/net/virtio_net.c
[...]
> >
> > --
> > 2.54.0
> >
>
^ permalink raw reply
* [PATCH] 9p/trans_virtio: bound mount_tag show copy to one page
From: Michael Bommarito @ 2026-06-10 11:42 UTC (permalink / raw)
To: Eric Van Hensbergen, Latchesar Ionkov, Dominique Martinet,
Christian Schoenebeck
Cc: v9fs, virtualization, linux-kernel, stable
p9_mount_tag_show() copies strlen(chan->tag) + 1 bytes into the
single-page buffer the sysfs core provides, with no upper bound. The
mount tag length comes from virtio_9p_config.tag_len, a 16-bit field read
from the device at probe in p9_virtio_probe() with no cap. Under the
confidential-computing threat model, where the guest does not trust the
host, a malicious or compromised host can present a 65535-byte tag with
no embedded NUL. A read of the world-readable /sys/.../mount_tag
attribute (udev reads it at probe) then copies ~64 KiB into the 4 KiB
sysfs page, a slab-out-of-bounds write of host-controlled content.
Bound the copy to the page size in the show handler.
Fixes: 179a5bc4b8cb ("net/9p: use memcpy() instead of snprintf() in p9_mount_tag_show()")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
net/9p/trans_virtio.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c
index 4cdab7094b273..b62aa7b309f1c 100644
--- a/net/9p/trans_virtio.c
+++ b/net/9p/trans_virtio.c
@@ -573,7 +573,11 @@ static ssize_t p9_mount_tag_show(struct device *dev,
chan = vdev->priv;
tag_len = strlen(chan->tag);
- memcpy(buf, chan->tag, tag_len + 1);
+ if (tag_len > PAGE_SIZE - 2)
+ tag_len = PAGE_SIZE - 2;
+
+ memcpy(buf, chan->tag, tag_len);
+ buf[tag_len] = '\0';
return tag_len + 1;
}
--
2.53.0
^ permalink raw reply related
* Re: [PATCH net] virtio_net: do not allow tunnel csum offload for non GSO packets
From: Gabriel Goller @ 2026-06-10 12:19 UTC (permalink / raw)
To: Paolo Abeni
Cc: netdev, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Eugenio Pérez, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, virtualization, Willem de Bruijn
In-Reply-To: <38d33d63ce5d1aee486bd6c7c47345040d526e35.1781016169.git.pabeni@redhat.com>
On Tue, 09 Jun 2026 16:44:26 +0200, Paolo Abeni <pabeni@redhat.com> wrote:
> [...]
>
> Fixes: 56a06bd40fab ("virtio_net: enable gso over UDP tunnel support.")
> Reported-by: Fiona Ebner <f.ebner@proxmox.com>
> Closes: https://bugzilla.proxmox.com/show_bug.cgi?id=7627
> Tested-by: Fiona Ebner <f.ebner@proxmox.com>
> Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Gave it a spin and it works alright, so consider:
>
>
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index f4adcfee7a80..07b8710639f9 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -6222,6 +6222,18 @@ static void virtnet_free_irq_moder(struct virtnet_info *vi)
> rtnl_unlock();
> }
>
> +static netdev_features_t virtnet_features_check(struct sk_buff *skb,
> + struct net_device *dev,
> + netdev_features_t features)
> +{
> + /* Inner csum offload is only available for GSO packets. */
> + if (skb->encapsulation && !skb_is_gso(skb))
A small question -- should we maybe check for skb_gso_ok here as well?
So add:
(!skb_is_gso(skb) || !skb_gso_ok(skb, features)))
Because skb_is_gso alone doesn't guarantee that the packets leaving virtio will
be gso'd, they could be software gso'd at validate_xmit_skb, which is called
after ndo_feature_check.
leaving the virtio device.
Not sure if this can happen though.
> @@ -6235,7 +6247,7 @@ static const struct net_device_ops virtnet_netdev = {
> .ndo_bpf = virtnet_xdp,
> .ndo_xdp_xmit = virtnet_xdp_xmit,
> .ndo_xsk_wakeup = virtnet_xsk_wakeup,
> - .ndo_features_check = passthru_features_check,
> + .ndo_features_check = virtnet_features_check,
> .ndo_get_phys_port_name = virtnet_get_phys_port_name,
> .ndo_set_features = virtnet_set_features,
> .ndo_tx_timeout = virtnet_tx_timeout,
Thanks,
Gabriel
Tested-by: Gabriel Goller <g.goller@proxmox.com>
So: packet is a GSO packet but will be segmented by validate_xmit_skb before
So: packet is a GSO packet but will be segmented by validate_xmit_skb before
--
Gabriel Goller <g.goller@proxmox.com>
^ permalink raw reply
* Re: [PATCH net] virtio_net: do not allow tunnel csum offload for non GSO packets
From: Gabriel Goller @ 2026-06-10 12:26 UTC (permalink / raw)
To: Gabriel Goller
Cc: Paolo Abeni, netdev, Michael S. Tsirkin, Jason Wang, Xuan Zhuo,
Eugenio Pérez, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, virtualization, Willem de Bruijn
In-Reply-To: <178109396600.129329.5073003425673349392.b4-review@b4>
On 2026-06-10 14:19:26+02:00, Gabriel Goller wrote:
> On Tue, 09 Jun 2026 16:44:26 +0200, Paolo Abeni <pabeni@redhat.com> wrote:
>
> > [...]
> >
> > Fixes: 56a06bd40fab ("virtio_net: enable gso over UDP tunnel support.")
> > Reported-by: Fiona Ebner <f.ebner@proxmox.com>
> > Closes: https://bugzilla.proxmox.com/show_bug.cgi?id=7627
> > Tested-by: Fiona Ebner <f.ebner@proxmox.com>
> > Signed-off-by: Paolo Abeni <pabeni@redhat.com>
>
> Gave it a spin and it works alright, so consider:
Tested-by: Gabriel Goller <g.goller@proxmox.com>
Missed this :(
> > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> > index f4adcfee7a80..07b8710639f9 100644
> > --- a/drivers/net/virtio_net.c
> > +++ b/drivers/net/virtio_net.c
> > @@ -6222,6 +6222,18 @@ static void virtnet_free_irq_moder(struct virtnet_info *vi)
> > rtnl_unlock();
> > }
> >
> > +static netdev_features_t virtnet_features_check(struct sk_buff *skb,
> > + struct net_device *dev,
> > + netdev_features_t features)
> > +{
> > + /* Inner csum offload is only available for GSO packets. */
> > + if (skb->encapsulation && !skb_is_gso(skb))
>
> A small question -- should we maybe check for skb_gso_ok here as well?
> So add:
>
> (!skb_is_gso(skb) || !skb_gso_ok(skb, features)))
>
> Because skb_is_gso alone doesn't guarantee that the packets leaving virtio will
> be gso'd, they could be software gso'd at validate_xmit_skb, which is called
> after ndo_feature_check.
> leaving the virtio device.
This is supposed to say:
So packet is a GSO packet but will be segmented by validate_xmit_skb before
leaving the virtio device.
b4 thought "So:" is a git trailer :(
> Not sure if this can happen though.
>
> > @@ -6235,7 +6247,7 @@ static const struct net_device_ops virtnet_netdev = {
> > .ndo_bpf = virtnet_xdp,
> > .ndo_xdp_xmit = virtnet_xdp_xmit,
> > .ndo_xsk_wakeup = virtnet_xsk_wakeup,
> > - .ndo_features_check = passthru_features_check,
> > + .ndo_features_check = virtnet_features_check,
> > .ndo_get_phys_port_name = virtnet_get_phys_port_name,
> > .ndo_set_features = virtnet_set_features,
> > .ndo_tx_timeout = virtnet_tx_timeout,
>
> Thanks,
> Gabriel
Sorry for the noise.
^ permalink raw reply
* [PATCH v2] mm: page_reporting: allow driver to set batch capacity
From: Michael S. Tsirkin @ 2026-06-10 13:17 UTC (permalink / raw)
To: linux-kernel
Cc: Miaohe Lin, David Hildenbrand (Arm), Jason Wang, Xuan Zhuo,
Eugenio Perez, Muchun Song, Oscar Salvador, Andrew Morton,
Lorenzo Stoakes, Liam R. Howlett, Vlastimil Babka, Mike Rapoport,
Suren Baghdasaryan, Michal Hocko, Brendan Jackman,
Johannes Weiner, Zi Yan, Baolin Wang, Nico Pache, Ryan Roberts,
Dev Jain, Barry Song, Lance Yang, Hugh Dickins, Matthew Brost,
Joshua Hahn, Rakie Kim, Byungchul Park, Gregory Price, Ying Huang,
Alistair Popple, Christoph Lameter, David Rientjes,
Roman Gushchin, Harry Yoo, Axel Rasmussen, Yuanchu Xie, Wei Xu,
Chris Li, Kairui Song, Kemeng Shi, Nhat Pham, Baoquan He,
virtualization, linux-mm, Andrea Arcangeli, Naoya Horiguchi,
Alexander Duyck
At the moment, if a virtio balloon device has a page reporting vq but
its size is < PAGE_REPORTING_CAPACITY (32), the balloon driver fails
probe.
But, there's no way for host to know this value, so it can easily
create a smaller vq and suddenly adding the reporting capability
to the device makes all of the driver fail. Not pretty.
Add a capacity field to page_reporting_dev_info so drivers can
control the maximum number of pages per report batch.
In virtio-balloon, set the capacity to the reporting virtqueue size,
letting page_reporting adapt to whatever the device provides.
Capacity need not be a power of two. Code previously called out
division by PAGE_REPORTING_CAPACITY as cheap since it was a power
of 2, but no performance difference was observed with non-power-of-2
values.
If capacity is 0 or exceeds PAGE_REPORTING_CAPACITY, it defaults
to PAGE_REPORTING_CAPACITY. The 0 check and the clamping is done in
page_reporting_register(), before the reporting work is scheduled,
so we never get division by 0.
Fixes: b0c504f15471 ("virtio-balloon: add support for providing free page reports to host")
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Assisted-by: Claude:claude-opus-4-6
---
Changes v1->v2:
- Document capacity=0 as default in commit log
- Document that capacity need not be a power of two
- Drop unnecessary comment about integer division cost
- Update comment on capacity field: "0 (default) means PAGE_REPORTING_CAPACITY"
drivers/virtio/virtio_balloon.c | 5 +----
include/linux/page_reporting.h | 3 +++
mm/page_reporting.c | 24 ++++++++++++------------
3 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/drivers/virtio/virtio_balloon.c b/drivers/virtio/virtio_balloon.c
index f6c2dff33f8a..6a1a610c2cb1 100644
--- a/drivers/virtio/virtio_balloon.c
+++ b/drivers/virtio/virtio_balloon.c
@@ -1017,10 +1017,6 @@ static int virtballoon_probe(struct virtio_device *vdev)
unsigned int capacity;
capacity = virtqueue_get_vring_size(vb->reporting_vq);
- if (capacity < PAGE_REPORTING_CAPACITY) {
- err = -ENOSPC;
- goto out_unregister_oom;
- }
vb->pr_dev_info.order = PAGE_REPORTING_ORDER_UNSPECIFIED;
@@ -1041,6 +1037,7 @@ static int virtballoon_probe(struct virtio_device *vdev)
vb->pr_dev_info.order = 5;
#endif
+ vb->pr_dev_info.capacity = capacity;
err = page_reporting_register(&vb->pr_dev_info);
if (err)
goto out_unregister_oom;
diff --git a/include/linux/page_reporting.h b/include/linux/page_reporting.h
index 9d4ca5c218a0..048578118a4b 100644
--- a/include/linux/page_reporting.h
+++ b/include/linux/page_reporting.h
@@ -22,6 +22,9 @@ struct page_reporting_dev_info {
/* Minimal order of page reporting */
unsigned int order;
+
+ /* Max pages per report batch; 0 (default) means PAGE_REPORTING_CAPACITY */
+ unsigned int capacity;
};
/* Tear-down and bring-up for page reporting devices */
diff --git a/mm/page_reporting.c b/mm/page_reporting.c
index 7418f2e500bb..942e84b6908a 100644
--- a/mm/page_reporting.c
+++ b/mm/page_reporting.c
@@ -173,11 +173,8 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone,
* any pages that may have already been present from the previous
* list processed. This should result in us reporting all pages on
* an idle system in about 30 seconds.
- *
- * The division here should be cheap since PAGE_REPORTING_CAPACITY
- * should always be a power of 2.
*/
- budget = DIV_ROUND_UP(area->nr_free, PAGE_REPORTING_CAPACITY * 16);
+ budget = DIV_ROUND_UP(area->nr_free, prdev->capacity * 16);
/* loop through free list adding unreported pages to sg list */
list_for_each_entry_safe(page, next, list, lru) {
@@ -222,10 +219,10 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone,
spin_unlock_irq(&zone->lock);
/* begin processing pages in local list */
- err = prdev->report(prdev, sgl, PAGE_REPORTING_CAPACITY);
+ err = prdev->report(prdev, sgl, prdev->capacity);
/* reset offset since the full list was reported */
- *offset = PAGE_REPORTING_CAPACITY;
+ *offset = prdev->capacity;
/* update budget to reflect call to report function */
budget--;
@@ -234,7 +231,7 @@ page_reporting_cycle(struct page_reporting_dev_info *prdev, struct zone *zone,
spin_lock_irq(&zone->lock);
/* flush reported pages from the sg list */
- page_reporting_drain(prdev, sgl, PAGE_REPORTING_CAPACITY, !err);
+ page_reporting_drain(prdev, sgl, prdev->capacity, !err);
/*
* Reset next to first entry, the old next isn't valid
@@ -260,13 +257,13 @@ static int
page_reporting_process_zone(struct page_reporting_dev_info *prdev,
struct scatterlist *sgl, struct zone *zone)
{
- unsigned int order, mt, leftover, offset = PAGE_REPORTING_CAPACITY;
+ unsigned int order, mt, leftover, offset = prdev->capacity;
unsigned long watermark;
int err = 0;
/* Generate minimum watermark to be able to guarantee progress */
watermark = low_wmark_pages(zone) +
- (PAGE_REPORTING_CAPACITY << page_reporting_order);
+ (prdev->capacity << page_reporting_order);
/*
* Cancel request if insufficient free memory or if we failed
@@ -290,7 +287,7 @@ page_reporting_process_zone(struct page_reporting_dev_info *prdev,
}
/* report the leftover pages before going idle */
- leftover = PAGE_REPORTING_CAPACITY - offset;
+ leftover = prdev->capacity - offset;
if (leftover) {
sgl = &sgl[offset];
err = prdev->report(prdev, sgl, leftover);
@@ -322,11 +319,11 @@ static void page_reporting_process(struct work_struct *work)
atomic_set(&prdev->state, state);
/* allocate scatterlist to store pages being reported on */
- sgl = kmalloc_objs(*sgl, PAGE_REPORTING_CAPACITY);
+ sgl = kmalloc_objs(*sgl, prdev->capacity);
if (!sgl)
goto err_out;
- sg_init_table(sgl, PAGE_REPORTING_CAPACITY);
+ sg_init_table(sgl, prdev->capacity);
for_each_zone(zone) {
err = page_reporting_process_zone(prdev, sgl, zone);
@@ -377,6 +374,9 @@ int page_reporting_register(struct page_reporting_dev_info *prdev)
page_reporting_order = pageblock_order;
}
+ if (!prdev->capacity || prdev->capacity > PAGE_REPORTING_CAPACITY)
+ prdev->capacity = PAGE_REPORTING_CAPACITY;
+
/* initialize state and work structures */
atomic_set(&prdev->state, PAGE_REPORTING_IDLE);
INIT_DELAYED_WORK(&prdev->work, &page_reporting_process);
--
MST
^ permalink raw reply related
* [PATCH v4] i2c: virtio: retain xfer with kref to fix UAF on interrupted wait
From: Gavin Li @ 2026-06-10 15:02 UTC (permalink / raw)
To: linux-i2c, viresh.kumar, Michael S. Tsirkin
Cc: Chen, Jian Jun, andi.shyti, virtualization, Gavin Li
commit a663b3c47ab1 ("i2c: virtio: Avoid hang by using interruptible
completion wait") switched virtio_i2c_complete_reqs() to
wait_for_completion_interruptible() so a stuck device cannot hang a
task forever. That left a use-after-free: if the wait returns early on
a signal, virtio_i2c_xfer() frees reqs and DMA bounce buffers while the
device may still hold virtqueue tokens pointing at &reqs[i] and DMA
into read buffers. When those requests complete later,
virtio_i2c_msg_done() calls complete() on freed memory.
Waiting uninterruptibly for every completion before freeing avoids the
UAF but can hang the caller indefinitely if the virtio side never
completes the request. Performing a queue reset / device reset is
possible in this scenario, but adds complexity.
This commit manages the freeing of the xfer allocations via kref, and
ensures that each in-flight request holds a reference. This fixes the
use-after-free by ensuring that the virtio device has a valid location
to write to until the request completes. This will cause a memory leak
in cases where the device hangs, but that is much preferable to memory
corruption.
Additionally, force usage of a bounce buffer even if the i2c_msg buf is
DMA-safe, since the buffer passed to the virtqueue must remain valid
even if the transfer is interrupted. Remove usage of
i2c_get_dma_safe_msg_buf() since it may pass through msg->buf directly.
All bounce buffers are part of the single xfer allocation, so there is
no additional allocation overhead.
Signed-off-by: Gavin Li <gavin.li@samsara.com>
---
Changes in v4:
- Pack bounce buffers into a single allocation after reqs[]
- Remove per-request buf pointer and xfer->num
- Remove req.msg, track message direction with req->read
- Simplify xfer release to a single kfree()
- Restore using j to track successful transfers in _complete_xfer()
---
drivers/i2c/busses/i2c-virtio.c | 121 +++++++++++++++++++++++++-------
1 file changed, 94 insertions(+), 27 deletions(-)
diff --git a/drivers/i2c/busses/i2c-virtio.c b/drivers/i2c/busses/i2c-virtio.c
index 5da6fef92bec3..33b469ca39d4b 100644
--- a/drivers/i2c/busses/i2c-virtio.c
+++ b/drivers/i2c/busses/i2c-virtio.c
@@ -13,7 +13,9 @@
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
+#include <linux/kref.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/virtio.h>
#include <linux/virtio_ids.h>
#include <linux/virtio_config.h>
@@ -31,39 +33,74 @@ struct virtio_i2c {
struct virtqueue *vq;
};
+struct virtio_i2c_xfer;
+
/**
* struct virtio_i2c_req - the virtio I2C request structure
+ * @xfer: owning transfer
* @completion: completion of virtio I2C message
+ * @read: true if this is a read message (I2C_M_RD is set)
* @out_hdr: the OUT header of the virtio I2C message
- * @buf: the buffer into which data is read, or from which it's written
* @in_hdr: the IN header of the virtio I2C message
*/
struct virtio_i2c_req {
+ struct virtio_i2c_xfer *xfer;
struct completion completion;
+ bool read;
struct virtio_i2c_out_hdr out_hdr ____cacheline_aligned;
- uint8_t *buf ____cacheline_aligned;
struct virtio_i2c_in_hdr in_hdr ____cacheline_aligned;
};
+/**
+ * struct virtio_i2c_xfer - a queued I2C transfer
+ * @ref: one ref for the caller, plus one per in-flight virtqueue request
+ * @bounce_buf_base: start of bounce buffer region
+ * @reqs: the virtio I2C requests
+ *
+ * Allocation layout:
+ * - struct virtio_i2c_xfer xfer
+ * - struct virtio_i2c_req reqs[num]
+ * - u8 bounce_buf[msgs[0].len]
+ * ...
+ * - u8 bounce_buf[msgs[num-1].len]
+ */
+struct virtio_i2c_xfer {
+ struct kref ref;
+ u8 *bounce_buf_base;
+ struct virtio_i2c_req reqs[];
+};
+
+static void virtio_i2c_xfer_release(struct kref *ref)
+{
+ struct virtio_i2c_xfer *xfer = container_of(ref, struct virtio_i2c_xfer, ref);
+ kfree(xfer);
+}
+
static void virtio_i2c_msg_done(struct virtqueue *vq)
{
struct virtio_i2c_req *req;
unsigned int len;
- while ((req = virtqueue_get_buf(vq, &len)))
+ while ((req = virtqueue_get_buf(vq, &len))) {
complete(&req->completion);
+ kref_put(&req->xfer->ref, virtio_i2c_xfer_release);
+ }
}
-static int virtio_i2c_prepare_reqs(struct virtqueue *vq,
- struct virtio_i2c_req *reqs,
+static int virtio_i2c_prepare_xfer(struct virtqueue *vq,
+ struct virtio_i2c_xfer *xfer,
struct i2c_msg *msgs, int num)
{
struct scatterlist *sgs[3], out_hdr, msg_buf, in_hdr;
+ struct virtio_i2c_req *reqs = xfer->reqs;
+ u8 *bounce_buf = xfer->bounce_buf_base;
int i;
for (i = 0; i < num; i++) {
int outcnt = 0, incnt = 0;
+ reqs[i].xfer = xfer;
+ reqs[i].read = !!(msgs[i].flags & I2C_M_RD);
init_completion(&reqs[i].completion);
/*
@@ -82,23 +119,31 @@ static int virtio_i2c_prepare_reqs(struct virtqueue *vq,
sgs[outcnt++] = &out_hdr;
if (msgs[i].len) {
- reqs[i].buf = i2c_get_dma_safe_msg_buf(&msgs[i], 1);
- if (!reqs[i].buf)
- break;
+ /*
+ * Even if msg->flags has I2C_M_DMA_SAFE set, a bounce
+ * buffer is required because the transfer may be
+ * interrupted, after which msg->buf is no longer valid.
+ */
+ if (!(msgs[i].flags & I2C_M_RD))
+ memcpy(bounce_buf, msgs[i].buf, msgs[i].len);
- sg_init_one(&msg_buf, reqs[i].buf, msgs[i].len);
+ sg_init_one(&msg_buf, bounce_buf, msgs[i].len);
if (msgs[i].flags & I2C_M_RD)
sgs[outcnt + incnt++] = &msg_buf;
else
sgs[outcnt++] = &msg_buf;
}
+ bounce_buf += msgs[i].len;
sg_init_one(&in_hdr, &reqs[i].in_hdr, sizeof(reqs[i].in_hdr));
sgs[outcnt + incnt++] = &in_hdr;
+ /* This reference is released in virtio_i2c_msg_done(). */
+ kref_get(&xfer->ref);
+
if (virtqueue_add_sgs(vq, sgs, outcnt, incnt, &reqs[i], GFP_KERNEL)) {
- i2c_put_dma_safe_msg_buf(reqs[i].buf, &msgs[i], false);
+ kref_put(&xfer->ref, virtio_i2c_xfer_release);
break;
}
}
@@ -106,26 +151,38 @@ static int virtio_i2c_prepare_reqs(struct virtqueue *vq,
return i;
}
-static int virtio_i2c_complete_reqs(struct virtqueue *vq,
- struct virtio_i2c_req *reqs,
- struct i2c_msg *msgs, int num)
+static int virtio_i2c_complete_xfer(struct virtio_i2c_xfer *xfer,
+ struct i2c_msg *msgs,
+ int num)
{
+ struct virtio_i2c_req *reqs = xfer->reqs;
+ u8 *bounce_buf = xfer->bounce_buf_base;
bool failed = false;
int i, j = 0;
for (i = 0; i < num; i++) {
struct virtio_i2c_req *req = &reqs[i];
+ struct i2c_msg *msg = &msgs[i];
+
+ if (wait_for_completion_interruptible(&req->completion))
+ return -EINTR;
+
+ if (req->in_hdr.status != VIRTIO_I2C_MSG_OK) {
+ /*
+ * Don't break yet. Try to wait until all requests
+ * complete to ensure that the virtqueue has enough
+ * descriptor slots for the next transfer.
+ */
+ failed = true;
+ }
if (!failed) {
- if (wait_for_completion_interruptible(&req->completion))
- failed = true;
- else if (req->in_hdr.status != VIRTIO_I2C_MSG_OK)
- failed = true;
- else
- j++;
+ if (req->read)
+ memcpy(msg->buf, bounce_buf, msg->len);
+ j++;
}
- i2c_put_dma_safe_msg_buf(reqs[i].buf, &msgs[i], !failed);
+ bounce_buf += msg->len;
}
return j;
@@ -136,14 +193,24 @@ static int virtio_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
{
struct virtio_i2c *vi = i2c_get_adapdata(adap);
struct virtqueue *vq = vi->vq;
- struct virtio_i2c_req *reqs;
- int count;
+ struct virtio_i2c_xfer *xfer;
+ size_t alloc_size;
+ int i, count;
- reqs = kzalloc_objs(*reqs, num);
- if (!reqs)
+ alloc_size = struct_size(xfer, reqs, num);
+ for (i = 0; i < num; i++) {
+ if (check_add_overflow(alloc_size, msgs[i].len, &alloc_size))
+ return -EOVERFLOW;
+ }
+
+ xfer = kzalloc(alloc_size, GFP_KERNEL);
+ if (!xfer)
return -ENOMEM;
- count = virtio_i2c_prepare_reqs(vq, reqs, msgs, num);
+ kref_init(&xfer->ref);
+ xfer->bounce_buf_base = (u8 *)(xfer->reqs + num);
+
+ count = virtio_i2c_prepare_xfer(vq, xfer, msgs, num);
if (!count)
goto err_free;
@@ -157,10 +224,10 @@ static int virtio_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
*/
virtqueue_kick(vq);
- count = virtio_i2c_complete_reqs(vq, reqs, msgs, count);
+ count = virtio_i2c_complete_xfer(xfer, msgs, count);
err_free:
- kfree(reqs);
+ kref_put(&xfer->ref, virtio_i2c_xfer_release);
return count;
}
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v4] i2c: virtio: retain xfer with kref to fix UAF on interrupted wait
From: Michael S. Tsirkin @ 2026-06-10 15:08 UTC (permalink / raw)
To: Gavin Li
Cc: linux-i2c, viresh.kumar, Chen, Jian Jun, andi.shyti,
virtualization
In-Reply-To: <20260610150232.24834-1-gavin.li@samsara.com>
On Wed, Jun 10, 2026 at 11:02:32AM -0400, Gavin Li wrote:
> commit a663b3c47ab1 ("i2c: virtio: Avoid hang by using interruptible
> completion wait") switched virtio_i2c_complete_reqs() to
> wait_for_completion_interruptible() so a stuck device cannot hang a
> task forever. That left a use-after-free: if the wait returns early on
> a signal, virtio_i2c_xfer() frees reqs and DMA bounce buffers while the
> device may still hold virtqueue tokens pointing at &reqs[i] and DMA
> into read buffers. When those requests complete later,
> virtio_i2c_msg_done() calls complete() on freed memory.
>
> Waiting uninterruptibly for every completion before freeing avoids the
> UAF but can hang the caller indefinitely if the virtio side never
> completes the request. Performing a queue reset / device reset is
> possible in this scenario, but adds complexity.
>
> This commit manages the freeing of the xfer allocations via kref, and
> ensures that each in-flight request holds a reference. This fixes the
> use-after-free by ensuring that the virtio device has a valid location
> to write to until the request completes. This will cause a memory leak
> in cases where the device hangs, but that is much preferable to memory
> corruption.
>
> Additionally, force usage of a bounce buffer even if the i2c_msg buf is
> DMA-safe, since the buffer passed to the virtqueue must remain valid
> even if the transfer is interrupted. Remove usage of
> i2c_get_dma_safe_msg_buf() since it may pass through msg->buf directly.
> All bounce buffers are part of the single xfer allocation, so there is
> no additional allocation overhead.
>
> Signed-off-by: Gavin Li <gavin.li@samsara.com>
>
> ---
>
> Changes in v4:
> - Pack bounce buffers into a single allocation after reqs[]
> - Remove per-request buf pointer and xfer->num
> - Remove req.msg, track message direction with req->read
> - Simplify xfer release to a single kfree()
> - Restore using j to track successful transfers in _complete_xfer()
> ---
> drivers/i2c/busses/i2c-virtio.c | 121 +++++++++++++++++++++++++-------
> 1 file changed, 94 insertions(+), 27 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-virtio.c b/drivers/i2c/busses/i2c-virtio.c
> index 5da6fef92bec3..33b469ca39d4b 100644
> --- a/drivers/i2c/busses/i2c-virtio.c
> +++ b/drivers/i2c/busses/i2c-virtio.c
> @@ -13,7 +13,9 @@
> #include <linux/err.h>
> #include <linux/i2c.h>
> #include <linux/kernel.h>
> +#include <linux/kref.h>
> #include <linux/module.h>
> +#include <linux/overflow.h>
> #include <linux/virtio.h>
> #include <linux/virtio_ids.h>
> #include <linux/virtio_config.h>
> @@ -31,39 +33,74 @@ struct virtio_i2c {
> struct virtqueue *vq;
> };
>
> +struct virtio_i2c_xfer;
> +
> /**
> * struct virtio_i2c_req - the virtio I2C request structure
> + * @xfer: owning transfer
> * @completion: completion of virtio I2C message
> + * @read: true if this is a read message (I2C_M_RD is set)
> * @out_hdr: the OUT header of the virtio I2C message
> - * @buf: the buffer into which data is read, or from which it's written
> * @in_hdr: the IN header of the virtio I2C message
> */
> struct virtio_i2c_req {
> + struct virtio_i2c_xfer *xfer;
> struct completion completion;
> + bool read;
> struct virtio_i2c_out_hdr out_hdr ____cacheline_aligned;
> - uint8_t *buf ____cacheline_aligned;
> struct virtio_i2c_in_hdr in_hdr ____cacheline_aligned;
> };
>
> +/**
> + * struct virtio_i2c_xfer - a queued I2C transfer
> + * @ref: one ref for the caller, plus one per in-flight virtqueue request
> + * @bounce_buf_base: start of bounce buffer region
> + * @reqs: the virtio I2C requests
> + *
> + * Allocation layout:
> + * - struct virtio_i2c_xfer xfer
> + * - struct virtio_i2c_req reqs[num]
> + * - u8 bounce_buf[msgs[0].len]
> + * ...
> + * - u8 bounce_buf[msgs[num-1].len]
You seems to be ignoring DMA alignment requirements here.
Did you try running with DMA debugging enabled?
> + */
> +struct virtio_i2c_xfer {
> + struct kref ref;
> + u8 *bounce_buf_base;
> + struct virtio_i2c_req reqs[];
> +};
> +
> +static void virtio_i2c_xfer_release(struct kref *ref)
> +{
> + struct virtio_i2c_xfer *xfer = container_of(ref, struct virtio_i2c_xfer, ref);
> + kfree(xfer);
> +}
> +
> static void virtio_i2c_msg_done(struct virtqueue *vq)
> {
> struct virtio_i2c_req *req;
> unsigned int len;
>
> - while ((req = virtqueue_get_buf(vq, &len)))
> + while ((req = virtqueue_get_buf(vq, &len))) {
> complete(&req->completion);
> + kref_put(&req->xfer->ref, virtio_i2c_xfer_release);
> + }
> }
>
> -static int virtio_i2c_prepare_reqs(struct virtqueue *vq,
> - struct virtio_i2c_req *reqs,
> +static int virtio_i2c_prepare_xfer(struct virtqueue *vq,
> + struct virtio_i2c_xfer *xfer,
> struct i2c_msg *msgs, int num)
> {
> struct scatterlist *sgs[3], out_hdr, msg_buf, in_hdr;
> + struct virtio_i2c_req *reqs = xfer->reqs;
> + u8 *bounce_buf = xfer->bounce_buf_base;
> int i;
>
> for (i = 0; i < num; i++) {
> int outcnt = 0, incnt = 0;
>
> + reqs[i].xfer = xfer;
> + reqs[i].read = !!(msgs[i].flags & I2C_M_RD);
> init_completion(&reqs[i].completion);
>
> /*
> @@ -82,23 +119,31 @@ static int virtio_i2c_prepare_reqs(struct virtqueue *vq,
> sgs[outcnt++] = &out_hdr;
>
> if (msgs[i].len) {
> - reqs[i].buf = i2c_get_dma_safe_msg_buf(&msgs[i], 1);
> - if (!reqs[i].buf)
> - break;
> + /*
> + * Even if msg->flags has I2C_M_DMA_SAFE set, a bounce
> + * buffer is required because the transfer may be
> + * interrupted, after which msg->buf is no longer valid.
> + */
> + if (!(msgs[i].flags & I2C_M_RD))
> + memcpy(bounce_buf, msgs[i].buf, msgs[i].len);
>
> - sg_init_one(&msg_buf, reqs[i].buf, msgs[i].len);
> + sg_init_one(&msg_buf, bounce_buf, msgs[i].len);
>
> if (msgs[i].flags & I2C_M_RD)
> sgs[outcnt + incnt++] = &msg_buf;
> else
> sgs[outcnt++] = &msg_buf;
> }
> + bounce_buf += msgs[i].len;
>
> sg_init_one(&in_hdr, &reqs[i].in_hdr, sizeof(reqs[i].in_hdr));
> sgs[outcnt + incnt++] = &in_hdr;
>
> + /* This reference is released in virtio_i2c_msg_done(). */
> + kref_get(&xfer->ref);
> +
> if (virtqueue_add_sgs(vq, sgs, outcnt, incnt, &reqs[i], GFP_KERNEL)) {
> - i2c_put_dma_safe_msg_buf(reqs[i].buf, &msgs[i], false);
> + kref_put(&xfer->ref, virtio_i2c_xfer_release);
> break;
> }
> }
> @@ -106,26 +151,38 @@ static int virtio_i2c_prepare_reqs(struct virtqueue *vq,
> return i;
> }
>
> -static int virtio_i2c_complete_reqs(struct virtqueue *vq,
> - struct virtio_i2c_req *reqs,
> - struct i2c_msg *msgs, int num)
> +static int virtio_i2c_complete_xfer(struct virtio_i2c_xfer *xfer,
> + struct i2c_msg *msgs,
> + int num)
> {
> + struct virtio_i2c_req *reqs = xfer->reqs;
> + u8 *bounce_buf = xfer->bounce_buf_base;
> bool failed = false;
> int i, j = 0;
>
> for (i = 0; i < num; i++) {
> struct virtio_i2c_req *req = &reqs[i];
> + struct i2c_msg *msg = &msgs[i];
> +
> + if (wait_for_completion_interruptible(&req->completion))
> + return -EINTR;
> +
> + if (req->in_hdr.status != VIRTIO_I2C_MSG_OK) {
> + /*
> + * Don't break yet. Try to wait until all requests
> + * complete to ensure that the virtqueue has enough
> + * descriptor slots for the next transfer.
> + */
> + failed = true;
> + }
>
> if (!failed) {
> - if (wait_for_completion_interruptible(&req->completion))
> - failed = true;
> - else if (req->in_hdr.status != VIRTIO_I2C_MSG_OK)
> - failed = true;
> - else
> - j++;
> + if (req->read)
> + memcpy(msg->buf, bounce_buf, msg->len);
> + j++;
> }
>
> - i2c_put_dma_safe_msg_buf(reqs[i].buf, &msgs[i], !failed);
> + bounce_buf += msg->len;
> }
>
> return j;
> @@ -136,14 +193,24 @@ static int virtio_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
> {
> struct virtio_i2c *vi = i2c_get_adapdata(adap);
> struct virtqueue *vq = vi->vq;
> - struct virtio_i2c_req *reqs;
> - int count;
> + struct virtio_i2c_xfer *xfer;
> + size_t alloc_size;
> + int i, count;
>
> - reqs = kzalloc_objs(*reqs, num);
> - if (!reqs)
> + alloc_size = struct_size(xfer, reqs, num);
> + for (i = 0; i < num; i++) {
> + if (check_add_overflow(alloc_size, msgs[i].len, &alloc_size))
> + return -EOVERFLOW;
> + }
> +
> + xfer = kzalloc(alloc_size, GFP_KERNEL);
> + if (!xfer)
> return -ENOMEM;
>
> - count = virtio_i2c_prepare_reqs(vq, reqs, msgs, num);
> + kref_init(&xfer->ref);
> + xfer->bounce_buf_base = (u8 *)(xfer->reqs + num);
> +
> + count = virtio_i2c_prepare_xfer(vq, xfer, msgs, num);
> if (!count)
> goto err_free;
>
> @@ -157,10 +224,10 @@ static int virtio_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
> */
> virtqueue_kick(vq);
>
> - count = virtio_i2c_complete_reqs(vq, reqs, msgs, count);
> + count = virtio_i2c_complete_xfer(xfer, msgs, count);
>
> err_free:
> - kfree(reqs);
> + kref_put(&xfer->ref, virtio_i2c_xfer_release);
> return count;
> }
>
> --
> 2.54.0
^ permalink raw reply
* Re: [PATCH v2] mm: page_reporting: allow driver to set batch capacity
From: David Hildenbrand (Arm) @ 2026-06-10 15:17 UTC (permalink / raw)
To: Michael S. Tsirkin, linux-kernel
Cc: Miaohe Lin, Jason Wang, Xuan Zhuo, Eugenio Perez, Muchun Song,
Oscar Salvador, Andrew Morton, Lorenzo Stoakes, Liam R. Howlett,
Vlastimil Babka, Mike Rapoport, Suren Baghdasaryan, Michal Hocko,
Brendan Jackman, Johannes Weiner, Zi Yan, Baolin Wang, Nico Pache,
Ryan Roberts, Dev Jain, Barry Song, Lance Yang, Hugh Dickins,
Matthew Brost, Joshua Hahn, Rakie Kim, Byungchul Park,
Gregory Price, Ying Huang, Alistair Popple, Christoph Lameter,
David Rientjes, Roman Gushchin, Harry Yoo, Axel Rasmussen,
Yuanchu Xie, Wei Xu, Chris Li, Kairui Song, Kemeng Shi, Nhat Pham,
Baoquan He, virtualization, linux-mm, Andrea Arcangeli,
Naoya Horiguchi, Alexander Duyck
In-Reply-To: <cb43adc61d2ed3069b2fe428f3e051dbdc4cc28d.1781097156.git.mst@redhat.com>
> --- a/include/linux/page_reporting.h
> +++ b/include/linux/page_reporting.h
> @@ -22,6 +22,9 @@ struct page_reporting_dev_info {
>
> /* Minimal order of page reporting */
> unsigned int order;
> +
> + /* Max pages per report batch; 0 (default) means PAGE_REPORTING_CAPACITY */
> + unsigned int capacity;
> };
I think PAGE_REPORTING_CAPACITY still documents
"This value should always be a power of 2, see page_reporting_cycle()"
With that:
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
--
Cheers,
David
^ permalink raw reply
* [PATCH v5 01/15] drm/amd/display: Handle struct drm_plane_state.ignore_damage_clips
From: Thomas Zimmermann @ 2026-06-10 15:18 UTC (permalink / raw)
To: mripard, maarten.lankhorst, airlied, airlied, simona, admin,
gargaditya08, paul, jani.nikula, mhklkml, zack.rusin,
bcm-kernel-feedback-list, harry.wentland, sunpeng.li, siqueira,
alexander.deucher, rodrigo.vivi, joonas.lahtinen, tursulin,
javierm, dmitry.osipenko, gurchetansingh, olvaffe
Cc: dri-devel, linux-hyperv, intel-gfx, intel-xe, linux-mips,
virtualization, amd-gfx, Thomas Zimmermann, Zack Rusin, stable
In-Reply-To: <20260610152505.260172-1-tzimmermann@suse.de>
The mode-setting pipeline can disabled damage clippings for a commit
by setting ignore_damage_clips in struct drm_plane_state. The commit
will then do a full display update.
Test the flag in DCN code and do a full update in DCN code if it has
been set.
Commit 35ed38d58257 ("drm: Allow drivers to indicate the damage helpers
to ignore damage clips") introduced ignore_damage_clips to selectively
ignore damage clipping in certain framebuffer changes. This driver does
not do that, but DRM's damage iterator will soon rely on the flag.
Therefore supporting it here as well make sense for consistency.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 35ed38d58257 ("drm: Allow drivers to indicate the damage helpers to ignore damage clips")
Cc: Javier Martinez Canillas <javierm@redhat.com>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Zack Rusin <zackr@vmware.com>
Cc: dri-devel@lists.freedesktop.org
Cc: <stable@vger.kernel.org> # v6.8+
---
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 0e20194e6662..4cbb27f65a0b 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -6614,8 +6614,8 @@ static void fill_dc_dirty_rects(struct drm_plane *plane,
{
struct dm_crtc_state *dm_crtc_state = to_dm_crtc_state(crtc_state);
struct rect *dirty_rects = flip_addrs->dirty_rects;
- u32 num_clips;
- struct drm_mode_rect *clips;
+ u32 num_clips = 0;
+ struct drm_mode_rect *clips = NULL;
bool bb_changed;
bool fb_changed;
u32 i = 0;
@@ -6631,8 +6631,10 @@ static void fill_dc_dirty_rects(struct drm_plane *plane,
if (new_plane_state->rotation != DRM_MODE_ROTATE_0)
goto ffu;
- num_clips = drm_plane_get_damage_clips_count(new_plane_state);
- clips = drm_plane_get_damage_clips(new_plane_state);
+ if (!new_plane_state->ignore_damage_clips) {
+ num_clips = drm_plane_get_damage_clips_count(new_plane_state);
+ clips = drm_plane_get_damage_clips(new_plane_state);
+ }
if (num_clips && (!amdgpu_damage_clips || (amdgpu_damage_clips < 0 &&
is_psr_su)))
--
2.54.0
^ permalink raw reply related
* [PATCH v5 00/15] drm: Improve logic behind damage handling
From: Thomas Zimmermann @ 2026-06-10 15:18 UTC (permalink / raw)
To: mripard, maarten.lankhorst, airlied, airlied, simona, admin,
gargaditya08, paul, jani.nikula, mhklkml, zack.rusin,
bcm-kernel-feedback-list, harry.wentland, sunpeng.li, siqueira,
alexander.deucher, rodrigo.vivi, joonas.lahtinen, tursulin,
javierm, dmitry.osipenko, gurchetansingh, olvaffe
Cc: dri-devel, linux-hyperv, intel-gfx, intel-xe, linux-mips,
virtualization, amd-gfx, Thomas Zimmermann
DRM clients can supply information on framebuffer areas to update as
part of each page flip, called damage-clipping rectangles. But DRM's
processing of this information is inconsistent and prone to errors.
- There are multiple fields and tests that decide if damage clips
should be taken or ignored.
- Sometimes damage clips are removed behind the back of the DRM client.
- Atomic helpers evaluate damage clipping in the middle of the atomic
check: after connectors and encoders, but before planes and CRTCs. Hence
pipeline stages have an inconsistent view.
- Which leads to drivers (ingenic) doing a re-evaluation if necessary.
- Tests of plane source coordinates only happen during commits. At this
point, the driver should already know if damage clips are to be taken or
not. Because of this, some drivers (appletbdrm) might operate on incorrect
damage information for their internal workings. This also leads to excessive
use of the old plane state.
Therefore go through DRM helpers and drivers and fix the logic.
- Run all of the atomic checks with the damage information supplied by
DRM clients. Afterwards evaluate plane and CRTC states on whether to
take or ignore damage clips. Do all related tests in a single atomic
helper.
- Do not discard damage clips. Set ignore_damage_clips in struct
drm_plane_state instead. This includes changes to plane source-coordinates.
The damage iterator now only has to look at this flag to detect if it
should use the damage clips.
- Go over drivers and fix the damage handling in the plane's
atomic_update helpers. Most drivers no longer need the old plane state
in their update.
- The appletbdrm driver requires a fix in how it uses damage information.
Ingenic and vmwgfx can be simplified. These changes improve the drivers'
code organization.
- Add support for ignore_damage_clips to various drivers that ignored it
until now.
- Kunit tests require some changes. Drop some obsolete tests and add a new
one for ignore_damage_flags.
Tested with bochs, mgag200, Kunit tests.
v5:
- support ignore_damage_clips in amdgpu, i915, virtgpu, vmwgfx
- reorder patches to avoid possible regressions during the series
- fix clearing ignore_damage_clips in a separate patch (Javier)
v4:
- reorder patches to avoid error-prone intermediate state
v3:
- fix error path in appletbdrm
v2:
- rebase on latest upstream
Thomas Zimmermann (15):
drm/amd/display: Handle struct drm_plane_state.ignore_damage_clips
drm/i915/display: Handle struct drm_plane_state.ignore_damage_clips
drm/vboxvideo: Handle struct drm_plane_state.ignore_damage_clips
drm/vmwgfx: Handle struct drm_plane_state.ignore_damage_clips
drm/appletbdrm: Allocate request/response buffers in begin_fb_access
drm/damage-helper: Clear ignore_damage_clips in plane-state
duplication
drm/damage-helper: Do not alter damage clips on modeset, but ignore
them
drm/atomic-helpers: Evaluate plane damage after atomic_check
drm/ingenic: Remove calls to drm_atomic_helper_check_plane_damage()
drm/atomic_helper: Do not evaluate plane damage before atomic_check
drm/damage-helper: Test src coord in
drm_atomic_helper_check_plane_damage()
drm/damage-helper: Remove old state from
drm_atomic_helper_damage_iter_init()
drm/damage-helper: Remove old state from
drm_atomic_helper_damage_merged()
drm/damage-helper: Rename state parameters in damage helpers
drm/vmwgfx: Remove unused field struct
vmwgfx_du_update_plane.old_state
.../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 10 +-
drivers/gpu/drm/ast/ast_cursor.c | 3 +-
drivers/gpu/drm/ast/ast_mode.c | 2 +-
drivers/gpu/drm/drm_atomic_helper.c | 6 +-
drivers/gpu/drm/drm_atomic_state_helper.c | 1 +
drivers/gpu/drm/drm_damage_helper.c | 44 ++--
drivers/gpu/drm/drm_fb_dma_helper.c | 2 +-
drivers/gpu/drm/drm_mipi_dbi.c | 3 +-
drivers/gpu/drm/gud/gud_pipe.c | 3 +-
drivers/gpu/drm/hyperv/hyperv_drm_modeset.c | 3 +-
drivers/gpu/drm/i915/display/intel_plane.c | 11 +-
drivers/gpu/drm/i915/display/intel_psr.c | 8 +-
drivers/gpu/drm/ingenic/ingenic-drm-drv.c | 3 -
drivers/gpu/drm/ingenic/ingenic-ipu.c | 8 +-
drivers/gpu/drm/mgag200/mgag200_mode.c | 3 +-
drivers/gpu/drm/sitronix/st7571.c | 3 +-
drivers/gpu/drm/sitronix/st7586.c | 3 +-
drivers/gpu/drm/sitronix/st7920.c | 3 +-
drivers/gpu/drm/solomon/ssd130x.c | 9 +-
drivers/gpu/drm/sysfb/drm_sysfb_modeset.c | 3 +-
.../gpu/drm/tests/drm_damage_helper_test.c | 200 +++---------------
drivers/gpu/drm/tiny/appletbdrm.c | 59 +++---
drivers/gpu/drm/tiny/bochs.c | 3 +-
drivers/gpu/drm/tiny/cirrus-qemu.c | 2 +-
drivers/gpu/drm/tiny/gm12u320.c | 2 +-
drivers/gpu/drm/tiny/ili9225.c | 3 +-
drivers/gpu/drm/tiny/repaper.c | 2 +-
drivers/gpu/drm/tiny/sharp-memory.c | 3 +-
drivers/gpu/drm/udl/udl_modeset.c | 3 +-
drivers/gpu/drm/vboxvideo/vbox_mode.c | 11 +-
drivers/gpu/drm/virtio/virtgpu_plane.c | 2 +-
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 5 +-
drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 2 -
drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 9 +-
drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 12 +-
drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 15 +-
include/drm/drm_damage_helper.h | 9 +-
37 files changed, 148 insertions(+), 325 deletions(-)
base-commit: fc59f76558703febba8056be87d1c97d14f7485e
--
2.54.0
^ permalink raw reply
* [PATCH v5 03/15] drm/vboxvideo: Handle struct drm_plane_state.ignore_damage_clips
From: Thomas Zimmermann @ 2026-06-10 15:18 UTC (permalink / raw)
To: mripard, maarten.lankhorst, airlied, airlied, simona, admin,
gargaditya08, paul, jani.nikula, mhklkml, zack.rusin,
bcm-kernel-feedback-list, harry.wentland, sunpeng.li, siqueira,
alexander.deucher, rodrigo.vivi, joonas.lahtinen, tursulin,
javierm, dmitry.osipenko, gurchetansingh, olvaffe
Cc: dri-devel, linux-hyperv, intel-gfx, intel-xe, linux-mips,
virtualization, amd-gfx, Thomas Zimmermann, Zack Rusin, stable
In-Reply-To: <20260610152505.260172-1-tzimmermann@suse.de>
The mode-setting pipeline can disabled damage clippings for a commit
by setting ignore_damage_clips in struct drm_plane_state. The commit
will then do a full display update.
Test the flag in the primary plane's atomic_update and do a full update
if it has been set.
Commit 35ed38d58257 ("drm: Allow drivers to indicate the damage helpers
to ignore damage clips") introduced ignore_damage_clips to selectively
ignore damage clipping in certain framebuffer changes. Vboxvideo does not
do that, but DRM's damage iterator will soon rely on the flag. Therefore
supporting it here as well make sense for consistency.
While at it, also replace uint32_t with the preferred u32.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 35ed38d58257 ("drm: Allow drivers to indicate the damage helpers to ignore damage clips")
Cc: Javier Martinez Canillas <javierm@redhat.com>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Zack Rusin <zackr@vmware.com>
Cc: dri-devel@lists.freedesktop.org
Cc: <stable@vger.kernel.org> # v6.8+
---
drivers/gpu/drm/vboxvideo/vbox_mode.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/vboxvideo/vbox_mode.c b/drivers/gpu/drm/vboxvideo/vbox_mode.c
index 8e4e5fc9d3c5..635777f1ca23 100644
--- a/drivers/gpu/drm/vboxvideo/vbox_mode.c
+++ b/drivers/gpu/drm/vboxvideo/vbox_mode.c
@@ -283,8 +283,9 @@ static void vbox_primary_atomic_update(struct drm_plane *plane,
struct drm_crtc *crtc = new_state->crtc;
struct drm_framebuffer *fb = new_state->fb;
struct vbox_private *vbox = to_vbox_dev(fb->dev);
- struct drm_mode_rect *clips;
- uint32_t num_clips, i;
+ struct drm_mode_rect *clips = NULL;
+ u32 num_clips = 0;
+ u32 i;
vbox_crtc_set_base_and_mode(crtc, fb,
new_state->src_x >> 16,
@@ -292,8 +293,10 @@ static void vbox_primary_atomic_update(struct drm_plane *plane,
/* Send information about dirty rectangles to VBVA. */
- clips = drm_plane_get_damage_clips(new_state);
- num_clips = drm_plane_get_damage_clips_count(new_state);
+ if (!new_state->ignore_damage_clips) {
+ clips = drm_plane_get_damage_clips(new_state);
+ num_clips = drm_plane_get_damage_clips_count(new_state);
+ }
if (!num_clips)
return;
--
2.54.0
^ permalink raw reply related
* [PATCH v5 02/15] drm/i915/display: Handle struct drm_plane_state.ignore_damage_clips
From: Thomas Zimmermann @ 2026-06-10 15:18 UTC (permalink / raw)
To: mripard, maarten.lankhorst, airlied, airlied, simona, admin,
gargaditya08, paul, jani.nikula, mhklkml, zack.rusin,
bcm-kernel-feedback-list, harry.wentland, sunpeng.li, siqueira,
alexander.deucher, rodrigo.vivi, joonas.lahtinen, tursulin,
javierm, dmitry.osipenko, gurchetansingh, olvaffe
Cc: dri-devel, linux-hyperv, intel-gfx, intel-xe, linux-mips,
virtualization, amd-gfx, Thomas Zimmermann, stable, Zack Rusin
In-Reply-To: <20260610152505.260172-1-tzimmermann@suse.de>
The mode-setting pipeline can disabled damage clippings for a commit
by setting ignore_damage_clips in struct drm_plane_state. The commit
will then do a full display update. Commit 35ed38d58257 ("drm: Allow
drivers to indicate the damage helpers to ignore damage clips") introduced
ignore_damage_clips to selectively ignore damage clipping in certain
framebuffer changes.
The i915 driver does not modify the flag, but DRM's damage iterator
will soon rely on it. Calling drm_atomic_helper_check_plane_damage()
right before drm_atomic_helper_damage_merged() guarantees that it
has the correct state. The i915 driver does not do this elsewhere
so far.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 35ed38d58257 ("drm: Allow drivers to indicate the damage helpers to ignore damage clips")
Cc: <stable@vger.kernel.org> # v6.4+
Cc: Javier Martinez Canillas <javierm@redhat.com>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Zack Rusin <zackr@vmware.com>
Cc: dri-devel@lists.freedesktop.org
Cc: <stable@vger.kernel.org> # v6.8+
---
drivers/gpu/drm/i915/display/intel_psr.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
index e138982dc91f..e4f43eb5bd72 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.c
+++ b/drivers/gpu/drm/i915/display/intel_psr.c
@@ -3018,6 +3018,9 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state,
src = drm_plane_state_src(&new_plane_state->uapi);
drm_rect_fp_to_int(&src, &src);
+ /* Prepare plane-damage state before using it */
+ drm_atomic_helper_check_plane_damage(&state->base, &new_plane_state->uapi);
+
if (!drm_atomic_helper_damage_merged(&old_plane_state->uapi,
&new_plane_state->uapi, &damaged_area))
continue;
--
2.54.0
^ permalink raw reply related
* [PATCH v5 04/15] drm/vmwgfx: Handle struct drm_plane_state.ignore_damage_clips
From: Thomas Zimmermann @ 2026-06-10 15:18 UTC (permalink / raw)
To: mripard, maarten.lankhorst, airlied, airlied, simona, admin,
gargaditya08, paul, jani.nikula, mhklkml, zack.rusin,
bcm-kernel-feedback-list, harry.wentland, sunpeng.li, siqueira,
alexander.deucher, rodrigo.vivi, joonas.lahtinen, tursulin,
javierm, dmitry.osipenko, gurchetansingh, olvaffe
Cc: dri-devel, linux-hyperv, intel-gfx, intel-xe, linux-mips,
virtualization, amd-gfx, Thomas Zimmermann, Zack Rusin, stable
In-Reply-To: <20260610152505.260172-1-tzimmermann@suse.de>
The mode-setting pipeline can disabled damage clippings for a commit
by setting ignore_damage_clips in struct drm_plane_state. The commit
will then do a full display update.
Test the flag in the primary ldu plane's atomic_update and do a full
update if it has been set.
Commit 35ed38d58257 ("drm: Allow drivers to indicate the damage helpers
to ignore damage clips") introduced ignore_damage_clips to selectively
ignore damage clipping in certain framebuffer changes. Vmwgfx does not
do that, but DRM's damage iterator will soon rely on the flag. Therefore
supporting it here as well make sense for consistency.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 35ed38d58257 ("drm: Allow drivers to indicate the damage helpers to ignore damage clips")
Cc: Javier Martinez Canillas <javierm@redhat.com>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Zack Rusin <zackr@vmware.com>
Cc: dri-devel@lists.freedesktop.org
Cc: <stable@vger.kernel.org> # v6.8+
---
drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index af3e32174563..40618759c940 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -342,10 +342,15 @@ vmw_ldu_primary_plane_atomic_update(struct drm_plane *plane,
.x2 = vfb->base.width,
.y2 = vfb->base.height
};
- struct drm_mode_rect *damage_rects = drm_plane_get_damage_clips(new_state);
- u32 rect_count = drm_plane_get_damage_clips_count(new_state);
+ struct drm_mode_rect *damage_rects = NULL;
+ u32 rect_count = 0;
int ret;
+ if (!new_state->ignore_damage_clips) {
+ damage_rects = drm_plane_get_damage_clips(new_state);
+ rect_count = drm_plane_get_damage_clips_count(new_state);
+ }
+
if (!damage_rects) {
damage_rects = &fb_rect;
rect_count = 1;
--
2.54.0
^ permalink raw reply related
* [PATCH v5 05/15] drm/appletbdrm: Allocate request/response buffers in begin_fb_access
From: Thomas Zimmermann @ 2026-06-10 15:18 UTC (permalink / raw)
To: mripard, maarten.lankhorst, airlied, airlied, simona, admin,
gargaditya08, paul, jani.nikula, mhklkml, zack.rusin,
bcm-kernel-feedback-list, harry.wentland, sunpeng.li, siqueira,
alexander.deucher, rodrigo.vivi, joonas.lahtinen, tursulin,
javierm, dmitry.osipenko, gurchetansingh, olvaffe
Cc: dri-devel, linux-hyperv, intel-gfx, intel-xe, linux-mips,
virtualization, amd-gfx, Thomas Zimmermann
In-Reply-To: <20260610152505.260172-1-tzimmermann@suse.de>
In atomic_check, damage handling is not fully evaluated. Another
atomic_check helper could trigger a full modeset and thus invalidate
damage clips.
Allocation of the request/response buffers in appletbdrm depends on
correct damage information. Otherwise it might allocate incorrectly
sized buffers. Allocate the buffers in the driver's begin_fb_access
helper. It runs early during the commit when damage clipping has been
fully evaluated.
v5:
- pass plane state as the old damage-iterator state
v2:
- allocate before drm_gem_begin_shadow_fb_access() to avoid leak on error
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Acked-by: Aditya Garg <gargaditya08@proton.me>
Acked-by: Zack Rusin <zack.rusin@broadcom.com>
---
drivers/gpu/drm/tiny/appletbdrm.c | 53 +++++++++++++++++--------------
1 file changed, 30 insertions(+), 23 deletions(-)
diff --git a/drivers/gpu/drm/tiny/appletbdrm.c b/drivers/gpu/drm/tiny/appletbdrm.c
index cdd35af49892..ef71b9957961 100644
--- a/drivers/gpu/drm/tiny/appletbdrm.c
+++ b/drivers/gpu/drm/tiny/appletbdrm.c
@@ -315,33 +315,16 @@ static const u32 appletbdrm_primary_plane_formats[] = {
DRM_FORMAT_XRGB8888, /* emulated */
};
-static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
- struct drm_atomic_commit *state)
+static int appletbdrm_primary_plane_helper_begin_fb_access(struct drm_plane *plane,
+ struct drm_plane_state *new_plane_state)
{
- struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
- struct drm_crtc *new_crtc = new_plane_state->crtc;
- struct drm_crtc_state *new_crtc_state = NULL;
struct appletbdrm_plane_state *appletbdrm_state = to_appletbdrm_plane_state(new_plane_state);
+ size_t frames_size = 0;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect damage;
- size_t frames_size = 0;
size_t request_size;
- int ret;
-
- if (new_crtc)
- new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
- ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
- DRM_PLANE_NO_SCALING,
- DRM_PLANE_NO_SCALING,
- false, false);
- if (ret)
- return ret;
- else if (!new_plane_state->visible)
- return 0;
-
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, new_plane_state);
+ drm_atomic_helper_damage_iter_init(&iter, plane->state, new_plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
frames_size += struct_size((struct appletbdrm_frame *)0, buf, rect_size(&damage));
}
@@ -366,6 +349,29 @@ static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
appletbdrm_state->request_size = request_size;
appletbdrm_state->frames_size = frames_size;
+ return drm_gem_begin_shadow_fb_access(plane, new_plane_state);
+}
+
+static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_commit *state)
+{
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, plane);
+ struct drm_crtc *new_crtc = new_plane_state->crtc;
+ struct drm_crtc_state *new_crtc_state = NULL;
+ int ret;
+
+ if (new_crtc)
+ new_crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
+
+ ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, false);
+ if (ret)
+ return ret;
+ else if (!new_plane_state->visible)
+ return 0;
+
return 0;
}
@@ -468,7 +474,7 @@ static int appletbdrm_flush_damage(struct appletbdrm_device *adev,
}
static void appletbdrm_primary_plane_helper_atomic_update(struct drm_plane *plane,
- struct drm_atomic_commit *old_state)
+ struct drm_atomic_commit *old_state)
{
struct appletbdrm_device *adev = drm_to_adev(plane->dev);
struct drm_device *drm = plane->dev;
@@ -552,7 +558,8 @@ static void appletbdrm_primary_plane_destroy_state(struct drm_plane *plane,
}
static const struct drm_plane_helper_funcs appletbdrm_primary_plane_helper_funcs = {
- DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
+ .begin_fb_access = appletbdrm_primary_plane_helper_begin_fb_access,
+ .end_fb_access = drm_gem_end_shadow_fb_access,
.atomic_check = appletbdrm_primary_plane_helper_atomic_check,
.atomic_update = appletbdrm_primary_plane_helper_atomic_update,
.atomic_disable = appletbdrm_primary_plane_helper_atomic_disable,
--
2.54.0
^ permalink raw reply related
* [PATCH v5 06/15] drm/damage-helper: Clear ignore_damage_clips in plane-state duplication
From: Thomas Zimmermann @ 2026-06-10 15:18 UTC (permalink / raw)
To: mripard, maarten.lankhorst, airlied, airlied, simona, admin,
gargaditya08, paul, jani.nikula, mhklkml, zack.rusin,
bcm-kernel-feedback-list, harry.wentland, sunpeng.li, siqueira,
alexander.deucher, rodrigo.vivi, joonas.lahtinen, tursulin,
javierm, dmitry.osipenko, gurchetansingh, olvaffe
Cc: dri-devel, linux-hyperv, intel-gfx, intel-xe, linux-mips,
virtualization, amd-gfx, Thomas Zimmermann, stable
In-Reply-To: <20260610152505.260172-1-tzimmermann@suse.de>
Clear ignore_damage_clips when duplicating struct drm_plane_state. The
flag track the state of the damage clips during atomic commits and should
not be kept across plane-state duplications.
Fixes a bug where ignore_damage_clips was not cleared. Once set, the
damage iterator would always ignore damage clips. Only happens in rare
cases in virtgpu and vmwgfx.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 35ed38d58257 ("drm: Allow drivers to indicate the damage helpers to ignore damage clips")
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Acked-by: Zack Rusin <zack.rusin@broadcom.com>
Cc: dri-devel@lists.freedesktop.org
Cc: <stable@vger.kernel.org> # v6.10+
---
drivers/gpu/drm/drm_atomic_state_helper.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c b/drivers/gpu/drm/drm_atomic_state_helper.c
index 07686e94aae0..3e26e208e784 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -409,6 +409,7 @@ void __drm_atomic_helper_plane_duplicate_state(struct drm_plane *plane,
state->fence = NULL;
state->commit = NULL;
state->fb_damage_clips = NULL;
+ state->ignore_damage_clips = false;
state->color_mgmt_changed = false;
}
EXPORT_SYMBOL(__drm_atomic_helper_plane_duplicate_state);
--
2.54.0
^ permalink raw reply related
* [PATCH v5 12/15] drm/damage-helper: Remove old state from drm_atomic_helper_damage_iter_init()
From: Thomas Zimmermann @ 2026-06-10 15:18 UTC (permalink / raw)
To: mripard, maarten.lankhorst, airlied, airlied, simona, admin,
gargaditya08, paul, jani.nikula, mhklkml, zack.rusin,
bcm-kernel-feedback-list, harry.wentland, sunpeng.li, siqueira,
alexander.deucher, rodrigo.vivi, joonas.lahtinen, tursulin,
javierm, dmitry.osipenko, gurchetansingh, olvaffe
Cc: dri-devel, linux-hyperv, intel-gfx, intel-xe, linux-mips,
virtualization, amd-gfx, Thomas Zimmermann, Hamza Mahfooz
In-Reply-To: <20260610152505.260172-1-tzimmermann@suse.de>
Nothing in drm_atomic_helper_damage_iter_init() requires the old
plane state. Remove the parameter and mass-convert callers.
Most callers now no longer require the old plane state in their plane's
atomic_update helper. Remove it as well.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Acked-by: Zack Rusin <zack.rusin@broadcom.com>
Acked-by: Hamza Mahfooz <hamzamahfooz@linux.microsoft.com> # hyperv
---
drivers/gpu/drm/ast/ast_mode.c | 2 +-
drivers/gpu/drm/drm_damage_helper.c | 4 +-
drivers/gpu/drm/drm_fb_dma_helper.c | 2 +-
drivers/gpu/drm/gud/gud_pipe.c | 3 +-
drivers/gpu/drm/hyperv/hyperv_drm_modeset.c | 3 +-
drivers/gpu/drm/mgag200/mgag200_mode.c | 3 +-
drivers/gpu/drm/sitronix/st7571.c | 3 +-
drivers/gpu/drm/sitronix/st7920.c | 3 +-
drivers/gpu/drm/solomon/ssd130x.c | 9 +--
drivers/gpu/drm/sysfb/drm_sysfb_modeset.c | 3 +-
.../gpu/drm/tests/drm_damage_helper_test.c | 55 ++++++-------------
drivers/gpu/drm/tiny/appletbdrm.c | 8 +--
drivers/gpu/drm/tiny/bochs.c | 3 +-
drivers/gpu/drm/tiny/cirrus-qemu.c | 2 +-
drivers/gpu/drm/udl/udl_modeset.c | 3 +-
drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 5 +-
include/drm/drm_damage_helper.h | 3 +-
17 files changed, 37 insertions(+), 77 deletions(-)
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index d5ed8c5c7925..6fe3fda6d145 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -579,7 +579,7 @@ static void ast_primary_plane_helper_atomic_update(struct drm_plane *plane,
/* if the buffer comes from another device */
if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) {
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+ drm_atomic_helper_damage_iter_init(&iter, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
ast_handle_damage(ast_plane, shadow_plane_state->data, fb, &damage,
&shadow_plane_state->fmtcnv_state);
diff --git a/drivers/gpu/drm/drm_damage_helper.c b/drivers/gpu/drm/drm_damage_helper.c
index f492a59edbeb..28f26234523d 100644
--- a/drivers/gpu/drm/drm_damage_helper.c
+++ b/drivers/gpu/drm/drm_damage_helper.c
@@ -209,7 +209,6 @@ EXPORT_SYMBOL(drm_atomic_helper_dirtyfb);
/**
* drm_atomic_helper_damage_iter_init - Initialize the damage iterator.
* @iter: The iterator to initialize.
- * @old_state: Unused, pass NULL.
* @state: Plane state from which to iterate the damage clips.
*
* Initialize an iterator, which clips plane damage
@@ -226,7 +225,6 @@ EXPORT_SYMBOL(drm_atomic_helper_dirtyfb);
*/
void
drm_atomic_helper_damage_iter_init(struct drm_atomic_helper_damage_iter *iter,
- const struct drm_plane_state *old_state,
const struct drm_plane_state *state)
{
struct drm_rect src;
@@ -324,7 +322,7 @@ bool drm_atomic_helper_damage_merged(const struct drm_plane_state *old_state,
rect->x2 = 0;
rect->y2 = 0;
- drm_atomic_helper_damage_iter_init(&iter, old_state, state);
+ drm_atomic_helper_damage_iter_init(&iter, state);
drm_atomic_for_each_plane_damage(&iter, &clip) {
rect->x1 = min(rect->x1, clip.x1);
rect->y1 = min(rect->y1, clip.y1);
diff --git a/drivers/gpu/drm/drm_fb_dma_helper.c b/drivers/gpu/drm/drm_fb_dma_helper.c
index fd71969d2fb1..bbad16d32b6f 100644
--- a/drivers/gpu/drm/drm_fb_dma_helper.c
+++ b/drivers/gpu/drm/drm_fb_dma_helper.c
@@ -138,7 +138,7 @@ void drm_fb_dma_sync_non_coherent(struct drm_device *drm,
continue;
daddr = drm_fb_dma_get_gem_addr(state->fb, state, i);
- drm_atomic_helper_damage_iter_init(&iter, old_state, state);
+ drm_atomic_helper_damage_iter_init(&iter, state);
drm_atomic_for_each_plane_damage(&iter, &clip) {
/* Ignore x1/x2 values, invalidate complete lines */
diff --git a/drivers/gpu/drm/gud/gud_pipe.c b/drivers/gpu/drm/gud/gud_pipe.c
index 5ef887d8485a..758990cd78aa 100644
--- a/drivers/gpu/drm/gud/gud_pipe.c
+++ b/drivers/gpu/drm/gud/gud_pipe.c
@@ -618,7 +618,6 @@ void gud_plane_atomic_update(struct drm_plane *plane,
{
struct drm_device *drm = plane->dev;
struct gud_device *gdrm = to_gud_device(drm);
- struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(atomic_state, plane);
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(atomic_state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(new_state);
struct drm_framebuffer *fb = new_state->fb;
@@ -647,7 +646,7 @@ void gud_plane_atomic_update(struct drm_plane *plane,
if (ret)
goto out;
- drm_atomic_helper_damage_iter_init(&iter, old_state, new_state);
+ drm_atomic_helper_damage_iter_init(&iter, new_state);
drm_atomic_for_each_plane_damage(&iter, &damage)
gud_fb_handle_damage(gdrm, fb, &shadow_plane_state->data[0], &damage);
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
index 44425f2d7e04..ee81056e5c53 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
@@ -171,14 +171,13 @@ static void hv_drm_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
struct hv_drm_device *hv = to_hv_drm(plane->dev);
- struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(new_state);
struct drm_rect damage;
struct drm_rect dst_clip;
struct drm_atomic_helper_damage_iter iter;
- drm_atomic_helper_damage_iter_init(&iter, old_state, new_state);
+ drm_atomic_helper_damage_iter_init(&iter, new_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
dst_clip = new_state->dst;
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index 7e07fc3f1a60..ea121428adf2 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -503,14 +503,13 @@ void mgag200_primary_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_device *dev = plane->dev;
struct mga_device *mdev = to_mga_device(dev);
struct drm_plane_state *plane_state = plane->state;
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(old_state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_framebuffer *fb = plane_state->fb;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect damage;
mgag200_set_datasiz(mdev, fb->format->format);
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+ drm_atomic_helper_damage_iter_init(&iter, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
mgag200_handle_damage(mdev, shadow_plane_state->data, fb, &damage);
}
diff --git a/drivers/gpu/drm/sitronix/st7571.c b/drivers/gpu/drm/sitronix/st7571.c
index 20954c33eca9..bc0e59c2600c 100644
--- a/drivers/gpu/drm/sitronix/st7571.c
+++ b/drivers/gpu/drm/sitronix/st7571.c
@@ -342,7 +342,6 @@ static int st7571_primary_plane_helper_atomic_check(struct drm_plane *plane,
static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_framebuffer *fb = plane_state->fb;
@@ -362,7 +361,7 @@ static void st7571_primary_plane_helper_atomic_update(struct drm_plane *plane,
if (!drm_dev_enter(drm, &idx))
goto out_drm_gem_fb_end_cpu_access;
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+ drm_atomic_helper_damage_iter_init(&iter, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
st7571->pformat->prepare_buffer(st7571,
&shadow_plane_state->data[0],
diff --git a/drivers/gpu/drm/sitronix/st7920.c b/drivers/gpu/drm/sitronix/st7920.c
index d320391801f3..7ee45a2b2d3e 100644
--- a/drivers/gpu/drm/sitronix/st7920.c
+++ b/drivers/gpu/drm/sitronix/st7920.c
@@ -390,7 +390,6 @@ static void st7920_primary_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
struct st7920_crtc_state *st7920_crtc_state = to_st7920_crtc_state(crtc_state);
@@ -407,7 +406,7 @@ static void st7920_primary_plane_atomic_update(struct drm_plane *plane,
return;
if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE) == 0) {
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+ drm_atomic_helper_damage_iter_init(&iter, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
dst_clip = plane_state->dst;
diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c
index cae92a3ae8a4..4b55532da31b 100644
--- a/drivers/gpu/drm/solomon/ssd130x.c
+++ b/drivers/gpu/drm/solomon/ssd130x.c
@@ -1198,7 +1198,6 @@ static void ssd130x_primary_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc_state);
@@ -1216,7 +1215,7 @@ static void ssd130x_primary_plane_atomic_update(struct drm_plane *plane,
if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE))
goto out_drm_dev_exit;
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+ drm_atomic_helper_damage_iter_init(&iter, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
dst_clip = plane_state->dst;
@@ -1239,7 +1238,6 @@ static void ssd132x_primary_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc_state);
@@ -1257,7 +1255,7 @@ static void ssd132x_primary_plane_atomic_update(struct drm_plane *plane,
if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE))
goto out_drm_dev_exit;
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+ drm_atomic_helper_damage_iter_init(&iter, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
dst_clip = plane_state->dst;
@@ -1280,7 +1278,6 @@ static void ssd133x_primary_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);
struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc_state);
@@ -1297,7 +1294,7 @@ static void ssd133x_primary_plane_atomic_update(struct drm_plane *plane,
if (drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE))
goto out_drm_dev_exit;
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+ drm_atomic_helper_damage_iter_init(&iter, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
dst_clip = plane_state->dst;
diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
index d2de29caf89e..9f1ae5ca9a11 100644
--- a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
+++ b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
@@ -331,7 +331,6 @@ void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_at
struct drm_device *dev = plane->dev;
struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_sysfb_plane_state *sysfb_plane_state = to_drm_sysfb_plane_state(plane_state);
struct drm_shadow_plane_state *shadow_plane_state = &sysfb_plane_state->base;
struct drm_framebuffer *fb = plane_state->fb;
@@ -351,7 +350,7 @@ void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_at
if (!drm_dev_enter(dev, &idx))
goto out_drm_gem_fb_end_cpu_access;
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+ drm_atomic_helper_damage_iter_init(&iter, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
struct iosys_map dst = sysfb->fb_addr;
struct drm_rect dst_clip = plane_state->dst;
diff --git a/drivers/gpu/drm/tests/drm_damage_helper_test.c b/drivers/gpu/drm/tests/drm_damage_helper_test.c
index ef931497baf9..2139ec8b0eb0 100644
--- a/drivers/gpu/drm/tests/drm_damage_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_damage_helper_test.c
@@ -20,7 +20,6 @@ struct drm_damage_mock {
struct drm_property prop;
struct drm_framebuffer fb;
struct drm_plane_state state;
- struct drm_plane_state old_state;
};
static int drm_damage_helper_init(struct kunit *test)
@@ -37,7 +36,6 @@ static int drm_damage_helper_init(struct kunit *test)
mock->state.fb = &mock->fb;
mock->state.visible = true;
- mock->old_state.plane = &mock->plane;
mock->state.plane = &mock->plane;
/* just enough so that drm_plane_enable_fb_damage_clips() works */
@@ -124,9 +122,8 @@ static void drm_test_damage_iter_no_damage(struct kunit *test)
u32 num_hits = 0;
/* Plane src same as fb size. */
- set_plane_src(&mock->old_state, 0, 0, mock->fb.width << 16, mock->fb.height << 16);
set_plane_src(&mock->state, 0, 0, mock->fb.width << 16, mock->fb.height << 16);
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
@@ -142,11 +139,9 @@ static void drm_test_damage_iter_no_damage_fractional_src(struct kunit *test)
u32 num_hits = 0;
/* Plane src has fractional part. */
- set_plane_src(&mock->old_state, 0x3fffe, 0x3fffe,
- 0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
set_plane_src(&mock->state, 0x3fffe, 0x3fffe,
0x3fffe + (1024 << 16), 0x3fffe + (768 << 16));
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
@@ -164,9 +159,8 @@ static void drm_test_damage_iter_no_damage_not_visible(struct kunit *test)
mock->state.visible = false;
- set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
@@ -182,9 +176,8 @@ static void drm_test_damage_iter_no_damage_no_crtc(struct kunit *test)
mock->state.crtc = NULL;
- set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
@@ -200,9 +193,8 @@ static void drm_test_damage_iter_no_damage_no_fb(struct kunit *test)
mock->state.fb = NULL;
- set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
@@ -218,13 +210,12 @@ static void drm_test_damage_iter_simple_damage(struct kunit *test)
struct drm_rect clip;
u32 num_hits = 0;
- set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
/* Damage set to plane src */
set_damage_clip(&damage, 0, 0, 1024, 768);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
@@ -241,12 +232,11 @@ static void drm_test_damage_iter_single_damage(struct kunit *test)
struct drm_rect clip;
u32 num_hits = 0;
- set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
set_damage_clip(&damage, 256, 192, 768, 576);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
@@ -263,13 +253,12 @@ static void drm_test_damage_iter_single_damage_intersect_src(struct kunit *test)
struct drm_rect clip;
u32 num_hits = 0;
- set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
/* Damage intersect with plane src. */
set_damage_clip(&damage, 256, 192, 1360, 768);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
@@ -286,13 +275,12 @@ static void drm_test_damage_iter_single_damage_outside_src(struct kunit *test)
struct drm_rect clip;
u32 num_hits = 0;
- set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
/* Damage clip outside plane src */
set_damage_clip(&damage, 1360, 1360, 1380, 1380);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
@@ -309,14 +297,12 @@ static void drm_test_damage_iter_single_damage_fractional_src(struct kunit *test
u32 num_hits = 0;
/* Plane src has fractional part. */
- set_plane_src(&mock->old_state, 0x40002, 0x40002,
- 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
set_plane_src(&mock->state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
set_damage_clip(&damage, 10, 10, 256, 330);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
@@ -334,15 +320,13 @@ static void drm_test_damage_iter_single_damage_intersect_fractional_src(struct k
u32 num_hits = 0;
/* Plane src has fractional part. */
- set_plane_src(&mock->old_state, 0x40002, 0x40002,
- 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
set_plane_src(&mock->state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
/* Damage intersect with plane src. */
set_damage_clip(&damage, 10, 1, 1360, 330);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
@@ -361,15 +345,13 @@ static void drm_test_damage_iter_single_damage_outside_fractional_src(struct kun
u32 num_hits = 0;
/* Plane src has fractional part. */
- set_plane_src(&mock->old_state, 0x40002, 0x40002,
- 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
set_plane_src(&mock->state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
/* Damage clip outside plane src */
set_damage_clip(&damage, 1360, 1360, 1380, 1380);
set_damage_blob(&damage_blob, &damage, sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
@@ -385,14 +367,13 @@ static void drm_test_damage_iter_damage(struct kunit *test)
struct drm_rect clip;
u32 num_hits = 0;
- set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
/* 2 damage clips. */
set_damage_clip(&damage[0], 20, 30, 200, 180);
set_damage_clip(&damage[1], 240, 200, 280, 250);
set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip) {
if (num_hits == 0)
check_damage_clip(test, &clip, 20, 30, 200, 180);
@@ -413,8 +394,6 @@ static void drm_test_damage_iter_damage_one_intersect(struct kunit *test)
struct drm_rect clip;
u32 num_hits = 0;
- set_plane_src(&mock->old_state, 0x40002, 0x40002,
- 0x40002 + (1024 << 16), 0x40002 + (768 << 16));
set_plane_src(&mock->state, 0x40002, 0x40002,
0x40002 + (1024 << 16), 0x40002 + (768 << 16));
/* 2 damage clips, one intersect plane src. */
@@ -422,7 +401,7 @@ static void drm_test_damage_iter_damage_one_intersect(struct kunit *test)
set_damage_clip(&damage[1], 2, 2, 1360, 1360);
set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip) {
if (num_hits == 0)
check_damage_clip(test, &clip, 20, 30, 200, 180);
@@ -443,14 +422,13 @@ static void drm_test_damage_iter_damage_one_outside(struct kunit *test)
struct drm_rect clip;
u32 num_hits = 0;
- set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
/* 2 damage clips, one outside plane src. */
set_damage_clip(&damage[0], 1360, 1360, 1380, 1380);
set_damage_clip(&damage[1], 240, 200, 280, 250);
set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
@@ -467,7 +445,6 @@ static void drm_test_damage_iter_damage_ignore(struct kunit *test)
struct drm_rect clip;
u32 num_hits = 0;
- set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
/* 2 damage clips, but ignore them. */
set_damage_clip(&damage[0], 20, 30, 200, 180);
@@ -475,7 +452,7 @@ static void drm_test_damage_iter_damage_ignore(struct kunit *test)
set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
set_plane_damage(&mock->state, &damage_blob);
mock->state.ignore_damage_clips = true;
- drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_helper_damage_iter_init(&iter, &mock->state);
drm_atomic_for_each_plane_damage(&iter, &clip) {
if (num_hits == 0)
check_damage_clip(test, &clip, 0, 0, 1024, 768);
diff --git a/drivers/gpu/drm/tiny/appletbdrm.c b/drivers/gpu/drm/tiny/appletbdrm.c
index ef71b9957961..eadc3aed7277 100644
--- a/drivers/gpu/drm/tiny/appletbdrm.c
+++ b/drivers/gpu/drm/tiny/appletbdrm.c
@@ -324,7 +324,7 @@ static int appletbdrm_primary_plane_helper_begin_fb_access(struct drm_plane *pla
struct drm_rect damage;
size_t request_size;
- drm_atomic_helper_damage_iter_init(&iter, plane->state, new_plane_state);
+ drm_atomic_helper_damage_iter_init(&iter, new_plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
frames_size += struct_size((struct appletbdrm_frame *)0, buf, rect_size(&damage));
}
@@ -376,7 +376,6 @@ static int appletbdrm_primary_plane_helper_atomic_check(struct drm_plane *plane,
}
static int appletbdrm_flush_damage(struct appletbdrm_device *adev,
- struct drm_plane_state *old_state,
struct drm_plane_state *state)
{
struct appletbdrm_plane_state *appletbdrm_state = to_appletbdrm_plane_state(state);
@@ -412,7 +411,7 @@ static int appletbdrm_flush_damage(struct appletbdrm_device *adev,
frame = (struct appletbdrm_frame *)request->data;
- drm_atomic_helper_damage_iter_init(&iter, old_state, state);
+ drm_atomic_helper_damage_iter_init(&iter, state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
struct drm_rect dst_clip = state->dst;
struct iosys_map dst = IOSYS_MAP_INIT_VADDR(frame->buf);
@@ -479,13 +478,12 @@ static void appletbdrm_primary_plane_helper_atomic_update(struct drm_plane *plan
struct appletbdrm_device *adev = drm_to_adev(plane->dev);
struct drm_device *drm = plane->dev;
struct drm_plane_state *plane_state = plane->state;
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(old_state, plane);
int idx;
if (!drm_dev_enter(drm, &idx))
return;
- appletbdrm_flush_damage(adev, old_plane_state, plane_state);
+ appletbdrm_flush_damage(adev, plane_state);
drm_dev_exit(idx);
}
diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c
index e2d957e51505..1e19e98694c0 100644
--- a/drivers/gpu/drm/tiny/bochs.c
+++ b/drivers/gpu/drm/tiny/bochs.c
@@ -447,7 +447,6 @@ static void bochs_primary_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_device *dev = plane->dev;
struct bochs_device *bochs = to_bochs_device(dev);
struct drm_plane_state *plane_state = plane->state;
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_framebuffer *fb = plane_state->fb;
struct drm_atomic_helper_damage_iter iter;
@@ -456,7 +455,7 @@ static void bochs_primary_plane_helper_atomic_update(struct drm_plane *plane,
if (!fb || !bochs->stride)
return;
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+ drm_atomic_helper_damage_iter_init(&iter, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
struct iosys_map dst = IOSYS_MAP_INIT_VADDR_IOMEM(bochs->fb_map);
diff --git a/drivers/gpu/drm/tiny/cirrus-qemu.c b/drivers/gpu/drm/tiny/cirrus-qemu.c
index 075221b431d3..44ffce563e51 100644
--- a/drivers/gpu/drm/tiny/cirrus-qemu.c
+++ b/drivers/gpu/drm/tiny/cirrus-qemu.c
@@ -350,7 +350,7 @@ static void cirrus_primary_plane_helper_atomic_update(struct drm_plane *plane,
if (!old_fb || old_fb->pitches[0] != fb->pitches[0])
cirrus_pitch_set(cirrus, fb->pitches[0]);
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+ drm_atomic_helper_damage_iter_init(&iter, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
unsigned int offset = drm_fb_clip_offset(fb->pitches[0], fb->format, &damage);
struct iosys_map dst = IOSYS_MAP_INIT_OFFSET(&vaddr, offset);
diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c
index 289711035b67..ac981ffca5d9 100644
--- a/drivers/gpu/drm/udl/udl_modeset.c
+++ b/drivers/gpu/drm/udl/udl_modeset.c
@@ -286,7 +286,6 @@ static void udl_primary_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_framebuffer *fb = plane_state->fb;
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_atomic_helper_damage_iter iter;
struct drm_rect damage;
int ret, idx;
@@ -301,7 +300,7 @@ static void udl_primary_plane_helper_atomic_update(struct drm_plane *plane,
if (!drm_dev_enter(dev, &idx))
goto out_drm_gem_fb_end_cpu_access;
- drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+ drm_atomic_helper_damage_iter_init(&iter, plane_state);
drm_atomic_for_each_plane_damage(&iter, &damage) {
udl_handle_damage(fb, &shadow_plane_state->data[0], &damage);
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 1b407b61f683..32617eb9538e 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -1733,7 +1733,6 @@ void vmw_kms_lost_device(struct drm_device *dev)
int vmw_du_helper_plane_update(struct vmw_du_update_plane *update)
{
struct drm_plane_state *state = update->plane->state;
- struct drm_plane_state *old_state = update->old_state;
struct drm_atomic_helper_damage_iter iter;
struct drm_rect clip;
struct drm_rect bb;
@@ -1750,7 +1749,7 @@ int vmw_du_helper_plane_update(struct vmw_du_update_plane *update)
* Iterate in advance to check if really need plane update and find the
* number of clips that actually are in plane src for fifo allocation.
*/
- drm_atomic_helper_damage_iter_init(&iter, old_state, state);
+ drm_atomic_helper_damage_iter_init(&iter, state);
drm_atomic_for_each_plane_damage(&iter, &clip)
num_hits++;
@@ -1818,7 +1817,7 @@ int vmw_du_helper_plane_update(struct vmw_du_update_plane *update)
bb.x2 = INT_MIN;
bb.y2 = INT_MIN;
- drm_atomic_helper_damage_iter_init(&iter, old_state, state);
+ drm_atomic_helper_damage_iter_init(&iter, state);
drm_atomic_for_each_plane_damage(&iter, &clip) {
uint32_t fb_x = clip.x1;
uint32_t fb_y = clip.y1;
diff --git a/include/drm/drm_damage_helper.h b/include/drm/drm_damage_helper.h
index e93eaa0fbcb6..fafe29b50fc6 100644
--- a/include/drm/drm_damage_helper.h
+++ b/include/drm/drm_damage_helper.h
@@ -73,8 +73,7 @@ int drm_atomic_helper_dirtyfb(struct drm_framebuffer *fb,
unsigned int num_clips);
void
drm_atomic_helper_damage_iter_init(struct drm_atomic_helper_damage_iter *iter,
- const struct drm_plane_state *old_state,
- const struct drm_plane_state *new_state);
+ const struct drm_plane_state *state);
bool
drm_atomic_helper_damage_iter_next(struct drm_atomic_helper_damage_iter *iter,
struct drm_rect *rect);
--
2.54.0
^ permalink raw reply related
* [PATCH v5 07/15] drm/damage-helper: Do not alter damage clips on modeset, but ignore them
From: Thomas Zimmermann @ 2026-06-10 15:18 UTC (permalink / raw)
To: mripard, maarten.lankhorst, airlied, airlied, simona, admin,
gargaditya08, paul, jani.nikula, mhklkml, zack.rusin,
bcm-kernel-feedback-list, harry.wentland, sunpeng.li, siqueira,
alexander.deucher, rodrigo.vivi, joonas.lahtinen, tursulin,
javierm, dmitry.osipenko, gurchetansingh, olvaffe
Cc: dri-devel, linux-hyperv, intel-gfx, intel-xe, linux-mips,
virtualization, amd-gfx, Thomas Zimmermann
In-Reply-To: <20260610152505.260172-1-tzimmermann@suse.de>
User space supplies rectangles for damage clipping in a plane property.
For full mode sets, drivers still require a full plane update. In this
case, leave the information as-is and set the ignore_damage_clips flag
instead. The damage iterator will later ignore any damage information.
Leaving the damage information as-is might be helpful to drivers that
benefit from this information even on full modesets (e.g., for cache
management). It will also help with consolidating the damage-handling
logic.
Also add a new unit test that evaluates the ignore_damage_clips flag. It
sets two damage clips plus the flag and tests if the reported damage
covers the entire framebuffer.
v5:
- clear ignore_damage_clips in a separate patch (Javier)
v4:
- slightly reword the commit description
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Acked-by: Zack Rusin <zack.rusin@broadcom.com>
---
drivers/gpu/drm/drm_damage_helper.c | 6 ++--
.../gpu/drm/tests/drm_damage_helper_test.c | 28 +++++++++++++++++++
2 files changed, 30 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/drm_damage_helper.c b/drivers/gpu/drm/drm_damage_helper.c
index 74a7f4252ecf..945fac8dc27b 100644
--- a/drivers/gpu/drm/drm_damage_helper.c
+++ b/drivers/gpu/drm/drm_damage_helper.c
@@ -78,10 +78,8 @@ void drm_atomic_helper_check_plane_damage(struct drm_atomic_commit *state,
if (WARN_ON(!crtc_state))
return;
- if (drm_atomic_crtc_needs_modeset(crtc_state)) {
- drm_property_blob_put(plane_state->fb_damage_clips);
- plane_state->fb_damage_clips = NULL;
- }
+ if (drm_atomic_crtc_needs_modeset(crtc_state))
+ plane_state->ignore_damage_clips = true;
}
}
EXPORT_SYMBOL(drm_atomic_helper_check_plane_damage);
diff --git a/drivers/gpu/drm/tests/drm_damage_helper_test.c b/drivers/gpu/drm/tests/drm_damage_helper_test.c
index 0df2e1a54b0d..64f038a62ffe 100644
--- a/drivers/gpu/drm/tests/drm_damage_helper_test.c
+++ b/drivers/gpu/drm/tests/drm_damage_helper_test.c
@@ -603,6 +603,33 @@ static void drm_test_damage_iter_damage_not_visible(struct kunit *test)
KUNIT_EXPECT_EQ_MSG(test, num_hits, 0, "Should not return any damage.");
}
+static void drm_test_damage_iter_damage_ignore(struct kunit *test)
+{
+ struct drm_damage_mock *mock = test->priv;
+ struct drm_atomic_helper_damage_iter iter;
+ struct drm_property_blob damage_blob;
+ struct drm_mode_rect damage[2];
+ struct drm_rect clip;
+ u32 num_hits = 0;
+
+ set_plane_src(&mock->old_state, 0, 0, 1024 << 16, 768 << 16);
+ set_plane_src(&mock->state, 0, 0, 1024 << 16, 768 << 16);
+ /* 2 damage clips, but ignore them. */
+ set_damage_clip(&damage[0], 20, 30, 200, 180);
+ set_damage_clip(&damage[1], 240, 200, 280, 250);
+ set_damage_blob(&damage_blob, &damage[0], sizeof(damage));
+ set_plane_damage(&mock->state, &damage_blob);
+ mock->state.ignore_damage_clips = true;
+ drm_atomic_helper_damage_iter_init(&iter, &mock->old_state, &mock->state);
+ drm_atomic_for_each_plane_damage(&iter, &clip) {
+ if (num_hits == 0)
+ check_damage_clip(test, &clip, 0, 0, 1024, 768);
+ num_hits++;
+ }
+
+ KUNIT_EXPECT_EQ_MSG(test, num_hits, 1, "Should return full-framebuffer damage.");
+}
+
static struct kunit_case drm_damage_helper_tests[] = {
KUNIT_CASE(drm_test_damage_iter_no_damage),
KUNIT_CASE(drm_test_damage_iter_no_damage_fractional_src),
@@ -625,6 +652,7 @@ static struct kunit_case drm_damage_helper_tests[] = {
KUNIT_CASE(drm_test_damage_iter_damage_one_outside),
KUNIT_CASE(drm_test_damage_iter_damage_src_moved),
KUNIT_CASE(drm_test_damage_iter_damage_not_visible),
+ KUNIT_CASE(drm_test_damage_iter_damage_ignore),
{ }
};
--
2.54.0
^ permalink raw reply related
* [PATCH v5 08/15] drm/atomic-helpers: Evaluate plane damage after atomic_check
From: Thomas Zimmermann @ 2026-06-10 15:18 UTC (permalink / raw)
To: mripard, maarten.lankhorst, airlied, airlied, simona, admin,
gargaditya08, paul, jani.nikula, mhklkml, zack.rusin,
bcm-kernel-feedback-list, harry.wentland, sunpeng.li, siqueira,
alexander.deucher, rodrigo.vivi, joonas.lahtinen, tursulin,
javierm, dmitry.osipenko, gurchetansingh, olvaffe
Cc: dri-devel, linux-hyperv, intel-gfx, intel-xe, linux-mips,
virtualization, amd-gfx, Thomas Zimmermann
In-Reply-To: <20260610152505.260172-1-tzimmermann@suse.de>
Each plane's and CRTC's atomic_check might trigger a full modeset. As
this affects the plane's damage handling, evaluate damage clips after
running the atomic_check helpers.
Examples can be found in a number of drivers, such as ast, gud, ingenic,
mgag200 or vmwgfx, which all set mode_changed in the CRTC state to true.
Ingenic even re-evaluates damage information in its plane's atomic_check.
Doing this after the atomic_check helpers ran benefits all drivers.
There's already a damage evaluation before the calls to atomic_check.
With a few fixes to drivers, this can be removed.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Acked-by: Zack Rusin <zack.rusin@broadcom.com>
---
drivers/gpu/drm/drm_atomic_helper.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 285aac3554df..8e080a42aec4 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1071,6 +1071,10 @@ drm_atomic_helper_check_planes(struct drm_device *dev,
}
}
+ for_each_oldnew_plane_in_state(state, plane, old_plane_state, new_plane_state, i) {
+ drm_atomic_helper_check_plane_damage(state, new_plane_state);
+ }
+
return ret;
}
EXPORT_SYMBOL(drm_atomic_helper_check_planes);
--
2.54.0
^ permalink raw reply related
* [PATCH v5 13/15] drm/damage-helper: Remove old state from drm_atomic_helper_damage_merged()
From: Thomas Zimmermann @ 2026-06-10 15:18 UTC (permalink / raw)
To: mripard, maarten.lankhorst, airlied, airlied, simona, admin,
gargaditya08, paul, jani.nikula, mhklkml, zack.rusin,
bcm-kernel-feedback-list, harry.wentland, sunpeng.li, siqueira,
alexander.deucher, rodrigo.vivi, joonas.lahtinen, tursulin,
javierm, dmitry.osipenko, gurchetansingh, olvaffe
Cc: dri-devel, linux-hyperv, intel-gfx, intel-xe, linux-mips,
virtualization, amd-gfx, Thomas Zimmermann
In-Reply-To: <20260610152505.260172-1-tzimmermann@suse.de>
Nothing in drm_atomic_helper_damage_merged() requires the old
plane state. Remove the parameter and mass-convert callers.
Most callers now no longer require the old plane state in their plane's
atomic_update helper. Remove it as well.
v5:
- also handle i915
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Acked-by: Zack Rusin <zack.rusin@broadcom.com>
---
drivers/gpu/drm/ast/ast_cursor.c | 3 +--
drivers/gpu/drm/drm_damage_helper.c | 4 +---
drivers/gpu/drm/drm_mipi_dbi.c | 3 +--
drivers/gpu/drm/i915/display/intel_plane.c | 11 ++---------
drivers/gpu/drm/i915/display/intel_psr.c | 7 ++++---
drivers/gpu/drm/sitronix/st7586.c | 3 +--
drivers/gpu/drm/tiny/gm12u320.c | 2 +-
drivers/gpu/drm/tiny/ili9225.c | 3 +--
drivers/gpu/drm/tiny/repaper.c | 2 +-
drivers/gpu/drm/tiny/sharp-memory.c | 3 +--
drivers/gpu/drm/virtio/virtgpu_plane.c | 2 +-
drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c | 4 +---
include/drm/drm_damage_helper.h | 3 +--
13 files changed, 17 insertions(+), 33 deletions(-)
diff --git a/drivers/gpu/drm/ast/ast_cursor.c b/drivers/gpu/drm/ast/ast_cursor.c
index fd19c45f2abe..12d5f93eec5f 100644
--- a/drivers/gpu/drm/ast/ast_cursor.c
+++ b/drivers/gpu/drm/ast/ast_cursor.c
@@ -251,7 +251,6 @@ static void ast_cursor_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_framebuffer *fb = plane_state->fb;
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct ast_device *ast = to_ast_device(plane->dev);
struct drm_rect damage;
u64 dst_off = ast_plane->offset;
@@ -266,7 +265,7 @@ static void ast_cursor_plane_helper_atomic_update(struct drm_plane *plane,
* engine to the offset.
*/
- if (drm_atomic_helper_damage_merged(old_plane_state, plane_state, &damage)) {
+ if (drm_atomic_helper_damage_merged(plane_state, &damage)) {
const u8 *argb4444 = ast_cursor_plane_get_argb4444(ast_cursor_plane,
shadow_plane_state,
&damage);
diff --git a/drivers/gpu/drm/drm_damage_helper.c b/drivers/gpu/drm/drm_damage_helper.c
index 28f26234523d..28b847636253 100644
--- a/drivers/gpu/drm/drm_damage_helper.c
+++ b/drivers/gpu/drm/drm_damage_helper.c
@@ -296,7 +296,6 @@ EXPORT_SYMBOL(drm_atomic_helper_damage_iter_next);
/**
* drm_atomic_helper_damage_merged - Merged plane damage
- * @old_state: Old plane state for validation.
* @state: Plane state from which to iterate the damage clips.
* @rect: Returns the merged damage rectangle
*
@@ -309,8 +308,7 @@ EXPORT_SYMBOL(drm_atomic_helper_damage_iter_next);
* Returns:
* True if there is valid plane damage otherwise false.
*/
-bool drm_atomic_helper_damage_merged(const struct drm_plane_state *old_state,
- const struct drm_plane_state *state,
+bool drm_atomic_helper_damage_merged(const struct drm_plane_state *state,
struct drm_rect *rect)
{
struct drm_atomic_helper_damage_iter iter;
diff --git a/drivers/gpu/drm/drm_mipi_dbi.c b/drivers/gpu/drm/drm_mipi_dbi.c
index 25cf04d029f7..4da201c38c93 100644
--- a/drivers/gpu/drm/drm_mipi_dbi.c
+++ b/drivers/gpu/drm/drm_mipi_dbi.c
@@ -380,7 +380,6 @@ void drm_mipi_dbi_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_plane_state *plane_state = plane->state;
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_framebuffer *fb = plane_state->fb;
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_rect rect;
int idx;
@@ -388,7 +387,7 @@ void drm_mipi_dbi_plane_helper_atomic_update(struct drm_plane *plane,
return;
if (drm_dev_enter(plane->dev, &idx)) {
- if (drm_atomic_helper_damage_merged(old_plane_state, plane_state, &rect))
+ if (drm_atomic_helper_damage_merged(plane_state, &rect))
mipi_dbi_fb_dirty(&shadow_plane_state->data[0], fb, &rect,
&shadow_plane_state->fmtcnv_state);
drm_dev_exit(idx);
diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c
index 3eaf82477f49..a75f89e16f08 100644
--- a/drivers/gpu/drm/i915/display/intel_plane.c
+++ b/drivers/gpu/drm/i915/display/intel_plane.c
@@ -373,7 +373,6 @@ static void intel_plane_clear_hw_state(struct intel_plane_state *plane_state)
static void
intel_plane_copy_uapi_plane_damage(struct intel_plane_state *new_plane_state,
- const struct intel_plane_state *old_uapi_plane_state,
const struct intel_plane_state *new_uapi_plane_state)
{
struct intel_display *display = to_intel_display(new_plane_state);
@@ -383,10 +382,9 @@ intel_plane_copy_uapi_plane_damage(struct intel_plane_state *new_plane_state,
if (DISPLAY_VER(display) < 12)
return;
- if (!drm_atomic_helper_damage_merged(&old_uapi_plane_state->uapi,
- &new_uapi_plane_state->uapi,
+ if (!drm_atomic_helper_damage_merged(&new_uapi_plane_state->uapi,
damage))
- /* Incase helper fails, mark whole plane region as damage */
+ /* In case the helper fails, mark whole plane region as damage */
*damage = drm_plane_state_src(&new_uapi_plane_state->uapi);
}
@@ -851,7 +849,6 @@ static int plane_atomic_check(struct intel_atomic_state *state,
const struct intel_plane_state *old_plane_state =
intel_atomic_get_old_plane_state(state, plane);
const struct intel_plane_state *new_primary_crtc_plane_state;
- const struct intel_plane_state *old_primary_crtc_plane_state;
struct intel_crtc *crtc = intel_crtc_for_pipe(display, plane->pipe);
const struct intel_crtc_state *old_crtc_state =
intel_atomic_get_old_crtc_state(state, crtc);
@@ -866,15 +863,11 @@ static int plane_atomic_check(struct intel_atomic_state *state,
new_primary_crtc_plane_state =
intel_atomic_get_new_plane_state(state, primary_crtc_plane);
- old_primary_crtc_plane_state =
- intel_atomic_get_old_plane_state(state, primary_crtc_plane);
} else {
new_primary_crtc_plane_state = new_plane_state;
- old_primary_crtc_plane_state = old_plane_state;
}
intel_plane_copy_uapi_plane_damage(new_plane_state,
- old_primary_crtc_plane_state,
new_primary_crtc_plane_state);
intel_plane_copy_uapi_to_hw_state(state,
diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c
index e4f43eb5bd72..6581b7de53bb 100644
--- a/drivers/gpu/drm/i915/display/intel_psr.c
+++ b/drivers/gpu/drm/i915/display/intel_psr.c
@@ -3019,10 +3019,11 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state,
drm_rect_fp_to_int(&src, &src);
/* Prepare plane-damage state before using it */
- drm_atomic_helper_check_plane_damage(&state->base, &new_plane_state->uapi);
+ drm_atomic_helper_check_plane_damage(&state->base,
+ &old_plane_state->uapi,
+ &new_plane_state->uapi);
- if (!drm_atomic_helper_damage_merged(&old_plane_state->uapi,
- &new_plane_state->uapi, &damaged_area))
+ if (!drm_atomic_helper_damage_merged(&new_plane_state->uapi, &damaged_area))
continue;
damaged_area.y1 += new_plane_state->uapi.dst.y1 - src.y1;
diff --git a/drivers/gpu/drm/sitronix/st7586.c b/drivers/gpu/drm/sitronix/st7586.c
index 28b2245f6b79..2cc0312595a4 100644
--- a/drivers/gpu/drm/sitronix/st7586.c
+++ b/drivers/gpu/drm/sitronix/st7586.c
@@ -176,7 +176,6 @@ static void st7586_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_plane_state *plane_state = plane->state;
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_framebuffer *fb = plane_state->fb;
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_rect rect;
int idx;
@@ -186,7 +185,7 @@ static void st7586_plane_helper_atomic_update(struct drm_plane *plane,
if (!drm_dev_enter(plane->dev, &idx))
return;
- if (drm_atomic_helper_damage_merged(old_plane_state, plane_state, &rect))
+ if (drm_atomic_helper_damage_merged(plane_state, &rect))
st7586_fb_dirty(&shadow_plane_state->data[0], fb, &rect,
&shadow_plane_state->fmtcnv_state);
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c
index 4ad074337af0..dcc1767c645d 100644
--- a/drivers/gpu/drm/tiny/gm12u320.c
+++ b/drivers/gpu/drm/tiny/gm12u320.c
@@ -583,7 +583,7 @@ static void gm12u320_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(state);
struct drm_rect rect;
- if (drm_atomic_helper_damage_merged(old_state, state, &rect))
+ if (drm_atomic_helper_damage_merged(state, &rect))
gm12u320_fb_mark_dirty(state->fb, &shadow_plane_state->data[0], &rect);
}
diff --git a/drivers/gpu/drm/tiny/ili9225.c b/drivers/gpu/drm/tiny/ili9225.c
index 5bf52a8fd75b..d821a659a585 100644
--- a/drivers/gpu/drm/tiny/ili9225.c
+++ b/drivers/gpu/drm/tiny/ili9225.c
@@ -185,7 +185,6 @@ static void ili9225_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_plane_state *plane_state = plane->state;
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct drm_framebuffer *fb = plane_state->fb;
- struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_rect rect;
int idx;
@@ -195,7 +194,7 @@ static void ili9225_plane_helper_atomic_update(struct drm_plane *plane,
if (!drm_dev_enter(drm, &idx))
return;
- if (drm_atomic_helper_damage_merged(old_plane_state, plane_state, &rect))
+ if (drm_atomic_helper_damage_merged(plane_state, &rect))
ili9225_fb_dirty(&shadow_plane_state->data[0], fb, &rect,
&shadow_plane_state->fmtcnv_state);
diff --git a/drivers/gpu/drm/tiny/repaper.c b/drivers/gpu/drm/tiny/repaper.c
index c8270591afc7..531831d2b73f 100644
--- a/drivers/gpu/drm/tiny/repaper.c
+++ b/drivers/gpu/drm/tiny/repaper.c
@@ -837,7 +837,7 @@ static void repaper_pipe_update(struct drm_simple_display_pipe *pipe,
if (!pipe->crtc.state->active)
return;
- if (drm_atomic_helper_damage_merged(old_state, state, &rect))
+ if (drm_atomic_helper_damage_merged(state, &rect))
repaper_fb_dirty(state->fb, shadow_plane_state->data,
&shadow_plane_state->fmtcnv_state);
}
diff --git a/drivers/gpu/drm/tiny/sharp-memory.c b/drivers/gpu/drm/tiny/sharp-memory.c
index 506e6432e70d..1dacd41ddbaa 100644
--- a/drivers/gpu/drm/tiny/sharp-memory.c
+++ b/drivers/gpu/drm/tiny/sharp-memory.c
@@ -241,7 +241,6 @@ static int sharp_memory_plane_atomic_check(struct drm_plane *plane,
static void sharp_memory_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_commit *state)
{
- struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
struct drm_plane_state *plane_state = plane->state;
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
struct sharp_memory_device *smd;
@@ -251,7 +250,7 @@ static void sharp_memory_plane_atomic_update(struct drm_plane *plane,
if (!smd->crtc.state->active)
return;
- if (drm_atomic_helper_damage_merged(old_state, plane_state, &rect))
+ if (drm_atomic_helper_damage_merged(plane_state, &rect))
sharp_memory_fb_dirty(plane_state->fb, shadow_plane_state->data,
&rect, &shadow_plane_state->fmtcnv_state);
}
diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c
index 1d1b27ece62a..4728047315a2 100644
--- a/drivers/gpu/drm/virtio/virtgpu_plane.c
+++ b/drivers/gpu/drm/virtio/virtgpu_plane.c
@@ -260,7 +260,7 @@ static void virtio_gpu_primary_plane_update(struct drm_plane *plane,
return;
}
- if (!drm_atomic_helper_damage_merged(old_state, plane->state, &rect))
+ if (!drm_atomic_helper_damage_merged(plane->state, &rect))
return;
bo = gem_to_virtio_gpu_obj(plane->state->fb->obj[0]);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
index 4139837f4caf..f0df2b1c8465 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c
@@ -977,7 +977,6 @@ vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane,
enum stdu_content_type new_content_type;
struct vmw_framebuffer_surface *new_vfbs;
uint32_t hdisplay = new_state->crtc_w, vdisplay = new_state->crtc_h;
- struct drm_plane_state *old_state = plane->state;
struct drm_rect rect;
int ret;
@@ -1101,8 +1100,7 @@ vmw_stdu_primary_plane_prepare_fb(struct drm_plane *plane,
struct vmw_surface *surf = vmw_user_object_surface(&vps->uo);
struct vmw_resource *res = &surf->res;
- if (!res->res_dirty && drm_atomic_helper_damage_merged(old_state,
- new_state,
+ if (!res->res_dirty && drm_atomic_helper_damage_merged(new_state,
&rect)) {
/*
* At some point it might be useful to actually translate
diff --git a/include/drm/drm_damage_helper.h b/include/drm/drm_damage_helper.h
index fafe29b50fc6..b5a4de779db6 100644
--- a/include/drm/drm_damage_helper.h
+++ b/include/drm/drm_damage_helper.h
@@ -77,8 +77,7 @@ drm_atomic_helper_damage_iter_init(struct drm_atomic_helper_damage_iter *iter,
bool
drm_atomic_helper_damage_iter_next(struct drm_atomic_helper_damage_iter *iter,
struct drm_rect *rect);
-bool drm_atomic_helper_damage_merged(const struct drm_plane_state *old_state,
- const struct drm_plane_state *state,
+bool drm_atomic_helper_damage_merged(const struct drm_plane_state *state,
struct drm_rect *rect);
#endif
--
2.54.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox