[PATCH v4] s390/qdio: Ensure QDIO_IRQ_STATE_ACTIVE is set only after firmware activates.

From: Nihar Panda

Date: Mon Sep 07 2026 - 01:10:38 EST


Set QDIO_IRQ_STATE_ACTIVE only if both the subchannel-active bit and
the QDIO-active bit are set in the Subchannel Status Word (SCSW).

The channel subsystem sets the SCSW_ACTL_SCHACT bit in scsw.actl and
scsw.qact = 1 in the SCHIB to indicate that the activate-QDIO-queues
CCW program is running and the queues are ready.

An interrupt-driven approach is not applicable here.
Using CCW_FLAG_PCI on the activate CCW generates an intermediate interrupt
too early, before the firmware sets qact=1.
Therefore, polling the SCHIB via cio_update_schib() is the only way to
reliably detect when the queues are ready.

Signed-off-by: Nihar Panda <niharp@xxxxxxxxxxxxx>
Reviewed-by: Alexandra Winter <wintera@xxxxxxxxxxxxx>
Reviewed-by: Benjamin Block <bblock@xxxxxxxxxxxxx>
Reviewed-by: Nagamani PV <nagamani@xxxxxxxxxxxxx>
---
arch/s390/include/asm/scsw.h | 4 +--
drivers/s390/cio/qdio_main.c | 54 +++++++++++++++++++++++++++---------
2 files changed, 43 insertions(+), 15 deletions(-)

diff --git a/arch/s390/include/asm/scsw.h b/arch/s390/include/asm/scsw.h
index 56003e26cdbf..bf00d827d72b 100644
--- a/arch/s390/include/asm/scsw.h
+++ b/arch/s390/include/asm/scsw.h
@@ -28,7 +28,7 @@
* @zcc: zero condition code
* @ectl: extended control
* @pno: path not operational
- * @res: reserved
+ * @qact: qdio active
* @fctl: function control
* @actl: activity control
* @stctl: status control
@@ -50,7 +50,7 @@ struct cmd_scsw {
__u32 zcc : 1;
__u32 ectl : 1;
__u32 pno : 1;
- __u32 res : 1;
+ __u32 qact : 1;
__u32 fctl : 3;
__u32 actl : 7;
__u32 stctl : 5;
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index c1e09fa34e77..821d501efdcc 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -1140,11 +1140,29 @@ EXPORT_SYMBOL_GPL(qdio_establish);
/**
* qdio_activate - activate queues on a qdio subchannel
* @cdev: associated cdev
+ *
+ * This function must only be called when the QDIO subchannel is in
+ * QDIO_IRQ_STATE_ESTABLISHED state (i.e., after successful qdio_establish()).
+ * Any other state indicates either the subchannel is not ready or an error
+ * condition that requires proper recovery through qdio_shutdown() and
+ * qdio_establish() before activation can be attempted.
+ *
+ * Return:
+ * * 0 - success
+ * * -ENODEV - device is not initialized
+ * * -EIO - adapter lacks QDIO activation support, or
+ * the IRQ state changed unexpectedly during activation
+ * * -EBUSY - subchannel state is not QDIO_IRQ_STATE_ESTABLISHED
+ * at call time
+ * * -ETIMEDOUT - subchannel failed to become active within the timeout
+ * * other - standard error code forwarded from ccw_device_start()
*/
int qdio_activate(struct ccw_device *cdev)
{
+ struct subchannel *sch = to_subchannel(cdev->dev.parent);
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
struct subchannel_id schid;
+ unsigned long timeout;
struct ciw *ciw;
int rc;

@@ -1161,7 +1179,8 @@ int qdio_activate(struct ccw_device *cdev)
}

mutex_lock(&irq_ptr->setup_mutex);
- if (irq_ptr->state == QDIO_IRQ_STATE_INACTIVE) {
+ if (irq_ptr->state != QDIO_IRQ_STATE_ESTABLISHED) {
+ DBF_ERROR("%4x act WS:%d", schid.sch_no, irq_ptr->state);
rc = -EBUSY;
goto out;
}
@@ -1178,23 +1197,32 @@ int qdio_activate(struct ccw_device *cdev)
0, DOIO_DENY_PREFETCH);
spin_unlock_irq(get_ccwdev_lock(cdev));
if (rc) {
- DBF_ERROR("%4x act IO ERR", irq_ptr->schid.sch_no);
- DBF_ERROR("rc:%4x", rc);
+ DBF_ERROR("%4x act IE:%d", irq_ptr->schid.sch_no, rc);
goto out;
}

- /* wait for subchannel to become active */
- msleep(5);
+ rc = -ETIMEDOUT;
+ timeout = jiffies + HZ;

- switch (irq_ptr->state) {
- case QDIO_IRQ_STATE_STOPPED:
- case QDIO_IRQ_STATE_ERR:
- rc = -EIO;
- break;
- default:
- qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ACTIVE);
- rc = 0;
+ while (time_before(jiffies, timeout)) {
+ msleep(1);
+ if (irq_ptr->state != QDIO_IRQ_STATE_ESTABLISHED) {
+ rc = -EIO;
+ DBF_ERROR("%4x act WS:%d", irq_ptr->schid.sch_no, irq_ptr->state);
+ break;
+ }
+ /* Query hardware */
+ if (cio_update_schib(sch) == 0) {
+ if ((sch->schib.scsw.cmd.actl & SCSW_ACTL_SCHACT)
+ && sch->schib.scsw.cmd.qact) {
+ qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ACTIVE);
+ rc = 0;
+ break;
+ }
+ }
}
+ if (rc == -ETIMEDOUT)
+ DBF_ERROR("%4x act TIMEOUT", irq_ptr->schid.sch_no);
out:
mutex_unlock(&irq_ptr->setup_mutex);
return rc;
--
2.53.0