Skip to content

Commit

Permalink
s390/qdio: handle deferred cc1
Browse files Browse the repository at this point in the history
[ Upstream commit 607638f ]

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 2297791 ("s390/cio: dont unregister subchannel from child-drivers")
commit 5ef1dc4 ("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: 2297791 ("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>
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
oberpar authored and gregkh committed Apr 27, 2024
1 parent 1dbd421 commit dc69cd1
Showing 1 changed file with 23 additions and 5 deletions.
28 changes: 23 additions & 5 deletions drivers/s390/cio/qdio_main.c
Expand Up @@ -722,24 +722,27 @@ 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");

if (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 */
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
}

Expand Down

0 comments on commit dc69cd1

Please sign in to comment.