[PATCH v1 6/6] s390/vfio_ccw: lock I/O resources alongside I/O regions

From: Eric Farman

Date: Tue Jul 14 2026 - 19:23:22 EST


The memory regions shared with userspace for vfio-ccw operations
are correctly accessed under a lock, but there are a handful of
related structures that are associated with the same lifespan of
a given SSCH (and thus the written-to memory region).

Some of these cases are done asynchronously from the guest
(e.g., hot-unplug of a device or channel path event), and so
should be protected in some similar way. Since a subchannel can
only have one I/O active at a time, redefine the I/O mutex from
protecting the region, to all the resources associated with the I/O.

Fixes: 4f76617378ee ("vfio-ccw: protect the I/O region")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Eric Farman <farman@xxxxxxxxxxxxx>
---
drivers/s390/cio/vfio_ccw_chp.c | 2 +-
drivers/s390/cio/vfio_ccw_drv.c | 2 ++
drivers/s390/cio/vfio_ccw_fsm.c | 5 +++++
drivers/s390/cio/vfio_ccw_private.h | 2 +-
4 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/s390/cio/vfio_ccw_chp.c b/drivers/s390/cio/vfio_ccw_chp.c
index 38c176cf6295..872620a9488d 100644
--- a/drivers/s390/cio/vfio_ccw_chp.c
+++ b/drivers/s390/cio/vfio_ccw_chp.c
@@ -90,13 +90,13 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private,
if (pos + count > sizeof(*region))
return -EINVAL;

+ mutex_lock(&private->io_mutex);
crw = list_first_entry_or_null(&private->crw,
struct vfio_ccw_crw, next);

if (crw)
list_del(&crw->next);

- mutex_lock(&private->io_mutex);
region = private->region[i].data;

if (crw)
diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c
index 1a095085bc72..385af7daca3b 100644
--- a/drivers/s390/cio/vfio_ccw_drv.c
+++ b/drivers/s390/cio/vfio_ccw_drv.c
@@ -292,7 +292,9 @@ static void vfio_ccw_queue_crw(struct vfio_ccw_private *private,
crw->crw.erc = erc;
crw->crw.rsid = rsid;

+ mutex_lock(&private->io_mutex);
list_add_tail(&crw->next, &private->crw);
+ mutex_unlock(&private->io_mutex);
queue_work(vfio_ccw_work_q, &private->crw_work);
}

diff --git a/drivers/s390/cio/vfio_ccw_fsm.c b/drivers/s390/cio/vfio_ccw_fsm.c
index 4d7988ea47ef..96f23da88a39 100644
--- a/drivers/s390/cio/vfio_ccw_fsm.c
+++ b/drivers/s390/cio/vfio_ccw_fsm.c
@@ -171,7 +171,9 @@ static void fsm_notoper(struct vfio_ccw_private *private,
private->state = VFIO_CCW_STATE_NOT_OPER;

/* This is usually handled during CLOSE event */
+ mutex_lock(&private->io_mutex);
cp_free(&private->cp);
+ mutex_unlock(&private->io_mutex);
}

/*
@@ -410,7 +412,10 @@ static void fsm_close(struct vfio_ccw_private *private,

private->state = VFIO_CCW_STATE_STANDBY;
spin_unlock_irq(&sch->lock);
+
+ mutex_lock(&private->io_mutex);
cp_free(&private->cp);
+ mutex_unlock(&private->io_mutex);
return;

err_unlock:
diff --git a/drivers/s390/cio/vfio_ccw_private.h b/drivers/s390/cio/vfio_ccw_private.h
index 0501d4bbcdbd..8f3792fdd31b 100644
--- a/drivers/s390/cio/vfio_ccw_private.h
+++ b/drivers/s390/cio/vfio_ccw_private.h
@@ -88,7 +88,7 @@ struct vfio_ccw_parent {
* @state: internal state of the device
* @completion: synchronization helper of the I/O completion
* @io_region: MMIO region to input/output I/O arguments/results
- * @io_mutex: protect against concurrent update of I/O regions
+ * @io_mutex: protect against concurrent update of I/O resources
* @region: additional regions for other subchannel operations
* @cmd_region: MMIO region for asynchronous I/O commands other than START
* @schib_region: MMIO region for SCHIB information
--
2.53.0