From: Cornelia Huck <cohuck@de.ibm.com>

Common i/o layer changes:
 - Update scsw information before checking activity control bits in
   the offline processing.
 - Clear irb structure after cio initiated I/O completed.
 - Modify cdev private structure only while holding the lock.
 - Update scsw information before checking whether we can start path
   verification.
 - Only generate a notoper event if the device is not already in the
   not operation state, otherwise we end up with two unregister calls.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/drivers/s390/cio/chsc.c        |    7 +++++--
 25-akpm/drivers/s390/cio/css.c         |   12 ++++++++++--
 25-akpm/drivers/s390/cio/device.c      |   10 +++++++++-
 25-akpm/drivers/s390/cio/device_fsm.c  |   21 ++++++++++++---------
 25-akpm/drivers/s390/cio/device_id.c   |    1 +
 25-akpm/drivers/s390/cio/device_ops.c  |    2 +-
 25-akpm/drivers/s390/cio/device_pgid.c |   10 ++++++++--
 7 files changed, 46 insertions(+), 17 deletions(-)

diff -puN drivers/s390/cio/chsc.c~s390-common-i-o-layer drivers/s390/cio/chsc.c
--- 25/drivers/s390/cio/chsc.c~s390-common-i-o-layer	2005-03-02 17:55:21.000000000 -0800
+++ 25-akpm/drivers/s390/cio/chsc.c	2005-03-02 17:55:21.000000000 -0800
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/chsc.c
  *   S/390 common I/O routines -- channel subsystem call
- *   $Revision: 1.115 $
+ *   $Revision: 1.118 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *			      IBM Corporation
@@ -423,12 +423,12 @@ s390_process_res_acc (u8 chpid, __u16 fl
 			     sch->schib.pmcw.pam &
 			     sch->schib.pmcw.pom)
 			    | chp_mask) & sch->opm;
-		spin_unlock_irq(&sch->lock);
 		if (!old_lpm && sch->lpm)
 			device_trigger_reprobe(sch);
 		else if (sch->driver && sch->driver->verify)
 			sch->driver->verify(&sch->dev);
 
+		spin_unlock_irq(&sch->lock);
 		put_device(&sch->dev);
 		if (fla_mask != 0)
 			break;
@@ -717,10 +717,12 @@ static inline void
 __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on)
 {
 	int chp, old_lpm;
+	unsigned long flags;
 
 	if (!sch->ssd_info.valid)
 		return;
 	
+	spin_lock_irqsave(&sch->lock, flags);
 	old_lpm = sch->lpm;
 	for (chp = 0; chp < 8; chp++) {
 		if (sch->ssd_info.chpid[chp] != chpid)
@@ -751,6 +753,7 @@ __s390_subchannel_vary_chpid(struct subc
 		}
 		break;
 	}
+	spin_unlock_irqrestore(&sch->lock, flags);
 }
 
 static int
diff -puN drivers/s390/cio/css.c~s390-common-i-o-layer drivers/s390/cio/css.c
--- 25/drivers/s390/cio/css.c~s390-common-i-o-layer	2005-03-02 17:55:21.000000000 -0800
+++ 25-akpm/drivers/s390/cio/css.c	2005-03-02 17:55:21.000000000 -0800
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/css.c
  *  driver for channel subsystem
- *   $Revision: 1.84 $
+ *   $Revision: 1.85 $
  *
  *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
  *			 IBM Corporation
@@ -180,6 +180,7 @@ css_evaluate_subchannel(int irq, int slo
 {
 	int event, ret, disc;
 	struct subchannel *sch;
+	unsigned long flags;
 
 	sch = get_subchannel_by_schid(irq);
 	disc = sch ? device_is_disconnected(sch) : 0;
@@ -221,7 +222,9 @@ css_evaluate_subchannel(int irq, int slo
 			 * coming operational again. It won't do harm in real
 			 * no path situations.
 			 */
+			spin_lock_irqsave(&sch->lock, flags);
 			device_trigger_reprobe(sch);
+			spin_unlock_irqrestore(&sch->lock, flags);
 			ret = 0;
 			break;
 		}
@@ -262,14 +265,19 @@ css_evaluate_subchannel(int irq, int slo
 			 * We can't immediately deregister the disconnected
 			 * device since it might block.
 			 */
+			spin_lock_irqsave(&sch->lock, flags);
 			device_trigger_reprobe(sch);
+			spin_unlock_irqrestore(&sch->lock, flags);
 			ret = 0;
 		}
 		break;
 	case CIO_OPER:
-		if (disc)
+		if (disc) {
+			spin_lock_irqsave(&sch->lock, flags);
 			/* Get device operational again. */
 			device_trigger_reprobe(sch);
+			spin_unlock_irqrestore(&sch->lock, flags);
+		}
 		ret = sch ? 0 : css_probe_device(irq);
 		break;
 	default:
diff -puN drivers/s390/cio/device.c~s390-common-i-o-layer drivers/s390/cio/device.c
--- 25/drivers/s390/cio/device.c~s390-common-i-o-layer	2005-03-02 17:55:21.000000000 -0800
+++ 25-akpm/drivers/s390/cio/device.c	2005-03-02 17:55:21.000000000 -0800
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/device.c
  *  bus driver for ccw devices
- *   $Revision: 1.129 $
+ *   $Revision: 1.131 $
  *
  *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
  *			 IBM Corporation
@@ -293,6 +293,14 @@ ccw_device_set_offline(struct ccw_device
 	cdev->online = 0;
 	spin_lock_irq(cdev->ccwlock);
 	ret = ccw_device_offline(cdev);
+	if (ret == -ENODEV) {
+		if (cdev->private->state != DEV_STATE_NOT_OPER) {
+			cdev->private->state = DEV_STATE_OFFLINE;
+			dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
+		}
+		spin_unlock_irq(cdev->ccwlock);
+		return ret;
+	}
 	spin_unlock_irq(cdev->ccwlock);
 	if (ret == 0)
 		wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
diff -puN drivers/s390/cio/device_fsm.c~s390-common-i-o-layer drivers/s390/cio/device_fsm.c
--- 25/drivers/s390/cio/device_fsm.c~s390-common-i-o-layer	2005-03-02 17:55:21.000000000 -0800
+++ 25-akpm/drivers/s390/cio/device_fsm.c	2005-03-02 17:55:21.000000000 -0800
@@ -560,6 +560,8 @@ ccw_device_offline(struct ccw_device *cd
 	struct subchannel *sch;
 
 	sch = to_subchannel(cdev->dev.parent);
+	if (stsch(sch->irq, &sch->schib) || !sch->schib.pmcw.dnv)
+		return -ENODEV;
 	if (cdev->private->state != DEV_STATE_ONLINE) {
 		if (sch->schib.scsw.actl != 0)
 			return -EBUSY;
@@ -668,6 +670,12 @@ ccw_device_online_verify(struct ccw_devi
 		return;
 	}
 	sch = to_subchannel(cdev->dev.parent);
+	/*
+	 * Since we might not just be coming from an interrupt from the
+	 * subchannel we have to update the schib.
+	 */
+	stsch(sch->irq, &sch->schib);
+
 	if (sch->schib.scsw.actl != 0 ||
 	    (cdev->private->irb.scsw.stctl & SCSW_STCTL_STATUS_PEND)) {
 		/*
@@ -982,21 +990,17 @@ void
 device_trigger_reprobe(struct subchannel *sch)
 {
 	struct ccw_device *cdev;
-	unsigned long flags;
 
 	if (!sch->dev.driver_data)
 		return;
 	cdev = sch->dev.driver_data;
-	spin_lock_irqsave(&sch->lock, flags);
-	if (cdev->private->state != DEV_STATE_DISCONNECTED) {
-		spin_unlock_irqrestore(&sch->lock, flags);
+	if (cdev->private->state != DEV_STATE_DISCONNECTED)
 		return;
-	}
+
 	/* Update some values. */
-	if (stsch(sch->irq, &sch->schib)) {
-		spin_unlock_irqrestore(&sch->lock, flags);
+	if (stsch(sch->irq, &sch->schib))
 		return;
-	}
+
 	/*
 	 * The pim, pam, pom values may not be accurate, but they are the best
 	 * we have before performing device selection :/
@@ -1014,7 +1018,6 @@ device_trigger_reprobe(struct subchannel
 	sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
 	/* We should also udate ssd info, but this has to wait. */
 	ccw_device_start_id(cdev, 0);
-	spin_unlock_irqrestore(&sch->lock, flags);
 }
 
 static void
diff -puN drivers/s390/cio/device_id.c~s390-common-i-o-layer drivers/s390/cio/device_id.c
--- 25/drivers/s390/cio/device_id.c~s390-common-i-o-layer	2005-03-02 17:55:21.000000000 -0800
+++ 25-akpm/drivers/s390/cio/device_id.c	2005-03-02 17:55:21.000000000 -0800
@@ -316,6 +316,7 @@ ccw_device_sense_id_irq(struct ccw_devic
 	if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
 		return;
 	ret = ccw_device_check_sense_id(cdev);
+	memset(&cdev->private->irb, 0, sizeof(struct irb));
 	switch (ret) {
 	/* 0, -ETIME, -EOPNOTSUPP, -EAGAIN or -EACCES */
 	case 0:			/* Sense id succeeded. */
diff -puN drivers/s390/cio/device_ops.c~s390-common-i-o-layer drivers/s390/cio/device_ops.c
--- 25/drivers/s390/cio/device_ops.c~s390-common-i-o-layer	2005-03-02 17:55:21.000000000 -0800
+++ 25-akpm/drivers/s390/cio/device_ops.c	2005-03-02 17:55:21.000000000 -0800
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/device_ops.c
  *
- *   $Revision: 1.53 $
+ *   $Revision: 1.55 $
  *
  *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
  *			 IBM Corporation
diff -puN drivers/s390/cio/device_pgid.c~s390-common-i-o-layer drivers/s390/cio/device_pgid.c
--- 25/drivers/s390/cio/device_pgid.c~s390-common-i-o-layer	2005-03-02 17:55:21.000000000 -0800
+++ 25-akpm/drivers/s390/cio/device_pgid.c	2005-03-02 17:55:21.000000000 -0800
@@ -156,7 +156,9 @@ ccw_device_sense_pgid_irq(struct ccw_dev
 	if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
 		return;
 	sch = to_subchannel(cdev->dev.parent);
-	switch (__ccw_device_check_sense_pgid(cdev)) {
+	ret = __ccw_device_check_sense_pgid(cdev);
+	memset(&cdev->private->irb, 0, sizeof(struct irb));
+	switch (ret) {
 	/* 0, -ETIME, -EOPNOTSUPP, -EAGAIN, -EACCES or -EUSERS */
 	case 0:			/* Sense Path Group ID successful. */
 		if (cdev->private->pgid.inf.ps.state1 == SNID_STATE1_RESET)
@@ -307,6 +309,7 @@ ccw_device_verify_irq(struct ccw_device 
 {
 	struct subchannel *sch;
 	struct irb *irb;
+	int ret;
 
 	irb = (struct irb *) __LC_IRB;
 	/* Retry set pgid for cc=1. */
@@ -319,7 +322,9 @@ ccw_device_verify_irq(struct ccw_device 
 	if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
 		return;
 	sch = to_subchannel(cdev->dev.parent);
-	switch (__ccw_device_check_pgid(cdev)) {
+	ret = __ccw_device_check_pgid(cdev);
+	memset(&cdev->private->irb, 0, sizeof(struct irb));
+	switch (ret) {
 	/* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
 	case 0:
 		/* Establish or Resign Path Group done. Update vpm. */
@@ -405,6 +410,7 @@ ccw_device_disband_irq(struct ccw_device
 		return;
 	sch = to_subchannel(cdev->dev.parent);
 	ret = __ccw_device_check_pgid(cdev);
+	memset(&cdev->private->irb, 0, sizeof(struct irb));
 	switch (ret) {
 	/* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
 	case 0:			/* disband successful. */
_