}
 }
 
+
+static void cio_apply_config(struct subchannel *sch, struct schib *schib)
+{
+       schib->pmcw.intparm = sch->config.intparm;
+       schib->pmcw.mbi = sch->config.mbi;
+       schib->pmcw.isc = sch->config.isc;
+       schib->pmcw.ena = sch->config.ena;
+       schib->pmcw.mme = sch->config.mme;
+       schib->pmcw.mp = sch->config.mp;
+       schib->pmcw.csense = sch->config.csense;
+       schib->pmcw.mbfc = sch->config.mbfc;
+       if (sch->config.mbfc)
+               schib->mba = sch->config.mba;
+}
+
+static int cio_check_config(struct subchannel *sch, struct schib *schib)
+{
+       return (schib->pmcw.intparm == sch->config.intparm) &&
+               (schib->pmcw.mbi == sch->config.mbi) &&
+               (schib->pmcw.isc == sch->config.isc) &&
+               (schib->pmcw.ena == sch->config.ena) &&
+               (schib->pmcw.mme == sch->config.mme) &&
+               (schib->pmcw.mp == sch->config.mp) &&
+               (schib->pmcw.csense == sch->config.csense) &&
+               (schib->pmcw.mbfc == sch->config.mbfc) &&
+               (!sch->config.mbfc || (schib->mba == sch->config.mba));
+}
+
 /*
- * Function: cio_modify
- * Issues a "Modify Subchannel" on the specified subchannel
+ * cio_commit_config - apply configuration to the subchannel
  */
-int
-cio_modify (struct subchannel *sch)
+int cio_commit_config(struct subchannel *sch)
 {
-       int ccode, retry, ret;
+       struct schib schib;
+       int ccode, retry, ret = 0;
+
+       if (stsch(sch->schid, &schib) || !css_sch_is_valid(&schib))
+               return -ENODEV;
 
-       ret = 0;
        for (retry = 0; retry < 5; retry++) {
-               ccode = msch_err (sch->schid, &sch->schib);
-               if (ccode < 0)  /* -EIO if msch gets a program check. */
+               /* copy desired changes to local schib */
+               cio_apply_config(sch, &schib);
+               ccode = msch_err(sch->schid, &schib);
+               if (ccode < 0) /* -EIO if msch gets a program check. */
                        return ccode;
                switch (ccode) {
                case 0: /* successfull */
-                       return 0;
-               case 1: /* status pending */
+                       if (stsch(sch->schid, &schib) ||
+                           !css_sch_is_valid(&schib))
+                               return -ENODEV;
+                       if (cio_check_config(sch, &schib)) {
+                               /* commit changes from local schib */
+                               memcpy(&sch->schib, &schib, sizeof(schib));
+                               return 0;
+                       }
+                       ret = -EAGAIN;
+                       break;
+               case 1: /* status pending */
                        return -EBUSY;
-               case 2: /* busy */
-                       udelay (100);   /* allow for recovery */
+               case 2: /* busy */
+                       udelay(100); /* allow for recovery */
                        ret = -EBUSY;
                        break;
-               case 3: /* not operational */
+               case 3: /* not operational */
                        return -ENODEV;
                }
        }
        if (cio_update_schib(sch))
                return -ENODEV;
 
-       for (retry = 5, ret = 0; retry > 0; retry--) {
-               sch->schib.pmcw.ena = 1;
-               sch->schib.pmcw.isc = sch->isc;
-               sch->schib.pmcw.intparm = intparm;
-               ret = cio_modify(sch);
-               if (ret == -ENODEV)
-                       break;
-               if (ret == -EIO)
+       sch->config.ena = 1;
+       sch->config.isc = sch->isc;
+       sch->config.intparm = intparm;
+
+       for (retry = 0; retry < 3; retry++) {
+               ret = cio_commit_config(sch);
+               if (ret == -EIO) {
                        /*
-                        * Got a program check in cio_modify. Try without
+                        * Got a program check in msch. Try without
                         * the concurrent sense bit the next time.
                         */
-                       sch->schib.pmcw.csense = 0;
-               if (ret == 0) {
-                       if (cio_update_schib(sch)) {
-                               ret = -ENODEV;
-                               break;
-                       }
-                       if (sch->schib.pmcw.ena)
-                               break;
-               }
-               if (ret == -EBUSY) {
+                       sch->config.csense = 0;
+               } else if (ret == -EBUSY) {
                        struct irb irb;
                        if (tsch(sch->schid, &irb) != 0)
                                break;
-               }
+               } else
+                       break;
        }
        sprintf (dbf_txt, "ret:%d", ret);
        CIO_TRACE_EVENT (2, dbf_txt);
 int cio_disable_subchannel(struct subchannel *sch)
 {
        char dbf_txt[15];
-       int retry;
        int ret;
 
        CIO_TRACE_EVENT (2, "dissch");
                 */
                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 == 0) {
-                       if (cio_update_schib(sch)) {
-                               ret = -ENODEV;
-                               break;
-                       }
-                       if (!sch->schib.pmcw.ena)
-                               break;
-               }
-       }
+       sch->config.ena = 0;
+       ret = cio_commit_config(sch);
+
        sprintf (dbf_txt, "ret:%d", ret);
        CIO_TRACE_EVENT (2, dbf_txt);
        return ret;
         * enable console I/O-interrupt subclass
         */
        isc_register(CONSOLE_ISC);
-       console_subchannel.schib.pmcw.isc = CONSOLE_ISC;
-       console_subchannel.schib.pmcw.intparm =
-               (u32)(addr_t)&console_subchannel;
-       ret = cio_modify(&console_subchannel);
+       console_subchannel.config.isc = CONSOLE_ISC;
+       console_subchannel.config.intparm = (u32)(addr_t)&console_subchannel;
+       ret = cio_commit_config(&console_subchannel);
        if (ret) {
                isc_unregister(CONSOLE_ISC);
                console_subchannel_in_use = 0;
 void
 cio_release_console(void)
 {
-       console_subchannel.schib.pmcw.intparm = 0;
-       cio_modify(&console_subchannel);
+       console_subchannel.config.intparm = 0;
+       cio_commit_config(&console_subchannel);
        isc_unregister(CONSOLE_ISC);
        console_subchannel_in_use = 0;
 }
 
                                /*  ... in an operand exception.       */
 } __attribute__ ((packed));
 
+/* Target SCHIB configuration. */
+struct schib_config {
+       u64 mba;
+       u32 intparm;
+       u16 mbi;
+       u32 isc:3;
+       u32 ena:1;
+       u32 mme:2;
+       u32 mp:1;
+       u32 csense:1;
+       u32 mbfc:1;
+} __attribute__ ((packed));
+
 /*
  * subchannel information block
  */
        struct css_driver *driver;
        void *private; /* private per subchannel type data */
        struct work_struct work;
+       struct schib_config config;
 } __attribute__ ((aligned(8)));
 
 #define IO_INTERRUPT_TYPE         0 /* I/O interrupt type */
 extern int cio_cancel (struct subchannel *);
 extern int cio_set_options (struct subchannel *, int);
 extern int cio_get_options (struct subchannel *);
-extern int cio_modify (struct subchannel *);
 extern int cio_update_schib(struct subchannel *sch);
+extern int cio_commit_config(struct subchannel *sch);
 
 int cio_tm_start_key(struct subchannel *sch, struct tcw *tcw, u8 lpm, u8 key);
 int cio_tm_intrg(struct subchannel *sch);
 
 static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
                     unsigned long address)
 {
-       int ret;
-       int retry;
        struct subchannel *sch;
-       struct schib *schib;
 
        sch = to_subchannel(cdev->dev.parent);
-       schib = &sch->schib;
-       /* msch can silently fail, so do it again if necessary */
-       for (retry = 0; retry < 3; retry++) {
-               /* prepare schib */
-               if (cio_update_schib(sch))
-                       return -ENODEV;
-               schib->pmcw.mme  = mme;
-               schib->pmcw.mbfc = mbfc;
-               /* address can be either a block address or a block index */
-               if (mbfc)
-                       schib->mba = address;
-               else
-                       schib->pmcw.mbi = address;
-
-               /* try to submit it */
-               switch(ret = msch_err(sch->schid, schib)) {
-                       case 0:
-                               break;
-                       case 1:
-                       case 2: /* in I/O or status pending */
-                               ret = -EBUSY;
-                               break;
-                       case 3: /* subchannel is no longer valid */
-                               ret = -ENODEV;
-                               break;
-                       default: /* msch caught an exception */
-                               ret = -EINVAL;
-                               break;
-               }
-               if (cio_update_schib(sch))
-                       return -ENODEV;
-
-               if (ret)
-                       break;
 
-               /* check if it worked */
-               if (schib->pmcw.mme  == mme &&
-                   schib->pmcw.mbfc == mbfc &&
-                   (mbfc ? (schib->mba == address)
-                         : (schib->pmcw.mbi == address)))
-                       return 0;
+       sch->config.mme = mme;
+       sch->config.mbfc = mbfc;
+       /* address can be either a block address or a block index */
+       if (mbfc)
+               sch->config.mba = address;
+       else
+               sch->config.mbi = address;
 
-               ret = -EINVAL;
-       }
-
-       return ret;
+       return cio_commit_config(sch);
 }
 
 struct set_schib_struct {
 
 {
        if (sch) {
                /* Reset intparm to zeroes. */
-               sch->schib.pmcw.intparm = 0;
-               cio_modify(sch);
+               sch->config.intparm = 0;
+               cio_commit_config(sch);
                kfree(sch->lock);
                kfree(sch);
        }
 
        sch = to_subchannel(cdev->dev.parent);
        css_sch_device_unregister(sch);
        /* Reset intparm to zeroes. */
-       sch->schib.pmcw.intparm = 0;
-       cio_modify(sch);
+       sch->config.intparm = 0;
+       cio_commit_config(sch);
        /* Release cdev reference for workqueue processing.*/
        put_device(&cdev->dev);
        /* Release subchannel reference for local processing. */
                spin_unlock_irq(former_parent->lock);
                css_sch_device_unregister(former_parent);
                /* Reset intparm to zeroes. */
-               former_parent->schib.pmcw.intparm = 0;
-               cio_modify(former_parent);
+               former_parent->config.intparm = 0;
+               cio_commit_config(former_parent);
        }
        sch_attach_device(sch, cdev);
 out:
                dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
 }
 
+void io_subchannel_init_config(struct subchannel *sch)
+{
+       memset(&sch->config, 0, sizeof(sch->config));
+       sch->config.csense = 1;
+       if ((sch->lpm & (sch->lpm - 1)) != 0)
+               sch->config.mp = 1;
+}
+
 static void io_subchannel_init_fields(struct subchannel *sch)
 {
        if (cio_is_console(sch->schid))
                      sch->schib.pmcw.dev, sch->schid.ssid,
                      sch->schid.sch_no, sch->schib.pmcw.pim,
                      sch->schib.pmcw.pam, sch->schib.pmcw.pom);
-       /* Initially set up some fields in the pmcw. */
-       sch->schib.pmcw.ena = 0;
-       sch->schib.pmcw.csense = 1;     /* concurrent sense */
-       if ((sch->lpm & (sch->lpm - 1)) != 0)
-               sch->schib.pmcw.mp = 1; /* multipath mode */
-       /* clean up possible residual cmf stuff */
-       sch->schib.pmcw.mme = 0;
-       sch->schib.pmcw.mbfc = 0;
-       sch->schib.pmcw.mbi = 0;
-       sch->schib.mba = 0;
+
+       io_subchannel_init_config(sch);
 }
 
 static void io_subchannel_do_unreg(struct work_struct *work)
        sch = container_of(work, struct subchannel, work);
        css_sch_device_unregister(sch);
        /* Reset intparm to zeroes. */
-       sch->schib.pmcw.intparm = 0;
-       cio_modify(sch);
+       sch->config.intparm = 0;
+       cio_commit_config(sch);
        put_device(&sch->dev);
 }
 
                spin_lock_irqsave(sch->lock, flags);
 
                /* Reset intparm to zeroes. */
-               sch->schib.pmcw.intparm = 0;
-               cio_modify(sch);
+               sch->config.intparm = 0;
+               cio_commit_config(sch);
                break;
        case REPROBE:
                ccw_device_trigger_reprobe(cdev);
 
 extern atomic_t ccw_device_init_count;
 
 void io_subchannel_recog_done(struct ccw_device *cdev);
+void io_subchannel_init_config(struct subchannel *sch);
 
 int ccw_device_cancel_halt_clear(struct ccw_device *);
 
 
         * we have before performing device selection :/
         */
        sch->lpm = sch->schib.pmcw.pam & sch->opm;
-       /* Re-set some bits in the pmcw that were lost. */
-       sch->schib.pmcw.csense = 1;
-       sch->schib.pmcw.ena = 0;
-       if ((sch->lpm & (sch->lpm - 1)) != 0)
-               sch->schib.pmcw.mp = 1;
+       /*
+        * Use the initial configuration since we can't be shure that the old
+        * paths are valid.
+        */
+       io_subchannel_init_config(sch);
+
        /* We should also udate ssd info, but this has to wait. */
        /* Check if this is another device which appeared on the same sch. */
        if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {