From: Hans-Joachim Picht <hpicht@redhat.com> Date: Fri, 27 Feb 2009 19:54:49 +0100 Subject: [s390] cio: Properly disable not operational subchannel Message-id: 20090227185449.GA2447@redhat.com O-Subject: [RHEL5 U4 PATCH 1/1] - cio: Properly disable not operational subchannel. Bugzilla: 487701 RH-Acked-by: Pete Zaitcev <zaitcev@redhat.com> Description ============ We might run into the following a Kernel BUG at drivers/s390/cio/device_fsm.c:1241! Problem: After deferred cc=3 was received on all paths, a device was set to not operational to prepare for re-evaluation. An unsolicited interrupt indicating that the device became accessible again triggered an invalid event in the state machine (interrupt in not operational state). Solution: Properly disable the subchannel before setting the device not operational. To be able to disable the subchannel reliably, pending status needs to be collected. Bugzilla ========= BZ 487701 https://bugzilla.redhat.com/show_bug.cgi?id=487701 Upstream status of the patch: ============================= The patch has been posted upstream http://marc.info/?l=linux-s390&m=123557461717201&w=2 This posting is a a port of all the s390 patches we would like to integrate into linux-2.6 once the 2.6.30 merge window opens. Test status: ============ The patch has been tested and fixes the problem. The fix has been verified by the IBM test department. Please ACK. With best regards, --Hans diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index acb393d..a6df5e4 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -466,25 +466,16 @@ cio_disable_subchannel (struct subchannel *sch) if (ccode == 3) /* Not operational. */ return -ENODEV; - if (sch->schib.scsw.actl != 0) - /* - * the disable function must not be called while there are - * requests pending for completion ! - */ - return -EBUSY; - for (retry = 5, ret = 0; retry > 0; retry--) { sch->schib.pmcw.ena = 0; ret = cio_modify(sch); if (ret == -ENODEV) break; - if (ret == -EBUSY) - /* - * The subchannel is busy or status pending. - * We'll disable when the next interrupt was delivered - * via the state machine. - */ - break; + if (ret == -EBUSY) { + struct irb irb; + if (tsch(sch->schid, &irb) != 0) + break; + } if (ret == 0) { stsch (sch->schid, &sch->schib); if (!sch->schib.pmcw.ena) diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 162154f..7a9bac0 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -802,8 +802,10 @@ static void ccw_device_generic_notoper(struct ccw_device *cdev, { struct subchannel *sch; - cdev->private->state = DEV_STATE_NOT_OPER; sch = to_subchannel(cdev->dev.parent); + ccw_device_set_timeout(cdev, 0); + cio_disable_subchannel(sch); + cdev->private->state = DEV_STATE_NOT_OPER; css_schedule_eval(sch->schid); } @@ -1169,7 +1171,7 @@ ccw_device_offline_irq(struct ccw_device *cdev, enum dev_event dev_event) sch = to_subchannel(cdev->dev.parent); /* * An interrupt in state offline means a previous disable was not - * successful. Try again. + * successful - should not happen, but we try to disable again. */ cio_disable_subchannel(sch); }