s390/qdio: handle deferred cc1
authorPeter Oberparleiter <oberpar@linux.ibm.com>
Wed, 10 Apr 2024 09:46:18 +0000 (11:46 +0200)
committerAlexander Gordeev <agordeev@linux.ibm.com>
Fri, 12 Apr 2024 14:13:02 +0000 (16:13 +0200)
A deferred condition code 1 response indicates that I/O was not started
and should be retried. The current QDIO implementation handles a cc1
response as I/O error, resulting in a failed QDIO setup. This can happen
for example when a path verification request arrives at the same time
as QDIO setup I/O is started.

Fix this by retrying the QDIO setup I/O when a cc1 response is received.

Note that since

commit 2297791c92d0 ("s390/cio: dont unregister subchannel from child-drivers")
commit 5ef1dc40ffa6 ("s390/cio: fix invalid -EBUSY on ccw_device_start")

deferred cc1 responses are much more likely to occur. See the commit
message of the latter for more background information.

Fixes: 2297791c92d0 ("s390/cio: dont unregister subchannel from child-drivers")
Reviewed-by: Alexandra Winter <wintera@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
drivers/s390/cio/qdio_main.c

index 3d9f0834c78bf1e6a55c17a21218b29bacbd1dce..a1cb39f4b7a27939dcf8ac247a30ed965957bd5e 100644 (file)
@@ -722,8 +722,8 @@ static void qdio_handle_activate_check(struct qdio_irq *irq_ptr,
        lgr_info_log();
 }
 
-static void qdio_establish_handle_irq(struct qdio_irq *irq_ptr, int cstat,
-                                     int dstat)
+static int qdio_establish_handle_irq(struct qdio_irq *irq_ptr, int cstat,
+                                    int dstat, int dcc)
 {
        DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qest irq");
 
@@ -731,15 +731,18 @@ static void qdio_establish_handle_irq(struct qdio_irq *irq_ptr, int cstat,
                goto error;
        if (dstat & ~(DEV_STAT_DEV_END | DEV_STAT_CHN_END))
                goto error;
+       if (dcc == 1)
+               return -EAGAIN;
        if (!(dstat & DEV_STAT_DEV_END))
                goto error;
        qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ESTABLISHED);
-       return;
+       return 0;
 
 error:
        DBF_ERROR("%4x EQ:error", irq_ptr->schid.sch_no);
        DBF_ERROR("ds: %2x cs:%2x", dstat, cstat);
        qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
+       return -EIO;
 }
 
 /* qdio interrupt handler */
@@ -748,7 +751,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
 {
        struct qdio_irq *irq_ptr = cdev->private->qdio_data;
        struct subchannel_id schid;
-       int cstat, dstat;
+       int cstat, dstat, rc, dcc;
 
        if (!intparm || !irq_ptr) {
                ccw_device_get_schid(cdev, &schid);
@@ -768,10 +771,12 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
        qdio_irq_check_sense(irq_ptr, irb);
        cstat = irb->scsw.cmd.cstat;
        dstat = irb->scsw.cmd.dstat;
+       dcc   = scsw_cmd_is_valid_cc(&irb->scsw) ? irb->scsw.cmd.cc : 0;
+       rc    = 0;
 
        switch (irq_ptr->state) {
        case QDIO_IRQ_STATE_INACTIVE:
-               qdio_establish_handle_irq(irq_ptr, cstat, dstat);
+               rc = qdio_establish_handle_irq(irq_ptr, cstat, dstat, dcc);
                break;
        case QDIO_IRQ_STATE_CLEANUP:
                qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
@@ -785,12 +790,25 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
                if (cstat || dstat)
                        qdio_handle_activate_check(irq_ptr, intparm, cstat,
                                                   dstat);
+               else if (dcc == 1)
+                       rc = -EAGAIN;
                break;
        case QDIO_IRQ_STATE_STOPPED:
                break;
        default:
                WARN_ON_ONCE(1);
        }
+
+       if (rc == -EAGAIN) {
+               DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qint retry");
+               rc = ccw_device_start(cdev, irq_ptr->ccw, intparm, 0, 0);
+               if (!rc)
+                       return;
+               DBF_ERROR("%4x RETRY ERR", irq_ptr->schid.sch_no);
+               DBF_ERROR("rc:%4x", rc);
+               qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
+       }
+
        wake_up(&cdev->private->wait_q);
 }